Flexible background management rights based on MongoDB design
MongoDB is a document-based NoSQL database that is now a community of fire, with a natural advantage in storing flexibility in the document (the same set can be any form of row data, and of course we're not going to rot with that flexibility:) ), with impressive performance and high availability of replica sets!
This permission granularity exists in the scope and the individual concept, can be understood as functional permissions and data attribution permissions. The data permission is subdivided again as a subset of functional permissions. Below, I will be a little bit of an analysis, the following case will give all the table structure, and the key operations of the SQL statement (based on the Golang language (MgO MongoDB driver) and the design diagram. Of course, this may not be the best practice, but I have designed my own experience, has solved the actual problem.
Background
Prior to making a prototype of an early serverless architecture (now only the model system:) Background management system, the company's personnel structure is very stable, and in order to quickly internal on-line to provide the use, we think of a simple department-based implementation of the general permissions of the functional rights management, meaning that this department of the following groups have all the view rights created by this group, the group has the function of the permissions defined by the role, If the senior developer role, then the administrator will assign template editing permissions, template clear Cache/Publish a set of can be edited from the code Web IDE to the external network access to publish the entire process permissions, the beginner may only edit permissions, and so on.
But we soon found that the problem, although the company has a unified authority system, but the control granularity is too coarse, so we began to put the authority of the personnel into their own library maintenance, a few months, with the company's massive expansion, personnel structure, departments and other changes, Personnel change means that previously based on the department design authority into dirty data, the data between departments can not automatically achieve access, only through our simple data rights operation mechanism, let a person have multiple departments of authority, so processing for inconvenient reasons may be ultra vires processing, because in a department you have the release permission, in the B Department I just want you to have query function, etc., so that there is a period of time we fell into the continuous adjustment of the authority of the after-sales work! And it will be inconvenient to deal with problems across departments.
Structure diagram
Thinking
At that time, we face a more element kind of back-end system, and the right combination of kinds of more, then how can design a can be free to combine, can self-drive to play the right, the rights under no longer by the single-person control, as well as when the handover of authority and resources can be a key handover. So there are a few features listed below.
- Authority control is decentralized, and developers are no longer struggling to maintain permissions.
- Permissions have role management control, and there is a subdivision concept of data permissions.
- Permissions and resources can be transferred one click.
- Permissions are based on a person as an individual, subdivided into, a certain person under a piece of data.
- Permissions can be made temporarily, and different developers can have one or more sets of permissions at the same time, facilitating collaboration across departments.
- Easy to use, isolated from user basic information
- Superior performance. Web Services/SDKs can be made
Practice
The next step is to comb the table structure, and all the structures will give the most concise key fields and annotations
In order to subdivide to the data permission, then the person/group must have an element to bind with the resource, here we use Signkey as a kind of signature, a person has this signature to have certain operation permission to this resource. Must have the intermediate signature link to establish the contact, otherwise the data quantity storage, the transfer, the change will have the question. If the resources belong to whom to deal with, perhaps a field has a lot of work number to identify.
So the user information table in our permissions table is designed like this
type UserDoc struct { Name string // 姓名 UserId string // 工号 唯一标识 SignKey map[string]string // 签名 key是签名 24字符id mongodb _id (可换成任意 唯一 字符串),value是签名的描述。}
You can see that a person can create multiple private signatures on their own
Groupname+userid as a federated unique index
So what are our role permissions designed for? A role has a variety of functions, we think of each function as an interface to achieve a different approach, then the role of permissions, is a combination of one or more interfaces implemented, interface implementation is reflected in the URL + method above, however, the problem is that some interface involves manipulating data, writing to the data, some interfaces, It's just a single event that doesn't involve any data queries or changes. In this case, the interface is divided into whether the data authentication is turned on. A combination of different interfaces enables the maintenance of role permissions.
type RoleDoc struct { RoleName string // 角色名称 Desc string // 角色描述 IsDefault bool // 是否为默认角色,就是用户登陆进来自带的角色权限 UserIds []string // 角色下面有哪些用户, 这里是一个优化点,可以使用外键表关联。 视系统大小可以调整 PathsDataVerify map[string]bool // 该接口是否开启数据验证权限,用于 sql 快速查询数据 PathMethods []PathMethod // 该角色有哪些功能即 API 的组合, 详细数据,是一份冗余数据,方便配置时使用 通过方法把详细数据转化为快速查询数据 Typ int // 角色类型,用于做特权处理, 例如 超级管理员角色,无需验证权限 则 type = 0}// restful API 风格设计 一个 path 有多个 method,// method 使用 int 代替 GET=1 POST=2 PUT=4 DELETE=8 HEAD=16... 可以用到 mongodb 位运算// 如果一个 path 的 get post 方法都赋予了这个角色, 则 MethodInt=3type PathMethod struct { Path string // 请求路径 MethodInt int // 方法的 int 和 9 代表 GET 和 DELETE 方法}
The above table structure pathsdataverify key= "1_/template" value=true because 1 means get so this representation is a template query method, need to turn on data authentication permissions
Rolename+typ as a federated unique index
With the path associated with the role, it is natural to have the details of the path, to facilitate management, as the source of the permissions that make up the role, and to set whether the interface turns on data validation permissions.
type RouterDoc struct { Path string // 请求路径 /template Desc string // 路径实现功能的大类, 例如 模块的 CRUD MethodMap map[string]MethoedDetail // key 方法 具体表现 "1" "2" ..}type MethoedDetail struct { DataVerify bool // 该方法是否开启 数据验证 Desc string // 模版的删除 则 MethodMap key = 8}
Through the above simple statement, you can establish the URL + method equals to the implementation of methods, can be very convenient for the Rights management
Path as a unique index
So how does our data access verification be implemented? What kind of relationship it has with role permissions. How to bind with people, note the following
type SignDoc struct { SignKey string // 签名, 某人的私有签名 + 用户唯一标识 可以理解成 这个人拥有这个签名的哪些数据权限 CreateUserId string // 签名的创建者 UserId string // 这个成员也拥有这个签名的一部分/全部权限, 相当于创建者把自己的签名共享出,用于多人使用 RouterMap map[string]int // key=Path value=Method $bitsAllSet 计算 {"/template":14} 表述 这个人拥有此签名下面的模版数据 增/改/删的权限}
Signkey+userid as a federated unique index
So that the creator can assign permissions for their own subset of role permissions, you can expose a private key to the public authorization of others, then the other person will sign all the resources below, have the corresponding permissions you configure, Routermap on behalf of what permissions he has, so you can put your own signature, Give different people, different people, have different permissions on this signature. Fully realized, their own data management. For a variety of coordinated work scenarios. For example, there is a temporary task to modify the activity logic, there are 3 people to do, then their group leader can create a public key, give these three people, for the task of all permissions operation.
All of the table structure is annotated, then how they are related to collaboration, below we will use the permission system from a user's perspective of the SQL statements involved in the simple parsing, in order to be familiar with the flow of the entire request.
Configure Permission Flow
Initialize the route Method description and whether to turn on data permissions---> Create roles---> Bind a created route to a created role---> Add a user---> role to create your own signature---> each request carries a signature--- > Bring this signature and resource binding when creating a resource
See what permissions your individual has
// userid 查询 RoleDoc 然后把所有返回的 PathMethods 做聚合 // 通过 PathMethods 也可以结合 RouterDoc 查询具体的数据验证权限
View permissions granted by others
// userid 查询 SignDoc 然后对返回,做聚合即可
In the configuration and management of permissions, there is no complex SQL, of course, in the actual project there are many SQL combinations to meet the different configuration methods. Overall is very convenient.
Examples of complex allocations, in the case of CRUD for resources
- U1 U2 U3, U1 has U2 signature u2-s-1 under the Ceph resource Read permission, U2 has u1-s-3 under the template CRU permission, u3-s-1 under the Ceph CU permissions. U3 has R permissions for all u1-s-3 data
Data storage Performance
// RoleDoc[ {"userId":"u1", "signKey":{"u1-s-3":"ceph kv manager"}}, {"userId":"u2", "signKey":{"u2-s-1":"template manager"}}, {"userId":"u3", "signKey":{"u3-s-1":"template manager"}}]
// RouterDoc[ {"path":"/ceph","methodMap":{ "1":{"dataVerify":true,"desc":"query ceph value"}, "2":{"dataVerify":true,"desc":"create ceph value"}, "4":{"dataVerify":true,"desc":"update ceph value"}, "8":{"dataVerify":true,"desc":"delete ceph value"}, } }, {"path":"/template","methodMap":{ "1":{"dataVerify":true,"desc":"query template value"}, "2":{"dataVerify":true,"desc":"create template value"}, "4":{"dataVerify":true,"desc":"update template value"}, "8":{"dataVerify":true,"desc":"delete template value"}, } }]
// RoleDoc 此角色没有删除权限[ {"reoleName":"ceph&template manager","userIds":["u1","u2","u3"], "pathMethods":[ {"path":"/ceph","methodInt":7}, {"path":"/template","methodInt":7} ]}]
// SignDoc 签名数据权限关联[ {"signKey":"u1-s-3","createUserId":"u1","userId":"u2","routerMap":{"/template":7}}, {"signKey":"u3-s-1","createUserId":"u3","userId":"u2","routerMap":{"/ceph":6}}, {"signKey":"u2-s-1","createUserId":"u2","userId":"u1","routerMap":{"/ceph":1}}, {"signKey":"u1-s-3","createUserId":"u1","userId":"u3","routerMap":{"/template":1,"/ceph":1}}]
When the data volume is very large, the nested document can be stored separately by the foreign key.
Verify Permissions
User (Unified single point) after landing---> Determine whether the initial user (the default role)---> Return role type, what permissions (the front-end can render control of the UI based on the permissions they have)
User request to carry UserID + Signkey request to get path + method (route if dynamic routing is supported, use Httprouter to parse to background configured route)
Use the UserID + path + method to query the Roledoc table to verify that the role permissions are present (if the role is Super Administrator, there is no need to verify later)
Depending on the role Permissions pathsdataverify the field value value to determine whether to validate data permissions.
If you need to determine the data permissions, then carry the UserID + Signkey + path + method query Signdoc table, whether there is a permission to write the appropriate permission information to the context for subsequent methods to use
If the query interface user does not need to carry signkey, need to query the Signkey by the permission processing intermediate method to make up the Signkey array, MongoDB uses $in query data
Signkey array: Own private key, other authorized data permissions key through the UserID + path + method query to all authorized Signdoc put inside the Signkey into the array
Summarize
This set of permission validation is proposed to address the previously inherent dependency on the design of the department, which does not exist with other dependencies, but depends only on the binding relationship between the signature and the person and the route. can be any combination!
Data permissions are a subset of role permissions and must be met for role permissions. For example U1 does not remove the role of/template permissions, U2 authorization U1 have u2-s-1/template Delete permission, is not possible.
This set of permissions to the previous data was split, the structure is easy to understand. Of course, this set of table structure is also applicable to other databases. If you have performance requirements, you can use Redis to cache permissions. Of course currently we are based on 200 + users, 200+ routing configuration, permission filtering and all management structures are under 20ms. is acceptable for back-office systems.
Poor design, for reference only