Table/Entity Relationships
Menu: [Data Permission Rules], [Document Permissions]
The relationship between the two: [Document Permission] Based on the Data Permission Rules, in order to facilitate re-encapsulation, the Create Document Permission actually automatically maintains several tables related to the Data Permission Rules;
[Data Permission Rules]
- hpfm_permission_range Data Shield Range
- The scope of the rule's role, which can be limited to tables, services, sqlid, tenants
- hpfm_permission_rule shielding rules
- Existing rules: 1. Prefix tables; 2. Document rights automatically generated
- hpfm_permission_rel shielding range rule relation
- Intermediate table of range and rule
- hpfm_permission_range_excl blocking range blacklist
- No data now
[Document Rights]
- hiam_role_auth_data Role Document Rights Management
- Includes: Roles, Document Rights Encoding
- hiam_role_auth_data_line
- Includes: header id,data_id
Data Source: [Role Management] - [Maintain Data Permissions]
- Hiam_ Role_ Auth_ Source of data
- Hiam_ Role_ Auth_ Data_ Sources of line, such as menu permissions, add label_when data is added ID inserted into hiam_role_auth_data_line.data_id;
Data Rights Implementation Process
principle
To assign functional privileges is to add and add privileges to users. Assigning data permissions is a subtraction that reduces the amount of data that users can access and usually adds filtering conditions to sql.
For example, query users:
select * from iam_user as user where .....
Restrict users who can only query Department 1:
select * from (select * from iam_user where dept_id = 1) as user where .....
Put table name iam_user replaced with iam_user's subquery restricts users who can only query Department 1 without affecting other parts of sql;
mybatis preprocesses SQL before executing sql, replacing table names with subqueries according to some rules; If rules can be maintained dynamically, dynamic data rights control can be achieved.
Code Flow
Interrupt PermissionSqlBuilder#getPermissionRange;
org.apache.ibatis.plugin.Interceptor //Plug-in mechanism for ibatis org.hzero.mybatis.parser.SqlParserInterceptor#intercept statement = sqlInterceptor.handleStatement(statement.... SqlInterceptor#handleStatement SqlInterceptor#handlePlainSelect FromItem afterHandlerFromItem = handleTable((Table) fromItem, serviceName, sqlId, args, userDetails); PermissionSqlBuilder#handleTable PermissionSqlBuilder#handleTable2FromItem PermissionSqlBuilder#getPermissionRange //permissionRange is the key PermissionRangeVO permissionRange = this.permissionSqlRepository.getPermissionRange(serviceName, table, sqlId, userDetails.getTenantId()); DefaultPermissionSqlRepository#getPermissionRange Map<String, String> permissionRangeVOMap = redisHelper.hshGetAll(cacheKey); //Read from redis, initialization is done when the platform service starts;
DefaultPermissionSqlRepository#getPermissionRange:
The control rules for data permissions come from the hpfm:permission:{table name} of redis db1 (in this case, all tables of hzero cannot have the same name), where the "table name" is the table that needs to be controlled; For example, for iam_menu table for permission control, key is hpfm:permission:iam_menu; value value:
//PermissionRangeVO object {"customRuleFlag":0,"sqlList":[],"dbPrefix":"","rangeExclList":[]}
The redis data is initialized from the time the platform service starts, (so if the redis data is deleted, the platform service needs to be restarted;)
Initialization method: org.hzero.platform.domain.entity.PermissionRange#initCache (found for the constructor breakpoint of PermissionRangeVO)
key point
- sql is handed and footed in PermissionSqlBuilder
- The control rules for data permissions come from: redis db1's hpfm:permission:{table name}, which is initialized when the platform service starts;
- The sql that handles sql replacement table names comes from the enumeration class: DocTypeScript;
Key Classes
SqlInterceptor
org.hzero.mybatis.parser.SqlInterceptor
Rewrite the SQL in the Mybatis interceptor, and rewrite the part of the SQL you need to rewrite when you implement the interface
SqlParserInterceptor
sqlParser interceptor:
SqlParserInterceptor#sqlInterceptors: org.hzero.boot.customize.interceptor.CustomizeSQLInterceptor org.hzero.boot.platform.data.permission.builder.PermissionSqlBuilder
Splicing replacement xml
xml from enumeration org.hzero.iam.infra.constant.DocTypeScript, DocTypeServiceImpl#createDocType of Iam gets xml and replaces the variable, and Iam calls platform to insert into hpfm_ Permission_ In the rule table;
Example:
<bind name="roleMergeIdList" value="@io.choerodon.core.oauth.DetailsHelper@getUserDetails().roleMergeIds()" /> <bind name="roleAuthHeader" value="@org.hzero.boot.platform.data.permission.util.DocRedisUtils@checkRoleAuthHeaderAssign(121684538047991808L, "BIZ", roleMergeIdList)" /> <bind name="roleAuthLine" value="@org.hzero.boot.platform.data.permission.util.DocRedisUtils@checkRoleAuthLineAssign(121684538047991808L, "BIZ", "SYS_API_SERVICE", roleMergeIdList)" /> <bind name="userAuthAssign" value="@org.hzero.boot.platform.data.permission.util.DocRedisUtils@checkUserAuthAssign(tenantId, "SYS_API_SERVICE", userId)" /> <choose> <when test="!roleAuthHeader"> 1=2 </when> <when test="!roleAuthLine"> 1=1 </when> <when test="!userAuthAssign"> (EXISTS ( SELECT 1 FROM hiam_role_auth_data hrad LEFT JOIN hiam_role_auth_data_line hradl ON hrad.auth_data_id = hradl.auth_data_id WHERE hrad.tenant_id = #{tenantId} AND hrad.role_id IN <foreach collection="roleMergeIdList" open="(" separator="," item="roleMergeId" close=")"> #{roleMergeId} </foreach> AND hrad.authority_type_code = 'SYS_API_SERVICE' AND (hrad.include_all_flag = 1 OR hradl.data_id IN (SELECT hs.service_id FROM hadm_service hs where ${tableAlias}.service_name = hs.service_code)))) </when> <when test="userAuthAssign"> (EXISTS ( SELECT 1 FROM hiam_user_authority hua1 LEFT JOIN hiam_user_authority_line hual1 ON hua1.authority_id = hual1.authority_id WHERE hua1.tenant_id = #{tenantId} AND hua1.user_id = #{userId} AND hua1.authority_type_code = 'SYS_API_SERVICE' AND (hua1.include_all_flag = 1 OR hual1.data_id IN (SELECT hs.service_id FROM hadm_service hs where ${tableAlias}.service_name = hs.service_code)))) </when> <otherwise> 1=2 </otherwise> </choose>
Instance: api interface permissions, menu permissions
Requirements:
1. When permission set adds permissions, only the interface of this system can be added;
2. System administrators can only see the menu of this system;
Steps for writing sql:
- Identify tables to filter (target tables): api(IAM_PERMISSION), menu(IAM_MENU)
- Determine the field of the table to be filtered: api.service_ Name, menu->label
- Determine the range of values for a field (Value Set/Value Set View): hadm_service, iam_label
- Determine hiam_role_auth_data_line.data_id field to be saved (Long only): hadm_service.service_id, iam_label.id
- Write a sql fragment to find out the data hiam_corresponding to the current row of the target table Role_ Auth_ Data_ Line.data_ ID
Actual api sql
Put iam_ Replace permission with:
( SELECT * FROM iam_permission DST__0 WHERE ( EXISTS ( SELECT 1 FROM hzero_platform.hiam_role_auth_data hrad LEFT JOIN hzero_platform.hiam_role_auth_data_line hradl ON hrad.auth_data_id = hradl.auth_data_id WHERE hrad.tenant_id = 0 AND hrad.role_id IN (91468303490486272) AND hrad.authority_type_code = 'SYS_API_SERVICE' AND ( hrad.include_all_flag = 1 OR hradl.data_id IN ( /*data_id Is the valueField of the value set view*/ SELECT hs.service_id FROM hzero_admin.hadm_service hs WHERE DST__0.service_name = hs.service_code ) ) ) ) ) ip
Actual menu sql
Put iam_ Replace menu with:
( SELECT * FROM iam_menu DST__0 WHERE ( EXISTS ( SELECT 1 FROM hiam_role_auth_data hrad LEFT JOIN hiam_role_auth_data_line hradl ON hrad.auth_data_id = hradl.auth_data_id WHERE hrad.tenant_id = 0 AND hrad.role_id IN (83532216818352128) AND hrad.authority_type_code = 'SUBSYS_MENU' AND ( hrad.include_all_flag = 1 OR hradl.data_id IN ( /*data_id Is the valueField of the value set view*/ SELECT hrl.label_id FROM hiam_label_rel hrl WHERE hrl.data_id = DST__0.id AND hrl.data_type = 'MENU' ) ) ) ) ) im
Ideas for troubleshooting problems
Possible problems:
- There are bug s in Data permission update, business scope, permission data and so on need to try several times to produce results;
- Document permissions are disabled, but data permissions are controlled. 1=2 is generated in sql
Ideas for sorting out:
Check according to the process in effect for data permissions:
- Look at the log and see if there are any errors. When the data rights interceptor fails, it will also query all the data.
- Modify the interface to check for changes in redis, database data:
- Check hpfm:permission:{table name} for redis db1, which is the direct source of filtering rules;
- Check database hzero_platform.HPFM_PERMISSION_RANGE, redis data comes from here;
Other
hzero's data rights seem flexible and powerful, but they are cumbersome to maintain, difficult to use, and prone to problems.
Prerequisites for using data permissions: access to hzero_ Tables under the platform, so you need to: use the same database instance with the platform and have access rights;
All services on the platform need to share a redis, a database instance, and the platform is equivalent to a distributed monolithic application.
Role inheritance cannot inherit data permissions;
hiam_role_auth_data_line.data_id is numeric and cannot store string; So there is no way to filter strings by mapping/associating numbers first;
Recommended ideas for controlling data permissions: control the filter conditions of queries according to the permission set of roles;