Google Apps Script Wants Unrestricted Scope - google-apps-script

Simple function here in Google Apps Script:
function driveSearch() {
// Log the name of every file in the user's Drive whose visibility is anyonewithLink or anyonecanfind
var files = DriveApp.searchFiles(
'visibility = "anyoneWithLink" or visibility = "anyoneCanFind"');
while (files.hasNext()) {
var file = files.next();
var owner = file.getOwner().getName();
var sa = file.getSharingAccess();
Logger.log(file.getName());
Logger.log('Owner:'+owner);
Logger.log("SharingAccess:"+sa);
}
}
It want to find shared files in my gsuite drive.
However, it says I don't have permissions to run DriveApp
My permissions are set and requested correctly, like so:
{
"timeZone": "America/New_York",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/drive.metadata.readonly"
]
}
The error is
You do not have permission to call DriveApp.searchFiles. Required permissions: https://www.googleapis.com/auth/drive (line 3, file "Code")
Why does searchFiles require delete permissions? I am very worried about inadvertent deletion, I don't want the full scope. Is there something else I'm doing wrong?
Lastly, yes my Gsuite allows Google Apps Script.

How about this workaround? In this workaround, the endpoint of Drive API is directly requested by UrlFetchApp, and the following 2 scopes are used.
https://www.googleapis.com/auth/drive.metadata.readonly
This is used to the access token which is used for using Drive API.
https://www.googleapis.com/auth/script.external_request
This is used for using UrlFetchApp.
Enable Drive API at API console
Before you use this script, please enable Drive API as follows.
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click "Explore and enable APIs".
At left side, click Library.
At "Search for APIs & services", input "Drive". And click Drive API.
Click Enable button.
If API has already been enabled, please don't turn off.
Sample script:
function myFunction() {
var baseUrl = "https://www.googleapis.com/drive/v3/files";
var q = 'visibility = "anyoneWithLink" or visibility = "anyoneCanFind"';
var fields = "files(id,name,owners,permissions),nextPageToken";
var pageToken = "";
var results = [];
var params = {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}};
do {
var endpoint = baseUrl + "?pageSize=1000&q=" + encodeURIComponent(q) + "&fields=" + encodeURIComponent(fields) + "&pageToken=" + pageToken;
var res = UrlFetchApp.fetch(endpoint, params);
res = JSON.parse(res.getContentText());
Array.prototype.push.apply(results, res.files);
pageToken = res.nextPageToken || "";
} while (pageToken);
results.forEach(function(e) {
Logger.log(e.name);
Logger.log('Owner: ' + e.owners.map(function(f) {return f.displayName}).join(","));
Logger.log("SharingAccess: " + e.permissions.map(function(f) {return f.id}).join(","));
});
}
Note:
In this sample script, the log is the same with your script.
References:
Drive API
list
UrlFetchApp
If this was not the result you want, I apologize.

Related

ScriptApp.getOAuthToken not getting the right permissions for drive through url fetch app?

