authentification for Google Storage using Python - python

I want to build an app which has easy interactions with google storage, i.e., list files in bucket, download a file, and upload a file.
Following this tutorial, I decided to use a service account (not a user one) for authentification and followed the procedure. I created a public/private key on my console and download the key on my machine. Then I created the .boto file which points to this private key, and finally launched this program and it worked:
import boto
import gcs_oauth2_boto_plugin
uri = boto.storage_uri('txxxxxxxxxxxxxx9.appspot.com', 'gs')
for obj in uri.get_bucket():
print '%s://%s/%s' % (uri.scheme, uri.bucket_name, obj.name)
As you can see, the package gcs_oauth2_boto_plugin is not used in the code, so I decided to get rid of it. But magically, when I comment the import gcs_oauth2_boto_plugin line and run the program again, I get this error:
C:\Users\...\Anaconda3\envs\snakes\python.exe C:/Users/.../Dropbox/Prog/s3_manifest_builder/test.py
Traceback (most recent call last):
File "C:/Users/.../Dropbox/Prog/s3_manifest_builder/test.py", line 10, in <module>
for obj in uri.get_bucket():
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\storage_uri.py", line 181, in get_bucket
conn = self.connect()
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\storage_uri.py", line 140, in connect
**connection_args)
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\gs\connection.py", line 47, in __init__
suppress_consec_slashes=suppress_consec_slashes)
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\s3\connection.py", line 190, in __init__
validate_certs=validate_certs, profile_name=profile_name)
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\connection.py", line 569, in __init__
host, config, self.provider, self._required_auth_capability())
File "C:\Users\...\Anaconda3\envs\snakes\lib\site-packages\boto\auth.py", line 987, in get_auth_handler
'Check your credentials' % (len(names), str(names)))
boto.exception.NoAuthHandlerFound: No handler was ready to authenticate. 1 handlers were checked. ['HmacAuthV1Handler'] Check your credentials
So my questions are:
1- how can you explain that deleting an import which IS NOT USED in the code makes it fail?
2- more generally, to be sure to understand the authentification process, if I want to run my app on a machine, I must be sure to have the .boto file (which points to my service account private key) generated previously? Or is there a cleaner/easier way to give access to my application to Google Storage for in/out interactions?
For instance, I only have to provide public and private key as strings to my program when I want to connect to a S3 bucket with boto. I don't needto generate a .boto file, importing packages etc..., which makes it so much easier to use, isn't it?

1- how can you explain that deleting an import which IS NOT USED in the code makes it fail?
The first hint is that the module is named a "plugin", although exactly how that's implemented isn't clear on the surface. It intuitively makes some sense that not importing a module would lead to an exception of this kind, though. Initially, I thought it was a bad practice of doing stateful activity on a global during the init of importing that module. In some ways, that is what it was, but only because class hierarchies are "state" in the meta-programmable python.
It turns out (as in many cases) that inspecting the location that stacktrace was thrown from (boto.auth.get_auth_handler()) provides the key to understanding the issue.
(see the linked source for commented version)
def get_auth_handler(host, config, provider, requested_capability=None):
ready_handlers = []
auth_handlers = boto.plugin.get_plugin(AuthHandler, requested_capability)
for handler in auth_handlers:
try:
ready_handlers.append(handler(host, config, provider))
except boto.auth_handler.NotReadyToAuthenticate:
pass
if not ready_handlers:
checked_handlers = auth_handlers
names = [handler.__name__ for handler in checked_handlers]
raise boto.exception.NoAuthHandlerFound(
'No handler was ready to authenticate. %d handlers were checked.'
' %s '
'Check your credentials' % (len(names), str(names)))
Note the reference to the class AuthHandler, which is defined in boto.auth_handler.
So, you can see that we need to look at the contents of boto.plugin.get_plugin(AuthHandler, requested_capability):
def get_plugin(cls, requested_capability=None):
if not requested_capability:
requested_capability = []
result = []
for handler in cls.__subclasses__():
if handler.is_capable(requested_capability):
result.append(handler)
return result
So, it becomes clear, at last finally when we see that the class definition of the class OAuth2Auth in gcs_oauth2_boto_plugin.oauth2_plugin, in which it is declared as a subclass of boto.auth_handler.AuthHandler, signaling its auth capabilities to the boto framework via the following member variable:
capability = ['google-oauth2', 's3']
2- more generally, to be sure to understand the authentification process, if I want to run my app on a machine, I must be sure to have the .boto file (which points to my service account private key) generated previously? Or is there a cleaner/easier way to give access to my application to Google Storage for in/out interactions?
This has a more generalized answer: You can use a .boto file, although you can also use service account credentials, and you could even use the REST API and go through an oauth2 flow to get the tokens needed to send in the Authorization header. The various methods of auth to cloud storage are in the documentation. The tutorial/doc you linked shows some methods, you've used .boto for another method. You can read about the Cloud Storage REST API (JSON) here and you can read about python oauth2 flows of various kinds here.

