Testing Power Apps with Role-Based Security

When we build apps (or applications before them), we often build different behaviors or capabilities based on the role of the current user. For example, when I ask for some time off, my manager can approve it, but I can’t.

In a Power App, there are many different ways to implement role-based permissions. I’ve put a few articles into the Resources below showing some approaches.

See the source image

Testing the Power App by playing the different roles can be very hard. Each user who loads the Power App is themselves, right?

Well, here’s a trick I’ve been using to make things much easier for testing.

Here are the high level steps:

  • Set up the roles using whatever mechanisms you choose. As a SharePoint guy, I like to use lists wherever possible so it’s easy to maintain the roles over time.
  • In your Power App’s initial screen, set up logic to determine the current user and set variables to reflect that user’s role(s) at an app level. (You’re likely to have additional roles when you get deeper into the app, perhaps working with specific items. This approach works there, too!)
  • Add a Combo box to the screen which allows you to select any user.
  • Switch the current user to the “assumed user” when you want to test with that user and their role(s).

Determining the current user is easy: we just use the User() function. But the User() function isn’t really complete enough. I always then define a second variable for currentUser by getting their Office365Users.UserProfile. This requires the Office365Users connector.

When I create a new Power App, one of the first things I do is add these lines to the OnVisible property of the initial landing screen:

// User 
Set(thisUser, User());
Set(currentUser, Office365Users.UserProfile(thisUser.Email));

Between User() and Office365Users.UserProfile, this tells me most of the base information Microsoft 365 knows about the user. Yes, I could use Office365Users.MyProfile() to get the user’s profile information, but then this testing trick wouldn’t work. By using Office365Users.UserProfile and passing in an email address, I can get anyone’s user profile.

Add a Combo box to the screen and rename it to ComboBox_AssumePerson.

Set the Items for ComboBox_AssumePerson to:

Office365Users.SearchUser({searchTerm:ComboBox_AssumePerson.SearchText,top:10}) 

For the OnChange property, add code similar to the following. Here, I’m setting two variables to the selected user from ComboBox_AssumePerson: assumeUser and the currentUser we’ve already defined.

Set(assumeUser, ComboBox_AssumePerson.Selected);
Set(currentUser, ComboBox_AssumePerson.Selected);
Set(returnToLanding, true);
Navigate(PermissionCheck, ScreenTransition.Cover); 

Because of the way we’ve set up the Items for ComboBox_AssumePerson, on selection, we get an object which is equivalent to our call to Office365Users.UserProfile: same type of object, with the same properties.

Now change the last line in the // User code above to assume the selected user if there is one.

Set(currentUser, If(IsBlank(assumeUser), Office365Users.UserProfile(thisUser.Email), assumeUser));

Because I’m setting all the role information in the OnVisible property for the landing screen, just changing the currentUser isn’t enough. To make the change “stick”, I’m navigating to another screen called PermissionCheck which looks at the returnToLanding value in its OnVisible property, and if it it set to true, it navigates right back. That means the role-based calculations all happen again, but this time in the context of assumeUser.

If(returnToLanding, Set(returnToLanding, false); Back()); 

By the way, that PermissionCheck screen can be really handy, too. The only thing going on in that screen is to show the current user’s roles. I tend to set variables like isAdmin, isManager, isExecutive, or isContactTracer on the landing screen. The PermissionCheck screen shows me those values for the current or assumed user whenever I need to be sure what they are. It doesn’t need to be pretty, but by displaying your role-based variables, you have an easy way to check your logic.

Now you can wander around your Power App as the user you have selected and the permissions you have determined for that user.

How do we undo that user assumption? We simply clear the ComboBox_AssumePerson, which fires its OnChange property. Then this line from above sets things back to the real user who is using the Power App because assumeUser no longer has a value.

Set(currentUser, If(IsBlank(assumeUser), Office365Users.UserProfile(thisUser.Email), assumeUser));

Caveats

As with most things, there are some very clear caveats to this approach.

  • When you assume another user’s identity in the app, you’re still you beneath the covers. That means any items created in lists will still be created by you. You’re only able to assume the other user’s identity in the Power App itself.
  • Everywhere you figure out the user’s roles, be sure to use currentUser.Mail – NOT thisUser.Email – to match users from your permission structures. There’s an example of this in my previous post Power Apps: Filtering by Multi-select SharePoint Columns, where I check the user in Section 3.
  • Be sure you have some indication on your screens showing who you are while you’re running the Power App. I think this is a good practice, anyway, but it’s especially important when you have this capability set up. (Who am I again?) I usually have something like this in the upper right of every screen.
  • Some logic may not work with this approach. For example, I can display the User()‘s photo, but not the currentUser‘s.
  • For debugging – even after you launch the Power App for production use – you may want to leave this “back door” in place. But be sure to set the Visible property for ComboBox_AssumePerson so only someone in a role you consider an admin can use it. And make sure they understand it!

Resources

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.