According to Google's OAUTH API documentation, the userinfo.profile and userinfo.email scopes have been deprecated in favor of using profile and email. There is a lot of information from other API users about this switch as well.
However, when trying to use the People API, I get this error:
2016-02-22 13:01:25,044 :Exception on /admin/testbank/settings [GET]
Traceback (most recent call last):
(Flask traceback omitted...)
File "/home/somedev/testweb/views/admin_view.py", line 78, in admin_view
res_acct_info = people_service.people().get(userId='me').execute()
File "/home/somedev/env/lib/python3.4/site-packages/googleapiclient/discovery.py", line 676, in method
raise TypeError('Got an unexpected keyword argument "%s"' % name)
TypeError: Got an unexpected keyword argument "userId"
Looking at the Google API sample, this should be the right call. However, dumping parameters.argmap within the library's discovery.py shows that userId does not exist. What am I doing wrong?
(Note: I'm trying to tag google-people, since this is where the API pages suggest, but I don't have enough rep to tag this. Could someone else add this tag for me?)
It turns out that I've been blind to the exact code I'm reading... all of these examples (particularly Google's example) have been using the Google+ API, which does indeed have the userId argument.
For reference, the "old way" is by using the oauth2/userinfo service:
service = build('oauth2', 'v2', http=http)
user = users_service.userinfo().get().execute()
name = user.get('name')
email = user.get('email')
You can use the Google+ API to get the same information - it will work, even if the user does not have a Google+:
service = discovery.build("plus", "v1", http=http)
user = service.people().get(userId='me').execute()
# This assumes that user['emailAddresses'] exists and
# has at least one element...
name = user.get('displayName')
email = user.get('emailAddresses')[0].get("value")
At the time of writing, it seems that the People API was released recently (February 10th, 2016)! It makes sense that there wouldn't be much documentation about it...
To use the newer People API (and maybe claim cleanliness from Google+), this is the correct way to fetch the current user's information:
service = discovery.build('people', 'v1', http_auth)
user = people_service.people().get(resourceName='people/me').execute()
# This assumes that user['names'] and user['emailAddresses']
# exists and has at least one element...
name = user.get('names')[0].get("displayName")
email = user.get('emailAddresses')[0].get("value")
resourceName replaces userId in the People API. It has a similar purpose (to identify the current user or another user), but has a different format, as seen with using 'people/me' versus just 'me'.
Both the Google+ and the newer People API simply require the email and profile scopes. However, unlike the former userinfo API, you need to manually enable the Google+ API and/or the People API in order to use them.
tl;dr: Google+ API uses userId='me', new People API uses resourceName='people/me', you should use one of these supported APIs - both return the same information, just in a slightly different format!
Related
I'm trying to write a function in a Function App that manipulates data in a CosmosDB. I get it working if I drop the read-write key in the environment variables. To make it more robust I wanted it to work as a managed identity app. The app has the role 'DocumentDB Account Contributor' on the Cosmos DB.
However, the CosmosClient constructor doesn't accept a Credential and needs the read-write key. I've been chasing down the rabbit hole of azure.mgmt.cosmosdb.operations where there is a DatabaseAccountsOperations class with a list_keys() method. I can't find a neat way to access that function though. If I try to create that object (which requires poaching the config, serializer and deserializer from my dbmgmt object) it still requires the resourceGroupName and accountName.
I can't help but think that I've taken a wrong turn somewhere because this has to be possible in a more straightforward manner. Especially given that the JavaScript SDK references a more logical class CosmosDBManagementClient in line with the SubscriptionClient. However, I can't find that class anywhere on the python side.
Any pointers?
from azure.identity import DefaultAzureCredential
from azure.cosmos import CosmosClient
from azure.mgmt.resource import SubscriptionClient
from azure.mgmt.cosmosdb import CosmosDB
from .cred_wrapper import CredentialWrapper
def main(req: func.HttpRequest) -> func.HttpResponse:
request_body = req.get_body()
# credential = DefaultAzureCredential()
# https://gist.github.com/lmazuel/cc683d82ea1d7b40208de7c9fc8de59d
credential = CredentialWrapper()
uri = os.environ.get('cosmos-db-uri')
# db = CosmosClient(url=uri, credential=credential) # Doesn't work, wants a credential that is a RW/R key
# Does work if I replace it with my primary / secondary key but the goal is to remove dependence on that.
subscription_client = SubscriptionClient(credential)
subscription = next(subscription_client.subscriptions.list())
dbmgmt = CosmosDB(credential, subscription.subscription_id) # This doesn't accept the DB URI??
operations = list(dbmgmt.operations.list()) # I see the list_keys() Operation there...
EDIT
A helpful soul provided a response here but removed it before I could even react or accept it as the answer. They pointed out that there is an equivalent python SDK and that from azure.mgmt.cosmosdb import CosmosDBManagementClient would do the trick.
From there, I was on my own as that resulted in
ImportError: cannot import name 'CosmosDBManagementClient' from 'azure.mgmt.cosmosdb'
I believe the root of the problem lies in an incompatibility of the package azure-mgmt. After removing azure-mgmt from my requirements.txt and only loading the cosmos and identiy related packages, the import error was resolved.
This solved 90% of the problem.
dbmgmt = CosmosDBManagementClient(credential, subscription.subscription_id, c_uri)
print(dbmgmt.database_accounts.list_keys())
TypeError: list_keys() missing 2 required positional arguments: 'resource_group_name' and 'account_name'
Does one really need to collect each of these parameters? Compared to the example that reads a secret from a Vault it seems so convoluted.
For other unfortunate ones looking to access CosmosDB with Managed Identity, it seems that this is, as of May 2021, not yet possible.
Source: Discussion on Github
Update 12/05/2021 - I came here finding a solution for this with Javascript/Typescript. So leaving the answer here for others. I think that a similar approach could work for Python.
You can use RBAC for data plane operations with Managed Identities. Finding the documentation was difficult.
RBAC for Cosmos DB data plane operations with Managed Identities
Important - If you get the error Request blocked by Auth mydb : Request is blocked because principal [xxxxxx-6fad-44e4-98bc-2d423a88b65f] does not have required RBAC permissions to perform action Microsoft.DocumentDB/databaseAccounts/readMetadata on resource [/]. Don't use the Portal to assign roles, use the Azure CLI for CosmosDB.
How to - creating a role assignment for a user/system MSI/user MSI is done using the Azure CosmosDB CLI
# Find the role ID:
resourceGroupName='<myResourceGroup>'
accountName='<myCosmosAccount>'
az cosmosdb sql role definition list --account-name $accountName --resource-group $resourceGroupName
# Assign to the MSI or user managed MSI:
readOnlyRoleDefinitionId = '<roleDefinitionId>' # as fetched above
principalId = '<aadPrincipalId>'
az cosmosdb sql role assignment create --account-name $accountName --resource-group $resourceGroupName --scope "/" --principal-id $principalId --role-definition-id $readOnlyRoleDefinitionId
Once this step is done, the code for connecting is very easy. Use the #azure/identity package's Default Credential. This works in Azure Function App with managed identity and on your laptop with VS code or with az login.
Docs for #azure/identity sdk
Examples of authentication with #azure/identity to get the credential object
import { CosmosClient } from "#azure/cosmos";
import { DefaultAzureCredential, ManagedIdentityCredential, ChainedTokenCredential } from "#azure/identity";
const defaultCredentials = new DefaultAzureCredential();
const managedCredentials = new ManagedIdentityCredential();
const aadCredentials = new ChainedTokenCredential(managedCredentials, defaultCredentials);
client = new CosmosClient({
endpoint: "https://mydb.documents.azure.com:443/",
aadCredentials
});
I am trying to read the list of open issues title which doesn't have label resolved. For that I am referring the API documentation (https://docs.gitlab.com/ee/api/issues.html) which mentions NOT but I couldn't able to get the NOT to work.
The following python script I have tried so far to read the list of issues now I am not able to find how to use NOT to filter the issue which doesn't have resolved label.
import gitlab
# private token or personal token authentication
gl = gitlab.Gitlab('https://example.com', private_token='XXXYYYZZZ')
# make an API request to create the gl.user object. This is mandatory if you
# use the username/password authentication.
gl.auth()
# list all the issues
issues = gl.issues.list(all=True,scope='all',state='opened',assignee_username='username')
for issue in issues:
print(issue.title)
From Gitlab issues api documentation, not is of type Hash. It's a special type documented here
For example to exclude the labels Category:DAST and devops::secure, and to exclude the milestone 13.11, you would use the following parameters:
not[labels]=Category:DAST,devops::secure
not[milestone]=13.11
api example: https://gitlab.com/api/v4/issues?scope=all&state=opened&assignee_username=derekferguson¬[labels]=Category:DAST,devops::secure¬[milestone]=13.11
Using gitlab python module, you would need to pass some extra parameters by adding more keyword arguments:
import gitlab
gl = gitlab.Gitlab('https://gitlab.com')
extra_params = {
'not[labels]': "Category:DAST,devops::secure",
"not[milestone]": "13.11"
}
issues = gl.issues.list(all=True, scope='all', state='opened',
assignee_username='derekferguson', **extra_params)
for issue in issues:
print(issue.title)
I am fetching a subscription's Secure Score using the Microsoft Azure Security Center (ASC) Management Client Library. All operations in the library state that
You should not instantiate directly this class, but create a Client instance that will create it for you and attach it as attribute.
Therefore, I am creating a SecurityCenter client with the following specification:
SecurityCenter(credentials, subscription_id, asc_location, base_url=None)
However, it seems to me like the only way to get the asc_location information properly is to use the SecurityCenter client to fetch it... The spec says the same as the quote above, You should not instantiate.... So I am stuck not being able to create the client because I need the ASC location to do so, and I need to create the client to get the ASC locations.
The documentation mentions
The location where ASC stores the data of the subscription. can be retrieved from Get locations
Googling and searching through the Python SDK docs for this "Get locations" gives me nothing (else than the REST API). Have I missed something? Are we supposed to hard-code the location like in this SO post or this GitHub issue from the SDK repository?
As offical API reference list locations indicates:
The location of the responsible ASC of the specific subscription (home
region). For each subscription there is only one responsible location.
It will not change, so you can hardcode this value if you already know the value of asc_location of your subscription.
But each subscription may have different asc_location values(my 2 Azure subscriptions have different asc_location value).
So if you have a lot of Azure subscriptions, you can just query asc_location by API (as far as I know, this is the only way I can find to do this)and then use SDK to get the Secure Score, try the code below:
from azure.mgmt.security import SecurityCenter
from azure.identity import ClientSecretCredential
import requests
from requests.api import head, request
TENANT_ID = ''
CLIENT = ''
KEY = ''
subscription_id= ''
getLocationsURL = "https://management.azure.com/subscriptions/"+subscription_id+"/providers/Microsoft.Security/locations?api-version=2015-06-01-preview"
credentials = ClientSecretCredential(
client_id = CLIENT,
client_secret = KEY,
tenant_id = TENANT_ID
)
#request for asc_location for a subscription
azure_access_token = credentials.get_token('https://management.azure.com/.default')
r = requests.get(getLocationsURL,headers={"Authorization":"Bearer " + azure_access_token.token}).json()
location = r['value'][0]['name']
print("location:" + location)
client = SecurityCenter(credentials, subscription_id, asc_location=location)
for score in client.secure_scores.list():
print(score)
Result:
I recently came across this problem.
Based on my observation, I can use whatever location under my subscription to initiate SecurityCenter client. Then later client.locations.list() gives me exactly one ASC location.
# Any of SubscriptionClient.subscriptions.list_locations will do
location = 'eastasia'
client = SecurityCenter(
credential, my_subscription_id,
asc_location=location
)
data = client.locations.list().next().as_dict()
pprint(f"Asc location: {data}")
In my case, the it's always westcentralus regardless my input was eastasia.
Note that you'll get exception if you use get instead of list
data = client.locations.get().as_dict()
pprint(f"Asc location: {data}")
# azure.core.exceptions.ResourceNotFoundError: (ResourceNotFound) Could not find location 'eastasia'
So what i did was a bit awkward,
create a SecurityCenter client using a location under my subscription
client.locations.list() to get ASC location
Use the retrieved ASC location to create SecurityCenter client again.
I ran into this recently too, and initially did something based on #stanley-gong's answer. But it felt a bit awkward, and I checked to see how the Azure CLI does it. I noticed that they hardcode a value for asc_location:
def _cf_security(cli_ctx, **_):
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.mgmt.security import SecurityCenter
return get_mgmt_service_client(cli_ctx, SecurityCenter, asc_location="centralus")
And the PR implementing that provides some more context:
we have a task to remove the asc_location from the initialization of the clients. currently we hide the asc_location usage from the user.
centralus is a just arbitrary value and is our most common region.
So... maybe the dance of double-initializing a client or pulling a subscription's home region isn't buying us anything?
I have been learning how to use Amazon S3 API by using the open source Eucalyptus. So far I have been able to successfully use REST, but now I would also like to use SOAP. I seem to be having trouble generating the correct signature for my request. The service is giving me a 403 Forbidden error:
Traceback (most recent call last):
File "soap.py", line 31, in <module>
r = w.download_file('mybucket', 'test.txt')
File "soap.py", line 27, in download_file
r = self.client.service.ListAllMyBuckets(self.access_key, timestamp, signature)
File "/usr/lib/python2.6/site-packages/suds/client.py", line 521, in __call__
return client.invoke(args, kwargs)
File "/usr/lib/python2.6/site-packages/suds/client.py", line 581, in invoke
result = self.send(soapenv)
File "/usr/lib/python2.6/site-packages/suds/client.py", line 619, in send
description=tostr(e), original_soapenv=original_soapenv)
File "/usr/lib/python2.6/site-packages/suds/client.py", line 677, in process_reply
raise Exception((status, description))
Exception: (403, u'Forbidden')
My code is in Python 2 and uses the SUDS-Jurko library for sending SOAP requests:
from suds.client import Client
class WalrusSoap:
wsdl_url = 'https://s3.amazonaws.com/doc/2006-03-01/AmazonS3.wsdl'
server_url = 'https://localhost:8773/services/Walrus'
def __init__(self, access_key, secret_key):
self.access_key = access_key
self.secret_key = secret_key
self.client = Client(self.wsdl_url)
self.client.wsdl.services[0].setlocation(self.server_url)
#print self.client
def create_signature(self, operation, timestamp):
import base64, hmac, hashlib
h = hashlib.sha1(self.secret_key)
h.update("AmazonS3" + operation + timestamp)
#h = hmac.new(key=self.secret_key, msg="AmazonS3" + operation + timestamp, digestmod=hashlib.sha1)
return base64.encodestring(h.digest()).strip()
def download_file(self, bucket, filename):
from time import gmtime, strftime
timestamp = strftime('%Y-%m-%dT%H:%M:%S.001Z', gmtime())
print(timestamp)
signature = self.create_signature('ListAllMyBuckets', timestamp)
print(signature)
r = self.client.service.ListAllMyBuckets(self.access_key, timestamp, signature)
return r
w = WalrusSoap(access_key='MOBSE7FNS6OC5NYC75PG8', secret_key='yxYZmSLCg5Xw6rQVgoIuVLMAx3hZRlxDc0VOJqox')
r = w.download_file('mybucket', 'test.txt')
print(r)
I changed the server endpoint, because otherwise the WSDL points to the regular S3 servers at Amazon. I also have two different ways of creating the signature in my create_signature function. I was swapping between one and the other by simply commenting out the second one. Neither of the two seem to work. My question is what am I doing wrong?
SOAP Authentication: http://docs.aws.amazon.com/AmazonS3/latest/dev/SOAPAuthentication.html
SUDS-Jurko Documentation: https://bitbucket.org/jurko/suds/overview
Edit: I realized I forgot to include an example of what timestamp and signature is printed for debugging purposes.
2014-12-05T00:27:41.001Z
0h8vxE2+k10tetXZQJxXNnNUjjw=
Edit 2: Also, I know that the download_file function does not download a file :) I am still in testing/debug phase
Edit 3: I am aware that REST is better to use, at least according to Amazon. (Personally I think REST is better also.) I am also already aware that SOAP is deprecated by Amazon. However I would like to go down this path anyways, so please do me a favor and do not waste my time with links to the deprecation. I assure you that while writing this SOAP code, I was already well aware of the deprecation. In fact one of the links I posted has the deprecation notice printed at the top of its page. However, if you have evidence showing that Walrus completely ditches SOAP or that they stopped working on the SOAP portion, I would like to see something like that. But please do not tell me Amazon has deprecated SOAP.
The S3 SOAP API does not support "new" features so the REST API should be used where possible:
http://docs.aws.amazon.com/AmazonS3/latest/dev/SOAPAPI3.html
https://forums.aws.amazon.com/message.jspa?messageID=77821
IIRC recent versions of Eucalyptus do not support SOAP with S3.
That said, the signature looks good to me so I would check if the client/service hosts have the correct time, if there is a difference of more than 15 minutes authentication would fail.
You could also check the cloud-error.log on the Walrus service host as there may be more details on the failure there.
Eucalyptus does not support SOAP for S3 as of Eucalyptus version 4.0.0.
If you are using an older version of Eucalyptus (pre 4.0), then SOAP should work. Note, however, that the wsdl provided by S3 is not necessarily current or accurate for even their own service. S3 is notorious for changing the API without version or wsdl bumps, particularly since they stopped updating the SOAP API. So, there are likely responses from Walrus that do not conform to the published WSDL because our XML was updated based on the responses we see from S3 (via REST) and the SOAP and REST responses diverged. The signatures should be compatible, however, so worst case you would see 500 or 400 errors, but not 403 responses.
My recommendation is if you really want to learn the S3 SOAP API, you'll have to use S3 proper. The S3 SOAP support in Eucalyptus is there pre-4.0 but may not be compliant with the current state of S3 SOAP--we stopped testing against the S3 SOAP API directly when the AWS SDKs stopped using SOAP in favor of better REST support since that was the API moving forward.
Eucalyptus does support SOAP (see ZachH's comment about deprecation):
https://www.eucalyptus.com/docs/eucalyptus/4.0.2/schemas/aws-apis/s3/index.html
I decided to scrap using python, and I produced some working code with C# instead. Turns out C# has better SOAP libraries.
I have a question regarding contact creation using the python Google data API.
I am trying the example for contact creation with python, exactly like it is in the documentation page (https://developers.google.com/google-apps/contacts/v3/#creating_contacts)
So, i created the client as following:
email='<my gmail uid>'
password='<my gmail pwd>'
gd_client = gdata.contacts.client.ContactsClient(source='GoogleInc-ContactsPythonSample-1')
try:
gd_client.ClientLogin(email, password, gd_client.source)
except gdata.client.BadAuthentication:
print 'Invalid user credentials given.'
gd_client = None
Then i executed the function using:
create_contact(gd_client)
What i get from this call is:
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
File "<ipython console>", line 23, in create_contact
AttributeError: 'module' object has no attribute 'PostCode'
So I want to ask whether i am doing something wrong, whether this is a known bug, or whether the documentation is simply outdated.
Thanks.
p.s. a small comment, i think a better wrapping of the Google data API in the python library could be useful. I spent significant time in finding, within the API implementation, what fields should be set (directly!) and what classes should be used to assign them.
Ok, it seems that i can answer my question, which is partly a duplicate of
how to create google contact?
It turns out that the sample code in the documentation is invalid (thanks, Google)
PostCode should be Postcode, and the instant messaging address is also incorrect.
Removing that, it completes successfully