ADAL for Python bearer token write access issue - python

Using this config in the https://github.com/Azure-Samples/ms-identity-python-webapp worked for me. The scope must include the CDS organization/environment API URL.
CLIENT_SECRET = "**********************************"
AUTHORITY = "https://login.microsoftonline.com/common"
CLIENT_ID = "3641e71b-ad98-4489-8423-f77532d0a5d5"
REDIRECT_PATH = "/getAToken"
ENDPOINT = 'https://graph.microsoft.com/v1.0/users'
CDS_ENDPOINT = "https://REPLACE_WITH_ORG_NAME.api.crm.dynamics.com/api/data/v9.0/msmrw_guides?$select=msmrw_name&$expand=msmrw_guide_Annotations"
# cds environment / crm org, MUST be a part of the scope
SCOPE = ["https://REPLACE_WITH_ORG_NAME.api.crm.dynamics.com/user_impersonation"]
SESSION_TYPE = "filesystem"
ADAL issue Pyhton bearer token issue
I am authenticating non-interactively with a client id and secret for my REST application that interacts with the Common Data Service. Testing first in Postman I am able to perform both read and write operations using my credentials successfully.
When implementing the REST API in Python I can only perform Read operations through the Azure AD Authentication Library (ADAL) with version adal==1.2.2. I am using the same credentials from my Postman environment in Python through a config file, an example is further below. I also tested this Python ADAL generated token in Postman to verify that I can make get requests since I noticed the token strings are different between the one I generate in Python and one I generate in Postman. When making a post request I get the error shown even further below. My guess is I need logic in python to somehow tell ADAL the user requesting the token is allowed write permissions.
Code and Postman error
import adal
from .config import *
class AzureAuth:
bearerToken = ""
def __init__(self):
authCtx = adal.AuthenticationContext(AUTHORITY_URL)
tokenResponse = authCtx.acquire_token_with_client_credentials(RESOURCE, CLIENT_ID, CLIENT_SECRET)
self.bearerToken = tokenResponse["accessToken"]
Here's an example of what my config.py looks like.
AUTHORITY_URL=""
CDS_API_URL=""
CLIENT_ID=""
CLIENT_SECRET=""
REDIRECT_URI=""
RESOURCE="" # like "https://your_cds_environment.api.crm.dynamics.com/"
TENANT="" # like "https://login.microsoftonline.com/put_your_tenant"
Error thrown in Postman when I copy paste the Python token into a Postman post request.
{
"error": {
"code": "0x80048306",
"message": "{\"CallerPrincipal\":{\"PrincipalId\":\"96b856f4-134c-e911-a823-000d3a1d5de8\",\"Type\":8,\"IsUserPrincipal\":true,\"IsTeamPrincipal\":false,\"TypeName\":\"user\"},\"OwnerPrincipal\":{\"PrincipalId\":\"96b856f4-134c-e911-a823-000d3a1d5de8\",\"Type\":8,\"IsUserPrincipal\":true,\"IsTeamPrincipal\":false,\"TypeName\":\"user\"},\"CallerInfo\":{\"IsSystemUser\":false,\"IsSupportUser\":false,\"IsAdministrator\":false,\"IsCustomizer\":false,\"IsDisabled\":false,\"IsIntegrationUser\":false,\"Privileges\":null,\"Teams\":null,\"Roles\":null},\"ObjectId\":\"00000000-0000-0000-0000-000000000000\",\"ObjectTypeCode\":10410,\"ObjectBusinessUnitId\":\"1abfdddc-8140-e911-a823-000d3a1a25b8\",\"OrganizationId\":\"08907b95-ee84-4861-b141-b584fecc774d\",\"EntityName\":\"msmrw_guide\",\"EntityOwnershipTypeMask\":1,\"EntityPrivileges\":[{\"Id\":\"915a1feb-2a56-4a8c-bea0-3256728785e5\",\"ObjectTypeCode\":10410,\"Name\":\"prvCreatemsmrw_guide\",\"AccessRight\":32,\"PrivilegeType\":0,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"6415b9e2-24c4-408a-a830-85c21ab8ac72\",\"ObjectTypeCode\":10410,\"Name\":\"prvReadmsmrw_guide\",\"AccessRight\":1,\"PrivilegeType\":1,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"229dfe7f-1394-47fe-84d4-8d5d7747cbd2\",\"ObjectTypeCode\":10410,\"Name\":\"prvWritemsmrw_guide\",\"AccessRight\":2,\"PrivilegeType\":2,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"d6624737-6846-49c7-bfef-25f6f482e297\",\"ObjectTypeCode\":10410,\"Name\":\"prvDeletemsmrw_guide\",\"AccessRight\":65536,\"PrivilegeType\":3,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"4f4fa37d-b977-4acf-a05a-d003ac32fc44\",\"ObjectTypeCode\":10410,\"Name\":\"prvAssignmsmrw_guide\",\"AccessRight\":524288,\"PrivilegeType\":4,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"09e5b82f-5cd8-46ea-a7ae-acbc472ae2b2\",\"ObjectTypeCode\":10410,\"Name\":\"prvSharemsmrw_guide\",\"AccessRight\":262144,\"PrivilegeType\":5,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"93b7f403-fc9b-442b-b602-9ba347ddda26\",\"ObjectTypeCode\":10410,\"Name\":\"prvAppendmsmrw_guide\",\"AccessRight\":4,\"PrivilegeType\":6,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"776967fe-67f4-4b3d-908a-dfb4306d2372\",\"ObjectTypeCode\":10410,\"Name\":\"prvAppendTomsmrw_guide\",\"AccessRight\":16,\"PrivilegeType\":7,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true}],\"RightsToCheck\":\"CreateAccess\",\"RoleAccessRights\":\"None\",\"PoaAccessRights\":\"None\",\"HsmAccessRights\":\"None\",\"Messages\":[\"PrincipalHasOwnerPrincipalWithAtLeastBasicPrivilegeDepth = False\",\"EntityUserGroupRights = None\",\"MinimumPrivilegeDepthRequired = Local\",\"GrantedRights = None\",\"SecLib::AccessCheckEx2 failed. Owner Data: roleCount=1, privilegeCount=0, accessMode=4; Principal Data: roleCount=1, privilegeCount=0, accessMode=4\"],\"IsHsmEnabled\":false,\"IsOwnerDirectReport\":false,\"IsDirectReportInOwningTeam\":false,\"IsReadAccessFromIndirectReport\":false}",
"innererror": {
"message": "{\"CallerPrincipal\":{\"PrincipalId\":\"96b856f4-134c-e911-a823-000d3a1d5de8\",\"Type\":8,\"IsUserPrincipal\":true,\"IsTeamPrincipal\":false,\"TypeName\":\"user\"},\"OwnerPrincipal\":{\"PrincipalId\":\"96b856f4-134c-e911-a823-000d3a1d5de8\",\"Type\":8,\"IsUserPrincipal\":true,\"IsTeamPrincipal\":false,\"TypeName\":\"user\"},\"CallerInfo\":{\"IsSystemUser\":false,\"IsSupportUser\":false,\"IsAdministrator\":false,\"IsCustomizer\":false,\"IsDisabled\":false,\"IsIntegrationUser\":false,\"Privileges\":null,\"Teams\":null,\"Roles\":null},\"ObjectId\":\"00000000-0000-0000-0000-000000000000\",\"ObjectTypeCode\":10410,\"ObjectBusinessUnitId\":\"1abfdddc-8140-e911-a823-000d3a1a25b8\",\"OrganizationId\":\"08907b95-ee84-4861-b141-b584fecc774d\",\"EntityName\":\"msmrw_guide\",\"EntityOwnershipTypeMask\":1,\"EntityPrivileges\":[{\"Id\":\"915a1feb-2a56-4a8c-bea0-3256728785e5\",\"ObjectTypeCode\":10410,\"Name\":\"prvCreatemsmrw_guide\",\"AccessRight\":32,\"PrivilegeType\":0,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"6415b9e2-24c4-408a-a830-85c21ab8ac72\",\"ObjectTypeCode\":10410,\"Name\":\"prvReadmsmrw_guide\",\"AccessRight\":1,\"PrivilegeType\":1,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"229dfe7f-1394-47fe-84d4-8d5d7747cbd2\",\"ObjectTypeCode\":10410,\"Name\":\"prvWritemsmrw_guide\",\"AccessRight\":2,\"PrivilegeType\":2,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"d6624737-6846-49c7-bfef-25f6f482e297\",\"ObjectTypeCode\":10410,\"Name\":\"prvDeletemsmrw_guide\",\"AccessRight\":65536,\"PrivilegeType\":3,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"4f4fa37d-b977-4acf-a05a-d003ac32fc44\",\"ObjectTypeCode\":10410,\"Name\":\"prvAssignmsmrw_guide\",\"AccessRight\":524288,\"PrivilegeType\":4,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"09e5b82f-5cd8-46ea-a7ae-acbc472ae2b2\",\"ObjectTypeCode\":10410,\"Name\":\"prvSharemsmrw_guide\",\"AccessRight\":262144,\"PrivilegeType\":5,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"93b7f403-fc9b-442b-b602-9ba347ddda26\",\"ObjectTypeCode\":10410,\"Name\":\"prvAppendmsmrw_guide\",\"AccessRight\":4,\"PrivilegeType\":6,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true},{\"Id\":\"776967fe-67f4-4b3d-908a-dfb4306d2372\",\"ObjectTypeCode\":10410,\"Name\":\"prvAppendTomsmrw_guide\",\"AccessRight\":16,\"PrivilegeType\":7,\"CanBeBasic\":true,\"CanBeLocal\":true,\"CanBeDeep\":true,\"CanBeGlobal\":true,\"CanBeEntityReference\":true,\"CanBeParentEntityReference\":true}],\"RightsToCheck\":\"CreateAccess\",\"RoleAccessRights\":\"None\",\"PoaAccessRights\":\"None\",\"HsmAccessRights\":\"None\",\"Messages\":[\"PrincipalHasOwnerPrincipalWithAtLeastBasicPrivilegeDepth = False\",\"EntityUserGroupRights = None\",\"MinimumPrivilegeDepthRequired = Local\",\"GrantedRights = None\",\"SecLib::AccessCheckEx2 failed. Owner Data: roleCount=1, privilegeCount=0, accessMode=4; Principal Data: roleCount=1, privilegeCount=0, accessMode=4\"],\"IsHsmEnabled\":false,\"IsOwnerDirectReport\":false,\"IsDirectReportInOwningTeam\":false,\"IsReadAccessFromIndirectReport\":false}",
"type": "System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]",
"stacktrace": " at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.CreateInternal(Entity entity, InvocationContext invocationContext, CallerOriginToken callerOriginToken, WebServiceType serviceType, Boolean checkAdminMode, Dictionary`2 optionalParameters)\r\n at Microsoft.Crm.Extensibility.OData.CrmODataExecutionContext.CreateOrganizationResponse(Entity entity)\r\n at Microsoft.Crm.Extensibility.OData.CrmODataServiceDataProvider.CreateEdmEntity(CrmODataExecutionContext context, String edmEntityName, EdmEntityObject entityObject, Boolean isUpsert)\r\n at Microsoft.Crm.Extensibility.OData.EntityController.PostEntitySetImplementation(String& entitySetName, EdmEntityObject entityObject)\r\n at Microsoft.PowerApps.CoreFramework.ActivityLoggerExtensions.Execute[TResult](ILogger logger, EventId eventId, ActivityType activityType, Func`1 func, IEnumerable`1 additionalCustomProperties)\r\n at Microsoft.Xrm.Telemetry.XrmTelemetryExtensions.Execute[TResult](ILogger logger, XrmTelemetryActivityType activityType, Func`1 func)\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
}
}
These are the fields I file out in Postman when requesting a token and the values for these fields, I removed the secret for security.
These are the permissions my app uses.

According to the code and picture you provided, in the postman, you use the OAuth 2.0 code grant flow to require Azure AD access token. The way is getting access on behalf of a user. The token is user token. But in your application, you use the method acquire_token_with_client_credentials to require Azure AD access token. It means that you use OAuth 2.0 Client Credentials Grant Flow to require Azure AD access token. The way is getting access without a user. The token is app token. They are different.
Besides, according to the error, you do not have enough privilege and you need to configure the permissions. It means that if you still want to use OAuth 2.0 Client Credentials Grant Flow to require access token, you need to provide enough privilege for the service principal(the AD application ) you use. Or you change your code to use OAuth 2.0 code grant flow to require access token. For more details about how to change, please refer to the sample

Related

msal ConfidentialClientApplication Sucessfully Obtains Bearer Token; Authorization_RequestDenied Despite Full Privileges Granted

Thank you in advance for your help.
I am trying to access email messages for a specific email account for an organization that uses Microsoft Graph.
The Python app I am creating needs to read and forward email messages that are in this specific email account at "someuser#myorganization.com". (I previously successfully read the emails using the Python library exchangelib, but now Basic Auth is no longer supported by Microsoft; exchangelib does not support Microsoft Graph).
The organization has registered an application "Email Service App" with the Microsoft identity platform. It appears that full permissions have been granted. See the screenshot the organization sent to me below. (I do not have access to the Graph Dashboard.)
I have been following the excellent example provided in the GitHub repository for the msal library: https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/dev/sample/confidential_client_secret_sample.py
The code I have written does successfully obtain a bearer token.
My expectation is that with that bearer token I could read emails for a specific email account: someuser#myorganization.com
The error from the code is simple: "Insufficient privileges to complete the operation."
I am nonetheless confused because it does appear that full permissions have been granted. (See above screenshot.)
Here is the code I am using:
config_data = {
"authority": "https://login.microsoftonline.com/<secret value>",
"client_id": "<secret value>",
"scope": ["https://graph.microsoft.com/.default"],
"secret": "<secret value>",
"endpoint": "https://graph.microsoft.com/v1.0/users"
}
# Optional logging
logging.basicConfig(level=logging.DEBUG) # Enable DEBUG log for entire script
logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs
config = json.loads(config_data)
# Create a preferably long-lived app instance which maintains a token cache.
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential=config["secret"],
)
LOG OUTPUT:
DEBUG:urllib3.util.retry:Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): login.microsoftonline.com:443
DEBUG:urllib3.connectionpool:https://login.microsoftonline.com:443 "GET /<secret>/v2.0/.well-known/openid-configuration HTTP/1.1" 200 1753
Code continues:
# The pattern to acquire a token looks like this.
result = None
# Firstly, looks up a token from cache
# Since we are looking for token for the current app, NOT for an end user,
# notice we give account parameter as None.
result = app.acquire_token_silent(config["scope"], account=None)
LOG OUTPUT:
INFO:root:No suitable token exists in cache. Let's get a new one from AAD.
DEBUG:urllib3.connectionpool:https://login.microsoftonline.com:443 "POST /<secret>/oauth2/v2.0/token HTTP/1.1" 200 1589
Code continues:
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
result = app.acquire_token_for_client(scopes=config["scope"])
LOG OUTPUT:
INFO:root:No suitable token exists in cache. Let's get a new one from AAD.
DEBUG:urllib3.connectionpool:https://login.microsoftonline.com:443 "POST /<secret>/oauth2/v2.0/token HTTP/1.1" 200 1589
Code continues:
print(result)
OUTPUT:
{'token_type': 'Bearer',
'expires_in': 3599,
'ext_expires_in': 3599,
'access_token': <removed, was successful>}
Code continues:
if "access_token" in result:
# Calling graph using the access token
graph_data = requests.get( # Use token to call downstream service
config["endpoint"],
headers={'Authorization': 'Bearer ' + result['access_token']},).json()
print("Graph API call result: %s" % json.dumps(graph_data, indent=2))
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bug
LOG OUTPUT:
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): graph.microsoft.com:443
DEBUG:urllib3.connectionpool:https://graph.microsoft.com:443 "GET /v1.0/users HTTP/1.1" 403 None
Graph API call result: {
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"date": "2022-11-06T13:07:13",
"request-id": "13a2593f-e9c9-4844-aca5-7c362a7f83b8",
"client-request-id": "13a2593f-e9c9-4844-aca5-7c362a7f83b8"
}
}
}
Is the issue with the code I am using or are there some other settings in Microsoft Graph that need to be set by the owning organization? Or is it another issue?
Thank you again for your help.
The problem is your trying to use the client credentials flow but all your permission are delegate permissions which aren't valid for that flow. You need to assign App permission and consent to them eg something like
By default this will give you access to every mailbox in the tenant as you mentioned you only want to access one mailbox you can then scope the permission down https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access

