Example #1
0
 def test_add_return_value(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     backend_return = back_storage.add(Policy(1))
     ec_return = ec.add(Policy(2))
     assert backend_return == ec_return
Example #2
0
 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'
Example #3
0
 def test_get_return_value(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1)
     ec.add(p1)
     backend_return = back_storage.get(1)
     ec_return = ec.get(1)
     assert backend_return == ec_return
Example #4
0
 def test_init_with_populate_for_dirty_cache_storage(self):
     cache = MemoryStorage()
     cache.add(Policy(1))
     policies = [Policy(1), Policy(2), Policy(3)]
     back = Mock(spec=MongoStorage,
                 **{'retrieve_all.side_effect': [policies]})
     with pytest.raises(Exception) as excinfo:
         EnfoldCache(back, cache=cache, populate=True)
     assert 'Conflicting UID = 1' == str(excinfo.value)
     assert back.retrieve_all.called
Example #5
0
 def test_update_return_value(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1, description='foo')
     ec.add(p1)
     p1.description = 'foo upd'
     backend_return = back_storage.update(p1)
     ec_return = ec.update(p1)
     assert backend_return == ec_return
Example #6
0
 def test_find_for_inquiry_return_value(self, storage):
     cache_storage = storage
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     inq = Inquiry(action='get')
     p1 = Policy(1)
     p2 = Policy(2)
     ec.add(p1)
     ec.add(p2)
     backend_return = back_storage.find_for_inquiry(inq)
     ec_return = ec.find_for_inquiry(inq)
     assert list(backend_return) == list(ec_return)
Example #7
0
 def test_retrieve_all_return_value(self, storage):
     cache_storage = storage
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1)
     p2 = Policy(2)
     p3 = Policy(3)
     ec.add(p1)
     ec.add(p2)
     ec.add(p3)
     backend_return = back_storage.retrieve_all()
     ec_return = back_storage.retrieve_all()
     assert list(backend_return) == list(ec_return)
Example #8
0
 def test_get_all_return_value(self, storage):
     cache_storage = storage
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1)
     p2 = Policy(2)
     p3 = Policy(3)
     ec.add(p1)
     ec.add(p2)
     ec.add(p3)
     backend_return = back_storage.get_all(100, 0)
     ec_return = back_storage.get_all(100, 0)
     assert backend_return == ec_return
Example #9
0
 def test_init_without_populate(self):
     cache = MemoryStorage()
     policies = [Policy(1), Policy(2), Policy(3)]
     back = Mock(spec=MongoStorage,
                 **{'retrieve_all.return_value': [policies]})
     c = EnfoldCache(back, cache=cache, populate=False)
     assert not back.retrieve_all.called
     assert [] == c.cache.get_all(1000, 0)
Example #10
0
 def test_find_for_inquiry_for_populated_cache(self, log_mock):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     inq = Inquiry(action='get')
     chk1 = RulesChecker()
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     cache_storage.add(p1)
     cache_storage.add(p2)
     ec = EnfoldCache(back_storage, cache=cache_storage, populate=True)
     # Make first request
     assert [p1, p2] == list(ec.find_for_inquiry(inquiry=inq, checker=chk1))
     assert 0 == log_mock.warning.call_count
     log_mock.reset_mock()
     # Make second request
     assert [p1, p2] == list(ec.find_for_inquiry(inquiry=inq, checker=chk1))
     assert 0 == log_mock.warning.call_count
Example #11
0
 def test_get_for_non_populated_cache(self, log_mock):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     back_storage.add(p1)
     back_storage.add(p2)
     ec = EnfoldCache(back_storage, cache=cache_storage, populate=False)
     assert p1 == ec.get(1)
     log_mock.warning.assert_called_with(
         '%s cache miss for get Policy with UID=%s. Trying to get it from backend storage',
         'EnfoldCache', 1)
     log_mock.reset_mock()
     assert p2 == ec.get(2)
     assert 1 == log_mock.warning.call_count
     log_mock.reset_mock()
     # test we won't return any inexistent policies
     assert ec.get(3) is None
