Awesome Django authorization, without the database

Overview

rules

rules is a tiny but powerful app providing object-level permissions to Django, without requiring a database. At its core, it is a generic framework for building rule-based systems, similar to decision trees. It can also be used as a standalone library in other contexts and frameworks.

https://travis-ci.org/dfunckt/django-rules.svg?branch=master

Features

rules has got you covered. rules is:

  • Documented, tested, reliable and easy to use.
  • Versatile. Decorate callables to build complex graphs of predicates. Predicates can be any type of callable -- simple functions, lambdas, methods, callable class objects, partial functions, decorated functions, anything really.
  • A good Django citizen. Seamless integration with Django views, templates and the Admin for testing for object-level permissions.
  • Efficient and smart. No need to mess around with a database to figure out whether John really wrote that book.
  • Simple. Dive in the code. You'll need 10 minutes to figure out how it works.
  • Powerful. rules comes complete with advanced features, such as invocation context and storage for arbitrary data, skipping evaluation of predicates under specific conditions, logging of evaluated predicates and more!

Table of Contents

Requirements

rules requires Python 2.7/3.4 or newer. It can optionally integrate with Django, in which case requires Django 1.11 or newer.

Note: At any given moment in time, rules will maintain support for all currently supported Django versions, while dropping support for those versions that reached end-of-life in minor releases. See the Supported Versions section on Django Project website for the current state and timeline.

Upgrading from 1.x

  • Support for Python 2.6 and 3.3, and Django versions before 1.11 has been dropped.
  • The SkipPredicate exception and skip() method of Predicate, that were used to signify that a predicate should be skipped, have been removed. You may return None from your predicate to achieve this.
  • The APIs to replace a rule's predicate have been renamed and their behaviour changed. replace_rule and replace_perm functions and replace_rule method of RuleSet have been renamed to set_rule, set_perm and RuleSet.set_perm respectively. The old behaviour was to raise a KeyError if a rule by the given name did not exist. Since version 2.0 this has changed and you can safely use set_* to set a rule's predicate without having to ensure the rule exists first.

How to install

Using pip:

$ pip install rules

Manually:

$ git clone https://github.com/dfunckt/django-rules.git
$ cd django-rules
$ python setup.py install

Run tests with:

$ ./runtests.sh

You may also want to read Best practices for general advice on how to use rules.

Configuring Django

Add rules to INSTALLED_APPS:

INSTALLED_APPS = (
    # ...
    'rules',
)

Add the authentication backend:

