Skip to content

Commit

Permalink
update perform_project_modify() and tests (#1617)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikkonie committed May 4, 2023
1 parent ebafbc8 commit c256003
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 76 deletions.
116 changes: 84 additions & 32 deletions taskflowbackend/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Projectroles dependency
from projectroles.app_settings import AppSettingAPI
from projectroles.models import RoleAssignment, SODAR_CONSTANTS
from projectroles.models import RoleAssignment, SODAR_CONSTANTS, ROLE_RANKING
from projectroles.plugins import (
BackendPluginPoint,
ProjectModifyPluginMixin,
Expand All @@ -24,6 +24,7 @@
PROJECT_TYPE_PROJECT = SODAR_CONSTANTS['PROJECT_TYPE_PROJECT']
PROJECT_TYPE_CATEGORY = SODAR_CONSTANTS['PROJECT_TYPE_CATEGORY']
PROJECT_ROLE_OWNER = SODAR_CONSTANTS['PROJECT_ROLE_OWNER']
PROJECT_ROLE_FINDER = SODAR_CONSTANTS['PROJECT_ROLE_FINDER']
PROJECT_ACTION_CREATE = SODAR_CONSTANTS['PROJECT_ACTION_CREATE']
PROJECT_ACTION_UPDATE = SODAR_CONSTANTS['PROJECT_ACTION_UPDATE']

Expand Down Expand Up @@ -71,12 +72,23 @@ def perform_project_modify(
:param old_settings: Old app settings in case of update (dict or None)
:param request: Request object or None
"""
# Skip for categories
if project.type != PROJECT_TYPE_PROJECT:
logger.debug('Skipping: {}'.format(IRODS_CAT_SKIP_MSG))
# Skip for categories unless moving under a different category
if project.type == PROJECT_TYPE_CATEGORY and (
action == PROJECT_ACTION_CREATE
or old_data['parent'] == project.parent
):
logger.debug('Skipping: Nothing to modify')
return

timeline = get_backend_api('timeline_backend')
taskflow = self.get_api()
owner = project.get_owner().user
all_roles = [
a
for a in project.get_roles()
if a.role.rank < ROLE_RANKING[PROJECT_ROLE_FINDER]
]
all_members = [a.user.username for a in all_roles]
tl_event = None
if timeline:
tl_event = timeline.add_event(
Expand All @@ -85,38 +97,73 @@ def perform_project_modify(
plugin_name='taskflow',
user=request.user if request else None,
event_name='project_{}'.format(action.lower()),
description='{} project in iRODS'.format(action.lower()),
description='{} {} in iRODS'.format(
action.lower(), project.type.lower()
),
)
tl_event.set_status('SUBMIT', TL_SUBMIT_DESC)

taskflow = self.get_api()
owner = project.get_owner().user
flow_data = {'owner': owner.username, 'settings': project_settings}
inh_owners = [
a.user.username for a in project.get_owners(inherited_only=True)
]

if action == PROJECT_ACTION_UPDATE: # Update
flow_data['users_add'] = []
all_members = [a.user.username for a in project.get_roles()]
if old_data['parent'] != project.parent:
flow_data['users_add'] = inh_owners
old_parent_owners = [
a.user.username for a in old_data['parent'].get_owners()
if project.type == PROJECT_TYPE_PROJECT:
flow_data = {
'owner': owner.username,
'settings': project_settings,
'users_add': [],
}
if (
action == PROJECT_ACTION_UPDATE
and old_data['parent'] != project.parent
):
inh_members = [
a.user.username
for a in all_roles
if a.project != project and a.user != owner
]
flow_data['users_add'] = inh_members
old_inh_members = [
a.user.username for a in old_data['parent'].get_roles()
]
flow_data['users_delete'] = [
u for u in old_parent_owners if u not in all_members
u for u in old_inh_members if u not in all_members
]
if owner.username not in flow_data['users_add']:
flow_data['users_add'].append(owner.username)
else: # Create
flow_data['users_add'] = inh_owners

taskflow.submit(
project=project,
flow_name='project_{}'.format(action.lower()),
flow_data=flow_data,
)
else: # Create
flow_data['users_add'] = all_members
taskflow.submit(
project=project,
flow_name='project_{}'.format(action.lower()),
flow_data=flow_data,
)
# If updating parent in category, add role_update_irods_batch call
elif (
action == PROJECT_ACTION_UPDATE
and old_data['parent'] != project.parent
):
flow_data = {'roles_add': [], 'roles_delete': []}
old_inh_members = (
[a.user.username for a in old_data['parent'].get_roles()]
if old_data['parent'] # Old parent may be None
else []
)
children = [
p
for p in project.get_children(flat=True)
if p.type == PROJECT_TYPE_PROJECT
]
for c in children:
for u in all_members:
flow_data['roles_add'].append(
{'project_uuid': str(c.sodar_uuid), 'user_name': u}
)
c_members = [a.user.username for a in c.get_roles()]
for u in old_inh_members:
if u not in c_members:
flow_data['roles_delete'].append(
{'project_uuid': str(c.sodar_uuid), 'user_name': u}
)
taskflow.submit(
project=project,
flow_name='role_update_irods_batch',
flow_data=flow_data,
)
if tl_event:
tl_event.set_status('OK')

Expand Down Expand Up @@ -174,8 +221,6 @@ def revert_project_modify(
)
tl_event.set_status('OK')

# TODO: Ensure inherited user access is updated in iRODS

def perform_role_modify(self, role_as, action, old_role=None, request=None):
"""
Perform additional actions to finalize role assignment creation or
Expand All @@ -186,6 +231,7 @@ def perform_role_modify(self, role_as, action, old_role=None, request=None):
:param old_role: Role object for previous role in case of an update
:param request: Request object or None
"""
# TODO: If creating for category, add iRODS access for category children
# Skip for categories
if role_as.project.type != PROJECT_TYPE_PROJECT:
logger.debug('Skipping: {}'.format(IRODS_CAT_SKIP_MSG))
Expand Down Expand Up @@ -232,6 +278,8 @@ def revert_role_modify(self, role_as, action, old_role=None, request=None):
:param old_role: Role object for previous role in case of an update
:param request: Request object or None
"""
# TODO: If category, remove access from category children
# (if no local role)
if action == PROJECT_ACTION_UPDATE:
return # No action needed for update

Expand Down Expand Up @@ -278,6 +326,8 @@ def perform_role_delete(self, role_as, request=None):
:param role_as: RoleAssignment object
:param request: Request object or None
"""
# TODO: If category, remove from iRODS in child projects
# if no local role
# Skip for categories
if role_as.project.type != PROJECT_TYPE_PROJECT:
logger.debug('Skipping: {}'.format(IRODS_CAT_SKIP_MSG))
Expand Down Expand Up @@ -318,6 +368,7 @@ def revert_role_delete(self, role_as, request=None):
:param role_as: RoleAssignment object
:param request: Request object or None
"""
# TODO: If deleting from category, add iRODS role to child projects
irods_backend = get_backend_api('omics_irods')
timeline = get_backend_api('timeline_backend')
group_name = irods_backend.get_user_group_name(role_as.project)
Expand Down Expand Up @@ -441,3 +492,4 @@ def perform_project_sync(self, project):
action=PROJECT_ACTION_CREATE,
project_settings=app_settings.get_all(project),
)
# TODO: Sync roles for children here (see issue #1648)
46 changes: 13 additions & 33 deletions taskflowbackend/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

# Projectroles dependency
from projectroles.app_settings import AppSettingAPI
from projectroles.models import Project, RoleAssignment, Role, SODAR_CONSTANTS
from projectroles.models import Project, RoleAssignment, SODAR_CONSTANTS
from projectroles.plugins import get_backend_api
from projectroles.tests.test_models import (
ProjectMixin,
Expand Down Expand Up @@ -46,6 +46,9 @@
IRODS_ACCESS_NULL = 'null'
IRODS_GROUP_PUBLIC = 'public'
TICKET_STR = 'ei8iomuDoazeiD2z'
TEST_MODE_ERR_MSG = (
'TASKFLOW_TEST_MODE not True, testing with SODAR Taskflow disabled'
)


class TaskflowTestMixin:
Expand Down Expand Up @@ -281,6 +284,7 @@ def tearDown(self):

class TaskflowAPIViewTestBase(
ProjectMixin,
RoleMixin,
RoleAssignmentMixin,
SODARAPIViewTestMixin,
TaskflowTestMixin,
Expand All @@ -293,33 +297,19 @@ class TaskflowAPIViewTestBase(
def setUp(self):
# Ensure TASKFLOW_TEST_MODE is True to avoid data loss
if not settings.TASKFLOW_TEST_MODE:
raise ImproperlyConfigured(
'TASKFLOW_TEST_MODE not True, '
'testing with SODAR Taskflow disabled'
)
raise ImproperlyConfigured(TEST_MODE_ERR_MSG)
self.taskflow = get_backend_api('taskflow', force=True)
self.irods_backend = get_backend_api('omics_irods')
self.irods = self.irods_backend.get_session_obj()

# Init roles
self.role_owner = Role.objects.get_or_create(name=PROJECT_ROLE_OWNER)[0]
self.role_delegate = Role.objects.get_or_create(
name=PROJECT_ROLE_DELEGATE
)[0]
self.role_contributor = Role.objects.get_or_create(
name=PROJECT_ROLE_CONTRIBUTOR
)[0]
self.role_guest = Role.objects.get_or_create(name=PROJECT_ROLE_GUEST)[0]

# Init user
self.init_roles()
# Init superuser
self.user = self.make_user('superuser')
self.user.is_staff = True
self.user.is_superuser = True
self.user.save()
# Get knox token for self.user
self.knox_token = self.get_token(self.user)

# Create category locally (categories are not handled with taskflow)
# Create category locally (creation is not handled with taskflow)
self.category = self.make_project(
'TestCategory', PROJECT_TYPE_CATEGORY, None
)
Expand All @@ -332,6 +322,7 @@ def tearDown(self):

class TaskflowAPIPermissionTestBase(
ProjectMixin,
RoleMixin,
RoleAssignmentMixin,
TaskflowAPIProjectTestMixin,
SODARAPIPermissionTestMixin,
Expand All @@ -342,30 +333,19 @@ class TaskflowAPIPermissionTestBase(
def setUp(self):
# Ensure TASKFLOW_TEST_MODE is True to avoid data loss
if not settings.TASKFLOW_TEST_MODE:
raise ImproperlyConfigured(
'TASKFLOW_TEST_MODE not True, testing with SODAR Taskflow '
'disabled'
)
raise ImproperlyConfigured(TEST_MODE_ERR_MSG)
# Init roles
self.role_owner = Role.objects.get_or_create(name=PROJECT_ROLE_OWNER)[0]
self.role_delegate = Role.objects.get_or_create(
name=PROJECT_ROLE_DELEGATE
)[0]
self.role_contributor = Role.objects.get_or_create(
name=PROJECT_ROLE_CONTRIBUTOR
)[0]
self.role_guest = Role.objects.get_or_create(name=PROJECT_ROLE_GUEST)[0]

self.init_roles()
# Init users
# Superuser
self.superuser = self.make_user('superuser')
self.superuser.is_staff = True
self.superuser.is_superuser = True
self.superuser.save()
self.knox_token = self.get_token(self.superuser)
# No user
self.anonymous = None
# Users with role assignments
# TODO: Add & test against all inherited user types
self.user_owner_cat = self.make_user('user_owner_cat')
self.user_owner = self.make_user('user_owner')
self.user_delegate = self.make_user('user_delegate')
Expand Down
Loading

0 comments on commit c256003

Please sign in to comment.