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

Add metadata permissions #107

Open
wants to merge 15 commits into
base: integration
Choose a base branch
from
35 changes: 35 additions & 0 deletions django/bosscore/migrations/0010_auto_20221106_2226.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 2.2.18 on 2022-11-06 22:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('bosscore', '0009_auto_20210517_2146'),
]

operations = [
migrations.AlterModelOptions(
name='channel',
options={'default_permissions': (), 'permissions': (('read', 'Can view resource'), ('update', 'Can update resource'), ('delete', 'Can delete resource'), ('add', 'Can add resources '), ('assign_group', 'Can assign groups permissions for the resource'), ('remove_group', 'Can remove groups permissions for the resource'), ('add_volumetric_data', 'Can add volumetric data for the channel'), ('read_volumetric_data', 'Can read volumetric data for the channel'), ('delete_volumetric_data', 'Can delete volumetric data for the channel'), ('add_metadata', 'Can add metadata for the channel'), ('read_metadata', 'Can read metadata for the channel'), ('update_metadata', 'Can update metadata for the channel'), ('delete_metadata', 'Can delete metadata for the channel'))},
),
migrations.AlterModelOptions(
name='collection',
options={'default_permissions': (), 'managed': True, 'permissions': (('read', 'Can view resource'), ('update', 'Can update resource'), ('delete', 'Can delete resource'), ('add', 'Can add resources '), ('assign_group', 'Can assign groups permissions for the resource'), ('remove_group', 'Can remove groups permissions for the resource'), ('add_metadata', 'Can add metadata for the channel'), ('read_metadata', 'Can read metadata for the channel'), ('update_metadata', 'Can update metadata for the channel'), ('delete_metadata', 'Can delete metadata for the channel'))},
),
migrations.AlterModelOptions(
name='experiment',
options={'default_permissions': (), 'permissions': (('read', 'Can view resource'), ('update', 'Can update resource'), ('delete', 'Can delete resource'), ('add', 'Can add resources '), ('assign_group', 'Can assign groups permissions for the resource'), ('remove_group', 'Can remove groups permissions for the resource'), ('add_metadata', 'Can add metadata for the channel'), ('read_metadata', 'Can read metadata for the channel'), ('update_metadata', 'Can update metadata for the channel'), ('delete_metadata', 'Can delete metadata for the channel'))},
),
migrations.AlterField(
model_name='channel',
name='cv_path',
field=models.CharField(blank=True, max_length=2000, null=True),
),
migrations.AlterField(
model_name='channel',
name='downsample_arn',
field=models.CharField(max_length=4096, null=True),
),
]
12 changes: 12 additions & 0 deletions django/bosscore/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ class Meta:
('add', 'Can add resources '),
('assign_group', 'Can assign groups permissions for the resource'),
('remove_group', 'Can remove groups permissions for the resource'),
('add_metadata', 'Can add metadata for the channel'),
('read_metadata', 'Can read metadata for the channel'),
('update_metadata', 'Can update metadata for the channel'),
('delete_metadata', 'Can delete metadata for the channel')

)

Expand Down Expand Up @@ -226,6 +230,10 @@ class Meta:
('add', 'Can add resources '),
('assign_group', 'Can assign groups permissions for the resource'),
('remove_group', 'Can remove groups permissions for the resource'),
('add_metadata', 'Can add metadata for the channel'),
('read_metadata', 'Can read metadata for the channel'),
('update_metadata', 'Can update metadata for the channel'),
('delete_metadata', 'Can delete metadata for the channel')
)

def __str__(self):
Expand Down Expand Up @@ -333,6 +341,10 @@ class Meta:
('add_volumetric_data', 'Can add volumetric data for the channel'),
('read_volumetric_data', 'Can read volumetric data for the channel'),
('delete_volumetric_data', 'Can delete volumetric data for the channel'),
('add_metadata', 'Can add metadata for the channel'),
('read_metadata', 'Can read metadata for the channel'),
('update_metadata', 'Can update metadata for the channel'),
('delete_metadata', 'Can delete metadata for the channel'),
)

def add_source(self, source):
Expand Down
48 changes: 32 additions & 16 deletions django/bosscore/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ def add_permissions_primary_group(user, obj):
assign_perm('delete', user_primary_group, obj)
assign_perm('assign_group', user_primary_group, obj)
assign_perm('remove_group', user_primary_group, obj)

# Add metadata permissions for primary user
if ct.model != 'coordinateframe':
assign_perm('read_metadata', user_primary_group, obj)
assign_perm('add_metadata', user_primary_group, obj)
assign_perm('update_metadata', user_primary_group, obj)
assign_perm('delete_metadata', user_primary_group, obj)

