"Insufficient permissions" on google calendar api's acl.list - google-api

I'm getting Insufficient permissions when trying to call the acl.list method of the google calendar api via python.
service.acl().list(calendarId='primary').execute();
*** HttpError: <HttpError 403 when requesting https://www.googleapis.com/calendar/v3/calendars/primary/acl?alt=json returned "Insufficient Permission">
I'm using the scope 'https://www.googleapis.com/auth/calendar' as recommended in the documentation. Additionally, other API methods do work, for example service.calendarList
service.calendarList().list(pageToken=page_token).execute()
What am I missing?
Here is the code I'm using based almost entirely on the sample they provide:
import sys
from oauth2client import client
from googleapiclient import sample_tools
def main(argv):
# Authenticate and construct service.
# import pdb;pdb.set_trace()
service, flags = sample_tools.init(
argv, 'calendar', 'v3', __doc__, __file__,
# scope='https://www.googleapis.com/auth/calendar.readonly')
scope='https://www.googleapis.com/auth/calendar')
try:
page_token = None
while True:
calendar_list = service.calendarList().list(pageToken=page_token).execute()
for calendar_list_entry in calendar_list['items']:
print calendar_list_entry['summary']
page_token = calendar_list.get('nextPageToken')
service.acl().list(calendarId='primary').execute();
if not page_token:
break
except client.AccessTokenRefreshError:
print ('The credentials have been revoked or expired, please re-run'
'the application to re-authorize.')
if __name__ == '__main__':
main(sys.argv)

You might have to delete existing credentials, in the form of .json files. I had a similar "Insufficient permissions" problem, and I had to delete stored credentials. I had the additional problem that because of trying out some of Google's scripts in their tutorials, unknowingly I had credentials stored in a hidden .credentials folder in my home directory (users/home). Since they were hidden, I had to look for them through Terminal (on Mac), and delete them there. Once deleted, the problem was solved, since I could create new and proper credentials, suitable for the scope of my new script.

Something is wrong with your authentication. Insufficent permissions means that you don't have access.
I can verify that the scope https://www.googleapis.com/auth/calendar is enough to display ACL.list on the primary calendar.

You have to find the location of "calendar-dotnet-quickstart.json" file and delete it. I used .NET example and I have to debug the following code the find exact location.
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/calendar-dotnet-quickstart.json");
Then change scope as bellow and rebuild the solution.
string[] scopes = { CalendarService.Scope.Calendar};
You will notice that google will ask to confirm the access again.

Related

Delete "Other Contact" using Python with Google People API

I used Google People API v1.otherContacts.copyOtherContactToMyContactsGroup (reference) to copy a contact from "Other Contacts" to "myContacts" contact group. I now want to delete the original contact from "Other Contacts" using the same API.
REST Resource v1.otherContacts (reference) does not list a DELETE action.
I tried using v1.people.deleteContact (reference) passing the resource name of my "Other Contact":
import pickle
from googleapiclient.discovery import build
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
people_api = build('people', 'v1', credentials=creds)
people_service = people_api.people()
response = people_service.deleteContact(resourceName='otherContacts/c1971897568350947161').execute()
But I got an error saying:
TypeError: Parameter "resourceName" value "otherContacts/c1971897568350947161" does not match the pattern "^people/[^/]+$"
Looks like v1.people.deleteContact does not work for deleting a contact in "Other Contacts".
How can I programmatically delete a contact from "Other Contacts"?
EDIT: Based on #DaImTo's suggestion below, I tried replacing otherContacts/ in the resource name with people/ and invoking the v1.people.deleteContact API, but I got an error saying:
googleapiclient.errors.HttpError: <HttpError 404 when requesting https://people.googleapis.com/v1/people/c1971897568350947161:deleteContact?alt=json returned " generic::NOT_FOUND: Contact person resources are not found.". Details: "[{'#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'resourceNames[0]', 'description': 'Resource not found.'}]}]">
I advice consulting the documentation for people.deleteContact
Required. The resource name of the contact to delete.
DELETE https://people.googleapis.com/v1/{resourceName=people/*}:deleteContact
That means it should be people/c1971897568350947161 assuming that is the id of the user you want to delete c1971897568350947161.
Looks like Other Contacts are read only, according to this announcement from Google: https://developers.google.com/contacts/v3/announcement
The new People API has the same functionality as the legacy Contacts
API for all features, with the following exceptions for “Other
Contacts”:
Administrators have read-only permissions for “Other Contacts” through
the new scope. As sending mutate/write signals back to “Other
Contacts” is not supported, your users will have to add the Other
Contact as a My Contact if they wish to update its data fields.

