Google App Script and PropertiesService.getUserProperties() - google-apps-script

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.

Related

Scripts are not executing for users with reader permission

I have added an image on my grid, and assign script to it. But script executes only when I click on image, and when another user(with reader permission) clicks on image nothing happens. The same situation is with onOpen() trigger - nothing happens when user whith reader permission opens the grid. I understand that editor permission will fix it, but I don't need the user to be able to edit the table, I only need that user be able click buttons(image)/get alive triggers/menus that i create, but not edit anything manually. How can i do this(may be i should give some extended reader permission, or to turn on something in GAS project)?
When users execute a script for the first time, they will get an allow permissions prompt, so they can authorize the script to make changes on their behalf.
However, if the user has viewing permissions over the sheet or file, they cannot authorize the script to run or make changes on their behalf since they cannot make changes to the file. (I found a similar question, with the difference that the sheet was embedded in a website that has some information about why it does work here).
And it's documented in the Apps Script documentation here:
An installable open trigger runs when a user opens a spreadsheet, document, or form that they have permission to edit.
You can also request a feature request to Google asking for an option to allow users with "viewing permissions" to run scripts here.
In this case I think you may give editor's permissions but protect the sheet/range so only you can edit them with this option -->
https://support.google.com/docs/answer/1218656?hl=en&co=GENIE.Platform%3DDesktop#zippy=%2Cprotect-a-range-or-sheet
Let me know if this worked!
Option 2:
Adding to those protected ranges you can too create a Library with your functions in another spreadsheet (or in Google App Script independently) -> read about it here
You can now set buttons/menus associated with those original functions, but I think that this can give you an altenative
For example:
function function1_toOrigin() {
LibraryName.function1()
}
function function2_toOrigin() {
LibraryName.function2()
}
Yes, they'll be able to access to these linking functions but nothing more, I think they won't be that unreliable??
IMPORTANT: If there are modifications to the script you should deploy them again as a new version of the library and update the version in your spreadsheet by double-clicking in your library

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.

Allow script to edit locked cells

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

Google App script: You do not have permission to call prompt

I have created a google script that show me a prompt dialog to write a comment when a column is edited. For some reason this only works with my email (script's creator), but with the other users that I have shared the spreadsheet don't work. When I open the script editor with other user accounts I can see the error in the View -> Execution Transcript: Execution failed: You do not have permission to call prompt.
My script function has a name "sendManualEmail" and I already have the trigger created when Event -> From spreadsheet -> On edit
I even created a new project for only that script and asked me the permissions to send emails with my account, but still not working for other users. I have read some other similar topic with same issue but I am still not able to fix mine. So thanks in advance for any comment!
You need your users to authorize your script. To do that, create a menu that activates on onOpen(). When clicked, send a message box, ensuring your users have to authorize your scripts to see the message.
Paste the following at the top of your script:
/** #OnlyCurrentDoc */
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('myName') // your name
.addItem("Activate myName's script", 'activateMyStuff')
.addToUi();
}
function activateMyStuff() {
browser.msgBox('Script is activated! thanks.');
}
Important: when your users click the menu, they will also be prompted to authorize your scripts and all the permissions on the script's page. Make sure you clean up that script, otherwise your users may have to authorize weird things - and likely won't. Do test it with an alternate email address to see what others will see.
Lastly, consider publishing your script as an addon instead. It will make it that much easier for your users to authorize and use your work.
Are you logged into multiple Google accounts in the same browser?
Google Scripts insides may sometimes not work as expected when multiple accounts are authorized in the same browser session. Try logging out of all accounts except the one that owns 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?