Related

Accessing Office 365 ProPlus OneDrive folder using the official Python SDK

We are currently trying to access a folder of an Office 365 ProPlus tenant using the official OneDrive SDK for Python (https://github.com/OneDrive/onedrive-sdk-python). One of our clients would like to use a OneDrive folder as a way of storing and sharing programmatically generated files, therefore, we would like to provide basic file operations.
We have a working solution for a personal OneDrive account, however, when we try to apply the same approach for their OneDrive, we face an issue during the authentication process.
We asked them to register the application in the Azure AD following the steps in the official documentation. Next, they sent us the redirect URI, client ID and client secret that we included in our script. We are trying to use the following code:
redirect_uri = 'REDIRECT_URI'
client_secret = 'CLIENT_SECRET'
client_id='CLIENT_ID'
discovery_uri = 'https://api.office.com/discovery/'
auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize'
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'
http_provider = onedrivesdk.HttpProvider()
auth_provider = onedrivesdk.AuthProvider(http_provider,
client_id,
auth_server_url=auth_server_url,
auth_token_url=auth_token_url)
auth_url = auth_provider.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
However, we get the following error message when executing the last line:
Traceback (most recent call last):
File "onedrive-test.py", line 25, in
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
File "/home/username/.local/lib/python3.6/site-packages/onedrivesdk/helpers/GetAuthCodeServer.py",
line 60, in get_auth_code
s = GetAuthCodeServer((host_address, port), code_acquired, GetAuthCodeRequestHandler)
File "/home/username/.local/lib/python3.6/site-packages/onedrivesdk/helpers/GetAuthCodeServer.py",
line 76, in init
HTTPServer.init(self, server_address, RequestHandlerClass)
File "/usr/lib/python3.6/socketserver.py", line 453, in init
self.server_bind()
File "/usr/lib/python3.6/http/server.py", line 136, in server_bind
socketserver.TCPServer.server_bind(self)
File "/usr/lib/python3.6/socketserver.py", line 467, in server_bind
self.socket.bind(self.server_address)
socket.gaierror: [Errno -2] Name or service not known
We also tried opening the auth_url manually, which took us one step further, but still could not authenticate the application with the following error:
AADSTS50020: User account 'USER ACCOUNT' from identity provider
'live.com' does not exist in tenant 'TENANT NAME' and cannot access
the application 'CLIENT ID' in that tenant. The account needs to be
added as an external user in the tenant first. Sign out and sign in
again with a different Azure Active Directory user account.
We have two questions:
What might casue the first error? This is the comment (see below) that can be found in the readme of the SDK about using the GetAuthCodeServer class. It seems to us that the server cannot be run. Are there any not explicitly defined dependencies that we should be aware of before trying to run the webserver? (We are running the script on Ubuntu 18.10)
If you want to remove some of that manual work, you can
use the helper class GetAuthCodeServer. That helper class spins up a
webserver, so this method cannot be used on all environments.
With respect to the second issue, can you recommend proper material for configuring OneDrive for Business for our use-case? We went through a lot of documentation, but after long hours of research, we still could not find the correct way to fix that issue, especially since we do not have direct acces to the tenant and we cannot easily experiment with things. We would need to give a step-by-step cookbook to our client to set up everything on their side.
Any help would be much appreciated! :)

Set IAM Policy works on local machine but not in GCE instance