Example #12
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()
Example #13
0
def test_guard_logs_messages_at_info_level(audit_log):
    log_capture_str = io.StringIO()
    h = logging.StreamHandler(log_capture_str)
    h.setLevel(logging.INFO)
    audit_log.setLevel(logging.INFO)
    audit_log.addHandler(h)
    g = Guard(MemoryStorage(), RegexChecker())
    g.is_allowed(Inquiry())
    assert 'No potential' in log_capture_str.getvalue().strip()
Example #14
0
 def test_init_with_populate_and_populate_is_true_by_default(self):
     cache = MemoryStorage()
     policies = [Policy(1), Policy(2), Policy(3)]
     back = Mock(spec=MongoStorage,
                 **{
                     'retrieve_all.side_effect': [policies],
                     'get_all.side_effect': []
                 })
     ec = EnfoldCache(back, cache=cache)
     assert back.retrieve_all.called
     assert policies == ec.cache.get_all(10000, 0)
Example #15
0
 def test_find_for_inquiry_for_non_populated_cache(self, log_mock):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     inq = Inquiry(action='get')
     chk1 = RulesChecker()
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     back_storage.add(p1)
     back_storage.add(p2)
     ec = EnfoldCache(back_storage, cache=cache_storage, populate=False)
     # Make first request
     assert [p1, p2] == list(ec.find_for_inquiry(inquiry=inq, checker=chk1))
     log_mock.warning.assert_called_with(
         '%s cache miss for find_for_inquiry. Trying it from backend storage',
         'EnfoldCache')
     log_mock.reset_mock()
     # Make second request
     assert [p1, p2] == list(ec.find_for_inquiry(inquiry=inq, checker=chk1))
     assert 1 == log_mock.warning.call_count
     log_mock.reset_mock()
Example #16
0
 def test_add_ok(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1)
     p2 = Policy(2)
     p3 = Policy(3)
     ec.add(p1)
     assert [p1] == cache_storage.get_all(100, 0)
     assert [p1] == back_storage.get_all(100, 0)
     ec.add(p2)
     ec.add(p3)
     assert [p1, p2, p3] == cache_storage.get_all(100, 0)
     assert [p1, p2, p3] == back_storage.get_all(100, 0)
Example #17
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'))
Example #18
0
def test_guard_logs_inquiry_decision(logger, inquiry, result, expect_message):
    # set up logging
    log_capture_str = io.StringIO()
    h = logging.StreamHandler(log_capture_str)
    h.setLevel(logging.INFO)
    logger.setLevel(logging.INFO)
    logger.addHandler(h)
    # set up Guard
    storage = MemoryStorage()
    storage.add(
        Policy(
            uid='1',
            effect=ALLOW_ACCESS,
            subjects=['Max'],
            actions=['watch'],
            resources=['TV'],
        ))
    g = Guard(storage, RegexChecker())
    assert result == g.is_allowed(inquiry)
    log_res = log_capture_str.getvalue().strip()
    # a little hack to get rid of <Object ID 4502567760> in inquiry output
    log_res = re.sub(r'<Object ID \d+>', '<Object ID some_ID>', log_res)
    assert expect_message == log_res
Example #19
0
 def test_add_fail(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1)
     p2 = Policy(2)
     p3 = Policy(3)
     ec.add(p1)
     back_storage.add = Mock(side_effect=PolicyExistsError('2'))
     with pytest.raises(PolicyExistsError) as excinfo:
         ec.add(p2)
     assert 'Conflicting UID = 2' == str(excinfo.value)
     assert [p1] == back_storage.get_all(1000, 0)
     # test general exception
     back_storage.add = Mock(side_effect=Exception('foo'))
     with pytest.raises(Exception) as excinfo:
         ec.add(p3)
     assert 'foo' == str(excinfo.value)
     assert [p1] == back_storage.get_all(1000, 0)
     assert [p1] == cache_storage.get_all(1000, 0)
