Allow script to edit locked cells - google-apps-script

I have a complex spreadsheet where most of the sheet is locked and the user can only edit a handful of cells which triggers a bunch of calculations. This used to work fine but the problem now is I have added a drawing which I attached a script to so it acts as a button. Doing this forces the user to have to authorize and now the scripts run as that user so when the script tries to update cells that are locked to the user it fails.
How can I make it so a user can't type into cells, but my scripts can still update them. Basically I want the script to have full access to the sheet, not restricted by user permissions.

Workaround#1 -Service account:
Create a service account
Share your spreadsheet with edit permissions to the service account's email
Install and Use the Google oauth2 library to get Bearer token with necessary scopes(Drive/Sheets/Both). This token can be used to impersonate the service account.
Using the bearer token above, You can directly access the
google-sheets-api using urlfetch
OR use a published webapp(set to execute as "User accessing the app" and "Anyone") to use inbuilt services such as SpreadsheetApp. See Second related answer linked below.
In this case, PRIVATE_KEY of the service account acts as a password to access the spreadsheet with edit privileges. So, exposing it directly in the script editor will give access to any of the editors to access protected areas of the spreadsheet and all service account resources. So, in a way, protected areas are not protected in a absolute way. If protected areas need to be absolutely protected, You may be able to bypass this limitation
using two script projects: a bound one posting data to a unbound one, which is published as a web app and holds the private key. Here, editors can be supplied with passwords to access the unbound script.
Another way is to simply publish a addon, as a addon's source code is never visible to end users.
Workaround#2 - Installable triggers:
Use a installable edit trigger with a checkbox. Users click a checkbox in the unprotected area and script modifies the protected area.
Installable triggers run under the authority of the user who installed it and not as the current user.
They can bypass permission restrictions of the sheet. But this is a double edged sword. Anyone with edit permission will be able to trigger the script. Not only that, they may also be able to access the script editor and modify the script as they see fit. To limit foul usage,
Set the script to run only at a specified version: This can be done by setting the edit trigger manually in Tools > Script editor> Edit > Current project triggers > Add trigger > Select version. Script must have a saved version and be deployed as a webapp(doesn't need to be working).
Avoid providing unnecessary scopes to the script. Limit oauthScopes by editing manifest file. Preferably the only scope provided should be https://www.googleapis.com/auth/spreadsheets.currentonly
Related:
Is there a way to let a user edit another spreadsheet with a script and hide it from him at the same time?
Google App Script execute function when a user selects a cell in a range

Related

Differentiate User and Owner Info in Apps Script Installable Trigger

When I call Session.getActiveUser() from a function called by an installable trigger (e.g. an OnOpen Event trigger in Google Sheets - but not the Simple Trigger) in Apps Script, what user information am I getting?
I understand that installable triggers run under the owner/creator of the trigger regardless of who has opened the sheet. So would this always return that person's info?
Either way, how do I get the information of the other person? (e.g. if it gives owner info, how do I get the info of the user actually opening it - and vice-versa)
Update:
I got another user to test my script. I watched the logs while they were in the file, and it definitely reported THEM as the user, even when the installable OnOpen trigger was triggered.
This is good from the perspective that it showed them the correct menu options - he and I saw different menus per my OnOpen script, which is what I want.
However, this raises two issues for me:
This seems to go against the Google Documentation, which states: "Installable triggers always run under the account of the person who created them. For example, if you create an installable open trigger, it runs when your colleague opens the document (if your colleague has edit access), but it runs as your account. This means that if you create a trigger to send an email when a document is opened, the email is always sent from your account, not necessarily the account that opened the document."
In a future function, I will be calling an API from another App. This API will need my credentials (API ID and Secret). I was hoping / expecting that I could "sandbox" my credentials in an installable trigger - invisible to other users - that will allow them to use my credentials just for the specific functions which I would script into the API. If the installable trigger is in fact, NOT using my credentials, then how can I do this? I don't want to have to make every user go to the other App and generate their own set of API credentials, that will be unsustainable in this organization, and not everyone should need to do that.
It should return whomever triggered the script. But it depends if the security policy does allow you to access the user's identity. Seeing the documentation:
Gets information about the current user. 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).
I have tested it and even an installable trigger won't return anything if it belongs to a different organization.
But you might be able to if other users belong to the same organization.
However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same Google Workspace domain as the user.
Workaround:
One thing I guess would be to assign the triggered function into a button and have the users click that upon opening the sheet. Via clicking the button, I have been able to show the User object using that method.
Or a webapp that will serve as a relay and will get the User details.

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