Trying to explore this with a very simple script but I'm getting an insufficient permissions error:
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
I tried enabling the v2 drive api in the advanced google services dropdown but that didn't work.
I believe your situation and goal as follows.
From gdriveId in your script, I thought that you want to retrieve the folder list in the root folder of gdriveId using the method of "Children: list" in Drive API v2.
You have already enabled Drive API at Advanced Google Services.
For this, how about this answer?
Modification points:
When your script is put to new GAS project and Drive API is enabled at Advanced Google Services, the scopes of the project is only https://www.googleapis.com/auth/script.external_request. The required scope can be automatically detected by the script editor. But, even when Drive API is only enabled, it seems that no scopes are added. I think that the reason of your issue is this.
Under above situation, if you want to retrieve the access token including the required scopes, in order to make the script editor automatically detect the scope of https://www.googleapis.com/auth/drive.readonly, for example, please put // DriveApp.getFiles() to the script as a comment line.
In this case, when you use the methods for other scopes in your script, those scopes can be automatically detected and added by the script editor.
Modified script 1:
When your script is modified, it becomes as follows.
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
// DriveApp.getFiles() // <--- Added this comment line. By this, the scope of https://www.googleapis.com/auth/drive.readonly is added.
Modified script 2:
When the method of Advanced Google service is used, the scope of https://www.googleapis.com/auth/drive is automatically added. By this, the following script works.
function test() {
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var res = Drive.Children.list(gdriveId);
console.log(res)
}
Other pattern:
From June 1, 2020, the files and folders in the shared Drive can be retrieved by Drive service. So you can also use the following script.
function myFunction() {
const getFolderList = (id, folders = []) => {
const f = DriveApp.getFolderById(id);
const fols = f.getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push({name: fol.getName(), id: fol.getId(), parent: f.getName()});
}
if (temp.length > 0) {
folders.push(temp);
temp.forEach((e) => getFolderList(e.id, folders));
}
return folders.flat();
};
var gdriveId = "###"; // Please set the Drive ID.
const res = getFolderList(gdriveId);
console.log(res);
}
References:
Advanced Google services
Children: list of Drive API v2
Authorization Scopes
If you want to give permission to write with ScriptApp.getOAuthToken(), just add the following code in a commented out form and authorize it at runtime. If you don't do this, you'll only be able to download and browse.
//DriveApp.addFile("test");
Reference URL:https://00m.in/UeeOB

Google script replaceAllShapesWithImage with image from drive doesn"t work any more

Since yesterday one of my google script doesn't work anymore.
The script
take an image on the drive
copie a slide
replace a shape with an image
But I got this error:
"The provided image is in an unsupported format."
-> I give all access to the image: it doesn't change anything
-> The script work if I take an url outside the drive
Any idea
function test_image(){
var imageUrls = DriveApp.getFilesByName("DSC_3632.png");
var file = "undefined";
while ( imageUrls.hasNext()) {
var file = imageUrls.next();
}
var imageUrl = file.getDownloadUrl() + "&access_token=" + ScriptApp.getOAuthToken();
var model_file = DriveApp.getFileById("your-id");
var presentation = model_file.makeCopy("totot");
var presentation =Slides.Presentations.get(presentation.getId())
var requests = [{
"replaceAllShapesWithImage":
{
"imageUrl": imageUrl,
"imageReplaceMethod": "CENTER_INSIDE",
"containsText": {
"text": "toto",
"matchCase": false,
}
}
}];
var presentationId = presentation.presentationId
var createSlideResponse = Slides.Presentations.batchUpdate({
requests: requests
}, presentationId);
}
How about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
I think that the reason of your issue is due to the following modification of official document.
First, we’re making changes to authorization for the Google Drive API. If you authorize download requests to the Drive API using the access token in a query parameter, you will need to migrate your requests to authenticate using an HTTP header instead. Starting January 1, 2020, download calls to files.get, revisions.get and files.export endpoints which authenticate using the access token in the query parameter will no longer be supported, which means you’ll need to update your authentication method.
By above situation, the URL of var imageUrl = file.getDownloadUrl() + "&access_token=" + ScriptApp.getOAuthToken(); cannot be used. For example, when it accesses to the URL, the login screen is displayed even when the access token is used.
In order to avoid this issue, how about the following modification?
Modification points:
The file is shared publicly and put to Google Slides. Then, the sharing file is closed.
In this case, even when the share of file is closed, the put image on Slides is not removed.
The webContentLink is used as the URL.
It's like https://drive.google.com/uc?export=download&id=###.
Modified script:
When your script is modified, it becomes as follows.
function test_image(){
var imageUrls = DriveApp.getFilesByName("DSC_3632.png");
var file; // Modified
while (imageUrls.hasNext()) {
file = imageUrls.next();
}
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); // Added
var imageUrl = "https://drive.google.com/uc?export=download&id=" + file.getId(); // Modified
var model_file = DriveApp.getFileById("your-id");
var presentation = model_file.makeCopy("totot");
var presentation =Slides.Presentations.get(presentation.getId())
var requests = [{
"replaceAllShapesWithImage": {
"imageUrl": imageUrl,
"imageReplaceMethod": "CENTER_INSIDE",
"containsText": {
"text": "toto",
"matchCase": false,
}
}
}];
var presentationId = presentation.presentationId
var createSlideResponse = Slides.Presentations.batchUpdate({requests: requests}, presentationId);
file.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.NONE); // Added
}
References:
Upcoming changes to the Google Drive API and Google Picker API
setSharing()
If I misunderstood your question and this was not the direction you want, I apologize.

