Access control refers to the control and management of users’ Access to protected resources. This control management ensures that the authorized party has access to the protected resource and the unauthorized party does not.

Real-world access control can be paid for or authenticated. When you go to the cinema to see a film, you have to buy enough tickets or the inspector won’t let you in.

There are many models of access control, such as:

  • Discretionary Access Control Model
  • Mandatory Access Control Model (MAC: Mandatory Access Control)
  • Role-based Access Control model (RBAC)
  • Attribute Access Control Model (ABAC)

DAC

Discretionary Access Control (DAC), which identifies the user and then accesses the object based on its Access Control List (ACL) or Access Control matrix (ACL). Access Control Matrix) to determine whether the user can perform any operations on it, such as read or modify. Users who have access to an object can assign access to that object to other users, so this is called Discretionary control.

Autonomous access control model is a relatively loose but effective means to protect resources from illegal access and use. It is loose because it is self-controlled, and the protection of resources is based on personal will. It is effective because it can clearly and explicitly indicate with what permissions the subject is accessing or using an object. Any access behavior exceeding the specified permissions will be blocked after the access control list determines.

A typical scenario is in a Linux file system: every file in the system (some special files may not, such as block device files) has an owner. The owner of a file is the consumer (or event, or another file) of the computer that created the file. It is up to its creator to decide how to set and assign autonomic access control rights to this file. The owner of the file has access and can assign access to himself and other users

MAC

