how to create django groups and handle permission dynamically - django

I am using django-userena application to handle user-registration, user-profile and log-in. Now I'm writing an application where -
A user can create a course(course is like a profile where we store information about course).
Other users can register for this course. Registration requires course creator's approval.
Only course creator can edit the course page and he can create an assignment for the course.
All users who registered for the course can see course page and assignment of the course(Read-only).
A course creator can provide permission to other users to edit all assignments of a course.
One way out is -- Create two groups creator and modifier. Now creator group can edit course page and modifier's member can modify assignments of that particular course. Problem with this solution is once we add a user to creator group it automatically gets permission to edit all courses. Similarly a member of modifier group can edit all assignments of all courses. This is not required.
How should I architect this application?

An alternative is django-authority, which can check permissions dynamically by methods rather than persisting them in DB.
The difference Django-guardian is, in your example, when an user moves to another course you will have to deassign the permission on the old group and assign the new one explicitly.
With authority, a function can_edit_course(user, course) (pseudocode) and the access decided in runtime.

You can either have two different groups attached to each course and create them when the course is created:
class Course(models.Model):
creators = models.ForeignKey(Group)
modifiers = models.ForeignKey(Group)
So this way you can set permssion to this group. And you can use django-guardian to assign and check for this permission.
Something like this:
assign_perm('edit_course', group, course)
and then check user if he can edit this course ( it will check group permission from user group automatically )
user.has_perm("edit_course",course)

What you basically want is row-level permission.
You might get some idea from row level permissions in django
Other way is you can keep two fields on your model Course..
class Course(models.Model):
creator = models.ForeignKey(User)
modifiers = models.ManyToManyField(User)
So, check if the user is the creator and then only allow to edit the course.
Check is user is in course.modifiers.all() and then allow him to modify.
Creator will have access to another page where is can add the modifiers for the course.

Related

The right way to set up permissions for "workspaces" in Django?

I am trying to create a site where users work together in "workspaces". A workspace is like an account for a group of users.
Each workspace will have a number of users. There are two user groups: "superusers" and "regular users".
A user may be a member of more than one workspace. The user can never belong to both user groups in the same workspace, but the user can be a "superuser" in one workspace and a "regular user" in another.
I am trying to find out whether I can create this type of setup using the out-of-the-box permissions framework in Django, or whether the best solution is to create my own supporting tables?
My own solution would be a data model like this:
#(assume default User model)
class Workspace(models.Model):
workspace_name = models.CharField(max_length=100)
class WorkspaceUser(models.Model):
user = models.ForeignKey(User)
workspace = models.ForeignKey(Workspace)
usertype = models.CharField(max_length=100) #for simplicity, would only have two options
Is this a sensible way to do it, or can I utilize the out-of-the-box framework to achieve the same instead?
No, the built-in permissions framework is just for the model level - ie "user can modify workspaces generally" rather than "user can modify this specific workspace".
Your solution looks sensible. One addition would be to add an explicit many-to-many declaration using the WorkspaceUser as the through table:
class Workspace(models.Model):
workspace_name = models.CharField(max_length=100)
users = models.ManyToManyField(User, through='WorkspaceUser')
This doesn't change the table structure but gives you the ability to do my_user.workspace_set.all() to get all workspaces for a user or my_workspace.users.all() to get all users for a workspace.

Rolling out own permission app or modifying django permission

I am working on a project which needs a separate admin interface. The django admin interface is for the super super user, there will be companies who will sign up for our app and then they will have their own admin interface. Everything is set and done despite the permission. We want model level permission that's what Django provides.
What I did is:
class CompanyGroup(models.Model):
name = models.CharField(max_length=254)
permissions = models.ManyToManyField(Permissions)
Now this list all the permissions of the site itself. So, Should I start working on my own permission app or I can modify django Permissions to provide object level permissions for only some models.
Thanks
Try one of the several existing 'row level' / 'per object' permissions apps for Django:
http://django-guardian.readthedocs.org/en/v1.2/
http://django-object-permissions.readthedocs.org/en/latest/
...there are probably others, those are just the first two in Google
We are using django-guardian in current project at work, seems fine.
I am assuming that you need to control access to sub-sets of each model's objects, depending on which company the current user belongs to (i.e. you don't want people from Company A to see items from Company B). For this reason you need row level permissions.
You probably also need to limit the permissions of all 'company users' to only certain actions:
You do not need to create a CompanyGroup class.
Instead just enable the admin site, log in as super user and create a django.contrib.auth.models.Group which contains the global permissions applicable to company users.
then ensure when you create any new company user logins that they are added to that Group

Django user groups only for permissions?

