Update Local $roles after changing role using GrantRole on server

Update Local $roles after changing role using GrantRole on server

  

After getting some help with this question (https://www.outsystems.com/forums/discussion/37920/role-based-menu-and-homescreen-in-mobile/) I have implemented a menu system where certain links are hidden or displayed based on an If block.The If condition uses a local IsTenant parameter in the menu. In the OnInitialize event of the menu I run a CheckRoles action which fires the following javascript and sets it to the local parameters:

In another screen a user can be granted the Tenant role, here using the MakeTenant2 action:

Inner flow of MakeTenant2:

I found that after granting the Tenant role using this action the menu event CheckRoles was still firing every time the menu was displayed, but that the $public.Security.checkIfCurrentUserHasRole($roles.Tenant) would not be updated, and therefore the incorrect links would be displayed, as though the role had never been granted.

My attempt was to try and set this manually using "$roles.Tenant = true;" in the JSMakeTenant node, but this was clearly incorrect. What is the best way to update the client-side $roles objects after granting a role on the server? Is it as simple as calling another server action with CheckTenantRole in it? Or do i need the user to log out and log back in again to be able to say that the user has the Tenant role.

I am stuck currently because in my Mainflow above you can see my endpoint is Mainflow\HomeScreenTenants which is restricted at the screen level by role to Tenants. After going through the action above to grant them the Tenant role and redirecting them to the Tenants only screen I am met with a permissions error.

So in essence I have two issues, one with the menu which I am trying to solve using $roles, and one with a role-restricted screen which I am yet to work out a solution.

In summary: How can I grant a role in a mobile application and then immediately give that user permissions to screens and links?

Hello Russell,


I read your post and you say this: "In another screen a user can be granted the Tenant role, here using the MakeTenant2 action"

Does the user grants the role to himself? Or to others?


I believe that $roles.Tenant has the same purpose as Roles.Tenant in the server-side actions. It's more an Identifier than a "property" itself.


I'm not 100% sure if what I'm about to say is correct, but I believe that you can't change roles that easily on client-side. That could pose security issues, at least. 


Let's see if you can this information or if someone else can help better :)


Cheers

Hi Armando, for clarity, yes the user that is currently logged in clicks a button and it grants that same user the Tenant role. (In future this would be based on an authentication process based on business logic, but for now I am just trying to grant the role and show or hide links and screens as a proof of concept).

In that case, why don't you create a different user with the role, then? Maybe would save some headaches :)

Hi Armando, I'm not sure why creating a different user would help.

If Alice registers for my app, and then goes through the business logic to become a Tenant, it is Alice who needs to be a Tenant, not another user.

If you're just trying to test if the menu links appear - or not - depending on the roles, you can either:


- Use Bob to grant the Tenant role to Alice through the app, or

- Go to Users (if using that as a user provider) and manually grant the Tenant role to Alice.


Either way, upon the login of Alice, she would have the Tenant role and you could verify if the links are shown as expected.


Cheers!

Hi Armando, I have probably confused the issue slightly with my question. I have already proved the concept of links appearing or disappearing if a user has a particular role. that is working fine.

I am trying to get the user to grant themselves a role in the app (by calling the MakeTenant action flow), and at that point of granting the role I want the checks to me made so that the app now realises that the user has the Tenant role.

I think at this point without any other answers I will be reduced to forcing the user to log out and log back in again.

Hello again,


Have some news for you. After doing some tests, I came across a possible solution for you. The scenario:

- Registered user (Neo)

- Custom role A (AdyenPayment)

- Home screen for application, accessible to Registered and AdyenPayment role

- Button to Grant/Revoke permissions

- Link to screen only accessible to AdyenPayment role.


What I do:

- I "Fetch data from other sources" which only retrieves if the user has the AdyenPayment role or not:

- On the home screen, I set a button with an expression as the name, with the following condition:

If(DataAction1.HasRole, "Revoke", "Grant")


- Then, the client action for the button has the following logic:

- The grant and revoke actions only invoke the Role default actions:

- On the previous point (client action), I'm refreshing the data. This means that anything affected by the output (if the role is granted or not), will be refreshed. Initial state:


If I click on SampleScreen link:

Clicking on "HomeScreen", the state is still the same:

Upon clicking on Grant:

And then, finally, clicking on SampleScreen (empty screen, really) link again:


If you revoke again and go to sample screen, it will thrown the invalid permissions screen.


So, once you grant the role, apparently your app updates on the background. Possibly, someone from the Engineering team could enlighten us on how it works on the background.


Hope this helps,

Cheers!


EDIT: this is using one forge component (Adyen Plugin Sample App) that I usually use as a sandbox... don't mind the "random elements" :)

Thank you for looking at this in such depth. The idea of your flow seems to be mostly the same as mine, apart from the RefreshDataAction node, which I do not have.

I will look at moving my CheckRoles action into the FetchDataFromOtherSources section, and refreshing the action after I have made a change and see what it can do. I suspect I may run into an issue as my CheckRole from other sources will be in the menu, and I will be granting the new role from a different screen/action, so I might not have access to the refresh action, but it's certainly a start, thank you.

Why don't you have an input parameter on your Menu block (isTenant) so that when you refresh your data action on the screen, the menu block will be refreshed as well?


Cheers!

I can put an input parameter of IsTenant on my menu block, but the menu block is only on the Layout block, so how can I then access my Layout block from a screen to refresh this parameter?

You give me problems, I'll give you (theoretical) solutions :)


What I did: Layout block with a new input parameter, "ShowMenuPlaceholder", Boolean, Non-mandatory, Default as False.

Then, on the Layout block, Search for Layout (block) > layout (container) > menu (container). Here, put an if condition using our new input parameter. Then, put a placeholder inside your true branch. Put the menu block on the false branch.


Then, on the screen that you would like to use this dynamic thing, just make the parameter on the layout as True and then put the block menu on the placeholder. When the value is False, the menu will still appear. I've added a text in the placeholder and set the menu behavior on purpose.

Add an input parameter on the menu block and all the required logic for role validation. Upon refreshing the query (fetch data from other sources), if the menu uses that input parameter, it should be refreshed automatically.


Disclaimer: I've only tested up until the if/placeholder/block thingy. The rest is theoretically the expected behavior.

Armando, I want to thank you for your instructions and advice so far, but I'm still struggling.

I've uploaded an .oml file attached to this post to show you where I've got to so far with adding input parameters to the layout block and the menu block.

At the moment there is a default home screen for the app which runs a CheckRoles action, and OnAfterFetch this uses a client action to redirect them to the correct screen. Each screen is restricted to the relevant role of guest, registered or Tenant. The registered screen will allow you to grant the tenant role, and send you back to the redirector screen for redirection to the tenant screen. On the Tenant screen you can revoke the role and it will send you back to the redirector screen, to redirect you to the registered screen.

However, when revoking the role and granting it again, the CheckRole function says the user HAS the Tenant role (and redirects them to the Tenant screen), and the Tenant screen says there are not the right permissions to access that screen! In neither case can I get the custom link on the menu to appear, although I can get it to fire a feedback message saying that the parameter has changed.


Hello Russell,


I've used your OML and made some changes. Please see the attached version which, I believe, fixes the issue you reported.


Let me know if you need further help!

Hi Armando,

I have downlaoded and been through the OML you attached, and thank you for your time. I see now what you meant about a menu or a menu placeholder in the If Statement, and granting and revoking the role makes the link appear or disappear.

However, it looks like on the screen only available to Tenants you have set the ShowMenuPlaceholder to True (hard-coded). Perhaps I have misunderstood, but consider now the scenario where there is a screen that is available to all registered users of the app. On that screen, when accessing the menu a Registered user should not see the custom link in the menu, but a user with the Tenant role SHOULD see the link. To dynamically determine this I would surely have to CheckRoles in the initialize of that page and then send through a varable instead. At this point though, I'm now calling server actions on every screen of the app to determine if the links should be visible based on the role, and surely I could move that server call to the menu itself.

It seems almost like the wrong way about things to have a server call in the menu as it slows down the app, but I simply can't understand another way to dynamically check link visibility in the menu.

I think that the role is cached until the user next logs in or out, so I may be at the point of when granting the Tenant role, that I force the user to log out and log back in again so that the next time it checks the roles it is not cached?

Solution

Russell Codd wrote:

Hi Armando,

I have downlaoded and been through the OML you attached, and thank you for your time. I see now what you meant about a menu or a menu placeholder in the If Statement, and granting and revoking the role makes the link appear or disappear.

Hopefully next time I will be able to explain myself better. :)