Mandatory access control (MAC: Mandatory Access Control (Mandatory Access Control), which is used to manage information encryption levels and classes in the system to ensure that each user can Access only those targeted Access controls. Users (or other subjects) and files (or other objects) are marked with fixed security attributes (such as security levels and Access permissions). At each access, the system checks the security attributes to determine whether a user has access to the file.

MAC was first used in military applications, and is usually used in combination with DAC. The filtering results of the two access control mechanisms are accumulated to achieve better access control effect. That is, a subject can’t really access an object until it passes the dual filter of DAC restriction checking and MAC restriction checking. On the one hand, users can use DAC to prevent other users from attacking objects that they own. On the other hand, since users cannot change MAC attributes directly, MAC provides an insurmountable, stronger layer of security against accidental or intentional abuse of the DAC by other users.

RBAC

Role-based Access Control (RBAC) : various permissions are not directly granted to specific users, but a Role set is established between user sets and permission sets. Each role has a set of rights. Once a user is assigned an appropriate role, the user has all the operation rights of the role. At present, role-based access control model is widely used, especially in the SAAS field in 2B direction, where applications are particularly common. Role access is the focus we will introduce today.

Although RBAC simplifies the management of permissions, it is still not flexible enough to manage roles in complex scenarios. For example, the authority between subject and object is complex and changeable, which may require maintenance of a large number of roles and their authorization relationships; New objects also need to be processed for all related roles. Attribute-based role access control is designed to solve this problem.

ABAC

Attribute -based Access Control is a very flexible Access Control model. Attributes include the attributes of the request subject, the attributes of the request object, the attributes of the request context, the attributes of the operation and so on. For example, Lao Zhang, as a class teacher (subject attribute), can kick Xiao Ming, as an ordinary student (object attribute), in class (context attribute). As you can see, ABAC can achieve very complex permission control as long as properties are precisely defined and partitioned.

For example: sophomore (grade) planning (professional) class 2 (class) class stem (position) can upload (operation) class photos on the school Intranet (environment).

However, due to the complexity of ABAC, it is a little overqualified for the current SAAS field. Therefore, there are few platforms using ABAC in the SAAS field. Currently, some cloud services are mostly using ABAC.

RBAC in the stack

Our products use the RBAC permission scheme, so we only analyze RBAC at present.

RBAC is role access control, so the first thing we need to know is the role of the user. In this aspect, there are two modules of user management and role management in our project.

User management

You can create, edit, and delete user accounts in uIC user management.

In the products of stack, there are tenants. For each tenant, there is also a user management to manage the users within the tenant. The ability to set the roles of the current user, including tenant owner, project owner, project manager, and so on.

Role management

In role management, you can see the definition of a role and its access rights.

Through user definition in user management and role management, we can obtain the complete product access permissions of current users. When users enter a certain function, we can compare the current access permissions with the access permissions of users, and then draw the conclusion whether to access.

For us front-end developers, that’s what we need

  1. Get a role permission for the user
  2. The resulting permissions are compared and the results are treated differently

Let’s look at how ant Design Pro’s permission scheme is handled.

Permission schemes in Ant Design Pro

How do you design the permissions scheme in ant Design Pro?

Obtain the rights of user roles

At first, login verification is performed at the same time you enter the page. If you do not log in, it will jump to the login page for login operation. After successful login, the role data of the current user will be saved into localStorage through setAuthority method, which is convenient for us to obtain when we re-enter the page. For those that have passed the login verification, they will enter the project directly and render the page BasicLayout BasicLayout component. In the BasicLayout component, we use the Authorized component. When the Authorized component is mounted, Trigger renderAuthorize to assign to CURRENT. The subsequent permission check will use CURRENT, which is more critical.

The renderAuthorize method is a Currified function that assigns CURRENT when the role data is obtained internally using the getAuthority.

let CURRENT: string | string[] = 'NULL';

type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
/**
 * use  authority or getAuthority
 * @param {string|()=>String} currentAuthority
 */
const renderAuthorize = (Authorized: any) => (currentAuthority: CurrentAuthorityType) => {
  if (currentAuthority) {
    if (typeof currentAuthority === 'function') {
      CURRENT = currentAuthority();
    }
    if (
      Object.prototype.toString.call(currentAuthority) === '[object String]' ||
      Array.isArray(currentAuthority)
    ) {
      CURRENT = currentAuthority as string[];
    }
  } else {
    CURRENT = 'NULL';
  }
  return Authorized;
};

export { CURRENT };
export default (Authorized: any) => renderAuthorize(Authorized);
Copy the code

At this point, permissions and updates for the project are complete. Next is the permission check

Check permissions

For permission verification, the following environment parameters are required:

  1. Authority: Indicates the current access permission
  2. CurrentAuthority: CURRENT role of the CURRENT user
  3. Target: verifies successfully displayed components
  4. Exception: indicates a component that fails to be verified

For the components requiring permission verification, the Authorized component is used for combination. In the Authorized component, the checkPermissions method is implemented to verify whether the current user role has permission to access. If yes, the current component is displayed. If no, messages such as no permission are displayed.

Implementation of the Authorized component

type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & { Secured: typeof Secured; check: typeof check; AuthorizedRoute: typeof AuthorizedRoute; }; const Authorized: React.FunctionComponent<AuthorizedProps> = ({ children, authority, noMatch = ( <Result status="403" title="403" subTitle="Sorry, you are not authorized to access this page." /> ), }) => { const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children; const dom = check(authority, childrenRender, noMatch); return <>{dom}</>; }; function check<T, K>(authority: IAuthorityType, target: T, Exception: K): T | K | React.ReactNode { return checkPermissions<T, K>(authority, CURRENT, target, Exception); } / * * * * general Permission check method Common check permissions method * @ param {permissions determine | Permission judgment} authority * @ param {| your Permission Your permission description} currentAuthority * @ param {by component | Passing components} target * @ param {| no failed parts pass components } Exception */ const checkPermissions = <T, K>( authority: IAuthorityType, currentAuthority: String | string [], target: T, Exception: K,) : T | | K React. ReactNode = > {/ / not determine permissions. // Retirement authority, return target; if (! authority) { return target; IsArray (authority)) {if (array.isarray (currentAuthority)) {if (currentAuthority. Some ((item) => authority.includes(item))) { return target; } } else if (authority.includes(currentAuthority)) { return target; } return Exception; } // String handles if (Typeof Authority === 'string') {if (array.isarray (currentAuthority)) {if (currentAuthority.some((item) => authority === item)) { return target; } } else if (authority === currentAuthority) { return target; } return Exception; If (authority instanceof Promise) {return <PromiseRender<T, K> ok={target} error={Exception} promise={authority} />; } // Function handle if (typeof authority === 'Function ') {const bool = authority(currentAuthority); Bool instanceof Promise (bool instanceof Promise) {return <PromiseRender<T, K> ok={target} error={Exception} promise={bool} />; } if (bool) { return target; } return Exception; } throw new Error('unsupported parameters'); };Copy the code

Using an Authorized Component

It is very convenient to use it on the page. The components that need permission control can be combined by using the Authorized component.

function NoMatch = () => {
	return <div>404</div>
}

<Authorized authority={'admin'} noMatch={NoMatch}>
  {children}
</Authorized>
Copy the code

We can also use routing to match components, taking advantage of the fact that in V4, components will be rendered wherever they are written.

<Authorized authority={authority} noMatch={<Route {... rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />} > <Route {... rest} render={(props: any) => (Component ? <Component {... props} /> : render(props))} /> </Authorized>Copy the code

Our access scheme

Currently, we have two permissions: one is the old permission scheme for offline quality and other products, and the other is the new permission scheme for asset label.

Old permission scheme

In the old scheme, data was retrieved through the interface, but only at the menu level. Get the data and store it in the cache for our business packages and sub-products. In the service package to monitor the change of the page address, determine whether there is the right to enter the current page, according to the results of the corresponding processing, actually is to do a route guard function. In the sub-product, the data is used to determine whether the current menu entry is displayed. The combination of the two forms our old scheme.

With the growth of the stack, the old scheme gradually exposed many problems.

  • Range of access control is too small, we only control the menu this level, and for the special page and some scenarios need to control the function of (such as: edit, add, delete, etc.), currently only to limit the backend interface, the page does not restrict, if need to implement this feature, you will need to add additional interface and processing logic,
  • We split the processing of permissions into two parts, the business package and the sub-product, but the coupling between the two is very high, often changes in one place, the other needs to change.
  • We are in the business package, each product page access logic control, whenever I need to add a menu, you need to add a corresponding menu processing logic, add a product, it need to increase the product corresponds to the logic, all menu now has several stack of products of more than 10 +, as you can imagine how much this part of the processing logic is bloated.

There are more practical problems than the three listed above, but they are enough for us to explore new access schemes.

New permission scheme

In the new scheme, the business package only retains the public method of permissions, and the logic of page permissions is delegated. Sub-products maintain their own permissions logic, and it is very easy to modify a permission logic.

Compared with ant Design Pro, we transfer the judgment logic of role permissions to the back end in the new scheme. After corresponding processing, the back end will return the corresponding code set.

We define a code code for each module whose access permission needs to be set to compare whether the same code can be found in the collection returned by the back end. If the same code can be found, we have access to the current module; otherwise, we do not.

After that, all we need to care about is getting in.

When a permission point is obtained, the system caches the list of routes that have permission to access according to the permission point. When the route changes, the system searches the list of routes that have permission to access. If no route is found, the system performs operations such as redirection, which is also the function of route guard.

conclusion

After the above introduction, we have some understanding of the permission scheme, which is mainly divided into two stages:

  1. Access stage: In the access stage, users usually get the corresponding permissions according to the user information at the first time when they log in or enter the project
  2. Permission verification phase: Compares the user’s permission with the access permission of the current module, and performs operations according to the result

Knowing these, you can work out the corresponding permission scheme based on your own scenario.