Databricks API call fails on Azure DevOps pipelines using python script, but run successfully on Postman from local machine

In Azure databricks API I am trying to pull latest changes to main branch in each folder in Databricks repos by using Azure Databricks API. This is where I am refering to:
When I use postman to make the calls by posting requests to the following endpoint, it pulls successfully as shown below:
endpoint:
https://<databricks-workspace>.azuredatabricks.net/api/2.0/repos/<repo-id>
This is the header of the same request:
To explain more, the header is constructed by sending a bearer token, a management token and another field which contains subscription, resource group and databricks workspace as shown below:
/subscriptions/<Azure-subscription-id>/resourceGroups/<resourcegroup-name>/providers/Microsoft.Databricks/workspaces/<databricks-workspace-name>
As shown above it works perfectly well when I call it on my local machine with postman. But when I use the same thing by using Azure DevOps it fails with the error:
Exception: b'{"error_code":"PERMISSION_DENIED","message":"Missing Git provider credentials. Go to User Settings > Git Integration to add your personal access token."}'
Note that following this link I have already generated a PAT token in Azure DevOps and added it to my Service Principal, otherwise it wouldn't have worked on my postman. Still it is giving this error in DevOps pipeline as shown below:
This pipeline is doing exactly what I already did with postman. This pipeline is calling a python script which is constructing the request header and body as shown above in postman. The python code is as below but I am almost sure it is not the python script that is causing the issue as I have used the same method to list repos, get specific repo, create clusters and many more by the same methodology. I think it must be some administrative problem which I cannot pin point.
The python script:
import requests
import os
import json
## Constructing the header request
DBRKS_REQ_HEADERS = {
'Authorization': 'Bearer ' + os.environ['DBRKS_BEARER_TOKEN'],
'X-Databricks-Azure-Workspace-Resource-Id': '/subscriptions/'+ os.environ['DBRKS_SUBSCRIPTION_ID'] +'/resourceGroups/'+ os.environ['DBRKS_RESOURCE_GROUP'] +'/providers/Microsoft.Databricks/workspaces/' + os.environ['DBRKS_WORKSPACE_NAME'],
'X-Databricks-Azure-SP-Management-Token': os.environ['DBRKS_MANAGEMENT_TOKEN']}
TRIGGERING_BRANCH = "\"" + os.environ["TRIGGERING_BRANCH"] + "\""
print("TRIGGERING_BRANCH path is {}".format(TRIGGERING_BRANCH))
## Constructing the body request
body_json = '''
{
"branch": "main"
}
'''
## Checking the request body format is correct
print("Request body in json format:")
print(body_json)
## The prints are only for code tracing
DBRKS_REPOS_LIST_JSON = os.environ["DBRKS_REPOS_LIST"]
print("Type of DBRKS_REPOS_LIST_JSON is {}".format(type(DBRKS_REPOS_LIST_JSON)))
## This section extracts repo Ids from the variable containing repo Ids and branches to later construct url endpoint
str_obj = DBRKS_REPOS_LIST_JSON.replace('[','').replace(']','').replace('(','').replace(')','').replace('\'','').replace(' ','').split(',')
output = {}
str_to_list = [str_obj[i:i+2] for i in range(0, len(str_obj), 2)]
print("str_to_list")
print(str_to_list)
for e in str_to_list:
output[e[0]] = e[1]
print("output")
print(output)
repo_ids_for_main_branch = []
for key, value in output.items():
if value == 'main':
repo_ids_for_main_branch.append(key)
print("repo_ids_for_main_branch")
print(repo_ids_for_main_branch)
## This is the main part which is making the API call like postman:
for repo_id in repo_ids_for_main_branch:
dbrks_pull_repo_url = "https://"+os.environ['DBRKS_INSTANCE']+".azuredatabricks.net/api/2.0/repos/"+str(repo_id)
print("endpoint url is {}".format(dbrks_pull_repo_url))
response = requests.patch(dbrks_pull_repo_url, headers=DBRKS_REQ_HEADERS, data=body_json)
if response.status_code == 200:
print("Branch changed or pulled successfully!")
print(response.status_code)
print(response.content)
print('####################')
else:
print("Couldn't pull or change branch!")
raise Exception(response.content)
All the os variables in the code as passed from Azure DevOps pipeline to the script and I have checked their values by printing and they are all correct.
I would like to know what the root cause problem is and how I can resolve it.
A small operation is there to implement. The major issue was with GIT integration. This can be resolved with the following steps.
Enable support for arbitrary files in Databricks Repos
This operation is needed to implement. Follow the link for the series of steps.