Add convert xls to Google sheets feature to function of download

I have working code for download any file in google docs by link. Most of files in xlx or xlsx format and need to be convert in google sheets type. Ive been tried to use some methods, that I find in Internet, but they dont work for me. In code below, I comment with code wich I test. And I guess, that google had changed some documentation
function downloadFile(fileURL,folder) {
var fileName = "";
var fileSize = 0;
var fileId = "";
var response = UrlFetchApp.fetch(fileURL, {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob() //.getAs(MimeType.GOOGLE_SHEETS) - get error
var folder = DriveApp.getFolderById(folder);
if (folder != null) {
var file = folder.createFile(fileBlob);//.getAs(MimeType.GOOGLE_SHEETS) - get error
fileName = file.getName();
fileSize = file.getSize();
fileId = file.getId();
}
}
//file.setMimeType("application/vnd.google-apps.spreadsheet") - not work
//makeCopy('ssssss', folder, {convert: true}) - get error
var fileInfo = [ rc, fileName, fileSize, fileId ];
return fileInfo;
}
I call function like this:
downloadFile("http://www.beltools.ru/prais_rar/price%20TD%20RI.xls","0B_E2P3ZhQySBY3BBMzdlazBLcTA")
In order to convert Excel to Spreadsheet, DriveApp cannot do it. So Drive API has to be used. You can use Drive API v2 from Advanced Google services. "Drive API v2" can be used at Google Apps Script by enabling Drive API of Advanced Google services and of Google API Console.
How to use it is as follows.
In the script editor, select Resources > Advanced Google services
In the dialog that appears, click the on/off switch for Drive API v2.
At the bottom of the dialog, click the link for the Google API Console.
In the console, click into the filter box and type part of the name of the API "Drive API", then click the name once you see it.
On the next screen, click Enable API.
Close the Developers Console and return to the script editor. Click OK in the dialog. The advanced service you enabled is now available in autocomplete.
The detail information is https://developers.google.com/apps-script/guides/services/advanced.
At the sample script, at first, a file is downloaded by fileURL, which is Excel file, as blob. The blob data is uploaded to Google Drive using Drive API. In this case, access token is not required. For downloadFile(), the Input data and output data are same to your downloadFile(). File name is retrieved from fileURL.
Script :
function downloadFile(fileURL, folder) {
var filename = fileURL.match(".+/(.+?)([\?#;].*)?$")[1];
var response = UrlFetchApp.fetch(fileURL);
var rc = response.getResponseCode();
var blob = response.getBlob();
var resource = {
"mimeType": "application/vnd.google-apps.spreadsheet",
"parents": [{id: folder}],
"title": filename
};
var res = Drive.Files.insert(resource, blob);
var fileInfo = [rc, res.title, blob.getBytes().length, res.id];
return fileInfo;
}
Result :
[
200,
sample.xlsx,
10000.0,
## file id ##
]
If I misunderstand your question, I'm sorry.

403 error when executing Google Apps Script form a different google account

I have this javascript to extract html table and then pass the arrays to google apps script as parameters.
var CLIENT_ID = 'some ID';
var SCRIPT_ID = 'some ID';
var SCOPES = ['https://www.googleapis.com/auth/drive.file'];
function handleAuthClick(event) {
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': true},
handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
// Access token has been successfully retrieved, requests can be sent to the API
} else {
// No access token could be retrieved, force the authorization flow.
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': false},
handleAuthResult);
}
}
function exportGsheet() {
var myTableArray = [];
$("table#fin tr").each(function() {
var arrayOfThisRow = [];
var tableData = $(this).find('td');
if (tableData.length > 0) {
tableData.each(function() { arrayOfThisRow.push($(this).text()); });
myTableArray.push(arrayOfThisRow);
}
});
var params = JSON.stringify(myTableArray);
var request = {
'function': 'setData',
'parameters': params,
'devMode': true // Optional.
};
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + SCRIPT_ID + ':run',
'method': 'POST',
'body': request
});
op.execute(function(resp){opensheet(resp)});
}
Below is the apps script. This uses Drive API and Executable API.
var DOC_ID = 'some id';
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var folder = DriveApp.getFolderById('some id');
function setData(parameters) {
var getFile = DriveApp.getFileById(DOC_ID);
var file = getFile.makeCopy(formattedDate, folder);
var ss = SpreadsheetApp.open(file);
var ssId = ss.getId();
ss.getSheets()[0].getRange(5,1,50,24).clear();
var e = JSON.parse(parameters);
var outerArray = [];
for(var i = 0; i < e.length; i++) {
outerArray.push(e[i]);
}
ss.getSheets()[0].getRange(5, 2, outerArray.length, outerArray[0].length).setValues(outerArray);
return {"url":ssId};
Logger.log(ssId);
}
Everything works fine when I authorize using the gmail ID that owns the apps script and project (my own gmail account). But when I authenticate using a different gmail account I get the below error:
error: {code: 403, message: "The caller does not have permission", status: "PERMISSION_DENIED"}
code: 403
message: "The caller does not have permission"
status: "PERMISSION_DENIED"
I intend to make this application public and anyone should be able to authenticate using their gmail account and execute the script. How do I do that? Please help.
Folks, I figured out the problem later. It happened to be permission issue n Developer console. We have to assign permission under Developer Console Project for the project which the apps-script is associated. So follow these steps:
Open your apps script Go to Resources-Developers Console Project
Click on the project name appearing in blue under "This script is
currently associated with project:" It will redirect you to Developer
Console Project.
Click on Menu on the left hand side upper corner and click on
Permissions
Under Permissions click on Add members
In the member type the email ID or domain you want to provide
permission and desired permission level. Click on 'Add'
You are done.
However, I wasnt able to add the entire gmail domain, nor able to add allAuthenticatedUsers. I have raised an issue with google support

