Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permissions per tasks and jobs #185

Merged
merged 10 commits into from
Nov 7, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Polyshape editing method has been improved. You can redraw part of shape instead of points cloning.
- Unified shortcut (Esc) for close any mode instead of different shortcuts (Alt+N, Alt+G, Alt+M etc.).
- Dump file contains information about data source (e.g. video name, archive name, ...)
- Update requests library due to https://nvd.nist.gov/vuln/detail/CVE-2018-18074
- Per task/job permissions to create/access/change/delete tasks and annotations

### Fixed
- Performance bottleneck has been fixed during you create new objects (draw, copy, merge etc).
- Label UI elements aren't updated after changelabel.
- Attribute annotation mode can use invalid shape position after resize or move shapes.


## [0.2.0] - 2018-09-28
### Added
- New annotation shapes: polygons, polylines, points
Expand Down
10 changes: 10 additions & 0 deletions cvat/apps/authentication/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@

default_app_config = 'cvat.apps.authentication.apps.AuthenticationConfig'

from enum import Enum

class AUTH_ROLE(Enum):
ADMIN = 'admin'
USER = 'user'
ANNOTATOR = 'annotator'
OBSERVER = 'observer'

def __str__(self):
return self.value
20 changes: 19 additions & 1 deletion cvat/apps/authentication/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@
# SPDX-License-Identifier: MIT

from django.contrib import admin
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.utils.translation import ugettext_lazy as _

# Register your models here.
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups',)}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)

class CustomGroupAdmin(GroupAdmin):
fieldsets = ((None, {'fields': ('name',)}),)


admin.site.unregister(User)
admin.site.unregister(Group)
admin.site.register(User, CustomUserAdmin)
admin.site.register(Group, CustomGroupAdmin)
13 changes: 2 additions & 11 deletions cvat/apps/authentication/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,11 @@
# SPDX-License-Identifier: MIT

from django.apps import AppConfig
from django.db.models.signals import post_migrate, post_save
from .settings.authentication import DJANGO_AUTH_TYPE

class AuthenticationConfig(AppConfig):
name = 'cvat.apps.authentication'

def ready(self):
from . import signals
from django.contrib.auth.models import User
from .auth import register_signals

post_migrate.connect(signals.create_groups)

if DJANGO_AUTH_TYPE == 'SIMPLE':
post_save.connect(signals.create_user, sender=User, dispatch_uid="create_user")

import django_auth_ldap.backend
django_auth_ldap.backend.populate_user.connect(signals.update_ldap_groups)
register_signals()
80 changes: 80 additions & 0 deletions cvat/apps/authentication/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (C) 2018 Intel Corporation
#
# SPDX-License-Identifier: MIT

import os
from django.conf import settings
import rules
from . import AUTH_ROLE

def register_signals():
from django.db.models.signals import post_migrate, post_save
from django.contrib.auth.models import User, Group

def create_groups(sender, **kwargs):
for role in AUTH_ROLE:
db_group, _ = Group.objects.get_or_create(name=role)
db_group.save()

post_migrate.connect(create_groups, weak=False)

if settings.DJANGO_AUTH_TYPE == 'BASIC':
from .auth_basic import create_user

post_save.connect(create_user, sender=User)
elif settings.DJANGO_AUTH_TYPE == 'LDAP':
import django_auth_ldap.backend
from .auth_ldap import create_user

django_auth_ldap.backend.populate_user.connect(create_user)

# AUTH PREDICATES
has_admin_role = rules.is_group_member(str(AUTH_ROLE.ADMIN))
has_user_role = rules.is_group_member(str(AUTH_ROLE.USER))
has_annotator_role = rules.is_group_member(str(AUTH_ROLE.ANNOTATOR))
has_observer_role = rules.is_group_member(str(AUTH_ROLE.OBSERVER))

@rules.predicate
def is_task_owner(db_user, db_task):
# If owner is None (null) the task can be accessed/changed/deleted
# only by admin. At the moment each task has an owner.
return db_task.owner == db_user

@rules.predicate
def is_task_assignee(db_user, db_task):
return db_task.assignee == db_user