if ct.model == 'channel':
assign_perm('add_volumetric_data', user_primary_group, obj)
assign_perm('read_volumetric_data', user_primary_group, obj)
Expand Down Expand Up @@ -182,6 +190,14 @@ def add_permissions_admin_group(obj):
assign_perm('delete', admin_group, obj)
assign_perm('assign_group', admin_group, obj)
assign_perm('remove_group', admin_group, obj)

# Add metadata permissions for admin user
if ct.model != 'coordinateframe':
assign_perm('read_metadata', admin_group, obj)
assign_perm('add_metadata', admin_group, obj)
assign_perm('update_metadata', admin_group, obj)
assign_perm('delete_metadata', admin_group, obj)

if ct.model == 'channel':
assign_perm('add_volumetric_data', admin_group, obj)
assign_perm('read_volumetric_data', admin_group, obj)
Expand All @@ -192,26 +208,24 @@ def add_permissions_admin_group(obj):
ErrorCodes.GROUP_NOT_FOUND)

@staticmethod
def check_resource_permissions(user, obj, method_type):
def check_data_permissions(user, obj, method_type):
"""
Check user permissions for a resource object
Check user permissions for a data
Args:
user: User name
obj: Obj
method_type: Method type specified in the request
obj: resource
method_type: Method type specified in the post

Returns:
bool. True if the user has the permission on the resource

"""
if method_type == 'GET':
permission = 'read'
elif method_type == 'POST':
permission = 'add'
elif method_type == 'PUT':
permission = 'update'
permission = 'read_volumetric_data'
elif method_type == 'POST' or method_type == 'PUT':
permission = 'add_volumetric_data'
elif method_type == 'DELETE':
permission = 'delete'
permission = 'delete_volumetric_data'
else:
raise BossError("Unable to get permissions for this request", ErrorCodes.INVALID_POST_ARGUMENT)

Expand All @@ -221,9 +235,9 @@ def check_resource_permissions(user, obj, method_type):
return False

@staticmethod
def check_data_permissions(user, obj, method_type):
def check_metadata_permissions(user, obj, method_type):
"""
Check user permissions for a data
Check user permissions for a metadata entry
Args:
user: User name
obj: resource
Expand All @@ -234,11 +248,13 @@ def check_data_permissions(user, obj, method_type):

"""
if method_type == 'GET':
permission = 'read_volumetric_data'
elif method_type == 'POST' or method_type == 'PUT':
permission = 'add_volumetric_data'
permission = 'read_metadata'
elif method_type == 'POST':
permission = 'add_metadata'
elif method_type == 'PUT':
permission = 'update_metadata'
elif method_type == 'DELETE':
permission = 'delete_volumetric_data'
permission = 'delete_metadata'
else:
raise BossError("Unable to get permissions for this request", ErrorCodes.INVALID_POST_ARGUMENT)

Expand Down
2 changes: 1 addition & 1 deletion django/bosscore/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ def check_permissions(self):
else:
raise BossError("Error encountered while checking permissions for this request",
ErrorCodes.UNABLE_TO_VALIDATE)
perm = BossPermissionManager.check_resource_permissions(self.user, obj, self.method)
perm = BossPermissionManager.check_metadata_permissions(self.user, obj, self.method)
elif self.service == 'reserve':
perm = BossPermissionManager.check_object_permissions(self.user, self.channel, self.method)

Expand Down
65 changes: 38 additions & 27 deletions django/bosscore/test/setup_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,13 @@ def insert_test_data(self):

self.add_coordinate_frame('cf1', 'Description for cf1', 0, 1000, 0, 1000, 0, 1000, 4, 4, 4)

self.add_experiment('col1', EXP1, 'cf1', NUM_HIERARCHY_LEVELS, 10, 1)
self.add_experiment('col1', EXP22, 'cf1', NUM_HIERARCHY_LEVELS, 500, 1)
self.add_experiment('col1', EXP1, 'cf1', 10, 500, 1)
self.add_experiment('col1', EXP22, 'cf1', NUM_HIERARCHY_LEVELS, 10, 1)
self.add_experiment('col1', EXP_BASE_RES, 'cf1', NUM_HIERARCHY_LEVELS, 10, 1)
self.add_experiment(COLL_NOT_PUBLIC, EXP_NOT_PUBLIC, 'cf1', NUM_HIERARCHY_LEVELS, 1, 1, public=False)

