Skip to content
Snippets Groups Projects
Commit 49db218c authored by Erwan Rouchet's avatar Erwan Rouchet
Browse files

Merge branch 'right-access-training-models' into 'master'

Right access training models

Closes #961

See merge request !1633
parents 6a4ff3f5 57dae3bb
No related branches found
No related tags found
1 merge request!1633Right access training models
......@@ -10,6 +10,7 @@ from rest_framework.serializers import CharField, Serializer
from arkindex.dataimport.models import DataImport, DataImportMode, Repository, Worker
from arkindex.documents.models import Corpus
from arkindex.project.pagination import CustomCursorPagination
from arkindex.training.models import Model
from arkindex.users.models import Role
from arkindex.users.utils import check_level_param, filter_rights, get_max_level
......@@ -157,6 +158,33 @@ class CorpusACLMixin(ACLMixin):
return self.has_access(corpus, Role.Admin.value)
class TrainingModelMixin(ACLMixin):
"""
Access control mixin for machine learning models
"""
@property
def readable_models(self):
return Model.objects.filter(
id__in=filter_rights(self.user, Model, Role.Guest.value).values('id')
)
@property
def editable_models(self):
return Model.objects.filter(
id__in=filter_rights(self.user, Model, Role.Contributor.value).values('id')
)
def has_read_access(self, model):
return self.has_access(model, Role.Guest.value)
def has_write_access(self, model):
return self.has_access(model, Role.Contributor.value)
def has_admin_access(self, model):
return self.has_access(model, Role.Admin.value)
class ProcessACLMixin(ACLMixin):
"""
Rights on processes are determined depending on its nature.
......
......@@ -5,8 +5,16 @@ from django.contrib.contenttypes.models import ContentType
from arkindex.dataimport.models import DataImport, DataImportMode, Repository, RepositoryType, Revision
from arkindex.documents.models import Corpus
from arkindex.project.mixins import ACLMixin, CorpusACLMixin, ProcessACLMixin, RepositoryACLMixin, WorkerACLMixin
from arkindex.project.mixins import (
ACLMixin,
CorpusACLMixin,
ProcessACLMixin,
RepositoryACLMixin,
TrainingModelMixin,
WorkerACLMixin,
)
from arkindex.project.tests import FixtureTestCase
from arkindex.training.models import Model
from arkindex.users.models import Group, Right, Role, User
from arkindex.users.utils import filter_rights, get_max_level
......@@ -16,27 +24,30 @@ class TestACLMixin(FixtureTestCase):
@classmethod
def setUpClass(cls):
r"""
Create user and groups with rights on a Corpus and a Repository
Create user and groups with rights on a Corpus, a Repository and a Model
We use a simple rights configuration for those tests
User1 User2 User3
/ | \ / | / |
| | 100 10 | 100 |
50 100 \ / 90 | 75
| | Group1 | Group2 |
| Worker / \ | /\ |
| | 80 75 | 100 50 |
\ | / \ | / \ |
Repo1 Corpus1 Corpus2
User1 User2 User3 User4 User5
/ | \ / | / | | /
| | 100 10 | 100 | | 100
50 100 \ / 90 | 75 100 |
| | Group1 | Group2 | | Group3
| Worker / \ | /\ | | / \
| | 80 75 | 100 50 | | 10 100
\ | / \ | / \ | | / \
Repo1 Corpus1 Corpus2 Model1 Model2
"""
super().setUpClass()
cls.user1 = User.objects.create_user('user1@test.test', display_name='User1')
cls.user2 = User.objects.create_user('user2@test.test', display_name='User2')
cls.user3 = User.objects.create_user('user3@test.test', display_name='User3')
cls.user4 = User.objects.create_user('user4@test.test', display_name='User4')
cls.user5 = User.objects.create_user('user5@test.test', display_name='User5')
cls.group1 = Group.objects.create(name='Group1')
cls.group2 = Group.objects.create(name='Group2')
cls.group3 = Group.objects.create(name='Group3')
cls.repo1 = Repository.objects.create(type=RepositoryType.Worker, url='http://repo1')
cls.worker = cls.repo1.workers.create(name='repo1 worker', slug='worker')
......@@ -44,6 +55,9 @@ class TestACLMixin(FixtureTestCase):
cls.corpus1 = Corpus.objects.create(name="Corpus1", id=uuid.UUID('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'))
cls.corpus2 = Corpus.objects.create(name="Corpus2", id=uuid.UUID('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'))
cls.model1 = Model.objects.create(name="Model1", id=uuid.UUID('cccccccc-cccc-cccc-cccc-cccccccccccc'))
cls.model2 = Model.objects.create(name="Model2", id=uuid.UUID('dddddddd-dddd-dddd-dddd-dddddddddddd'))
Right.objects.bulk_create([
Right(user=cls.user1, content_object=cls.group1, level=100),
Right(user=cls.user1, content_object=cls.repo1, level=50),
......@@ -52,10 +66,14 @@ class TestACLMixin(FixtureTestCase):
Right(user=cls.user2, content_object=cls.corpus1, level=90),
Right(user=cls.user3, content_object=cls.group2, level=100),
Right(user=cls.user3, content_object=cls.corpus2, level=75),
Right(user=cls.user4, content_object=cls.model1, level=100),
Right(user=cls.user5, content_object=cls.group3, level=100),
Right(group=cls.group1, content_object=cls.repo1, level=80),
Right(group=cls.group1, content_object=cls.corpus1, level=75),
Right(group=cls.group2, content_object=cls.corpus1, level=100),
Right(group=cls.group2, content_object=cls.corpus2, level=50),
Right(group=cls.group3, content_object=cls.model1, level=10),
Right(group=cls.group3, content_object=cls.model2, level=100),
])
cls.corpus_type = ContentType.objects.get_for_model(Corpus)
cls.repo_type = ContentType.objects.get_for_model(Repository)
......@@ -341,3 +359,52 @@ class TestACLMixin(FixtureTestCase):
readable_process_ids,
[process_1.id, process_2.id, process_3.id]
)
def test_models_access_user(self):
"""
User5 has guest access to Model1 via Group3
"""
with self.assertNumQueries(3):
read_access = TrainingModelMixin(user=self.user5).has_read_access(self.model1)
self.assertTrue(read_access)
def test_models_no_access_user(self):
"""
User3 has no guest access to Model1
"""
with self.assertNumQueries(3):
read_access = TrainingModelMixin(user=self.user3).has_read_access(self.model1)
self.assertFalse(read_access)
def test_models_contrib_access(self):
"""
User4 has contributor access to Model1
"""
with self.assertNumQueries(3):
write_access = TrainingModelMixin(user=self.user4).has_write_access(self.model1)
self.assertTrue(write_access)
def test_models_admin_access_group(self):
"""
User5 has admin access to Model2 via Group3's access rights
"""
with self.assertNumQueries(3):
admin_access = TrainingModelMixin(user=self.user5).has_admin_access(self.model2)
self.assertTrue(admin_access)
def test_models_readable(self):
"""
To view a model, a user needs guest access.
"""
with self.assertNumQueries(3):
readable_models = list(TrainingModelMixin(user=self.user4).readable_models)
self.assertListEqual(readable_models, [self.model1])
def test_models_editable(self):
"""
To edit a model, a user needs contributor access.
User5 only has that access on model2.
"""
with self.assertNumQueries(3):
editable_models = list(TrainingModelMixin(user=self.user5).editable_models)
self.assertListEqual(editable_models, [self.model2])
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from enumfields import Enum, EnumField
......@@ -19,6 +20,9 @@ class Model(IndexableModel):
# Link to the workers that are able to use this model
compatible_workers = models.ManyToManyField('dataimport.Worker', related_name='models')
# Memberships to handle access rights on models
memberships = GenericRelation('users.Right', 'content_id')
def __str__(self):
return self.name
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment