I was trying to create a custom permission in a migration, however after running migrate, the permission was not created in the permission table. Could someone point out what the error was?
Also I am not sure what I should use as the related model for ContentType as the permission is used for restricting users that can view a page which shows summary of users on the site.
Any help will be greatly appreciated, thanks.
def add_view_aggregated_data_permissions(apps, schema_editor):
ContentType = apps.get_model('django', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
content_type = ContentType.objects.get(app_label='auth', model='user')
permission = Permission.objects.create(codename='can_view_data',
name='Can view data',
content_type=content_type)
I would recommend you to use the standard way to use custom permissions as described in the Django documentation. You will avoid many issues altogether.
To create custom permissions for a given model object, use the permissions model Meta attribute.
This example model creates a custom permission:
class MyModel(models.Model):
...
class Meta:
permissions = (
('view_data', "Can see available data"),
)
The only thing this does is create those extra permissions when you run manage.py migrate. Your code is in charge of checking the value of these permissions when a user is trying to access the functionality provided by the application...
Then you can use the permission_required decorator with your view to check for the specific permission:
from django.contrib.auth.decorators import permission_required
#permission_required('myapp.view_data')
def my_view(request):
...
I wanted to created a custom permission (read) for all app models. I did this two steps:
Create an extended permission from DjangoModelPermissions:
class DjangoModelPermissionsExtended(DjangoModelPermissions):
"""
"""
perms_map = {
'GET': ['%(app_label)s.read_%(model_name)s'],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
Put it in each view I want to have read permission:
class ExampleViewSet(viewsets.ModelViewSet):
permission_classes = (
DjangoModelPermissionsExtended,
)
Create a django command customread.py:
from django.core.management.base import BaseCommand, CommandError
from project.app import models as app_models
from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
import inspect
class Command(BaseCommand):
help = 'Create the read permission to app models'
def handle(self, *args, **options):
for name, obj in inspect.getmembers(app_models):
if inspect.isclass(obj) and issubclass(obj, models.Model):
try:
self.add_canread(obj)
self.stdout.write(self.style.SUCCESS(
'created permission for %s' % obj
))
except Exception as e:
self.stdout.write(self.style.ERROR(
'Permission already exists for %s' % obj
))
def add_canread(self, object_class):
"""This a function that can be executed in order to create
new permissions (read view) to a class in DB.
"""
if inspect.isclass(object_class):
content_type = ContentType.objects.get_for_model(object_class)
permission = Permission.objects.create(
codename='read_{}'.format(object_class._meta.model_name),
name='Can view {}'.format(object_class.__name__),
content_type=content_type,
)
else:
msg = "The object is not a class"
print(msg)
Execute it after doing migrations:
python manage.py customread
As of django 1.8 and built-in migrations this is very painless.
All you need to do is add the permissions you want to the relevant
model
Run makemigration
./manage.py makemigrations
run the migration created in the step above
./manage.py migrate
Related
I want to create the permissions in views dynamically instead of the defaults at models
I create a main class called CreatorView
from django.views.generic import View
from django.forms.models import modelform_factory
class CreatorView(View):
model = None
fields = None
exclude = None
form = None
page_title = ''
def create_form(self):
default =dict()
if self.fields == None:
if self.exclude == None:
default['fields'] = self.fields = self.model._meta.fileds
else:
default['exclude'] = self.exclude
else:
if self.exclude:
raise Exception('error')
default['fields'] = self.fields
return modelform_factory(self.model,**default)
def get(self,request,*args,**kwargs):
return render('','base.html')
def post(self,request,*args,**kwargs):
... and so on
the main urls is:
urlpatterns = [
path('%s/%s' % (cls.model._meta.app_label, cls.__name__.lower()), cls.as_view(),
name='%s/%s' % (cls.model._meta.app_label, cls.__name__.lower())) for cls
in CreatorView.__subclasses__()]
if I make inheritance from CreatorView then my class should create a page
for example:
class Login(CreatorView):
model = Users
""" my overwrite methods and actions """
class Configurations(CreatorView):
model = Configure
""" my overwrite methods and actions """
class Teachers(CreatorView):
model = Teachers
""" my overwrite methods and actions """
class Students(CreatorView):
model = Students
""" my overwrite methods and actions """
and so on
this code will create to me four pages
I want to create table semi to django content_type model to be like:
id
app_label
page
1
myapp
login
2
myapp
configurations
3
myapp
teachers
4
myapp
students
Can I modify auth_permission table in Django to make content_type foreign key from my content_type?
If I can how to prevent insert default permissions and make my insertion for default permission?
You can create Permissions manually according to docs here
from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
codename='can_publish',
name='Can Publish Posts',
content_type=content_type,
)
OR
An enhanced permission library which enables a logic-based permission system to handle complex permissions in Django.
here
I am using django-rest-framework and default auth_user table of Django and I create new table to manage new user_role and I extend it using auth_user.id to manage more roles like (teacher, student, staff, admin, school) but Now In Django there is only 3 decorators permission_classes (IsAuthenticated, AllowAny, IsAdminUser) to authenticate different role users to authenticate user.
I am using below JWT Token Module in my Django App.
JWT TOKEN: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/
Solution which I think: first make sure user is authenticated or login already, using IsAuthenticated decorator permission_classes and then I filter table and check user have enough permission to access specific route or not on basis of user role from extended user table
ISSUE: I need to filter data again and again in every route to check user role.
Please let me know best approach to manage this thing
What you think the solution should be is how I would approach it. IsAdminUser is built into django-rest-framework but when using it with a JWT library, it makes more sense to only check IsAuthenticated. I am currently working on something similar in an application with multiple roles.
# models.py
from django.db import models
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
class MyUser(AbstractBaseUser, PermissionsMixin):
ADMIN = 1
MANAGER = 2
EMPLOYEE = 3
ROLE_CHOICES = (
(ADMIN, 'Admin'),
(MANAGER, 'Manager'),
(EMPLOYEE, 'Employee'),
)
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, blank=True)
# Other fields omitted
If your users can have many roles, you can create a role table and do a ManyToMany field. Once you get to your View logic, you could do something like this:
# views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenicated
from rest_framework.response import Response
class SomeApiView(APIView):
# declare serializer
permission_classes = (IsAuthenticated, )
def get(self, request):
user = request.user
if user.role != 1:
return Response({}, HTTP_403_FORBIDDEN)
else:
# get logic for requested view
I'm a new django user looking for some help with django-rules. I'm trying to set up an authorization system that is 'OR' based. I have a 'File' model. I would like only the creator to be able to delete it, but a specific set of users to edit it. I've been able to followed their tutorial and implementation throughout; it works in the shell but not on my site. At the moment no one can delete or update anything.
My view currently looks like:
class FileUpdateView(PermissionRequiredMixin, generics.RetrieveUpdateAPIView):
"""
View updating details of a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.change_file'
raise_exception = True
class FileDeleteView(PermissionRequiredMixin, generics.RetrieveDestroyAPIView):
"""
View for deleting a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.delete_file'
raise_exception = True
The rules themselves are:
import rules
#rules.predicate
def is_creator(user, file):
"""Checks if user is file's creator"""
return file.owner == user
is_editor = rules.is_group_member('ReadAndWrite')
rules.add_perm('fileupload.change_file', is_editor | is_creator)
rules.add_perm('fileupload.delete_file', is_creator)
I know I'm close I feel like I'm just missing one step.
Thanks in advance!
please check & add settings file authentication backend for Django-rules. Also, you are mixing Django rest permissions with Django-rules permission. you need to check Django-rules permission in Django-rest permissions on view.
in short.
define a custom permission in rest-framework like this.
from rest_framework import permissions
class RulesPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return request.user.has_perm('books.edit_book', obj)
in viewset.
class BookView(viewsets.ModelViewSet):
permission_classes = (RulesPermissions,)
I've been working with the django REST framework and django-rules for a project and found an answer to your question.
The django REST framework uses an API view which is not compatible with rules' views.PermissionRequiredMixin, the authorization workflow and methods called during the API dispatch is different from django's class based view.
Try the following mixin for django REST framework API views and their subclasses:
import six
from django.core.exceptions import ImproperlyConfigured
class PermissionRequiredMixin:
permission_required = None
def get_permission_object(self):
object_getter = getattr(self, 'get_object', lambda: None)
return object_getter()
def get_permission_required(self):
if self.permission_required is None:
raise ImproperlyConfigured(
'{0} is missing the permission_required attribute. Define '
'{0}.permission_required, or override '
'{0}.get_permission_required().'
.format(self.__class__.__name__)
)
if isinstance(self.permission_required, six.string_types):
perms = (self.permission_required, )
else:
perms = self.permission_required
return perms
def check_permissions(self, request):
obj = self.get_permission_object()
user = request.user
missing_permissions = [perm for perm in self.get_permission_required()
if not user.has_perm(perm, obj)]
if any(missing_permissions):
self.permission_denied(
request,
message=('MISSING: {}'.format(', '.join(missing_permissions))))
With this mixin you're not forced to write a REST framework permission for each rules permission.
After seeing this post, I tried to create my own group at project setup with this migration :
from django.db import migrations
from django.contrib.auth.models import Group, Permission
def create_group(apps, schema_editor):
group, created = Group.objects.get_or_create(name='thing_managers')
if created:
add_thing = Permission.objects.get(codename='add_thing')
group.permissions.add(add_thing)
group.save()
class Migration(migrations.Migration):
dependencies = [
('main', '0002_auto_20160720_1809'),
]
operations = [
migrations.RunPython(create_group),
]
But I got the following error :
django.contrib.auth.models.DoesNotExist: Permission matching query does not exist.
Here is my model :
class Thing(models.Model):
pass
Why can't I do that? How could I solve this?
I use django 1.9.
Permissions are created in a post_migrate signal. They don't exist the first time migrations are run after a new model is added. It is probably easiest to run the post_migrate signal handler manually:
from django.contrib.auth.management import create_permissions
def create_group(apps, schema_editor):
for app_config in apps.get_app_configs():
create_permissions(app_config, apps=apps, verbosity=0)
group, created = Group.objects.get_or_create(name='thing_managers')
if created:
add_thing = Permission.objects.get(codename='add_thing')
group.permissions.add(add_thing)
group.save()
create_permissions checks for existing permissions, so this won't create any duplicates.
From this Django ticket, here's what worked for me in Django 3.0.4 and apparently will work in >=1.9:
from django.core.management.sql import emit_post_migrate_signal
def create_group(apps, schema_editor):
# Ensure permissions and content types have been created.
db_alias = schema_editor.connection.alias
emit_post_migrate_signal(2, False, db_alias)
# Now the content types and permissions should exist
Permission = apps.get_model('auth', 'Permission')
...
One solution is call the update_permissions command before try to append a permission
from django.core.management import call_command
def update_permissions(schema, group):
call_command('update_permissions')
operations = [
migrations.RunPython(update_permissions, reverse_code=migrations.RunPython.noop),
migrations.RunPython(create_group),
]
And as was commented don't import Group and Permission models use:
Group = apps.get_model("auth","Group")
Permission = apps.get_model("auth","Permission")
I can't figure out why the permission required decorator isn't working. I would like to allow access to a view only for staff members. I have tried
#permission_required('request.user.is_staff',login_url="../admin")
def series_info(request):
...
and also
#permission_required('user.is_staff',login_url="../admin")
def series_info(request):
...
As the superuser, I can access the view, but any users I create as staff can't access it and are redirected to the login url page. I tested the login_required decorator and that works fine.
permission_required() must be passed a permission name, not a Python expression in a string. Try this instead:
from contrib.auth.decorators import user_passes_test
def staff_required(login_url=None):
return user_passes_test(lambda u: u.is_staff, login_url=login_url)
#staff_required(login_url="../admin")
def series_info(request)
...
Thanks. That does work. Do you have an
example of how to use
permission_required? From the
documentation
docs.djangoproject.com/en/1.0/… and
djangobook.com/en/2.0/chapter14 I
thought what I had should work.
Re-read the links you posted; permission_required() will test if a user has been granted a particular permission. It does not test the attributes of the user object.
From http://www.djangobook.com/en/2.0/chapter14/:
def vote(request):
if request.user.is_authenticated() and request.user.has_perm('polls.can_vote'):
# vote here
else:
return HttpResponse("You can't vote in this poll.")
#
#
# # #
###
#
def user_can_vote(user):
return user.is_authenticated() and user.has_perm("polls.can_vote")
#user_passes_test(user_can_vote, login_url="/login/")
def vote(request):
# vote here
#
#
# # #
###
#
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# vote here
This is how I would do it:
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def series_info(request):
...
The documentation says about staff_member_required:
Decorator for views that checks that the user is logged in and is a staff member, displaying the login page if necessary.
Here is an example of behavior I don't understand. I create a user, request and decorate a test function with permission_required checking for 'is_staff'. If the user is superuser, then access is granted to the test function. If the user only has is_staff = True, access is not granted.
from django.http import HttpRequest
from django.contrib.auth.models import User
from django.contrib.auth.decorators import permission_required
#permission_required('is_staff')
def test(dummy='dummy'):
print 'In test'
mb_user = User.objects.create_user('mitch', 'mb#home.com', 'mbpassword')
mb_user.is_staff = True
req = HttpRequest()
req.user = mb_user
test(req) # access to test denied - redirected
req.user.is_staff = False
test(req) # same as when is_staff is True
req.user.is_superuser = True
test(req) # access to test allowed
By the way, if you're using class-based views, you should wrap your decorator in the method_decorator decorator (go figure):
class MyView(DetailView):
...
#method_decorator(permission_required('polls.can_vote', login_url=reverse_lazy('my_login')))
def dispatch(self, request, *args, **kwargs):
.... blah ....
class MyModel(models.Model):
...
def has_perm(self perm, obj=None):
if perm == 'polls.canvote':
return self.can_vote()
This works for me on my 'project' table/model:
#permission_required('myApp.add_project')
def create(request):
# python code etc...
Obviously change the add_project to the add_[whatever your model/table is]. To edit it would be:
#permission_required('myApp.edit_project')
and to delete:
#permission_required('myApp.delete_project')
But I found that the key thing is to make sure your auth tables are set up correctly. This is what caused me problems. Here is a MySQL SQL query I wrote to check permissions if you are using groups. This should work in most dBs:
select usr.id as 'user id',usr.username,grp.id as 'group id',grp.name as 'group name',grpu.id as 'auth_user_groups',grpp.id as 'auth_group_permissions',perm.name,perm.codename
from auth_user usr
left join auth_user_groups grpu on usr.id = grpu.user_id
left join auth_group grp on grpu.group_id = grp.id
left join auth_group_permissions grpp on grp.id = grpp.group_id
left join auth_permission perm on grpp.permission_id = perm.id
order by usr.id;
I found that my permissions were not set up correctly, and also watch out for the django_content_type table which must have rows for each app and table for each of add, edit, delete. So if you have a project table you should see this in django_content_type:
id [generated by dB]
app_label myApp
model project
If you are having trouble another good idea is to enable and use the django admin app. This will show you where your problems are, and by setting up some test permissions, users and groups you can then examine the tables discussed above to see what is being inserted where. This will give you a good idea of how auth permissions works.
I write this to maybe save someone from having to spend a few hours figuring out what I did!