Implemented merging of permissions.

Permissions are now merged between project, node type and node, instead
of having the lower-level permissions override the higher-level
permissions.
This commit is contained in:
2016-05-06 18:15:50 +02:00
parent 4dc5b4dbbf
commit 899497b3b1
6 changed files with 338 additions and 88 deletions

View File

@@ -28,7 +28,6 @@ EXAMPLE_FILE = {u'_id': ObjectId('5672e2c1c379cf0007b31995'),
u'link': 'http://localhost:8002/file',
u'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)}
EXAMPLE_PROJECT = {
u'_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc),
u'_etag': u'cc4643e98d3606f87bbfaaa200bfbae941b642f3',
@@ -116,7 +115,7 @@ EXAMPLE_PROJECT = {
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
u'methods': [u'DELETE', u'GET']},
{u'group': ObjectId('564733b56dcaf85da2faee8a'),
u'methods': [u'GET']}],
u'users': [],
@@ -240,9 +239,9 @@ EXAMPLE_PROJECT = {
u'order': {u'type': u'integer'},
u'resolution': {u'type': u'string'},
u'stat_ensure_file_existsus': {u'allowed': [u'published',
u'pending',
u'processing'],
u'type': u'string'},
u'pending',
u'processing'],
u'type': u'string'},
u'tags': {u'schema': {u'type': u'string'}, u'type': u'list'}},
u'form_schema': {u'aspect_ratio': {},
u'categories': {},
@@ -270,7 +269,7 @@ EXAMPLE_PROJECT = {
u'organization': ObjectId('55a99fb43004867fb9934f01'),
u'owners': {u'groups': [], u'users': []},
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'PUT', u'POST']}],
u'methods': [u'GET', u'POST', u'PUT']}],
u'users': [],
u'world': [u'GET']},
u'picture_header': ObjectId('5673f260c379cf0007b31bc4'),
@@ -279,3 +278,21 @@ EXAMPLE_PROJECT = {
u'summary': u'Texture collection from all Blender Institute open projects.',
u'url': u'textures',
u'user': ObjectId('552b066b41acdf5dec4436f2')}
EXAMPLE_NODE = {
u'_id': ObjectId('572761099837730efe8e120d'),
u'picture': ObjectId('572761f39837730efe8e1210'),
u'description': u'',
u'node_type': u'asset',
u'user': ObjectId('57164ca1983773118cbaf779'),
u'properties': {
u'status': u'published',
u'content_type': u'image',
u'file': ObjectId('572761129837730efe8e120e')
},
u'_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc),
u'name': u'Image test',
u'project': EXAMPLE_PROJECT_ID,
u'_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc),
u'_etag': u'6b8589b42c880e3626f43f3e82a5c5b946742687'
}

View File