The following lines from my Python app execute with no problems on my local machine.
import googleapiclient.discovery
project_id = 'some-project-id'
resource_manager = googleapiclient.discovery.build('cloudresourcemanager', 'v1')
iam_policy_request = resource_manager.projects().getIamPolicy(resource=project_id, body={})
iam_policy_response = iam_policy_request.execute(num_retries=3)
new_policy = dict()
new_policy['policy'] = iam_policy_response
del new_policy['policy']['version']
iam_policy_update_request = resourcemanager.projects().setIamPolicy(resource=project_id, body=new_policy)
update_result = iam_policy_update_request.execute(num_retries=3)
When I run the app in a GCE instance, and more precisely from within a Docker container inside the GCE instance, I get the exception:
URL being requested: POST https://cloudresourcemanager.googleapis.com/v1/projects/some-project-id:setIamPolicy?alt=json
Traceback (most recent call last):
File "/env/lib/python3.5/site-packages/google/api_core/grpc_helpers.py", line 54, in error_remapped_callable
return callable_(*args, **kwargs)
File "/env/lib/python3.5/site-packages/grpc/_channel.py", line 487, in __call__
return _end_unary_response_blocking(state, call, False, deadline)
File "/env/lib/python3.5/site-packages/grpc/_channel.py", line 437, in _end_unary_response_blocking
raise _Rendezvous(state, None, None, deadline)
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with (StatusCode.PERMISSION_DENIED, User not authorized to perform this action.)>
i.e. an authorization error. Oddly, when I open a Python terminal session inside the GCE instance and run the Python code line by line, I do not get the exception. It only throws the exception when the code is running as part of the app.
I am using a service account inside of the GCE instance, as opposed to my regular account on my local machine. But I don't think that is the problem since I am able to run the lines of code one by one inside of the instance while still relying on the service account roles.
I would like to be able to run the app without the exception within the Docker container inside of GCE. I feel like I'm missing something but can't figure out what the missing piece is.
Looking to your issue it seems an authentication issue, because your application is not properly authenticated :
1- First run this command it will let your application temporarily use your own user credentials:
gcloud beta auth application-default login
the output should be like this:
Credentials saved to file: $SOME_PATH/application_default_credentials.json
2-Then you have set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path to the key file:
export GOOGLE_APPLICATION_CREDENTIALS=$SOME_PATH/application_default_credentials.json
Try to run you Application after that.

BadParametersError: Invalid signature when using OVH Python wrapper

I'm using OVH API along with python wrapper:
https://pypi.python.org/pypi/ovh
When trying to execute this code:
import ovh
client = ovh.Client()
# Print nice welcome message
print "Welcome", client.get('/me')['firstname']
I get this error:
Traceback (most recent call last):
File "index.py", line 6, in <module>
print "Welcome", client.get('/me')['firstname']
File "/home/rubinhozzz/.local/lib/python2.7/site-packages/ovh/client.py", line 290, in get
return self.call('GET', _target, None, _need_auth)
File "/home/rubinhozzz/.local/lib/python2.7/site-packages/ovh/client.py", line 419, in call
raise BadParametersError(json_result.get('message'))
ovh.exceptions.BadParametersError: Invalid signature
My info is saved in the ovh.conf as the documentation suggests.
[default]
; general configuration: default endpoint
endpoint=ovh-eu
[ovh-eu]
application_key=XXXlVy5SE7dY7Gc5
application_secret=XXXdTEBKHweS5F0P0tb0lfOa8GoQPy4l
consumer_key=pscg79fXXX8ESMIXXX7dR9ckpDR7Pful
It looks that I can connect but when trying to use the services like for instance "/me", the error raises!
It is difficult to reproduce the issue because it requires an application key and it seems that it is only granted to existing customers of OVH. I couldn't even see a link to an account registration page on their site.
By looking at the code of the call() method in /ovh/client.py, it seems that their server doesn't recognise the format or the content off the signature sent by your script. According to the inline documentation the signature is generated from these parameters:
application_secret
consumer_key
METHOD
full request url
body
server current time (takes time delta into account)
Since your call is identical to the example code provided on the OVH Python package web page, the last four parameters should be valid. In that case it looks like either the application secret or the customer key (or both) in your config file are not correct.
See also the documentation on OVH site under the 'Signing requests' heading. They explain how the signature is made and what it should look like.
Perhaps try to re-create a new application API to obtain new key and secret and make sure you copy them without any additional character.

Finding gapps users groups using the python admin-sdk libraries via the Directory API?

