Write articles carefully and share with your heart.

Personal website: Yasinshaw.com

Public number: XY’s technical circle

In the last article “system permission design – basic concepts and ideas”, introduced some points we need to pay attention to when we do permission design. There are two key points, and here they are again:

  • Granularity: The granularity is difficult to grasp. It is recommended to use a basic “business operation” granularity.
  • Validation: Access is independent of data and can be blocked at the gateway level. Validation is related to data and can be done in code downstream of the Service.

Here are recommended practice details for the entire permission design, from back end to front end.

Back-end implementation details

The implementation of Access and Validation is introduced.

What does Access do?

Access is written permissions one by one. In Spring Security, for example, it is a string. You can either write it to a Config file or store it in a database.

In general, an existing database is more flexible and easier to manage when coupled with an administrative interface. Here’s a quick guide to how to do it. In the case of Spring Security, you can use custom beans to intercept all requests. In the Bean, you can get the URL of the request and other information, and then check the database or session or parse JWT Token to get the permissions of the current user, and then match.

How do I do Validation?

Validation verifies that information such as the current state of the data meets the criteria. Sometimes, different roles have different permissions for the same state. When designing and using Validation, consider four factors: how multiple roles determine permissions, short-circuit design, elimination of roles, and whitelisting or blacklisting. Each of these factors is described in detail below, followed by a recommended generic Validator code implementation.

How do I determine permissions for multiple roles

In general, one person tends to have multiple roles in slightly more complex rights management requirements. So how do you determine if this person has permission for the current operation?

Generally speaking, as long as a role of the current user has permission for this operation, we assume that the current user has permission for this operation.

Short circuit design

Validation needs to query the data. In the context of microservices, it even sometimes needs to call other apis. As mentioned earlier, we can assume that the current user has permission for the action as long as one role has permission for the action. Then the subsequent judgment logic can not go, the program made short circuit design, is conducive to reduce data query and API calls, improve performance.

Eliminate role

When we write the Validation code, the narrative from the business side may be relevant to the role. For example, on a writing platform, after publishing an article, the author cannot modify the article, but the editor of the website can. We use pseudocode to represent the logic for validation:

So we write the character into the code. In the future, another role could be used to modify the article, such as a network security auditor. Then you need to change the code and redistribute it. It’s not very flexible.

We can try to eliminate “roles” from the code and change them to permissions instead. For example, we give a role like editor a permission called edit_published_article, so our code can look like this:

In this case, we just need to grant this permission to the new role, it can do this operation. No code changes required.

When can you not eliminate characters?

But does Validation completely eliminate roles? Isn’t. If your system business puts the role ID in the business database, you cannot eliminate the role in validation.

For example, in our last article, if the current user is a teacher, he can view his own exam paper. If you are an academic dean, you can view all the papers for the current grade. At this time, according to different roles, go to different tables to get different data. So the “role” must be written to the Validation code. It can’t be avoided.

But in most businesses, we can eliminate roles. The benefits of eliminating roles are obvious, but the only downside is that it adds a lot of permissions and makes managing permissions a little more complicated. Usually on an enumeration, the value of an enumeration will correspond to a permission. However, we can solve this problem by adding the concept of “permission groups,” which are described later.

Generic Validator code implementation

Here is a generic Validator implementation based on Java code and its usage. Readers can also make enhancements according to their own needs:

Front-end implementation details

For the sake of system security, we must do permission control on the back end. And the front end sometimes also need to do the corresponding permission control, is hoping to give the user a better experience on the UI. For example, pages that shouldn’t be seen by the current user don’t appear in the left navigation bar. Buttons that the user cannot click should be hidden or grayed out.

Page permission control

Page display is usually coarse-grained UI control. If the role and its permissions are relatively stable, it can die in the front-end configuration, which is cheaper to develop.

If roles and their permissions are easy to change, you can return route configuration from the back end. In this way, users, roles, and routes can be dynamically configured and managed in a unified manner.