@@ -1,11 +1,15 @@
# -*- encoding: utf-8 -*-
import copy
import datetime
import responses
import json
from bson import tz_util, ObjectId
from werkzeug.exceptions import Forbidden
from common_test_class import AbstractPillarTest, TEST_EMAIL_USER, TEST_EMAIL_ADDRESS
from common_test_data import EXAMPLE_PROJECT, EXAMPLE_NODE
PUBLIC_USER_FIELDS = {'full_name', 'email'}
@@ -352,3 +356,117 @@ class UserListTests(AbstractPillarTest):
resp = self.client.delete('/users/323456789abc123456789abc',
headers={'Authorization': self.make_header('admin-token')})
self.assertEqual(405, resp.status_code, resp.data)
class PermissionComputationTest(AbstractPillarTest):
maxDiff = None
def test_merge_permissions(self):
from application.utils.authorization import merge_permissions
with self.app.test_request_context():
self.assertEqual({}, merge_permissions())
self.assertEqual({}, merge_permissions({}))
self.assertEqual({}, merge_permissions({}, {}, {}))
# Merge one level deep
self.assertEqual(
{},
merge_permissions({'users': []}, {'groups': []}, {'world': []}))
self.assertEqual(
{'users': [{'user': 'micak', 'methods': ['GET', 'POST', 'PUT']}],
'groups': [{'group': 'manatees', 'methods': ['DELETE', 'GET']}],
'world': ['GET']},
merge_permissions(
{'users': [{'user': 'micak', 'methods': ['GET', 'POST', 'PUT']}]},
{'groups': [{'group': 'manatees', 'methods': ['DELETE', 'GET']}]},
{'world': ['GET']}))
# Merge two levels deep.
self.assertEqual(
{'users': [{'user': 'micak', 'methods': ['GET', 'POST', 'PUT']}],
'groups': [{'group': 'lions', 'methods': ['GET']},
{'group': 'manatees', 'methods': ['GET', 'POST', 'PUT']}],
'world': ['GET']},
merge_permissions(
{'users': [{'user': 'micak', 'methods': ['GET', 'PUT', 'POST']}],
'groups': [{'group': 'lions', 'methods': ['GET']}]},
{'groups': [{'group': 'manatees', 'methods': ['GET', 'PUT', 'POST']}]},
{'world': ['GET']}))
# Merge three levels deep
self.assertEqual(
{'users': [{'user': 'micak', 'methods': ['DELETE', 'GET', 'POST', 'PUT']}],
'groups': [{'group': 'lions', 'methods': ['GET', 'PUT', 'SCRATCH']},
{'group': 'manatees', 'methods': ['GET', 'POST', 'PUT']}],
'world': ['GET']},
merge_permissions(
{'users': [{'user': 'micak', 'methods': ['GET', 'PUT', 'POST']}],
'groups': [{'group': 'lions', 'methods': ['GET']},
{'group': 'manatees', 'methods': ['GET', 'PUT', 'POST']}],
'world': ['GET']},
{'users': [{'user': 'micak', 'methods': ['DELETE']}],
'groups': [{'group': 'lions', 'methods': ['GET', 'PUT', 'SCRATCH']}],
}
))
def sort(self, permissions):
"""Returns a sorted copy of the permissions."""
from application.utils.authorization import merge_permissions
return merge_permissions(permissions, {})
def test_effective_permissions(self):
from application.utils.authorization import compute_aggr_permissions
with self.app.test_request_context():
# Test project permissions.
self.assertEqual(
{
u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'POST', u'PUT']}],
u'world': [u'GET']
},
self.sort(compute_aggr_permissions('projects', EXAMPLE_PROJECT, None)))
# Test node type permissions.
self.assertEqual(
{
u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'POST', u'PUT']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
{u'group': ObjectId('564733b56dcaf85da2faee8a'),
u'methods': [u'GET']}],
u'world': [u'GET']
},
self.sort(compute_aggr_permissions('projects', EXAMPLE_PROJECT, 'texture')))
# Test node permissions with non-existing project.
node = copy.deepcopy(EXAMPLE_NODE)
self.assertRaises(Forbidden, compute_aggr_permissions, 'nodes', node, None)
# Test node permissions without embedded project.
self.ensure_project_exists()
self.assertEqual(
{u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'POST', u'PUT']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'DELETE', u'GET']},
{u'group': ObjectId('564733b56dcaf85da2faee8a'),
u'methods': [u'GET']}],
u'world': [u'GET']},
self.sort(compute_aggr_permissions('nodes', node, None)))
# Test node permissions with embedded project.
node = copy.deepcopy(EXAMPLE_NODE)
node['project'] = EXAMPLE_PROJECT
self.assertEqual(
{u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'methods': [u'GET', u'POST', u'PUT']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'DELETE', u'GET']},
{u'group': ObjectId('564733b56dcaf85da2faee8a'),
u'methods': [u'GET']}],
u'world': [u'GET']},
self.sort(compute_aggr_permissions('nodes', node, None)))