Giving default permissions or a default group to new users - django

Default behaviour seems to be new users have no permissions and no groups. However I'd like not to have to manually grant every single new users basic permissions, and I'd assume they'd like not to have to wait for me to do so.
How should I assign default permissions for new users?
Some similar questions have been asked but with no clear answer for the general case:
Django default user permissions
How do I define default permissions for users in Django Guardian?
Django Assign-perm w/ Post-Save Signal
I'm using python-social-auth so I don't have my own create-user form and view which I guess is where everyone else sets default permissions. I assume I need an on-user-create hook of some sort but not sure what the cleanest approach is.
This is unrelated to creating the default possible permissions that may be granted to users, although for reference,
Default permission/group data in Django
In Django, how do you programmatically create a group with permissions

You can subscribe to post_save signal on User model and put newly created user to desired group or add permissions.
from django.contrib.auth.models import Group
def add_to_default_group(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
group = Group.objects.get(name='groupname')
user.groups.add(group)
And on django 1.8+ put following code into your AppConfig.ready()
from django.conf import settings
post_save.connect(add_to_default_group, sender=settings.AUTH_USER_MODEL)

Related

How do I apply higher permissions to child pages in Wagtail?

I am building an intranet site for my organization with Wagtail and we are in the process of adding a knowledge base. The entire site needs to be restricted to logged-in users, but certain pages need to only be accessible to users in certain groups. For instance, only members of the IT group should be able to access the pages underneath the IT Knowledge Base page.
Currently if I set the top-level page to be accessible only by logged-in users, that permission is applied to every page on the site, and I am barred from setting more specific permissions on any child page. It is imperative that I be able to set more specific permissions on child pages.
I was able to find Wagtail Bug #4277 which seems to indicate that the logic for more specific permissions is implemented but not exposed in the admin UI.
I am not familiar with the inner workings of Wagtail yet, especially how Wagtail permissions intersect with Django permissions. How can I add more specific permissions to child pages?
You can restrict or allow users to view a site. You can also restrict or allow users to do some actions (maybe modifying an article).
To pass these restrictions or allowances django uses groups and permissions. Basically it all is based on permissions but sometimes you want to pass the permission to an entire group rather than passing permissions to users explicitly.
Therefore you could create your it_group. Then you would add the permission, let's call it it_permission to that group. When you then add a user to that group, that user then has all the group permissions. As said you don't need to organize these steps with groups. You could also add a permission, let's call it admin_status to a user directly.
When you build your views there are multiple operators that check for permissions of currently logged in user.
You could decorate your view with the permission-required-operator.
See the example:
from django.contrib.auth.decorators import permission_required
#permission_required('your_user_app.it_permission')
def my_view(request):
# only users with permissions can view this view.
Django and Wagtail are both awful at object authorisation.
For your case, it depends how tight you want to make the security, and how complex the authorization model is.
You can set permissions on a per page basis via the group edit page in the admin menu, any page below will inherit those permissions. The problem with this is that the least restrictive permissions apply and there is no deny option. If they have edit permission on a parent page, they'll have edit permission on the child page.
If you just want a superficial prevention to stop unauthorised people editing all knowledge base pages, you might look at using hooks to assess the permissions of the logged in user.
You can use before_edit_page to check before the page form is rendered for editing and redirect to a notification page if they fail, or use after_page_edit to prevent saving (not very friendly after the editor has spent some time on the page).
For before edit, for a simple one-off case and where there is a class for the KB page, where you want to allow only members of the IT Department and Site Managers groups to have access, it could be something like:
# wagtail_hooks.py
from django.contrib import messages
from django.http import HttpResponseRedirect
from wagtail import hooks
from kb.models import KnowledgeBasePage
#hooks.register("before_create_page")
#hooks.register("before_delete_page")
#hooks.register("before_edit_page")
def check_kb_permissions(request, page, page_class=None):
if (page_class or page.specific_class) == KnowledgeBasePage:
if not request.user.groups.get_queryset().filter(name__in=['Site Managers','IT Department']).exists():
messages.error(
request,
'You do not have permission to add, edit or delete knowledge base articles.\
<br><span style="padding-left:2.3em;">Contact support \
to report this issue</span>'
)
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/admin/'))
If the user fails, they stay on the same page with an error message in red banner at the top of the page.
You can build this out to a more complex authorisation model, matching user groups with models and permissions if need be, rather than the hard coded example above.
This doesn't affect CRUD permissions for programmatic operations, but if your concern is just the editor interface then this works.

Create custom permission classes

I have designed my own RBAC system for my Django app. Unlike the original Django's role-based that uses only permissions, content_types, groups, and users table, I designed mine so that it includes a model for defined operations (i.e. read, write, print), the objects (i.e. Post, Events, Documents), the roles (Writer Level 1, Events Manager) and the necessary relationships i.e. my own permissions table which takes in the object reference and a matching operation reference (i.e. Post | add).
My problem now is I am not quite sure how to implement this in DRF. When I use permission_class = (DjangoObjectPermissions,) and someone sends a 'POST' request to the Post model/table, we all know Django will check if the user has Can add post permission. I want to do this as well but I want Django to refer to my RBAC models/tables.
I want to write my own permission classes in my permissions.py but I might need a bit hint on how to implement this.
I've read you can create custom permissions, but I am not sure if that also means you can enforce your own RBAC tables with it.
Alright after checking rest_frameworks' permissions.py, I think I can enforce my own permission class behavior this way:
Create a custom permission class that subclasses rest_framework.permissions.BasePermission
override has_object_permission() method
Inside the method, write a logic that maps the requesting user into my custom RBAC models to check whether or not he has given the appropriate permission
Return the appropriate boolean
According to the DRF Permissions doc
As with DjangoModelPermissions, this permission must only be applied to views that have a .queryset property or .get_queryset() method. Authorization will only be granted if the user is authenticated and has the relevant per-object permissions and relevant model permissions assigned.
So, you should define either queryset attribute or get_queryset(...) method in your view class.
Example:
class MyViewClass(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
...
...
...
def get_queryset(self):
return SomeModel.objects.filter(some_field='foo')
...
...
...
The permissions to a User or groups can be controlled via Django's Admin site as well as Django shell

Django: derive permissions from user_type

I am implementing permissions functionality into my webapp. The requirements specified by the customer are that users can perform some actions based on what kind of user they are. The kind of user is defined based on a combination of user_type:user_role. This is the model:
class User(AbstractUser):
usertype = models.IntegerField(choices=UserType.CHOICES, default=UserType.BUYER_USER)
role = models.IntegerField(choices=UserRole.CHOICES, default=UserRole.NORMAL_USER)
For example we can have:
adviser:regular: can manager_orders
adviser:admin: can do what an adviser:regular does and also manage_advisers (this is no django admin, but an admin for advisers!)
(there are more user_type:user_role)
This seems to conflict with the django permission system, which is based on what permission a user has, instead of what kind of user it is.
That is, when using the django permission system I need to define all possible permissions and assign those permissions to the affected user. Instead, with the approach that I am planning, I would need to derive the permission from the user_type:user_role.
Does it make sense to use the django permission system in this context? How can I derive the permission from the user_type:user_role
If you don't want to stick to #ShangWangs approach in the comments, I would recommend taking a look at Django Object Based Permissions.
With this, you are able to grant permissions to users for objects that derive from a super Model you want to apply the permissions to.
Per the Django docs, you can do this with a custom authentication backend:
https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#handling-authorization-in-custom-backends
Any time Django needs to check if a user has a particular permission it will delegate to the auth backend, passing the current user object as an arg. At this point you can check the usertype and role fields and return appropriate boolean value.
example from the docs:
class SettingsBackend(object):
...
def has_perm(self, user_obj, perm, obj=None):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
From what you describe in your question you do not need the complication of 'object-level' permissions as mentioned by others (since you don't have unique permissions for each user, they are based on the type and role)

How to save the permissions after assigning them to a user in django 1.3

I have a set of permissions, from which I am assigning some specific permissions to the user. i am using this:
assign('view_userreports', <objectofuser>, <objectofreport>)
The permission is assigned successfully, but when I goto admin interface to see the assigned permissions it dosen't highlight any of the permissions. Please help me how can I save the assigned permissions?
I believe the shortcut method should be assign_perm and not assign. See the django-guardian docs here.
Also, note that object-level permissions will not show up on the admin Auth view (http://localhost:8000/admin/auth/user/) for the user. In your app's admin.py (or wherever you register your models to the admin interface), do the following:
from myapp.models import SomeModel
from django.contrib import admin
from guardian.admin import GuardedModelAdmin
class SomeModelAdmin(GuardedModelAdmin):
pass
admin.site.register(SomeModel, SomeModelAdmin)
Once you have granted a user some object-level permissions for a specific object, browse to that object in the Admin interface. Now you should have an Object Permissions link in the upper right of the page:
Click on that and you will be able to see and manage the object-level permissions for that object for any Django user.

Setting Django admin permissions programmatically

The Django site I'm working on has the possibility for users to sign up for an account. To provide them with some editing functionality, I use the built-in Django admin. However, I'm having a problem: After a user has signed up, they don't have any permissions inside the Django admin, not even view permissions. Thus my question: How do I, in code, assign admin permissions to the user for the relevant models, in the same way I can assign them manually in the "User Permissions" section when editing the user in the admin? I've already tried with the usual has_xxx_permissions() using custom ModelAdmin classes, but that didn't work. So my guess is that I overlooked something obvious. Any ideas?
https://docs.djangoproject.com/en/dev/topics/auth/default/#permissions-and-authorization
new_user.user_permissions.add(permission1, permission2, etc...)
For your purposes, it would probably be much more easy and and efficient to assign all new users to a particular group, and then give that group all the permissions the user needs. Any member of the group will inherit those permissions as well.
You can create the group and assign the permissions to it in the admin. Then, you just need to add something like the following to your registration code.
try:
group = Group.objects.get(name='The User Group')
except Group.DoesNotExist:
# group should exist, but this is just for safety's sake, it case the improbable should happen
pass
else:
user.groups.add(group)
dgel's answer pointed me in the direction which lead to a working solution for me. Essentially, what he seems to be suggesting is:
Retrieve a ContentType for the model you want to set permissions for. In this context, a content type is an object that holds information about a Django model.
Create a Permission object consisting of the content type and the action you want to allow inside the admin, using Permission.objects.get(). The only difficulty here is figuring out the codename parameter, which, for admin permissions, consists of an action ("add", "change" or "delete"), an underscore, and the model name. So if you have a model called Foo and you want to create all permissions for it, you'll need 3 permissions, each with the content type of your Foo model plus the code names add_foo, change_foo, and delete_foo.
Assign these permissions using user.user_permissions.add(permission).
Head over to dgel's answers for code examples. Looking at a data dump of the auth app (manage.py dumpdata auth) of an existing Django database provided me with insights into the inner workings of permissions, too.
I'll answer your question exactly since I found this question with Google. I'll show what I'm doing in Django 1.9 with groups, then show how to do it to a user.
from django.contrib.auth.models import Group, Permission
group, __ = Group.objects.get_or_create(name='my_group')
permissions = Permission.objects.all()
for p in permissions:
group.permissions.add(p)
group.save()
It's pretty easy to adapt to user:
from django.contrib.auth.models import Permission
permissions = Permission.objects.all()
for p in permissions:
youruser.user_permissions.add(p)
youruser.save()
I prefer group because you may be adding permissions in the future and can just add to group instead of re-doing all users.
As of Django 1.6:
Every User has a many-to-many field user_permissions to Permission - you can add permissions to this:
your_user.user_permissions.add(permission)
v1.6 Docs:
Django.contrib.auth API (shows User, Group and Permission objects)
Auth default permissions (shows how to clear, add, remove)