Code Monkey home page Code Monkey logo

serious-django-permissions's Introduction

Serious Django: Permissions

https://github.com/serioeseGmbH/serious-django-permissions

serious-django-permissions is a Django extension that makes it possible to define Permissions in each app, and Groups in a central place. It makes these Permissions import-able and checkable via user.has_perm:

from some_app.permissions import ChangeSomethingPermission

def change_something(something, user):
    if not user.has_perm(ChangeSomethingPermission):
        raise PermissionDenied("You can't change this something.)
    else:
        return something.change()

Both permissions and groups can then be created in the database with a single manage.py call (see Quick start below).

Quick start

  1. Install the package with pip:

    pip install serious-django-permissions
    
  2. Add "serious_django_permissions" to your INSTALLED_APPS setting like this:

    INSTALLED_APPS = [
        ...
        'serious_django_permissions',
    ]
    
  3. Add serious_django_permissions.permissions.PermissionModelBackend to your AUTHENTICATION_BACKENDS setting. This enables you to do permission checks like user.has_perm(SomePermission):

    AUTHENTICATION_BACKENDS = [
        ...
        'serious_django_permissions.permissions.PermissionModelBackend',
    ]
    
  4. In each app that should define a permission, import serious_django_permissions.permissions.Permission and create subclasses of it.

    The name of your subclasses must end in Permission, and each subclass must define a description attribute. For instance, let's say you have the file myapp/permissions.py:

    from serious_django_permissions.permissions import Permission
    
    from .models import MyModel
    
    class MyPermission(Permission):
        model = 'MyModel' # should be a model inside myapp.models, or not defined for global permissions.
        description = 'A description for this permission'
    
  5. Run python manage.py create_permissions to create all defined permissions on the database level.

New in version 0.7: Create all permissions (including groups) programmatically by calling setup_permissions():

from serious_django_permissions.helpers import setup_permissions

setup_permissions()
  1. If you'd like to use the Groups feature as well:

    1. Create a file named something like some_app/groups.py inside one of your apps, or in the folder where your settings live. An example:

      from serious_django_permissions.groups import Group
      
      from app_one.permissions import\
          AppOnePermissionA, AppOnePermissionB
      from app_two.permissions import\
          AppTwoPermission
      
      class GroupA(Group):
          permissions = [
              AppOnePermissionA,
              AppTwoPermission
          ]
      
      class GroupB(Group):
          permissions = [
              AppOnePermissionB,
              AppTwoPermission
          ]
      
    2. Reference the defined groups file in your settings:

      DEFAULT_GROUPS_MODULE = 'some_app.groups'
      
    3. Run python manage.py create_groups to create all permissions and assign them to the groups.

Authorization

Setting a permission for a model does not automatically restrict the access to the model. Authorization still has to be checked via the view. Check the Django documentation on how to authorize access to views for further information.

serious-django-permissions's People

Contributors

lilithwittmann avatar seblat avatar

Stargazers

Oleg Kleshchunov avatar Wanderson Reis avatar Adrian Likins avatar Nikolaus Schlemm avatar  avatar Jorik Kraaikamp avatar  avatar

Watchers

Simon Welker avatar James Cloos avatar  avatar Lena Schimmel avatar vanita5 avatar  avatar  avatar Anne Lorenz avatar

serious-django-permissions's Issues

permission template tag

Sometimes you want to use your permissions inside a template. Maybe we should create a template tag that makes it easier to user sd-permissions in the context of a django template? Or is this out of scope of this project? What do you think @seblat @cobalamin?

shortcut to setup permissions and groups in tests easier

As a developer I want to use the groups and permissions inside my tests. Currently I have to do this manually via something like:

from serious_django_permissions.management.commands import create_groups, create_permissions

class SomethingTest(TestCase):

    def setUp(self):
        create_permissions.Command.execute()
        create_groups.Command.execute()

It would be cooler if I could write something like:

from serious_django_permissions.helpers import setup_permissions

class SomethingTest(TestCase):

    def setUp(self):
        setup_permissions()

Nice string representations of Groups and Permissions

Sometimes you want to display group or permission names to the user (e.g. error messages). It would look much nicer if the Groups and Permissions classes would have a __str__ representation. Could we add them?
screenshot 2019-02-21 at 23 09 22

Make model attribute for Permissions optional

Currently you need to provide a modelattribute inside a Permission class. This attribute can be empty or none, if a permission should be public.

But if the model attribute is not defined there is an error:

======================================================================
ERROR: test_application_creation_by_admin (applications.tests.ApplicationAdminServiceTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/lilithwittmann/code/ressource_management/applications/tests.py", line 12, in setUp
    create_permissions.Command().handle()
  File "/Users/lilithwittmann/code/.virtualenvs/ressource_management-h_07sZrZ/lib/python3.7/site-packages/serious_django_permissions/management/commands/create_permissions.py", line 29, in handle
    perm, created_at = obj.get_or_create()
  File "/Users/lilithwittmann/code/.virtualenvs/ressource_management-h_07sZrZ/lib/python3.7/site-packages/serious_django_permissions/permissions.py", line 55, in get_or_create
    if cls.model is not None:
AttributeError: type object 'ApplicationAdminPermission' has no attribute 'model'

----------------------------------------------------------------------
Ran 1 test in 0.007s

FAILED (errors=1)
Destroying test database for alias 'default'...

It would be nice if we just could check if the model attribute exists beforehand so it don't has to be set to None explicitly.

Django Guardian compatibility

Currently I'm trying to use Permissions from django-permissions together with djano guardian and facing some issues:

I would like to use guardian functions like assign_perm to add permissions for specific objects to groups of users:

assign_perm(MyPermission, group, app)

this fails first because guardian tries to use the str representation of the Permission here (because it assumes that our Permission object is a string if it is not a Permission object). This should be pretty easy to fix, by adding a string representation. (see https://github.com/django-guardian/django-guardian/blob/8382df9362dc80059864124e93d90418ae44637c/guardian/shortcuts.py#L94)

But if I use Permission.codename instead of Permission it still fails because then django-guardian tries to use the codename of the permission and the content type of the object to lookup the permission in the django Permissions table (see: https://github.com/django-guardian/django-guardian/blob/devel/guardian/managers.py#L34)

I have no idea how to fix this.

Edit: The 2nd issue only exists if the permission assigned to the object is a global permission or a permission that is not related to the model of the object. (what sometimes is the case if we continue to use our permissions like we do right now.)

Simplify usage of `@permission_required()` decorator

When using the @permission_required() decorator on a view, the permssion is pased to the decorator in a <app label>.<permission codename> format. E.g.: @permission_required('pollsapp.can_vote')

Using serious-django-permissions you would expect the permission to be passed via the .codename property. E.g.: @permission_required('test_app.RestrictedModelPermission.codename'). Yet this way the decorator does not recognize the permission.

The permission codename has to be passed explicitly. Eg.: @permission_required('test_app.restricted_model')
with RestrictedModelPermission.codename = "restricted_model"

I guess passing the permission via @permission_required('test_app.RestrictedModelPermission.codename') or even @permission_required('test_app.RestrictedModelPermission') would simplify using the decorator with serious-django-permissions. @cobalamin any thoughts on that?

simplify accessing group instances

At the moment SeriousDjangoPermissionsPermissionClass.get_or_create()[0] is necessary to access the group of a SeriousDjangoPermissionsPermissionClass. E.g.:

from some_app.default_groups import ApplicationDeveloperGroup
from serious_django_permissions.groups import Group as sdpGroup

(Pdb) ApplicationDeveloperGroup
<class 'resource_management.default_groups.ApplicationDeveloperGroup'>
(Pdb) issubclass(ApplicationDeveloperGroup, sdpGroup)
True
(Pdb) ApplicationDeveloperGroup.get_or_create()[0]
<Group: application_developer>

The access to the group instance could be simplified by providing something like SeriousDjangoPermissionsPermissionClass.get().

Comments by @cobalamin on this::

On your last paragraph: Yes, I agree about immediate usability, but each of those costs us a database roundtrip. So I have intentionally not exposed it as a property so far, because properties tend to hide this fact, while we might do a lot of these accesses. A good compromise would be something short but still indicative of it being a function call that has side effects, e.g. ApplicationDeveloperGroup.get().

(Maybe we could throw the simplest version of caching at it (with no invalidation). For that to work, the IDs of the Django Group instances in the database that correspond to our defined sdp groups must never change while any server instance accessing one database is up. Or we need to come up with clever cache invalidation.)

Easier access to the model representation of one Group

There are some cases when you need the model representation of a Group. Currently we have to write something like:

self.user.groups.add(AuthorizedGroup.get_or_create()[0].pk)
# or
self.user.groups.add(Group.objects.filter(name=AuthorizedGroup.group_name).pk)

to add a user to a group.

It would be much cooler if there would be an easier option to access the model representation of a Group Object.

# maybe something like
self.user.groups.add(AuthorizedGroup)

# or
self.user.groups.add(AuthorizedGroup.model.pk)

# or

self.user.groups.add(AuthorizedGroup.group_id)

What do you think @cobalamin?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.