Clarification on Active User Scope and Script Authorization - google-apps-script

I have a spreadsheet with a UI app script. The app reads/updates from the spreadsheet.
My current settings are: Execute the app as "me".
Who has access to the app: "anyone".
I'm essentially sending out workflows that have assignments by email. Those emails can be outside of the domain but would all be gmail addresses.
I had hoped I'd be able to access the Session.getActiveUser() as I need to allow certain functionality on the app based on user accessing the UiApp. That function does not seem to provide any info unless I'm logged in as the script owner. Session.getActiveUser() is blank when accessing from a different address.
Is there a way to get the active user without having to Execute the app as "User accessing the app". I'd rather not have to share the spreadsheet with all the end users.
Thanks for any suggestions.

Rewrite from comments for readability :
Sharing the spreadsheet in this context doesn't mean anybody could open the spreadsheet directly as a document as long as the url of the spreadsheet remains unknown. The app will indeed need to have access to it if you deploy your app as 'User accessing the webapp' but no one can find the url of the sheet unless you publish it. I use this configuration very often but of course I don't know your exact use case... ;-) In my case the app itself determines what a specific user can do or not (by enabling /disabling certain functions / panels depending on the logged user.
Here is an example of an app like this, can you find the source spreadsheet ?
My spreadsheet has "anyone with the link" so the app can read and write to it but the name of the spreadsheet and its url is unknown (except for me) so it never appears in the document list of the user even if the user "writes" something in the document through the webapp (since he never actually "opens" it). I checked that on different accounts and never found any exception.

The active user and the property's like email etc.. Are not accessible with a public google account. It is accessible and useable with a google apps account within your own domain. See http://www.google.com/intl/en/enterprise/apps/business/pricing.html
Otherwise it's just a point of privacy on the web.
A litle disappointing i can imagine but logical if you think about it.
I do have another tip for you. Have al look at this link http://shop.oreilly.com/product/0636920023210.do
O'reilly also implemented a workflowsystem. It uses the url parameters to pass email (identity) information. Not really secure, but in combination with the Utilities.DigestAlgorithm SHA encoding you could take steps to encode your url email-parameters.
Hope this is helpfull.

Related

Getting all viewers of spreadsheet using App Script

i wrote code for getting all viewers of spreadsheet in app script .
i have used getViewers() method to get viewers names who actually viewed it. but that method is returning me the names of people to whom i actually shared the spreadsheet....
is there any other way that i can get all viewers of spreadsheet.?
is there any web automation tools that can solve my problem?
Answer:
It is not possible to get a list of people that have opened your a Google Drive file using Google Apps Script - a method that returns this list does not exist. The getViewers() method returns the list of people with view and comment permissions for a file, while getEditors() retrieves the list of people that have edit permissions.
The Issue:
is there any other way that i can get all viewers of spreadsheet.? is there any web automation tools that can solve my problem?
There is no way of getting viewers of a Google Sheet as this is a huge security issue. This information is not stored and is therefore not retrievable.
Workaround:
You can make a custom function which stores the username of a person when they open the file - though be aware that triggers have restrictions and will only run if the person opening the file has edit access. I have included a list of Apps Script Trigger Restrictions below for you to look through and decide what is the best approach for you.
Code:
function onOpen(e) {
var user = e.user.getEmail();
// do some code to store or save this parameter
// for example save it to a hidden sheet or email it to yourself
// though an email would require an installable trigger to be made
}
Simple Trigger Restrictions:
These are not all of the restrictions (full restrictions are available here), but these are the ones that I believe to be most relevant for you.
As per the Google Apps Script Simple Triggers documentation:
They do not run if a file is opened in read-only (view or comment) mode.
They cannot access services that require authorization. For example, a simple trigger cannot send an email because the Gmail service requires authorization, but a simple trigger can translate a phrase with the Language service, which is anonymous.
They can modify the file they are bound to, but cannot access other files because that would require authorization.
They may or may not be able to determine the identity of the current user, depending on a complex set of security restrictions.
This last point is important - getting information about the current user is possible depending on the security policies of the the G Suite domain. A detailed explanation of this can be found in the getActiveUser() method documentation:
If security policies do not allow access to the user's identity, User.getEmail() returns a blank string. The circumstances in which the email address is available vary: for example, the user's email address is not available in any context that allows a script to run without that user's authorization, like a simple onOpen(e) or onEdit(e) trigger, a custom function in Google Sheets, or a web app deployed to "execute as me" (that is, authorized by the developer instead of the user). However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same G Suite domain as the user.
There are big security issues with getting a list of people that have viewed a file, for good reason, and so what you are looking to do it highly restricted by Google.
References:
Class File of Google Apps Script
getViewers() method of Class File
getEditors() method of Class File
Simple Triggers
onOpen(e) Trigger
Simple Trigger Restrictions
Installable Triggers
Event objects
Class User of Apps Script
User.getEmail() method
Class Session
getActiveUser() method of Session Class

Do I need OAuth - Google Contact Form data (all) to Sheet then to Email

I want to use Google Form as a contact form on a website, and email the contents of the form (not just a notification) to a specified address.
I created a Google Form, embedded it on a website to use as a contact form for enquiries. Then created a Sheet to pull in the data from the Form. After that I created a Trigger for onFormSubmission which runs the following:
/**
* #OnlyCurrentDoc
*/
function onFormSubmission (e) {
var values = e.namedValues;
var htmlBody ='<ul>';
for (Key in values) {
var label = Key;
var data = values[Key];
htmlBody += '<li>' + label + ": " + data + '</li>';
Logger.log(label + ": " + data);
};
htmlBody += '</ul>';
GmailApp.sendEmail('me#gmail.com', 'Web Form Enquiry', '', {htmlBody:htmlBody})
}
Running this brings up a dialogue stating:
Authorization required.
'Send email' needs your permission to access your data on Google
Review Permissions or Cancel
['Send email' is my 'App' in G Suite]
Clicking Review Permissions opens a popup asking to choose an account to continue. Clicking on the account (the same account I'm logged in with and have created the form, etc with) brings up:
This app isn't verified. This app hasn't been verified by Google yet. Only proceed if you know and trust the developer.
Advanced or Back to safety
Clicking Advanced:
Google hasn't reviewed this app yet and can't confirm it's authentic. Unverified apps may pose a threat to your personal data.
Go to 'Send email' (unsafe)
It then states:
'Send email' wants to access your Google account
This will allow 'Send email' to Read, compose, send and permanently delete all your email from Gmail
Connect to an external service (Create a network connection to any external service (e.g. to read or write data)
Make sure that you trust Send emails
You may be sharing sensitive info with this site or app. Find out how Send emails will handle your data by reviewing its terms of service and privacy policies. You can always see or remove access in your Google Account.
Find out about the risks
Cancel or Allow
So, for testing purposes I clicked Allow. And it all worked beautifully! And I made the form responsive, and then I remembered I had to go back and review the permissions. After 4 hours I got lost down the rabbit hole, and crawled my way back up to here in confusion. So my questions are:
Do I need to implement OAuth?
Does Google need to verify the app?
If yes to 2, does this take weeks? (If yes, probably need to abandon this solution)
If I give the app 'permissive permissions' (as I did for testing) is there any security risk?
If I give the app 'permissive permissions' is Google going to review it and decide to stop it running?
The only reason I can imagine that OAuth is needed is that data is being taken from Sheets, an email is being created and the data is being passed to that. Is it because of the data being passed to a different 'service' than collected it? Or because a different service is instructing the creation of an email? (Or both?)
If OAuth is implemented does this mean that users will be asked to verify? Obviously, they will not require to be logged in to use a contact form on a website.
Is it possible to restrict the permissions eg to only compose emails, rather than have the ability to delete any or all emails in the account (manifest?)
Would it make any difference if I skipped the spreadsheet ie Form to email? (Can't see how, but asking just in case.)
This is my first time using these services, so hopefully, I've explained it ok.
Any advice gratefully received!
I want to use Google Form as a contact form on a website, and email the contents of the form (not just a notification) to a specified address.
Do I need to implement OAuth?
What you're doing is already OAuth flow. But it's managed by apps script. So, No.
Does Google need to verify the app?
Not for your use case.
If yes to 2, does this take weeks? (If yes, probably need to abandon this solution)
Maybe more
If I give the app 'permissive permissions' (as I did for testing) is there any security risk?
Anyone with access to your script(by implication, anyone with edit access to your spreadsheet) can change the script to send email from your account to any other website or Read or delete your email. So, Avoid providing edit permission to your Spreadsheet or script.
If I give the app 'permissive permissions' is Google going to review it and decide to stop it running?
Not if you're the only user.
The only reason I can imagine that OAuth is needed is that data is being taken from Sheets, an email is being created and the data is being passed to that. Is it because of the data being passed to a different 'service' than collected it? Or because a different service is instructing the creation of an email? (Or both?)
Yes.
If OAuth is implemented does this mean that users will be asked to verify? Obviously, they will not require to be logged in to use a contact form on a website.
You are the owner of the email, sheet and the form responses. Only you need to authorize the app/script to act on your behalf to do certain jobs like copy/paste.
Is it possible to restrict the permissions eg to only compose emails, rather than have the ability to delete any or all emails in the account
Yes.
You can use MailApp instead of GmailApp
OR Limit the scopes in apps script manifest file:
https://www.googleapis.com/auth/gmail.send
Would it make any difference if I skipped the spreadsheet ie Form to email? (Can't see how, but asking just in case.)
Reducing the number of hops it takes for the script flow is good for security. But doesn't make much difference in terms of oauth flow/permissions requested. You can use the formsubmission trigger in the form itself. Note however that you need to redesign the script according to the event object provided on formsubmit to the form. This is different from the event object provided to the spreadsheet onformsubmit.

How to get the user property for the "active user" in google apps script custom function

I've published a Google Apps Script standalone spreadsheet add-on, while I met some problem using PropertiesService.getUserProperties() in a custom function.
If User A has installed the add-on and saved some data in the user property.
Then User A shared the spreadsheet to User B. When user B run the custom function, he can get the saved data in user property for user A. This is not what I suppose user Property works. Both User A and B should get their own saved data.
So what is the active user for a shared spreadsheet? I don't want all the spreadsheet viewers to get the saved data for the spreadsheet creator. Is there anything I'm doing wrong in code or "Cloud Platform project" option?
In other words, how can I get different data saved in user properties for different viewers in a shared spreadsheet?
I tried to get the active user email in a custom function, but it does not work.
Please note, I can't use Session.getActiveUser in the custom function, because Google Apps Script Custom function does not support Session service. It will get an invalid permission exception.
https://developers.google.com/apps-script/guides/sheets/functions#Advanced
Thanks,
After writing tons of code to test different scenarios, I believe it has no way to solve the above problem.
So I will use a workaround to achieve the goal. In case someone also run into the trouble. I will put down my test results here.
If Google account A shared spreadsheet to Google account B.
In custom function, they all get A's user property.
From a webpage, call into server function via script.run, both A and B can
get their own user property.
Custom functions can't access PropertiesServices.userProperties() (maybe the exception is the spreadsheet owner).
According to https://developers.google.com/apps-script/guides/sheets/functions
Unlike most other types of Apps Scripts, custom functions never ask users to authorize access to personal data. Consequently, they can only call services that do not have access to personal data, specifically the following:
While Properties is included on the list and there isn't any note for this service, since the custom function doesn't ask users to authorize to access personal data it should not return PropertiesServices.userProperties() as it potentially store user personal data.

Google Script and Anonymous user in Google Sheet

I've a public Google Sheet (everyone can access it and edit it). In it, I use a script, a function, that download a csv with the UrlFetchApp.fetch(url);.
When an anonymous user access it (without the google login), the user can view, and edit the sheet, but, the user cannot run the script. The user must be logged with a google account to the script do the working.
The stranger thing is that this sheet with this script was working for anonymous users early. This problem didn't occur before a couple weeks ago.
Do you know if the google change something about security or is it an error that I am doing?
In order to run apps script on a particular user's behalf, Google Workspace requires that user's permission, via an OAuth flow.
If a user is anonymous therefore, you can see why this permission cannot be granted, which is why the script won't work for those users.
I guess it is an intended behavior as long as the anonymous user access the sheet via shareable link. As stated in this support page, you might see a name you don’t recognize or "anonymous animals" viewing your document, spreadsheet, or presentation. This can happen when a document is shared publicly or with anyone who has the link.
Limit how people can view your file
If you want to stop sharing a file you can edit, you can learn how to:
Turn off link sharing for a file.
Prevent others from sharing files you own.
Hope this helps.

Google Apps Script - get/assign user unique identifier

I am developing a Google Docs Add-on and need to know if the user currently using the add-on has used it before in the current document or any document. I can't figure out which service gives me some kind of unique identifier for the current user that I can use to identify the current user.
The base service's User.getEmail is not good enough as it doesn't give access to the email under certain conditions and is not reliable.
Thoughts on best way to do this?
Unfortunately, there is no such service. Google wants users' personal information to remain private, so AFAIK you won't usually get even user's email, unless your script is written for Google Apps domain and the user is in the same domain as the script's author.
The only thing you can do in the situation is use something like DocumentApp.getActiveDocument().getId() and check if the script was used in this document, but of course this won't give you an idea if user has used your script in other documents or not.
Also you can show a Prompt Dialog to user, asking him to enter email, but of course there is no way you can check if he entered his real email.
Unique user identifier you can get for apps script is Session.getEffectiveUser().getEmail()
This will return the email of user for which the script run. This is better than ActiveUser because if you use trigger EffectiveUser will well return the email of the user who install the script.
I use that for several apps to identify license assignment and it works great.
It looks like the best solution is to roll your own solution using the following to set the userId at an appropriate time:
PropertiesService.getUserProperties().setProperty("userId",<someGeneratedUId>)
then whenever you want to look up if this user is a recognizable user,
var userId = PropertiesServices.getUserProperties().getProperty("userId")
Looks like once set, the UserProperties thus set is available to this particular user (per user) running this add-on everywhere (i.e. all docs, not just the current on) that have this particular add-on enabled/running.
I'll validate this further but seems like this works in my tests.