AUTHENTICATION_BACKENDS = (
    'rules.permissions.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Using Rules

rules is based on the idea that you maintain a dict-like object that maps string keys used as identifiers of some kind, to callables, called predicates. This dict-like object is actually an instance of RuleSet and the predicates are instances of Predicate.

Creating predicates

Let's ignore rule sets for a moment and go ahead and define a predicate. The easiest way is with the @predicate decorator:

>>> @rules.predicate
>>> def is_book_author(user, book):
...     return book.author == user
...
>>> is_book_author
<Predicate:is_book_author object at 0x10eeaa490>

This predicate will return True if the book's author is the given user, False otherwise.

Predicates can be created from any callable that accepts anything from zero to two positional arguments:

  • fn(obj, target)
  • fn(obj)
  • fn()

This is their generic form. If seen from the perspective of authorization in Django, the equivalent signatures are:

  • fn(user, obj)
  • fn(user)
  • fn()

Predicates can do pretty much anything with the given arguments, but must always return True if the condition they check is true, False otherwise. rules comes with several predefined predicates that you may read about later on in API Reference, that are mostly useful when dealing with authorization in Django.

Setting up rules

Let's pretend that we want to let authors edit or delete their books, but not books written by other authors. So, essentially, what determines whether an author can edit or can delete a given book is whether they are its author.

In rules, such requirements are modelled as rules. A rule is a map of a unique identifier (eg. "can edit") to a predicate. Rules are grouped together into a rule set. rules has two predefined rule sets:

  • A default rule set storing shared rules.
  • Another rule set storing rules that serve as permissions in a Django context.

So, let's define our first couple of rules, adding them to the shared rule set. We can use the is_book_author predicate we defined earlier:

>>> rules.add_rule('can_edit_book', is_book_author)
>>> rules.add_rule('can_delete_book', is_book_author)

Assuming we've got some data, we can now test our rules:

>>> from django.contrib.auth.models import User
>>> from books.models import Book
>>> guidetodjango = Book.objects.get(isbn='978-1-4302-1936-1')
>>> guidetodjango.author
<User: adrian>
>>> adrian = User.objects.get(username='adrian')
>>> rules.test_rule('can_edit_book', adrian, guidetodjango)
True
>>> rules.test_rule('can_delete_book', adrian, guidetodjango)
True

Nice... but not awesome.

Combining predicates

Predicates by themselves are not so useful -- not more useful than any other function would be. Predicates, however, can be combined using binary operators to create more complex ones. Predicates support the following operators:

  • P1 & P2: Returns a new predicate that returns True if both predicates return True, otherwise False. If P1 returns False, P2 will not be evaluated.
  • P1 | P2: Returns a new predicate that returns True if any of the predicates returns True, otherwise False. If P1 returns True, P2 will not be evaluated.
  • P1 ^ P2: Returns a new predicate that returns True if one of the predicates returns True and the other returns False, otherwise False.
  • ~P: Returns a new predicate that returns the negated result of the original predicate.

Suppose the requirement for allowing a user to edit a given book was for them to be either the book's author, or a member of the "editors" group. Allowing users to delete a book should still be determined by whether the user is the book's author.

With rules that's easy to implement. We'd have to define another predicate, that would return True if the given user is a member of the "editors" group, False otherwise. The built-in is_group_member factory will come in handy:

>>> is_editor = rules.is_group_member('editors')
>>> is_editor
<Predicate:is_group_member:editors object at 0x10eee1350>

We could combine it with the is_book_author predicate to create a new one that checks for either condition:

>>> is_book_author_or_editor = is_book_author | is_editor
>>> is_book_author_or_editor
<Predicate:(is_book_author | is_group_member:editors) object at 0x10eee1390>

We can now update our can_edit_book rule:

>>> rules.set_rule('can_edit_book', is_book_author_or_editor)
>>> rules.test_rule('can_edit_book', adrian, guidetodjango)
True
>>> rules.test_rule('can_delete_book', adrian, guidetodjango)
True

Let's see what happens with another user:

>>> martin = User.objects.get(username='martin')
>>> list(martin.groups.values_list('name', flat=True))
['editors']
>>> rules.test_rule('can_edit_book', martin, guidetodjango)
True
>>> rules.test_rule('can_delete_book', martin, guidetodjango)
False

Awesome.

So far, we've only used the underlying, generic framework for defining and testing rules. This layer is not at all specific to Django; it may be used in any context. There's actually no import of anything Django-related in the whole app (except in the rules.templatetags module). rules however can integrate tightly with Django to provide authorization.

Using Rules with Django

rules is able to provide object-level permissions in Django. It comes with an authorization backend and a couple template tags for use in your templates.

Permissions

In rules, permissions are a specialised type of rules. You still define rules by creating and combining predicates. These rules however, must be added to a permissions-specific rule set that comes with rules so that they can be picked up by the rules authorization backend.

Creating permissions

The convention for naming permissions in Django is app_label.action_object, and we like to adhere to that. Let's add rules for the books.change_book and books.delete_book permissions:

>>> rules.add_perm('books.change_book', is_book_author | is_editor)
>>> rules.add_perm('books.delete_book', is_book_author)

See the difference in the API? add_perm adds to a permissions-specific rule set, whereas add_rule adds to a default shared rule set. It's important to know however, that these two rule sets are separate, meaning that adding a rule in one does not make it available to the other.

Checking for permission

Let's go ahead and check whether adrian has change permission to the guidetodjango book:

>>> adrian.has_perm('books.change_book', guidetodjango)
False

When you call the User.has_perm method, Django asks each backend in settings.AUTHENTICATION_BACKENDS whether a user has the given permission for the object. When queried for object permissions, Django's default authentication backend always returns False. rules comes with an authorization backend, that is able to provide object-level permissions by looking into the permissions-specific rule set.

Let's add the rules authorization backend in settings:

AUTHENTICATION_BACKENDS = (
    'rules.permissions.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Now, checking again gives adrian the required permissions:

>>> adrian.has_perm('books.change_book', guidetodjango)
True
>>> adrian.has_perm('books.delete_book', guidetodjango)
True
>>> martin.has_perm('books.change_book', guidetodjango)
True
>>> martin.has_perm('books.delete_book', guidetodjango)
False

Permissions in models

NOTE: The features described in this section work on Python 3+ only.

It is common to have a set of permissions for a model, like what Django offers with its default model permissions (such as add, change etc.). When using rules as the permission checking backend, you can declare object-level permissions for any model in a similar way, using a new Meta option.

First, you need to switch your model's base and metaclass to the slightly extended versions provided in rules.contrib.models. There are several classes and mixins you can use, depending on whether you're already using a custom base and/or metaclass for your models or not. The extensions are very slim and don't affect the models' behavior in any way other than making it register permissions.

  • If you're using the stock django.db.models.Model as base for your models, simply switch over to RulesModel and you're good to go.

  • If you already have a custom base class adding common functionality to your models, add RulesModelMixin to the classes it inherits from and set RulesModelBase as its metaclass, like so:

    from django.db.models import Model
    from rules.contrib.models import RulesModelBase, RulesModelMixin
    
    class MyModel(RulesModelMixin, Model, metaclass=RulesModelBase):
        ...
    
  • If you're using a custom metaclass for your models, you'll already know how to make it inherit from RulesModelBaseMixin yourself.

Then, create your models like so, assuming you're using RulesModel as base directly:

import rules
from rules.contrib.models import RulesModel

class Book(RulesModel):
    class Meta:
        rules_permissions = {
            "add": rules.is_staff,
            "read": rules.is_authenticated,
        }

This would be equivalent to the following calls:

rules.add_perm("app_label.add_book", rules.is_staff)
rules.add_perm("app_label.read_book", rules.is_authenticated)

There are methods in RulesModelMixin that you can overwrite in order to customize how a model's permissions are registered. See the documented source code for details if you need this.

Of special interest is the get_perm classmethod of RulesModelMixin, which can be used to convert a permission type to the corresponding full permission name. If you need to query for some type of permission on a given model programmatically, this is handy:

if user.has_perm(Book.get_perm("read")):
    ...

Permissions in views

rules comes with a set of view decorators to help you enforce authorization in your views.

Using the function-based view decorator

For function-based views you can use the permission_required decorator:

from django.shortcuts import get_object_or_404
from rules.contrib.views import permission_required
from posts.models import Post

def get_post_by_pk(request, post_id):
    return get_object_or_404(Post, pk=post_id)

@permission_required('posts.change_post', fn=get_post_by_pk)
def post_update(request, post_id):
    # ...

Usage is straight-forward, but there's one thing in the example above that stands out and this is the get_post_by_pk function. This function, given the current request and all arguments passed to the view, is responsible for fetching and returning the object to check permissions against -- i.e. the Post instance with PK equal to the given post_id in the example. This specific use-case is quite common so, to save you some typing, rules comes with a generic helper function that you can use to do this declaratively. The example below is equivalent to the one above:

from rules.contrib.views import permission_required, objectgetter
from posts.models import Post

@permission_required('posts.change_post', fn=objectgetter(Post, 'post_id'))
def post_update(request, post_id):
    # ...

For more information on the decorator and helper function, refer to the rules.contrib.views module.

Using the class-based view mixin

Django includes a set of access mixins that you can use in your class-based views to enforce authorization. rules extends this framework to provide object-level permissions via a mixin, PermissionRequiredMixin.

The following example will automatically test for permission against the instance returned by the view's get_object method:

from django.views.generic.edit import UpdateView
from rules.contrib.views import PermissionRequiredMixin
from posts.models import Post

class PostUpdate(PermissionRequiredMixin, UpdateView):
    model = Post
    permission_required = 'posts.change_post'

You can customise the object either by overriding get_object or get_permission_object.

For more information refer to the Django documentation and the rules.contrib.views module.

Checking permission automatically based on view type

If you use the mechanisms provided by rules.contrib.models to register permissions for your models as described in Permissions in models, there's another convenient mixin for class-based views available for you.

rules.contrib.views.AutoPermissionRequiredMixin can recognize the type of view it's used with and check for the corresponding permission automatically.

This example view would, without any further configuration, automatically check for the "posts.change_post" permission, given that the app label is "posts":

from django.views.generic import UpdateView
from rules.contrib.views import AutoPermissionRequiredMixin
from posts.models import Post

class UpdatePostView(AutoPermissionRequiredMixin, UpdateView):
    model = Post

By default, the generic CRUD views from django.views.generic are mapped to the native Django permission types (add, change, delete and view). However, the pre-defined mappings can be extended, changed or replaced altogether when subclassing AutoPermissionRequiredMixin. See the fully documented source code for details on how to do that properly.

Permissions and rules in templates

rules comes with two template tags to allow you to test for rules and permissions in templates.

Add rules to your INSTALLED_APPS:

INSTALLED_APPS = (
    # ...
    'rules',
)

Then, in your template:

{% load rules %}

{% has_perm 'books.change_book' author book as can_edit_book %}
{% if can_edit_book %}
    ...
{% endif %}

{% test_rule 'has_super_feature' user as has_super_feature %}
{% if has_super_feature %}
    ...
{% endif %}

Permissions in the Admin

If you've setup rules to be used with permissions in Django, you're almost set to also use rules to authorize any add/change/delete actions in the Admin. The Admin asks for four different permissions, depending on action:

  • <app_label>.add_<modelname>
  • <app_label>.view_<modelname>
  • <app_label>.change_<modelname>
  • <app_label>.delete_<modelname>
  • <app_label>

Note: view permission is new in Django v2.1 and should not be added in versions before that.

The first four are obvious. The fifth is the required permission for an app to be displayed in the Admin's "dashboard". Overriding it does not restrict access to the add, change or delete views. Here's some rules for our imaginary books app as an example:

>>> rules.add_perm('books', rules.always_allow)
>>> rules.add_perm('books.add_book', is_staff)
>>> rules.add_perm('books.view_book', is_staff | has_secret_access_code)
>>> rules.add_perm('books.change_book', is_staff)
>>> rules.add_perm('books.delete_book', is_staff)

Django Admin does not support object-permissions, in the sense that it will never ask for permission to perform an action on an object, only whether a user is allowed to act on (any) instances of a model.

If you'd like to tell Django whether a user has permissions on a specific object, you'd have to override the following methods of a model's ModelAdmin:

  • has_view_permission(user, obj=None)
  • has_change_permission(user, obj=None)
  • has_delete_permission(user, obj=None)

rules comes with a custom ModelAdmin subclass, rules.contrib.admin.ObjectPermissionsModelAdmin, that overrides these methods to pass on the edited model instance to the authorization backends, thus enabling permissions per object in the Admin:

# books/admin.py
from django.contrib import admin
from rules.contrib.admin import ObjectPermissionsModelAdmin
from .models import Book

class BookAdmin(ObjectPermissionsModelAdmin):
    pass

admin.site.register(Book, BookAdmin)

Now this allows you to specify permissions like this:

>>> rules.add_perm('books', rules.always_allow)
>>> rules.add_perm('books.add_book', has_author_profile)
>>> rules.add_perm('books.change_book', is_book_author_or_editor)
>>> rules.add_perm('books.delete_book', is_book_author)

To preserve backwards compatibility, Django will ask for either view or change permission. For maximum flexibility, rules behaves subtly different: rules will ask for the change permission if and only if no rule exists for the view permission.

Permissions in Django Rest Framework

Similar to rules.contrib.views.AutoPermissionRequiredMixin, there is a rules.contrib.rest_framework.AutoPermissionViewSetMixin for viewsets in Django Rest Framework. The difference is that it doesn't derive permission from the type of view but from the API action (create, retrieve etc.) that's tried to be performed. Of course, it also requires you to declare your models as described in Permissions in models.

Here is a possible ModelViewSet for the Post model with fully automated CRUD permission checking:

from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from rules.contrib.rest_framework import AutoPermissionViewSetMixin
from posts.models import Post

class PostSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"

class PostViewSet(AutoPermissionViewSetMixin, ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

By default, the CRUD actions of ModelViewSet are mapped to the native Django permission types (add, change, delete and view). The list action has no permission checking enabled. However, the pre-defined mappings can be extended, changed or replaced altogether when using (or subclassing) AutoPermissionViewSetMixin. Custom API actions defined via the @action decorator may then be mapped as well. See the fully documented source code for details on how to properly customize the default behavior.

Advanced features

Custom rule sets

You may create as many rule sets as you need:

>>> features = rules.RuleSet()

And manipulate them by adding, removing, querying and testing rules:

>>> features.rule_exists('has_super_feature')
False
>>> is_special_user = rules.is_group_member('special')
>>> features.add_rule('has_super_feature', is_special_user)
>>> 'has_super_feature' in features
True
>>> features['has_super_feature']
<Predicate:is_group_member:special object at 0x10eeaa500>
>>> features.test_rule('has_super_feature', adrian)
True
>>> features.remove_rule('has_super_feature')

Note however that custom rule sets are not available in Django templates -- you need to provide integration yourself.

Invocation context

A new context is created as a result of invoking Predicate.test() and is only valid for the duration of the invocation. A context is a simple dict that you can use to store arbitrary data, (eg. caching computed values, setting flags, etc.), that can be used by predicates later on in the chain. Inside a predicate function it can be used like so:

>>> @predicate
... def mypred(a, b):
...     value = compute_expensive_value(a)
...     mypred.context['value'] = value
...     return True

Other predicates can later use stored values:

>>> @predicate
... def myotherpred(a, b):
...     value = myotherpred.context.get('value')
...     if value is not None:
...         return do_something_with_value(value)
...     else:
...         return do_something_without_value()

Predicate.context provides a single args attribute that contains the arguments as given to test() at the beginning of the invocation.

Binding "self"

In a predicate's function body, you can refer to the predicate instance itself by its name, eg. is_book_author. Passing bind=True as a keyword argument to the predicate decorator will let you refer to the predicate with self, which is more convenient. Binding self is just syntactic sugar. As a matter of fact, the following two are equivalent:

>>> @predicate
... def is_book_author(user, book):
...     if is_book_author.context.args:
...         return user == book.author
...     return False

>>> @predicate(bind=True)
... def is_book_author(self, user, book):
...     if self.context.args:
...         return user == book.author
...     return False

Skipping predicates

You may skip evaluation by returning None from your predicate:

>>> @predicate(bind=True)
... def is_book_author(self, user, book):
...     if len(self.context.args) > 1:
...         return user == book.author
...     else:
...         return None

Returning None signifies that the predicate need not be evaluated, thus leaving the predicate result up to that point unchanged.

Logging predicate evaluation

rules can optionally be configured to log debug information as rules are evaluated to help with debugging your predicates. Messages are sent at the DEBUG level to the 'rules' logger. The following dictConfig configures a console logger (place this in your project's settings.py if you're using rules with Django):

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'rules': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

When this logger is active each individual predicate will have a log message printed when it is evaluated.

Best practices

Before you can test for rules, these rules must be registered with a rule set, and for this to happen the modules containing your rule definitions must be imported.

For complex projects with several predicates and rules, it may not be practical to define all your predicates and rules inside one module. It might be best to split them among any sub-components of your project. In a Django context, these sub-components could be the apps for your project.

On the other hand, because importing predicates from all over the place in order to define rules can lead to circular imports and broken hearts, it's best to further split predicates and rules in different modules.

rules may optionally be configured to autodiscover rules.py modules in your apps and import them at startup. To have rules do so, just edit your INSTALLED_APPS setting:

INSTALLED_APPS = (
    # replace 'rules' with:
    'rules.apps.AutodiscoverRulesConfig',
)

Note: On Python 2, you must also add the following to the top of your rules.py file, or you'll get import errors trying to import rules itself:

from __future__ import absolute_import

API Reference

The core APIs are accessible from the root rules module. Django-specific functionality for the Admin and views is available from rules.contrib.

Class rules.Predicate

You create Predicate instances by passing in a callable:

>>> def is_book_author(user, book):
...     return book.author == user
...
>>> pred = Predicate(is_book_author)
>>> pred
<Predicate:is_book_author object at 0x10eeaa490>

You may optionally provide a different name for the predicate that is used when inspecting it:

>>> pred = Predicate(is_book_author, name='another_name')
>>> pred
<Predicate:another_name object at 0x10eeaa490>

Also, you may optionally provide bind=True in order to be able to access the predicate instance with self:

>>> def is_book_author(self, user, book):
...     if self.context.args:
...         return user == book.author
...     return False
...
>>> pred = Predicate(is_book_author, bind=True)
>>> pred
<Predicate:is_book_author object at 0x10eeaa490>

Instance methods

test(obj=None, target=None)
Returns the result of calling the passed in callable with zero, one or two positional arguments, depending on how many it accepts.

Class rules.RuleSet

RuleSet extends Python's built-in dict type. Therefore, you may create and use a rule set any way you'd use a dict.

Instance methods

add_rule(name, predicate)
Adds a predicate to the rule set, assigning it to the given rule name. Raises KeyError if another rule with that name already exists.
set_rule(name, predicate)
Set the rule with the given name, regardless if one already exists.
remove_rule(name)
Remove the rule with the given name. Raises KeyError if a rule with that name does not exist.
rule_exists(name)
Returns True if a rule with the given name exists, False otherwise.
test_rule(name, obj=None, target=None)
Returns the result of calling predicate.test(obj, target) where predicate is the predicate for the rule with the given name. Returns False if a rule with the given name does not exist.

Decorators

@predicate

Decorator that creates a predicate out of any callable:

>>> @predicate
... def is_book_author(user, book):
...     return book.author == user
...
>>> is_book_author
<Predicate:is_book_author object at 0x10eeaa490>

Customising the predicate name:

>>> @predicate(name='another_name')
... def is_book_author(user, book):
...     return book.author == user
...
>>> is_book_author
<Predicate:another_name object at 0x10eeaa490>

Binding self:

>>> @predicate(bind=True)
... def is_book_author(self, user, book):
...     if 'user_has_special_flag' in self.context:
...         return self.context['user_has_special_flag']
...     return book.author == user

Predefined predicates

always_allow(), always_true()
Always returns True.
always_deny(), always_false()
Always returns False.
is_authenticated(user)
Returns the result of calling user.is_authenticated(). Returns False if the given user does not have an is_authenticated method.
is_superuser(user)
Returns the result of calling user.is_superuser. Returns False if the given user does not have an is_superuser property.
is_staff(user)
Returns the result of calling user.is_staff. Returns False if the given user does not have an is_staff property.
is_active(user)
Returns the result of calling user.is_active. Returns False if the given user does not have an is_active property.
is_group_member(*groups)
Factory that creates a new predicate that returns True if the given user is a member of all the given groups, False otherwise.

Shortcuts

Managing the shared rule set

add_rule(name, predicate)
Adds a rule to the shared rule set. See RuleSet.add_rule.
set_rule(name, predicate)
Set the rule with the given name from the shared rule set. See RuleSet.set_rule.
remove_rule(name)
Remove a rule from the shared rule set. See RuleSet.remove_rule.
rule_exists(name)
Returns whether a rule exists in the shared rule set. See RuleSet.rule_exists.
test_rule(name, obj=None, target=None)
Tests the rule with the given name. See RuleSet.test_rule.

Managing the permissions rule set

add_perm(name, predicate)
Adds a rule to the permissions rule set. See RuleSet.add_rule.
set_perm(name, predicate)
Replace a rule from the permissions rule set. See RuleSet.set_rule.
remove_perm(name)
Remove a rule from the permissions rule set. See RuleSet.remove_rule.
perm_exists(name)
Returns whether a rule exists in the permissions rule set. See RuleSet.rule_exists.
has_perm(name, user=None, obj=None)
Tests the rule with the given name. See RuleSet.test_rule.

Licence

django-rules is distributed under the MIT licence.

Copyright (c) 2014 Akis Kesoglou

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Comments
  • CBV mixin for checking permissions automatically based on view type

    CBV mixin for checking permissions automatically based on view type

    As mentioned in #99 already, here is a possible implementation of the CBV mixin. It's rather short and simply extends PermissionRequiredMixin.

    While I already use a similar thing in production, I rewrote this one completely to be more generic, so don't expect it to be 100% complete. Docs are still missing as well.

    opened by bob1de 20
  • Problem using  template tags

    Problem using template tags

    I am having difficulty using template tags in my templates. For example this works

    In the view:

    from __future__                 import absolute_import
    import rules
    ...
    def detail(request, slug):
        obj = get_object_or_404(NewsStory, slug=slug)
        return render(request, 'news/detail.html', {
            'story': obj,
            'can_publish_newsstory': rules.has_perm('can_publish_newsstory', request.user)
            })
    

    In the template

           {% if can_publish_newsstory %}
            <li><a href="{% url 'news:edit' story.slug %}" class="button expand secondary">Edit Story</a></li>
            <li><a href="{% url 'news:delete' story.slug %}" class="button expand secondary">Delete Story</a></li>
            {% endif %}
    

    If I change this to remove 'can_publish_newsstory' from the view and include it in the template, nothing shows.

    Revised Template

    {% load rules %}
    {% has_perm 'can_publish_newsstory' user as can_publish_newsstory %}
           {% if can_publish_newsstory %}
            <li><a href="{% url 'news:edit' story.slug %}" class="button expand secondary">Edit Story</a></li>
            <li><a href="{% url 'news:delete' story.slug %}" class="button expand secondary">Delete Story</a></li>
            {% endif %}
    

    I am using Python 2.

    opened by IrishGussy 13
  • django-rules not working for Django Admin

    django-rules not working for Django Admin

    I am trying to use django-rules to restrict access to parts of Admin based on some predicates. Here is what I am doing according to the documentation

    # python version: 2.7.12
    # django version: 1.11.22
    
    # ---------------
    INSTALLED_APPS = [
        'rules.apps.AutodiscoverRulesConfig'
    ]
    
    AUTHENTICATION_BACKENDS = (
        'rules.permissions.ObjectPermissionBackend',
    )
    
    # ---------------
    # allapps/restore/admin.py
    class RestoreAdmin(ObjectPermissionsModelAdmin):
         # Code for Admin goes here
    
    # ---------------
    #  allapps/restore/rules.py
    
    @rules.predicate
    def request_user_has_restore_access(user=None):
        # for the purpose of testing I am returning False
        return False
    
    rules.add_perm('restore', rules.always_deny) # Using even this doesn't work
    rules.add_perm('restore.view_restore', request_user_has_restore_access)
    rules.add_perm('restore.add_restore', request_user_has_restore_access)
    rules.add_perm('restore.change_restore', request_user_has_restore_access)
    rules.add_perm('restore.delete_restore', request_user_has_restore_access)
    
    

    This doesn't work. I can still see the Restore app in django admin

    opened by zainab-amir 12
  • Predicate call context (WIP)

    Predicate call context (WIP)

    Related to #6, #7, #9.

    Adds the ability to specify that a predicate wants a dict containing information about the current test_rule() invocation.

    >>> @rules.predicate(takes_context=True)
    ... def mypred(a, b, **kwargs):
    ...    print(kwargs['context'])
    ...    return True
    ...
    >>> rules.add_rule('myrule', mypred)
    >>> rules.test_rule('myrule', 1, 2)
    {'args': (1, 2), 'kwargs': {}}
    True
    

    Predicates that want context must accept **kwargs.

    A nice advantage of this PR is that it allows predicates to store arbitrary data that other predicates may later use. It is possible for example, to store an expensive computation done in one predicate (maybe fetch a bunch of objects from db, related to the given user/object) and another predicate access it later on.

    This is backwards-compatible -- at least all tests pass.

    Here are some things that need to be done in order for this PR to be merged:

    • [ ] Decide on the API -- pretty close already, I guess.
    • [ ] Write docs -- add a new section in README for now.
    • [ ] Find any glaring bugs.
    • [ ] Some more tests maybe.

    All feedback welcome.

    opened by dfunckt 12
  • Rules 2.0

    Rules 2.0

    Hey all, I'm thinking of releasing Rules 2.0 soon and was wondering if there are ideas for things that can be implemented now but couldn't due to having to keep backwards compatibility. I intend to include (breaking) changes related to #44 and #52. It would also be nice if we tackled #32 too (I remember there were some compatibility issues when I looked at it back when it was reported).

    1.2.X will still be supported for some time, back-porting fixes for bugs that are severe enough to warrant it but no new features will be implemented.

    What do you all think? I'll go ahead and release 2.0 in a couple of weeks if nothing comes up.

    breaking 
    opened by dfunckt 11
  • Rule chain for if A: return B

    Rule chain for if A: return B

    I have some rules like

    @rules.predicate
    def can_do_something(user, target):
        if user.is_anonymous:
            return test_A(user, target)
        elif user.is_admin:
            return test_B(user, target)
        else:
            return test_C(user, target)
    

    which I would like to translate to

    can_do_something = (rules.is_anonymous & test_A) | (rules.is_admin & test_B) | (rules.is_authenticated & test_C)
    

    The problem is that in the original implementation if user.is_anonymous is True, the checker will return immediately with test_A, but in the new implementation the checker will go through all cases if is_anonymous & test_A is False. How do I avoid evaluating all three cases? It looks like I am missing something obvious here.

    opened by BoPeng 10
  • Add support for automatic permission registration with Django models

    Add support for automatic permission registration with Django models

    Hi,

    I built this as part of a project I'm working on currently and I wonder whether that's something to add to the rules library directly.

    The code is documented and should be self-explanatory. Here's how to use it:

    from django.db.models import Model
    from rules import is_staff
    from rules.contrib.models import RulesModelBase
    
    class MyModel(Model, metaclass=RulesModelBase):
        class Meta:
            rules_permissions = {
                "add": is_staff,
                "change": is_staff | is_obj_owner,
            }
    

    What do you think? If it can be included in rules I could add user documentation for it as well.

    Best regards Robert

    opened by bob1de 10
  • ImportError on python 2.7.10

    ImportError on python 2.7.10

    Hello,

    I'm getting the following error (I didn't install the other package, django-rules, 🤐 ) on Django 1.9.

    Unhandled exception in thread started by <function wrapper at 0x104001758>
    Traceback (most recent call last):
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
        fn(*args, **kwargs)
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 109, in inner_run
        autoreload.raise_last_exception()
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 249, in raise_last_exception
        six.reraise(*_exception)
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
        fn(*args, **kwargs)
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
        apps.populate(settings.INSTALLED_APPS)
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/apps/registry.py", line 85, in populate
        app_config = AppConfig.create(entry)
      File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/apps/config.py", line 116, in create
        mod = import_module(mod_path)
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module
        __import__(name)
      File "/Users/dacian/Desktop/dev/project_name/extras/dashboard/rules.py", line 6, in <module>
        from rules import predicate
    ImportError: cannot import name predicate
    
    INSTALLED_APPS = [
        'modeltranslation',
        'django.contrib.sites',
        'jet',
        'django.contrib.admin',
        'django.contrib.auth',
        'polymorphic',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'django.contrib.humanize',
        # 'rules',
        'rules.apps.AutodiscoverRulesConfig',
        'django_extensions',
        'rest_framework',
    ]
    

    extras/dashboard/rules.py

    # -*- coding: utf-8 -*-
    
    from __future__ import absolute_import
    from __future__ import unicode_literals
    
    from rules import predicate
    
    
    @predicate(name='has_menu_perm')
    def has_menu_perm(user, menu_item):
        return any([user.has_perm(perm) for perm in menu_item.perms])
    
    opened by jab3z 10
  • Support permission-based queryset filters

    Support permission-based queryset filters

    I really like how rules works for individual object permissions, but it doesn't really cover permission-based queryset filtering (which comes naturally for database-centric permission systems like django-guardian). While thinking about how to compensate for that, I thought of an extension to the API that could help fill that gap. Rather than rushing off to write a pull request, I figured I'd outline it here for feedback first. I'm envisioning something like this:

    from django.db.models import Q
    
    @rules.filter
    def is_book_author(user):
        return Q(author=user)
    
    is_book_author_or_superuser = is_book_author | rules.predicates.is_superuser
    
    rules.add_filter('books.view_book', is_book_author_or_superuser)
    
    Book.objects.filter(rules.q(user, 'books.view_book')
    

    Filters would have to be defined separately from the object permission predicates, but would work very similarly; Q objects can be combined in ways that are pretty compatible with the predicate combinations already supported in rules. Existing predicates which only depend on properties of the user could be combined with Q-based filters, with predicate outcomes being represented as always-True (like Q(pk__isnull=False)) or always-False (like Q(pk__isnull=True)) Q objects.

    This would also make it pretty straightforward to create a Django REST Framework filter that would use the filter associated with the correct permission:

    from rest_framework.compat import get_model_name
    
    class DjangoPermissionRulesFilter(BaseFilterBackend):
    
        perm_format = '%(app_label)s.view_%(model_name)s'
    
        def filter_queryset(self, request, queryset, view):
            user = request.user
            model_cls = queryset.model
            kwargs = {
                'app_label': model_cls._meta.app_label,
                'model_name': get_model_name(model_cls)
            }
            permission = self.perm_format % kwargs
            return queryset.filter(rules.q(user, permission))
    

    Some of the things I like about this design:

    • Keeps implementation of the permissions out of the models and model managers, so they can be grouped together with the predicate definitions
    • Allows reuse of some basic filtering operations (at least within permissions on the same model or other ones with the same lookup path for the fields to compare)
    • Consistency with implementing predicates for the object-based permissions
    • Ability to reuse predicates that don't depend on the object in filters (this part just occurred to me and hasn't been as carefully thought through as the rest, but it seems like it should work)
    • Very simple to support in Django REST Framework with only one custom filter class that can be reused for many views

    Some downsides that I don't see good ways to work around yet:

    • One permission can have 2 different implementations: a predicate function for a single object, or a Q object for a queryset. I really don't see any way around this without really limiting and complicating the case where you don't even need a filter for the permission (which is pretty common).
    • Models with different lookup paths to the user (or related models) generally can't share filter functions; one may need Q(author=user) while another has Q(owner=user), Q(status__user=user), or even Q(creator__organization=user.organization).
    • I'd kind of prefer a query filtering syntax like Book.objects.has_perm(user, 'books.view_book'), but it doesn't seem worth the effort to create a model manager mixin for it that would need to be explicitly included in all relevant models.

    Thoughts? Do you think something like this would fit in rules or should go into a separate app which depends on it? And can you think of any good improvements on the API?

    opened by jmbowman 10
  • help w/ example for docs

    help w/ example for docs

    Hi @dfunckt, I've been building a test application w/ rules that I ultimately want to submit as a pull request. You can look at it in https://github.com/highpost/rules-testapp. It's a simple blog app with a number of users with different privilege levels.

    I'm also writing an article that you can either use in your documentation or we can find another home. The first chunk (in explore.txt) is an overview of basic Django permissions. I'll get to rules after that.

    My problem now is that my app works ... except with rules. If you look at views.py, you'll see that I'm using CBVs. If you remove PermissionRequiredMixin from each view, you can use the various test URLs in the README.txt file without any problem. But if you include them, then the buttons that access the views will fail with "127.0.0.1 redirected you too many times." I haven't had any luck tracking this down. I will note that the CreateView seems to work, and DetailView, UpdateView and DeleteView (with all take pk arguments) fail.

    Thanks for your help.

    opened by highpost 10
  • Missing decorator for views?

    Missing decorator for views?

    In the documentation it's not specified how to limit a view to users that have object based permissions. Just like normally we can do:

    @permission_required('app.action_object')
    def my_view(request, ...):
        pass
    

    I think that we should be able to somehow take incoming object IDs arguments and automatically block the request if the user does not meet the requirements.

    For example, something along the lines of:

    @permission_required('app.view_posts', 'post_id')
    def view_post(request, post_id):
        pass
    

    What do you think?

    opened by shoooe 10
  • How do you map predicates to objects and/or users?

    How do you map predicates to objects and/or users?

    I'm trying to build this permission mapping

    | Users | Permissions | Object | |-------|---------------|-------------------| | Bob | Can edit book | Lord of the rings | | Bob | Can edit book | Harry Potter | | Bob | Can read book | Lord of the rings | | Bob | Can read book | Harry Potter | | Tim | Can edit book | Lord of the rings | | Tim | Can edit book | Harry Potter | | Tim | Can read book | Lord of the rings | | Tim | Can read book | Harry Potter |

    But from the documentation I don't see how "Bob and Tim can edit Lord of the rings". Bob is not in any group and is not the owner of the book. Basically what is missing is a table to map Users, Permissions, Objects.

    Predicates can be created from any callable that accepts anything from zero to two positional arguments

    All examples show a predicate with user and object as arguments, but not a "permission". How do you test against above matrix? One way would be create a table like:

    | Users | Permissions | Content Type | Object ID | |---------|--------------------|--------------|-----------------------| | 1 (Bob) | 1 (Can edit book) | 1 (books) | 1 (Lord of the Rings) | | 1 (Bob) | 1 (Can edit book) | 1 (books) | 2 (Harry Potter) | | 1 (Bob) | 2 (Can read book) | 1 (books) | 1 (Lord of the Rings) | | 1 (Bob) | 2 (Can read book) | 1 (books) | 2 (Harry Potter) | | 1 (Bob) | 3 (Can drive cars) | 2 (cars) | 1 (Ferrari SF90) | | 1 (Bob) | 3 (Can drive cars) | 2 (cars) | 2 (Fiat 500) | | 1 (Bob) | 4 (Can fix cars) | 2 (cars) | 1 (Ferrari) | | 1 (Bob) | 4 (Can fix cars) | 2 (cars) | 2 (Fiat 500) | | 2 (Tim) | 1 (Can edit book) | 1 (books) | 1 (Lord of the Rings) | | 2 (Tim) | 1 (Can edit book) | 1 (books) | 2 (Harry Potter) | | 2 (Tim) | 2 (Can read book) | 1 (books) | 1 (Lord of the Rings) | | 2 (Tim) | 2 (Can read book) | 1 (books) | 2 (Harry Potter) | | 2 (Tim) | 3 (Can drive cars) | 2 (cars) | 1 (Ferrari SF90) | | 2 (Tim) | 3 (Can drive cars) | 2 (cars) | 2 (Fiat 500) | | 2 (Tim) | 4 (Can fix cars) | 2 (cars) | 1 (Ferrari) | | 2 (Tim) | 4 (Can fix cars) | 2 (cars) | 2 (Fiat 500) |

    And from the view you have:

    1. required permissions
    2. user from request
    3. current object_id (from get_object) which take you to step 4
    4. get content_type with object_id

    Now you have to test that all permissions you retrieve from the table are in required permission defined by view. Is the right approach or there's another way?

    opened by realnot 0
  • Django rules with abstract base class throw error after addition

    Django rules with abstract base class throw error after addition

    Adding django-rules to my Django project I am encountering the following issue: I want to add default permissions to my abstract base class and overwrite them, if needed. Below an example of a base class and the inheriting class:

    class BaseModel(RulesModelBaseMixin):
        company_id = models.ForeignKey('company.Company', on_delete=models.CASCADE)
        created_by = models.ForeignKey(
            'user.User', on_delete=models.SET_NULL, null=True, blank=True)
    
        class Meta:
            abstract = True
            rules_permissions = {
                "can_create": can_create_in_company | is_superuser,
                "can_view": can_view_in_company | is_author | is_superuser
            }
    
    class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
        name = models.CharField(max_length=512, null=True, blank=True)
    

    After adding this abstract base class, there is this seemingly unrelated error message:

    Traceback (most recent call last):
        File "REPO_PATH/.venv/lib/python3.10/site-packages/django/utils/module_loading.py", line 30, in import_string
        return cached_import(module_path, class_name)
        File "REPO_PATH/.venv/lib/python3.10/site-packages/django/utils/module_loading.py", line 15, in cached_import
        import_module(module_path)
        File "/opt/homebrew/Cellar/[email protected]/3.10.4/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
        File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
        File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
        File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
        File "<frozen importlib._bootstrap_external>", line 883, in exec_module
        File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
        File "REPO_PATH/app/api/pagination.py", line 10, in <module>
        from ticket.models import Ticket, TicketStatus, TicketType
        File "REPO_PATH/app/ticket/models.py", line 106, in <module>
        class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
        File "REPO_PATH/.venv/lib/python3.10/site-packages/rules/contrib/models.py", line 36, in __new__
        new_class._meta.rules_permissions = perms
    AttributeError: type object 'Ticket' has no attribute '_meta'. Did you mean: 'Meta'?
    

    It seems to have something to do with the custom pagination later on, but I don't think that this is the cause of the problem, as this worked before. The base class case is even mentioned in the docs, but it doesn't work.

    See SO post

    opened by creyD 0
  • Django: how does one know which permission failed on a particular request?

    Django: how does one know which permission failed on a particular request?

    Since permission_required takes a tuple, I imagined it would follow that which one of the permissions specified failed would also be available to us. This way, one is able to specify a tailor-made error message that is helpful to the end-user.

    Any thoughts/direction for this one?

    opened by MaybeThisIsRu 1
  • Using `AND` (`&`) with a predicate that returns `None` incorrectly returns `True`

    Using `AND` (`&`) with a predicate that returns `None` incorrectly returns `True`

    When using & to combine predicates, if one of the predicates returns None (a falsey value), the overall predicate will mistakenly/confusingly return True. This is not consistent with running the predicate by itself or ORed with other predicates.

    This behavior can be demonstrated with the following test added to test_predicates.py—the final assertion fails:

        def test_returns_none(self):
            @predicate
            def returns_none(arg1, arg2):
                return None
    
            # Just the predicate (works)
            assert not returns_none(2, 3)
    
            # OR the predicate with itself (works)
            p_OR = returns_none | returns_none
            assert not p_OR()
    
            # AND the predicate with a truthy-predicate (FAILS)
            p_AND = always_true & returns_none
            assert not p_AND()
    

    I noticed this in production in my app, where my logic in one predicate was doing return obj and obj.boolean_field, expecting that if obj were None, it'd still be treated as False in all contexts. It took me a while to figure out what was going on. I didn't feel sure about where/how to fix this in django-rules, but hope the above example will help to resolve quickly if possible. Thanks in advance (and thanks for building this great library)!

    opened by sjdemartini 5
Releases(v3.3.0)
  • v3.3.0(Mar 23, 2022)

  • v3.2.1(Mar 2, 2022)

  • v3.2.0(Mar 2, 2022)

  • v3.1.0(Dec 22, 2021)

  • v3.0.0(May 10, 2021)

    • Dropped support for Python 2
    • Dropped support for Django versions before 2.2

    Upgrading from 2.x: The are no significant changes between rules 2.x and 3.x except dropping support for Python 2, so before upgrading to 3.x you just need to make sure you're running a supported Python 3 version.

    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Apr 30, 2020)

  • v2.1.0(Aug 11, 2019)

    • Added ability to automatically check for permissions in Django Rest Framework viewsets.
    • Added ability to automatically check for permissions in Django class-based views.
    • Added ability to automatically register permissions for models.
    • Added shim for "six" in anticipation for Django 3.0 dropping support for Python 2 and removing "six" from its codebase.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Dec 7, 2018)

  • v2.0.0(Jul 22, 2018)

    • Removed support for Python 2.6 and 3.3
    • Removed support for Django versions before 1.11
    • Removed SkipPredicate exception and skip method of Predicate
    • Removed replace_rule and related APIs
    • Added set_rule and related APIs to safely replace a rule without having to ensure one already exists
    • Added compatibility with Django v2.1
    • Re-introduced support for PyPy and PyPy 3
    • Changed Python and Django supported versions policy to exclude end-of-life versions. Support for EOL'd versions will be dropped in minor version updates of rules from now on.
    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Jul 21, 2018)

    • Fixed masking AttributeErrors raised from CBV get_object
    • Fixed compatibility with inspect in newer Python 3 versions
    • Added ability to replace rules and permissions
    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Dec 12, 2017)

  • v1.2.1(May 13, 2017)

  • v1.2.0(Feb 11, 2017)

  • v1.1.1(Dec 7, 2015)

  • v1.1.0(Dec 7, 2015)

    • Fixed regression that wouldn't short-circuit boolean expressions
    • Added support for Django 1.9 and Python 3.5
    • Added support for skipping predicates simply by returning None
    • Deprecated skipping predicates by raising SkipPredicate
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Dec 7, 2015)

    • Initial stable public release
    • Dropped support for Python 3.2
    • Added Django test suite
    • Added function-based view decorator
    • Added class-based view mixin
    Source code(tar.gz)
    Source code(zip)
  • v0.4(Dec 7, 2015)

    • Added support for creating predicates from partial functions
    • Added support for creating predicates from instance methods
    • Added predicate invocation context
    • Added support for automatically passing self to a predicate
    • Added support for discarding a predicate's result
    Source code(tar.gz)
    Source code(zip)
  • v0.3(Dec 7, 2015)

  • v0.2(Dec 7, 2015)

  • v0.1(Dec 7, 2015)

