def test_dict_specializers(polar, qvar, qeval, query): class Animal: def __init__(self, species=None, genus=None, family=None): self.genus = genus self.species = species polar.register_class(Animal) rules = """ what_is(_: {genus: "canis"}, res) if res = "canine"; what_is(_: {species: "canis lupus", genus: "canis"}, res) if res = "wolf"; what_is(_: {species: "canis familiaris", genus: "canis"}, res) if res = "dog"; """ polar.load_str(rules) wolf = 'new Animal(species: "canis lupus", genus: "canis", family: "canidae")' dog = 'new Animal(species: "canis familiaris", genus: "canis", family: "canidae")' canine = 'new Animal(genus: "canis", family: "canidae")' assert len(query(f"what_is({wolf}, res)")) == 2 assert len(query(f"what_is({dog}, res)")) == 2 assert len(query(f"what_is({canine}, res)")) == 1 assert qvar(f"what_is({wolf}, res)", "res") == ["wolf", "canine"] assert qvar(f"what_is({dog}, res)", "res") == ["dog", "canine"] assert qvar(f"what_is({canine}, res)", "res") == ["canine"]
def test_anything_works(polar, query): polar.load_str("f(1);") results = query("f(x)") assert results[0]["x"] == 1 results = query("f(y)") assert results[0]["y"] == 1
def test_instance_from_external_call(polar, load_policy, query): user = Actor(name="guest") resource = Widget(id="1", name="name") assert query(Predicate(name="allow", args=[user, "frob", resource])) resource = Widget(id="2", name="name") assert not query(Predicate(name="allow", args=[user, "frob", resource]))
def test_class_specializers(polar, qvar, qeval, query): class A: def a(self): return "A" def x(self): return "A" class B(A): def b(self): return "B" def x(self): return "B" class C(B): def c(self): return "C" def x(self): return "C" class X: def x(self): return "X" polar.register_class(A) polar.register_class(B) polar.register_class(C) polar.register_class(X) rules = """ test(_: A); test(_: B); try(_: B, res) if res = 2; try(_: C, res) if res = 3; try(_: A, res) if res = 1; """ polar.load_str(rules) assert qvar("new A().a() = x", "x", one=True) == "A" assert qvar("new A().x() = x", "x", one=True) == "A" assert qvar("new B().a() = x", "x", one=True) == "A" assert qvar("new B().b() = x", "x", one=True) == "B" assert qvar("new B().x() = x", "x", one=True) == "B" assert qvar("new C().a() = x", "x", one=True) == "A" assert qvar("new C().b() = x", "x", one=True) == "B" assert qvar("new C().c() = x", "x", one=True) == "C" assert qvar("new C().x() = x", "x", one=True) == "C" assert qvar("new X().x() = x", "x", one=True) == "X" assert len(query("test(new A())")) == 1 assert len(query("test(new B())")) == 2 assert qvar("try(new A(), x)", "x") == [1] assert qvar("try(new B(), x)", "x") == [2, 1] assert qvar("try(new C(), x)", "x") == [3, 2, 1] assert qvar("try(new X(), x)", "x") == []
def test_class_specializers(polar, qvar, qeval, query): class A: def a(self): return "A" def x(self): return "A" class B(A): def b(self): return "B" def x(self): return "B" class C(B): def c(self): return "C" def x(self): return "C" class X: def x(self): return "X" polar.register_class(A) polar.register_class(B) polar.register_class(C) polar.register_class(X) rules = """ test(_: A{}); test(_: B{}); try(_: B{}, res) if res = 2; try(_: C{}, res) if res = 3; try(_: A{}, res) if res = 1; """ polar.load_str(rules) assert qvar("new A{}.a = x", "x", one=True) == "A" assert qvar("new A{}.x = x", "x", one=True) == "A" assert qvar("new B{}.a = x", "x", one=True) == "A" assert qvar("new B{}.b = x", "x", one=True) == "B" assert qvar("new B{}.x = x", "x", one=True) == "B" assert qvar("new C{}.a = x", "x", one=True) == "A" assert qvar("new C{}.b = x", "x", one=True) == "B" assert qvar("new C{}.c = x", "x", one=True) == "C" assert qvar("new C{}.x = x", "x", one=True) == "C" assert qvar("new X{}.x = x", "x", one=True) == "X" assert len(query("test(new A{})")) == 1 assert len(query("test(new B{})")) == 2 assert qvar("try(new A{}, x)", "x") == [1] assert qvar("try(new B{}, x)", "x") == [2, 1] assert qvar("try(new C{}, x)", "x") == [3, 2, 1] assert qvar("try(new X{}, x)", "x") == []
def test_instance_initialization(polar, query, qvar): # test round trip through kb query user = Actor("sam") env = query('new Actor{name:"sam"} = returned_user')[0] assert polar.host.to_python(env["returned_user"]) == user env = query('new Actor(name:"sam") = returned_user')[0] assert polar.host.to_python(env["returned_user"]) == user
def test_is_allowed(polar, load_policy, query): actor = Actor(name="guest") resource = Widget(id="1") action = "get" assert query(Predicate(name="allow", args=[actor, action, resource])) actor = Actor(name="president") assert query(Predicate(name="actorInRole", args=[actor, "admin", resource])) assert query(Predicate(name="allowRole", args=["admin", "create", resource]))
def test_cut(polar, load_policy, query): set_frobbed([]) actor = Actor(name="guest") resource = Widget(id="1") action = "get" assert query(Predicate(name="allow_with_cut", args=[actor, action, resource])) assert get_frobbed() == ["Widget"] set_frobbed([]) resource = DooDad(id="2") assert query(Predicate(name="allow_with_cut", args=[actor, action, resource])) assert get_frobbed() == ["DooDad"]
def test_clear_rules(polar, query): class Test: pass polar.register_class(Test) polar.load_str("f(x) if x = 1;") assert len(query("f(1)")) == 1 assert len(query("x = new Test()")) == 1 polar.clear_rules() assert len(query("f(1)")) == 0 assert len(query("x = new Test()")) == 1
def test_lookup_errors(polar, query): class Foo: def foo(self): return "foo" polar.register_class(Foo) # Unify with an invalid field doesn't error. assert query('new Foo() = {bar: "bar"}') == [] # Dot op with an invalid field does error. with pytest.raises(exceptions.PolarRuntimeError) as e: query('new Foo().bar = "bar"') == [] assert "Application error: 'Foo' object has no attribute 'bar'" in str(e.value)
def test_method_resolution_order(polar, load_policy, query): set_frobbed([]) actor = Actor(name="guest") resource = Widget(id="1") action = "get" assert query(Predicate(name="allow", args=[actor, action, resource])) assert get_frobbed() == ["Widget"] # DooDad is a Widget set_frobbed([]) resource = DooDad(id="2") assert query(Predicate(name="allow", args=[actor, action, resource])) assert get_frobbed() == ["DooDad", "Widget"]
def test_patching(polar, widget_in_company, actor_in_role, load_policy, query): user = Actor("test") assert not query( Predicate(name="actorInRole", args=[user, "admin", Widget(id="1")])) with widget_in_company: with actor_in_role("admin"): assert query( Predicate(name="actorInRole", args=[user, "admin", Widget(id="1")])) assert not query( Predicate(name="actorInRole", args=[user, "admin", Widget(id="1")]))
def test_querystring_resource_map(polar, load_policy, query): assert query( Predicate( name="allow", args=[ Actor(name="sam"), "what", Http(path="/widget/12", query={"param": "foo"}), ], )) assert not query( Predicate(name="allow", args=[Actor(name="sam"), "what", Http(path="/widget/12")]))
def test_runtime_errors(polar, query): rules = """ foo(a,b) if a in b; """ polar.load_str(rules) with pytest.raises(exceptions.PolarRuntimeException) as e: query("foo(1,2)") assert (str(e.value) == """trace (most recent evaluation last): in query at line 1, column 1 foo(1,2) in rule foo at line 2, column 17 a in b Type error: can only use `in` on a list, this is Variable(Symbol("_a_3")) at line 2, column 17""" )
def test_clear(polar, load_policy, query): old = Path(__file__).parent / "policies" / "load.pol" fails = Path(__file__).parent / "policies" / "reload_fail.pol" new = Path(__file__).parent / "policies" / "reload.pol" polar.clear() polar.load_file(old) actor = Actor(name="milton", id=1) resource = Widget(id=1, name="thingy") assert query(Predicate(name="allow", args=[actor, "make", resource])) assert query(Predicate(name="allow", args=[actor, "get", resource])) assert query(Predicate(name="allow", args=[actor, "edit", resource])) assert query(Predicate(name="allow", args=[actor, "delete", resource])) # raises exception because new policy file specifies on a class defined in the old file, # but not in the new file polar.clear() with pytest.raises(PolarRuntimeException): polar.load_file(fails) polar.clear() polar.load_file(new) assert query(Predicate(name="allow", args=[actor, "make", resource])) assert not query(Predicate(name="allow", args=[actor, "get", resource])) assert not query(Predicate(name="allow", args=[actor, "edit", resource])) assert not query(Predicate(name="allow", args=[actor, "delete", resource]))
def test_class_field_specializers(polar, qvar, qeval, query): class Animal: def __init__(self, species=None, genus=None, family=None): self.genus = genus self.species = species self.family = family polar.register_class(Animal) rules = """ what_is(_: Animal, res) if res = "animal"; what_is(_: Animal{genus: "canis"}, res) if res = "canine"; what_is(_: Animal{family: "canidae"}, res) if res = "canid"; what_is(_: Animal{species: "canis lupus", genus: "canis"}, res) if res = "wolf"; what_is(_: Animal{species: "canis familiaris", genus: "canis"}, res) if res = "dog"; what_is(_: Animal{species: s, genus: "canis"}, res) if res = s; """ polar.load_str(rules) wolf = 'new Animal(species: "canis lupus", genus: "canis", family: "canidae")' dog = 'new Animal(species: "canis familiaris", genus: "canis", family: "canidae")' canine = 'new Animal(genus: "canis", family: "canidae")' canid = 'new Animal(family: "canidae")' animal = "new Animal()" assert len(query(f"what_is({wolf}, res)")) == 5 assert len(query(f"what_is({dog}, res)")) == 5 assert len(query(f"what_is({canine}, res)")) == 4 assert len(query(f"what_is({canid}, res)")) == 2 assert len(query(f"what_is({animal}, res)")) == 1 assert qvar(f"what_is({wolf}, res)", "res") == [ "wolf", "canis lupus", "canine", "canid", "animal", ] assert qvar(f"what_is({dog}, res)", "res") == [ "dog", "canis familiaris", "canine", "canid", "animal", ] assert qvar(f"what_is({canine}, res)", "res") == [None, "canine", "canid", "animal"] assert qvar(f"what_is({canid}, res)", "res") == ["canid", "animal"] assert qvar(f"what_is({animal}, res)", "res") == ["animal"]
def test_runtime_errors(polar, query): rules = """ foo(a,b) if a in b; """ polar.load_str(rules) with pytest.raises(exceptions.PolarRuntimeError) as e: query("foo(1,2)") assert ( str(e.value) == """trace (most recent evaluation last): in query at line 1, column 1 foo(1,2) in rule foo at line 2, column 17 a in b Type error: can only use `in` on an iterable value, this is Number(Integer(2)) at line 1, column 7""" )
def create_widget(): if not query( Predicate( name="allow", args=[g.user, request.method.lower(), Http(path=request.path)], ) ): return Response("Denied", status=403) return Response("Ok", status=204)
def test_unbound_variable(polar, query): """Test that unbound variable is returned.""" polar.load_str("rule(x, y) if y = 1;") first = query("rule(x, y)")[0] # y will be bound to 1 first["y"] = 1 # x should be unbound assert isinstance(first["x"], Variable)
def test_query(load_file, polar, query): """Test that queries work with variable arguments""" load_file(Path(__file__).parent / "test_file.polar") # plaintext polar query: query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert query(Predicate(name="f", args=[Variable("a")])) == [ {"a": 1}, {"a": 2}, {"a": 3}, ]
def test_return_list(polar, query): class User: def groups(self): return ["engineering", "social", "admin"] polar.register_class(User) # for testing lists polar.load_str('allow(actor: User, "join", "party") if "social" in actor.groups();') assert query(Predicate(name="allow", args=[User(), "join", "party"]))
def test_groups(load_file, qeval, query): # Contains test queries. load_file(Path(__file__).parent / "policies/groups.pol") # Check that we can't instantiate groups. with pytest.raises(PolarRuntimeException): qeval("G{}") # Test rule ordering with groups. results = query("check_order(A{}, action)") expected = ["A", "G", "H"] assert expected == [result["action"] for result in results]
def test_datetime(polar, query): # test datetime comparison t1 = datetime(2020, 5, 25) t2 = datetime.now() t3 = datetime(2030, 5, 25) t4 = datetime(2020, 5, 26) polar.load_str("lt(a, b) if a < b;") assert query(Predicate("lt", [t1, t2])) assert not query(Predicate("lt", [t2, t1])) # test creating datetime from polar polar.load_str("dt(x) if x = new Datetime(year: 2020, month: 5, day: 25);") assert query(Predicate("dt", [Variable("x")])) == [{ "x": datetime(2020, 5, 25) }] polar.load_str("ltnow(x) if x < Datetime.now();") assert query(Predicate("ltnow", [t1])) assert not query(Predicate("ltnow", [t3])) polar.load_str( "timedelta(a: Datetime, b: Datetime) if a.__sub__(b) == new Timedelta(days: 1);" ) assert query(Predicate("timedelta", [t4, t1]))
def test_external_op(polar, query): class A: def __init__(self, a): self.a = a def __gt__(self, other): return self.a > other.a def __lt__(self, other): return self.a < other.a def __eq__(self, other): return self.a == other.a polar.register_class(A) a1 = A(1) a2 = A(2) polar.load_str("lt(a, b) if a < b;") polar.load_str("gt(a, b) if a > b;") assert query(Predicate("lt", [a1, a2])) assert not query(Predicate("lt", [a2, a1])) assert query(Predicate("gt", [a2, a1]))
def test_load_function(polar, query, qvar): """Make sure the load function works.""" # Loading the same file twice doesn't mess stuff up. polar.load_file(Path(__file__).parent / "test_file.polar") with pytest.raises(exceptions.PolarRuntimeException) as e: polar.load_file(Path(__file__).parent / "test_file.polar") assert ( str(e.value) == f"Problem loading file: File {Path(__file__).parent}/test_file.polar has already been loaded." ) with pytest.raises(exceptions.PolarRuntimeException) as e: polar.load_file(Path(__file__).parent / "test_file_renamed.polar") assert ( str(e.value) == f"Problem loading file: A file with the same contents as {Path(__file__).parent}/test_file_renamed.polar named {Path(__file__).parent}/test_file.polar has already been loaded." ) assert query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert qvar("f(x)", "x") == [1, 2, 3] polar.clear() polar.load_file(Path(__file__).parent / "test_file.polar") polar.load_file(Path(__file__).parent / "test_file_gx.polar") assert query("f(x)") == [{"x": 1}, {"x": 2}, {"x": 3}] assert query("g(x)") == [{"x": 1}, {"x": 2}, {"x": 3}]
def test_instance_cache(polar, qeval, query): class Counter: count = 0 def __init__(self): self.__class__.count += 1 polar.register_class(Counter) polar.load_str("f(c: Counter) if c.count > 0;") assert Counter.count == 0 c = Counter() assert Counter.count == 1 assert query(Predicate(name="f", args=[c])) assert Counter.count == 1 assert c not in polar.host.instances.values()
def test_inf_nan(polar, qeval, query): polar.register_constant("inf", inf) polar.register_constant("neg_inf", -inf) polar.register_constant("nan", nan) assert isnan(query("x = nan")[0]["x"]) assert not query("nan = nan") assert query("x = inf")[0]["x"] == inf assert qeval("inf = inf") assert query("x = neg_inf")[0]["x"] == -inf assert qeval("neg_inf = neg_inf") assert not query("inf = neg_inf") assert not query("inf < neg_inf") assert qeval("neg_inf < inf")
def test_register_class(polar, load_policy, query): actor = Actor(name="guest") resource = Widget(id="1") action = "get" assert query(Predicate(name="allow", args=(actor, action, resource)))
def test_iter_fields(polar, load_policy, query): resource = Widget(id=1, name="stapler") actor = Actor(name="milton", id=1) assert query(Predicate(name="allow", args=[actor, "can_have", resource]))
def test_type_fields(polar, load_policy, query): resource = Widget(id=1, name="goldfish") actor = Actor(name="elmo", id=1, widget=resource) assert query(Predicate(name="allow", args=[actor, "keep", resource]))