问题
I would like to find out, is there any chance to get a list of G Suite users (of one domain) who are using Google Apps Script?
回答1:
After you retrieve the list of script projects from Google Drives of your users, you will have to first request project metadata for them from the Apps Script API. Presently, the only way to get a project is to go one by one, supplying requests with scriptId
.
The trick is that the Id of the script project file happens to be the the same as script Id (if you look at the source code of the CLASP project's list
command, you will see that they utilize this fact to display project Ids).
To acquire a Project
resource, we need to call the get
method:
GET https://script.googleapis.com/v1/projects/{scriptId}
Below is a simple utility for retrieving a single Project
resource from the API. Note that your manifest file will have to include at least the https://www.googleapis.com/auth/script.projects.readonly
scope, otherwise the API will return a 403
response code.
/**
* @typedef {{
* domain : string,
* email : string,
* name : string
* }} GSuiteUser
*
* @typedef {{
* scriptId : string,
* title : string,
* createTime : string,
* updateTime : string,
* creator : GSuiteUser,
* lastModifyUser : GSuiteUser
* }} ScriptProject
*
* @summary gets script project metadata
* @param {{
* id : string,
* token : string
* }}
* @returns {ScriptProject}
*/
const getProject = ({
id = ScriptApp.getScriptId(),
token = ScriptApp.getOAuthToken()
}) => {
const uri = `https://script.googleapis.com/v1/projects/${id}`;
/** @type {GoogleAppsScript.URL_Fetch.URLFetchRequestOptions} */
const params = {
contentType : "application/json",
headers : {
Authorization: `Bearer ${token}`
},
muteHttpExceptions : true,
method : "get"
};
const response = UrlFetchApp.fetch(uri, params);
const successChecker = getSuccessChecker();
const success = successChecker(response);
if(!success) {
return {};
}
return JSON.parse(response.getContentText());
};
Map it over the list of script files you obtained using ziganotschka's method, and you will get detailed info about the projects. Next, if by using you mean running projects, you can invoke the processes.list
API method instead:
GET https://script.googleapis.com/v1/processes
Required OAuth scope is https://www.googleapis.com/auth/script.processes
.
/**
* @typedef {{
* projectName : string,
* functionName : string,
* processType : string,
* processStatus : string,
* userAccessLevel : string,
* startTime : string,
* duration : string
* }} ScriptProcess
*
* @summary lists script processes for a user
* @param {{
* id : (string|"any"),
* pageSize : (number|50),
* token : string,
* start : (Date|undefined),
* end : (Date|undefined),
* statuses : string[],
* types : string[]
* }}
* @returns {ScriptProcess[]}
*/
const listScriptProcesses = ({
id = ScriptApp.getScriptId(),
token = ScriptApp.getOAuthToken(),
pageSize = 50,
start, end,
statuses = [],
types = []
} = {}) => {
const query = [
`pageSize=${pageSize}`,
`userProcessFilter.startTime=${toZuluTimestamp(start)}`,
`userProcessFilter.endTime=${toZuluTimestamp(end)}`
];
id !== "any" && query.push(`userProcessFilter.scriptId=${id}`);
types.length && query.push(`userProcessFilter.types=${types.join(",")}`);
statuses.length && query.push(`userProcessFilter.statuses=${statuses.join(",")}`);
const uri = `https://script.googleapis.com/v1/processes?${query.join("&")}`;
/** @type {GoogleAppsScript.URL_Fetch.URLFetchRequestOptions} */
const params = {
contentType: "application/json",
headers: {
Authorization: `Bearer ${token}`
},
muteHttpExceptions: true,
method: "get"
};
const response = UrlFetchApp.fetch(uri, params);
const content = response.getContentText();
const successChecker = getSuccessChecker();
const success = successChecker(response);
if (!success) {
console.warn(response.getResponseCode(), content);
return [];
}
const { processes = [] } = JSON.parse(content);
return processes;
};
In response, you will get metadata about script executions on behalf of the user whose credentials are passed with the bearer token (you will need a service account for each user).
The rest is easy: if the response is not empty, then the user ran a script project at some point in time (note that the utility above defaults both start
and end
timestamp parameters to now
). If you supply any
as the script Id, the request will return every execution made on behalf of the user.
An added benefit of the approach is that every type of script project execution is returned, including Web Apps, Add-ons, and bound projects (see ProcessType enum for details).
The only difficulty with this approach is presented by Web Apps deployed to "execute as me" which will always run under the authority of the script project owner, so you will have to track the users of the Web App separately.
Snippets above use the following utility scripts:
/**
* @summary checks HTTPResponse for being successful
* @param {GoogleAppsScript.URL_Fetch.HTTPResponse} resp
* @returns {boolean}
*/
const getSuccessChecker = ({ successOn = [200] } = {}) => (resp) => {
const code = resp.getResponseCode();
return successOn.some(c => c === code);
};
/**
* @summary converts input into RFC3339 UTC "Zulu" format
* @param {Date|number|string} [date]
* @returns {string}
*/
const toZuluTimestamp = (date = Date.now()) => new Date(date).toISOString().replace('Z','000000Z');
You will need to enable V8 runtime enabled for the snippets above to work (or transpile them to ES5 syntax).
回答2:
How to verify either domain users have Standalone Apps Script projects on their Google Drive
- In order to have as an admin access to your users' Drive, you need to set-up a service account in your GCP Console
- Make sure to enable domain-wide delegation - this allows oyu to authenticate as a domain user and list his Drive files
- Go to your Admin console and got to
Main menu menu> Security > API controls
and add the necessary scopes for the freshly created service account - as described here - Download the .json file with the service account credentials - it should contain a
"private_key"
field - The necessary scope in your case is
https://www.googleapis.com/auth/drive.readonly
- To use a service accounbt within Apps Script, you need to install the OAuth2 for Apps Script library from as described herehere
- For this, go to your Apps Script editor,
"Resources > Libraries... > Find a library"
and enter the code1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
- Chose a version (currently the newest is 38)
- Now, the first thing you need to do programmaticallyis to enable the adminSDK to list all domain users - as suggested by @TheMaster
- The second step is to loop through all users, and to create a service account token on their behalf (setting them as a subject)
- With the access token you can list the files with the query parameters
q: 'mimeType="application/vnd.google-apps.script"'
for each user to find out either the user has standaolone Apps Script files on his Drive
Code sample:
function myFunction() {
var users = AdminDirectory.Users.list({"domain":"PASTE HERE YOUR DOMAIN NAME"}).users;
users.forEach(function(user){
user = user.primaryEmail;
getService(user).reset();
var service = getService(user);
if (service.hasAccess()) {
Logger.log("service has access");
var url = "https://www.googleapis.com/drive/v3/files";
var query = '?q=mimeType%3D%22application%2Fvnd.google-apps.script%22';
var headers ={
"Authorization": 'Bearer ' + service.getAccessToken()
};
var options = {
'headers': headers,
'method' : 'get',
'muteHttpExceptions': true
};
var response=UrlFetchApp.fetch(url+query, options).getContentText();
if(JSON.parse(response).files.length==0){
Logger.log("User " + user + " does not have any Standalone Apps Script projects on his Drive");
}else{
Logger.log("User " + user + " has Standalone Apps Script projects on his Drive");
}
}
else {
Logger.log(service.getLastError());
}
}
)
}
var PRIVATE_KEY ="-----BEGIN PRIVATE KEY-----PASTE HERE YOUR PRIVATE KEY FROM THE JSON FILE-----END PRIVATE KEY-----\n";
var CLIENT_EMAIL = 'PASTE HERE THE EMAIL OF THE SERVICE ACCOUNT';
function getService(user) {
return OAuth2.createService('List users')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
.setSubject(user)
.setPropertyStore(PropertiesService.getScriptProperties())
.setParam('access_type', 'offline')
.setScope("https://www.googleapis.com/auth/drive.readonly");
}
来源:https://stackoverflow.com/questions/63342311/how-to-get-a-list-of-g-suite-users-which-are-using-google-apps-script