diff options
author | Zuul <zuul@review.opendev.org> | 2019-07-31 18:43:52 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2019-07-31 18:43:52 +0000 |
commit | 259627f9a0a3d78a1d8b9043445b854f1bf7d291 (patch) | |
tree | f279691a0210d779b8e6bf42b2d311b375d2f659 /zuul/model.py | |
parent | df1d3f1c3b8397c46914d3d92e203101b4661bda (diff) | |
parent | 7a622a5823893e1d255bbd0defb84f36c0f310d1 (diff) | |
download | zuul-259627f9a0a3d78a1d8b9043445b854f1bf7d291.tar.gz |
Merge "Add Authorization Rules configuration"
Diffstat (limited to 'zuul/model.py')
-rw-r--r-- | zuul/model.py | 118 |
1 files changed, 117 insertions, 1 deletions
diff --git a/zuul/model.py b/zuul/model.py index efb4e650c..de2d61ff5 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -27,6 +27,8 @@ import textwrap import types import itertools +import jsonpath_rw + from zuul import change_matcher from zuul.lib.config import get_default from zuul.lib.artifacts import get_artifacts_from_result_data @@ -3445,16 +3447,18 @@ class UnparsedAbideConfig(object): """A collection of yaml lists that has not yet been parsed into objects. - An Abide is a collection of tenants. + An Abide is a collection of tenants and access rules to those tenants. """ def __init__(self): self.tenants = [] + self.admin_rules = [] self.known_tenants = set() def extend(self, conf): if isinstance(conf, UnparsedAbideConfig): self.tenants.extend(conf.tenants) + self.admin_rules.extend(conf.admin_rules) return if not isinstance(conf, list): @@ -3470,6 +3474,8 @@ class UnparsedAbideConfig(object): self.tenants.append(value) if 'name' in value: self.known_tenants.add(value['name']) + elif key == 'admin-rule': + self.admin_rules.append(value) else: raise ConfigItemUnknownError() @@ -4239,6 +4245,8 @@ class Tenant(object): # The per tenant default ansible version self.default_ansible_version = None + self.authorization_rules = [] + def _addProject(self, tpc): """Add a project to the project index @@ -4431,6 +4439,7 @@ class UnparsedBranchCache(object): class Abide(object): def __init__(self): + self.admin_rules = OrderedDict() self.tenants = OrderedDict() # project -> branch -> UnparsedBranchCache self.unparsed_project_branch_cache = {} @@ -4621,3 +4630,110 @@ class WebInfo(object): if self.tenant: d['tenant'] = self.tenant return d + + +# AuthZ models + +class AuthZRule(object): + """The base class for authorization rules""" + + def __ne__(self, other): + return not self.__eq__(other) + + +class ClaimRule(AuthZRule): + """This rule checks the value of a claim. + The check tries to be smart by assessing the type of the tested value.""" + def __init__(self, claim=None, value=None): + super(ClaimRule, self).__init__() + self.claim = claim or 'sub' + self.value = value + + def __call__(self, claims): + matches = [match.value + for match in jsonpath_rw.parse(self.claim).find(claims)] + if len(matches) == 1: + match = matches[0] + if isinstance(match, list): + return self.value in match + elif isinstance(match, str): + return self.value == match + else: + # unsupported type - don't raise, but this should be notified + return False + else: + # TODO we should differentiate no match and 2+ matches + return False + + def __eq__(self, other): + if not isinstance(other, ClaimRule): + return False + return (self.claim == other.claim and self.value == other.value) + + def __repr__(self): + return '<ClaimRule "%s":"%s">' % (self.claim, self.value) + + def __hash__(self): + return hash(repr(self)) + + +class OrRule(AuthZRule): + + def __init__(self, subrules): + super(OrRule, self).__init__() + self.rules = set(subrules) + + def __call__(self, claims): + return any(rule(claims) for rule in self.rules) + + def __eq__(self, other): + if not isinstance(other, OrRule): + return False + return self.rules == other.rules + + def __repr__(self): + return '<OrRule %s>' % (' || '.join(repr(r) for r in self.rules)) + + def __hash__(self): + return hash(repr(self)) + + +class AndRule(AuthZRule): + + def __init__(self, subrules): + super(AndRule, self).__init__() + self.rules = set(subrules) + + def __call__(self, claims): + return all(rule(claims) for rule in self.rules) + + def __eq__(self, other): + if not isinstance(other, AndRule): + return False + return self.rules == other.rules + + def __repr__(self): + return '<AndRule %s>' % (' && '.join(repr(r) for r in self.rules)) + + def __hash__(self): + return hash(repr(self)) + + +class AuthZRuleTree(object): + + def __init__(self, name): + self.name = name + # initialize actions as unauthorized + self.ruletree = None + + def __call__(self, claims): + return self.ruletree(claims) + + def __eq__(self, other): + if not isinstance(other, AuthZRuleTree): + return False + return (self.name == other.name and + self.ruletree == other.ruletree) + + def __repr__(self): + return '<AuthZRuleTree [ %s ]>' % self.ruletree |