Added django-guardian, set owner permissions on post save, testing, CSS fixes

pull/26/head
Piero Toffanin 2016-10-04 16:36:08 -04:00
rodzic d686e63c40
commit 90cc52ff2d
16 zmienionych plików z 112 dodań i 15 usunięć

Wyświetl plik

@ -1 +1,8 @@
from django.contrib import admin from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from .models import Project
class ProjectAdmin(GuardedModelAdmin):
pass
admin.site.register(Project, ProjectAdmin)

Wyświetl plik

@ -11,7 +11,7 @@ class ProjectSerializer(serializers.HyperlinkedModelSerializer):
class ProjectViewSet(viewsets.ModelViewSet): class ProjectViewSet(viewsets.ModelViewSet):
""" """
Projects the current user has access to, including the ability to create new ones. Projects the current user has access to.
""" """
queryset = models.Project.objects.all() queryset = models.Project.objects.all()
serializer_class = ProjectSerializer serializer_class = ProjectSerializer

Wyświetl plik

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import AppConfig from django.apps import AppConfig
import .signals
class MainConfig(AppConfig): class MainConfig(AppConfig):
name = 'main' name = 'main'

Wyświetl plik

@ -20,7 +20,7 @@
# password: test1234 # password: test1234
password: pbkdf2_sha256$30000$pFwIz88hEEZ4$siNaZefZB3bb0DlemWNOkj6+tCq3I3LW+IgVw5VsRmE= password: pbkdf2_sha256$30000$pFwIz88hEEZ4$siNaZefZB3bb0DlemWNOkj6+tCq3I3LW+IgVw5VsRmE=
last_login: null last_login: null
is_superuser: true is_superuser: false
username: testuser2 username: testuser2
first_name: '' first_name: ''
last_name: '' last_name: ''
@ -29,4 +29,20 @@
is_active: true is_active: true
date_joined: '2016-09-11T22:26:52.582858+00:00' date_joined: '2016-09-11T22:26:52.582858+00:00'
groups: [] groups: []
user_permissions: []
- model: auth.user
pk: 3
fields:
# password: test1234
password: pbkdf2_sha256$30000$pFwIz88hEEZ4$siNaZefZB3bb0DlemWNOkj6+tCq3I3LW+IgVw5VsRmE=
last_login: null
is_superuser: false
username: testuser3
first_name: ''
last_name: ''
email: test2@mail.com
is_staff: true
is_active: true
date_joined: '2016-09-11T22:26:52.582858+00:00'
groups: []
user_permissions: [] user_permissions: []

Wyświetl plik

@ -1,10 +1,15 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models from django.db import models
from django.db.models import signals
from django.utils import timezone from django.utils import timezone
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.postgres import fields from django.contrib.postgres import fields
from nodeodm.models import ProcessingNode from nodeodm.models import ProcessingNode
from django.dispatch import receiver
from guardian.shortcuts import get_perms_for_model, assign_perm
from guardian.models import UserObjectPermissionBase
from guardian.models import GroupObjectPermissionBase
def assets_directory_path(taskId, projectId, filename): def assets_directory_path(taskId, projectId, filename):
# files will be uploaded to MEDIA_ROOT/project_<id>/task_<id>/<filename> # files will be uploaded to MEDIA_ROOT/project_<id>/task_<id>/<filename>
@ -14,12 +19,37 @@ def assets_directory_path(taskId, projectId, filename):
class Project(models.Model): class Project(models.Model):
owner = models.ForeignKey(User, on_delete=models.PROTECT, help_text="The person who created the project") owner = models.ForeignKey(User, on_delete=models.PROTECT, help_text="The person who created the project")
name = models.CharField(max_length=255, help_text="A label used to describe the project") name = models.CharField(max_length=255, help_text="A label used to describe the project")
description = models.TextField(null=True, help_text="More in-depth description of the project") description = models.TextField(null=True, blank=True, help_text="More in-depth description of the project")
created_at = models.DateTimeField(default=timezone.now, help_text="Creation date") created_at = models.DateTimeField(default=timezone.now, help_text="Creation date")
def __str__(self): def __str__(self):
return self.name return self.name
class Meta:
permissions = (
('view_project', 'Can view project'),
)
@receiver(signals.post_save, sender=Project, dispatch_uid="project_post_save")
def project_post_save(sender, instance, created, **kwargs):
"""
Automatically assigns all permissions to the owner. If the owner changes
it's up to the user/developer to remove the previous owner's permissions.
"""
for perm in get_perms_for_model(sender).all():
assign_perm(perm.codename, instance.owner, instance)
class ProjectUserObjectPermission(UserObjectPermissionBase):
content_object = models.ForeignKey(Project)
class ProjectGroupObjectPermission(GroupObjectPermissionBase):
content_object = models.ForeignKey(Project)
# from guardian.shortcuts import get_objects_for_user
# ...
# videos = get_objects_for_user(request.user, "view_video", Video.objects.all())
def gcp_directory_path(task, filename): def gcp_directory_path(task, filename):
return assets_directory_path(task.id, task.project.id, filename) return assets_directory_path(task.id, task.project.id, filename)

Wyświetl plik