However, it looks like on the screen only available to Tenants you have set the ShowMenuPlaceholder to True (hard-coded). Perhaps I have misunderstood, but consider now the scenario where there is a screen that is available to all registered users of the app. On that screen, when accessing the menu a Registered user should not see the custom link in the menu, but a user with the Tenant role SHOULD see the link. To dynamically determine this I would surely have to CheckRoles in the initialize of that page and then send through a varable instead. At this point though, I'm now calling server actions on every screen of the app to determine if the links should be visible based on the role, and surely I could move that server call to the menu itself.

I've marked as true just "because reasons". It was just to show how it would work  since you were missing the placeholder.

It seems almost like the wrong way about things to have a server call in the menu as it slows down the app, but I simply can't understand another way to dynamically check link visibility in the menu.

You have to make a design decision here. Either you assume that roles are really volatile and you go to the server to check if the role has changed or you just assume they are not that volatile and, upon login or upon the application being ready - or similar -, you just check the roles and put that on local storage as "cache".

I think that the role is cached until the user next logs in or out, so I may be at the point of when granting the Tenant role, that I force the user to log out and log back in again so that the next time it checks the roles it is not cached?

I'm not going to say 100% that it is not because I've tested this only on browser. What I did: 

On app, incognito: booted the app in PreviewInDevices. Logged in. Granted Tenant role.

On Users eSpace: Logged in, searched for user, revoked the Tenant role.

On app, incognito, without reloading: went to menu, Home Screen Redirector.

The result was the "Grant Tenant Role" screen.


Does this help?

Solution

I think that you've hit the nail on the head with the business decision needing to be made about whether we decide if roles are volatile or not, and how we approach as a result.

I think we're going to have to lean towards a short caching of the role, and forcing users to log out once a new role is granted, or store variables in local storage about what they can see and not see and redirect to screens based on that rather than the screen level permissions. It will mean that if something changes on the server end, the app will be 'out of date' but I guess we can always recheck any permissions on the server side for each necessary eaction anyway.

I will mark one of your posts as the solution, as I think this is probably beyond the realms of what OutSystems is capable of, and you've been a great help. Thank you for your time.