I'm a bit unsure what to use Django user groups for.
I have an application where every user belongs to a different organisation. The organisations don't have anything to do with read/write permissions. It's just a way to separate groups of users. Every organisation needs some additional fields, like a name, URL, and email address. New organisations will be added to the system over time.
Within every organisation, users can have different permissions for moderation and administration, for which I (also) want to use user groups.
My question: Should I use Django's user groups to define the organisations, or should I just make an 'Organisation' model with a relation to the user?
Nope. User groups are made for different reasons. You CAN use them to define organisations but I think you should think bit further ahead:
will the organisation require more fields than just name?
perhaps you will need permissions in the future to define users roles within organisations?
I'm sure you can come up with more things to think of. But if you answered yes to one of those questions then just create your Organisation model.
1) You need to add group from django admin side under group table.
2) And while creating new user, assign specific group to user using user_obj.groups.add(group_id). Or Let user select group at frontend.
and then user_obj.save()
in Group table, you can create organization
OR
You can create individual organization table and assign assign user to specific organization.

Django Auth, Users and Permissions

I have a Organization and Employee models
class Organization(models.Model):
is_active = models.BooleanField()
name = models.CharField(u'Name', max_length = 255)
...
class Employee(models.Model):
user = models.OneToOneField(User)
organization = models.ForeignKey(Organization)
...
Will it be good if I use AUTH_PROFILE_MODULE, so Employee becomes user profile?
This way I can use Django permission system to set employees permissions, like
can see all documents
can see own organization documents
can see his own documents
Is is OK to have a permissions that are not a global one like "can see all documents"?
And what If I also want to have a permissions per Organization? How to do this? And how to distinguish permissions per Organization and per Employee?
Edit: I'm using Django 1.4
In short, yes, you're ok.
Because:
1) Using AUTH_PROFILE_MODULE=Employee will make Employee instance to be available for instance in this way:
def view(request):
employee_instance = request.user.get_profile()
2) Using custom permissions is easy, see: https://docs.djangoproject.com/en/dev/topics/auth/#custom-permissions
Edit:
having custom permissions on organizations is possible as well, probably best if you create permissions programatically, like mentioned in the manual, this way:
content_type = ContentType.objects.get(app_label='myapp', model='Organization')
permission = Permission.objects.create(codename='can_do_something', name='Can Do something',
content_type=content_type)
now, you have permission aware organization model, you just assign it to your user.
To clarify more:
Django auth system is sort of a fixed ACL. You assign roles to a user (or group) and that's pretty much it. Django offers helper wrapper function to easily filter out users who don't have a given permission. If you need to decide at runtime and/or in more generic way, whether an object has permission to do something, you either need full blown ACL system (and which django.auth is not) or you code that kind of behavior yourself. This depends on your needs and obviously on the need to manage those permissions. In the OP's case, the behavior is fixed, therefore I would recommend just coding this in and be happy. But the needs may vary and so does the solution. Django auth is good at assigning static permissions to user, gropu or a "profile" object. What that means to your app is up to you in the end.
So in this case, the good solution would be to have a fixed set of permissions like "can view own documents" or "can view organization documents" that is assigned to user/group. And you app should decide, what it means and serve documents accordingly, taking either runtime state in the account or using models structure to determine the proper data set to serve.

Django role based views?

