文前说明
作为码农中的一员, 需要不断的学习, 我工作之余将一些分析总结和学习笔记写成博客与大家一起交流, 也希望采用这种方式记录自己的学习之旅.
本文仅供学习交流使用, 侵权必删.
分析整理的版本为 Ovirt 3.4.5 版本.
数据库结构
roles 角色信息表
字段名称 | 字段类型 | 说明 |
---|---|---|
id | uuid | 唯一性 ID |
name | string | 名称 |
description | string | 描述信息 |
role_type | integer | 角色类型,1 为管理员,2 为用户 |
app_mode | integer | 应用模式,1 为 gluster 分布式存储节点,255 为 virt 虚拟化节点 |
roles_groups 角色与操作组关系表
字段名称 | 字段类型 | 说明 |
---|---|---|
role_id | uuid | 角色 ID |
action_group_id | integer | 操作组 ID |
permissions 权限信息表
字段名称 | 字段类型 | 说明 |
---|---|---|
id | uuid | 权限 ID |
role_id | uuid | 角色 ID |
ad_element_id | uuid | 登录用户 ID |
object_id | uuid | 操作对象 ID |
object_type_id | integer | 操作对象类型 |
定义操作组
通过枚举 ActionGroup 进行操作组的自定义.
枚举属性 | 说明 | 用例 |
---|---|---|
value | 枚举值 | DELETE_VM |
id | 唯一性 ID | 1 |
roleType | 角色类型 | RoleType.USER |
vdcObjectType | 操作对象类型 | VdcObjectType.VM |
定义操作
通过枚举 VdcActionType 进行操作自定义.
通过枚举设置操作与操作组的从属关系.
枚举属性 | 说明 | 用例 |
---|---|---|
value | 枚举值 | RemoveVm |
intValue | 唯一性 ID | 1 |
actionGroup | 操作组 | ActionGroup.DELETE_VM |
quotaDependency | 配额 | QuotaDependency.STORAGE |
准备工作
通过 insert_predefined_roles.sql 定义初始化角色权限.
- ---Vm Groups
- INSERT INTO roles_groups(role_id,action_group_id) VALUES(v_SUPER_USER_ID,v_DELETE_VM);
v_SUPER_USER_ID 是 SUPER_USER 角色的 ID
v_DELETE_VM 是 DELETE_VM 删除虚拟机操作组 ID
通过 engine 界面分配登录用户权限.
权限验证事务
以删除虚拟机操作为例, 执行操作 RemoveVmCommand 删除虚拟机.
系统中所有操作 Command 都提供了 getPermissionCheckSubjects 方法, 用来定义执行操作的权限列表.
- @Override
- public List<PermissionSubject> getPermissionCheckSubjects() {
- List<PermissionSubject> permissionList = new ArrayList<PermissionSubject>();
- permissionList.add(new PermissionSubject(getParameters().getVmId(),
- VdcObjectType.VM,
- getActionType().getActionGroup()));
- return permissionList;
- }
permissionList 权限列表中, 包含权限对象 PermissionSubject, 如上代码说明:
权限对象属性 | 说明 | 用例 | 用例说明 |
---|---|---|---|
objectId | 操作对象 ID | getParameters().getVmId() | 需要删除的虚拟机对象 ID |
objectType | 操作对象类型 | VdcObjectType.VM | 虚拟机 |
actionGroup | 操作组 | getActionType().getActionGroup() | 操作组权限,ActionGroup.DELETE_VM |
message | 无权限操作提示信息 | VdcBllMessages.USER_NOT_AUTHORIZED_TO_PERFORM_ACTION | 没有删除虚拟机的权限 |
操作 Command 的执行, 会检测权限
- protected boolean isUserAuthorizedToRunAction() {
- // Skip check if this is an internal action:
- if (isInternalExecution()) {
- if (log.isDebugEnabled()) {
- log.debugFormat("Permission check skipped for internal action {0}.", getActionType());
- }
- return true;
- }
- // Skip check if multilevel administration is disabled:
- if (!MultiLevelAdministrationHandler.isMultilevelAdministrationOn()) {
- if (log.isDebugEnabled()) {
- log.debugFormat("Permission check for action {0} skipped because multilevel administration is disabled.",
- getActionType());
- }
- return true;
- }
- // Deny the permissions if there is no logged in user:
- if (getCurrentUser() == null) {
- addCanDoActionMessage(VdcBllMessages.USER_IS_NOT_LOGGED_IN);
- return false;
- }
- // Get identifiers and types of the objects whose permissions have to be
- // checked:
- final List<PermissionSubject> permSubjects = getPermissionCheckSubjects();
- ......
每一个操作 Command 执行, 必须定义操作权限列表, 否则直接提示没有操作权限.
- if (permSubjects == null || permSubjects.isEmpty()) {
- if (log.isDebugEnabled()) {
- log.debugFormat("The set of objects to check is null or empty for action {0}.", getActionType());
- }
- addCanDoActionMessage(VdcBllMessages.USER_NOT_AUTHORIZED_TO_PERFORM_ACTION);
- return false;
- }
权限检测
- return checkPermissions(permSubjects);
- protected boolean checkPermissions(final List<PermissionSubject> permSubjects) {
- for (PermissionSubject permSubject : permSubjects) {
- if (!checkSinglePermission(permSubject, getReturnValue().getCanDoActionMessages())) {
- return false;
- }
- }
- return true;
- }
- getDbFacade().getPermissionDao().getEntityPermissions(userId, actionGroup, object, type);
最终通过存储过程进行权限查询 get_entity_permissions
- CREATE OR REPLACE FUNCTION get_entity_permissions(
- v_user_id uuid,
- v_action_group_id integer,
- v_object_id uuid,
- v_object_type_id integer)
RETURNS SETOF uuid AS
- $BODY$
- DECLARE
- v_everyone_object_id UUID;
- BEGIN
- v_everyone_object_id := getGlobalIds('everyone'); -- hardcoded also in MLA Handler
- RETURN QUERY
- select id from permissions where
- -- get all roles of action
- role_id in(select role_id from roles_groups where action_group_id = v_action_group_id)
- -- get allparents of object
- and (object_id in(select id from fn_get_entity_parents(v_object_id,v_object_type_id)))
- -- get user and his groups
- and (ad_element_id = v_everyone_object_id or
- ad_element_id = v_user_id or ad_element_id in(select * from getUserAndGroupsById(v_user_id))) LIMIT 1;
- END; $BODY$
- LANGUAGE plpgsql STABLE
- COST 100
- ROWS 1000;
- ALTER FUNCTION get_entity_permissions(uuid, integer, uuid, integer)
- OWNER TO engine;
查询参数 | 说明 | 备注 |
---|---|---|
v_user_id | 用户 ID | 系统登录用户 ID |
v_action_group_id | 操作组 ID | ActionGroup.DELETE_VM |
v_object_id | 操作对象 ID | 需要删除的虚拟机 ID |
v_object_type_id | 操作对象类型 ID | VdcObjectType.VM |
满足权限的条件判断 (1,2 必须满足, 3,4,5 满足其中一个)
1. 系统登录用户 (user_id) 所属的 角色 (role), 必须与 操作组 (action_group_id) 在 roles_groups 表中有对应关系.
role_id in(select role_id from roles_groups where action_group_id = v_action_group_id)
2. 必须拥有需要删除的虚拟机的权限 或者 所属群集, 数据中心的权限.
- and (object_id in(select id from fn_get_entity_parents(v_object_id,v_object_type_id)))
- WHEN v_entity_type = 2 THEN -- VM
- -- get cluster id
- cluster_id := ( SELECT vds_group_id FROM vm_static WHERE vm_guid = v_entity_id );
- -- get data center id
- ds_id := ( SELECT storage_pool_id FROM vds_groups WHERE vds_group_id = cluster_id );
- RETURN QUERY
- SELECT system_root_id AS id
- UNION
- SELECT ds_id AS id
- UNION
- SELECT cluster_id AS id
- UNION
- SELECT v_entity_id AS id;
3. 功能分配了 everyone 权限
- v_everyone_object_id := getGlobalIds('everyone'); -- hardcoded also in MLA Handler
- ad_element_id = v_everyone_object_id
4. 功能分配了 该登录用户的 的权限
ad_element_id = v_user_id
5. 功能分配了 该登录用户所属用户组 的权限.
- ad_element_id in(select * from getUserAndGroupsById(v_user_id))
- CREATE OR REPLACE FUNCTION getuserandgroupsbyid(v_id uuid)
RETURNS SETOF iduuidtype AS
- $BODY$
- BEGIN
- RETURN QUERY
- select ID from ad_groups,users where users.user_id = v_id
- and ad_groups.id in(select * from fnsplitteruuid(users.group_ids))
- UNION
- select v_id
- UNION
- -- user is also member of 'Everyone'
- select 'EEE00000-0000-0000-0000-123456789EEE';
- END; $BODY$
- LANGUAGE plpgsql STABLE
- COST 100
- ROWS 1000;
- ALTER FUNCTION getuserandgroupsbyid(uuid)
- OWNER TO engine;
配额权限
如果操作的配额非 NONE, 操作对象所在数据中心的配额设置为 强制的, 系统自动添加配额相关权限
- if (isQuotaDependant()) {
- addQuotaPermissionSubject(permSubjects);
- }
- quotaPermissionList.add(new PermissionSubject(parameter.getQuotaGuid(), VdcObjectType.Quota, ActionGroup.CONSUME_QUOTA, VdcBllMessages.USER_NOT_AUTHORIZED_TO_CONSUME_QUOTA));
权限的增加与删除
通过 AddPermissionCommand 操作添加权限
- Permissions permission =
- getPermissionDAO().getForRoleAndAdElementAndObject(paramPermission.getrole_id(), principalId,
- paramPermission.getObjectId());
- if (permission == null) {
- paramPermission.setId(Guid.newGuid());
- paramPermission.setad_element_id(principalId);
- TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() {
- @Override
- public Void runInTransaction() {
- getPermissionDAO().save(paramPermission);
- getCompensationContext().snapshotNewEntity(paramPermission);
- getCompensationContext().stateChanged();
- return null;
- }
- });
- permission = paramPermission;
- }
通过 paramPermission 操作删除权限, 根据权限 ID 删除.
getPermissionDAO().remove(perms.getId());
来源: http://www.jianshu.com/p/be46c5d20b69