@ -19,6 +19,8 @@
/* HEADER */ /* HEADER */
/* SIDEBAR */ /* SIDEBAR */
/* POPUP */ } /* POPUP */ }
.admin-area #changelist-filter {
display: none; }
.admin-area a:link, .admin-area a:visited { .admin-area a:link, .admin-area a:visited {
color: #447e9b; color: #447e9b;
text-decoration: none; } text-decoration: none; }

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -3,6 +3,10 @@
*/ */
.admin-area{ .admin-area{
#changelist-filter{
display: none;
}
a:link, a:visited { a:link, a:visited {
color: #447e9b; color: #447e9b;
text-decoration: none; text-decoration: none;

Wyświetl plik

@ -72,8 +72,8 @@
.admin-area .colMS .aligned .vLargeTextField, .admin-area .colMS .aligned .vXMLLargeTextField { .admin-area .colMS .aligned .vLargeTextField, .admin-area .colMS .aligned .vXMLLargeTextField {
width: 350px; } width: 350px; }
.admin-area form .aligned ul { .admin-area form .aligned ul {
margin-left: 160px; margin-left: 6px;
padding-left: 10px; } padding-left: 3px; }
.admin-area form .aligned ul.radiolist { .admin-area form .aligned ul.radiolist {
display: inline-block; display: inline-block;
margin: 0; margin: 0;
@ -302,8 +302,8 @@
background-image: url(../img/search.svg); } background-image: url(../img/search.svg); }
.admin-area form .related-widget-wrapper ul { .admin-area form .related-widget-wrapper ul {
display: inline-block; display: inline-block;
margin-left: 0; margin-left: 6px;
padding-left: 0; } padding-left: 3px; }
.admin-area .clearable-file-input input { .admin-area .clearable-file-input input {
margin-top: 0; } margin-top: 0; }

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -105,8 +105,8 @@
} }
form .aligned ul { form .aligned ul {
margin-left: 160px; margin-left: 6px;
padding-left: 10px; padding-left: 3px;
} }
form .aligned ul.radiolist { form .aligned ul.radiolist {
@ -489,8 +489,8 @@
form .related-widget-wrapper ul { form .related-widget-wrapper ul {
display: inline-block; display: inline-block;
margin-left: 0; margin-left: 6px;
padding-left: 0; padding-left: 3px;
} }
.clearable-file-input input { .clearable-file-input input {

Wyświetl plik

@ -24,6 +24,7 @@
{% block extra-headers %}{% endblock %} {% block extra-headers %}{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'app/css/main.css' %}" /> <link rel="stylesheet" type="text/css" href="{% static 'app/css/main.css' %}" />
<script src="{% static 'app/js/vendor/modernizr-2.8.3.min.js' %}"></script> <script src="{% static 'app/js/vendor/modernizr-2.8.3.min.js' %}"></script>
<script src="{% static 'app/js/vendor/knockout-3.4.0.js' %}"></script>
<script src="{% static 'app/js/vendor/jquery-1.11.2.min.js' %}"></script> <script src="{% static 'app/js/vendor/jquery-1.11.2.min.js' %}"></script>
<title>{{title|default:"Login"}} - WebODM</title> <title>{{title|default:"Login"}} - WebODM</title>
</head> </head>

Wyświetl plik

@ -84,3 +84,32 @@ class TestApp(TestCase):
res = c.get('/processingnode/abc/') res = c.get('/processingnode/abc/')
self.assertTrue(res.status_code == 404) self.assertTrue(res.status_code == 404)
def test_projects(self):
# Get a normal user
user = User.objects.get(pk=2)
self.assertFalse(user.is_superuser)
# Create a new project
p = Project(owner=user, name="test")
p.save()
# Have the proper permissions been set?
self.assertTrue(user.has_perm("view_project", p))
self.assertTrue(user.has_perm("add_project", p))
self.assertTrue(user.has_perm("change_project", p))
self.assertTrue(user.has_perm("delete_project", p))
# Get a superuser
superUser = User.objects.get(pk=1)
self.assertTrue(superUser.is_superuser)
# He should also have permissions, although not explicitly set
self.assertTrue(superUser.has_perm("delete_project", p))
# Get another user
anotherUser = User.objects.get(pk=3)
self.assertFalse(anotherUser.is_superuser)
# Should not have permission
self.assertFalse(anotherUser.has_perm("delete_project", p))

Wyświetl plik

@ -15,7 +15,7 @@ def dashboard(request):
no_processingnodes = ProcessingNode.objects.count() == 0 no_processingnodes = ProcessingNode.objects.count() == 0
# Create first project automatically # Create first project automatically
if Project.objects.count() == 0: if Project.objects.filter(owner=request.user).count() == 0:
proj = Project(owner=request.user, name=_("First Project")) proj = Project(owner=request.user, name=_("First Project"))
proj.save() proj.save()

Wyświetl plik

@ -8,6 +8,8 @@ cryptography==1.5
Django==1.10 Django==1.10
django-common-helpers==0.8.0 django-common-helpers==0.8.0
django-filter==0.15.2 django-filter==0.15.2
django-guardian==1.4.6
django-knockout==0.3.2
djangorestframework==3.4.7 djangorestframework==3.4.7
enum34==1.1.6 enum34==1.1.6
fido==3.2.0 fido==3.2.0

Wyświetl plik

@ -38,6 +38,7 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'guardian',
'rest_framework', 'rest_framework',
'app', 'app',
'nodeodm', 'nodeodm',
@ -111,6 +112,11 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
# Hook guardian
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # this is default
'guardian.backends.ObjectPermissionBackend',
)
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/ # https://docs.djangoproject.com/en/1.10/topics/i18n/