I have a quick question about how you usually handle user and rights management in A12 projects. I’m referring to the standard setup where users are managed in Keycloak (or another directory service), and roles and permissions are maintained in the application itself.
Do you typically maintain an application specific user document (entity) which you can extend with whatever attributes are relevant for your rights?
Is there an easy way to users from Keycloak into the application’s user document model or vice versa?
The scenario I’m thinking of is probably pretty common:
- Managers should be able to see all orders.
- Warehouse staff should only see orders for their own warehouse.
- Customers should only see their own orders (this one is easy).
- The customer manager should see the orders of her or his customers (the association could be done by sales area, e.g the customer manager for Saxony sees only orders from customers whose invoice address is in Saxony)
This kind of access control feels like a typical use case, but I haven’t found a clear best practice or official guidance on how to implement it in A12. If there’s something already out there and I just missed it, I’d be grateful for a pointer.
hi @daniel-misty-packet
The replication Users from Keycloak to application’s user document model is implemented in A12 User Management service with bi-directional syncronization support out of the box for Keycloak implement as IDP.
After you have your User information + Objects which you would like to protect it’s important to express your use cases as follow:
- Managers should be able to see all orders.
- Managers is an attribute of logged user → let say it’s Role attribute with value is “managers”
- Default authorization in A12 which require annotation roles “Managers” is added to Order Document Models with access right DOCUMENT_READ and MODEL_READ
- Then User which has role “Managers” should see all orders.
- Warehouse staff should only see orders for their own warehouse.
- In order to identify a Warehouse Staff to be logged user you should have a dedicate attribute eg “user type” or even using Role of User called “warehouse staff” role.
- Writing an authorization rules to check if user login with role is warehouse staff
- Writing an repository authorization to return a template which select orders which has property Location (as identify warehouse).
- Append the repository authorization to existing query to select orders
- Identify edit or view action of “see orders” write same authorization rules to make sure User login has role “warehouse staff” only see orders which has property Location equals to User Login location. This would avoid bypassing query by access every order in the list with edit or view.
-
- Customers should only see their own orders (this one is easy).
- In edit or view action of Order → Identify user name of user login and write authorization rules which make sure userlogin.username == order.createdBy.
- In query action of Orders → write respository query to make sure the query goes to Order list will append “where order.createdBy == userLogin.userName” where userLogin.userName is a template and will be resolved as userName when evaluated so you can have “where order.cretedBy == dbrodkor” as final query resolved.
- The customer manager should see the orders of her or his customers
- Identify what does it mean “customer manager” → I assume it’s a dedicated role attribute of user which has value "customer manager.
- Within Order object you should have an attribute called “customerName”
- After identify the user login has role Customer Manager → you need to load all Customers which are beloging to this user and put it in an attribute for further authorization check called “customerNames” as a list of string.
- Write an repository authorization condition to always append security query “order.customerName IN #customerNames” where #customerNames is resolve before which is list of customer names which are belonging to User Login which has role “Customer Manager”
According to your 4 use cases → out of the box RBAC from A12 Data Services (to my knowledge) can be only applicable for your 1st user case, the other 3 use cases requires writing more ABAC rules and using Resource, Repository Authorizations from UAA.
Hopes this helps.
Tuan Do
One of the key questions that spring to mind is: how do I handle the users in an A12 application? In the standard setup Keycloak acts a user directory, but in my example I need the users locally in my application as well to e.g. relate them to the warehouse. One solution could be to have a “user” document model (entity) in my application which I can then enrich with additional attributes or use on my model graph with relationships, in this case to the warehouse. Additionally, it is very likely that an application has a mix of application (technical) roles (system admin, normal user, …) and business roles (warehouse worker, regional manager, …) where the roles plus one or more additional attributes related to the user need to be interpreted in order to provide the correct access level.
This opens to generic topics, which I believe should be solved by A12 or we should at least offer a “best practice” solution:
- Do we need to synchronise users from Keycloak (or any other IDP) to the A12 application? If yes: should be provide out-of-the-box functionality to handle this?
- How do we handle the mix of technical and functional (business related) roles? Do we use the A12 concept of roles and extend it per project?
A classical case of not having read the first line of Tuan Do’s response, where he writes that the users need to synchronised. Sorry, Tuan Do!
Having followed up with Tuan Do on the phone we agreed that it would be a good idea to implement the standard user synchronisation in the project template to make to make it available to everybody.