I'm porting our old user management scripts from the Google Provisioning API (which used the python gdata libraries) to the Google Directory API (the python admin-sdk libaries). So far most things have gone fine, however I've run into issues when attempting to do a discovery on what groups a user belongs to (which I need to remove membership from before a user deletion). Even stripping the code down to the barest essentials (replaced e-mails/credentials for public consumption):
#!/usr/bin/python
import httplib2
from apiclient import errors
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
SERVICE_ACCOUNT_EMAIL = 'XXXXXXXXXXXXXXXXXXXXXX#developer.gserviceaccount.com'
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '/blah/blah/XXXXXXXX-privatekey.p12'
f = file(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(SERVICE_ACCOUNT_EMAIL, key,
scope='https://www.googleapis.com/auth/admin.directory.user', sub='serviceaccount#our.tld')
service.users()
members = service.members().get(memberKey = 'serviceaccount#our.old', groupKey = 'googlegroup#our.tld').execute()
print members
This returns a 403 permissions error:
Traceback (most recent call last):
File "group_tests.py", line 39, in <module>
members = service.members().get(memberKey = 'serviceaccount#our.tld', groupKey = 'googlegroup#our.tld').execute()
File "/XXX/bin/gapps/lib/python2.6/site-packages/oauth2client/util.py", line 137, in positional_wrapper
return wrapped(*args, **kwargs)
File "/XXX/bin/gapps/lib/python2.6/site-packages/googleapiclient/http.py", line 729, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/admin/directory/v1/groups/googlegroup%40our.tld/members/serviceaccount%40our.tld?alt=json returned "Insufficient Permission">
I don't recognize if the scope is wrong here, and if so what it should be? This service account is already set with permission for the following scopes (in Security>Advanced Security>API>Manage API client access):
https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/admin.directory.group
Or should I be using groups instead of members? Like:
members = service.groups().get(memberKey = 'serviceaccount#our.old', groupKey = 'googlegroup#our.tld').execute()
Any pointers appreciated, I've been goggling around for any help on this for a week now to no avail.
Got this working:
First off, the scope in the credentials definition was incorrect.
admin.directory.user
changed to:
admin.directory.group
Also had the wrong initialization for the "build":
service.users()
changed to:
service.groups()
And the query statement itself was completely wrong, I went back to the reference doc and kept trying different changes until it took:
members = service.groups().list(domain = 'our.tld',userKey = 'user_whos_groups_i_want#our.tld',pageToken=None,maxResults=500).execute()
Hopefully this will be useful to someone else running into the same issue later. Please be aware that not all permission errors google will throw back are literally because of permissions, it may be your own code has conflicting scopes that you're trying to use.

PyGithub BadCredentialsException when calling Github.get_organization()

I'm using PyGithub v1.25 to create a little webapp where members of my organization can create private repositories in our github organization. Right now, I'm getting a BadCredentialsException when trying to call the get_organization() method of the Github base class.
Here is the relevant portion of my code:
from github import Github
import settings
GIT_OBJECT = Github(login_or_token=settings.AUTH_TOKEN)
ORG_OBJECT = GIT_OBJECT.get_organization('My-Organization-Name')
The auth token I am using was generated from my github user account, which has sufficient privileges to create private repositories in this organization when using the github web interface. I created the token with "user", "repo", and "admin:org" scopes selected. I am getting an error at the creation of ORG_OBJECT.
The stack trace:
File "/local/path/to/my/code/github_console/console/org_manage.py", line 10, in <module>
ORG_OBJECT = GIT_OBJECT.get_organization(‘My-Organization-Name’)
File "/local/path/to/my/code/github_console/lib/github/MainClass.py", line 187, in get_organization
"/orgs/" + login
File "/local/path/to/my/code/github_console/lib/github/Requester.py", line 169, in requestJsonAndCheck
return self.__check(*self.requestJson(verb, url, parameters, headers, input, cnx))
File "/local/path/to/my/code/github_console/lib/github/Requester.py", line 177, in __check
raise self.__createException(status, responseHeaders, output)
BadCredentialsException: 401 {u'documentation_url': u'https://developer.github.com/v3', u'message': u'Bad credentials'}
If anyone who has used either PyGithub or the github API before (or someone who is better than me at reading docs) has any insights, I appreciate the help!
Here's the PyGithub source code, in case anyone wants a look at that.
Doh!
Apparently, the above displayed code works great, and I just effed up importing local settings into my settings module, so a dummy AUTH_TOKEN was being used, and of course, resulting in a BadCredentialsException.
On the plus side, I guess the above is a demonstration of correct PyGithub usage.

Categories