【Ovirt 笔记】权限实现分析
2017-07-17 本文已影响24人
58bc06151329
文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
分析整理的版本为 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());