From 9d17792e976f4d55270214512a751c6c35fbff7b Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 6 Oct 2023 10:06:43 -0400 Subject: [PATCH 1/4] Fix SA2.0 usage in tool_shed.webapp.security --- lib/tool_shed/webapp/security/__init__.py | 40 +++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/tool_shed/webapp/security/__init__.py b/lib/tool_shed/webapp/security/__init__.py index a2cea3ade3bf..8bf4d52fbdb7 100644 --- a/lib/tool_shed/webapp/security/__init__.py +++ b/lib/tool_shed/webapp/security/__init__.py @@ -3,13 +3,19 @@ from typing import List from sqlalchemy import ( - and_, false, + select, ) from galaxy.model.base import transaction from galaxy.util import listify from galaxy.util.bunch import Bunch +from tool_shed.webapp.model import ( + Group, + Role, +) + +IUC_NAME = "Intergalactic Utilities Commission" log = logging.getLogger(__name__) @@ -159,16 +165,7 @@ def get_item_actions(self, action, item): return [permission for permission in item.actions if permission.action == action.action] def get_private_user_role(self, user, auto_create=False): - role = ( - self.sa_session.query(self.model.Role) - .filter( - and_( - self.model.Role.table.c.name == user.email, - self.model.Role.table.c.type == self.model.Role.types.PRIVATE, - ) - ) - .first() - ) + role = _get_private_user_role(self.sa_session, user.email) if not role: if auto_create: return self.create_private_user_role(user) @@ -276,16 +273,7 @@ def user_can_import_repository_archive(self, user, archive_owner): if user.username == archive_owner: return True # A member of the IUC is authorized to create new repositories that are owned by another user. - iuc_group = ( - self.sa_session.query(self.model.Group) - .filter( - and_( - self.model.Group.table.c.name == "Intergalactic Utilities Commission", - self.model.Group.table.c.deleted == false(), - ) - ) - .first() - ) + iuc_group = get_iuc_group(self.sa_session) if iuc_group is not None: for uga in iuc_group.users: if uga.user.id == user.id: @@ -300,3 +288,13 @@ def get_permitted_actions(filter=None): tmp_bunch = Bunch() [tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith(filter)] return tmp_bunch + + +def get_iuc_group(session): + stmt = select(Group).where(Group.name == IUC_NAME).where(Group.deleted == false()).limit(1) + return session.scalars(stmt).first() + + +def _get_private_user_role(session, user_email): + stmt = select(Role).where(Role.name == user_email).where(Role.type == Role.types.PRIVATE).limit(1) + return session.scalars(stmt).first() From 8acdc891fa12a74e3d8c5577958b0aeea655456a Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 6 Oct 2023 10:07:23 -0400 Subject: [PATCH 2/4] Fix SA2.0 usage in tool_shed.webapp.util.shed_statistics --- lib/tool_shed/webapp/util/shed_statistics.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tool_shed/webapp/util/shed_statistics.py b/lib/tool_shed/webapp/util/shed_statistics.py index 2b2ae9db2d2a..d864420ecb2d 100644 --- a/lib/tool_shed/webapp/util/shed_statistics.py +++ b/lib/tool_shed/webapp/util/shed_statistics.py @@ -3,6 +3,10 @@ strftime, ) +from sqlalchemy import select + +from tool_shed.webapp.model import Repository + class ShedCounter: def __init__(self, model): @@ -38,7 +42,7 @@ def generate_statistics(self): self.unique_valid_tools = 0 self.workflows = 0 unique_user_ids = [] - for repository in self.sa_session.query(self.model.Repository): + for repository in self.sa_session.scalars(select(Repository)): self.repositories += 1 self.total_clones += repository.times_downloaded is_deleted = repository.deleted From 7e7cbf1463e0e79728e33170a532c206fdeeb6a7 Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 6 Oct 2023 10:07:48 -0400 Subject: [PATCH 3/4] Refactor, fix bug, SA2.0 in tool_shed.webapp.app2.users Bug: check if prev session is not None before accessing its user attr. --- lib/tool_shed/webapp/api2/users.py | 62 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/lib/tool_shed/webapp/api2/users.py b/lib/tool_shed/webapp/api2/users.py index fefef4e71a7d..f55e51438cbc 100644 --- a/lib/tool_shed/webapp/api2/users.py +++ b/lib/tool_shed/webapp/api2/users.py @@ -11,8 +11,9 @@ ) from pydantic import BaseModel from sqlalchemy import ( - and_, + false, true, + update, ) import tool_shed.util.shed_util_common as suc @@ -23,6 +24,7 @@ ) from galaxy.managers.api_keys import ApiKeyManager from galaxy.managers.users import UserManager +from galaxy.model.base import transaction from galaxy.webapps.base.webapp import create_new_session from tool_shed.context import SessionRequestContext from tool_shed.managers.users import ( @@ -31,7 +33,10 @@ index, ) from tool_shed.structured_app import ToolShedApp -from tool_shed.webapp.model import User as SaUser +from tool_shed.webapp.model import ( + GalaxySession, + User as SaUser, +) from tool_shed_client.schema import ( CreateUserRequest, UserV2 as User, @@ -299,42 +304,43 @@ def ensure_csrf_token(trans: SessionRequestContext, request: HasCsrfToken): def handle_user_login(trans: SessionRequestContext, user: SaUser) -> None: trans.app.security_agent.create_user_role(user, trans.app) - # Set the previous session - prev_galaxy_session = trans.get_galaxy_session() - if prev_galaxy_session: - prev_galaxy_session.is_valid = False - # Define a new current_session - new_session = create_new_session(trans, prev_galaxy_session, user) - trans.set_galaxy_session(new_session) - trans.sa_session.add_all((prev_galaxy_session, new_session)) - trans.sa_session.flush() - set_auth_cookie(trans, new_session) + replace_previous_session(trans, user) def handle_user_logout(trans, logout_all=False): """ Logout the current user: - - invalidate the current session + - invalidate current session + previous sessions (optional) - create a new session with no user associated """ + if logout_all: + prev_session = trans.get_galaxy_session() + if prev_session and prev_session.user_id: + invalidate_user_sessions(trans.sa_session, prev_session.user_id) + replace_previous_session(trans, None) + + +def replace_previous_session(trans, user): prev_galaxy_session = trans.get_galaxy_session() + # Invalidate previous session if prev_galaxy_session: prev_galaxy_session.is_valid = False - new_session = create_new_session(trans, prev_galaxy_session, None) + # Create new session + new_session = create_new_session(trans, prev_galaxy_session, user) trans.set_galaxy_session(new_session) trans.sa_session.add_all((prev_galaxy_session, new_session)) - trans.sa_session.flush() - - galaxy_user_id = prev_galaxy_session.user_id - if logout_all and galaxy_user_id is not None: - for other_galaxy_session in trans.sa_session.query(trans.app.model.GalaxySession).filter( - and_( - trans.app.model.GalaxySession.table.c.user_id == galaxy_user_id, - trans.app.model.GalaxySession.table.c.is_valid == true(), - trans.app.model.GalaxySession.table.c.id != prev_galaxy_session.id, - ) - ): - other_galaxy_session.is_valid = False - trans.sa_session.add(other_galaxy_session) - trans.sa_session.flush() + with transaction(trans.sa_session): + trans.sa_session.commit() set_auth_cookie(trans, new_session) + + +def invalidate_user_sessions(session, user_id): + stmt = ( + update(GalaxySession) + .values(is_valid=false()) + .where(GalaxySession.user_id == user_id) + .where(GalaxySession.is_valid == true()) + ) + session.execute(stmt) + with transaction(session): + session.commit() From 248b8bb90e2fcfca6abb19768da645383257dd0d Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 6 Oct 2023 10:17:58 -0400 Subject: [PATCH 4/4] SA2.0: Replacde flush with commit --- lib/tool_shed/webapp/api2/__init__.py | 4 +++- lib/tool_shed/webapp/api2/repositories.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/tool_shed/webapp/api2/__init__.py b/lib/tool_shed/webapp/api2/__init__.py index 6961c8407b93..3630038280f3 100644 --- a/lib/tool_shed/webapp/api2/__init__.py +++ b/lib/tool_shed/webapp/api2/__init__.py @@ -29,6 +29,7 @@ from galaxy.exceptions import AdminRequiredException from galaxy.managers.session import GalaxySessionManager from galaxy.managers.users import UserManager +from galaxy.model.base import transaction from galaxy.security.idencoding import IdEncodingHelper from galaxy.util import unicodify from galaxy.web.framework.decorators import require_admin_message @@ -331,7 +332,8 @@ def ensure_valid_session(trans: SessionRequestContext) -> None: # be needed. if prev_galaxy_session: sa_session.add(prev_galaxy_session) - sa_session.flush() + with transaction(sa_session): + sa_session.commit() def set_auth_cookie(trans: SessionRequestContext, session): diff --git a/lib/tool_shed/webapp/api2/repositories.py b/lib/tool_shed/webapp/api2/repositories.py index 776e4064a7ab..4c809e2c1cec 100644 --- a/lib/tool_shed/webapp/api2/repositories.py +++ b/lib/tool_shed/webapp/api2/repositories.py @@ -20,6 +20,7 @@ from starlette.datastructures import UploadFile as StarletteUploadFile from galaxy.exceptions import InsufficientPermissionsException +from galaxy.model.base import transaction from galaxy.webapps.galaxy.api import as_form from tool_shed.context import SessionRequestContext from tool_shed.managers.repositories import ( @@ -357,7 +358,8 @@ def set_malicious( repository_metadata = get_repository_metadata_for_management(trans, encoded_repository_id, changeset_revision) repository_metadata.malicious = True trans.sa_session.add(repository_metadata) - trans.sa_session.flush() + with transaction(trans.sa_session): + trans.sa_session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT) @router.delete( @@ -374,7 +376,8 @@ def unset_malicious( repository_metadata = get_repository_metadata_for_management(trans, encoded_repository_id, changeset_revision) repository_metadata.malicious = False trans.sa_session.add(repository_metadata) - trans.sa_session.flush() + with transaction(trans.sa_session): + trans.sa_session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT) @router.put( @@ -392,7 +395,8 @@ def set_deprecated( raise InsufficientPermissionsException("You do not have permission to update this repository.") repository.deprecated = True trans.sa_session.add(repository) - trans.sa_session.flush() + with transaction(trans.sa_session): + trans.sa_session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT) @router.delete( @@ -410,7 +414,8 @@ def unset_deprecated( raise InsufficientPermissionsException("You do not have permission to update this repository.") repository.deprecated = False trans.sa_session.add(repository) - trans.sa_session.flush() + with transaction(trans.sa_session): + trans.sa_session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT) @router.delete(