-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add user roles for organizations/gangs/sections (#1257)
* Create user roles for organizations/gangs/sections * Fix user role admin names * Type annotations * Ruff format * Remove content_type from Role * Add remaining resolvers for recruitment, and org/gang/section * Add working examples for Interview and InterviewRoom views * ruff method order * mypy * Remove roles from user model * Move to samfundet app * ruff * get_perm: support permissions without period * Fix hierarchy permission checking * Add some tests * Remove RoleMixin to avoid unnecessary method calls and exception raising/catching This means when the obj hasattr check is True, we can assume the method is implemented. * Begin docs * Update migration * Add code examples to docs * Add link to docs * Fix import merge * Update migrations * Create fixture_organization2, update fixture_gang2 to use it This lets us more easily test different organizational hierarchies * Add fixture_gang_section2 * Update tests, and add hierarchy testing * Revert changes in views * ruff * Remove RoleAuthBackend fixture for more readable code * Add note about return_id to docs * [skip ci] Add real-world example to docs * Update example to a more "normal" model * Update migration
- Loading branch information
Showing
16 changed files
with
773 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
from django.contrib.auth.backends import BaseBackend | ||
|
||
from samfundet.utils import get_perm | ||
from samfundet.models import User | ||
from samfundet.models.role import UserOrgRole, UserGangRole, UserGangSectionRole | ||
|
||
|
||
class RoleAuthBackend(BaseBackend): | ||
def has_perm(self, user_obj: User, perm: str, obj: Any = None) -> bool: # noqa: C901 | ||
if not user_obj.is_active or obj is None: | ||
return False | ||
|
||
if user_obj.is_superuser: | ||
return True | ||
|
||
permission = get_perm(perm=perm, model=obj) | ||
|
||
if hasattr(obj, 'resolve_org'): | ||
org_id = obj.resolve_org(return_id=True) | ||
if org_id is not None and UserOrgRole.objects.filter(user=user_obj, obj__id=org_id, role__permissions=permission).exists(): | ||
return True | ||
|
||
if hasattr(obj, 'resolve_gang'): | ||
gang_id = obj.resolve_gang(return_id=True) | ||
if gang_id is not None and UserGangRole.objects.filter(user=user_obj, obj__id=gang_id, role__permissions=permission).exists(): | ||
return True | ||
|
||
if hasattr(obj, 'resolve_section'): | ||
section_id = obj.resolve_section(return_id=True) | ||
if section_id is not None and UserGangSectionRole.objects.filter(user=user_obj, obj__id=section_id, role__permissions=permission).exists(): | ||
return True | ||
|
||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
backend/samfundet/migrations/0002_role_usergangrole_usergangsectionrole_userorgrole.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Generated by Django 5.0.7 on 2024-09-17 18:27 | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('auth', '0012_alter_user_first_name_max_length'), | ||
('samfundet', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Role', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('name', models.CharField(max_length=255)), | ||
('permissions', models.ManyToManyField(to='auth.permission')), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='UserGangRole', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('version', models.PositiveIntegerField(blank=True, default=0, editable=False, null=True)), | ||
('created_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('obj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.gang')), | ||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.role')), | ||
('updated_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='UserGangSectionRole', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('version', models.PositiveIntegerField(blank=True, default=0, editable=False, null=True)), | ||
('created_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('obj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.gangsection')), | ||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.role')), | ||
('updated_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='UserOrgRole', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('version', models.PositiveIntegerField(blank=True, default=0, editable=False, null=True)), | ||
('created_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), | ||
('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('obj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.organization')), | ||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='samfundet.role')), | ||
('updated_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.