This project brings a declaritive, organized approach to managing access control in Django REST Framework projects. Each ViewSet or function-based view can be assigned an explicit policy for the exposed resource(s). No more digging through views or seralizers to understand access logic -- it's all in one place in a format that less technical stakeholders can understand. If you're familiar with other declaritive access models, such as AWS' IAM, the syntax will be familiar.
In short, you can start expressing your access rules like this:
class ArticleAccessPolicy(AccessPolicy):
statements = [
{
"action": ["list", "retrieve"],
"principal": "*",
"effect": "allow"
},
{
"action": ["publish", "unpublish"],
"principal": ["group:editor"],
"effect": "allow"
}
]
This project has complete test coverage and the base AccessPolicy
class is only ~150 lines of code: there's no magic here.
Additionally, this project also provides FieldAccessMixin
that can be added to a serializer to dynamically set fields to read_only
, based on the access policy. Assign the appropriate access policy class inside the Meta
declaration. See example below for how this works:
class UserAccountAccessPolicy(AccessPolicy):
statements = [
{"principal": "group:admin", "action": ["create", "update"], "effect": "allow"},
{
"principal": "group:dev",
"action": ["update", "partial_update"],
"effect": "allow",
},
]
field_permissions = {"read_only": [{"principal": "group:dev", "fields": "status"}]}
class UserAccountSerializer(FieldAccessMixin, serializers.ModelSerializer):
class Meta:
model = UserAccount
fields = ["username", "first_name", "last_name", "status"]
access_policy = UserAccountAccessPolicy
# Incoming POST/PUT/PATCH request from a user in group:dev...
# serializer = UserAccountSerializer(account, context={'request': request})
# print(serializer.fields["status"].read_only) -> True
:warning: 1.0 Breaking Change :warning:
See migration notes if your policy statements combine multiple conditions into boolean expressions.
Documentation: https://rsinger86.github.io/drf-access-policy
Source Code: https://github.com/rsinger86/drf-access-policy
Statement
dataclass as alternative to dictionaries. Drops Python 3.5 support.PermittedSlugRelatedField
to re-use scope_queryset
methods on policies. Thanks @bradydean!PermittedPkRelatedField
to re-use scope_queryset
methods on policies.AccessPolicy.scope_fields(request, fields: dict, instance=None)
method and the FieldAccessMixin
. Thanks @gianpieropa!ViewSet
.condition
element no longer supports the evaluation of multiple methods joined with boolean logic. These statements must be updated to use the new condition_expression
element, which does support complex boolean logic.reusable_conditions
module as a list. Thanks @HonakerM!request.user
is None
, which is the case when Django's AuthenticationMiddleware
is not used. If request.user
is None
, the user is anonymous.admin
and staff
principal keys to match users with is_superuser
and is_staff
set to True
. Thanks @BarnabasSzabolcs!condition
statement elements. Thanks @tanonl!action_map
. Thanks @oguzhancelikarslan!action
not always being set. Thanks @oguzhancelikarslan!action
element of statements to match request.
"action": ["<method:post>"]
will match all POST requests.user.pk
instead of user.id
in user principal check, for compatibility with non-id
primary keys.get_user_group_values
if needed in private method. Thanks KillianMeersman!prefetch_related_objects
to ensure that user's groups aren't fetched more than once. Thanks filwaline!{method_name}:{arg_value}
.<safe_methods>
action key that matches when the current request is an HTTP read-only method: HEAD, GET, OPTIONS.authenticated
and anonymous
principal keys to match any authenticated user and any non-authenticated user, respectively. Thanks @bogdandm for discussion/advice!Tests are found in a simplified Django project in the /tests
folder. Install the project requirements and do ./manage.py test
to run them.
See License.