Owner
null
Simple yet powerful authorization / authentication client library for Python web applications.

Authomatic Authomatic is a framework agnostic library for Python web applications with a minimalistic but powerful interface which simplifies authenti

null 1k Dec 28, 2022
Customizable User Authorization & User Management: Register, Confirm, Login, Change username/password, Forgot password and more.

Flask-User v1.0 Attention: Flask-User v1.0 is a Production/Stable version. The previous version is Flask-User v0.6. User Authentication and Management

Ling Thio 997 Jan 6, 2023
Implements authentication and authorization as FastAPI dependencies

FastAPI Security Implements authentication and authorization as dependencies in FastAPI. Features Authentication via JWT-based OAuth 2 access tokens a

Jacob Magnusson 111 Jan 7, 2023
Customizable User Authorization & User Management: Register, Confirm, Login, Change username/password, Forgot password and more.

Flask-User v1.0 Attention: Flask-User v1.0 is a Production/Stable version. The previous version is Flask-User v0.6. User Authentication and Management

Ling Thio 916 Feb 15, 2021
Simple yet powerful authorization / authentication client library for Python web applications.

Authomatic Authomatic is a framework agnostic library for Python web applications with a minimalistic but powerful interface which simplifies authenti

null 962 Feb 4, 2021
Simple yet powerful authorization / authentication client library for Python web applications.