Google gapi.auth2.getAuthInstance().isSignedIn().get() is always false when multiple google accounts

I am having a weird problem with google gapi auth. For some reason, the value for gapi.auth2.getAuthInstance().isSignedIn().get() is always returning false. This is my setup:
gapi.load("auth2", initAuth2);
initAuth2(){
gapi.auth2.init({
client_id: "xxxxx-yyyyy.apps.googleusercontent.com",
hosted_domain: "domain.com",
redirect_uri: "http://localhost:4200",
ux_mode: "redirect",
}).then(performAuth, error=>{
console.error(`Error initiating gapi auth2: ${error.details}`);
});
}
performAuth(googleAuth){
const isSignedIn = googleAuth.isSignedIn.get();
if(!isSignedIn){
googleAuth.signIn();
return;
}
const user = googleAuth.currentUser.get();
console.log(user);
}
I have two google workspace accounts sign in the same chrome profile. When I run this script, I get the prompt to select an account. No matter which one I choose, the flow just keeps looping. The reason for that is that the line const isSignedIn = googleAuth.isSignedIn.get(); is always returning false.
Things I've tried so far:
I thought that maybe the client_id was corrupted so I generated a new one. Same behaviour.
I though the GCP project was corrupted, so I created a new project with new credentials. Same behaviour.
Thought there was an issue with cookies, so I deleted and clear cookies and history. Same behaviour.
Thought is was related only to localhost so I deployed to the web. Same behaviour.
If I change the init options from ux_mode: "redirect" to ux_mode: "prompt". It works. However, that is not the desired experience. Also, if I only have one google workspace in the chrome profile, it works. Even more interesting... if I use a client id from an older project... it works! The problem is that the consent screen shows the wrong app name.
I know this question is similar to this one, however I feel it's different because none of the above troubleshooting works. Any insights?
There is a case for the exact same problem here, it is most likely a bug in the api set

Issue with authenticating to google api or issue with app

