django: context info for permissions/rules - django

Folks, I run into a scenario which I believe is quite common but am a little lost over it.
In a company different categories of employees can have different
access rights. In django, this can be implemented using Groups and Permissions.
All employees have common permissions to access their company 's
portal, and also while everyone can insert comments inside the
portal, the number and type of comments one can make can differ
according to the category he is in.
Admin can change these numbers and also the permissions
(common/unique) each group have. I've looked at django-rules but I need to store this extra info somewhere.
Admin can add more categories and assign the same kind of permissions.
Currently, I use this:
class GroupPermExtra(models.Model):
group = models.ForeignKey(Group)
permission = models.ForeignKey(Permission)
context_info = models.TextField()
class Meta:
unique_together = (("group", "permission"),)
to store the extra info (in this case, the number of comments), which I thought may be stored as json.
However, I believe the above is not a good solution as there is already a many
to many attribute between Group and Permission but since I can't add
a intermediate through, what would be a better way to do this?

Related

Django Graphene/GraphQL best design for model field-level permissions

I am using Django as back-end with graphene-django serving the front-end. I am new to both django and graphene so I am not sure what is the best approach to achieve field-level permissions with no code repetition in this set-up. For example, if my model is:
class MyModel(models.Model):
field1 = models.CharField()
field2 = models.CharField()
I want to be able to specify that user1 can read field1 but not field2; when the user1 queries GraphQL for all MyModels it would be only allow to retrieve field1 from rows (nodes) and not field2.
I did a bit of research and found 2 possible alternative solutions but I fear they may not be in the spirit of Django framework and/or conflicting with each other and there may be a better way to achieve this in Django. Ultimately I don't want to be repeating the permissions code in multiple parts of the Django and Graphene back-end so want this to be centralised at the lowest possible level, ideally at the Django model. I need to be able to control full CRUD per field depending on user_id and maybe even have some additional logic. The options I found were:
overriding get_node resolver in graphene-django DjangoObjectType to check for permissions there. Personally I see this as a very bad and last-resort solution as the checks are done only on graphql query/mutation layer and not elsewhere in Django. I could easily write a Django form or view that would not benefit from the permission check unless this is coded again in that form/view.
I could extend the Django model to perform arbitrary per-field checks and this seems the right level where to enforce permission checks but ideally I would prefer to use built-in features or a 'popular' library for this type of stuff. I tried searching for a library but I couldn't find anything that is even remotely production ready or gaining any traction - which leads me to the consider that there may be a better approach to address this problem. Django-field-permissions package seemed on the right path though.
I was wondering if anyone has a view on the best approach to solving this problem that fits Django and Graphene frameworks and without repeating the permissions code everywhere?
You're not going to find a stable/popular package for this, as it's not a design that a database can support well.
You cannot link (Foreign Key) a field and a table, you can only link two tables using a field. Therefore, any work to determine whether a row in a table has access to a field in another table, will be costly and error prone.
The best you could do, is to write a service layer that sits in between a resolver and a model that nullifies fields a user has no access to. You pass it the user (info.context.user) and a model, and it does a separate query to a field permissions model, fetches the record and nullifies each field according to permissions.
This inherently means each field needs to be nullable in order to support the permissions, complicating the frontend - it's a lot of work...
A better approach if your business logic allows it, is to group those fields into models, that can then be tied to "roles" or groups. So if this were medical data in a hospital:
- name ----- \
- address | -> Person model => all personnel, except custodial
- birth date -/
- medication -------\
- patient history | => PatientStatus model => all medically trained personnel
- current physician /
- ...
That way you use standard built-in permissions to deny access to entire tables (as illustrated above) and object level permissions (Guardian, Authority) to deny access to all but the current physician for the really classified things.
Hope this helps and good luck!

Approaches for Database model with multiple junctions

I work with the python Django Framework and try to build a website. I want to achieve a good performance for the case described below and search for a good database/model design. Since this is a database-relating question in my eyes, I wrote about columns and tables. In Django I would replace them with the respective models and fields.
I have a model with a user and a profile table. user contains the primary user data like username and email. profile contains all other profile details like name, birthdate, favorite color and so on. profile has also some dependent tables like e.g. custom_fields, where users can add custom fields to their profile.
One user has exactly one profile that he owns. Every contact can see the users profile. I need to store the profile owner with the profile.
If a user (A) views the profile of another user (B) and misses some information, A can add this information into the profile of B. That changes are not visible for anyone but the creator A. That means I need to store the editor (A) and the target (B) together with the profile.
I thought about how to design this and found three approaches:
Copy the table profile (containing only the users own profile) into edited_profile (containing all edits), add a column editor for the user who does the edit and manage both tables. That means that I also have to copy all tables that depend on profile or need a layer of junction tables between profile and the dependent tables. Also, if I ever want to improve or extend the model, I will always have to watch to keep both tables consistent. This seems to be not the best solution in my eyes.
Add columns user and editor into profile. The users own profile is identified by editor is NULL. If I need a profile for something else (e.g. a group), I need to add respective columns to the profile table to refer to it. That may include a lot of changes in the SQL queries because I may need to filter for the new columns, too.
Add a column profile_id into profile and create some junction tables:
user_profile (fk_user_id -> user_id, fk_profile_id -> profile_id) and
edited_profile (fk_user_id -> user_id, fk_editor_id -> user_id, fk_profile_id -> profile_id).
If I need a profile for something else (e.g. a group), I need to add respective junction tables.
I want to have a good database design with performant look ups. Usually I need the users own profile alone or together with the edits that another user has applied. Which one of the approaches is the best regarding lookup/update performance and maintenance? Is there another better design that I have missed?
Thank you for your help!

