Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use "Domain-wide Delegation of Authority" with gdata-python-client?

I'm using gdata-python-client to access the Google Domain Shared Contacts API.

In enterprise applications you may want to programmatically access users data without any manual authorization on their part.

There was a protocol called 2LO (2 legged OAuth), but seems like it was linked to OAuth1 which was deprecated: "Important: OAuth 1.0 is deprecated, and registration of new OAuth 1.0 clients is closed." is all over the Oauth1 docs.

There is a new OAuth2 based recipe for "Domain-wide Delegation of Authority":

In Google Apps domains, the domain administrator can grant to third party applications domain-wide access to its users' data — this is referred as domain-wide delegation of authority. To delegate authority this way, domain administrators can use service accounts with OAuth 2.0.

This works with google-api-python-client but not with gdata-python-client.

Question: Is there any way to achieve this with Python? Seems like code from the gdata client is prehistoric - is there any other GAE runtime with a modern client library supporting delegation for the data APIs?

[update]

If I sign an httplib2 connection and call the Atom endpoint at I'm able to retrieve the feed.

http = httplib2.Http()
http = credentials.authorize(http)
resp, content = http.request(
    'https://www.google.com/m8/feeds/contacts/default/full', 'GET'
)

Unfortunately gdata-python-client uses httplib instead of httplib2.

[solved]

Perhaps I'm missing some step but looks like the token is not valid until we perform a call using httplib2. I have to run the above code BEFORE running the sample given in [aeijdenberg]'s answer, otherwise I get a 401.

like image 561
Paulo Scardine Avatar asked Jan 11 '23 15:01

Paulo Scardine


1 Answers

Here's an example of how to do domain-wide delegation in Python, in Google App Engine using gdata libraries:

  1. Create a project (https://cloud.google.com/console#/project).

  2. Under "APIs & Auth" enable the APIs you need to use (some gdata APIs don't appear here, if so, skip this step).

  3. Under "APIs & Auth" -> "Credentials" create a new OAuth2 client ID of type service account. Take note of the email address and client ID, and save the private key that is downloaded to a secure location.

  4. As a domain administrator go to the Admin Console (https://admin.google.com/AdminHome), navigate to "Security" -> "Advanced Settings" -> "Managed third party OAuth Client access".

  5. Paste the full client ID from earlier into the "Client Name" field, and paste in the scopes you need for your API access into the scopes field.

  6. Since we are running on Google App Engine, we need to convert the PKCS12 formatted private key to PEM format (since the PyCrypto libraries currently deployed on Google App Engine won't support PCKS12):

    cat secret-privatekey.p12 | openssl pkcs12 -nodes -nocerts -passin pass:notasecret | openssl rsa > secret-privatekey.pem
    
  7. Put this file in your app directory.

  8. Download the Google API Python client from https://code.google.com/p/google-api-python-client/downloads/list, select google-api-python-client-gae-1.2.zip.

  9. Unzip this in your app directory:

    unzip ~/Downloads/google-api-python-client-gae-1.2.zip
    
  10. Download the gdata python client from https://code.google.com/p/gdata-python-client/downloads/list, select gdata-2.0.18.zip.

  11. Install this in your app directory:

    unzip ~/Downloads/gdata-2.0.18.zip
    mv gdata-2.0.18/src/* .
    rm -rf gdata-2.0.18/
    
  12. Make sure PyCrypto is installed locally (but not in your application directory):

    sudo easy_install pycrypto
    
  13. In your app.yaml, add PyCrypto as a library:

    libraries:
    - name: pycrypto
      version: "2.6"
    
  14. Declare the following helper class:

    import httplib2
    
    class TokenFromOAuth2Creds:
      def __init__(self, creds):
        self.creds = creds
      def modify_request(self, req):
        if self.creds.access_token_expired or not self.creds.access_token:
          self.creds.refresh(httplib2.Http())
        self.creds.apply(req.headers)
    
  15. Use the private key to create a SignedJwtAssertionCredentials object:

    from oauth2client.client import SignedJwtAssertionCredentials
    
    credentials = SignedJwtAssertionCredentials(
      "<service account email>@developer.gserviceaccount.com",
      file("secret-privatekey.pem", "rb").read(),
      scope=["http://www.google.com/m8/feeds/"],
      prn="<user to impersonate>@your-domain.com"
    )
    
  16. Create a gdata client and use it:

    gd_client = gdata.contacts.client.ContactsClient('your-domain.com')
    gd_client.auth_token = TokenFromOAuth2Creds(credentials)
    xxx = gd_client.get_contacts()
    
like image 173
aeijdenberg Avatar answered Feb 27 '23 01:02

aeijdenberg