So, just a heads up, this is my first time working with google's apis and developer console, so forgive me if I missed something obvious that a more seasoned google developer would deem common sense. That being said. I am trying to create an installed application which will upload a video to youtube under my account. I am writing the application in powershell, so I am importing the appropriate google .Net libraries when the script is launched. From there, I essentially used the sample located here and just converted the content to powershell:
Add-Type -AssemblyName mscorlib
Add-Type -AssemblyName System.Net.Http
Add-Type -AssemblyName System
Add-Type -AssemblyName System.Core
Add-Type -AssemblyName System.Numerics
Add-Type -AssemblyName System.Xml
Add-Type -AssemblyName System.Xml.Linq
Add-Type -AssemblyName System.Data
Add-Type -AssemblyName System.Runtime.Serialization
#the below command imports the following assemblies: Google.Apis.Auth.dll, Google.Apis.Auth.PlatformServices.dll, Google.Apis.Core.dll, Google.Apis.dll, Google.Apis.PlatformServices.dll, Google.Apis.YouTube.v3.dll
Get-ChildItem 'C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\*.dll' | % {[reflection.assembly]::LoadFrom($_.FullName)}
$vid = "C:\Users\whiggs\Documents\gery2.mp4"
#$file = [System.IO.File]::OpenRead("C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_id.json")
$filemode = [System.IO.FileMode]::Open
$fileaccess = [System.IO.FileAccess]::Read
$stream = New-object System.IO.FileStream -ArgumentList "C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_secret.json", $filemode, $fileaccess
$googlebroker = New-object Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker
$thing = [Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream)
[string[]]$scope = [Google.Apis.YouTube.v3.YouTubeService+ScopeConstants]::YoutubeUpload
#$scope = [Google.Apis.YouTube.v3.YouTubeService+Scope]::YoutubeUpload
$cancellation = [System.Threading.CancellationToken]::None
$googlebroker = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync($thing.Secrets, $scope, "<google_username>", $cancellation)
$googlebroker.Wait()
[Google.Apis.Auth.OAuth2.UserCredential]$cred = $googlebroker.Result
$baseclient = new-object Google.Apis.Services.BaseClientService+Initializer
$baseclient.HttpClientInitializer = $cred
$baseclient.ApplicationName = "Contacts Tool"
$service = New-Object Google.Apis.YouTube.v3.YouTubeService($baseclient)
$video = New-Object Google.Apis.YouTube.v3.Data.Video
$video.Snippet = New-Object Google.Apis.YouTube.v3.Data.VideoSnippet
$video.Snippet.Title = "test"
$video.Snippet.Description = "none"
$video.Status = New-Object Google.Apis.YouTube.v3.Data.VideoStatus
$video.Status.PrivacyStatus = "public"
$vidstream = New-Object System.IO.FileStream -ArgumentList $vid, $filemode
$request = $service.Videos.Insert($video, "public", $vidstream, "video/*")
$task = $request.UploadAsync()
$task.Wait()
$vidstream.close()
$vidstream.Dispose()
Don't really need to include the code, because I know it is written correctly as no exception is generated. When I run the above code, it runs to completion without generating an exception, but if I take a look at the object stored in $task (type System.Threading.Tasks.Task), while the overall object reports that it ran to completion, digging deeper into the object's "Result" property reveals the task actually failed, and digging even further into the "exception" property provides the below error message:
The service youtube has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry. [403]
Errors [
Message[Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.] Location[ - ] Reason[accessNotConfigured] Domain[usageLimits]
]
at Google.Apis.Upload.ResumableUpload`1.<InitiateSessionAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<UploadAsync>d__70.MoveNext()
So, it is clear that there is some kind of issue with the app as it is configured or the way in which I am authenticating to it. However, I know that the app is at least receiving the requests, as you can see here. So, after doing so research, I have a couple of educated guesses as to what the problem might be, and need some input as to a) which of these (if any) is the actual problem and b) what needs to be done to correct it. My first educated guess involves the parameters I passed to the "AuthorizeAsync" method of the Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker class. According to this document:
"In this sample code a new UserCredential instance is created by calling the GoogleWebAuthorizationBroker.AuthorizeAsync method. This static method gets the client secret (or a stream to the client secret), the required scopes, the user identifier, the cancellation token for cancelling an operation, and an optional data store. If the data store is not specified, the default is a FileDataStore with a default Google.Apis.Auth folder. The folder is created in Environment.SpecialFolder.ApplicationData."
The part I want to focus on in the above statement is "the user identifier", as that is all the information that provided as to this parameter's description.
The value that I put in was a string containing the user name for the google account that the app is registered under and the account that would upload the youtube videos, but I don't know if that was the value that was needed, as I had to login to the account anyway via a web browser as part of this process. If this is indeed the problem, what is "the user identifier" as far as this parameter is concerned. A little more detail in the documentation can go a long way. My second educated guess as to what is causing this has to do with the configuration of the application, but more specifically, the generated oauth credentials. The scopes that the app would need access to are apparently considered sensitive, and, if I am understanding this correctly, I have to authenticate from a verified domain and configure a bunch of advanced settings that, as someone writing this project for myself and not a company, I just do not have access to. I just want to upload a youtube video to my account, so why do I need to authenticate from a verified domain? What can I do to get around this? Any info would be great.
Correcting error in your understanding
"<google_username>" is used by filedatastore to store the credeitals for the user once they have consented access to your client. If you want to understand more about this then you should try and read my tutorial on file datastore
$googlebroker = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync($thing.Secrets, $scope, "<google_username>", $cancellation)
Answer for your issue
YouTube Data API has not been used in project before or it is disabled.
Means that you have not enabled the YouTube api in your project on google Developer console or you have not applied and been granted any quota to this api yet. In Google developer console go to API Library -> YouTube Data API v3 and enable it. Once you have done that click manage then go to Quota. If you have not previously enabled it which i suspect you hadn't then now you will have 0 quota.
click the penile icon and apply for a quota for this api. It can take a while to get a response.
That error message simply means that you have not enabled the API in the Cloud Console. Can you post the error message you get, after you enabled the API? Also, make sure you're running the script with the credentials for the right project. You can run glcoud config list to see which project you're using.

Using script to fire Xcode bot

Is there a way to manually fire existing Xcode bots using shell scripts? I have a manual bot and I'd like to fire it based on certain custom logic criteria.
Yes.
You'll need to do a couple of things:
Firstly, I'm going to call your Xcode Server's IP address XCS_IP, usually localhost if you're on the machine where Xcode Server's running.
Find out the ID of the bot: in Terminal, run curl -k "https://XCS_IP:20343/api/bots". Copy the output to some editor and find the value for key _id for your bot, will be something like 6b3de48352a8126ce7e08ecf85093613. Let's call it BOT_ID.
Trigger an integration by running curl -k -X POST -u "username:password" "https://XCS_IP:20343/api/bots/BOT_ID/integrations" -i
Where username and password are credentials of a user that is allowed to create bots on the server, an admin will do.
If you're interested in more details, I have an app in Swift that uses that API and many more: https://github.com/czechboy0/Buildasaur/blob/master/BuildaCIServer/XcodeServer.swift#L324
And checkout my article on how to find Xcode Server's API "documentation": http://honzadvorsky.com/blog/2015/5/4/under-the-hood-of-xcode-server.
TL;DR? On your Mac, look at /Applications/Xcode.app/Contents/Developer/usr/share/xcs/xcsd/routes/routes.js, where you can find the available APIs.
Hope this helped.
Apple has added documentation for the Xcode server API that you can use to trigger bots.
https://developer.apple.com/library/tvos/documentation/Xcode/Conceptual/XcodeServerAPIReference/index.html#//apple_ref/doc/uid/TP40016472-CH1-SW1
Below is some example code on how you can make a python script that triggers a bot.
import requests
xcodeIP = '1.2.3.4.5'
def main():
botName = "name of bot"
runBot(botName)
def runBot(botName):
requests.post(xcodeIP + '/api/bots/' + getBot(botName)["_id"] + '/integrations', auth=('username', 'password'), verify=False)
def getBot(botName):
botIDRequest = requests.get(xcodeIP + '/api/bots', auth=('username', 'password'), verify=False)
bots = botIDRequest.json()["results"]
for bot in bots:
if bot["name"] == botName:
return bot
if __name__ == "__main__":
main()

Force google to show account chooser and consent screen

I am able to use both prompt=consent and prompt=select_account individually, but Google doesn't seem to allow me to combine them. I tried the prompt=consent+select_account as suggested in an answer of Force google account chooser, but that fails with the error: "Invalid prompt: consent+select_account".
The doc (https://developers.google.com/accounts/docs/OAuth2Login) says "A space-delimited list", so I tried consent select_account but that fails with: "The requested URL was not found on this server."
I also tried combining prompt=select_account and approval_prompt=force, but Google doesn't like that either.
Anyone else have luck with combining consent screen and account chooser?
Update:
This is my JavaScript method creating URL for getting contacts from gmail
$scope.importGmailContacts = function() {
provider = 'gmail';
$scope.importing_from_gmail = true;
window.open(protocol + "://" + host + ":" + port + "/contacts/gmail", "_blank",
"toolbar=yes, scrollbars=yes, resizable=yes, top=0, left=0, width=600, height=600, prompt='select_account+consent', approval_prompt=force");
}
I have tried setting prompt and approval_prompt both collectively and individually but it does not seems to work. Refer to this question.
You need add: access_type=online&prompt=select_account+consent:
private static final String AUTHORIZE_URL
= "https://accounts.google.com/o/oauth2/auth?"
+ "response_type=code&access_type=online&prompt=select_account+consent"
+ "&client_id=xxx&redirect_uri=xxx";
private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=xxx";
..
I just tried this and it DID work when space-delimited:
options[:prompt] = 'select_account consent'
https://accounts.google.com/o/oauth2/auth?client_id=XXXX&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/analytics.readonly&response_type=code&prompt=select_account+consent&approval_prompt=force
Results in the error
That’s an error.
Error: invalid_request
Conflict params: approval_prompt and prompt
Prompt and approval_prompt parameters can not be used together.
prompt Optional. A space-delimited, case-sensitive list of prompts to present the user. If you don't specify this parameter, the user will be prompted only the first time your app requests access. Possible values are:
none Do not display any authentication or consent screens. Must not be specified with other values.
consent Prompt the user for consent.
select_account Prompt the user to select an account.
If memory serves approval_prompt is the older way of doing it and google added Prompt sometime in 2012. I cant actually find any documentation on approval_prompt anymore but if memory serves it was the same as doing prompt=consent it just requested access again.

Resources