Checking group security in Power Apps

Published at Feb 22, 2024

#PowerApps #PowerFx #Dataverse #SecurityRoles #Security 

Table of contents

Introduction

This is a continuation of my post from last week, so please read Setting permissions in Power Apps using Security Roles if you haven’t done so yet.

The situation we ended with was a really elegant way to check if a user has specific security roles, but it doesn’t account for security roles inherited from a security group, so we’re going to implement that today!

Microsoft Entra Security Groups

Firstly, what’s an Entra Security Group? It’s a group that you would create to manage user access to shared resources, in our case that’s a canvas app.

Microsoft Entra Security Group Creation

Instead of having to share the app with individuals, and setting the security roles every time, this way we can manage the membership of this group and the members will automatically have access to the app and have the correct security roles.

Share canvas app with security group Allocating security roles to security group

The Power Fx code

We already have some code in our App.Formulas to check for the user’s individual security roles.

frmUserRoles = ShowColumns(LookUp(Users, 'Azure AD Object ID' = User().EntraObjectId).'Security Roles (systemuserroles_association)', "name");

Before we can check for their security group’s roles we need to get their security group. This code is very similar to the above, except this is extracting the names of all of the Teams they belong to.

frmTeams = ShowColumns(LookUp(Users, 'Azure AD Object ID' = User().EntraObjectId).'Teams (teammembership_association)', "name");

Now we can grab the security roles that belong to those Teams, by querying the Teams table in Dataverse.

frmTeamRoles = ShowColumns(LookUp(Teams, 'Team Name' in frmTeams).'Security Roles', "name");

We’ve got two separate lists of security roles, which is good, but we really want to merge them so that it’s one easy-to-use list. I could think of quite a few ways to do this outside of App.Formulas, but inside this property you’re quite limited. The approach I decided to take was this…

I decided to concatenate both lists into a comma separated string, which I could then stitch back together with the Split() function.

Split(Concat(frmTeamRoles, name & ", ") & Concat(frmUserRoles, name & ", "), ", ")

I now have a list that could look something like this:

System Administrator, Basic User, System Administrator, Bot Author, 

So it’s not too bad, but there are two glaring issues. Duplicates and the comma at the end. Let’s deal with the comma first. We can just replace it with an empty string.

frmSecurityRoles = Substitute(Split(Concat(frmTeamRoles, name & ", ") & Concat(frmUserRoles, name & ", "), ", "), ", ", "");

Nice. And as for the duplicates, that’s incredibly easy with a single column table.

frmSecurityRoles = Distinct(Substitute(Split(Concat(frmTeamRoles, name & ", ") & Concat(frmUserRoles, name & ", "), ", "), ", ", ""), Value);

That’s it!

But…

…there’s something very important you need to know in order to make sure all of this works.

Assigning all Security Roles permissions

Everyone who accesses the app needs to have read permissions on the Security Role, Team and User tables. If you don’t do this, Power Apps will not show your users any error messages; they just won’t be able to get past your security.

Speaking of which, please don’t forget to put error checking in your apps.

Permission to read securities from Dataverse

Conclusion

Now you can check the user’s directly assigned security roles and roles inherited from their security groups, all with a really simple line of code!

"System Administrator" in frmSecurityRoles

Here’s all of the App.Formulas code in one place, for easy copying.

// Get the user's directly assigned security roles
frmUserRoles = ShowColumns(LookUp(Users, 'Azure AD Object ID' = User().EntraObjectId).'Security Roles (systemuserroles_association)', "name");

// Get the user's teams/groups
frmTeams = ShowColumns(LookUp(Users, 'Azure AD Object ID' = User().EntraObjectId).'Teams (teammembership_association)', "name");

// Get the group security roles
frmTeamRoles = ShowColumns(LookUp(Teams, 'Team Name' in frmTeams).'Security Roles', "name");

// Get the user's security roles
frmSecurityRoles = Distinct(Substitute(Split(Concat(frmTeamRoles, name & ", ") & Concat(frmUserRoles, name & ", "), ", "), ", ", ""), Value);