I'm looking for some input on how others would architect this. I'm going to provide class (django group) based views.
For example, a user's group will determine what views/templates he or she will have access to. I'm thinking of perhaps storing paths to view functions in a table to determine what a user's link bar will consist of. Filter specifications can also be stored to determine what rows will fill these templates.
A good example is a hospital nursing units. Nurses at one unit need not see the entire hospital's patients. They only need to see their patients. Doctors on the same unit need only to see those patients as well, but they should have access to much greater functionality.
Has this been done via some third party application? And how would you approach this problem?
Thanks,
Pete
Django already has a groups and permissions system, which may be sufficient for your purpose.
http://docs.djangoproject.com/en/dev/topics/auth/
Generally in your code you check if a user has a permission. A user has his own permissions and those of the groups he belongs to. You can administer this pretty easily from the admin console.
There are two parts you need to look at.
Check that a user requesting a page
has permission to do so.
Only display links to the user if he
has the permission.
For 1. you can check permissions in a decorator as such:
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote')
def some_view(request):
For 2. the currently logged-in user's permissions are stored in the template variable {{ perms }}. This code checks the same permission as above.
{% if perms.polls.can_vote %}
vote
{% endif %}
To generate a list of links you can iterate over user.get_all_permissions() and fetch the links (or function that generates the link) from a dict:
def more_elaborate_list_of_links_for_a_perm(user):
return ["/link1", ...]
_LINKS = {
'polls.can_vote' : lambda u: ["/user/specific/link/" + u.id],
'polls.can_close': lambda u: ['/static/link/1', 'static/link/2'],
'polls.can_open' : more_elaborate_list_of_links_for_a_perm
}
def gen_links(user):
# get_all_permissions also gets permissions for users groups
perms = user.get_all_permissions()
return sum((_LINKS[p](user) for p in perms if p in _LINKS), [])
There are probably many other approaches.
We had a similar problem. Django's groups aren't REALLY suited to this, but you can shoehorn them in.
The way we did it was as follows:
Every access-controlled object has a ManyToMany relation to the groups table. Each group was used to define a specific type of permission ("can view patient basics", "can edit patient contact info", and so on). Users are added to the groups that they should have permissions for (in your example of seeing only patients in this hospital, you could have a "valley-view-hospital" group).
Then when you go to display a list of records to a user, you filter based on the conjunction of the two groups. A user has to have all the associated group permissions to view a given object.
If your system requires it, you can keep a separate ManyToMany of negative permissions, or separate read/write permissions. You could also define a set of meta-groups (doctor, nurse) that result in your lookup filter retrieving the actual subset of permissions.
As far as your link-bar problem goes, you can generate those programatically using the same system - filter based on the classes of objects the user can see or edit, and then use a get_absolute_url() type function (maybe call it get_index_url()) to return the links for the index of each class of object.
Because all this is fairly complex, you'll probably end up wanting to do some level of caching for these things, but get it working before you bother optimizing. It is possible, and it's less ugly in code than it is in words.
There is a new very interesting project about role based permissions in Django:
http://bitbucket.org/nabucosound/django-rbac
I had a similar problem not too long ago. Our solution did the trick, though it might be too simple for your situation. Like everyone is suggesting, we used the django permission system to control what user interactions with models. However, we didn't just try to group users, we also grouped objects through a GenericForeignKey.
We built a model that linked to itself to allow for hierarchies to be developed.
class Group( models.Model ):
name = models.CharField( ... )
parent = models.ForeignKey( 'self', blank=True, null=True)
content_type = models.ForeignKey( ContentType )
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey( 'content_type', 'object_id' )
...
To make it work, we also created a model to serve as the django User model's user profile. All it contained was a ManyToManyField linked to the Group model above. This allowed us to give users access to zero or more Groups as required. (documentation)
class UserProfile( models.Model ):
user = models.ForeignKey( User, unique=True )
groups = models.ManyToManyField( Group )
...
This gave us the best of both worlds and kept us from trying to shoehorn everything into django's permission system. I'm using this basic setup to control user's access to sports content (some users can access whole leagues, some only one or two conferences, some only have access to individual teams), and it works well in that situation. It could probably be a generalized enough to fit your needs.
If you don't need real per-object ACLs, then you can just use the Django permission system. To get a list of all available permissions:
from django.contrib.auth.models import Permission
perms = Permission.objects.all()
There is an API for other authentication and authorization sources, so you do not need to stick with this permissions table.
You may hack this Django system to fit your needs in terms of this authorization model (RBAC) or you may come up with an ACL-like solution.
On a site for an expert on Pinot Noir wine we created per-object access based on a number of different criteria. If the inbound link had a referer field that matched the domain name of a featured winery, then the user got a 'winery token' which expanded to all articles, tasting notes, etc. related to that winery. We use 'named tokens' for give aways at tasting events and they gave access to specific parts of the site. We even use this to grant certain types of permissions to search engine spiders and then make sure that links that come from those search engines have the same permissions as the spider did (ie. no cloaking games).
The short version is that you can create a class (we called them TokenBuckets which hold Tokens) and each object (on a detail page, or a list page, or whatever) can ask the user's TokenBucket if a certain level of access is allowed.
Basically it's a weird kind of ACL system. It wasn't that hard to create the mechanics. All of the magic is in determining under what circumstances which tokens go into the bucket.
You can use django user roles
https://github.com/dabapps/django-user-roles
This question has been asked in Oct 2009 and the problem still exists in July 2012.
I have searched for a good Role-Based app, and found django-permission as the best result.
Three important features that I needed were Roles, view Decorators and Templatetag; apparently django-permissions has all of them. Read it's docs for it's usage.
The only drawback is that it's under development.
We used a role base system for a similar problem.
Basically users have permissions to assume different roles.
View functions got decorated:
def needs_capability(capability,redirect_to="/cms/"):
def view_func_wrapper(view_func):
def wrapped_view_func(request,*args,**kwargs):
if not request.role._can(capability):
return HttpResponseRedirect(redirect_to)
return view_func(request,*args,**kwargs)
return wrapped_view_func
return view_func_wrapper
The rest of the magic is inside the request.role attribute which got
set inside a context processor. Authenticated users got a Role, for the unwashed masses
a DummyRole.
Access to information was restricted further inside the templates:
{% if not request.role.can.view_all_products %}
Lots of products, yeah!
{% endif %}
Not the cleanest solution in my opinion, but worked as expected.