def forwards_func(apps, schema_editor): # add AuthzAction AuthzAction = apps.get_model('noclook', 'AuthzAction') list_aa, created = AuthzAction.objects.get_or_create(name="list") # get storage and guard storage, guard = sriutils.get_vakt_storage_and_guard() Context = apps.get_model('noclook', 'Context') DjPolicy = apps.get_model('djangovakt', 'Policy') # iterate over all the existent contexts to add # a list policy for each of them all_contexts = Context.objects.all() for context in all_contexts: # check if the policy exist first qs = DjPolicy.objects.filter(doc__actions__0__val=list_aa.name) qs = qs.filter(doc__context__module__elem__in=(context.name,)) if not qs.exists(): policy = Policy( uuid.uuid4(), actions=[vakt_rules.Eq(list_aa.name)], resources=[vakt_rules.Any()], subjects=[srirules.HasAuthAction(list_aa, context)], context={ 'module': srirules.ContainsElement(context.name) }, effect=ALLOW_ACCESS, description='Automatically created policy' ) storage.add(policy)
def forwards_func(apps, schema_editor): # get storage and guard storage, guard = sriutils.get_vakt_storage_and_guard() # create policies using storage instead Context = apps.get_model('noclook', 'Context') AuthzAction = apps.get_model('noclook', 'AuthzAction') # iterate over all existent contexts and authzactions # and create policies for each of them all_contexts = Context.objects.all() rw_authzactions = AuthzAction.objects.filter(name__in=( sriutils.READ_AA_NAME, sriutils.WRITE_AA_NAME, )) # add read and write policies for context in all_contexts: for authzaction in rw_authzactions: policy = Policy( uuid.uuid4(), actions=[vakt_rules.Eq(authzaction.name)], resources=[srirules.BelongsContext(context)], subjects=[srirules.HasAuthAction(authzaction, context)], context={'module': srirules.ContainsElement(context.name)}, effect=ALLOW_ACCESS, description='Automatically created policy') storage.add(policy) # add admin policies admin_aa = AuthzAction.objects.get(name=sriutils.ADMIN_AA_NAME) for context in all_contexts: policy = Policy( uuid.uuid4(), actions=[vakt_rules.Eq(admin_aa.name)], resources=[vakt_rules.Any()], subjects=[srirules.HasAuthAction(admin_aa, context)], context={'module': srirules.ContainsElement(context.name)}, effect=ALLOW_ACCESS, description='Automatically created policy') storage.add(policy)
def test_crud(self): for dbpolicy in self.storage.get_all(): self.storage.delete(dbpolicy.uid) # create policies self.policy1 = Policy( uuid.uuid4(), actions=[vrules.In('get', 'list', 'read')], resources=[vrules.StartsWith('repos/google/tensor')], subjects=[{ 'name': vrules.Any(), 'role': vrules.Any() }], context={'module': vrules.Eq('Test')}, effect=ALLOW_ACCESS, description= 'Grant read-access for all Google repositories starting with "tensor" to any User' ) self.policy2 = Policy( uuid.uuid4(), actions=[vrules.In('delete', 'prune', 'exterminate')], resources=[vrules.StartsWith('repos/')], subjects=[{ 'name': vrules.Any(), 'role': vrules.Eq('admin') }], context={'module': vrules.Eq('Test')}, effect=ALLOW_ACCESS, description='Grant admin access') ## Create (add) self.storage.add(self.policy1) # try to add it again, it should raise a exception try: self.storage.add(self.policy1) raise Exception( "The DjangoStorage should't store a policy more than once") except PolicyExistsError: pass ## Read # get test_policy = self.storage.get(self.policy1.uid) assert test_policy, "The storage doesn't return a policy from get" assert self.policy1.uid == test_policy.uid, "These policies must be equal" must_be_none = self.storage.get(self.policy2.uid) assert not must_be_none, "This policy shouldn't exists" # get all self.storage.add(self.policy2) test_policies = self.storage.get_all() assert test_policies, "This should return a policies list" assert test_policies[0].uid == self.policy1.uid \ and test_policies[1].uid == self.policy2.uid,\ "The returned list should match" ## Update policy1b = Policy(self.policy1.uid, actions=[vrules.In('get', 'list')], resources=[{ 'category': vrules.Eq('administration'), 'sub': vrules.In('panel', 'switch') }], subjects=[{ 'name': vrules.Any(), 'role': vrules.NotEq('developer') }], effect=ALLOW_ACCESS, context={'module': vrules.Eq('Test')}, description=""" Allow access to administration interface subcategories: 'panel', 'switch' if user is not a developer and came from local IP address. """) self.storage.update(policy1b) test_policy = self.storage.get(self.policy1.uid) assert self.policy1.uid == test_policy.uid and\ test_policy.to_json() == policy1b.to_json(),\ "The test policy values doesn't match with the updated version" ## Delete self.storage.delete(self.policy2.uid) test_policies = self.storage.get_all() test_none = self.storage.get(self.policy2.uid) assert not test_none, "This policy shouldn't be stored as it has been deleted" assert len(test_policies) == 1 and test_policies[0].uid == self.policy1.uid, \ "The returned list should match" ## Find for inquiry # check a matching policy inquiry1 = Inquiry(action='get', resource='repos/google/tensorflow', subject={ 'name': 'Jane', 'role': 'admin' }, context={'module': 'Test'}) matching_policies = self.storage.find_for_inquiry( inquiry1, self.guard.checker) assert matching_policies, "The matching policies list should not be empty" # check it doesn't match and inquiry inquiry2 = Inquiry(action='delete', resource='repos/google/tensorflow', subject={ 'name': 'Max', 'role': 'developer' }, context={'module': 'Test'}) matching_policies = self.storage.find_for_inquiry( inquiry2, self.guard.checker) assert not matching_policies, "The matching policies list should be empty"
def test_rulechecker(self): # ensure empty policy set for dbpolicy in self.storage.get_all(): self.storage.delete(dbpolicy.uid) # add policies policy1 = Policy( uuid.uuid4(), actions=[vrules.Eq('read')], resources=[vrules.StartsWith('forum/')], subjects=[{ 'group': vrules.In('can_read', 'can_write', 'can_admin') }], context={'module': vrules.Eq('forum')}, effect=ALLOW_ACCESS, description= 'Grant read-access to the forum section to users with a certain profile' ) policy2 = Policy( uuid.uuid4(), actions=[vrules.Eq('write')], resources=[vrules.StartsWith('forum/')], subjects=[{ 'group': vrules.In('can_write', 'can_admin') }], context={'module': vrules.Eq('forum')}, effect=ALLOW_ACCESS, description= 'Grant write-access to the forum section to users with a certain profile' ) policy3 = Policy( uuid.uuid4(), actions=[vrules.Eq('admin')], resources=[vrules.StartsWith('forum/')], subjects=[{ 'group': vrules.In('can_admin') }], context={'module': vrules.Eq('forum')}, effect=ALLOW_ACCESS, description= 'Grant admin-access to the forum section to users with a certain profile' ) policy4 = Policy( uuid.uuid4(), actions=[vrules.Any()], resources=[vrules.StartsWith('forum/')], subjects=[{ 'group': vrules.NotIn('can_read', 'can_write', 'can_admin') }], context={'module': vrules.Eq('forum')}, effect=DENY_ACCESS, description='Deny access to any user without a group defined') self.storage.add(policy1) self.storage.add(policy2) self.storage.add(policy3) self.storage.add(policy4) # forge successful inquiry for 1st policy inqu1_ok = Inquiry(action='read', resource='forum/users/list', subject={ 'name': 'Jane', 'group': 'can_read' }, context={'module': 'forum'}) assert self.guard.is_allowed( inqu1_ok), "This inquiry should be allowed" inq1_ko = Inquiry(action='read', resource='forum/users/list', subject={ 'name': 'James', 'group': 'new_users' }, context={'module': 'forum'}) assert not self.guard.is_allowed( inq1_ko), "This inquiry should be denied"
def create_policy(self, req, internal=False): # '''Creates a policy based on the JSON-type request body and stores it onto the PRP.''' if internal: req_json = req else: if not req.data: return False, "ERROR: request body is empty" # load JSON body try: req_json = req.json except: return False, "ERROR: malformed JSON - syntax error" try: #### # 'subjects' is mandatory and has to be a list of key-value subject definitions #### if type(req_json['subjects']) is not list: return False, "ERROR: malformed access JSON - 'subjects' must be a list." subject = [] for s in req_json['subjects']: sub = {} for k in s: if k in ['admin', 'student', 'teacher']: sub.update({ k: (rules.Truthy() if s[k].lower() == 'true' else rules.Falsy()) }) elif k in ['student_courses', 'teacher_courses']: sub.update({k: rules.AnyIn(*s[k])}) else: sub.update({k: rules.string.Equal(s[k])}) if 'admin' not in sub: sub.update({'admin': rules.Falsy()}) subject.append(sub) if not subject: return False, "ERROR: malformed access JSON - 'subjects' doesn't have attributes defined." #### # 'actions' value has to be a json list, although not mandatory #### action = [rules.Any()] if 'actions' in req_json: if type(req_json['actions']) is not list: return False, "ERROR: malformed access JSON - 'actions' must be a list." action = [rules.string.Equal(a) for a in req_json['actions']] #### # 'resource' is not mandatory, defaults to any element #### resource = [] if 'resources' in req_json: if type(req_json['resources']) is not list: return False, "ERROR: malformed access JSON - 'actions' must be a list." for s in req_json['resources']: elem_res = {} for k in s: elem_res[k] = rules.string.Equal(s[k]) #resource = [{k : rules.string.Equal(s[k])} for s in req_json['resources']] resource.append(elem_res) if not resource: resource = [rules.Any()] #### # 'context' is not mandatory, defaults to empty #### context = {} if 'context' in req_json: for k in req_json['context']: if k == 'hour': if 'from' in req_json['context'][ 'hour'] and 'to' in req_json['context']['hour']: context['hour'] = rules.And( rules.GreaterOrEqual( ABAC.daytime_in_s(time=req_json['context'] ['hour']['from'])), rules.LessOrEqual( ABAC.daytime_in_s(time=req_json['context'] ['hour']['to']))) else: return False, "ERROR: Malformed access JSON - context's hours needs 'from' and 'to' attributes!" elif k == 'date': if 'from' in req_json['context'][ 'date'] and 'to' in req_json['context']['date']: context['date'] = rules.And( rules.GreaterOrEqual( ABAC.unix_timestamp( req_json['context']['date']['from'])), rules.LessOrEqual( ABAC.unix_timestamp( req_json['context']['date']['to']))) elif 'from' in req_json['context']['date']: context['date'] = rules.GreaterOrEqual( ABAC.unix_timestamp( req_json['context']['date']['from'])) elif 'to' in req_json['context']['date']: context['date'] = rules.LessOrEqual( ABAC.unix_timestamp( req_json['context']['date']['to'])) else: return False, "ERROR: Malformed access JSON - context's date needs 'from' and 'to' attributes!" elif k == 'ip': internal_rule = rules.Or(rules.CIDR('10.0.0.0/8'), rules.CIDR('172.16.0.0/12'), rules.CIDR('192.168.0.0/16')) if req_json['context']['ip'].lower() == "internal": context['ip'] = internal_rule else: context['ip'] = rules.Any() else: context[k] = rules.string.Equal(req_json['context'][k]) #### # 'description is not mandatory, defaults to None #### description = None if 'description' in req_json: description = req_json['description'] #### # 'effect' is not mandatory, defaults to allow #### effect = ALLOW_ACCESS # default effect - not needed in JSON if 'effect' in req_json: effect = ALLOW_ACCESS if 'allow' in req_json['effect'].lower( ) else DENY_ACCESS except KeyError as e: return False, f"ERROR: malformed policy JSON - invalid '{str(e)}' key." # add uid to JSON uid = str(uuid4()) req_json.update({'uid': uid}) self._raw_policy_collection.insert_one(req_json) self._storage.add( Policy(uid, subjects=subject, effect=effect, resources=resource, actions=action, context=context, description=description)) return True, "OK"