Example #20
0
 def test_delete_fail(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     ec.add(p1)
     ec.add(p2)
     back_storage.delete = Mock(side_effect=Exception('error!'))
     with pytest.raises(Exception) as excinfo:
         ec.delete(2)
     assert 'error!' == str(excinfo.value)
     assert [p1, p2] == back_storage.get_all(1000, 0)
     assert [p1, p2] == cache_storage.get_all(1000, 0)
Example #21
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()
Example #22
0
 def test_delete_ok(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     ec.add(p1)
     ec.add(p2)
     ec.delete(1)
     ec.delete(2)
     assert [] == cache_storage.get_all(100, 0)
     assert [] == back_storage.get_all(100, 0)
Example #23
0
    def test_cache_is_invalidated_on_policy_change(self):
        def assert_after_modification():
            assert 0 == cache.info().hits
            assert 0 == cache.info().misses
            assert 0 == cache.info().currsize
            assert not guard.is_allowed(inq1)
            assert not guard.is_allowed(inq1)
            assert guard.is_allowed(inq2)
            assert guard.is_allowed(inq2)
            assert guard.is_allowed(inq2)
            assert 3 == cache.info().hits
            assert 2 == cache.info().misses
            assert 2 == cache.info().currsize

        inq1 = Inquiry(action='get', resource='book', subject='Max')
        inq2 = Inquiry(action='get', resource='book', subject='Jim')
        guard, storage, cache = create_cached_guard(MemoryStorage(),
                                                    RulesChecker(),
                                                    maxsize=256)
        p1 = Policy(1,
                    actions=[Eq('get')],
                    resources=[Eq('book')],
                    subjects=[Eq('Max')],
                    effect=ALLOW_ACCESS)
        p2 = Policy(2,
                    actions=[Eq('get')],
                    resources=[Eq('magazine')],
                    subjects=[Eq('Max')],
                    effect=ALLOW_ACCESS)
        storage.add(p1)
        assert guard.is_allowed(inq1)
        assert guard.is_allowed(inq1)
        assert 1 == cache.info().hits
        assert 1 == cache.info().misses
        assert 1 == cache.info().currsize
        # start modifications
        p1.subjects = [Eq('Jim')]
        storage.update(p1)
        assert_after_modification()
        storage.add(p2)
        assert_after_modification()
        storage.delete(p2)
        assert_after_modification()
Example #24
0
 def test_same_inquiries_are_cached(self):
     guard, storage, cache = create_cached_guard(MemoryStorage(),
                                                 RulesChecker(),
                                                 maxsize=256)
     p1 = Policy(1,
                 actions=[Eq('get')],
                 resources=[Eq('book')],
                 subjects=[Eq('Max')],
                 effect=ALLOW_ACCESS)
     storage.add(p1)
     inq1 = Inquiry(action='get', resource='book', subject='Max')
     inq2 = Inquiry(action='get', resource='book', subject='Jamey')
     assert guard.is_allowed(inq1)
     assert guard.is_allowed(inq1)
     assert guard.is_allowed(inq1)
     assert guard.is_allowed(inq1)
     assert guard.is_allowed(inq1)
     assert not guard.is_allowed(inq2)
     assert 4 == cache.info().hits
     assert 2 == cache.info().misses
     assert 2 == cache.info().currsize
Example #25
0
 def test_update_ok(self):
     cache_storage = MemoryStorage()
     back_storage = MemoryStorage()
     ec = EnfoldCache(back_storage, cache=cache_storage)
     p1 = Policy(1, description='foo')
     p2 = Policy(2, description='bar')
     p3 = Policy(3, description='baz')
     ec.add(p1)
     ec.add(p2)
     ec.add(p3)
     p1.description = 'foo2'
     ec.update(p1)
     p2.description = 'bar2'
     ec.update(p2)
     assert [p1, p2, p3] == cache_storage.get_all(100, 0)
     assert [p1, p2, p3] == back_storage.get_all(100, 0)
Example #26
0
def test_guard_uses_audit_correctly(audit_log, inquiry, is_allowed, expect_msg):
    # setup logger consumer
    log_capture_str = io.StringIO()
    h = logging.StreamHandler(log_capture_str)
    h.setFormatter(logging.Formatter(
        'msg: %(message)s | effect: %(effect)s | deciders: %(deciders)s | candidates: %(candidates)s | ' +
        'inquiry: %(inquiry)s'
    ))
    h.setLevel(logging.INFO)
    audit_log.setLevel(logging.INFO)
    audit_log.addHandler(h)
    # setup guard
    st = MemoryStorage()
    st.add(PolicyAllow(uid='a', subjects=['Max'], actions=['<.*>'], resources=['<.*>']))
    st.add(PolicyAllow(uid='b', subjects=['Max'], actions=['get'], resources=['<.*>']))
    st.add(PolicyAllow(uid='c', subjects=['Jim'], actions=['<.*>'], resources=['<.*>']))
    st.add(PolicyDeny(uid='d', subjects=['Jim'], actions=['<.*>'], resources=['<.*>']))
    st.add(PolicyAllow(uid='e'))
    g = Guard(st, RegexChecker())
    # Run tests
    assert is_allowed == g.is_allowed(inquiry)
    result = log_capture_str.getvalue().strip()
    # a little hack to get rid of <Object ID 4502567760> in inquiry output
    result = re.sub(r'<Object ID \d+>', '<Object ID some_ID>', result)
    assert expect_msg == result
Example #27
0
def st():
    return MemoryStorage()
Example #28
0
def test_is_allowed_for_inquiry_match_rules(desc, policy, inquiry, result):
    storage = MemoryStorage()
    storage.add(policy)
    g = Guard(storage, RulesChecker())
    assert result == g.is_allowed(inquiry), 'Failed for case: ' + desc
Example #29
0
def test_is_allowed_for_none_policies():
    g = Guard(MemoryStorage(), RegexChecker())
    assert not g.is_allowed(
        Inquiry(subject='foo', action='bar', resource='baz'))
Example #30
0
def test_is_allowed(desc, inquiry, should_be_allowed, checker):
    # Create all required test policies
    st = MemoryStorage()
    policies = [
        Policy(
            uid='1',
            description="""
            Max, Nina, Ben, Henry are allowed to create, delete, get the resources
            only if the client IP matches and the inquiry states that any of them is the resource owner
            """,
            effect=ALLOW_ACCESS,
            subjects=('Max', 'Nina', '<Ben|Henry>'),
            resources=('myrn:example.com:resource:123',
                       'myrn:example.com:resource:345',
                       'myrn:something:foo:<.+>'),
            actions=('<create|delete>', 'get'),
            context={
                'ip': CIDR('127.0.0.1/32'),
                'owner': SubjectEqual(),
            },
        ),
        Policy(
            uid='2',
            description='Allows Max to update any resource',
            effect=ALLOW_ACCESS,
            subjects=['Max'],
            actions=['update'],
            resources=['<.*>'],
        ),
        Policy(
            uid='3',
            description='Max is not allowed to print any resource',
            effect=DENY_ACCESS,
            subjects=['Max'],
            actions=['print'],
            resources=['<.*>'],
        ),
        Policy(uid='4'),
        Policy(
            uid='5',
            description=
            'Allows Nina to update any resources that have only digits',
            effect=ALLOW_ACCESS,
            subjects=['Nina'],
            actions=['update'],
            resources=[r'<[\d]+>'],
        ),
        Policy(
            uid='6',
            description=
            'Allows Nina to update any resources that have only digits. Defined by rules',
            effect=ALLOW_ACCESS,
            subjects=[Eq('Nina')],
            actions=[Eq('update'), Eq('read')],
            resources=[{
                'id': RegexMatch(r'\d+'),
                'magazine': RegexMatch(r'[\d\w]+')
            }],
        ),
    ]
    for p in policies:
        st.add(p)
    g = Guard(st, checker)
    assert should_be_allowed == g.is_allowed(inquiry)