Generating Django QuerySet filters from oso policies
List authorization now
First, let’s see how this looks with oso now. For this post, we’ll return to the social feed app from our previous post, implemented in Django. The app allows users to login, submit new posts, and view a feed of posts. A post can either be public or private. Below is a code sample from the social app clonable on GitHub: def list_posts(request):
# Limit to 10 latest posts.
posts = Post.objects.all().order_by('-created_at')
authorized_posts = []
for post in posts:
try:
authorize(request, post, action="view")
authorized_posts.append(post)
except PermissionDenied:
continue
return render(request, 'social/list.html', {'posts': authorized_posts})
for
loop we authorize
each Post, only adding those that are successfully authorized to the authorized_posts
list. If many posts are unauthorized (the common case since users can only see their own private posts), we will have wasted (potentially significant) effort instead of using the database to efficiently fetch only relevant private posts for the current user.
Authorize collections
With django-oso 0.3.0, this code can be rewritten as follows: def list_posts(request):
posts = (
Post.objects.authorize(request, action="view")
.order_by("-created_at")
)
return render(request, "social/list.html", {"posts": posts})
# Allow anyone to view any public posts.
allow(_actor, "view", post: social::Post) if
post.access_level = social::Post.ACCESS_PUBLIC;
# Allow a user to view their private posts.
allow(actor: social::User, "view", post: social::Post) if
post.access_level = social::Post.ACCESS_PRIVATE and
post.created_by = actor;
allow(actor: social::User, "view", _: social::Post) if
actor.is_moderator();
SELECT *
FROM "social_post"
WHERE (("social_post"."access_level" = 1
AND "social_post"."created_by_id" = 1)
OR "social_post"."access_level" = 0);
OR
s:
social_post.access_level = 1 AND social_post.created_by_id = 1
: private posts created by the current actor (The logged in user has ID 1).social_post.access_level = 0
: public posts
SELECT *
FROM "social_post"
WHERE "social_post"."access_level" = 0;
actor
has type django::contrib::auth::AnonymousUser
, not social::User
so the type specifier on the second and third rules does not match. We can see that the only constraint added to the query is that the post must be public.
Finally, if we log in as a user that is a moderator (which would match the last rule), the ORM will execute the below SQL query:
SELECT *
FROM "social_post";
WHERE
clause because moderators can view every Post (due to _: Post
as the resource parameter in the allow
rule).
No changes to our policy or app were required to take advantage of this feature. The authorization policy is still fully abstracted from the rest of our application, but we have efficient enforcement of that policy. If we want to update the policy to change authorization filters, no changes in Python are necessary. The only policy interaction is the interface:
Post.objects.authorize(request, action="view")
Let us know what you think
This feature is available in preview for thedjango-oso
library in 0.3.0, with some limitations:
- Not all policies can be executed through the
Model.objects.authorize
interface. - Some policies may be translated to invalid constraints that cannot be evaluated by Django, so we do not recommend using this in production yet.
About Author
osohq
oso helps developers fast track authorization in their applications.
Original Source: Original Post
Please share your Feedback:
Did you enjoy reading or think it can be improved? Don’t forget to leave your thoughts in the comments section below! If you liked this article, please share it with your friends, and read a few more!