Google App Script and PropertiesService.getUserProperties()

I use the google app script in my google spreadsheet document.
The settings of each user of my spreadsheet doc is saved in:
var userProperties = PropertiesService.getUserProperties();.
To save the settings I use
PropertiesService.getUserProperties()setProperties({some properties}, true)
The problem is the first user saves his settings and another users get the settings of the first user using PropertiesService.getUserProperties()
But they should not, they should get own settings.
Do you have any idea how it is possible?
They must be logging in with the same user account for this to happen; a user can only ever access their own user properties. This could be because any triggers were created on your own account (say for form submissions), or web apps were deployed to run as you.
Share a copy of your sheet (andrew#roberts.net) if you would like me to a take a deeper look.
In fact as the support answered me, it is not a bug. I had wrong user properties on handling triggering event.
User properties are private to the user executing the script. However, when you setup an installable onEdit trigger, the code always runs as the user that setup the trigger:
"... runs with the authorization of the user who created the trigger, even if another user with edit access opens the spreadsheet."
https://developers.google.com/apps-script/guides/triggers/installable
Since the code always runs as the same user, it's always accessing the same user properties. In general it's not possible to get the identity of a user if they haven't authorized the script.

How to run a spreadsheet bound script as the creator (not viewer/editor)

I made some script with my spreadsheet, which uses some Trigger and SendEmail functionalities. I have menu items in spreadsheet to control these triggers and sendemails.
Now when I share this spreadsheet with someone other, when he tried to access the trigger or sendemail functions from menu, the script asks for authorization as that user. IF authorized it will function as that user. e.g. send email as that user or make a new trigger as that user. This makes things double and useless
I want that any user accessing the script can use those functionalities, but won't require authorization to run as that user. The script should run as the creator of the sheet, so that no double triggering occurs. How should I do it?
You could create a web-app. Web-apps have the ability to run either as the user himself, or as the developer/publisher.
Under Execute the app as, select whose authorization the app should
run with: your account (the developer's) or the account of the user
who visits the app (see permissions).
https://developers.google.com/apps-script/guides/web
This web-app wouldn't have a sheet linked to it, but if you only use 1 sheet you can have the web-app access the sheet through the ID of the sheet. You could use your existing menu-items to trigger the webapp
Would this be a possible solution for your problem?

Google Forms with script transfer of ownership still runs script as previous user

I made a google script in a spreadsheet that responds on form submit. It modifies the files, makes a pdf and emails that pdf to specific people. This was made for someone else and then I transferred ownership. However, the edits to those files and emails sent, still appear under my email. How do I completely transfer the folders and everything in them a different user on the same domain so that they are the ones sending emails and modifying files?
You need to go into the script and add a function that calls ScriptApp.invalidateAuth https://developers.google.com/apps-script/reference/script/script-app#invalidateAuth() then call any function like doGet using the new account to reauthenticate
How do I completely transfer the folders and everything in them a different user on the same domain
When you transferred ownership of your files & folders, everything in them did get transferred.
... so that they are the ones sending emails and modifying files?
However, the TRIGGERS that you had set up previously belong to your account, not to the associated scripts.
You have a couple of options for stopping those triggers from running as you.
Disable specific installable triggers.
If you still have access to the offending script, open it in script editor, go to resources > Current project's triggers, and disable the triggers. Done.
If you don't have access to the script anymore, open the script editor on any of your scripts, go to resources > All your triggers, and disable the triggers if you can identify them. This can be challenging if you use the same names for trigger functions in multiple scripts, as there is no indication here of which script they came from.
De-authorize the offending script.
As Zig's answer describes, if you still have access to the offending script, you can add a function to revoke authorization, and run it from your account.
Alternatively, you can see all scripts that you have authorized, and revoke access directly. Go to your Account Settings page, and select "View all" under Connected apps and services, or use this link.
Select the offending script, then click "Revoke access". Done!
To get the script running under the new owner's account, you need to repeat whatever steps set it up for you, including authorization from their account.