self.add_channel('col1', EXP1, 'channel1', 0, 0, 'uint8', 'image')
self.add_channel('col1', EXP1, 'channel2', 0, 0, 'uint8', 'image')
self.add_channel('col1', EXP1, 'channel2', 0, 0, 'uint16', 'image')
self.add_channel('col1', EXP1, 'channel3', 0, 0, 'uint64', 'annotation', ['channel1'])
self.add_channel('col1', EXP_BASE_RES, CHAN_BASE_RES, 0, BASE_RESOLUTION, 'uint8', 'image')
self.add_channel('col1', EXP1, 'layer1', 0, 0, 'uint64', 'annotation', ['channel1'])
Expand Down Expand Up @@ -192,33 +192,33 @@ def insert_cloudvolume_test_data(self):
"""
Test data for cloudvolume integration.
"""
self.add_collection('col1', 'Description for collection1')
self.add_coordinate_frame('cf1', 'Description for cf1', 0, 100000, 0, 100000, 0, 100000, 4, 4, 4)
self.add_experiment('col1', 'exp1', 'cf1', 10, 500, 1)
self.add_collection('col1-cvdb', 'Description for collection1')
self.add_coordinate_frame('cf1-cvdb', 'Description for cf1', 0, 100000, 0, 100000, 0, 100000, 4, 4, 4)
self.add_experiment('col1-cvdb', 'exp1', 'cf1-cvdb', 10, 500, 1)

