角色授权
题目背景
为了响应国家发展新基建的倡议,西西艾弗岛上兴建了西西艾弗数据中心,并以此为基础运营了西西艾弗云。作为数据中心的运营和维护方,
西西艾弗云公司十分重视西西艾弗云的网络安全管理工作。众所周知,安全性和便捷性难以兼得,同时,
一个混乱的权限模型可能会导致人员被授予不必要的权限,从而造成安全风险。因此在西西艾弗云公司的网络安全部工作的小 C
专门设计了一种科学的权限模型。
这种安全模型将验证流程分为两个步骤。第一步是验证用户的身份(鉴别),第二步是验证用户的权限(授权)。在第一步,
首先验证一个用户是否是该用户所声称的那个身份。例如,通过验证用户提供的口令(Password)是否正确,或者通过验证用户提供的智能卡是否合法有效。
接下来,在授权的步骤中,权限策略会被检索以便判断来访的用户是否能够操作系统中的某个资源。
为了能够灵活地表达用户和授权之间的关系,西西艾弗云公司设计了一种简洁而灵活的授权模型:基于角色的授权模型。它的思路是:首先设定若干角色,
每个角色中指明了一个清单,表明允许访问的资源的种类、资源的名称和对资源的操作;然后将被前一步骤已经鉴别过的用户和一个或多个角色相关联。
某个用户能够执行的操作,即为与其关联的全部角色中允许的操作的并集。
小 C 将实现授权模型的工作交给了你,希望你能够把它们实现出来。
问题描述
用户表示授权模型中的一个已识别的主体,该识别过程由此前的鉴别过程完成。一个用户具有下列要素:
名称:是一个字符串,用于唯一标识一个用户;
用户组:是一个数组,包含若干个字符串,表示该用户所属的用户组。
一个待授权的行为,包括下列要素:
主体:是一个用户,包括试图进行该行为的用户的名称和该用户所属的用户组;
操作:是一个字符串,一般是一个动词,例如 Read、Open、Close 等;
资源:表示该行为的操作对象,由资源种类和资源名称描述。资源种类例如 Door、File 等;在一个特定的资源种类中,资源名称唯一确定了一个资源。
需要注意的是,一个待授权的行为的主体信息,即用户名称和所属用户组,是由前一步骤的鉴别过程完成的。因此,每次授权过程中,
每个待授权的行为都会包含主体用户和其关联的用户组的信息。由于鉴权过程中的其它因素,同一个名称的用户在先后两次待授权的行为中所属的用户组可能有区别,
不能存储或记忆此前每个待授权的行为中,用户与用户组的关联情况,而是要按照每次待授权的行为中给出的信息独立判断。
角色是这种授权模型的基本单位,它指明了一个用户可以执行的操作,角色的清单中描述了角色所允许的操作。一个角色包含下列要素:
名称,是一个字符串,用于唯一标识一个角色;
操作清单,是一个数组,包含一个或多个操作,表示该角色允许执行的操作集合;
资源种类清单,是一个数组,包含一个或多个资源种类,表示该角色允许操作的资源的种类集合;
资源名称清单,是一个数组,包含若干个资源名称,表示该角色允许操作的资源的名称集合。
判断一个角色能否对某个资源执行某个操作的过程是:
检查该角色的操作清单,如果该角色的操作清单中不包含该操作,且该角色的操作清单中也不包含字符串 *,那么不能执行该操作;
检查该角色的资源种类清单,如果该角色的资源种类清单中不包含该资源的种类,且该角色的资源种类清单中也不包含字符串 *,那么不能执行该操作;
检查该角色的资源名称清单,如果该角色的资源名称清单中不包含该资源的名称,且该角色的资源名称清单不是空数组,那么不能执行该操作;
允许执行该操作。
例如,假设有某个角色 Doorman,其允许执行的操作有 Open 和 Close,其允许操作的资源类型有 Door,其允许操作的资源名称有 FrontDoor 和 BackDoor。
如果某用户与这个角色关联,那么该用户可以对名为 FrontDoor 的 Door 执行 Open 操作,但是不能对 BackDoor 的 Door 执行 Delete 操作。
同时,一个角色能允许进行的操作可以用通配符来表示。例如,另有一个角色 Admin,其允许执行的操作有 *,允许操作的资源类型是 *,其允许操作的资源名称列表为空,
那么与该角色关联的所有用户可以执行任何操作。值得注意的是,一个角色的操作清单,只能用允许列表的方式列举该角色允许进行的操作,而不能禁止角色进行某个操作。
角色关联指明了一个用户和一个或多个角色之间的关系。一个角色关联包含下列要素:
角色名称,是一个字符串,用于指明一个角色;
授权对象清单,是一个数组,包含一个或多个用户名称或者用户组名称,表示该角色关联的用户和用户组的集合。
判断一个用户能否执行某个操作的过程是:
检查所有的角色关联的授权对象清单,如果清单中包含该用户的名称,或者该清单中包含该用户所属的某一个用户组的名称,那么选取该角色关联所关联的角色;
对于所有被选取的角色,判断这些角色是否能对该资源执行该操作,如果所有角色都不能执行该操作,那么不能执行该操作;
允许执行该操作。
由此可见,一个角色关联可以将一个角色与多个用户或用户组关联起来。例如,如果有一个角色关联,其关联的角色名称为 Doorman,其关联的用户和用户组清单为
用户 foo1、用户 foo2、用户组 bar。那么这些用户会与 Doorman 角色关联:
名为 foo1 的用户,属于用户组 bar;
名为 foo2 的用户,属于用户组 barz;
名为 foo3 的用户,属于用户组 bar 和 barz。
但是,属于用户组 barz 的名为 foo4 的用户不能与 Doorman 的角色关联。
从上述判断规则可以知道,一个用户可能与多个角色相关联,在这种情况下,该用户允许进行的操作是这些角色被允许进行的操作集合的并集。
输入格式
从标准输入读入数据。
输入的第一行包含三个正整数 、、,分别表示角色数量、角色关联数量和待检查的操作数量。
输入接下来的 行中,每行表示一个角色,包括空格分隔的若干元素,依次为:
一个字符串,表示该角色的名称;
一个正整数 ,表示操作清单中包含的操作数量;
个字符串,依次表示操作清单中的操作;
一个正整数 ,表示资源种类清单中包含的资源种类的数量;
个字符串,依次表示资源种类清单中的资源种类;
一个非负整数 ,表示资源名称清单中包含的资源名称的数量;
个字符串,依次表示资源名称清单中的资源名称。
输入接下来的 行中,每行表示一个角色关联,包括空格分隔的若干元素,依次为:
一个字符串,表示该角色关联的角色名称;
一个正整数 ,表示授权对象清单中包含的授权对象的数量;
个字符串,每两个表示授权对象清单中的授权对象,前一个字符串为 u 或 g,分别表示这个授权对象是一个用户名称或者用户组名称,后一个字符串为用户名称或者用户组名称。
输入接下来的 行中,每行表示一个待授权的行为,包括空格分隔的若干元素,依次为:
一个字符串,表示执行该操作的用户名称;
一个正整数 ,表示该用户所属的用户组的数量;
个字符串,依次表示该用户所属的用户组的名称;
一个字符串,表示待查操作的名称;
一个字符串,表示被操作的资源种类;
一个字符串,表示被操作的资源名称。
输出格式
输出到标准输出。
输出 行,每行表示一个操作是否可以被执行,0 表示不能执行,1 表示可以执行。
样例输入
1 2 3
op 1 open 1 door 0
op 1 g sre
op 1 u xiaop
xiaoc 2 sre ops open door room302
xiaop 1 ops open door room501
xiaoc 2 sre ops remove door room302
Data
样例输出
1
1
0
Data
样例解释
在本例中,定义了一个名为 op 的角色,授予了对任意 door 类型的对象的 open 操作的权限,同时定义了两个指向 op 的角色关联。
注意,可以针对一个角色定义多于一个角色关联。本例给出了三个待授权的行为。其中,第一个行为,授权的主体用户是 xiaoc,
该用户所属的用户组 sre 被关联 op 角色,因此可以执行开门动作。第二个行为中,授权的主体用户是 xiaop,
该用户被直接关联了 op 角色,因此也可以执行开门动作。第三个行为中,授权的主体用户仍是 xiaoc,关联的角色仍为 op。但是,
由于 op 角色并未被授予 remove 操作的权限,因此该动作被拒绝。
//#include "debug.h"
//using namespace debug;
#include <bits/stdc++.h>
using namespace std;
using Uset=unordered_set<string>;
class Role{
public:
int nv;
Uset nvset;//操作
int no;
Uset noset;//资源 类型
int nn;
Uset nnset;//资源名
};
unordered_map<string,Role> nrole;
unordered_map<string,Uset> urolemp,grolemp;
int n,m,q;
char input[1000];
void InputString(string &s)
{
scanf("%s",input);
s=string(input);
}
bool IsOperated(string &username,unordered_set<string> &group,string &operatname,string &sourcetype,string &sourcename)
{
if(urolemp.find(username)!=urolemp.end())
for(auto &rolename:urolemp[username])
{
Role &role=nrole[rolename];
bool op1=false,op2=false,op3=false;
if(role.nvset.find(string("*"))!=role.nvset.end() || role.nvset.find(operatname)!=role.nvset.end())
op1=true;
else continue;
if(role.noset.find(string("*"))!=role.noset.end() || role.noset.find(sourcetype)!=role.noset.end())
op2=true;
else continue;
if(role.nn==0 || role.nnset.find(string("*"))!=role.nnset.end() || role.nnset.find(sourcename)!=role.nnset.end())
op3=true;
else continue;
return true;
}
for(auto &groupname:group)
{
if(grolemp.find(groupname)!=grolemp.end())
for(auto &rolename:grolemp[groupname])
{
Role &role=nrole[rolename];
bool op1=false,op2=false,op3=false;
if(role.nvset.find(string("*"))!=role.nvset.end() || role.nvset.find(operatname)!=role.nvset.end())
op1=true;
else continue;
if(role.noset.find(string("*"))!=role.noset.end() || role.noset.find(sourcetype)!=role.noset.end())
op2=true;
else continue;
if(role.nn==0 || role.nnset.find(string("*"))!=role.nnset.end() || role.nnset.find(sourcename)!=role.nnset.end())
op3=true;
else continue;
return true;
}
}
return false;
}
int main()
{
cin >> n >> m >> q;
for(int j=0;j<n;j++)
{
string rolename;
InputString(rolename);
Role role;
cin >> role.nv;
for(int i=0;i<role.nv;i++)
{
string name;
InputString(name);
role.nvset.insert(name);
}
cin >> role.no;
for(int i=0;i<role.no;i++)
{
string name;
InputString(name);
role.noset.insert(name);
}
cin >> role.nn;
for(int i=0;i<role.nn;i++)
{
string name;
InputString(name);
role.nnset.insert(name);
}
nrole[rolename]=role;
}
for(int i=0;i<m;i++)
{
string rolename;
InputString(rolename);
int ns;
cin >> ns;
for(int j=0;j<ns;j++)
{
string type,name;
InputString(type);
InputString(name);
if(type==string("u"))
{
urolemp[name].insert(rolename);
}
else
{
grolemp[name].insert(rolename);
}
}
}
for(int i=0;i<q;i++)
{
string username;
InputString(username);
int ng;
cin >> ng;
unordered_set<string> group;
for(int j=0;j<ng;j++)
{
string groupname;
InputString(groupname);
group.insert(groupname);
}
string operatname,sourcetype,sourcename;
InputString(operatname);
InputString(sourcetype);
InputString(sourcename);
cout << IsOperated(username,group,operatname,sourcetype,sourcename) << endl;
}
return 0;
}