Django admin -- restricting access by user - django

I was wondering if the django admin page can be used for external users.
Let's say that I have these models:
class Publisher(models.Model):
admin_user = models.ForeignKey(Admin.User)
..
class Publication(models.Model):
publisher = models.ForeignKey(Publisher)
..
I'm not exactly sure what admin_user would be -- perhaps it could be the email of an admin user?
Anyways. Is there a way allow an admin user to only add/edit/delete Publications whose publisher is associated with that admin user?
-Thanks!
-Chris

If you need finer-grained permissions in your own applications, it should be noted that Django's administrative application supports this, via the following methods which can be overridden on subclasses of ModelAdmin. Note that all of these methods receive the current HttpRequest object as an argument, allowing for customization based on the specific authenticated user:
queryset(self, request): Should return a QuerySet for use in the admin's list of objects for a model. Objects not present in this QuerySet will not be shown.
has_add_permission(self, request): Should return True if adding an object is permitted, False otherwise.
has_change_permission(self, request, obj=None): Should return True if editing obj is permitted, False otherwise. If obj is None, should return True or False to indicate whether editing of objects of this type is permitted in general (e.g., if False will be interpreted as meaning that the current user is not permitted to edit any object of this type).
has_delete_permission(self, request, obj=None): Should return True if deleting obj is permitted, False otherwise. If obj is None, should return True or False to indicate whether deleting objects of this type is permitted in general (e.g., if False will be interpreted as meaning that the current user is not permitted to delete any object of this type).
[django.com]

I see chris's answer was useful at the time question was asked.
But now it's almost 2016 and I guess it gets more easier to enable restricted access of Django Admin panel to end user.
Django authentication system provides:
Groups: A generic way of applying labels and permissions to more than one user.
Where one can add specific permissions and apply that group to user via admin panel or with writing codes.
After adding user to those specific groups, Admin need to enable is_staff flag for those users.
User will be able access restricted registered models in admin.
I hope this helps.

django admin can, to a certain extent, be restricted. For a given user, first, they must have admin rights in order to log into the admin site. Anyone with this flag set can view all admin pages. If you want to restrict viewing, you're out of luck, because that just isn't implemented. From there, each user has a host of permissions, for create, update and delete, for each model in the admin site. The most convenient way to handle this is to create groups, and then assign permissions to the groups.

Related

Django: Creating permissions that relate two `User` objects together

Is there any way to set up a permissions system where permissions relate User objects together, i.e. where one User may have the permission to read another User's info (but not necessarily the other way around)? I've read the documentation but it's sparse and from what I can tell permissions are not capable of handling dynamic use-cases.
Basically, I want a permission system that determines what two+ model objects can "do" to each other.
Bonus points if you have any input on how to integrate this into Django Rest Framework.
One approach is to override the BasePermission class to create your custom permission. You can specify the access permission using an extra field in the User model.
class UserPermission(BasePermission):
def has_permission(self, request, view):
is_allowed_user = False
try:
permission = Permission.objects.get(user=request.user.id, user1=request.GET.get('user'))
if permission.is_read_enabled:
is_allowed_user = True
else
is_allowed_user = False
except Permission.DoesNotExist as e:
is_allowed_user = False
return is_allowed_user
Create a model to specify the Permissions
class Permission(models.Model):
user = models.ForeignKey(User,...)
user1 = models.ForeignKey(User,...)
is_read_enabled = models.BooleanField(defualt=False)
Add this permission to view as needed.

Wagtail per page user permission

My task is to enable users to edit their own page.
I see how to give permissions to a particular page to a group, but how do I do this for the user?
I'd tried use django guardian:
UserObjectPermission.objects.assign_perm('change_custompage', user, obj=custompage)
but this wasn't given me results
I also found the wagtailcore_grouppagepermission table and realized that the permissions principle is not the same as used in django auth_permission
I want to give permissions to edit, and access wagtail admin, I do not need anything else
In this particular task, wagtail I need because of the struct_blocks, I think this is a terrific concept.
Now I see two options: either an override template and the ability to use all the power of the struct_block outside the wagtail, or something to solve with permissions.
Two possibilities:
Create a group for each user - might not seem like a very sophisticated approach, but easily achievable with a bit of scripting...
Put your users in a group that has add permission, but not edit permission, on the parent page that contains all of your users' pages. In Wagtail's permission model http://docs.wagtail.io/en/v1.10.1/topics/permissions.html, 'add' permission also includes the ability to edit pages that you have created yourself. (If you'd rather have the pages created in advance, rather than having your users create them, then you need to set the owner field of the page record to the relevant user.)
Thanks to the help and advice of #gasman, I did this as follows:
Created two models:
first:
class PersonsIndexPage(RoutablePageMixin, Page):
...
#route(r'^$')
def index_view(self, request):
pages = PersonPage.objects.live().all()
return render(request, 'myapp/person_index_page.html', {
'page': self,
'pages': pages,
})
...
subpage_types = ['PersonPage']
second:
class PersonPage(Page):
...
parent_page_types = ['PersonsIndexPage']
subpage_types = []
For the group add access to wagtail admin, as well as gave permissions to create PersonsIndexPage
Since I create personal pages from Django admin, I created a simple action that creates a page for selected users:
I do not know if bulk_create is possible
def create_person_page(modeladmin, request, queryset):
root_page = PersonIndexPage.objects.first()
for user in queryset:
page = PersonPage(title=username), owner=user)
root_page.add_child(instance=page)
create_person_page.short_description = 'Create person page'

Django: Object level permissions DRY