Authomatic Authomatic is a framework agnostic library for Python web applications with a minimalistic but powerful interface which simplifies authenti

null 962 Feb 19, 2021
Phishing Abusing Microsoft 365 OAuth Authorization Flow

Microsoft365_devicePhish Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack This is a simple proof-of-concept script that allows an at

bigb0ss 11 Dec 11, 2022
Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack

Microsoft365_devicePhish Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack This is a simple proof-of-concept script that allows an at

Optiv Security 76 Jan 2, 2023
A Python package, that allows you to acquire your RecNet authorization bearer token with your account credentials!

RecNet-Login This is a Python package, that allows you to acquire your RecNet bearer token with your account credentials! Installation Done via git: p

Jesse 6 Aug 18, 2022
Some scripts to utilise device code authorization for phishing.

OAuth Device Code Authorization Phishing Some scripts to utilise device code authorization for phishing. High level overview as per the instructions a

Daniel Underhay 6 Oct 3, 2022
A wagtail plugin to replace the login by an OAuth2.0 Authorization Server

Wagtail OAuth2.0 Login Plugin to replace Wagtail default login by an OAuth2.0 Authorization Server. What is wagtail-oauth2 OAuth2.0 is an authorizatio

Gandi 7 Oct 7, 2022
Skit-auth - Authorization for skit.ai's platform