# Dev Note: Prepopulated cloudvolume layer for uint8 data located at this cloudpath
self.add_channel('col1', 'exp1', 'chan1', 0, 0, 'uint8', 'image',
self.add_channel('col1-cvdb', 'exp1', 'chan1', 0, 0, 'uint8', 'image',
storage_type='cloudvol',
bucket=CLOUD_VOL_BUCKET,
cv_path=CVPATH_CHAN1)

# Dev Note: Prepopulated cloudvolume layer for uint16 data located at this cloudpath
self.add_channel('col1', 'exp1', 'chan2', 0, 0, 'uint16', 'image',
self.add_channel('col1-cvdb', 'exp1', 'chan2', 0, 0, 'uint16', 'image',
storage_type='cloudvol',
bucket=CLOUD_VOL_BUCKET,
cv_path=CVPATH_CHAN2)

# Dev Note: Prepopulated cloudvolume layer for uint16 data located at this cloudpath
self.add_channel('col1', 'exp1', 'anno1', 0, 0, 'uint64', 'annotation',
self.add_channel('col1-cvdb', 'exp1', 'anno1', 0, 0, 'uint64', 'annotation',
storage_type='cloudvol',
bucket=CLOUD_VOL_BUCKET,
cv_path=CVPATH_ANNO1)

def insert_ingest_test_data(self):

self.add_collection('my_col_1', 'Description for collection1')
self.add_coordinate_frame('cf1', 'Description for cf1', 0, 100000, 0, 100000, 0, 100000, 4, 4, 4)
self.add_experiment('my_col_1', 'my_exp_1', 'cf1', 10, 500, 1)
self.add_coordinate_frame('cf2-ingest', 'cf2-ingest', 0, 100000, 0, 100000, 0, 100000, 4, 4, 4)
self.add_experiment('my_col_1', 'my_exp_1', 'cf2-ingest', 10, 500, 1)
self.add_channel('my_col_1', 'my_exp_1', 'my_ch_1', 0, 0, 'uint8', 'image')

def insert_iso_data(self):
Expand Down Expand Up @@ -266,6 +266,13 @@ def add_permissions(group, obj):
assign_perm('delete', user_primary_group, obj)
assign_perm('assign_group', user_primary_group, obj)
assign_perm('remove_group', user_primary_group, obj)

if ct.model != 'coordinateframe':
assign_perm('read_metadata', user_primary_group, obj)
assign_perm('add_metadata', user_primary_group, obj)
assign_perm('update_metadata', user_primary_group, obj)
assign_perm('delete_metadata', user_primary_group, obj)

if ct.model == 'channel':
assign_perm('add_volumetric_data', user_primary_group, obj)
assign_perm('read_volumetric_data', user_primary_group, obj)
Expand All @@ -286,12 +293,13 @@ def add_collection(self, collection_name, description, public=False):
Collection

"""
col = Collection.objects.create(name=collection_name, description=description, creator=self.user, public=public)
col, created = Collection.objects.get_or_create(name=collection_name, description=description, creator=self.user, public=public)

# Add a lookup key
lkup_key = str(col.pk)
bs_key = col.name
BossLookup.objects.create(lookup_key=lkup_key, boss_key=bs_key, collection_name=col.name)
if created:
BossLookup.objects.create(lookup_key=lkup_key, boss_key=bs_key, collection_name=col.name)

# Give permissions to the users primary group
primary_group = self.user.username + '-primary'
Expand Down Expand Up @@ -320,7 +328,7 @@ def add_coordinate_frame(self, coordinate_frame, description, x_start, x_stop, y
Coordinate Frame

"""
cf = CoordinateFrame.objects.create(name=coordinate_frame, description=description,
cf, created = CoordinateFrame.objects.get_or_create(name=coordinate_frame, description=description,
x_start=x_start, x_stop=x_stop, y_start=y_start, y_stop=y_stop,
z_start=z_start, z_stop=z_stop,
x_voxel_size=x_voxel_size, y_voxel_size=y_voxel_size,
Expand Down Expand Up @@ -349,15 +357,16 @@ def add_experiment(self, collection_name, experiment_name, coordinate_name, num_
"""
col = Collection.objects.get(name=collection_name)
cf = CoordinateFrame.objects.get(name=coordinate_name)
exp = Experiment.objects.create(name=experiment_name, collection=col, coord_frame=cf,
exp, created = Experiment.objects.get_or_create(name=experiment_name, collection=col, coord_frame=cf,
num_hierarchy_levels=num_hierarchy_levels, hierarchy_method=hierarchy_method,
num_time_samples=num_time_samples, time_step=time_step, creator=self.user,
public=public)

lkup_key = str(col.pk) + '&' + str(exp.pk)
bs_key = col.name + '&' + str(exp.name)
BossLookup.objects.create(lookup_key=lkup_key, boss_key=bs_key, collection_name=col.name,
experiment_name=exp.name)
if created:
lkup_key = str(col.pk) + '&' + str(exp.pk)
bs_key = col.name + '&' + str(exp.name)
BossLookup.objects.create(lookup_key=lkup_key, boss_key=bs_key, collection_name=col.name,
experiment_name=exp.name)

# Give permissions to the users primary group
primary_group = self.user.username + '-primary'
Expand Down Expand Up @@ -397,7 +406,7 @@ def add_channel(self, collection_name, experiment_name, channel_name,

col = Collection.objects.get(name=collection_name)
exp = Experiment.objects.get(name=experiment_name, collection=col)
channel = Channel.objects.create(name=channel_name, experiment=exp,
channel, created = Channel.objects.get_or_create(name=channel_name, experiment=exp,
default_time_sample=default_time_sample, base_resolution=base_resolution,
type=channel_type, datatype=datatype, creator=self.user,
public=public, storage_type=storage_type, bucket=bucket, cv_path=cv_path)
Expand All @@ -412,11 +421,12 @@ def add_channel(self, collection_name, experiment_name, channel_name,
# Set lookup key.
base_lkup_key = str(col.pk) + '&' + str(exp.pk) + '&' + str(channel.pk)
base_bs_key = col.name + '&' + exp.name + '&' + channel.name
BossLookup.objects.create(lookup_key=base_lkup_key, boss_key=base_bs_key,
collection_name=col.name,
experiment_name=exp.name,
channel_name=channel.name
)
if created:
BossLookup.objects.create(lookup_key=base_lkup_key, boss_key=base_bs_key,
collection_name=col.name,
experiment_name=exp.name,
channel_name=channel.name
)

# Give permissions to the users primary group
primary_group = self.user.username + '-primary'
Expand All @@ -433,10 +443,11 @@ class DjangoSetupLayer(AWSSetupLayer):
@classmethod
def setUp(cls):
# Create a user in django
cls.superuser = cls.django_setup_helper.create_super_user()
cls.user = cls.django_setup_helper.create_user('testuser')
cls.superuser = cls.django_setup_helper.create_super_user('django-superuser')
cls.user = cls.django_setup_helper.create_user('django-testuser')
cls.django_setup_helper.add_role('resource-manager')
cls.django_setup_helper.set_user(cls.user)

# Populate django models DB
cls.django_setup_helper.insert_spatialdb_test_data()
cls.django_setup_helper.insert_cloudvolume_test_data()
4 changes: 2 additions & 2 deletions django/bosscore/views/views_permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def post(self, request):

try:
# public group can only have read permission
if group_name == PUBLIC_GRP and not (set(perm_list).issubset({'read', 'read_volumetric_data'})):
if group_name == PUBLIC_GRP and not (set(perm_list).issubset({'read', 'read_volumetric_data', 'read_metadata'})):
return BossHTTPError("The public group can only have read permissions",
ErrorCodes.INVALID_POST_ARGUMENT)

Expand Down Expand Up @@ -299,7 +299,7 @@ def patch(self, request):

try:
# public group can only have read permission
if group_name == PUBLIC_GRP and (len(perm_list) != 1 or perm_list[0] != 'read'):
if group_name == PUBLIC_GRP and not (set(perm_list).issubset({'read', 'read_volumetric_data', 'read_metadata'})):
return BossHTTPError("The public group can only have read permissions",
ErrorCodes.INVALID_POST_ARGUMENT)
# If the user is not a member or maintainer of the group, they cannot patch permissions
Expand Down
Loading