def test_partial_subfield_isa(): from test_app.models import Post, User alice = User(name="alice") alice.save() not_alice = User(name="not alice") not_alice.save() Post(created_by=alice).save(), Post(created_by=not_alice).save(), Post(created_by=alice).save(), Oso.load_str(""" allow(_, _, post: test_app::Post) if check(post.created_by); check(user: test_app::User) if user.name = "alice"; check(post: test_app::Post) if post.is_private = false; """) authorize_filter = authorize_model(None, Post, actor="foo", action="bar") assert ( str(authorize_filter) == "(OR:" + " (AND: (NOT (AND: ('pk__in', []))), (NOT (AND: ('pk__in', []))), ('created_by__name', 'alice'))," + " ('pk__in', []))") authorized_posts = Post.objects.filter(authorize_filter) assert ( str(authorized_posts.query) == 'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",' + ' "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"' + ' FROM "test_app_post" LEFT OUTER JOIN "test_app_user"' + ' ON ("test_app_post"."created_by_id" = "test_app_user"."id") WHERE "test_app_user"."name" = alice' ) assert authorized_posts.count() == 2
def test_partial_isa_with_path(): from test_app.models import Post, User alice = User(name="alice") alice.save() not_alice = User(name="not alice") not_alice.save() Post(created_by=alice).save(), Post(created_by=not_alice).save(), Post(created_by=alice).save(), Oso.load_str(""" allow(_, _, post: test_app::Post) if check(post.created_by); check(user: test_app::User) if user.name = "alice"; check(post: test_app::Post) if post.is_private = false; """) authorize_filter = authorize_model(None, Post, actor="foo", action="bar") assert (str(authorize_filter) == f"(AND: {str(TRUE_FILTER)}, ('created_by__name', 'alice'))") authorized_posts = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" INNER JOIN "test_app_user" ON ("test_app_post"."created_by_id" = "test_app_user"."id") WHERE "test_app_user"."name" = alice """ assert str(authorized_posts.query) == " ".join(expected.split()) assert authorized_posts.count() == 2
def test_partial(rf, settings, partial_policy): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Post(name="test_past", is_private=False, timestamp=-1).save() Post(name="test_public", is_private=False, timestamp=1).save() Post(name="test_private", is_private=True, timestamp=1).save() Post(name="test_private_2", is_private=True, timestamp=1).save() request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model="test_app::Post") q = Post.objects.filter(authorize_filter) assert q.count() == 2 request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) q = Post.objects.filter(authorize_filter) assert q.count() == 5 q = Post.objects.authorize(request, action="get") assert q.count() == 5
def test_partial_errors(rf): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Post(name="test_past", is_private=False, timestamp=-1).save() Post(name="test_public", is_private=False, timestamp=1).save() Post(name="test_private", is_private=True, timestamp=1).save() Post(name="test_private_2", is_private=True, timestamp=1).save() request = rf.get("/") request.user = "******" # No rules for this. q = Post.objects.authorize(request, action="get") assert q.count() == 0
def test_negated_matches_with_partial(rf): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Oso.load_str(""" allow(1, _, post) if not post matches test_app::Post; allow(2, _, post) if not post matches test_app::User; allow(3, _, post) if not post.created_by matches test_app::User; allow(4, _, post) if not post.created_by matches test_app::Post; """) request = rf.get("/") request.user = 1 authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == ( f"(AND: {str(TRUE_FILTER)}, (NOT (AND: {str(TRUE_FILTER)})))") authorized_posts = Post.objects.filter(authorize_filter) # For some reason, this only seems to be raised when stringifying. with pytest.raises(EmptyResultSet): str(authorized_posts.query) assert authorized_posts.count() == 0 request.user = 2 authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == str(TRUE_FILTER) authorized_posts = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" """ assert str(authorized_posts.query) == " ".join(expected.split()) assert authorized_posts.count() == 1 request.user = 3 authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == ( f"(AND: {str(TRUE_FILTER)}, (NOT (AND: {str(TRUE_FILTER)})))") authorized_posts = Post.objects.filter(authorize_filter) # For some reason, this only seems to be raised when stringifying. with pytest.raises(EmptyResultSet): str(authorized_posts.query) assert authorized_posts.count() == 0 request.user = 4 authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == str(TRUE_FILTER) authorized_posts = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" """ assert str(authorized_posts.query) == " ".join(expected.split()) assert authorized_posts.count() == 1
def test_partial_errors(rf, settings): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Post(name="test_past", is_private=False, timestamp=-1).save() Post(name="test_public", is_private=False, timestamp=1).save() Post(name="test_private", is_private=True, timestamp=1).save() Post(name="test_private_2", is_private=True, timestamp=1).save() request = rf.get("/") request.user = "******" Oso.load_str('allow(_, "fail", post: test_app::Post) if post matches {x: 1};') with pytest.raises(UnsupportedError): q = Post.objects.authorize(request, action="fail") # No rules for this. q = Post.objects.authorize(request, action="get") assert q.count() == 0
def test_partial_with_allow_all(rf): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Oso.load_str("allow(_, _, _);") request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == str(TRUE_FILTER) authorized_posts = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" """ assert str(authorized_posts.query) == " ".join(expected.split()) assert authorized_posts.count() == 1
def test_partial(rf, partial_policy): from test_app.models import Post posts = [ Post(name="test", is_private=False, timestamp=1).save(), Post(name="test_past", is_private=False, timestamp=-1).save(), Post(name="test_public", is_private=False, timestamp=1).save(), Post(name="test_private", is_private=True, timestamp=1).save(), Post(name="test_private_2", is_private=True, timestamp=1).save(), Post(name="test_option", is_private=False, timestamp=1, option=True).save(), ] request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) assert ( str(authorize_filter) == f"(AND: {str(TRUE_FILTER)}, ('is_private', False), ('timestamp__gt', 0), ('option', None))" ) q = Post.objects.filter(authorize_filter) bool_cond = negated_condition('"test_app_post"."is_private"') expected = f""" SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" WHERE ({bool_cond} AND "test_app_post"."timestamp" > 0 AND "test_app_post"."option" IS NULL) """ assert str(q.query) == " ".join(expected.split()) assert q.count() == 2 request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) assert str(authorize_filter) == str(TRUE_FILTER) q = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" """ assert str(q.query) == " ".join(expected.split()) assert q.count() == len(posts) q = Post.objects.authorize(request, action="get") assert q.count() == len(posts)
def test_null_with_partial(rf): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Oso.load_str("allow(_, _, post: test_app::Post) if post.option = nil;") request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, Post) assert (str(authorize_filter) == "(AND: (NOT (AND: ('pk__in', []))), ('option', None))") authorized_posts = Post.objects.filter(authorize_filter) assert str(authorized_posts.query) == ( 'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", ' + '"test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"' + ' FROM "test_app_post"' + ' WHERE "test_app_post"."option" IS NULL') assert authorized_posts.count() == 1
def test_null_with_partial(rf): from test_app.models import Post Post(name="test", is_private=False, timestamp=1).save() Oso.load_str("allow(_, _, post: test_app::Post) if post.option = nil;") request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, Post) assert str(authorize_filter) == "(AND: ('option', None))" authorized_posts = Post.objects.filter(authorize_filter) expected = """ SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id" FROM "test_app_post" WHERE "test_app_post"."option" IS NULL """ assert str(authorized_posts.query) == " ".join(expected.split()) assert authorized_posts.count() == 1
def test_partial(rf, partial_policy): from test_app.models import Post posts = [ Post(name="test", is_private=False, timestamp=1).save(), Post(name="test_past", is_private=False, timestamp=-1).save(), Post(name="test_public", is_private=False, timestamp=1).save(), Post(name="test_private", is_private=True, timestamp=1).save(), Post(name="test_private_2", is_private=True, timestamp=1).save(), Post(name="test_option", is_private=False, timestamp=1, option=True).save(), ] request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) assert ( str(authorize_filter) == "(AND: (NOT (AND: ('pk__in', []))), ('is_private', False), ('timestamp__gt', 0), ('option', None))" ) q = Post.objects.filter(authorize_filter) assert ( str(q.query) == 'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",' + ' "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"' + ' FROM "test_app_post"' + ' WHERE (NOT "test_app_post"."is_private" AND "test_app_post"."timestamp" > 0 AND "test_app_post"."option" IS NULL)' ) assert q.count() == 2 request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) assert str(authorize_filter) == "(NOT (AND: ('pk__in', [])))" q = Post.objects.filter(authorize_filter) assert ( str(q.query) == 'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",' + ' "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"' + ' FROM "test_app_post"') assert q.count() == len(posts) q = Post.objects.authorize(request, action="get") assert q.count() == len(posts)
def test_partial(rf, settings, partial_policy): from test_app.models import Post posts = [ Post(name="test", is_private=False, timestamp=1).save(), Post(name="test_past", is_private=False, timestamp=-1).save(), Post(name="test_public", is_private=False, timestamp=1).save(), Post(name="test_private", is_private=True, timestamp=1).save(), Post(name="test_private_2", is_private=True, timestamp=1).save(), Post(name="test_option", is_private=False, timestamp=1, option=True).save(), ] request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model="test_app::Post") assert ( str(authorize_filter) == "(AND: ('is_private', False), ('timestamp__gt', 0), ('option', None))") q = Post.objects.filter(authorize_filter) assert q.count() == 2 request = rf.get("/") request.user = "******" authorize_filter = authorize_model(request, action="get", model=Post) assert str(authorize_filter) == "(AND: )" q = Post.objects.filter(authorize_filter) assert q.count() == len(posts) q = Post.objects.authorize(request, action="get") assert q.count() == len(posts)