skit-auth This is a simple authentication library for Skit's platform. Provides

Skit 3 Jan 8, 2022
User-related REST API based on the awesome Django REST Framework

Django REST Registration User registration REST API, based on Django REST Framework. Documentation Full documentation for the project is available at

Andrzej Pragacz 399 Jan 3, 2023
Django CAS 1.0/2.0/3.0 client authentication library, support Django 2.0, 2.1, 2.2, 3.0 and Python 3.5+

django-cas-ng django-cas-ng is Django CAS (Central Authentication Service) 1.0/2.0/3.0 client library to support SSO (Single Sign On) and Single Logou

django-cas-ng 347 Dec 18, 2022
Django-registration (redux) provides user registration functionality for Django websites.

Description: Django-registration provides user registration functionality for Django websites. maintainers: Macropin, DiCato, and joshblum contributor

Andrew Cutler 920 Jan 8, 2023
Complete Two-Factor Authentication for Django providing the easiest integration into most Django projects.

Django Two-Factor Authentication Complete Two-Factor Authentication for Django. Built on top of the one-time password framework django-otp and Django'

Bouke Haarsma 1.3k Jan 4, 2023
Django Admin Two-Factor Authentication, allows you to login django admin with google authenticator.

Django Admin Two-Factor Authentication Django Admin Two-Factor Authentication, allows you to login django admin with google authenticator. Why Django

Iman Karimi 9 Dec 7, 2022
Django-react-firebase-auth - A web app showcasing OAuth2.0 + OpenID Connect using Firebase, Django-Rest-Framework and React

Demo app to show Django Rest Framework working with Firebase for authentication

Teshank Raut 6 Oct 13, 2022
A simple username/password database authentication solution for Streamlit

TL;DR: This is a simple username/password login authentication solution using a backing database. Both SQLite and Airtable are supported.

Arvindra 49 Nov 25, 2022