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! :)
Related
I'm trying to authenticate to SharePoint Online. Using sharepy v 2.0, pyCharm community edition, and python 3.9.
When I run:
'sharepy.connect('siteurl')'
From within PyCharm, Sharepy will freeze after I input my username in the run dialog box.
If I add the 'username' parameter and run it. Nothing happens. I'm never prompted for a password
If I use the console and enter in sharepy.connect('siteurl') then username and password (same goes for passing those parameters) I will get an error:
Traceback (most recent call last):
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\site-packages\sharepy\auth\adfs.py", line 75, in _get_token
token = root.find('.//wsse:BinarySecurityToken', ns).text
AttributeError: 'NoneType' object has no attribute 'text'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\site-packages\sharepy\session.py", line 15, in connect
return SharePointSession(site, auth=autoauth)
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\site-packages\sharepy\session.py", line 61, in __init__
self.auth.login(self.site)
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\site-packages\sharepy\auth\adfs.py", line 27, in login
self._get_token()
File "C:\Users\Andrew\AppData\Local\Programs\Python\Python39\lib\site-packages\sharepy\auth\adfs.py", line 77, in _get_token
raise errors.AuthError('Token request failed. Invalid server response')
sharepy.errors.AuthError: Token request failed. Invalid server response
It should be noted I'm getting O365 from godaddy and the login page is federated? I think is the correct term.
According to the new release of Sharepy, this shouldn't matter.
Has anyone else had this freezing problem happen for them?
How would I authenticate with sharepoint using sharepy given my current situation?
The source of this problem ended up being GoDaddy. As we were federated using GoDaddy as the O365 provider. There was no way to authenticate correctly using sharepy.
The ultimate solution was to defederate away from GoDaddy (pretty easy to do thanks to this guy: Defederation Guide)
The reason we were unable to authenticate was because our provider redirects the login to their own login site. And unfortunately the sharepy builtin method of "auth" wouldn't work with GoDaddy.
I tested this theory before migrating away from GoDaddy. By using a fresh tenant. I also found that when you enable MFA the password/username method of authentication doesn't work.
NOTE: When new tenants are created they utilize a blanket security protocol which forces MFA. Even though MFA is shown as disabled in the Azure AD > Users section. To turn this off you must disable "Security Defaults": portal.azure.com > Azure Active Directory > Properties > "Manage security defaults" (at the bottom of the screen, its a small hyperlink).
A note on MFA and authentication with sharepy. There are methods to leave MFA enabled which work with other sharepoint/python things. I haven't tested them using sharepy yet, but will be turning on MFA and using one of the following methods:
App Password
Sharepoint API client secret
Azure App Registration (Azure App Reg)
Setup:
Users are created on On-Prem AD and synced to Azure AD via Azure AD Connect
I have a single-tenant app set up on Azure AD
I created a user (On-Prem, synced to AAD) that can authenticate without MFA (we need to use username-password authentication due to an internal limitation).
Here is the non-interactive authentication code:
import msal
# create a public client app
authority_url = f"https://login.microsoftonline.com/{TENANT_ID}"
msal_app = msal.PublicClientApplication(client_id=CLIENT_ID, authority=authority_url)
# acquire token
token = msal_app.acquire_token_by_username_password(username=USERNAME, password=PASSWORD, scopes=SCOPES)
I'm getting the following error:
Traceback (most recent call last):
File "/./scripts/aad.py", line 8, in <module>
token = msal_app.acquire_token_by_username_password(
File "/usr/local/lib/python3.10/site-packages/msal/application.py", line 1420, in acquire_token_by_username_password
response = _clean_up(self._acquire_token_by_username_password_federated(
File "/usr/local/lib/python3.10/site-packages/msal/application.py", line 1447, in _acquire_token_by_username_password_federated
wstrust_result = wst_send_request(
File "/usr/local/lib/python3.10/site-packages/msal/wstrust_request.py", line 60, in send_request
return parse_response(resp.text)
File "/usr/local/lib/python3.10/site-packages/msal/wstrust_response.py", line 49, in parse_response
raise RuntimeError("WsTrust server returned error in RSTR: %s" % (error or body))
RuntimeError: WsTrust server returned error in RSTR: {'reason': 'MSIS7068: Access denied.', 'code': 'a:FailedAuthentication'}
Searching through Google I found that this can be caused by MFA, but the user is excluded from MFA. I've also verified that there are no Conditional Access policies in place to block the user accessing the app.
Using Interactive auth works as expected. Any ideas on how to get non-interactive auth to work or what might be the issue here?
First, no guesswork! You would need to login to Azure AD with elevated privilege (Security Reader at the least if not Global Administrator).
Go to Enterprise Applications and locate your application by client id.
One you are at the application, go to Sign-in tab/pane.
Review the sign-in activities. You should see the reason authentication failed in overview tab. Look at the Conditional Access tab and you will know if there is any policy that blocked the sign-in.
Take action based on what you identified in sign-in activity.
Okay, I am going to make an educated guess! When you login as non-interactive, you have two authentication choices - ROPC and Client Credential- both requires client_secret to be passed in the request but you have not! Since you are using username and password, it implies that msal is using ROPC and you must include client secret.
I am trying to create a job using the python api. I have created my own config, but the authentication fails. It produces an error message:
File "/usr/lib/python2.7/dist-packages/jenkins/__init__.py", line 415, in create_job
self.server + CREATE_JOB % locals(), config_xml, headers))
File "/usr/lib/python2.7/dist-packages/jenkins/__init__.py", line 236, in jenkins_open
'Possibly authentication failed [%s]' % (e.code)
jenkins.JenkinsException: Error in request.Possibly authentication failed [403]
The config file I have created was copied from another job config file as it was the easiest way to build it:
I am using the import jenkins module.
The server instance I create is using these credentials:
server = jenkins.Jenkins(jenkins_url, username = 'my_username', password = 'my_APITOKEN')
Any help will be greatly appreciated.
Error 403 is basically issued when the user is not allowed to access the resource. Are you able to access the resource manually using the same credentials? If there are some other admin credentials, then you can try using those.
Also, I am not sure but may be you can try running the python script with admin rights.
As far as I know for security reasons in Jenkins 2.x only admins are able to create jobs (to be specific - are able to send PUT requests). At least that's what I encountered using Jenkins Job Builder (also Python) and Jenkins 2.x.
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.
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.