How to only sync custom permissions with syncdb? - django

Is it possible to have the manage.py syncdb command ONLY sync custom permissions to the auth_permission table? I don't want the default three permissions installed for the app models, i.e. foo.add_bar, foo.change_bar, foo.delete_bar. I specify my custom permissions in my model classes like the django documentation suggests:
class Meta:
permissions = (
("admin", "Can create/edit/delete projects"),
("user", "Can view projects"),
)
Thank you!

You have 2 solutions here, AFAIK :
manually insert in the auth_permission table ;
Add in a command this snippet.
Adding a command is pretty straightforward. Just create a module sync_perms.py (with the snippet code) in a management.commands package in one of your apps (here the official documentation).
Then run :
python manage.py sync_perms
Et voilĂ  !

Just posted the solution to
Remove (or hide) default Permissions from Django.
The idea is not to prevent them from being created by syncdb, but to hide them from the admin interface.

first you must find the content type id for your application the content type
can be found with this query
select * from django_content_type;
then you execute the query to add to database the custom permission you want for example
insert into auth_permission(name,content_type_id,codename) values
('Can add name',12,'can_add_name');
in the above query , name is a string which the user see in permissions list , the number is the application id found from the first query and codename is the code that you will use in templates for example in template you can use
{{perms.application.can_add_name}}

You need to specify default_permissions on Meta to be empty tuple. (default value is ('add', 'change', 'delete')). Docs.
class MyModel(models.Model):
# properties definition...
class Meta:
permissions = (
("admin", "Can create/edit/delete projects"),
("user", "Can view projects"),
)
default_permissions = ()
I also like to have special MyAppProperties model with Meta.managed=False to hold my app-wide permissions.
class MyAppPermissions(models.Model):
# properties definition...
class Meta:
managed = False
permissions = (
("admin", "Can create/edit/delete projects"),
("user", "Can view projects"),
)
default_permissions = ()
this way it won't create a database table, but permissions for it. Docs.

Related

Django Wagtail dynamic Menu's based on User and User Role

Using Django-Wagtail and Wagtail Menu's. I want to build a system with the following types of characteristics.
I have a class of user (Vendor, Supplier)
Each created user would need to be one of these two classes.
Each class of user has different roles and the roles differ from class to class. e.g.
Vendor: finance, stock, admin
Supplier: finance, stock, admin, driver.
What I have done is create a class called UserType and both "Vendor" & "Supplier" inherit from this class. Then added a field to the User class that selects which type of user they are.
class UserType(model.Model):
common fields.......
class Vendor(UserType):
child_vendor_only_fields.....
class Supplier(UserType):
child_supplier_only_fields.....
class User(AbstractUser):
userVar = models.ForeignKey(UserType, on_delete=models.SET_NULL, null=True, blank=True)
I have also used the wagtail admin to create custom roles for each of these classes as described above.
Clearly what "Vendors" can create, read, update and delete varies from that of a "Supplier". And still within that what the finance role can do within the "Vendor" differs from what "Stock" can do.
How would I create a dynamic menu that displays different menu's for these permutations?
My initial thoughts were inspired by this. Simply adding a choicefield
USER_TYPE_CHOICES = (
('Vendor', 'Vendor'),
('Supplier', 'Suplier'))
class GenericPage(MenuPage):
"""
This model will gain the fields, methods and `setting_panels` attribute
from `MenuPage`, but `settings_panels` is being overridden to include
other fields in the `Settings` tab.
"""
availableTo = CharField(choices=USER_TYPE_CHOICES,)
# 'menupage_panel' is a collapsible `MultiFieldPanel` with the important
# fields already grouped together, making it easy to include in custom
# panel definitions, like so:
settings_panels = [
FieldPanel('custom_settings_field_one'),
menupage_panel
]
I have two questions:
Am I on the correct path for generating custom menu's for type of users? If so how would I then do the same for roles under type of users?
In terms of CRUD should I simply implement a check each time a user does a crud action whether or whether not they can commit that action pragmatically?
For CRUD, I'm not a fan of giving someone the option to do something then tell them that option isn't allowed after the fact. They might have spent half an hour creating/editing that page then find out they've wasted that time. Better to tell them on page load.
For user type, I'd just create those as roles that you add the users to. Then you just have a simple test for the user.groups to see what role they have.
You could easily develop a custom restricted panel for each of the panel types you need to restrict, easier if they're all FieldPanels. In the BoundPanel class you have access to the request object, so you can pull the user object from that at runtime and and decide what to do then.
The Panel.BoundPanel has a method is_shown(), so you can create a custom panel that inherits FieldPanel, override that method to test if the request user in an authorised list parameter and set true/false accordingly.
# restricted_field_panel.py
from wagtail.admin.panels import FieldPanel
class RestrictedFieldPanel(FieldPanel):
def __init__(self, field_name, authorised_groups, **kwargs):
self.field_name = field_name
self.authorised_groups = authorised_groups if isinstance(authorised_groups, list) else [authorised_groups]
super().__init__(self.field_name, **kwargs)
def clone_kwargs(self):
kwargs = super().clone_kwargs()
kwargs.update(
authorised_groups=self.authorised_groups
)
return kwargs
class BoundPanel(FieldPanel.BoundPanel):
def is_shown(self):
show_field = super().is_shown()
is_authorised = self.request.user.groups.get_queryset().filter(name__in=self.panel.authorised_groups).exists()
return (show_field and is_authorised)
In the page model:
content_panels = Page.content_panels + [
RestrictedFieldPanel('some_vendor_field', 'Vendors'),
RestrictedFieldPanel('some_supplier_field', 'Suppliers'),
....
]
For the authorised groups, you might want to append a site admin role by default so that site admins will always see any restricted panel regardless.
Other panel types you'd need to inherit and do test wherever it's doing the rendering.

