If the app knows whoโs using it, then I can easily control many different aspects of the app:
To accomplish this functionality in your app, you need the following:
Details
Create a table for your app that will hold information specific to the user. This table must contain at least the following:
My rules for Table Keys
Your User table can contain any other information about the user you may need; for example:
Why?
In order to restrict when certain things should be allowed or not, the app has to have some way of differentiating one user from another. While there is another way to accomplish this, requiring someone to sign in is the simplest of them all.
By requiring people to sign into the app before they can use it, youโre ensuring the when you use the function USEREMAIL() - there will be an email in response, and from this we can base permissions and such.
Explanation
What I mean by this is that for each user accessing your app, you need to have a corresponding record in the User table for that user.
If you do not have a record for the user, but youโre going to allow users to access the app anyways, then you need to conform your app around that eventuality - handling what happens when someone logs into the app but doesnโt have a User record.
Many of the techniques Iโll describe below (on how to use this system) depends on there only being a single record inside the User table for each user.
Create a new slice of your User table
Inside the condition formula space, add something like this:
USEREMAIL() = [User_Login_Email]
(Optional) Remove the โAddโ permissions from this slice (keep edit and delete, if you wish for users to able able to have those permissions for their User record)
Now that youโve got everything setup and put in place, youโre ready to actually pull information from the Current_User slice and use that in conditions throughout your app.
To pull a value
INDEX(Current_User[User_Role], 1)
ANY(Current_User[User_Name]) - though I suggest INDEX() in preference to ANY()
To pull a list
SPLIT(CONCATENATE(Current_User[Related Timesheets]), " , ")
SPLIT(CONCATENATE(Current_User[User_Assigned_Clients]), " , ")
To restrict something for Admin role only
INDEX(Current_User[User_Role], 1) = "Admin"
To restrict something for Admin & Team Leader roles only
IN(INDEX(Current_User[User_Role], 1), LIST("Admin", "Team Leader"))
To see if a Client ID is inside the assigned clientโs list for the user
IN([ClientID], SPLIT(CONCATENATE(Current_User[User_Assigned_Clients]), " , "))
There are an endless amount of example formulas to give, but the oneโs Iโve highlighted above provide instructions on how to handle both values and lists in some common scenarios.
This technique of isolating out a record in a slice can be very helpful when you want to conform the UX around a specific context or situation the user might be in.
Thanks again. I canโt get this to work to save my life. The current error Iโm receiving is "
Table โCompaniesโ has an invalid update mode expression โ=IF(INDEX(Current_User[User_Company_Link], 1) = [CompanyID], โALL_CHANGESโ, โREAD_ONLYโ)โ. Unable to find column โUSER_COMPANY_LINKโ
The Userโs table is a single ref column to the companies table. I have not tried implementing any of these formulas on the work orders table for the companies, since I cannot get it to work on the companies table yet.
I have also tried changing the companies column to EnumList of refs and using your recommended formula.
I appreciate any help. I have been at this specific problem all week and I feel as though something this simple should not take this long.
Hey @rmsmeltz
The problem youโre having is that youโve just copied the sample formula I provided; youโll need to change the column names so they match whatever youโve got in your system.
So in the formula, [User_Company_Link] should be replaced with the column name that IS the ref column to the Companies table.
I tried it that way at first, then I changed the names of my columns and types to match what you showed me in the example so it was easier. It still gave me that error message.
@MultiTech_Visions Iโm watching the extended version of your video on Patreon. I donโt think I have my users and companies tables set up properly. Can you provide any more in-depth guidance on how to make sure theyโre set up correctly?
Hmmmโฆ at this point it might be best to move this into itโs own discussion, instead of at the bottom of this Tip & Trick post.
You canโt specify the Update Mode per record like this. Here you are setting it for the entire Table. If you need to specify it per record beyond this, then do so by filtering down in a Slice, and setting the Update Mode of the Slice.
The expression assistant is not infallible. Sometime the reported error is not actually the real problem. This may be one of the cases. To double check this exact error, just try to set some temp VC expression in some Table to
Current_User[whatever-column-name]
You know @Marc_Dillon youโre about not being able to do things on a per-record bases here; I canโt believe I missed that. lol I have no idea what I was thinking.
Honestly, Iโve actually taken to controlling edits and things on the Action side of things, I rarely use the actual table-updates permissions anymore.
And with actions, you can control them on a per-record bases.
So I just put my โaccessโ permissions formulas inside the individual actions for each table nowadays.
Now to figure out a way to clean up this post! haha
Matt, but please remove the โbackdoorโ because that is inviting a security hack. Nobody should allow user impersonation that way in their apps. USEREMAIL() is something the platform controls and not hackable. Having a lookup table is not just a backdoor but really a frontdoor also :]
Speeking of hackers, could you do a webinar about how secure appsheet is in terms of cyber security? And ways for us as creators to know what to do to make it safer
Does "backdoor' refer to creating a slice to return the current user and then using something like ANY(CurrentUser[ID]) to filter or limit the app instead of just relying on USEREMAIL()? If so, how do you suggest handling our data when a user changes their email?
Hi
How do you actualy use it with views/slices? Do you add this condition to any slice with AND()?
Yes you can do this, just keep in mind you wont be able to tell who did what - unless you build in some way for each user to differentiate themselves from the other.
Hi, Iโm trying to use this - How do I use this, but with the role column or similar column being an Enumlist, and it should check if a particular value of a role type is in that Column for a user?
Iโve tried tweaking this formula
To restrict something for Admin & Team Leader roles only
IN(INDEX(Current_User[User_Role], 1), list(โAdminโ, โTeam Leaderโ))
For example, Iโve tried:
IN(INDEX(Current_User_Slice[user_permissions], 1), list(โall_permissionsโ, โshow_cost_column_table_current_stocksheetโ, โedit_cost_column_table_current_stocksheetโ ))
Iโve also tried:
IN(INDEX(Current_User_Slice[user_permissions], any), list(โshow_cost_column_table_current_stocksheetโ, โall_permissionsโ))
@Muhammad_Saloojee When comparing two lists, you have to get a little more clever.
IsNotBlank(Intersect(
Split(Current_User[User_Roles], " , "),
List("Option1", "Option2")
))
This will produce true
when there are matching elements in each list.
Thank you. Iโm trying to get it to workโฆ Itโs not quite working thoughโฆ Playing with the spacing between the quotation marksโฆ Itโs either hiding them from users who itโs not supposed to hide, or showing them to all users all together when i change the quotation marksโฆ I have the expression written as this:
IsNotBlank(Intersect(
Split(current_user_slice[user_permissions]," , "),
List(โshow_cost_column_table_current_stocksheetโ , โall_permissionsโ)
))
As you can deduce, I have: a user table, current user slice, a column in the user table called " USER_PERMISSIONS" instead of user role. In this way i wish to create an easy way for managers to add or remove permissions like how they would with check boxes in other apps.
The user table has a column that i want to allow various different permissions forโฆ For example:
all_permissions , show_cost_column_table_current_stocksheet ,cost_show_current_stocksheet , cost_edit_current_stocksheet , cost_delete_current_stocksheet , cost_edit_current_stocksheet , show_user_table
hmmmโฆ @Muhammad_Saloojee Looks like it should work
Perhaps pulling out the value from the Current_User_Slice would work:
IsNotBlank(Intersect(
Split(INDEX(current_user_slice[user_permissions], 1), " , "),
List(โshow_cost_column_table_current_stocksheetโ , โall_permissionsโ)
))
Aside from that, you might change the SPLIT() delimitator: try just using a comma (no spaces)
concatenate()
SPLIT(CONCATENATE(Current_User[List]), " , ")
This first pulls the list-of-a-list, then converts that into a string (via concatenate); then split can process that string to give you your actual list.
How do I just restrict table data to a specific user once I have a Current_User slice? (What security filter do I use?)
In the table that you want to restrict, you should add a column that includes the email address of the person you want to restrict it to. Make this column hidden.
Then in the security filter of that table, use something like =[THE_EMAIL_COL]= INDEX(CurrentUser[Email]).
Or better yet, bake it toniser settings as described in my next post below.
I have saved many hours of implementing currentUser setup by following what @SkrOYC suggested in the tips and tricks section HERE
It is combining a currentUser slice with the User Settings. However, instead of using Initial Values in the User Settings, I use the App Formula instead.
The idea is this, instead of using ANY(currentUser[Column]) or INDEX(currentUser[Column]) to refer to some columns in the current user, do the ff instead.
Create Columns in User Settings for each of Current User slice fields you will use throughout the app.
For example, if my currentUser slice will have [Emp ID], [Role], and [Email], create those columns in User Settings as well.
Next, assign app formula for each of the User Settings columns as follows:
[Emp ID] uses INDEX(currentUser[Emp ID], 1)
[Role] uses INDEX(currentUser[Role], 1)
[Email] uses INDEX(currentUser[Email], 1)
Now, whenever you need to use it, simply use [_THISUSER].[EMP ID] which is equivalent to using INDEX(currentUser[Emp ID], 1)
if you use the QREW tools extension from AppSheetTraining. com, the autofill will greatly save your time typing vs uaing the ANY() or INDEX() approach.
Anyone have this or the advanced version setup in a sample app that I can take a look at? I'm new to AppSheet and having a bit of trouble pulling this off. I really want to set my current and future apps up so that only users who signup can have access to them.
Fรผrs erste reicht es dieses einzurichten:
Erstellen Sie einen neuen Slice Ihrer User-Tabelle
Fรผgen Sie innerhalb des Bedingungsformelraums Folgendes hinzu:
USEREMAIL() = [User_Login_Email]
I found using this method seems to make my Sync time out. Why might this be happening?
Dear Matt,
Security filters are recommended as a way to scale an app, provided that the expression used to filter the rows doesn't destroy performance. I read on that forum that functions like SELECT() or IN() should be avoided. So how about your recommended approach with INDEX()? If I filter one of my tables like that:
OR([Author]=USEREMAIL(), INDEX(Current_User[User_Role], 1) = "Admin")
to give the administrator full visibility on the records, would that affect performance on a large table ?
Thanks
New AppSheet user/dev here. The tips/articles/comments written/shared by experts like @MultiTech , @Steve in this Forum are exceptional With that being said, this particular post is > 4 years old now and it would appear AppSheet has matured quite a bit since then, thus as I dig into User management, my inquiry here is:
Does the new way Users are managed (via the Share feature) impact this topic, ie, the use of a User table/list, and more specifically, identification (via column) of user Roles?
Nope, @MultiTech's method is still valid and very useful.