Skip to content

Commit

Permalink
Role and RoleMapping Models
Browse files Browse the repository at this point in the history
  • Loading branch information
rbinrais authored and margaretmeehan committed Sep 3, 2019
1 parent 8d66e69 commit c1082e1
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 39 deletions.
41 changes: 41 additions & 0 deletions app/api/migrations/0004_roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 2.1.7 on 2019-07-25 03:33

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('api', '0003_support_sql_server'),
]

operations = [
migrations.CreateModel(
name='Role',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('description', models.TextField(default='', null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
name='RoleMapping',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_mapping', to='api.Project')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Role')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_mapping', to=settings.AUTH_USER_MODEL)),
],
),
migrations.AlterUniqueTogether(
name='rolemapping',
unique_together={('user', 'project', 'role')},
),
]
48 changes: 48 additions & 0 deletions app/api/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import string

from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save, pre_delete
from django.urls import reverse
from django.contrib.auth.models import User
from django.contrib.staticfiles.storage import staticfiles_storage
Expand Down Expand Up @@ -237,3 +239,49 @@ class Seq2seqAnnotation(Annotation):

class Meta:
unique_together = ('document', 'user', 'text')


class Role(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(default='')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)


class RoleMapping(models.Model):
user = models.ForeignKey(User, related_name='role_mapping', on_delete=models.CASCADE)
project = models.ForeignKey(Project, related_name='role_mapping', on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def clean(self):
other_rolemappings = self.project.role_mappings.exclude(id=self.id)

if other_rolemappings.filter(user=self.user, project=self.project).exists():
raise ValidationError('This user is already assigned to a role in this project.')

class Meta:
unique_together = ("user", "project", "role")


@receiver(post_save, sender=RoleMapping)
def add_linked_project(sender, instance, created, **kwargs):
userInstance = instance.user
projectInstance = instance.project
if created and userInstance and projectInstance:
user = User.objects.get(pk=userInstance.pk)
project = Project.objects.get(pk=projectInstance.pk)
user.projects.add(project)
user.save()


@receiver(pre_delete, sender=RoleMapping)
def delete_linked_project(sender, instance, using, **kwargs):
userInstance = instance.user
projectInstance = instance.project
if userInstance and projectInstance:
user = User.objects.get(pk=userInstance.pk)
project = Project.objects.get(pk=projectInstance.pk)
user.projects.remove(project)
user.save()
69 changes: 61 additions & 8 deletions app/api/permissions.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from django.conf import settings
from django.contrib.auth.mixins import UserPassesTestMixin
from django.db.models import Subquery
from django.shortcuts import get_object_or_404
from rest_framework.permissions import BasePermission, SAFE_METHODS, IsAdminUser

from .models import Project
from .models import Project, Role, RoleMapping


class IsProjectUser(BasePermission):
class ProjectMixin:
def get_project_id(self, request, view):
return view.kwargs.get('project_id') or request.query_params.get('project_id')

def has_permission(self, request, view):
user = request.user
project_id = view.kwargs.get('project_id') or request.query_params.get('project_id')
project = get_object_or_404(Project, pk=project_id)

class IsProjectUser(ProjectMixin, BasePermission):

def has_permission(self, request, view):
project = get_object_or_404(Project, pk=self.get_project_id(request, view))
return user in project.users.all()


Expand All @@ -30,13 +34,62 @@ def test_func(self):
return self.request.user.is_superuser


class IsOwnAnnotation(BasePermission):
class IsOwnAnnotation(ProjectMixin, BasePermission):

def has_permission(self, request, view):
project_id = view.kwargs.get('project_id')
project_id = self.get_project_id(request, view)
annotation_id = view.kwargs.get('annotation_id')
project = get_object_or_404(Project, pk=project_id)
model = project.get_annotation_class()
annotation = model.objects.filter(id=annotation_id, user=request.user)

return annotation.exists()


class RolePermission(ProjectMixin, BasePermission):
UNSAFE_METHODS = ('POST', 'PATCH', 'DELETE')
unsafe_methods_check = True
role_name = ''

def is_super_user(self, user):
return user.is_superuser

def has_permission(self, request, view):
is_super_user = self.is_super_user(request.user)
if is_super_user:
return True

if self.unsafe_methods_check and request.method in self.UNSAFE_METHODS:
return is_super_user

project_id = self.get_project_id(request, view)
if not project_id and request.method in SAFE_METHODS:
return True

return is_in_role(self.role_name, request.user.id, project_id)


class IsProjectAdmin(RolePermission):
unsafe_methods_check = False
role_name = settings.ROLE_PROJECT_ADMIN


class IsAnnotatorAndCreator(RolePermission):
unsafe_methods_check = False
role_name = settings.ROLE_ANNOTATOR


class IsAnnotator(RolePermission):
role_name = settings.ROLE_ANNOTATOR


class IsAnnotationApprover(RolePermission):
role_name = settings.ROLE_ANNOTATION_APPROVER


def is_in_role(role_name, user_id, project_id):
return RoleMapping.objects.filter(
user_id=user_id,
project_id=project_id,
role_id=Subquery(Role.objects.filter(name=role_name).values('id')),
).exists()

0 comments on commit c1082e1

Please sign in to comment.