Object level permissions
Example from http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#object-level-permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user
My need: Queryset of all objects a user can edit
I want to have a django-orm queryset which contains all objects which a given user can edit.
I guess I could solve this by creating a complex django-orm filter (with OR and distinct)
Not DRY
But that's not DRY. That's not DRY because I need to code the stuff twice. One time in has_object_permission() and one time in the django-orm filter.
Question
How to solve my need (queryset of all objects a user can edit) without duplication the permission checking?
If you want that hard to keep things DRY, you'll have to load the entire database entries and apply permission check to every one.
I doubt that's what you really want.
Sometime you can't keep things DRY.
It's the same when you display data to a user. You'll usually apply basic permissions implicitly when performing the query and then ensure the full permissions are valid or not.

How to restrict editing of records to the logged-in user?

Sorry, I am still new to Django, hopefully the question isn't out of place.
When I have the following in my template:
<td>{{ item.last_name }}</td>
By clicking on last name the user will be redirected to the following link in order to edit it.
http://127.0.0.1:8000/contact/edit/?id=1
But then what prevents any logged in user to just inject a different id in there on the browser and edit a record that doesn't belong to him?
Update
I just had an idea when I read the comment and answer below. Rather than using a third party app, couldn't I just create a UserProfile for each user and attach a unique company wide uuid.uuid1(). Each time a loggedin user attempts to edit something, his unique company uuid will be also passed in the link as an additional parameter.
On the edit side, it would harvest this guid and compare it against the logged in user and see if they match. If they do match, he is authorized to proceed with the editing, otherwise he will be redirected.
What do you think? Any weaknesses?
If you use Django's new class based views, e.g. the generic UpdateView, you can extend the dispatch handler.
def dispatch(self, request, *args, **kwargs):
handler = super(MyEditView, self).dispatch(request, *args, **kwargs)
# Only allow editing if current user is owner
if self.object.author != request.user:
return HttpResponseForbidden(u"Can't touch this.")
return handler
In this case, the code verifies that the author field of the model object corresponds to the currently logged in user, before even handling the rest of the request.
You can see a reallife example of this in a project of mine.
When you're using Django auth, always rely on the session mechanism to identify an user instead of making some other id such as uuid1() (except, for example, when you need extra sessions in an e-commerce site under HTTPS).
For the permission part, you could check the ownership directly, mainly as described by Koliber Services. The relationships between Company, User and Contact are crucial for the logic of permission checking. There are many ways to model the relationships and thus the code would differ much. For example:
# a modeling way
User.company -> Company : an user belongs to a company
Contact.contributor -> User : a contact is contributed by an user, would be invalid is user is leaving the company
# could check accessibility by
can_view = contact.contributor.company_id == current_user.company_id
# another modeling way
User.company -> Company : an user belongs to a company
Contact.company -> Company : a contact info is owned by a company, does not share globally
# could check accessibility by
can_view = contact.company_id == current_user.company_id
When can_view is False, user should get a 403 for his unauthorized attempting and get logged.
Normally the above method is enough for content protection(not yet in Django Admin). However, when you have many different types of permission checking and even row-permission checkings, it's better to use some uniform permission API.
Take Django-guardian for example, you could simply map companies to groups and assign can_view permission for a contact for the group representing the user's company. Or, assign the can_view permission to all users in a company when a contact is created by using signal or celery task.
Furthermore, you could use /contact/1/edit/ instead of /contact/edit/?id=1. In this way the int(request.GET('id')) part is moved to urlconf like r'^contact/(?P<pk>\d+)/$', less code and much clearer.
There are some third-party apps that does what you want, its called "row-level permission" where you can give different users different access to specific objects, "Row level" comes from SQL where each object is a row in the database
I use django-guardian to do the job
In the function handling the saving of the data, check to see if the object being edited has the same ID as the presently logged in user.
For example, if the object in question is called EmailPrefs and it has a field called user_id:
Load the EmailPrefs object with the ID of the object being edited
If the user_id does not match the current user, stop further processing
Modify the EmailPrefs object
Save the EmailPrefs object to the database

Non-superuser access to Django Admin

I'd like to restrict which parts of the admin interface are visibile to which staff members - for example, one person might be responsible for only billing related items, another only for customer support, and a third might have full access.
I can provide full access by setting is_superuser to True, but I don't think I want that (I don't want all staff with access to the Admin interface to be super users).
I can allow staff members login to the Admin interface by setting is_staff to True, but once logged in the staff member doesn't have access to anything - they see "You don't have permission to edit anything'.
I tried the method described in this question but I'm getting the same "You don't have permission" result:
class MyAdmin(admin.ModelAdmin):
def has_edit_permission(self, request):
return True
Is there a way to have staff members be able to access parts of the Django admin interface without being superuser?
There's no such thing as has_edit_permission. It's has_change_permission.
For more info on the three permission methods, see the Django ModelAdmin docs:
ModelAdmin.has_add_permission(self, request)
Should return True if adding an object is permitted, False otherwise.
ModelAdmin.has_change_permission(self, request, obj=None)
Should return True if editing obj is permitted, False otherwise. If obj is None, should return True or False to indicate whether editing of objects of this type is permitted in general (e.g., False will be interpreted as meaning that the current user is not permitted to edit any object of this type).
ModelAdmin.has_delete_permission(self, request, obj=None)
Should return True if deleting obj is permitted, False otherwise. If obj is None, should return True or False to indicate whether deleting objects of this type is permitted in general (e.g., False will be interpreted as meaning that the current user is not permitted to delete any object of this type).
For what you are wanting to do, don't override has_change_permission - just give the user(s) and/or user group(s) the corresponding permission ("Can change Xs") in addition to checking "is staff".