Classroom API - Access classroom with Service Account - google-api

I need some help as I'm really stuck!!!! I have spent days and hours on this one but I can't figure it. I have searched all possible forums and other similar posts without any success.
So, the requirement :
We are trying to integrate Classroom API on our .Net platforms. The tricky part is that they want to use service accounts.
The problem :
Google.Apis.Requests.RequestError The caller does not have permission [403]
The caller does not have permission] Location[ - ] Reason[forbidden] Domain[global]
I have followed the documentation as shown in :
https://developers.google.com/identity/protocols/OAuth2ServiceAccount.
I understand that we need to set up a service account in the Google API Console, so I've done the following:
1) I have created a service account and enabled G Suite Domain-wide Delegation in the Google API Console
2) In the Admin Console, in Manage API Client Access, I have entered the service account's client id and have enabled scopes.
3) A have downloaded the json file with all the service account credentials (private key, email)
and the code...
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(cr.client_email)
{
Scopes = new[] { ClassroomService.Scope.ClassroomCourses },
}.FromPrivateKey(cr.private_key));
// Create the service.
service = new ClassroomService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Demo-School",
});
and the actual call to the Classroom API to create a course :
var resource = service.Courses.Create(course);
var result = await resource.ExecuteAsync();
So, despite all that, whenever i try to create a course, I get the above error.
When I try to create a Course using the Reference page (https://developers.google.com/classroom/reference/rest/v1/courses/create) it works fine. I can create courses, teachers, set permissions etc..
But when I try to do that programmatically.. i.e from a console app, there is NO way I can get it working.
Can ANYONE please advice???????What am I missing??

Related

Get Google Sheet Data using V4 API without OAuth

Using this nodeJS example, I could get the data from a public sheet.
But how do I get the data from a non-public sheet owned by me (my google a/c) ?
Is there some way to send in the username and password as arguments ?
I don't want OAuth way as I want the data to be pulled from the sheet & displayed on a public webpage.
The other option I can think of is to have OAuth2 done once write a script to handle refresh tokens automatically as a cron every hour ?
Since this is a file that you the developer own i would recommend using a service account
If you share the file with the service account it will then have permissions to access it without you needing to go though the oauth2 steps of authorizing your application.
On google cloud console simply create Service account credentials
const {google} = require('googleapis');
const auth = new google.auth.GoogleAuth({
keyFile: '/path/to/your-secret-key.json',
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
Then change your auth code slightly. Open the service account key file and look for the service account email address its the only one witha # in it. Share the file with the service account like you would any other user in google drive web app.
Once it has access you shouldn't need to authorize the app again.
I have a video on Google drive API upload file with Nodejs + service account which might help you a bit you just need the authorization code. Everything else you have should work as is.

.Net - Google Play Download Sales Reports

I have a .Net Core application attempting to download the latest sales reports from my Google Play account to view and track sales statistics. These reports are stored on a Google Cloud Storage bucket "owned"/managed by the Google Play Store.
I've been unable to find many other examples or related issues in .Net and have been experiencing a storage permission issue.
I'm following the rather limited guide here: https://support.google.com/googleplay/android-developer/answer/6135870#export under "Download reports using a client library and service account"
Step 1: Create a service account
I've created a new service account for a new project as shown below. I've also granted that service account permissions for all storage objects as shown.
Step 2: Add the service account on your play console
I've also invited this new service account user to my play console and granted it permissions to the app to view it's information and financial reports.
Step 3: Fetch reports using an API call
I've created and downloaded a .JSON key for the service user.
// Scope as specified in https://support.google.com/googleplay/android-developer/answer/6135870#export
string[] scopes = new string[] { "https://www.googleapis.com/auth/devstorage.read_only" };
// Import JSON credential
var credential = GoogleCredential.FromFile(Path.Combine(Environment.CurrentDirectory, "keys/googleplay.json")).CreateScoped(scopes);
// Bucket ID of Google Play Store - Found from reports page "Copy URL"
string bucketId = "pubsite_prod_5XXXXXXXXX2";
var storage = StorageClient.Create(credential);
var bucketObjects = storage.ListObjects(bucketId);
foreach (var bucketObject in bucketObjects)
{
Console.WriteLine(bucketObject.Name);
}
This results in a permission error:
Google.Apis.Requests.RequestError\r\nsupportapp-googleplay#ascendant-nova-300105.iam.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. [403]\r\nErrors [\r\n\tMessage[supportapp-googleplay#ascendant-nova-300105.iam.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket.
The service account I've created has the storage.objects.list permission and has been invited to my Play Console as well (as shown in the screenshots). This is a storage bucket hosted/owned by Google Play and not me. How can this permissions issue be resolved to allow my linked account to access Google Play's sales reports via API?
I came back in the morning and magically things now work.
I have also changed to using fromStream instead of fromFile when creating the Google credential.
Changed:
string[] scopes = new string[] { "https://www.googleapis.com/auth/devstorage.read_only" };
var credential = GoogleCredential.FromFile(Path.Combine(Environment.CurrentDirectory, "keys/googleplay.json")).CreateScoped(scopes);
To:
GoogleCredential credential;
using (var stream = new FileStream(Path.Combine(Environment.CurrentDirectory, "keys/googleplay.json"), FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream).CreateScoped(scopes);
}
However, both of these credential creation systems work just fine... Perhaps it took Google 12 hours to sync the permissions? Hopefully this example can help someone else out in the future.

Use a service account to get the list of users from Google domain

Hello all.
I have been assigned the task of fetching unanswered emails from the inbox of each member of our Google domain using Spring Boot, but I haven't been able to do it.
In first place, I need the list of users from the domain. This can be achieved via Directory API (which cannot be enabled by that name in the Google Developer console, by the way. Looks like it belongs to Admin SDK or so).
What I have faced so far is this:
There are many related questions on SO, but most of them are outdated.
Java Quickstart for Google Directory API does not include an example using service accounts, and I want to use them because my app runs in a docker container, and using Oauth means I need to manually authorize it every time I deploy a new version or restart the container.
Google documentation makes reference to "API Reference" settings in Admin console, but I don't see that section there.
I am not storing credentials in a JSON file, I have them in an environment variable instead. I am doing this:
var inputStream = IOUtils.toInputStream(apiCredentials, Charset.defaultCharset()); //apiCredentials is a string with the JSON contents.
var credential = GoogleCredential
.fromStream(inputStream, httpTransport, JacksonFactory.getDefaultInstance())
.createScoped(Collections.singleton(DirectoryScopes.ADMIN_DIRECTORY_USER));
var directoryService = new Directory.Builder(httpTransport, JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("My App")
.build();
var result = directoryService.users().list()
.setPageToken(pageToken)
.setDomain("my.domain")
.setMaxResults(10)
.execute();
After this, I get a 400 Bad request error, with no further description.
What am I doing wrong here?

Domain wide delegation authentication for google groups settings API?

We have a google corporate account and are already using the Groups Provisioning API to manage groups in our domain. We now need to tweak group settings via the "group settings api". Our java code acts as special admin user in our domain for all Group Provisioning API as it manages groups.
a) I am reading https://developers.google.com/google-apps/groups-settings/auth
b) "If your application has certain unusual authorization requirements, .... or domain-wide delegation of authority (2LO), then you cannot currently use OAuth 2.0 tokens. In such cases, you must instead use OAuth 1.0 tokens and an API key."
c) I read: https://developers.google.com/console/help/#generatingdevkeys and generated a new server key for this app
d) Now what? How do I use this with the v1-rev25-1.14.2-beta version of the google-api-services-groupssettings API and the "google-api-client" version 1.14.1-beta? The only options I see in any examples (which are only for oauth 2.0 mind you) are using this GoogleCredential object which is only centered around oauth 2.0, which according to (a) above, we can't use.
e) Given no examples or helpful info on using the API keys with this library, I decided to just try to wing it using an example for creating the Groupsettings object via oauth 2.0 and one of our special service accounts clientEmail and privatekey. In some respects I'm not sure why this would not work given that groups are not "user data" but seems like they should be able to be managed by this admin api account I am connecting with.
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId("ourspecial_client_email#from.api.console")
.setServiceAccountScopes(GroupssettingsScopes.APPS_GROUPS_SETTINGS)
.setServiceAccountUser("my.special.admin.user#ourdomain.com")
.setServiceAccountPrivateKeyFromP12File(
new java.io.File("/path/to/privatekey"))
.build();
Groupssettings service = new Groupssettings.Builder(httpTransport, jsonFactory, null)
.setApplicationName("my API Integration")
.setHttpRequestInitializer(credential).build();
Groups groups= service.groups().get("someexistingtestgroup#mydomain.com").execute();
Groups group = new Groups();
group.setWhoCanJoin("ALL_IN_DOMAIN_CAN_JOIN");
service.groups().patch("someexistingtestgroup#mydomain.com", group).execute();
When the code above executes, (the patch() call) I get back this error: (I also tried "update()" same result. What does this message mean?? Is this related to auth? or is this some invalid call in the update/patch?
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Permission denied: Cannot hide from Groups directory.",
"reason" : "invalid"
} ],
ALSO side note: Your "help" page states "Google engineers monitor and answer against the tag google-groups-settings." when posting here for help, but stackoverflow requires us to have XXXXX points in order to use it! Great, so nobody will see this.
I would not recommend using Two Legged OAuth 1.0 authentication as it's been deprecated by Google.
You can use OAuth 2.0 Service Accounts with the Groups Settings API. Just follow the instructions in the Drive SDK domain-wide guide substituting groups settings where necessary. However, there's very little reason to do this as your app will still need to know the address of a Google Apps Super Administrator for the Service Account to impersonate in order to have access to Groups Settings API.
The best method for accessing the Groups Settings API is probably standard OAuth 2.0 authentication. You can authorize as the user account which has Super Admin access but with a scope of only the Group Settings API. If you request offline access for your OAuth authentication, you'll be able to perform Group Settings API calls as long as your token isn't revoked and the user accounts exists as a super admin.
The specific error you are getting means that in the Control Panel Settings for the Google Apps domain, under Settings -> Groups for Business -> Sharing Options -> Group Visibility, "Group owners can hide groups from the groups directory" is not checked. This prevents any groups from being hidden.

