In typical smaller Appian applications, we use a static group setup. Assuming we have some process roles like Manager, Agent and Compliance, we then create a group for each role and use these groups for assignment of tasks and to define security.
But when designing a more complex application that requires dynamic group management including rules-based record security, it becomes a bit more challenging. We have to create new groups dynamically,and we also need a way to lookup groups by specific attributes.
In this post, I want to discuss the aspects of such a complex group setup, design considerations, options, and my pattern of choice.
Think of a group you aim to use for assignment and security. We call it A-Team (“A” like Appian, not the other one from the 80s). This A-Team belongs to a department, which belongs to a unit. There are other Teams, as well, which belong to the same or other departments. The Team is also responsible for a specific territory, has a specific role in the process and accompanies a specific function within the organization.
These four dimensions, or categories, define the exact coordinates of our group. This means that we need to store these coordinates with these groups to be able to identify them.
Let’s have a look at the options.
Coordinates in the Database
Relying on the database seems to be the most familiar approach. We create a table holding the group ID and any additional attributes for any newly created group.
Then we do a simple query to find the right groups when assigning tasks.
But what about security? I store the group I want to permit access to a record in the record itself. Then I define a security rule (https://docs.appian.com/suite/help/latest/record-level-security.html#security-rules) in the synchronized record to allow access to members only. This is simple, but what do I do when I want to give permission to all groups of a certain type or coordinate value?
To use dynamically created groups for security purposes, you need to create higher level groups and add the new group as members. For example, you create a group for a specific territory, and then add all groups belonging to it.
With Appian, you do not have to store everything in the relational database. Let’s see how this could work.
Coordinates in Group Type Attributes
The Appian platform supports custom group types (https://docs.appian.com/suite/help/latest/Group_Types.html). A group is always of a specific type, which cannot be changed after creation. Group types support creating custom attributes we can use to store our coordinates directly with each group.
There is one big issue in terms of scalability, though. We cannot query groups by these custom attributes. The only option is to query all groups of that specific type, and then filter for the attributes using the getgroupattribute() function. It would be fine for a few groups, but it definitely doesn’t scale well.
Then, what do you do about using these groups for security purposes. Similar to the database option, you have to create higher level groups, adding newly created groups as members.
This solution keeps all the data inside Appian, but does improve the security aspect and has a scalability issue. I think we can do better.
Coordinates using Group Membership
I already mentioned that we need some kind of higher level groups used in Record security rules. Why not set up a group structure, allowing us to directly use group membership for storing coordinates, identify specific groups and use them for security purposes?
You will need to set up one level of static groups, one for each dimension. In my example, this would be:
- All Department Types
- All Territories
- All Roles
- All Functions
The next level can be static, or dynamic user created groups, depending on your requirements. Create your “HR Department” group and add it to “All Department Types”. Allow a user to manage territories and add all territory groups to “All Territories.
Keep in mind that, in Appian, defining a group parent is about inheritance of group security and includes membership. Membership alone does not define the security of a group. As a group can only have one parent, use membership only to avoid confusion.
To get a list of all the groups for a dimension, use the a!groupMembers() function. Make sure to set the parameters as follows:
- direct: true
- memberType: “GROUP”
After you created all the necessary coordinate groups, create the last set of groups for each combination of coordinates you need. This might become a larger number of groups, but no worries, groups in Appian are cheap. Malcom Ross said that to me a while ago while we had a conversation on certain design decisions in Appian regarding scalability. The a!groupMembers function has a limit of returning a maximum of 10k groups per call.
You now should have groups like
- HR Germany Manager Onboarding
- IT Italy Admin Data Center
- CC Japan Head Loan
or any other combination of coordinates you require.
The last step in setting up this group structure is, to add each group as a member to its relevant category group.
“HR Germany Manager Risk” becomes a member of the following groups
- Department: HR
- Territory: Germany
- Role: Manager
- Function: Onboarding
When creating the data structure for a record, add fields to store the ID of the lowest level group whose members should have access. Depending on your requirements, you might only need one field. Multiple fields allow you a more fine-grained permission model.
You can define that a case can only be accessed by people in their respective country. If you need to change access along the process, modify the stored group IDs in the record in the process flow. You can even define a group which is a member of all the European countries to summarise permissions.
Then define your security rules as documented here: https://docs.appian.com/suite/help/latest/record-level-security.html#users-found-in-fields
Finding Groups for Assignment
To dynamically assign a user input task to one or more groups, you will have to find groups matching your specific coordinates. I assume, we have a case of a high-volume loan request in Japan. Let’s write this down in words first.
Assign the task “Approve Loan” to the head of loan controlling in Japan
The country comes from the field “countryGroup” stored in the case. The “head of” is coming from a decision checking the requested volume. Loan and controlling are defined by the specific step in the process.
Translating this into our coordinates, this becomes:
The group must be member of the CC department AND the Japan territory AND the Head role AND the Loan function
Now, how would you query the group in this 4-dimensional space?
Thanks to Georg Cantor, we have mastered set theory 150 years ago. You now form 4 sets, each of which represents all groups that belong to one of the selected categories. The intersection of these sets results in exactly those groups that meet all criteria.
Have a look at this visual representation:
And now a small code example:
a!localVariables( /* All groups member of a given department */ local!departments: togroup(a!groupMembers(ri!department, true, "GROUP").data), /* All groups member of a given territory */ local!territories: togroup(a!groupMembers(ri!territory, true, "GROUP").data), /* All groups member of a given role */ local!roles: togroup(a!groupMembers(ri!role, true, "GROUP").data), /* All groups member of a given function */ local!functions: togroup(a!groupMembers(ri!function, true, "GROUP").data), /* INTERSECTION */ intersection( local!departments local!territories, local!roles, local!functions ) )
An interesting detail is, that this might return more than a single group. Appian is happy assigning a task to a mix of any number of users and groups. And you can allow your application users more flexibility when creating groups.
To have an idea of how this performs with a larger number of groups, I created 5000 groups and put them into one of eight groups for each dimension. Calculating the intersection takes about 50-100 milliseconds on an Appian Cloud instance of the smallest size.
Just like Dr. Strange, I don’t feel so good after a trip through multidimensional space. But I apologise for dragging you through this because a proper model for dynamic groups and permissions is, well, a complex matter.
At the beginning of this post, I promised you my preferred solution. While all options have some pros and cons, I think that the last option is the one that fits best to the way Appian works. And it serves all our requirements by nature.
Users can dynamically create groups and assign them to any number of dimensions. We can use the groups to define security on any level we need. And, last but not least, we can easily look up groups for task assignment purposes.
Tell me in the comments below what you think and about your experiences implementing complex group and permission models in Appian.