ViewFlow and django-guardian

I want to make use of django-guardian's object permissions and grant specific rights for specific users to one or more Django users.
I have tried to add some permissions to my Process class like this:
class TestProcess(Process):
title = models.CharField(max_length=64)
something = models.ForeignKey(ForInheritage, null=True, on_delete=models.CASCADE)
no_approval = models.BooleanField(default=False)
approved = models.BooleanField(default=False)
def something_is_approved(self):
try:
return self.something.approved
except:
return None
class Meta:
permissions = (
('view_process', 'View Process'),
)
Unfortunately this causes viewflow to immediately throw an error after starting runserver:
File "/home/me/.virtualenvs/viewflow3/lib/python3.4/site-packages/viewflow/mixins.py", line 253, in ready
self.flow_class.process_class._meta.permissions.append(
AttributeError: 'tuple' object has no attribute 'append'
My initial plan was to subclass Start and View flow classes to change how the Permission function, that is inherited from the PermissionMixin, works. But this seems to be more work than just this, too.
django-guardian is already mentioned in one of the cookbook sections here but currently leads to a 404 page.
What would be the recommended/cleanest way to use guardian permissions on Processes and Tasks?
Your specific problem happens b/c you specify permissions like a tuple, try list instead
class Meta:
permissions = [
('view_process', 'View Process'),
]
Viewflow already adds the 'view' and 'manage' permissions so you can reuse them.
But further restriction per-process view permissions on the object level with django-guardian is not very practical. On each new process creation, in a start view, you will have to grant view permission to all process participant. That leads to hudge permission table grows and slow down the permissions lookup.
The reasonable use case for the object-level permission could be something like to restrict user access to a task based on a user department, for example.
deliver = flow.View(
views.deliver
).Permission(
'parcel.land_on_planet',
obj=lambda process: process.department
).Next(this.report)

Create user roles and permissions extending the existing User model

I want to create user roles (member, partner, admin, etc) extending the existing User model.
class Member(models.Model):
user = models.OneToOneField(User)
#custom fields
What is the best way to create and assign the permissions in this case?
I see many ways to do it:
Programmatically creating permissions:
permission = Permission.objects.create(codename='can_publish',
name='Can Publish Posts',
content_type=content_type)
Included in a model object:
class Task(models.Model):
...
class Meta:
permissions = (
("view_task", "Can see available tasks"),
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
)
is it harcoded? But I want to include the permissions in the Member class, I don't know if it's a good way to do it.
How can I add the Member class to member group? Or do I have to add permissions to member group?:
view_task = Permission.objects.get(name='view_task')
membergroup.permissions.add(view_task)
and after that add user by user from Member class to member group?:
user = User.objects.get(username='test')
membergroup = Group.objects.get(name='member')
user.groups.add(membergroup)
So, I want to put the permissions in Member class and put the class in a member group. I don't know if it's ok.
I want to know if a user is in a member group for example. I see a way but I'm not sure if it is good. Because I have to retrive all user from the group each time:
users_in_group = Group.objects.get(name="member").user_set.all()
if user in users_in_group:
I'm not an expert with permissions in Django, but as far as I know, the main tasks to do (not especially in this order, you do what you need when you need it) are the following:
You create permissions
You create groups
You link users to your groups
You link permissions to your groups
Using this "pattern" allows you to be flexible giving users the ability to be in 0->N groups and the groups being linked to 0->N permissions.
This part of the doc explains how to use permissions with examples :
https://docs.djangoproject.com/en/dev/topics/auth/default/#topic-authorization
That being said, be careful to not mix model extension with permissions:
You extends User model when you want to add special attributes on it. For example, ClientUser and BusinessSeller could be User model extensions because you do want to have different attributes on them. This is not related to permissions.
This means you can use permissions on users without extending the User model.
Not sure this answers totally your question, but I hope it makes things clearer to you.

Dynamically created proxy models do not have listed permissions on admin site

Here is my models.py:
class Item(models.Model):
# ... some irrelevent fields ...
tags = models.ManyToManyField('Tag')
class Tag(models.Model):
name = models.CharField(max_lenght=30)
category_id = models.IntegerField()
Tag is actually a general-purpose name. Each item has many different type of tags - currently there are four types: team tags, subject tags, admin tags and special tags. eventually there will probably be a few more.
The idea is, they all have basically the same fields, so instead of having like 4 tables with manytomany relationship, and instead of adding a new column for Item whenever adding a new type, everything is called a 'tag' and it's very easy to add new types without any change to the schema.
Now to handle this in the admin.py I'm using dyamically created proxy models (based on this), as such:
def create_modeladmin(modeladmin, model, name = None):
class Meta:
proxy = True
app_label = model._meta.app_label
attrs = {'__module__': '', 'Meta': Meta}
newmodel = type(name, (model,), attrs)
admin.site.register(newmodel, modeladmin)
return modeladmin
class TagAdmin(models.Model):
def queryset(self):
return self.model.objects.filter(category_id = self.cid)
class TeamAdmin(TagAdmin):
cid = 1
class SubjectAdmin(TagAdmin):
cid = 2
# ... and so on ...
create_modeladmin(TeamAdmin, name='Teams', model=Tag)
create_modeladmin(SubjectAdmin, name='Subject', model=Tag)
#... and so on ...
This works great for me. However, different staff members need different editing permissions - one guy shouldn't access admin tags, while the other should only have access to edit subject-tags and team-tags. But as far as the admin site is concerned - the dynamic models do not exist in the permission list, and I can't give anyone permissions regarding them.
i.e. a user given all permissions on the list will still not have access to edit any of the dynamic models, and the only way to let anyone access them at all is to give him a superuser which obviously defies the point
I searched SO and the web and I can't anyone with a similar problem, and the docs don't say anything about this not in the dynamic models section or the proxy models section. so I'm guessing this is a different kind of problem. Any help would be greatly appreciated
UPDATE
So after some research into it, the answer was simple enough. Since permissions in django are objects that are saved to the database, what I needed to do was simple - add the relevent permissions (and create new ContentType objects as well) to the db, and then I could give people specific pemissions.
However, this raised a new question - is it a good convention to put the function that creates the permissions inside create_modeladmin as a find_or_create sort of function (that basically runs every time) or should it be used as an external script that I should run once every time I add a new dynamic model (sort of like how syncdb does it)?
And is there a way to also create the permissions dynamically (which seems to me like the ideal and most fitting solution)?
of course you can create permissions, django have django.contrib.auth.management.create_permissions to do this

Better to use 3rd Party Row-Level Permission App or to Filter Query Results in a Django View?

I am writing a Django app that involves the creation of documents. I have a few requirements:
Check to see if the user is able to view any documents at all.
If the user is allowed to view documents, only allow them to view documents they have permission for.
I have come up with two solutions and am wondering if one is philosophically/practically better than the other.
The two solutions I've come up with are:
Solution One (using third-party Django-Guardian)
Models.py
class Document(models.Model)
owner = models.ForeignKey(User)
document_name = models.CharField(max_length=60)
document_content = models.TextField()
class Meta:
permissions = (
('view_document', 'View Document'),
)
views.py
#permission_required('document.view_document', (Document, 'pk', 'document_id'))
def view_document(request, document_id):
document = Document.objects.get(pk=document_id)
return render_to_response("native/view_events.html",
{
'document' : document,
}, context_instance=RequestContext(request))
The downside I see to solution number one is that I have to explicitly set permissions every time I create an object, plus I have to hit the database twice: once to check permissions and again to retrieve the document object.
Solution Two (using built-in Django permissions)
Models.py
class Document(models.Model)
owner = models.ForeignKey(User)
document_name = models.CharField(max_length=60)
document_content = models.TextField()
viewers = models.ManyToManyField(User)
class Meta:
permissions = (
('view_document', 'View Document'),
)
views.py
#permission_required('document.view_document')
def view_document(request, document_id):
document = Document.objects.filter(pk=document_id, viewers__pk=request.user.pk)[0]
return render_to_response("native/view_events.html",
{
'document' : document,
}, context_instance=RequestContext(request))
The downside I see to solution number one is that I have to do two checks, one to see if they are able to view documents at all, and one to see if they can view the particular document. A plus side to this is that if I have an admin that I want to be able to view all documents, I don't need to explicitly grant permission to each one; I can just give him the 'view document' permission.
It seems both solutions have their pros and cons. Is there one that is better in theory/practice?
I find the second approach better. Since, you can check the model level permission on the object. Though in the first I guess, you should be able to accomplish similar things. I am not sure but if django-guardian provides a way to check the permission inside the view code instead of the decorator. You could manually check for Model Level Permission.
For example,
def view_doc(request, doc_id):
if user can not view doc: #Model Level Permission
return HttpResponse("Sorry can not view")
if check django-guardian permission #Object Level Permission
return HttpResponse("Can not view doc")
#further code
But I would suggest the second approach since, you can create an api just to check permission and customize it which is more scalable.