@rules.predicate
def is_task_annotator(db_user, db_task):
from functools import reduce

db_segments = list(db_task.segment_set.prefetch_related('job_set__assignee').all())
return any([is_job_annotator(db_user, db_job)
for db_segment in db_segments for db_job in db_segment.job_set.all()])

@rules.predicate
def is_job_owner(db_user, db_job):
return is_task_owner(db_user, db_job.segment.task)

@rules.predicate
def is_job_annotator(db_user, db_job):
db_task = db_job.segment.task
# A job can be annotated by any user if the task's assignee is None.
has_rights = db_task.assignee is None or is_task_assignee(db_user, db_task)
if db_job.assignee is not None:
has_rights |= (db_user == db_job.assignee)

return has_rights

# AUTH PERMISSIONS RULES
rules.add_perm('engine.task.create', has_admin_role | has_user_role)
rules.add_perm('engine.task.access', has_admin_role | has_observer_role |
is_task_owner | is_task_annotator)
rules.add_perm('engine.task.change', has_admin_role | is_task_owner |
is_task_assignee)
rules.add_perm('engine.task.delete', has_admin_role | is_task_owner)

rules.add_perm('engine.job.access', has_admin_role | has_observer_role |
is_job_owner | is_job_annotator)
rules.add_perm('engine.job.change', has_admin_role | is_job_owner |
is_job_annotator)
12 changes: 12 additions & 0 deletions cvat/apps/authentication/auth_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (C) 2018 Intel Corporation
#
# SPDX-License-Identifier: MIT
from . import AUTH_ROLE
from django.conf import settings

def create_user(sender, instance, created, **kwargs):
from django.contrib.auth.models import Group

if instance.is_superuser and instance.is_staff:
db_group = Group.objects.get(name=AUTH_ROLE.ADMIN)
instance.groups.add(db_group)
29 changes: 29 additions & 0 deletions cvat/apps/authentication/auth_ldap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

# Copyright (C) 2018 Intel Corporation
#
# SPDX-License-Identifier: MIT

from django.conf import settings
from . import AUTH_ROLE

AUTH_LDAP_GROUPS = {
AUTH_ROLE.ADMIN: settings.AUTH_LDAP_ADMIN_GROUPS,
AUTH_ROLE.ANNOTATOR: settings.AUTH_LDAP_ANNOTATOR_GROUPS,
AUTH_ROLE.USER: settings.AUTH_LDAP_USER_GROUPS,
AUTH_ROLE.OBSERVER: settings.AUTH_LDAP_OBSERVER_GROUPS
}

def create_user(sender, user=None, ldap_user=None, **kwargs):
from django.contrib.auth.models import Group
user_groups = []
for role in AUTH_ROLE:
db_group = Group.objects.get(name=role)

for ldap_group in AUTH_LDAP_GROUPS[role]:
if ldap_group.lower() in ldap_user.group_dns:
user_groups.append(db_group)
if role == AUTH_ROLE.ADMIN:
user.is_staff = user.is_superuser = True

user.groups.set(user_groups)
user.save()
8 changes: 4 additions & 4 deletions cvat/apps/authentication/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
#
# SPDX-License-Identifier: MIT

from functools import wraps
from urllib.parse import urlparse
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.shortcuts import resolve_url, reverse
from django.http import JsonResponse
from urllib.parse import urlparse
from django.contrib.auth.views import redirect_to_login

from functools import wraps
from django.conf import settings

def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None, redirect_methods=['GET']):
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME,
login_url=None, redirect_methods=['GET']):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
Expand Down
5 changes: 0 additions & 5 deletions cvat/apps/authentication/settings/__init__.py

This file was deleted.

56 changes: 0 additions & 56 deletions cvat/apps/authentication/settings/auth_ldap.py

This file was deleted.

8 changes: 0 additions & 8 deletions cvat/apps/authentication/settings/auth_simple.py

This file was deleted.

58 changes: 0 additions & 58 deletions cvat/apps/authentication/settings/authentication.py

This file was deleted.

Loading