For details, please refer to the article “How to gracefully add permissions to VUE.”

Component permission control

Component permission control is a fine-grained UI control. Specifically, there are two plans:

  • Front-end write validation logic;
  • All logic goes to the back end, and the back end returns a Flag, which the front end determines.

Each of these two schemes has its advantages and disadvantages. Let’s discuss them below.

Front-end write validation logic

If it is front-end write validation logic, it is the front-end through the existing data, to determine whether the component can be displayed or can operate. For example, in many cases, whether a button can be clicked depends on the role of the user or the state of the current data. On a tabular page, the user’s role and current data are known. So the front end just needs to write exactly the same logic as the back end, and you can control it.

And that brings up a problem. For example, if we delete a data, we will check the weight according to the state of the data. The back end must write the validation logic, and if the front end writes it again, it will maintain a piece of logic with the same function on the front and back ends. If the logic needs to be modified later, it needs to be modified at the same time, resulting in the inconvenience of code maintenance.

Another problem is that if the front and back ends don’t understand each other, it might make the front button look clickable, but when clicked, the back end reports error 403. This may be due to a BUG in the program, but if the front and back ends are separated, it increases the chance of such bugs during development and reduces development efficiency.

There is also a case where writing validation logic on the front end is not appropriate. Validation is a complex task that requires checking data from other databases or even from other services. This is not a good thing to do in the front end, or you may need to Call several apis.

The back end returns Flag

The two problems mentioned above can be solved if the back end returns Flag. At this point, all Validation logic is put into the back end, which can reuse the same logic when “reading” the data and when the actual business operation “writes” the data.

Is returning Flag from the back end the perfect solution? Isn’t. It also has two problems.

The first is the intrusion into the response structure. We will add one or even multiple flags in response, but these flags are actually irrelevant to business data. It is recommended to name Flag business-friendly rather than front-end UI friendly. For example, canDeleteXXX is better than showXXXButton.

Another problem is that some operations may only require Access control and do not require Validation. At this point, the backend does not reuse any of the code, because the “write” operation will pass through Access at the gateway level without any Validation. Therefore, in this case, it is not good to write logical Flag judgment when reading data simply to add Flag.

recommendation

There are two common cases of permission control for an operation:

  • All you need is Access,
  • You need Access + Validation

After comparing the two implementations, we recommend that the Flag returned from the back end only be associated with Validation, and the dead code in the front end only be associated with Access.

Here is an example of Vue code:

Of course, different teams can make trade-offs and improvements according to their own actual situation.

User group and permission group

Sometimes we may make enhancements to the RBAC model based on business requirements. For example, user groups and permission groups.

User groups

If there are too many users, it may be difficult to manage roles for each user. At this point we can abstract out the concept of “user groups”. Equivalent to the “department” of the company. This makes it easier to manage roles for a group of users.

Permissions set

As we mentioned earlier, sometimes in Validation, you can “eliminate roles.” This comes at the cost of creating multiple permissions based on the state of the data. For example, if there are three grades in high school and we want to have different permissions for each grade, we need to create three permissions.

Another case is Access’s relationship to apis. In the previous article, I recommended a granularity of “business operations.” For example, let’s say you have three steps to post on moments: upload a picture, get the current location, and confirm the post. We actually only need one Access to post on moments, not three. But this Access actually corresponds to three apis, and each API may have more than one Access. For example, uploading files, we also use this API when we chat. So Access is many-to-many with apis.

With more permissions, it’s not easy to manage. Therefore, the concept of a permission group can be abstracted to better manage permissions.

Of course, adding user groups and permission groups introduces some complexity that complicates the existing permission model. So remind you again, when doing permission design must follow the principle of “enough is enough”, do not over design.

The above two articles are the author’s understanding and summary of the permission system design. If there is any confusion or inconsistency in the reader’s understanding. Welcome to comment ~