本文地址:http://blog.csdn.net/spch2008/article/details/9698667
当用户在命令行中输入 quantum port-show 时,最终将会转到 quantum\api\v2\base.py的Controller中的show方法。
def show(self, request, id, **kwargs):
return {self._resource:
self._view(self._item(request,
id,
do_authz=True,
field_list=field_list,
parent_id=parent_id),
fields_to_strip=added_fields)}
def _item(self, request, id, do_authz=False, field_list=None,
parent_id=None):
action = self._plugin_handlers[self.SHOW]
obj_getter = getattr(self._plugin, action)
obj = obj_getter(request.context, id, **kwargs)
if do_authz:
policy.enforce(request.context, action, obj, plugin=self._plugin)
return obj
而在show中,会跳转到_item方法中,该方法调用policy.enforce进行权限认证。
quantum\policy.py中
def enforce(context, action, target, plugin=None):
init()
real_target = _build_target(action, target, plugin, context)
match_rule = _build_match_rule(action, real_target)
credentials = context.to_dict()
return policy.check(match_rule, real_target, credentials,
exceptions.PolicyNotAuthorized, action=action)
在这里,假设用户输入的是quantum port-show命令,则action为get_port, context为
上一篇所述的Context对象.而target为一个用户查询的结果,是一个字典,内容格式如下:
def _make_port_dict(self, port, fields=None):
res = {"id": port["id"],
'name': port['name'],
"network_id": port["network_id"],
'tenant_id': port['tenant_id'],
"mac_address": port["mac_address"],
"admin_state_up": port["admin_state_up"],
"status": port["status"],
"fixed_ips": [{'subnet_id': ip["subnet_id"],
'ip_address': ip["ip_address"]}
for ip in port["fixed_ips"]],
"device_id": port["device_id"],
"device_owner": port["device_owner"]}
return self._fields(res, fields)
/etc/quantum/policy.json内容,只列出get_port涉及到的内容。
"admin_or_owner": [["role:admin"], ["tenant_id:%(tenant_id)s"]],
"get_port": [["rule:admin_or_owner"]],
init进行初始化操作,从policy.json文件中读取权限信息,交给_set_rules处理。
def _set_rules(data):
default_rule = 'default'
policy.set_rules(policy.Rules.load_json(data, default_rule))
set_rules设置全局变量_rules,变量指向一个Rules对象,存放所有加载的规则。Rules继承于dict,即Rules本身具有字典功能。
内容为:
{ 'get_port' : RuleCheck('rule', 'admin_or_owner'), 'admin_or_owner' : OrCheck([RoleCheck('role', 'admin'),GenericCheck('tenant_id','%(network_tenant_id)s')])}
具体加载过程在下一篇中介绍。
def _build_match_rule(action, target):
match_rule = policy.RuleCheck('rule', action)
return match_rule
返回一个RuleCheck对象,self.kind = 'rule', self.match = action = 'get_port'。
credentials = context.to_dict()调用to_dict得到一个字典,包含验证所需要的用户信息。
def check(rule, target, creds, exc=None, *args, **kwargs):
if isinstance(rule, BaseCheck):
result = rule(target, creds)
return result
验证过程,调用match_rule(RuleCheck对象)的call方法。
@register("rule")
class RuleCheck(Check):
def __call__(self, target, creds):
"""
Recursively checks credentials based on the defined rules.
"""
return _rules[self.match](target, creds)
是一个递归调用的过程,由上可知,self.match值为'get_port',_rules['get_port']值为RuleCheck('rule', 'admin_or_owner'),继续调用
__call__方法。此时self.match值为‘admin_or_owner’,_rules['admin_or_owner']值为OrCheck([RoleCheck('role', 'admin'),GenericCheck('tenant_id','%(network_tenant_id)s')])
调用OrCheck的call方法:
def __call__(self, target, cred):
for rule in self.rules:
if rule(target, cred):
return True
return False
只要有一个规则符合条件,即返回真。先转向RoleCheck的call方法。
@register("role")
class RoleCheck(Check):
def __call__(self, target, creds):
"""Check that there is a matching role in the cred dict."""
return self.match.lower() in [x.lower() for x in creds['roles']]
self.match为admin, 检测creds用户的角色是否有admin,如果有,返回真,否则,验证失败。
如果验证失败,则进行GenericCheck验证。如果二者都验证失败,则说明用户没有进行此操作的权限。
其实思路很简单:对于一个action(get_port)对应一个RuleCheck对象,由该对象转向相应规则。
RuleCheck('rule', 'get_port') 在第一个递归调用时候,找到_rules = {'get_port': RuleCheck('rule', 'admin_or_owner'},这样,get_port找到对应的RuleCheck。
再进行一次递归调用,转向OrCheck('[RoleCheck('role', 'admin'),GenericCheck('tenant_id','%(network_tenant_id)s')]))