I am trying to fetch the files of a suspended user, using his/her email id in google apps script, I have created a service account and implemented an app script API with service account authentication for which I used the oauth2 api even though I am trying to to "impersonate" users by making api calls, I am only able to access only the public files of the user, I am trying to all the files(including private and public) files, may I know what I am doing wrong?
/**
* Entrypoint
*/
var PRIVATE_KEY = 'PPRIVATE_KEY goes in here';
var CLIENT_EMAIL = 'CLIENT_EMAIL goes in here';
function test() {
fetchUser();
}
/**
* Fetches the suspended user details from the AdminDirectory.
*/
function fetchUser() {
// Set the constant options only once.
const options = {
domain: 'xyz.com',
orderBy: 'email',
query: 'isSuspended=true',
maxResults: 500,
fields: "nextPageToken,users"
};
// Could log the options here to ensure they are valid and in the right format.
const results = [];
do {
var search = AdminDirectory.Users.list(options);
// Update the page token in case we have more than 1 page of results.
options.pageToken = search.nextPageToken;
// Append this page of results to our collected results.
if(search.users && search.users.length)
Array.prototype.push.apply(results, search.users);
} while (options.pageToken);
//Logger.log(results);
for(var k = 0; k < results.length; k++){
var fullEmail = results[k].primaryEmail;
//Logger.log(fullEmail);
fetchAllFilesOwnedByEmail(fullEmail);
}
}
/**
* Fetch all files based on the user email id
*/
function fetchAllFilesOwnedByEmail(email) {
var service = getService(email);
if (service.hasAccess()) {
var url = 'https://www.googleapis.com/drive/v2/files?pageSize=1';
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
console.log("gs test1");
Logger.log(JSON.stringify(result, null, 2));
} else {
console.log("gs test1");
Logger.log(service.getLastError());
}
const searchParams = {
corpora: 'domain',
orderBy: 'createdDate',
q: "'" + email + "' in owners",
fields: 'nextPageToken,items(id,title,mimeType,userPermission)'
};
Logger.log('searchParams:' +searchParams);
const results = [];
do {
var search = Drive.Files.list(searchParams);
if (search.items)
Array.prototype.push.apply(results, search.items);
searchParams.pageToken = search.nextPageToken;
} while (searchParams.pageToken);
//return results;
Logger.log('Files:' +results);
}
/**
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
var service = getService();
service.reset();
}
/**
* Configures the service.
*/
function getService(userEmail) {
return OAuth2.createService('GoogleDrive:' + userEmail)
// Set the endpoint URL.
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey('PRIVATE_KEY goes in here')
.setIssuer('CLIENT_EMAIL goes in here')
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject(userEmail)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/drive');
}
Related
I need to build a community connector. So, I have a get AuthType() function where I specify that the users have to login with their username and password.
I have all the other necessary functions that I have find here : https://developers.google.com/looker-studio/connector/auth#user_pass
When i try to use my connector, it ask me some authorizations relative to google account, but it never show me the login form.
Here is my code :
var cc = DataStudioApp.createCommunityConnector();
function getAuthType() {
resetAuth(); // I tried to call this function to reset credentials; It's define later
return cc.newAuthTypeResponse()
.setAuthType(cc.AuthType.USER_PASS)
.setHelpUrl('https://api.smartxsp.test/auth-help')
.build();
}
/**
* Sets the credentials.
*/
function setCredentials(request) {
var creds = request.userPass;
var username = creds.username;
var password = creds.password;
var validCreds = checkForValidCreds(username, password);
if (!validCreds) {
return {
errorCode: 'INVALID_CREDENTIALS'
};
}
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('dscc.username', username);
userProperties.setProperty('dscc.password', password);
return {
errorCode: 'NONE'
};
}
/**
Call to website to check if credentials are valid
*/
function checkForValidCreds(username, password) {
// This url calls a php function. If credentials are good, it return true, else it return false
var url = 'https://*****?username='+username+'&password='+password;
if(url){
return true;
} else {
return false;
}
return false;
}
function isAuthValid() {
var userProperties = PropertiesService.getUserProperties();
var userName = userProperties.getProperty('dscc.username');
var password = userProperties.getProperty('dscc.password');
return checkForValidCreds(userName, password);
}
function resetAuth() {
var userProperties = PropertiesService.getUserProperties();
userProperties.deleteProperty('dscc.username');
userProperties.deleteProperty('dscc.password');
}
I used this codelab for base and adapted it for my project. https://codelabs.developers.google.com/codelabs/community-connectors#4.
I looked the documentation of google apps script:
https://developers.google.com/looker-studio/connector/auth#user_pass,
https://developers.google.com/looker-studio/connector/reference#required_functions
Finally, I want to be able to give credentials to the applications with a form, in order to get datas linked to the user's profile
Running a Google Sheet w/ App Script to Post Tweets to Twitter. Twitter imports an image from the URL within the tweet, but would like to upload an image from a separate URL instead so that the image is not cropped into a card in the tweet, or so that we can choose a different image.
Using this code (esentially taken from the answer here - How can I tweet from Google App Script with OAuth2 library? ) :
ar CONSUMER_KEY = 'your consumer key';
var CONSUMER_SECRET = 'your consumer secret';
var TOKEN = 'your access token';
var TOKEN_SECRET = 'your access token secret';
/**
* Authorizes and makes a request to the Twitter API.
*/
function run() {
var service = getService();
var tweet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname").getRange("A1").getValue().toString();
var imageUrl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname").getRange("A2").getValue().toString();
Logger.log(service.getCallbackUrl())
if (service.hasAccess()) {
var url = 'https://api.twitter.com/1.1/statuses/update.json';
var payload = {
status: tweet,
image: imageUrl
};
var response = service.fetch(url, {
method: 'post',
payload: payload
});
var result = JSON.parse(response.getContentText());
Logger.log(JSON.stringify(result, null, 2));
} else {
var authorizationUrl = service.authorize();
Logger.log('Open the following URL and re-run the script: %s',
authorizationUrl);
}
}
function doGet() {
return HtmlService.createHtmlOutput(ScriptApp.getService().getUrl());
}
/**
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
var service = getService();
service.reset();
}
/**
* Configures the service.
*/
function getService() {
return OAuth1.createService('Twitter')
// Set the endpoint URLs.
.setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
.setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
.setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
// Set the consumer key and secret.
.setConsumerKey(CONSUMER_KEY)
.setConsumerSecret(CONSUMER_SECRET)
// Set your user's access token key and secret
.setAccessToken(TOKEN, TOKEN_SECRET)
.setCallbackFunction('authCallback')
}
/**
* Handles the OAuth callback.
*/
function authCallback(request) {
var service = getService();
var authorized = service.handleCallback(request);
if (authorized) {
return HtmlService.createHtmlOutput('Success!');
} else {
return HtmlService.createHtmlOutput('Denied');
}
}
Works great for the tweet, but not for uploading an image. No error message. Other solutions on the net seem obsolete / use a deprecated library (https://medium.com/javascript-in-plain-english/i-made-a-twitter-bot-from-nothing-but-a-google-sheet-ef0ba6e1b194 and https://www.labnol.org/code/19702-twitter-image-upload ) and couldn't adapt what was available as a solution.
I understand that the following probably has to happen:
Get the Image as a Blob
Upload the image first to Twitter ( https://developer.twitter.com/en/docs/tutorials/uploading-media )
Get the Returned Media ID and then attach it when uploading the Tweet.
But so far I've not been able to get any results. Any help is much appreciated.
Screenshot of smartsheet and google setup screens
When attempting to get data out of Smartsheet, I'm encountering an error that says the redirect URI is missing or invalid when I follow the link that was logged by my apps script project.
I've generated a client ID and client secret on both google and smartsheet but I don't know what to do next.
Google Credentials:
I'm not sure what to put in the redirect Url section or the authorized Javascript origins at the link below.
https://console.developers.google.com/apis/credentials/oauthclient/########################2d.apps.googleusercontent.com?project=project-id-##############
Smartsheet Credentials:
I have activated my Smartsheet Developer profile and generated a client ID and client secret for my app that I've called 'Google Sheets'
Shown below is the code that I have right now which I found on gitHub.
var CLIENT_ID = '...'; // what do I put here?
var CLIENT_SECRET = '...'; // what do I put here?
/**
* Authorizes and makes a request to the Smartsheet API.
*/
function run()
{
var service = getService();
if (service.hasAccess())
{
var url = 'https://api.smartsheet.com/2.0/users/me';
var response = UrlFetchApp.fetch(url,
{
headers:
{
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
Logger.log(JSON.stringify(result, null, 2));
}
else
{
var authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s', authorizationUrl);
}
}
/**
* Reset the authorization state, so that it can be re-tested.
*/
function reset()
{
getService().reset();
}
/**
* Configures the service.
*/
function getService()
{
return OAuth2.createService('Smartsheet')
// Set the endpoint URLs.
.setAuthorizationBaseUrl('https://app.smartsheet.com/b/authorize')
.setTokenUrl('https://api.smartsheet.com/2.0/token')
// Set the client ID and secret.
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
// Set the name of the callback function that should be invoked to
// complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Scopes to request
.setScope('READ_SHEETS')
// Set the handler for adding Smartsheet's required SHA hash parameter to
// the payload:
.setTokenPayloadHandler(smartsheetTokenHandler);
}
/**
* Handles the OAuth callback.
*/
function authCallback(request)
{
var service = getService();
var authorized = service.handleCallback(request);
if (authorized)
{
return HtmlService.createHtmlOutput('Success!');
}
else
{
return HtmlService.createHtmlOutput('Denied.');
}
}
/**
* Adds the Smartsheet API's required SHA256 hash parameter to the access token
* request payload.
*/
function smartsheetTokenHandler(payload)
{
var codeOrRefreshToken = payload.code ? payload.code : payload.refresh_token;
var input = CLIENT_SECRET + '|' + codeOrRefreshToken;
var hash = Utilities.computeDigest(
Utilities.DigestAlgorithm.SHA_256, input, Utilities.Charset.UTF_8);
hash = hash.map(function(val)
{
// Google appears to treat these as signed bytes, but we need them
// unsigned.
if (val < 0)
{
val += 256;
}
var str = val.toString(16);
// pad to two hex digits:
if (str.length == 1)
{
str = '0' + str;
}
return str;
});
payload.hash = hash.join('');
// The Smartsheet API doesn't need the client secret sent (secret is verified
// by the hash).
if (payload.client_secret)
{
delete payload.client_secret;
}
return payload;
}
/**
* Logs the redict URI to register.
*/
function logRedirectUri()
{
Logger.log(OAuth2.getRedirectUri());
}
function dataHandler(thing)
{
thing = getData2();
var rowTemp = thing.split(','), i, j, chunk = 7, rows = [];
for (i=0,j=rowTemp.length; i<j; i+=chunk)
{
for(var k = 0; k<2; k++)
{
rowTemp[k+2] = new Date(rowTemp[k+2])
}
rows.push(rowTemp.slice(i,i+chunk));
}
Logger.log(rows);
}
var CLIENT_ID = 'SmartSheet Client ID'; // I'm not sure if this is
// supposed to come from google
// or smartsheet
var CLIENT_SECRET = 'Smartsheet Client Secret'; // Same here
/**
* Authorizes and makes a request to the Smartsheet API.
*/
function run() {
var service = getService();
if (service.hasAccess()) {
var url = 'https://api.smartsheet.com/2.0/users/me';
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
Logger.log(JSON.stringify(result, null, 2));
} else {
var authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s',
authorizationUrl);
}
}
/**
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
getService().reset();
}
/**
* Configures the service.
*/
function getService()
{
return OAuth2.createService('Smartsheet')
// Set the endpoint URLs.
.setAuthorizationBaseUrl('https://app.smartsheet.com/b/authorize')
.setTokenUrl('https://api.smartsheet.com/2.0/token')
// Set the client ID and secret.
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
// Set the name of the callback function that should be invoked to
// complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Scopes to request
.setScope('READ_SHEETS')
// Set the handler for adding Smartsheet's required SHA hash parameter to
// the payload:
.setTokenPayloadHandler(smartsheetTokenHandler);
}
/**
* Handles the OAuth callback.
*/
function authCallback(request)
{
var service = getService();
var authorized = service.handleCallback(request);
if (authorized)
{
return HtmlService.createHtmlOutput('Success!');
}
else
{
return HtmlService.createHtmlOutput('Denied.');
}
}
/**
* Adds the Smartsheet API's required SHA256 hash parameter to the access token
* request payload.
*/
function smartsheetTokenHandler(payload)
{
var codeOrRefreshToken = payload.code ? payload.code : payload.refresh_token;
var input = CLIENT_SECRET + '|' + codeOrRefreshToken;
var hash = Utilities.computeDigest(
Utilities.DigestAlgorithm.SHA_256, input, Utilities.Charset.UTF_8);
hash = hash.map(function(val)
{
// Google appears to treat these as signed bytes, but we need them
// unsigned.
if (val < 0)
{
val += 256;
}
var str = val.toString(16);
// pad to two hex digits:
if (str.length == 1)
{
str = '0' + str;
}
return str;
});
payload.hash = hash.join('');
// The Smartsheet API doesn't need the client secret sent (secret is verified
// by the hash).
if (payload.client_secret)
{
delete payload.client_secret;
}
return payload;
}
/**
* Logs the redict URI to register.
*/
function logRedirectUri()
{
Logger.log(OAuth2.getRedirectUri());
}
I don't know much about Apps Script or the library that you are using, but you need to find the actual callback URI used by Apps Script and register that as the App Redirect URL in Smartsheet. It looks like the callback should be in the form https://script.google.com/macros/d/{SCRIPT ID}/usercallback (at least according to the library docs). That should issue the redirect which will eventually call your library authCallback with the authorization code for the token.
Here's another useful document of the process (but uses Node). https://developers.smartsheet.com/blog/creating-a-smartsheet-o-auth-flow-in-node-js
This is a complicated process that I have documented here: https://smartsheet-platform.github.io/api-docs/#third-party-app-development
If you still have questions after looking at this documentation/tutorial section, please keep asking. I'm here to help.
I have a script that pulls user data from the directory and pushes it to a google sheet. I keep getting an error that I don't have access and have tried everything. I have no idea why.
Here's my code:
var PRIVATE_KEY =
'-----BEGIN PRIVATE KEY--------END PRIVATE KEY-----\n';
var CLIENT_EMAIL = 'contacts#automation.iam.gserviceaccount.com';
var USER_EMAIL = 'admin#domain.com';
function getService() {
return OAuth2.createService('Domain:' + USER_EMAIL)
// Set the endpoint URL.
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject(USER_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/admin.directory.user.readonly');
}
function listAllUsers() {
var service = getService();
if (service.hasAccess()) {
var ss = SpreadsheetApp.getActive();
var pageToken,
page,
count = 0;
var listArray = [];
listArray.push(['full name', 'first name', 'last name', 'email', 'orgunit', 'department', 'title', 'phoneType', 'phoneNumber', 'ID'])
do {
page = AdminDirectory.Users.list({
domain: "domain.com", // Google Apps domain name
orderBy: "email",
pageToken : pageToken
});
var users = page.users;
if (users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
var department,
title, phonetype, phonenumber, orgunit; // Addded two new variables
try { // Try to get the users department if there is an error push the error to the array
department = user.organizations[0].department;
} catch (e) {
department = e
}
try {// Try to get the users title if there is an error push the error to the array
title = user.organizations[0].title;
} catch (e) {
title = e
}
try {// Try to get the users PhoneType if there is an error push the error to the array
phonetype = user.phones[0].type;
} catch (e) {
title = e
}
try {// Try to get the users PhoneNumber if there is an error push the error to the array
phonenumber = user.phones[0].value;
} catch (e) {
title = e
}
try {// Try to get the users PhoneNumber if there is an error push the error to the array
orgunit = user.organizations[0].name;
} catch (e) {
title = e
}
listArray.push([user.name.fullName, user.name.givenName, user.name.familyName, user.primaryEmail, orgunit, department, title, phonetype, phonenumber, user.id]);
}
}
pageToken = page.nextPageToken;
// This means you only get one page
} while (pageToken);
try {
var outputSheet = ss.getSheetByName('allMembers');
outputSheet.getDataRange();
} catch (err) {
var outputSheet = ss.insertSheet('allMembers', 2);
}
outputSheet.getDataRange().clear();
outputSheet.getRange(1, 1, listArray.length, listArray[0].length).setValues(listArray);
outputSheet.getRange(1, 6, outputSheet.getLastRow(), 4).setHorizontalAlignment("center");
outputSheet.getRange(1, 1, outputSheet.getLastRow(), 1).setHorizontalAlignment("center");
var width = [150, 150, 180, 250, 250, 200];
} }
When I run it with the viewtype domain_public it works, but only pulls global directory data and not hidden data like organization units.
I get the error Not Authorized to access this resource/api (line 41, file "Code")
I made sure that the advanced services and API is enabled for the project, the service account has domain wide delegation with scope: https://www.googleapis.com/auth/admin.directory.user.readonly
and I still can't seem to get it to work no matter what I try.
I tried adding the auth code from https://github.com/gsuitedevs/apps-script-oauth2 but as you can see, it still doesn't work.
I need to get all messages with labels to do statistics. I have a cuestion, because when I execute with my email I don't have any problem but the goal is getting statistics of shared account of gmail. For example, if I've used the next script:
/**
* Lists the labels in the user's account.
*/
function listLabels() {
var response = Gmail.Users.Labels.list('test#company.com');
if (response.labels.length == 0) {
Logger.log('No labels found.');
} else {
Logger.log('Labels:');
for (var i = 0; i < response.labels.length; i++) {
var label = response.labels[i];
Logger.log('- %s', label.name);
}
}
}
Where test#company.com is my shared account and I'm always getting a denied. I'm not sure if a problem with shared company or the method for logging is incorrect. The fact, If I do a logging with my shared email, really I see my personal account.
Thanks for your help.
Update with new code (JSON data are fake):
var JSON = {
"private_key": "xxxx",
"client_email": "mail_shared#test.com",
"client_id": "client_id.apps.googleusercontent.com",
"user_email": "personal#test.com"
};
function getOAuthService(user) {
return OAuth2.createService("Service Account")
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(JSON.private_key)
.setIssuer(JSON.client_email)
.setSubject(JSON.user_email)
.setPropertyStore(PropertiesService.getScriptProperties())
.setParam('access_type', 'offline')
.setScope('https://www.googleapis.com/auth/gmail.labels');
}
function reset() {
var service = getOAuthService();
service.reset();
}
/**
* Lists the labels in the user's account.
*/
function listLabels() {
var service = getOAuthService();
service.reset();
var response = Gmail.Users.Messages.list('mail_shared#test.com');
if (response.labels.length == 0) {
Logger.log('No labels found.');
} else {
Logger.log('Labels:');
for (var i = 0; i < response.labels.length; i++) {
var label = response.labels[i];
Logger.log('- %s', label.name);
}
}
}
I've tried the next differents scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.labels