美文网首页Ovirt程序员
【Ovirt 笔记】权限实现分析

【Ovirt 笔记】权限实现分析

作者: 58bc06151329 | 来源:发表于2017-07-17 14:20 被阅读24次

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

分析整理的版本为 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_IDSUPER_USER 角色的 ID
  • v_DELETE_VMDELETE_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());

相关文章

网友评论

    本文标题:【Ovirt 笔记】权限实现分析

    本文链接:https://www.haomeiwen.com/subject/aygfkxtx.html