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_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(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_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 authorize(self, request, *, actor=None, action=None): """Return a new ``Queryset`` filtered to contain only authorized models. .. warning:: This feature is currently in preview. :param actor: The actor making the request. Defaults to ``request.user``. :param action: The action to authorize the actor to perform. Defaults to ``request.method``. """ try: filter = authorize_model(request=request, model=self.model, actor=actor, action=action) except PermissionDenied: return self.none() # SELECT DISTINCT on inner query to support chaining methods on # returned QuerySet. if filter == TRUE_FILTER: return self.filter(filter) else: return self.filter(pk__in=self.filter(filter))
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_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_rewrite_parameters(): from test_app.models import Post Oso.load_str("""allow(_, _, resource) if g(resource.created_by); g(resource) if resource matches test_app::User; """) authorize_filter = authorize_model(None, Post, actor="foo", action="bar") assert str(authorize_filter) == str(TRUE_FILTER)
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)
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_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 authorize(self, request, *, actor=None, action=None): """Return a new ``Queryset`` filtered to contain only authorized models. .. warning:: This feature is currently in preview. :param actor: The actor making the request. Defaults to ``request.user``. :param action: The action to authorize the actor to perform. Defaults to ``request.method``. """ try: filter = authorize_model( request=request, model=self.model, actor=actor, action=action ) except PermissionDenied: return self.none() return self.filter(filter)
def repos_index(request, org_name): if request.method == "GET": repo_filter = authorize_model(request, Repository, action="read") repos = Repository.objects.filter(repo_filter, organization__name=org_name) context = {"org_name": org_name, "repo_list": repos} return render(request, "repos/index.html", context) if request.method == "POST": try: name = request.POST["name"] except KeyError as e: messages.add_message(request, DANGER, "Missing field: %s" % str(e)) return render(request, f"repos/new.html") else: if Repository.objects.filter(name=name).count() > 0: messages.add_message(request, DANGER, "Repository already exists: %s" % name) return render(request, "repos/new.html", context={"org_name": org_name}) # create the repository repo = Repository( name=name, organization=Organization.objects.get(name=org_name)) repo.save() # Create the base roles for each repository created # NOTE: better to do this lazily for (role_level, _) in RepositoryRoleLevel.choices: role = RepositoryRole(name=role_level, repository=repo) role.save() if role.name == RepositoryRoleLevel.ADMIN: role.users.add(request.user) role.save() messages.success(request, 'Repository "%s" created successfully' % name) print(org_name) return redirect(f"/orgs/{org_name}/repos/")
def orgs_index(request): if request.method == "GET": org_filter = authorize_model(request, Organization, action="read") orgs = Organization.objects.filter(org_filter) context = {"org_list": orgs} return render(request, "orgs/index.html", context)
def teams_index(request, org_name): if request.method == "GET": team_filter = authorize_model(request, Team, action="read") teams = Team.objects.filter(team_filter, organization__name=org_name) context = {"org_name": org_name, "team_list": teams} return render(request, "teams/index.html", context)