Authorizing Google Apps Script to access Drive files

The code below works fine for getting my own Google+ user attributes:
// Google oAuth
function getAuth(service, scopes) {
var oAuthConfig = UrlFetchApp.addOAuthService(service);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken? scope="+scopes);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey("anonymous");
oAuthConfig.setConsumerSecret("anonymous");
return {oAuthServiceName:service, oAuthUseToken:"always"};
}
function test_API_Key() {
var gPlusAuth = getAuth("plus", "https://www.googleapis.com/auth/plus.me");
var url = "https://www.googleapis.com/plus/v1/people/me?key=" + API_KEY;
var httpResponse = UrlFetchApp.fetch(url, gPlusAuth);
Logger.log(httpResponse);
}
When I use the same approach for accessing metadata about files on my Google Drive, I run into the HTTP error, 403, and "access not configured." Below is the code. What am I doing wrong?
function getFileMetaData(fileId) {
var driveAuth = getAuth("drive", "https://www.googleapis.com/auth/drive");
var url = "https://www.googleapis.com/drive/v2/files/" +
documentID + "?key=" + API_KEY;
var response = UrlFetchApp.fetch(url, driveAuth);
Logger.log(response);
}
Taken from google developer site[1]
At a high-level, all apps follow the same basic authorization pattern:
Register the application in the Google Developers Console.
Request that the user grant access to data in their Google account.
If the user consents, your application requests and receives credentials to access the Drive API.
Refresh the credentials (if necessary).
<script type="text/javascript">
var CLIENT_ID = '<YOUR_CLIENT_ID>';
var SCOPES = [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
// Add other scopes needed by your application.
];
[1] https://developers.google.com/drive/web/about-auth