How to restrict certain rows in a Django model to a department?

This looks like it should be easy but I just can't find it.
I'm creating an application where I want to give admin site access to people from different departments. Those people will read and write the same tables, BUT they must only access rows belonging to their department! I.e. they must not see any records produced by the other departments and should be able to modify only the records from their own department. If they create a record, it should automatically "belong" to the department of the user which created it (they will create records only from the admin site).
I've found django-guardian, but it looks like an overkill - I don't really want to have arbitrary per-record permissions.
Also, the number of records will potentially be large, so any kind of front-end permission checking on a per-record basis is not suitable - it must be done by DB-side filtering. Other than that, I'm not really particular how it will be done. E.g. I'm perfectly fine with mapping departments to auth groups.

Django auth_user - Best practice to exclude inactive users

Our app sets the is_active field in User Model to False to represent a deleted user.
What's the best practice for excluding the deleted users (where is_active=False) from each and every access to the user table?
Please consider the following:
1. The app is already written, so we'd appreciate as minimum code changes as possible.
2. The app uses: request.user, get_object_or_404() and of course User.objects, so the solution has to take all of them into account.
From the research I've done, I found:
1. Proxy model: will force me to make a lot of changes in the code; I don't know how it works with request and get_object_or_404().
2. contribute_to_class: Can it be used to override objects manager or to just to add a new one? Is it safe?
3. Middleware changes: I don't want to get into this. Too risky for me.
Is there an elegant way for doing this?
There's no way to do this, nor should you try. The only way to limit every action to a selection of model instances is to limit the default queryset, which then effectively orphans the excluded instances, providing no way to access them ever again. The Django docs explicitly warn against this behavior.
If you override the get_query_set() method and filter out any rows, Django will return incorrect results. Don't do that. A manager that filters results in get_query_set() is not appropriate for use as an automatic manager. (emphasis mine)
"Automatic" managers are basically the same as the default manager. It's what's used for related fields, in the Django admin, and in countless other areas of the Django machinery. If you limit the default manager, you limit everything across the board.
Now, there's other options for quickly accessing the limited queryset; they simply aren't "automatic", meaning you still must make a point of using them instead of just having everything happen by magic. However, "magic" violates one of the core Python tenants in this respect: explicit is better than implicit. Limiting the User queryset by default is an implicit action. Filtering the queryset manually, referencing a custom manager method, or using a subclass of User are all explicit actions, and preferable as a result.

Django Admin: Managing database

I am using Django admin for managing my data. I have the following tables: Users, Groups, and Domains. Users has a many-to-many relationship with both Groups and Domains. Domains has a one-to-many relationship with Groups. When I remove a User from a Domain, I also want to remove any entries in Users_Groups for that particular User and Groups belonging to the Domain.
How do I do this? Where do I put the code?
Thanks.
The Django book (specifically, Appendix B) seems to suggest you override the delete method on the User model class and have it trigger the extra deletes.
I would suggest overriding save, but I'm guessing you're using the django.contrib.auth.User object. In that case, you can accomplish about the same thing with a pre_save signal:
def manage_domains(signal,**kwargs):
if kwargs.has_key('instance'):
instance = kwargs['instance']
else: return
old_instance = User.objects.get(pk=instance.pk)
instance_categories = instance.categories.all()
for group in old_instance.groups.all():
if group not in instance_categories:
instance.groups.clear()
pre_save.connect(manage_domains, sender=User)
This is not even close to an efficient solution. What will happen is that when a User object is saved, the above changes will be made to the object in memory, then the save will overwrite the object in the database. You've gone to the trouble not only of hitting the database to find out if the unmodified version of the object agrees with with what you're about to save, but you're also looping through two category sets, both requiring a database hit to load.
The best way to make an efficiency improvement here would be to subclass the ManyToMany field manager itself, because that's what's paying attention to changes as they occur in memory, rather than merely comparing state after the fact. But this would be a bit more involved and would also require you to abandon the built-in User object.