Exemple #1
0
 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
Exemple #2
0
 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)
Exemple #3
0
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
Exemple #4
0
 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)
Exemple #5
0
 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)
Exemple #6
0
 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)
Exemple #7
0
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()
Exemple #8
0
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'))
Exemple #9
0
 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))
Exemple #10
0
 def test_find_for_inquiry_for_empty_database(self, st):
     assert [] == list(st.find_for_inquiry(Inquiry(), RegexChecker()))
Exemple #11
0
 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()))
Exemple #12
0
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)
Exemple #13
0
def test_json_decode_fails_for_incorrect_data():
    with pytest.raises(ValueError):
        Inquiry.from_json('{')
Exemple #14
0
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] | ' +
Exemple #15
0
    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)
Exemple #16
0
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')
Exemple #17
0
        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,
    ),
    (
Exemple #18
0

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())))
Exemple #20
0
def test_default_values():
    i = Inquiry()
    assert '' == i.subject
    assert '' == i.action
    assert '' == i.resource
    assert {} == i.context
Exemple #21
0
def test_can_create_empty_inquiry():
    i = Inquiry()
    assert isinstance(i, Inquiry)
    i2 = Inquiry.from_json('{}')
    assert isinstance(i2, Inquiry)
Exemple #22
0
    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)
Exemple #24
0
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',
Exemple #26
0

@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,
Exemple #27
0
    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', []))
Exemple #28
0
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)
Exemple #30
0
    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(