Connecting to SharePoint with Certificate Authentication using Python - python

I am trying to connect to SharePoint Online using Python using Certificate Authentication using https://github.com/vgrem/Office365-REST-Python-Client
If I connect to the root of my SharePoint, it works. But if I try to connect to any specific site, it gets an error. But both work through PowerShell. So I don't think this is a setup issue.
Example - this is PowerShell using the cert in a .pfx file, connecting to the root (https://mytenant.sharepoint.com)
Connect-PnPOnline -Url https://mytenant.sharepoint.com -Tenant mytenant.onmicrosoft.com -ClientId 5fa2148c-d484-444a-bcf1-db632a0fed71 -CertificatePath 'PowershellPnp.pfx' -CertificatePassword $(ConvertTo-Securestring -string "MyCertPassword" -AsPlainText -Force)
Now I change it to connect to a specific site: https://mytenant.sharepoint.com/sites/MySite
Connect-PnPOnline -Url https://mytenant.sharepoint.com/sites/MySite -Tenant mytenant.onmicrosoft.com -ClientId 5fa2148c-d484-444a-bcf1-db632a0fed71 -CertificatePath 'PowershellPnp.pfx' -CertificatePassword $(ConvertTo-Securestring -string "MyCertPassword" -AsPlainText -Force)
Still works, no errors.
Now I try to do the same thing through Python. I use openssl to convert the .pfx to a .pem file, which is what the Python library wants.
First I try the root https://mytenant.sharepoint.com
site_url = "https://mytenant.sharepoint.com"
cert_settings = {
'client_id': '5fa2148c-d484-444a-bcf1-db632a0fed71',
'thumbprint': "D1656C4AAC5CFBB971477230A5FBACCD356829D3",
'cert_path': 'PowershellPnP.pem'
}
ctx = ClientContext(site_url).with_client_certificate('mytenant.onmicrosoft.com',**cert_settings)
This connects without error.
However, if I change the site to https://mytenant.sharepoint.com/MySite:
site_url = "https://mytenant.sharepoint.com/sites/MySite"
cert_settings = {
'client_id': '5fa2148c-d484-444a-bcf1-db632a0fed71',
'thumbprint': "D1656C4AAC5CFBB971477230A5FBACCD356829D3",
'cert_path': 'PowershellPnP.pem'
}
ctx = ClientContext(site_url).with_client_certificate('mytenant.onmicrosoft.com',**cert_settings)
I get this error:
ValueError: {'error': 'invalid_resource', 'error_description':
'AADSTS500011: The resource principal named
https://mytenant.sharepoint.com/sites/MySite was not found in the
tenant named mytenant. This can happen if the application has not been
installed by the administrator of the tenant or consented to by any
user in the tenant. You might have sent your authentication request to
the wrong tenant
I might consider what that error says, but I can connect to that site using the certificate method through PowerShell. So there should be no problem or other requirements to connect to it through Python, no?
It's not the wrong tenant, and Azure shows everything is consented. And it all works in PowersShell.

Turns out this was a bug in the library.
A workaround until it is fixed is to add a value for the scope parameter:
'scopes': ['https://{tenant}.sharepoint.com/.default']

Related

Python SMTP 'unable to get local issuer certificate' in python virtualenv

Sorry if title is unclear.
I'm trying to send an email through python 3.10 through the standard library. It works locally on my machine with these default settings.
smtp_server = "smtp.office365.com"
port = 587 # For starttls
sender_email = os.environ.get("EMAIL")
password = os.environ.get("EMAIL_PASSWORD")
# Create a secure SSL context
context = ssl.create_default_context()
server = smtplib.SMTP(smtp_server,port)
try:
server.starttls(context=context) # Secure the connection
server.login(sender_email, password)
except Exception as e:
print(e)
However when I run this code manually from my company's server, I get a "unable to get local issuer certificate" error.
I've been able to remedy the issue by setting the ssl context to unverified:
context = ssl._create_unverified_context() # was ssl.create_default_context()
And this works when running the python file manually. However, this needs to run as a cronjob, and when the crontab runs the script with this 'fix' I get a different error.
Authentication unsuccessful, the user credentials were incorrect.
Which is ridiculous, because the same credentials worked with a different ssl context.
I'm using pythons virtualenv to run the script. I don't know any networking or certificate specific things to the company ubuntu server, but there's clearly something up. I just don't know what specifically.
Yes, I have looked through the multitude of similar questions, though none of them seem to quite fit this set of circumstances to help me.
Thanks in advance.
You are seeing two separate issues. TLS/certificate issues, and then an authentication issue which most likely has nothing to do with TLS.
There is no need for you to use unverified TLS. You can get the catrust from mozilla via the certifi package. Not sure if microsoft uses browser trust to sign their certificates.
>>> import certifi, ssl
>>> context = ssl.create_default_context()
>>> context.load_verify_locations(certifi.where())
That being said, a common mistake people make is assuming that cron jobs will load the same shell profile files that a login does. I would add error checking/debug loggin to make sure EMAIL_PASSWORD and EMAIL are set according to your expectations, since the error message is most likely coming from server.login line which has nothing to do with TLS.

Downloading files from nextcloud with python script with 2-factor authentication enabled

I set up a nextcloud instance and I would like to download files from there using a python script. My nextcloud instance enforces 2-factor authentication for all users and I want it to remain that way.
My dream scenario would be to use the requests library, so following the docs here https://docs.nextcloud.com/server/15/developer_manual/client_apis/WebDAV/basic.html , I tried to do something like this:
from requests.auth import HTTPBasicAuth
r = requests.request(
method='get',
url='https://mycloudinstance/index.php/apps/files/?dir=/Test&fileid=431',
auth=('username', 'pass')
)
print(r.status_code)
print(r.text)
That gives me an 401 error saying {"message":"Current user is not logged in"}.
When I change the above URL to https://remote.php/dav/myinstance/index.php/apps/files/?dir=/Test&fileid=431 I get
ConnectionError(': Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known'))
As an alternative I was trying to use trying to use this library https://github.com/owncloud/pyocclient just to see if I can create a testfolder with it (it is from owncloud but should work with nextcloud too):
import owncloud
oc = owncloud.Client('https://mycloudinstance')
oc.login('username', 'pass')
oc.mkdir('cooldir')
This throws me an owncloud.owncloud.HTTPResponseError: HTTP error: 401 error. I think that might either be because I just use it incorrectly or because of 2-factor auth.
I am not sure how to use the webdav protocol combined with the python requests library and also I am not sure how to get two-factor authorization to work with it. Has anyone ever done this?
Help is very much appreciated, thanks so much in advance.
You can bypass the 2-factor authentication by generating a secure password for a single application.
In next cloud, go to: Settings -> Personal -> Security -> Create New App Password
The password will be shown to you once (and only once), use it in place of your normal password in your script.

I am Unable to authenticate CKAN 2.7.2 using oauth2 on http

I am using CKAN 2.7.2 .
I have added the following configurations in my development.ini file of ckan
ckan.oauth2.authorization_endpoint = https://account.lab.fiware.org/oauth2/authorize
ckan.oauth2.token_endpoint = https://account.lab.fiware.org/oauth2/token
ckan.oauth2.profile_api_url = https://account.lab.fiware.org/user
ckan.oauth2.client_id = xyz
ckan.oauth2.client_secret = xyz
ckan.oauth2.profile_api_user_field = abc
ckan.oauth2.profile_api_mail_field = abc#gmail.com
Also, have exported the following while running ckan using paster serve :
export OAUTHLIB_INSECURE_TRANSPORT=True
Also, I have added an application in fiware.lab also with callback URL where the CKAN instance is running (i.e a private IP of 172.30.66.XX type running on port 5000)
And when I click on Login,i get redirect to fiware lab login page and after logging in i get the following error
{"state": "eyJjYW1lX2Zyb20iOiAiL2Rhc2hib2FyZCJ9", "error": "mismatching_redirect_uri"} (HTTP 400)
If anyone could please help me in this. It would be of great help.
That error means that the redirect URL attached by CKAN is not the same as the one you have registered as callback URL when registering the application in the IDM.
Ensure that the callback URL you have included in the IDM is:
http://YOUR_CKAN_INSTANCE/oauth2/callback
The URL must match exactly (so no trailing backslash)

How to authenticate in Jenkins while remotely accessing its JSON API?

I need to access the Jenkins JSON API from a Python script. The problem is that our Jenkins installation is secured so to log in users have to select a certificate. Sadly, in Jenkins Remote Access Documentation they don't mention a thing about certificates and I tried using the API Token without success.
How can I get to authenticate from a Python script to use their JSON API?
Thanks in advance!
You have to authenticate to the JSON API using HTTP Basic Auth.
To make scripted clients (such as wget) invoke operations that require authorization (such as scheduling a build), use HTTP BASIC authentication to specify the user name and the API token. This is often more convenient than emulating the form-based authentication
https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients
Here is a sample of using Basic Auth with Python.
http://docs.python-requests.org/en/master/user/authentication/
Keep in mind if you are using a Self Signed certificate on an internal Jenkin Server you'll need to turn off certificate validation OR get the certificate from the server and add it to the HTTP request
http://docs.python-requests.org/en/master/user/advanced/
I finally found out how to authenticate to Jenkins using certs and wget. I had to convert my pfx certificates into pem ones with cert and keys in separate files For more info about that come here. In the end this is the command I used.
wget --certificate=/home/B/cert.pem --private-key=/home/B/key.pem --no-check-certificate --output-document=jenkins.json https:<URL>
I'm not completely sure it covers your certificate use case, but since it took me some time to find out, I still want to share this snipped that retrieves the email address for a given user name in Python without special Jenkins libraries. It uses an API token and "supports" (actually ignores) https:
def _get_email_adress(user):
request = urllib.request.Request("https://jenkins_server/user/"+ user +"/api/json")
#according to https://stackoverflow.com/a/28052583/4609258 the following is ugly
context = ssl._create_unverified_context()
base64string = base64.b64encode(bytes('%s:%s' % ('my user name', 'my API token'),'ascii'))
request.add_header("Authorization", "Basic %s" % base64string.decode('utf-8'))
with urllib.request.urlopen(request, context=context) as url:
user_data = json.loads(url.read().decode())
for property in user_data['property']:
if property["_class"]=="hudson.tasks.Mailer$UserProperty":
return property["address"];

Basic authentication with jira-python

I'm new to Python, new to the jira-python library, and new to network programming, though I do have quite a bit of experience with application and integration programming and database queries (though it's been a while).
Using Python 2.7 and requests 1.0.3
I'm trying to use this library - http://jira-python.readthedocs.org/en/latest/ to query Jira 5.1 using Python. I successfully connected using an unauthenticated query, though I had to make a change to a line in client.py, changing
I changed
self._session = requests.session(verify=verify, hooks={'args': self._add_content_type})
to
self._session = requests.session()
I didn't know what I was doing exactly but before the change I got an error and after the change I got a successful list of project names returned.
Then I tried basic authentication so I can take advantage of my Jira permissions and do reporting. That failed initially too. And I made the same change to
def _create_http_basic_session
in client.py , but now I just get another error. So problem not solved. Now I get a different error:
HTTP Status 415 - Unsupported Media Type
type Status report
message Unsupported Media Type
description The server refused this request because the request entity is in
a format not` `supported by the requested resource for the requested method
(Unsupported Media Type).
So then I decided to do a super simple test just using the requests module, which I believe is being used by the jira-python module and this code seemed to log me in. I got a good response:
import requests
r = requests.get(the_url, auth=(my username , password))
print r.text
Any suggestions?
Here's how I use the jira module with authentication in a Python script:
from jira.client import JIRA
import logging
# Defines a function for connecting to Jira
def connect_jira(log, jira_server, jira_user, jira_password):
'''
Connect to JIRA. Return None on error
'''
try:
log.info("Connecting to JIRA: %s" % jira_server)
jira_options = {'server': jira_server}
jira = JIRA(options=jira_options, basic_auth=(jira_user, jira_password))
# ^--- Note the tuple
return jira
except Exception,e:
log.error("Failed to connect to JIRA: %s" % e)
return None
# create logger
log = logging.getLogger(__name__)
# NOTE: You put your login details in the function call connect_jira(..) below!
# create a connection object, jc
jc = connect_jira(log, "https://myjira.mydom.com", "myusername", "mypassword")
# print names of all projects
projects = jc.projects()
for v in projects:
print v
Below Python script connects to Jira and does basic authentication and lists all projects.
from jira.client import JIRA
options = {'server': 'Jira-URL'}
jira = JIRA(options, basic_auth=('username', 'password'))
projects = jira.projects()
for v in projects:
print v
It prints a list of all the project's available within your instance of Jira.
Problem:
As of June 2019, Atlassian Cloud users who are using a REST endpoint in Jira or Confluence Cloud with basic or cookie-based authentication will need to update their app or integration processes to use an API token, OAuth, or Atlassian Connect.
After June 5th, 2019 attempts to authenticate via basic auth with an Atlassian account password will return an invalid credentials error.
Reference: Deprecation of basic authentication with passwords for Jira and Confluence APIs
Solution to the Above-mentioned Problem:
You can use an API token to authenticate a script or other process with an Atlassian cloud product. You generate the token from your Atlassian account, then copy and paste it to the script.
If you use two-step verification to authenticate, your script will need to use a REST API token to authenticate.
Steps to Create an API Token from your Atlassian Account:
Log in to https://id.atlassian.com/manage/api-tokens
Click Create API token.
From the dialog that appears, enter a memorable and concise Label for your token and click Create.
Click Copy to clipboard, then paste the token to your script.
Reference: API tokens
Python 3.8 Code Reference
from jira.client import JIRA
jira_client = JIRA(options={'server': JIRA_URL}, basic_auth=(JIRA_USERNAME, JIRA_TOKEN))
issue = jira_client.issue('PLAT-8742')
print(issue.fields.summary)
Don't change the library, instead put your credentials inside the ~/.netrc file.
If you put them there you will also be able to test your calls using curl or wget.
I am not sure anymore about compatibility with Jira 5.x, only 7.x and 6.4 are currently tested. If you setup an instance for testing I could modify the integration tests to run against it, too.
My lucky guess is that you broke it with that change.
As of 2019 Atlassian has deprecated authorizing with passwords.
You can easily replace the password with an API Token created here.
Here's a minimalistic example:
pip install jira
from jira import JIRA
jira = JIRA("YOUR-JIRA-URL", basic_auth=("YOUR-EMAIL", "YOUR-API-TOKEN"))
issue = jira.issue("YOUR-ISSUE-KEY (e.g. ABC-13)")
print(issue.fields.summary)
I recommend storing your API Token as an environment variable and accessing it with os.environ[key].

Categories