Instagram APINotAllowed Error

I'm learning APIs and was testing with Instagram's API.
Currently, I have an client in sandbox mode and an access token with public_content scope. I created another instagram account that is set to private profile. This new account is a sandbox user for the client.
This is my code.
import requests
import json
parameters = {'ACCESS_TOKEN':'4831128049.31d6072.13cfcadf494344cba7d7f47f18f8ba97'} #modified fake access for question sake
response = requests.get('https://api.instagram.com/v1/{i-put-the-user-id-here}/self/media/recent?access_token='+parameters['ACCESS_TOKEN'])
json_data = response.json()
print(response.status_code)
print(json_data)
But I keep getting this.
{
'meta':{
'code':400,
'error_type':'APINotAllowedError',
'error_message':'you cannot view this resource'
}
}
Edit 1: But this works if the user is the owner of the access token, that is it works perfectly for my own account but not for other private profiles that is also a sandbox account.
Am I doing something wrong?
If this is not possible, then how are there other 3rd party apps doing it? like Flume for Mac?
You cannot get private user via API even if you are following that user, this behavior changed last year with API policy. APINotAllowedError is expected response when trying to access a private user.

Insufficient permissions when trying to create a Quizlet set

I am trying to create a set on Quizlet.com, using its API found here: https://quizlet.com/api/2.0/docs/sets#add
Here is my code of a set I am trying to create:
import requests
quizkey = my_client_id
authcode = my_secret_code # I'm not sure if I need this or not
data = {"client_id":quizkey, "whitespace":1, "title":"my-api-set",
"lang_terms":"it", "lang_definitions":"en",
"terms":['uno','due'], "definitions":["one","two"]}
apiPrefix = "https://api.quizlet.com/2.0/sets"
r = requests.post(url=apiPrefix, params=data)
print r.text
The response is:
{
"http_code": 401,
"error": "invalid_scope",
"error_title": "Not Allowed",
"error_description": "You do not have sufficient permissions to perform the requested action."
}
I also tried "access_token":authcode instead of "client_id":quizkey, but this resulted in the error: "You do not have sufficient permissions to perform the requested action."
How can I fix this and not get a 401 error?
Alright so 3 and a half years later (!!) I've looked into this again and here's what I've discovered.
To add a set you need an access token - this is different to the client_id (what I call quizkey in my code), and to be quite honest I don't remember what authcode in my code is.
This token is obtained by going through the user authentication flow. To summarise it:
Send a POST request to https://quizlet.com/authorize like so:
https://quizlet.com/authorize?response_type=code&client_id=MY_CLIENT_ID&scope=read&state=RANDOM_STRING
Keep the response_type as code, replace client_id with your client_id, keep the scope as read, and state can be anything
I believe this requires human intervention because you're literally authorising your own account? Not sure of another way...
You'll receive a response back with a code
Let's call this RESPONSE_CODE for now
Send a POST request to https://api.quizlet.com/oauth/token, specifying 4 mandatory parameters:
grant_type="authorization_code" (this never changes)
code=RESPONSE_CODE
redirect_uri=https://yourredirecturi.com (this can be found at your personal API dashboard)
client ID and secret token separated by a colon and then base64-encoded (the user authentication flow link above tells you what this is if you don't want to do any of the encoding)
You'll receive the access_token from this API call
Now you can use that access_token in your call to create a set like I've done above (just replace "client_id":quizkey with "access_token":access_token)
You will need to authenticate in order to make sets. This link gives an overview:
https://quizlet.com/api/2.0/docs/making_api_calls
And this one provides details about the authentication process:
https://quizlet.com/api/2.0/docs/authorization_code_flow

Im using python (django framework) to gain a request token from google api, but the request token always comes back empty

Here is sample code that I'm working with.
def index(request):
flow = OAuth2WebServerFlow(
client_id='xyz.apps.googleusercontent.com',
client_secret='xyz',
scope='https://www.googleapis.com/auth/plus.me',
user_agent='sample/1.0')
callback = 'http://%s/oauth2callback' % request.META[ 'HTTP_HOST' ]
authorize_url = flow.step1_get_authorize_url(callback)
return HttpResponse(flow)
For some reason 'flow' is always set to " " or empty instead of a request token. I have searched for days on this issue.
Can anyone tell me why I can't get a request token from google using this method?
fyi: I know that I should be redirecting the user to the authorize url, but I want to see if flow is set before I do since Google will provide the authorize url even if a request token wasn't returned.
Before you can use OAuth 2.0, you must register your application using
the Google APIs Console. After you've registered, go to the API Access
tab and copy the "Client ID" and "Client secret" values, which you'll
need later.
http://code.google.com/p/google-api-python-client/wiki/OAuth2#Registering
If this answer actually helps with your problem then I must bid an R.I.P. to S.O.

Categories