Skip to content

Commit

Permalink
update perform_role_delete() and revert_role_delete(), add tests, ref…
Browse files Browse the repository at this point in the history
…actor (#1617)
  • Loading branch information
mikkonie committed May 15, 2023
1 parent 0c5b573 commit d68300d
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 64 deletions.
163 changes: 107 additions & 56 deletions taskflowbackend/plugins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Plugins for the taskflowbackend app"""

# TODO: Update sync API call timeline events to be created in the end
# TODO: Update sync API timeline calls

import logging

Expand Down Expand Up @@ -53,6 +53,26 @@ class BackendPlugin(ProjectModifyPluginMixin, BackendPluginPoint):
#: Description string
description = 'SODAR Taskflow backend for iRODS data transactions'

# Internal helpers ---------------------------------------------------------

@classmethod
def _get_child_projects(cls, project):
"""
Return category children of type PROJECT.
:param project: Project object
:return: List
"""
if project.type != PROJECT_TYPE_CATEGORY:
return []
return [
p
for p in project.get_children(flat=True)
if p.type == PROJECT_TYPE_PROJECT
]

# API methods --------------------------------------------------------------

def get_api(self):
"""Return API entry point object."""
return TaskflowAPI()
Expand Down Expand Up @@ -244,13 +264,7 @@ def perform_role_modify(self, role_as, action, old_role=None, request=None):
timeline = get_backend_api('timeline_backend')
project = role_as.project
user = role_as.user
children = None
if project.type == PROJECT_TYPE_CATEGORY:
children = [
p
for p in project.get_children(flat=True)
if p.type == PROJECT_TYPE_PROJECT
]
children = self._get_child_projects(project)

tl_event = None
if timeline:
Expand Down Expand Up @@ -307,13 +321,6 @@ def revert_role_modify(self, role_as, action, old_role=None, request=None):
project = role_as.project
user = role_as.user
user_name = user.username
children = []
if project.type == PROJECT_TYPE_CATEGORY:
children = [
p
for p in project.get_children(flat=True)
if p.type == PROJECT_TYPE_PROJECT
]

timeline = get_backend_api('timeline_backend')
tl_event = None
Expand All @@ -329,16 +336,14 @@ def revert_role_modify(self, role_as, action, old_role=None, request=None):
)
tl_event.add_object(user, 'user', user_name)

flow_data = {
'roles_add': [],
'roles_delete': [],
}
flow_data = {'roles_add': [], 'roles_delete': []}
# Revert creation or update from finder role for project
if project.type == PROJECT_TYPE_PROJECT and (
action == PROJECT_ACTION_CREATE or old_role.rank >= RANK_FINDER
):
flow_data['roles_delete'].append(get_batch_role(project, user_name))
elif project.type == PROJECT_TYPE_CATEGORY:
children = self._get_child_projects(project)
for c in children:
batch_role = get_batch_role(c, user_name)
# Search for inherited roles for child
Expand Down Expand Up @@ -375,37 +380,61 @@ 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))
return
taskflow = self.get_api()
project = role_as.project
user = role_as.user
user_name = user.username

timeline = get_backend_api('timeline_backend')
tl_event = None
if timeline:
tl_event = timeline.add_event(
project=role_as.project,
project=project,
app_name=APP_NAME,
plugin_name='taskflow',
user=request.user if request else None,
event_name='role_delete',
description='remove project iRODS access from user {user}',
)
tl_event.add_object(
obj=role_as.user, label='user', name=role_as.user.username
)
tl_event.add_object(obj=user, label='user', name=user_name)
tl_event.set_status('SUBMIT', TL_SUBMIT_DESC)

taskflow = self.get_api()
flow_data = {'username': role_as.user.username}
taskflow.submit(
project=role_as.project,
flow_name='role_delete',
flow_data=flow_data,
tl_event=tl_event,
)
flow_data = {'roles_add': [], 'roles_delete': []}
if project.type == PROJECT_TYPE_PROJECT:
inh_as = (
RoleAssignment.objects.filter(
user=user, project__in=project.get_parents()
)
.order_by('role__rank')
.first()
)
if not inh_as or inh_as.role.rank >= RANK_FINDER:
flow_data['roles_delete'].append(
get_batch_role(project, user_name)
)
else: # Category
children = self._get_child_projects(project)
for c in children:
# NOTE: role_as still exists so it has to be excluded
c_as = (
RoleAssignment.objects.filter(
user=user, project__in=[c] + list(c.get_parents())
)
.order_by('role__rank')
.exclude(sodar_uuid=role_as.sodar_uuid)
.first()
)
if not c_as or c_as.role.rank >= RANK_FINDER:
flow_data['roles_delete'].append(
get_batch_role(c, user_name)
)

if flow_data['roles_add'] or flow_data['roles_delete']:
taskflow.submit(
project=project,
flow_name='role_update_irods_batch',
flow_data=flow_data,
)
if tl_event:
tl_event.set_status('OK')

Expand All @@ -417,27 +446,49 @@ 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)
user_name = role_as.user.username
reverted = False
taskflow = self.get_api()
project = role_as.project
user = role_as.user
user_name = user.username

with irods_backend.get_session() as irods:
try:
group = irods.user_groups.get(group_name)
logger.debug(
'Adding user {} to group {}'.format(user_name, group_name)
flow_data = {'roles_add': [], 'roles_delete': []}
if project.type == PROJECT_TYPE_PROJECT:
user_as = project.get_role(user)
if user_as and user_as.role.rank < RANK_FINDER:
flow_data['roles_add'].append(
get_batch_role(project, user_name)
)
if not group.hasmember(user_name):
group.addmember(user_name=user_name, user_zone=irods.zone)
reverted = True
except Exception as ex:
logger.error('Error adding member: {}'.format(ex))
return
else: # Category
children = self._get_child_projects(project)
for c in children:
batch_role = get_batch_role(c, user_name)
# NOTE: role_as still exists so it has to be excluded
if role_as.role.rank < RANK_FINDER:
flow_data['roles_add'].append(batch_role)
else:
c_as = (
RoleAssignment.objects.filter(
user=user, project__in=[c] + list(c.get_parents())
)
.order_by('role__rank')
.exclude(sodar_uuid=role_as.sodar_uuid)
.first()
)
if c_as and c_as.role.rank < RANK_FINDER:
k = 'roles_add'
else:
k = 'roles_delete'
flow_data[k].append(batch_role)

if timeline and reverted:
if flow_data['roles_add'] or flow_data['roles_delete']:
taskflow.submit(
project=project,
flow_name='role_update_irods_batch',
flow_data=flow_data,
)

timeline = get_backend_api('timeline_backend')
if timeline:
tl_event = timeline.add_event(
project=role_as.project,
app_name=APP_NAME,
Expand All @@ -446,9 +497,9 @@ def revert_role_delete(self, role_as, request=None):
event_name='role_delete_revert',
description='revert removing iRODS access from '
'user {{{}}}'.format('user'),
status_type='OK',
)
tl_event.add_object(role_as.user, 'user', user_name)
tl_event.set_status('OK')

def perform_owner_transfer(
self, project, new_owner, old_owner, old_owner_role, request=None
Expand Down
Loading

0 comments on commit d68300d

Please sign in to comment.