def test_find_for_inquiry_with_exact_string_checker(self, st): st.add(Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add(Policy('2', subjects=['maxim'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add(Policy('3', subjects=['sam', 'nina'])) st.add(Policy('4', subjects=[Eq('sam'), Eq('nina')])) inquiry = Inquiry(subject='max', action='get', resource='books') found = st.find_for_inquiry(inquiry, StringExactChecker()) found = list(found) assert 1 == len(found) assert '1' == found[0].uid
def test_find_for_inquiry_returns_generator(self, st): st.add(Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) st.add(Policy('2', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) inquiry = Inquiry(subject='max', action='get', resource='comics') found = st.find_for_inquiry(inquiry) assert isinstance(found, types.GeneratorType) l = [] for p in found: l.append(p.uid) assert 2 == len(l)
def test_find_for_inquiry(st): st.add(Policy('1', subjects=['max', 'bob'])) st.add(Policy('2', subjects=['sam', 'nina'])) st.add(Policy('3', subjects=[Eq('max'), Eq('bob')])) inquiry = Inquiry(subject='sam', action='get', resource='books') found = st.find_for_inquiry(inquiry) found = list(found) assert 3 == len(found) assert ['max', 'bob'] == found[0].subjects or \ ['max', 'bob'] == found[1].subjects or \ ['max', 'bob'] == found[2].subjects
def test_find_for_inquiry_returns_existing_policies(self, st, checker, expect_number): st.add(Policy('1', subjects=['<[mM]ax>', '<.*>'], actions=['delete'], resources=['server'])) st.add(Policy('2', subjects=['Ji<[mM]+>'], actions=['delete'], resources=[r'server<\s*>'])) st.add(Policy('3', subjects=['sam<.*>', 'foo'])) st.add(Policy('5', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add(Policy('4', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add(Policy('6', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, checker) found = list(found) assert expect_number == len(found)
def test_find_for_inquiry_with_rules_checker(self, st): assertions = unittest.TestCase('__init__') st.add(Policy(1, subjects=[{'name': Equal('Max')}], actions=[{'foo': Equal('bar')}])) st.add(Policy(2, subjects=[{'name': Equal('Max')}], actions=[{'foo': Equal('bar2')}])) st.add(Policy(3, subjects=['sam', 'nina'])) st.add(Policy(4, actions=[r'<\d+>'], effect=ALLOW_ACCESS, resources=[r'<\w{1,3}>'], subjects=[r'<\w{2}-\d+>'])) st.add(Policy(5, subjects=[{'name': Equal('Jim')}], actions=[{'foo': Equal('bar3')}])) inquiry = Inquiry(subject={'name': 'max'}, action='get', resource='books') found = st.find_for_inquiry(inquiry, RulesChecker()) found = list(found) assert 3 == len(found) found_uids = list(map(operator.attrgetter('uid'), found)) found_uids.sort() assertions.assertListEqual(['1', '2', '5'], found_uids)
def test_regex_checker_find_for_inquiry_policies_count_return_by_storage(self, st, dialect, expect_number): if st.dialect != dialect: pytest.skip('skipping for %s dialect' % dialect) st.add(Policy('1', subjects=['<[mM]ax>', '<.*>'], actions=['delete'], resources=['server'])) st.add(Policy('2', subjects=['Ji<[mM]+>'], actions=['delete'], resources=[r'server<\s*>'])) st.add(Policy('3', subjects=['sam<.*>', 'foo'])) st.add(Policy('5', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add(Policy('6', subjects=['Ji<[mM]+>'], actions=[r'<[a-zA-Z]{6}>'], resources=['serve<(r|rs)>'])) st.add(Policy('7', subjects=['Ji<[mM]+>'], actions=[r'<[a-zA-Z]{6}>'], resources=['serve<(u|rs)>'])) st.add(Policy('8', subjects=['Ji<[mM]+>'], actions=[r'<[a-zA-Z]{6}>'], resources=['serve<(u|rs)>', 'server'])) st.add(Policy('40', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add(Policy('60', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, RegexChecker()) found = list(found) assert expect_number == len(found)
def test_guard_can_use_specific_policies_message_class(audit_log): # setup logger consumer log_capture_str = io.StringIO() h = logging.StreamHandler(log_capture_str) h.setFormatter(logging.Formatter('decs: %(deciders)s, candidates: %(candidates)s')) h.setLevel(logging.INFO) audit_log.setLevel(logging.INFO) audit_log.addHandler(h) # setup guard st = MemoryStorage() st.add(PolicyAllow('122')) st.add(PolicyAllow('123', actions=['<.*>'], resources=['<.*>'], subjects=['<.*>'])) st.add(PolicyDeny('124', actions=['<.*>'], resources=['<.*>'], subjects=['<.*>'])) st.add(PolicyDeny('125', actions=['<.*>'], resources=['<.*>'], subjects=['<.*>'])) g = Guard(st, RegexChecker(), audit_policies_cls=PoliciesCountMsg) # Run tests g.is_allowed(Inquiry(action='get', subject='Kim', resource='TV')) assert 'decs: count = 1, candidates: count = 3' == log_capture_str.getvalue().strip()
def test_not_allowed_when_similar_policies_have_at_least_one_deny_access(): st = MemoryStorage() policies = ( Policy( uid='1', effect=ALLOW_ACCESS, subjects=['foo'], actions=['bar'], resources=['baz'], ), Policy( uid='2', effect=DENY_ACCESS, subjects=['foo'], actions=['bar'], resources=['baz'], ), ) for p in policies: st.add(p) g = Guard(st, RegexChecker()) assert not g.is_allowed( Inquiry(subject='foo', action='bar', resource='baz'))
def test_find_for_inquiry_with_regex_checker_for_mongodb_prior_to_4_2( self, st): # mock db server version for this test st.db_server_version = (3, 4, 0) st.add(Policy('1', subjects=['<[mM]ax>', '<.*>'])) st.add(Policy('2', subjects=['sam<.*>', 'foo'])) st.add( Policy('3', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add( Policy('3.1', subjects=['Jim'], actions=[r'del<\w+>'], resources=['server'])) st.add(Policy('4', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add(Policy('5', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, RegexChecker()) found = list(found) # should return all string-based polices, but not only matched ones assert 4 == len(found) assert ['1', '2', '3', '3.1'] == sorted(map(attrgetter('uid'), found))
def test_find_for_inquiry_for_empty_database(self, st): assert [] == list(st.find_for_inquiry(Inquiry(), RegexChecker()))
def test_find_for_inquiry_with_unknown_checker(self, st): st.add(Policy('1')) inquiry = Inquiry(subject='sam', action='get', resource='books') with pytest.raises(UnknownCheckerType): list(st.find_for_inquiry(inquiry, Inquiry()))
class TestSQLStorage: @pytest.yield_fixture def session(self): engine = create_test_sql_engine() Base.metadata.create_all(engine) session = scoped_session(sessionmaker(bind=engine)) yield session Base.metadata.drop_all(engine) @pytest.yield_fixture def st(self, session): yield SQLStorage(scoped_session=session) session.remove() def test_add(self, st): id = str(uuid.uuid4()) p = Policy( uid=id, description='foo bar баз', subjects=('Edward Rooney', 'Florence Sparrow'), actions=['<.*>'], resources=['<.*>'], context={ 'secret': Equal('i-am-a-teacher'), }, ) st.add(p) back = st.get(id) assert id == back.uid assert 'foo bar баз' == back.description assert isinstance(back.context['secret'], Equal) st.add( Policy('2', actions=[Eq('get'), Eq('put')], subjects=[Any()], resources=[{ 'books': Eq('Harry') }])) assert '2' == st.get('2').uid assert 2 == len(st.get('2').actions) assert 1 == len(st.get('2').subjects) assert isinstance(st.get('2').subjects[0], Any) assert 1 == len(st.get('2').resources) assert isinstance(st.get('2').resources[0]['books'], Eq) assert 'Harry' == st.get('2').resources[0]['books'].val def test_add_with_bson_object_id(self, st): id = str(ObjectId()) p = Policy( uid=id, description='foo', ) st.add(p) back = st.get(id) assert id == back.uid def test_policy_create_existing(self, st): id = str(uuid.uuid4()) st.add(Policy(id, description='foo')) with pytest.raises(PolicyExistsError): st.add(Policy(id, description='bar')) def test_get(self, st): st.add(Policy('1')) st.add(Policy(2, description='some text')) assert isinstance(st.get('1'), Policy) assert '1' == st.get('1').uid # SQL storage stores all uid as string assert '2' == st.get('2').uid assert 'some text' == st.get('2').description def test_get_nonexistent(self, st): assert None is st.get('123456789') @pytest.mark.parametrize( 'limit, offset, result', [ (500, 0, 200), (101, 1, 101), (500, 50, 150), (200, 0, 200), (200, 1, 199), (199, 0, 199), (200, 50, 150), # (0, 0, 200), --> The output of this test should be 0 as checked by the following parameters (0, 0, 0), (1, 0, 1), (5, 4, 5), ]) def test_get_all(self, st, limit, offset, result): for i in range(200): desc = ''.join(random.choice('abcde') for _ in range(30)) st.add(Policy(str(i), description=desc)) policies = list(st.get_all(limit=limit, offset=offset)) assert result == len(policies) def test_get_all_check_policy_properties(self, st): p = Policy( uid='1', description='foo bar баз', subjects=('Edward Rooney', 'Florence Sparrow'), actions=['<.*>'], resources=['<.*>'], context={ 'secret': Equal('i-am-a-teacher'), }, ) st.add(p) policies = list(st.get_all(100, 0)) assert 1 == len(policies) assert '1' == policies[0].uid assert 'foo bar баз' == policies[0].description assert ['Edward Rooney', 'Florence Sparrow'] == policies[0].subjects assert ['<.*>'] == policies[0].actions assert ['<.*>'] == policies[0].resources assert isinstance(policies[0].context['secret'], Equal) def test_get_all_with_incorrect_args(self, st): for i in range(10): st.add(Policy(str(i), description='foo')) with pytest.raises(ValueError) as e: list(st.get_all(-1, 9)) assert "Limit can't be negative" == str(e.value) with pytest.raises(ValueError) as e: list(st.get_all(0, -3)) assert "Offset can't be negative" == str(e.value) def test_get_all_returns_generator(self, st): st.add(Policy('1')) st.add(Policy('2')) found = st.get_all(500, 0) assert isinstance(found, types.GeneratorType) l = [] for p in found: l.append(p.uid) assert 2 == len(l) @pytest.mark.parametrize('checker, expect_number', [ (None, 5), (RegexChecker(), 3), (RulesChecker(), 2), (StringExactChecker(), 1), (StringFuzzyChecker(), 1), ]) def test_find_for_inquiry_returns_existing_policies( self, st, checker, expect_number): st.add(Policy('1', subjects=['<[mM]ax', '<.*>'])) st.add(Policy('2', subjects=['sam<.*>', 'foo'])) st.add(Policy('3', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add( Policy('4', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add(Policy('5', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, checker) assert expect_number == len(list(found)) def test_find_for_inquiry_with_exact_string_checker(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add( Policy('2', subjects=['maxim'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add(Policy('3', subjects=['sam', 'nina'])) st.add(Policy('4', subjects=[Eq('sam'), Eq('nina')])) inquiry = Inquiry(subject='max', action='get', resource='books') found = st.find_for_inquiry(inquiry, StringExactChecker()) found = list(found) assert 1 == len(found) assert '1' == found[0].uid def test_find_for_inquiry_with_fuzzy_string_checker(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add( Policy('2', subjects=['maxim'], actions=['get'], resources=['books', 'foos'])) st.add( Policy('3', subjects=['Max'], actions=['get'], resources=['books', 'comics'])) st.add(Policy('4', subjects=['sam', 'nina'])) st.add(Policy('5', subjects=[Eq('sam'), Eq('nina')])) inquiry = Inquiry(subject='max', action='et', resource='oo') found = st.find_for_inquiry(inquiry, StringFuzzyChecker()) found = list(found) assert 2 == len(found) ids = [found[0].uid, found[1].uid] assert '1' in ids assert '2' in ids inquiry = Inquiry(subject='Max', action='get', resource='comics') found = st.find_for_inquiry(inquiry, StringFuzzyChecker()) found = list(found) assert 1 == len(found) assert '3' == found[0].uid @pytest.mark.parametrize('policies, inquiry, expected_reference', [ ( [ Policy(uid=1, actions=['get', 'post'], effect=ALLOW_ACCESS, resources=['<.*>'], subjects=['<[Mm]ax>', '<Jim>']), ], Inquiry(action='get', resource='printer', subject='Max'), True, ), ( [ Policy(uid=1, actions=['<.*>'], effect=ALLOW_ACCESS, resources=['<.*>'], subjects=['<.*>']), ], Inquiry(action='get', resource='printer', subject='Max'), True, ), ( [ Policy(uid=1, actions=['<.*>'], effect=ALLOW_ACCESS, resources=['library:books:<.+>'], subjects=['<.*>']), ], Inquiry( action='get', resource='library:books:dracula', subject='Max'), True, ), ( [ Policy(uid=1, actions=[r'<\d+>'], effect=ALLOW_ACCESS, resources=[r'<\w{1,3}>'], subjects=[r'<\w{2}-\d+>']), ], Inquiry(action='12', resource='Pie', subject='Jo-1'), True, ), ]) def test_find_for_inquiry_with_regex_checker(self, st, policies, inquiry, expected_reference): mem_storage = MemoryStorage( ) # it returns all stored policies so we consider Guard as a reference for p in policies: st.add(p) mem_storage.add(p) reference_answer = Guard(mem_storage, RegexChecker()).is_allowed(inquiry) assert expected_reference == reference_answer, 'Check reference answer' assert reference_answer == Guard(st, RegexChecker()).is_allowed(inquiry), \ 'SQL storage should give the same answers as reference' def test_find_for_inquiry_with_rules_checker(self, st): assertions = unittest.TestCase('__init__') st.add( Policy(1, subjects=[{ 'name': Equal('Max') }], actions=[{ 'foo': Equal('bar') }])) st.add( Policy(2, subjects=[{ 'name': Equal('Max') }], actions=[{ 'foo': Equal('bar2') }])) st.add(Policy(3, subjects=['sam', 'nina'])) st.add( Policy(4, actions=[r'<\d+>'], effect=ALLOW_ACCESS, resources=[r'<\w{1,3}>'], subjects=[r'<\w{2}-\d+>'])) st.add( Policy(5, subjects=[{ 'name': Equal('Jim') }], actions=[{ 'foo': Equal('bar3') }])) inquiry = Inquiry(subject={'name': 'max'}, action='get', resource='books') found = st.find_for_inquiry(inquiry, RulesChecker()) found = list(found) assert 3 == len(found) # assertions.assertListEqual([1, 2, 5], list(map(operator.attrgetter('uid'), found))) --> SQL storage treats # UID as strings assertions.assertListEqual(['1', '2', '5'], list(map(operator.attrgetter('uid'), found))) def test_find_for_inquiry_with_unknown_checker(self, st): st.add(Policy('1')) inquiry = Inquiry(subject='sam', action='get', resource='books') with pytest.raises(UnknownCheckerType): list(st.find_for_inquiry(inquiry, Inquiry())) def test_find_for_inquiry_returns_generator(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) st.add( Policy('2', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) inquiry = Inquiry(subject='max', action='get', resource='comics') found = st.find_for_inquiry(inquiry) assert isinstance(found, types.GeneratorType) l = [] for p in found: l.append(p.uid) assert 2 == len(l) def test_update(self, st): # SQL storage stores all uids as string id = str(uuid.uuid4()) policy = Policy(id) st.add(policy) assert id == st.get(id).uid assert None is st.get(id).description assert [] == st.get(id).actions policy.description = 'foo' policy.actions = ['a', 'b', 'c'] st.update(policy) assert id == st.get(id).uid assert 'foo' == st.get(id).description assert ['a', 'b', 'c'] == st.get(id).actions p = Policy('2', actions=[Any()], subjects=[Eq('max'), Eq('bob')]) st.add(p) assert '2' == st.get('2').uid p.actions = [Eq('get')] st.update(p) assert 1 == len(st.get('2').actions) assert 'get' == st.get('2').actions[0].val def test_update_non_existing_does_not_create_anything(self, st): id = str(uuid.uuid4()) st.update(Policy(id, actions=['get'], description='bar')) assert st.get(id) is None def test_delete(self, st): policy = Policy('1') st.add(policy) assert '1' == st.get('1').uid st.delete('1') assert None is st.get('1') def test_delete_nonexistent(self, st): uid = str(ObjectId()) st.delete(uid) assert None is st.get(uid)
def test_json_decode_fails_for_incorrect_data(): with pytest.raises(ValueError): Inquiry.from_json('{')
def test_guard_does_not_log_messages_at_more_than_info_level(audit_log): log_capture_str = io.StringIO() h = logging.StreamHandler(log_capture_str) h.setLevel(logging.WARN) audit_log.setLevel(logging.WARN) audit_log.addHandler(h) g = Guard(MemoryStorage(), RegexChecker()) g.is_allowed(Inquiry()) assert '' == log_capture_str.getvalue().strip() @pytest.mark.skipif(sys.version_info <= (3, 5, 99), reason='unpredictable sorting order for policies uids on python3.5') @pytest.mark.parametrize('inquiry, is_allowed, expect_msg', [ ( Inquiry(action='get', subject='Max', resource='book'), True, 'msg: All matching policies have allow effect | ' + 'effect: allow | ' + 'deciders: [a, b] | ' + 'candidates: [a, b] | ' + "inquiry: <class 'vakt.guard.Inquiry'> <Object ID some_ID>: " + "{'resource': 'book', 'action': 'get', 'subject': 'Max', 'context': {}}", ), ( Inquiry(action='update', subject='Max', resource='book'), True, 'msg: All matching policies have allow effect | ' + 'effect: allow | ' + 'deciders: [a] | ' + 'candidates: [a] | ' +
def test_up(self, storage): migration = Migration1x1x0To1x1x1(storage) # prepare docs that might have been saved by users in v 1.1.0 docs = [ (""" { "_id" : 10, "uid" : 10, "description" : null, "subjects" : [ ], "effect" : "allow", "resources" : [ ], "actions" : [ ], "rules" : { "secret" : "{\\"type\\": \\"vakt.rules.string.StringEqualRule\\", \\"contents\\": {\\"val\\": \\"i-am-a-foo\\"}}", "name":"{\\"type\\": \\"vakt.rules.string.StringEqualRule\\", \\"contents\\":{\\"val\\": \\"Max\\"}}" }} """, """ { "_id" : 10, "actions" : [ ], "description" : null, "effect" : "allow", "resources" : [ ], "rules" : { "name" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "Max" }, "secret" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "i-am-a-foo"} }, "subjects" : [ ], "uid" : 10 } """), (""" { "_id" : 20, "uid" : 20, "description" : "foo bar", "subjects" : [ "<.*>" ], "effect" : "allow", "resources" : [ "<.*>" ], "actions" : [ "<.*>" ], "rules" : { "secret" : "{\\"type\\": \\"vakt.rules.string.StringEqualRule\\", \\"contents\\": {\\"val\\": \\"John\\"}}" } } """, """ { "_id" : 20, "actions" : [ "<.*>" ], "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "rules" : { "secret" : { "py/object": "vakt.rules.string.StringEqualRule", "val": "John"} }, "subjects" : [ "<.*>" ], "uid" : 20 } """), (""" { "_id" : 30, "uid" : 30, "description" : "foo bar", "subjects" : [ "<.*>" ], "effect" : "allow", "resources" : [ "<.*>" ], "actions" : [ "<.*>" ], "rules" : { } } """, """ { "_id" : 30, "actions" : [ "<.*>" ], "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "rules" : { }, "subjects" : [ "<.*>" ], "uid" : 30 } """), (""" { "_id" : 40, "uid" : 40, "description" : null, "subjects" : [ "<.*>" ], "effect" : "allow", "resources" : [ "<.*>" ], "actions" : [ "<.*>" ], "rules" : { "num" : "{\\"type\\": \\"storage.test_mongo_migration.Simple\\", \\"contents\\": {\\"val\\": \\"123\\"}}", "a" : "{\\"type\\": \\"vakt.rules.string.StringEqualRule\\", \\"contents\\": {\\"val\\": \\"foo\\"}}" }} """, """ { "_id" : 40, "actions" : [ "<.*>" ], "description" : null, "effect" : "allow", "resources" : ["<.*>"], "rules" : { "a" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "foo"}, "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "123"} }, "subjects" : ["<.*>"], "uid" : 40 } """), (""" { "_id" : 50, "uid" : 50, "description" : null, "subjects" : [ ], "effect" : "allow", "resources" : [ ], "actions" : [ ], "rules" : { "num" : "{\\"type\\": \\"storage.test_mongo_migration.Simple\\", \\"contents\\": {\\"val\\": \\"46\\"}}" } } """, """ { "_id" : 50, "actions" : [ ], "description" : null, "effect" : "allow", "resources" : [ ], "rules" : { "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "46"} }, "subjects" : [ ], "uid" : 50 } """), ] for (doc, _) in docs: d = b_json.loads(doc) migration.storage.collection.insert_one(d) migration.up() # test no new docs were added and no docs deleted assert len(docs) == len(list(migration.storage.collection.find({}))) # test Policy.from_json() is called without errors for each doc (implicitly) assert len(docs) == len(list(migration.storage.get_all(1000, 0))) # test string contents of each doc for (doc, result_doc) in docs: new_doc = migration.storage.collection.find_one( {'uid': json.loads(doc)['uid']}) expected = result_doc.replace("\n", '').replace(' ', '') actual = json.dumps(new_doc, sort_keys=True).replace("\n", '').replace(' ', '') assert expected == actual # test full guard allowance run if version_info() < (1, 2, 0): # pragma: no cover g = Guard(migration.storage, RegexChecker()) inq = Inquiry(action='foo', resource='bar', subject='Max', context={ 'val': 'foo', 'num': '123' }) assert g.is_allowed(inq)
class TestMongoStorage: @pytest.fixture() def st(self): client = create_client() yield MongoStorage(client, DB_NAME, collection=COLLECTION) client[DB_NAME][COLLECTION].delete_many({}) client.close() def test_add(self, st): id = str(uuid.uuid4()) p = Policy( uid=id, description='foo bar баз', subjects=('Edward Rooney', 'Florence Sparrow'), actions=['<.*>'], resources=['<.*>'], context={ 'secret': Equal('i-am-a-teacher'), }, ) st.add(p) back = st.get(id) assert id == back.uid assert 'foo bar баз' == back.description assert isinstance(back.context['secret'], Equal) st.add( Policy('2', actions=[Eq('get'), Eq('put')], subjects=[Any()], resources=[{ 'books': Eq('Harry') }])) assert '2' == st.get('2').uid assert 2 == len(st.get('2').actions) assert 1 == len(st.get('2').subjects) assert isinstance(st.get('2').subjects[0], Any) assert 1 == len(st.get('2').resources) assert isinstance(st.get('2').resources[0]['books'], Eq) assert 'Harry' == st.get('2').resources[0]['books'].val def test_add_with_bson_object_id(self, st): id = str(ObjectId()) p = Policy( uid=id, description='foo', ) st.add(p) back = st.get(id) assert id == back.uid def test_policy_create_existing(self, st): id = str(uuid.uuid4()) st.add(Policy(id, description='foo')) with pytest.raises(PolicyExistsError): st.add(Policy(id, description='bar')) def test_get(self, st): st.add(Policy('1')) st.add(Policy(2, description='some text')) assert isinstance(st.get('1'), Policy) assert '1' == st.get('1').uid assert 2 == st.get(2).uid assert 'some text' == st.get(2).description def test_get_nonexistent(self, st): assert None is st.get(123456789) @pytest.mark.parametrize('limit, offset, result', [ (50, 0, 20), (11, 1, 11), (50, 5, 15), (20, 0, 20), (20, 1, 19), (19, 0, 19), (20, 5, 15), (0, 0, 0), (0, 10, 0), (1, 0, 1), (5, 4, 5), ]) def test_get_all(self, st, limit, offset, result): for i in range(20): desc = ''.join(random.choice('abcde') for _ in range(30)) st.add(Policy(str(i), description=desc)) policies = list(st.get_all(limit=limit, offset=offset)) assert result == len(policies) def test_get_all_check_policy_properties(self, st): p = Policy( uid='1', description='foo bar баз', subjects=('Edward Rooney', 'Florence Sparrow'), actions=['<.*>'], resources=['<.*>'], context={ 'secret': Equal('i-am-a-teacher'), }, ) st.add(p) policies = list(st.get_all(100, 0)) assert 1 == len(policies) assert '1' == policies[0].uid assert 'foo bar баз' == policies[0].description assert ['Edward Rooney', 'Florence Sparrow'] == policies[0].subjects assert ['<.*>'] == policies[0].actions assert ['<.*>'] == policies[0].resources assert isinstance(policies[0].context['secret'], Equal) def test_get_all_with_incorrect_args(self, st): for i in range(10): st.add(Policy(str(i), description='foo')) with pytest.raises(ValueError) as e: list(st.get_all(-1, 9)) assert "Limit can't be negative" == str(e.value) with pytest.raises(ValueError) as e: list(st.get_all(0, -3)) assert "Offset can't be negative" == str(e.value) def test_get_all_returns_generator(self, st): st.add(Policy('1')) st.add(Policy('2')) found = st.get_all(500, 0) assert isinstance(found, types.GeneratorType) l = [] for p in found: l.append(p.uid) assert 2 == len(l) def test_get_all_ascending_sorting_order(self, st): for i in range(1, 20): st.add(Policy(i)) assert list(range(1, 20)) == list( map(attrgetter('uid'), st.get_all(30, 0))) @pytest.mark.parametrize('checker, expect_number', [ (None, 6), (RegexChecker(), 2), (RulesChecker(), 2), (StringExactChecker(), 1), (StringFuzzyChecker(), 1), ]) def test_find_for_inquiry_returns_existing_policies( self, st, checker, expect_number): st.add(Policy('1', subjects=['<[mM]ax>', '<.*>'])) st.add(Policy('2', subjects=['sam<.*>', 'foo'])) st.add( Policy('3', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add( Policy('3.1', subjects=['Jim'], actions=[r'del<\w+>'], resources=['server'])) st.add(Policy('4', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add(Policy('5', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, checker) assert expect_number == len(list(found)) def test_find_for_inquiry_with_exact_string_checker(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add( Policy('2', subjects=['maxim'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add(Policy('3', subjects=['sam', 'nina'])) st.add(Policy('4', subjects=[Eq('sam'), Eq('nina')])) inquiry = Inquiry(subject='max', action='get', resource='books') found = st.find_for_inquiry(inquiry, StringExactChecker()) found = list(found) assert 1 == len(found) assert '1' == found[0].uid def test_find_for_inquiry_with_fuzzy_string_checker(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['books', 'comics', 'magazines'])) st.add( Policy('2', subjects=['maxim'], actions=['get'], resources=['books', 'foos'])) st.add( Policy('3', subjects=['Max'], actions=['get'], resources=['books', 'comics'])) st.add(Policy('4', subjects=['sam', 'nina'])) st.add(Policy('5', subjects=[Eq('sam'), Eq('nina')])) inquiry = Inquiry(subject='max', action='et', resource='oo') found = st.find_for_inquiry(inquiry, StringFuzzyChecker()) found = list(found) assert 2 == len(found) ids = [found[0].uid, found[1].uid] assert '1' in ids assert '2' in ids inquiry = Inquiry(subject='Max', action='get', resource='comics') found = st.find_for_inquiry(inquiry, StringFuzzyChecker()) found = list(found) assert 1 == len(found) assert '3' == found[0].uid @pytest.mark.parametrize('policies, inquiry, expected_reference', [ ( [ Policy(uid=1, actions=['get', 'post'], effect=ALLOW_ACCESS, resources=['<.*>'], subjects=['<[Mm]ax>', '<Jim>']), ], Inquiry(action='get', resource='printer', subject='Max'), True, ), ( [ Policy(uid=1, actions=['<.*>'], effect=ALLOW_ACCESS, resources=['<.*>'], subjects=['<.*>']), ], Inquiry(action='get', resource='printer', subject='Max'), True, ), ( [ Policy(uid=1, actions=['<.*>'], effect=ALLOW_ACCESS, resources=['library:books:<.+>'], subjects=['<.*>']), ], Inquiry( action='get', resource='library:books:dracula', subject='Max'), True, ), ( [ Policy(uid=1, actions=[r'<\d+>'], effect=ALLOW_ACCESS, resources=[r'<\w{1,3}>'], subjects=[r'<\w{2}-\d+>']), ], Inquiry(action='12', resource='Pie', subject='Jo-1'), True, ), ( [ Policy(uid=1, actions=['parse'], effect=ALLOW_ACCESS, resources=['library:books'], subjects=['Max']), ], Inquiry(action='parse', resource='library:books', subject='Max'), True, ), ( [ Policy(uid=1, actions=['parse'], effect=ALLOW_ACCESS, resources=['library:manu<(al|scripts)>'], subjects=['Max']), ], Inquiry(action='parse', resource='library:books', subject='Max'), False, ), ( [ Policy(uid=1, actions=['parse'], effect=ALLOW_ACCESS, resources=['library:books'], subjects=['Max']), Policy(uid=2, actions=['parse'], effect=ALLOW_ACCESS, resources=['library:manu<(al|scripts)>'], subjects=['Max']), ], Inquiry( action='parse', resource='library:manuscripts', subject='Max'), True, ), ]) def test_find_for_inquiry_with_regex_checker(self, st, policies, inquiry, expected_reference): mem_storage = MemoryStorage( ) # it returns all stored policies so we consider Guard as a reference for p in policies: st.add(p) mem_storage.add(p) reference_answer = Guard(mem_storage, RegexChecker()).is_allowed(inquiry) assert expected_reference == reference_answer, 'Check reference answer' assert reference_answer == Guard(st, RegexChecker()).is_allowed(inquiry), \ 'Mongo storage should give the same answers as reference' def test_find_for_inquiry_with_regex_checker_for_mongodb_prior_to_4_2( self, st): # mock db server version for this test st.db_server_version = (3, 4, 0) st.add(Policy('1', subjects=['<[mM]ax>', '<.*>'])) st.add(Policy('2', subjects=['sam<.*>', 'foo'])) st.add( Policy('3', subjects=['Jim'], actions=['delete'], resources=['server'])) st.add( Policy('3.1', subjects=['Jim'], actions=[r'del<\w+>'], resources=['server'])) st.add(Policy('4', subjects=[{'stars': Eq(90)}, Eq('Max')])) st.add(Policy('5', subjects=[Eq('Jim'), Eq('Nina')])) inquiry = Inquiry(subject='Jim', action='delete', resource='server') found = st.find_for_inquiry(inquiry, RegexChecker()) found = list(found) # should return all string-based polices, but not only matched ones assert 4 == len(found) assert ['1', '2', '3', '3.1'] == sorted(map(attrgetter('uid'), found)) def test_find_for_inquiry_with_rules_checker(self, st): assertions = unittest.TestCase('__init__') st.add( Policy(1, subjects=[{ 'name': Equal('Max') }], actions=[{ 'foo': Equal('bar') }])) st.add( Policy(2, subjects=[{ 'name': Equal('Max') }], actions=[{ 'foo': Equal('bar2') }])) st.add(Policy(3, subjects=['sam', 'nina'])) st.add( Policy(4, actions=[r'<\d+>'], effect=ALLOW_ACCESS, resources=[r'<\w{1,3}>'], subjects=[r'<\w{2}-\d+>'])) st.add( Policy(5, subjects=[{ 'name': Equal('Jim') }], actions=[{ 'foo': Equal('bar3') }])) inquiry = Inquiry(subject={'name': 'max'}, action='get', resource='books') found = st.find_for_inquiry(inquiry, RulesChecker()) found = list(found) assert 3 == len(found) assertions.assertListEqual([1, 2, 5], list(map(operator.attrgetter('uid'), found))) def test_find_for_inquiry_with_unknown_checker(self, st): st.add(Policy('1')) inquiry = Inquiry(subject='sam', action='get', resource='books') with pytest.raises(UnknownCheckerType): list(st.find_for_inquiry(inquiry, Inquiry())) def test_find_for_inquiry_returns_generator(self, st): st.add( Policy('1', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) st.add( Policy('2', subjects=['max', 'bob'], actions=['get'], resources=['comics'])) inquiry = Inquiry(subject='max', action='get', resource='comics') found = st.find_for_inquiry(inquiry) assert isinstance(found, types.GeneratorType) l = [] for p in found: l.append(p.uid) assert 2 == len(l) def test_update(self, st): id = str(uuid.uuid4()) policy = Policy(id) st.add(policy) assert id == st.get(id).uid assert None is st.get(id).description assert [] == st.get(id).actions policy.description = 'foo' policy.actions = ['a', 'b', 'c'] st.update(policy) assert id == st.get(id).uid assert 'foo' == st.get(id).description assert ['a', 'b', 'c'] == st.get(id).actions p = Policy(2, actions=[Any()], subjects=[Eq('max'), Eq('bob')]) st.add(p) assert 2 == st.get(2).uid p.actions = [Eq('get')] st.update(p) assert 1 == len(st.get(2).actions) assert 'get' == st.get(2).actions[0].val def test_update_non_existing_does_not_create_anything(self, st): id = str(uuid.uuid4()) st.update(Policy(id, actions=['get'], description='bar')) assert st.get(id) is None def test_delete(self, st): policy = Policy('1') st.add(policy) assert '1' == st.get('1').uid st.delete('1') assert None is st.get('1') def test_delete_nonexistent(self, st): uid = str(ObjectId()) st.delete(uid) assert None is st.get(uid) def test_returned_condition(self, st): uid = str(uuid.uuid4()) p = Policy( uid=uid, context={ 'secret': Equal('i-am-a-teacher'), 'secret2': Equal('i-am-a-husband'), }, ) st.add(p) context = st.get(uid).context assert context['secret'].satisfied('i-am-a-teacher') assert context['secret2'].satisfied('i-am-a-husband')
uid='5', description='Allows Nina to update any resources that have only digits', effect=ALLOW_ACCESS, subjects=['Nina'], actions=['update'], resources=['<[\d]+>'], ), ] for p in policies: st.add(p) @pytest.mark.parametrize('desc, inquiry, should_be_allowed', [ ( 'Empty inquiry carries no information, so nothing is allowed, even empty Policy #4', Inquiry(), False, ), ( 'Max is allowed to update anything', Inquiry(subject='Max', resource='myrn:example.com:resource:123', action='update'), True, ), ( 'Max is allowed to update anything, even empty one', Inquiry(subject='Max', resource='', action='update'), True, ), (
def single_inquiry_benchmark(): global guard if guard.is_allowed(inq): return True return False overall_policies_created = 0 similar_regexp_policies_created = 0 store = MemoryStorage() checker = RegexChecker(CACHE_SIZE) if CACHE_SIZE else RegexChecker() guard = Guard(store, checker) inq = Inquiry(action='get', subject='xo', resource='library:books:1234', context={'ip': '127.0.0.1'}) if __name__ == '__main__': line_length = 80 print('=' * line_length) print('Populating MemoryStorage with Policies') print_generation(populate_storage, int(POLICIES_NUMBER / 100 * 1), line_length) print('START BENCHMARK!') start = timeit.default_timer() allowed = single_inquiry_benchmark() stop = timeit.default_timer() print('Number of unique Policies in DB: {:,}'.format( overall_policies_created)) print('Among them there are Policies with the same regexp pattern: {:,}'.
def test_up(self, storage): migration = Migration1x2x0To1x4x0(storage) # prepare docs that might have been saved by users in v 1.2.0 docs = [ ( """ { "_id" : 10, "actions" : [ ], "context" : { "name" : {"py/object": "vakt.rules.string.Equal", "val": "Max" }, "secret" : {"py/object": "vakt.rules.string.Equal", "val": "i-am-a-foo"} }, "description" : null, "effect" : "allow", "resources" : [ ], "subjects" : [ ], "type": 1, "uid" : 10 } """, """ { "_id" : 10, "actions" : [ ], "actions_compiled_regex" : [ ], "context" : { "name" : {"py/object": "vakt.rules.string.Equal", "val": "Max" }, "secret" : {"py/object": "vakt.rules.string.Equal", "val": "i-am-a-foo"} }, "description" : null, "effect" : "allow", "resources" : [ ], "resources_compiled_regex" : [ ], "subjects" : [ ], "subjects_compiled_regex" : [ ], "type": 1, "uid" : 10 } """ ), ( """ { "_id" : 20, "actions" : [ "<.*>" ], "context" : { "secret" : { "py/object": "vakt.rules.string.Equal", "val": "John"} }, "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "subjects" : [ "<.*>" ], "type": 1, "uid" : 20 } """, """ { "_id" : 20, "actions" : [ "<.*>" ], "actions_compiled_regex" : [ "^(.*)$" ], "context" : { "secret" : { "py/object": "vakt.rules.string.Equal", "val": "John"} }, "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "resources_compiled_regex" : [ "^(.*)$" ], "subjects" : [ "<.*>" ], "subjects_compiled_regex" : [ "^(.*)$" ], "type": 1, "uid" : 20 } """ ), ( """ { "_id" : 30, "actions" : [ "get", "list" ], "context" : { }, "description" : "foo bar", "effect" : "allow", "resources" : [ "fax", "<[pP]rinter>" ], "subjects" : [ "<.*>" ], "type": 1, "uid" : 30 } """, """ { "_id" : 30, "actions" : [ "get", "list" ], "actions_compiled_regex" : [ "get", "list" ], "context" : { }, "description" : "foo bar", "effect" : "allow", "resources" : [ "fax", "<[pP]rinter>" ], "resources_compiled_regex" : [ "fax", "^([pP]rinter)$" ], "subjects" : [ "<.*>" ], "subjects_compiled_regex" : [ "^(.*)$" ], "type": 1, "uid" : 30 } """ ), ( """ { "_id" : 40, "actions" : [ ], "context" : { }, "description" : null, "effect" : "allow", "resources" : [ ], "subjects" : [ { "name" : {"py/object": "vakt.rules.string.StartsWith", "val": "Max" } } ], "type": 2, "uid" : 40 } """, """ { "_id" : 40, "actions" : [ ], "context" : { }, "description" : null, "effect" : "allow", "resources" : [ ], "subjects" : [ { "name" : {"py/object": "vakt.rules.string.StartsWith", "val": "Max" } } ], "type": 2, "uid" : 40 } """ ), ] for (doc, _) in docs: d = b_json.loads(doc) storage.collection.insert_one(d) migration.up() # test indices exist created_indices = [i['name'] for i in storage.collection.list_indexes()] assert created_indices == [ '_id_', 'actions_compiled_regex_idx', 'subjects_compiled_regex_idx', 'resources_compiled_regex_idx' ] # test no new docs were added and no docs deleted assert len(docs) == len(list(storage.collection.find({}))) # test Policy.from_json() is called without errors for each doc (implicitly) assert len(docs) == len(list(storage.get_all(1000, 0))) # test string contents of each doc for (doc, result_doc) in docs: new_doc = storage.collection.find_one({'uid': json.loads(doc)['uid']}) expected = result_doc.replace("\n", '').replace(' ', '') actual = json.dumps(new_doc, sort_keys=True).replace("\n", '').replace(' ', '') assert expected == actual # test we can retrieve policy for inquiry: 2 policies fit in our case inq = Inquiry(action='get', resource='printer', subject='Max') assert 2 == len(list(storage.find_for_inquiry(inq, RegexChecker())))
def test_default_values(): i = Inquiry() assert '' == i.subject assert '' == i.action assert '' == i.resource assert {} == i.context
def test_can_create_empty_inquiry(): i = Inquiry() assert isinstance(i, Inquiry) i2 = Inquiry.from_json('{}') assert isinstance(i2, Inquiry)
with pytest.raises(TypeError) as excinfo: And(Inquiry()) assert expected_msg in str(excinfo.value) with pytest.raises(TypeError) as excinfo: Or(Inquiry(), 123) assert expected_msg in str(excinfo.value) @pytest.mark.parametrize('rules, what, inquiry, result', [ ([], 1, None, False), ([Greater(-1)], 1, None, True), ([Greater(55)], 1, None, False), ([Greater(-1), Less(10)], 1, None, True), ([Greater(-1), Less(10), Eq(700)], 1, None, False), ([Eq('read'), In('read', 'write'), ActionEqual()], 'read', Inquiry(action='read'), True), ([Eq('read'), In('write'), ActionEqual() ], 'read', Inquiry(action='read'), False), ]) def test_and_rule(rules, what, inquiry, result): r = And(*rules) assert result == r.satisfied(what, inquiry) # test after (de)serialization assert result == Rule.from_json(And(*rules).to_json()).satisfied( what, inquiry) @pytest.mark.parametrize('rules, what, inquiry, result', [ ([], 1, None, False), ([Greater(-1)], 1, None, True), ([Greater(55)], 1, None, False),
def test_resource_in_satisfied(what, resource, result): i = Inquiry(action='get', resource=resource, subject='Max') c = ResourceInRule() assert result == c.satisfied(what, i)
from vakt.rules.operator import Eq, NotEq, Greater from vakt.rules.logic import Any from vakt.effects import DENY_ACCESS, ALLOW_ACCESS from vakt.policy import Policy from vakt.guard import Guard, Inquiry @pytest.mark.parametrize('desc, policy, inquiry, checker, should_be_allowed', [ ( 'RegexChecker: Should not match since actions and resources are not specified', Policy( uid=1, effect=ALLOW_ACCESS, subjects=['foo'], ), Inquiry(subject='foo'), RegexChecker(), False, ), ( 'RulesChecker: Should not match since actions and resources are not specified', Policy( uid=1, effect=ALLOW_ACCESS, subjects=[{ 'name': Eq('Sally') }], ), Inquiry(subject={'name': 'Sally'}), RulesChecker(), False,
import pytest from vakt.rules.inquiry import ActionMatch, ResourceMatch, SubjectMatch from vakt.guard import Inquiry @pytest.mark.parametrize( 'attribute, what, inquiry, result', [ # no inquiry passed ('foo', 'bar', None, False), ('foo', 'foo', None, False), # matching on not attribute (None, 'get', Inquiry(action='get', resource='fax', subject='Max'), True), (None, 'put', Inquiry(action='get', resource='fax', subject='Max'), False), (None, 'getty', Inquiry(action='get', resource='fax', subject='Max'), False), (None, 'iget', Inquiry(action='get', resource='fax', subject='Max'), False), (None, 9999, Inquiry(action=9999, resource='fax', subject='Max'), True), (None, 8888, Inquiry(action=9999, resource='fax', subject='Max'), False), # matching on attribute ('user_id', 'get', Inquiry(action='get', resource='fax', subject='Max'), False), ('user_id', '123', Inquiry(action='get', resource='fax',
@pytest.fixture() def logger(): log = logging.getLogger('vakt.guard') initial_handlers = log.handlers[:] initial_level = log.getEffectiveLevel() yield log log.handlers = initial_handlers log.setLevel(initial_level) @pytest.mark.parametrize('desc, inquiry, should_be_allowed, checker', [ ( 'Empty inquiry carries no information, so nothing is allowed, even empty Policy #4', Inquiry(), False, RegexChecker(), ), ( 'Max is allowed to update anything', Inquiry(subject='Max', resource='myrn:example.com:resource:123', action='update'), True, RegexChecker(), ), ( 'Max is allowed to update anything, even empty one', Inquiry(subject='Max', resource='', action='update'), True,
def test_up(self, storage): migration = Migration1x1x1To1x2x0(storage) # prepare docs that might have been saved by users in v 1.1.1 docs = [ (""" { "_id" : 10, "actions" : [ ], "description" : null, "effect" : "allow", "resources" : [ ], "rules" : { "name" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "Max" }, "secret" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "i-am-a-foo"} }, "subjects" : [ ], "uid" : 10 } """, """ { "_id" : 10, "actions" : [ ], "context" : { "name" : {"py/object": "vakt.rules.string.Equal", "val": "Max" }, "secret" : {"py/object": "vakt.rules.string.Equal", "val": "i-am-a-foo"} }, "description" : null, "effect" : "allow", "resources" : [ ], "subjects" : [ ], "type": 1, "uid" : 10 } """), (""" { "_id" : 20, "actions" : [ "<.*>" ], "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "rules" : { "secret" : { "py/object": "vakt.rules.string.StringEqualRule", "val": "John"} }, "subjects" : [ "<.*>" ], "uid" : 20 } """, """ { "_id" : 20, "actions" : [ "<.*>" ], "context" : { "secret" : { "py/object": "vakt.rules.string.Equal", "val": "John"} }, "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "subjects" : [ "<.*>" ], "type": 1, "uid" : 20 } """), (""" { "_id" : 30, "actions" : [ "<.*>" ], "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "rules" : { }, "subjects" : [ "<.*>" ], "uid" : 30 } """, """ { "_id" : 30, "actions" : [ "<.*>" ], "context" : { }, "description" : "foo bar", "effect" : "allow", "resources" : [ "<.*>" ], "subjects" : [ "<.*>" ], "type": 1, "uid" : 30 } """), (""" { "_id" : 40, "actions" : [ "<.*>" ], "description" : null, "effect" : "allow", "resources" : ["<.*>"], "rules" : { "a" : {"py/object": "vakt.rules.string.StringEqualRule", "val": "foo"}, "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "123"} }, "subjects" : ["<.*>"], "uid" : 40 } """, """ { "_id" : 40, "actions" : [ "<.*>" ], "context" : { "a" : {"py/object": "vakt.rules.string.Equal", "val": "foo"}, "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "123"} }, "description" : null, "effect" : "allow", "resources" : ["<.*>"], "subjects" : ["<.*>"], "type": 1, "uid" : 40 } """), (""" { "_id" : 50, "actions" : [ ], "description" : null, "effect" : "allow", "resources" : [ ], "rules" : { "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "46"} }, "subjects" : [ ], "uid" : 50 } """, """ { "_id" : 50, "actions" : [ ], "context" : { "num" : { "py/object": "storage.test_mongo_migration.Simple", "val": "46"} }, "description" : null, "effect" : "allow", "resources" : [ ], "subjects" : [ ], "type": 1, "uid" : 50 } """), ] for (doc, _) in docs: d = b_json.loads(doc) storage.collection.insert_one(d) # set logger for capturing output l = logging.getLogger('vakt.storage.mongo') log_handler = self.MockLoggingHandler() l.setLevel(logging.INFO) l.addHandler(log_handler) migration.up() # test indices exist created_indices = [ i['name'] for i in storage.collection.list_indexes() ] assert created_indices == ['_id_', 'type_idx'] # test no new docs were added and no docs deleted assert len(docs) == len(list(storage.collection.find({}))) # test Policy.from_json() is called without errors for each doc (implicitly) assert len(docs) == len(list(storage.get_all(1000, 0))) # test string contents of each doc for (doc, result_doc) in docs: new_doc = storage.collection.find_one( {'uid': json.loads(doc)['uid']}) expected = result_doc.replace("\n", '').replace(' ', '') actual = json.dumps(new_doc, sort_keys=True).replace("\n", '').replace(' ', '') assert expected == actual # test we can retrieve policy for inquiry inq = Inquiry(action='foo', resource='bar', subject='Max', context={ 'val': 'foo', 'num': '123' }) assert len(docs) == len( list(storage.find_for_inquiry(inq, RegexChecker()))) # test failed policies report assert 10 == len(log_handler.messages.get('info', [])) assert 0 == len(log_handler.messages.get('warning', [])) assert 0 == len(log_handler.messages.get('error', []))
def test_is_allowed_for_none_policies(): g = Guard(MemoryStorage(), RegexChecker()) assert not g.is_allowed( Inquiry(subject='foo', action='bar', resource='baz'))
def test_action_equal_satisfied(what, action, result): i = Inquiry(action=action, resource=None, subject=None) c = ActionEqualRule() assert result == c.satisfied(what, i)
assert isinstance(i2, Inquiry) def test_pretty_print(): i = Inquiry(resource='books:abc', action='view', context={'ip': '127.0.0.1'}) assert "<class 'vakt.guard.Inquiry'>" in str(i) assert "'resource': 'books:abc'" in str(i) assert "'action': 'view'" in str(i) assert "'context': {'ip': '127.0.0.1'}" in str(i) @pytest.mark.parametrize('first, second, must_equal', [ ( Inquiry( resource='books:abc', action='view', context={'ip': '127.0.0.1'}), Inquiry( action='view', resource='books:abc', context={'ip': '127.0.0.1'}), True, ), ( Inquiry( action='view', resource='books:abc', context={'ip': '127.0.0.1'}), Inquiry( resource='books:абс', action='view', context={'ip': '127.0.0.1'}), False, ), ( Inquiry( resource='books:абс', action='view', context={'ip': '127.0.0.1'}), Inquiry(