Using Delegates with Exchange Web Services

Has anyone used delegates with exchnage web services? I would like one user to be able to control other users' calendars in Exchange. I'm finding this problem to be a little tricky, and I'd like to see how others have been able to get it to work properly.
I'm just getting started here, but i managed to get access to Resource calendars via a delegate account.
I used the recommendations from this article about delegate account and resource accounts. (Resource accounts are tricky because they are disabled in the AD, and you have to use a delegate account to get access to them)
After setting up the delegate account on the server, I set up the ExchangeServerBinding using the credentials of the delegate account:
ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Url = #"https://dc1.litwareinc.com/ews/exchange.asmx";
// Setup binding with username and password of the delegate account
binding.Credentials =
new NetworkCredential(delegateuserName, delegatepassword, "litwareinc.com");
(I'm using Microsofts prepared virtual server image for testing)
Then when accessing the mailbox, I set up a FindItemType request and use the smtp address of the account i want to access:
// Prepare request
var findItemRequest = new FindItemType();
// Setup the mailbox using the smtp address of the account wanted
var mailbox = new EmailAddressType {EmailAddress = mailboxId};
findItemRequest.ParentFolderIds =
new[] {new DistinguishedFolderIdType {Mailbox = mailbox}};
((DistinguishedFolderIdType) findItemRequest.ParentFolderIds[0]).Id =
DistinguishedFolderIdNameType.calendar;
findItemRequest.Traversal = ItemQueryTraversalType.Shallow;
// Add ItemResponseShapeType and Calendarview to request here ...
// The create a FindItemResponseType using the binding and the request
var response = binding.FindItem(findItemRequest);
So in short:
Setup an account with delegate access on the Exchange server, this can be done via owa or with a Exchange Shell script
Use the account with delegate access on the ExchangeServiceBinding object
Access target account using a FindItemType with the target account smtp-addres as EmailAddressType
Regards
Jesper Hauge

Resources