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
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
- Adds
Statement
dataclass as alternative to dictionaries. Drops Python 3.5 support.
- Fixes read-only scenario for FieldAccessMixin. Thanks @hungryseven!
- Adds
PermittedSlugRelatedField
to re-usescope_queryset
methods on policies. Thanks @bradydean!
- Adds
PermittedPkRelatedField
to re-usescope_queryset
methods on policies.
- Fixes issue with boolean parser and shared request state. Thanks @mari8i!
- Adds support for field-level permissions via a
AccessPolicy.scope_fields(request, fields: dict, instance=None)
method and theFieldAccessMixin
. Thanks @gianpieropa!
- Adds a mixin for explicitly defining a single access policy per
ViewSet
.
- Fixes race condition between concurrent requests in evaluation of condition expressions. Thanks @goranpavlovic!
⚠️ Breaking Change⚠️ - The
condition
element no longer supports the evaluation of multiple methods joined with boolean logic. These statements must be updated to use the newcondition_expression
element, which does support complex boolean logic.
- The
- Allow defining
reusable_conditions
module as a list. Thanks @HonakerM!
- Fixes attribute error when
request.user
isNone
, which is the case when Django'sAuthenticationMiddleware
is not used. Ifrequest.user
isNone
, the user is anonymous.
- Adds special
admin
andstaff
principal keys to match users withis_superuser
andis_staff
set toTrue
. Thanks @BarnabasSzabolcs!
- Fixed bug preventing argument being passed to custom condition method if "*" character used.
- Adds missing requirement to setup.py. Thanks @daviddavis!
- Adds support for boolean expressions in
condition
statement elements. Thanks @tanonl!
- Fixes case where object has no
action_map
. Thanks @oguzhancelikarslan! - Added missing info to docs. Thanks @hardntrash!
- Workaround for quirk resulting in
action
not always being set. Thanks @oguzhancelikarslan!
- Allows using HTTP method placeholders in
action
element of statements to match request.- For example,
"action": ["<method:post>"]
will match all POST requests.
- For example,
- Uses
user.pk
instead ofuser.id
in user principal check, for compatibility with non-id
primary keys. - Fixes to documentation. Thanks @oguzhancelikarslan!
- Replaces references to "delete" action with "destroy" in docs/tests, to be consistent with DRF's ViewSet actions. Thanks @greenled!
- Only call database-hitting
get_user_group_values
if needed in private method. Thanks KillianMeersman! - Use
prefetch_related_objects
to ensure that user's groups aren't fetched more than once. Thanks filwaline!
- Tox config updates and typo fixes in docs.
- Add option to define re-usable custom conditions/permissions in a module that can be referenced by multiple policies.
- Fixes readme format for Pypy display.
- Allow passing arguments to condition methods, via condition values formatted as
{method_name}:{arg_value}
.
- Adds special
<safe_methods>
action key that matches when the current request is an HTTP read-only method: HEAD, GET, OPTIONS.
- Adds special
authenticated
andanonymous
principal keys to match any authenticated user and any non-authenticated user, respectively. Thanks @bogdandm for discussion/advice!
- Initial release
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.