From 1f9a2fa826e72d7d48a3e7500b63c2b7efaec714 Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Thu, 6 Jun 2024 12:49:35 +0200 Subject: [PATCH 01/12] make sure that all Linter subclasses are imported for listing them needed in planemo --- lib/galaxy/tool_util/lint.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index 599006457349..92a1ed291ae4 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -106,6 +106,7 @@ def list_listers(cls) -> List[str]: """ list the names of all linter derived from Linter """ + submodules.import_submodules(galaxy.tool_util.linters) return [s.__name__ for s in cls.__subclasses__()] From c05246396978a652e9fd8b67be164827224925af Mon Sep 17 00:00:00 2001 From: M Bernt Date: Thu, 6 Jun 2024 18:25:09 +0200 Subject: [PATCH 02/12] Fix typo --- lib/galaxy/tool_util/lint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index 92a1ed291ae4..926d7a69ea3f 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -102,7 +102,7 @@ def name(cls) -> str: return cls.__name__ @classmethod - def list_listers(cls) -> List[str]: + def list_linters(cls) -> List[str]: """ list the names of all linter derived from Linter """ From 0f5bbc05cdeeec7105685e8d1642155e5f986587 Mon Sep 17 00:00:00 2001 From: M Bernt Date: Thu, 6 Jun 2024 19:05:49 +0200 Subject: [PATCH 03/12] Add alias Co-authored-by: Nicola Soranzo --- lib/galaxy/tool_util/lint.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index 926d7a69ea3f..c798b4761a5a 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -109,6 +109,8 @@ def list_linters(cls) -> List[str]: submodules.import_submodules(galaxy.tool_util.linters) return [s.__name__ for s in cls.__subclasses__()] + list_listers = list_linters # deprecated alias + class LintMessage: """ From 089cc1f407d6c9646f4e9d337466100f51f975c2 Mon Sep 17 00:00:00 2001 From: M Bernt Date: Fri, 7 Jun 2024 09:57:48 +0200 Subject: [PATCH 04/12] Alias needs to be a classmethod --- lib/galaxy/tool_util/lint.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index c798b4761a5a..8c9f6c50adb5 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -109,6 +109,7 @@ def list_linters(cls) -> List[str]: submodules.import_submodules(galaxy.tool_util.linters) return [s.__name__ for s in cls.__subclasses__()] + @classmethod list_listers = list_linters # deprecated alias From 645370755ef0899e6802ffd01341acf02ec49673 Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Fri, 7 Jun 2024 10:55:04 +0200 Subject: [PATCH 05/12] make alias work --- lib/galaxy/tool_util/lint.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index 8c9f6c50adb5..4ae0976b5a97 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -101,7 +101,6 @@ def name(cls) -> str: """ return cls.__name__ - @classmethod def list_linters(cls) -> List[str]: """ list the names of all linter derived from Linter @@ -109,10 +108,12 @@ def list_linters(cls) -> List[str]: submodules.import_submodules(galaxy.tool_util.linters) return [s.__name__ for s in cls.__subclasses__()] - @classmethod list_listers = list_linters # deprecated alias +Linter.list_listers = classmethod(Linter.list_listers) + + class LintMessage: """ a message from the linter From 8beef0b4d7d2dd70fff8294b9c58c44e0fcb3a8b Mon Sep 17 00:00:00 2001 From: Nicola Soranzo Date: Fri, 7 Jun 2024 16:17:18 +0100 Subject: [PATCH 06/12] Fix ``list_listers`` alias definition to appease mypy --- lib/galaxy/tool_util/lint.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/tool_util/lint.py b/lib/galaxy/tool_util/lint.py index 4ae0976b5a97..3d2796a2927e 100644 --- a/lib/galaxy/tool_util/lint.py +++ b/lib/galaxy/tool_util/lint.py @@ -101,6 +101,7 @@ def name(cls) -> str: """ return cls.__name__ + @classmethod def list_linters(cls) -> List[str]: """ list the names of all linter derived from Linter @@ -108,10 +109,13 @@ def list_linters(cls) -> List[str]: submodules.import_submodules(galaxy.tool_util.linters) return [s.__name__ for s in cls.__subclasses__()] - list_listers = list_linters # deprecated alias + list_listers: Callable[[], List[str]] # deprecated alias -Linter.list_listers = classmethod(Linter.list_listers) +# Define the `list_listers` alias outside of the `Linter` class so that +# @classmethod's change to `list_linters`s signature has taken effect and mypy +# doesn't report an [assignment] error +Linter.list_listers = Linter.list_linters class LintMessage: From 0a2d50af872aa6c77ed238da92896bfe5a4613fd Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 11 Jun 2024 18:25:48 +0200 Subject: [PATCH 07/12] Drop unnecessary escaping for workflow name and annotation These shouldn't ever have been escaped before storing in the database, this should've always just been applied on the way out. We also don't need to do that anymore since we don't use `v-html` for these fields. Fixes https://github.com/galaxyproject/galaxy/issues/18354 --- lib/galaxy/webapps/galaxy/api/workflows.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py index 2fef4d09e3fe..fd93e27dbce0 100644 --- a/lib/galaxy/webapps/galaxy/api/workflows.py +++ b/lib/galaxy/webapps/galaxy/api/workflows.py @@ -22,7 +22,6 @@ status, ) from gxformat2._yaml import ordered_dump -from markupsafe import escape from pydantic import ( UUID1, UUID4, @@ -87,7 +86,6 @@ from galaxy.tools import recommendations from galaxy.tools.parameters import populate_state from galaxy.tools.parameters.workflow_utils import workflow_building_modes -from galaxy.util.sanitize_html import sanitize_html from galaxy.version import VERSION from galaxy.web import ( expose_api, @@ -270,7 +268,7 @@ def create(self, trans: GalaxyWebTransaction, payload=None, **kwd): ) import_source = "URL" except Exception: - raise exceptions.MessageException(f"Failed to open URL '{escape(archive_source)}'.") + raise exceptions.MessageException(f"Failed to open URL '{archive_source}'.") elif hasattr(archive_file, "file"): uploaded_file = archive_file.file uploaded_file_name = uploaded_file.name @@ -450,7 +448,7 @@ def update(self, trans: GalaxyWebTransaction, id, payload, **kwds): name_updated = new_workflow_name and new_workflow_name != stored_workflow.name steps_updated = "steps" in workflow_dict if name_updated and not steps_updated: - sanitized_name = sanitize_html(new_workflow_name or old_workflow.name) + sanitized_name = new_workflow_name or old_workflow.name if not sanitized_name: raise exceptions.MessageException("Workflow must have a valid name.") workflow = old_workflow.copy(user=trans.user) @@ -474,7 +472,7 @@ def update(self, trans: GalaxyWebTransaction, id, payload, **kwds): require_flush = True if "annotation" in workflow_dict and not steps_updated: - newAnnotation = sanitize_html(workflow_dict["annotation"]) + newAnnotation = workflow_dict["annotation"] self.add_item_annotation(trans.sa_session, trans.user, stored_workflow, newAnnotation) require_flush = True @@ -601,7 +599,7 @@ def __api_import_from_archive(self, trans: GalaxyWebTransaction, archive_data, s workflow = workflow.latest_workflow response = { - "message": f"Workflow '{escape(workflow.name)}' imported successfully.", + "message": f"Workflow '{workflow.name}' imported successfully.", "status": "success", "id": trans.security.encode_id(workflow_id), } From 4f5d81325eb817cbe865077222d64e82a568bab4 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 11 Jun 2024 19:35:20 +0200 Subject: [PATCH 08/12] Sanitize FormElement error messages v-html was introduced in https://github.com/galaxyproject/galaxy/commit/6682ca60fe64087a15d3e69c68a2054ca950d538 to show bold items. `FormElement` however is used so widely that it's hard to keep track on whether or not user-modifiable fields are shown, so better safe than sorry. --- client/src/components/Form/FormElement.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/src/components/Form/FormElement.vue b/client/src/components/Form/FormElement.vue index 17314b77cbcc..34baae64c846 100644 --- a/client/src/components/Form/FormElement.vue +++ b/client/src/components/Form/FormElement.vue @@ -3,9 +3,12 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faCaretSquareDown, faCaretSquareUp } from "@fortawesome/free-regular-svg-icons"; import { faArrowsAltH, faExclamation, faTimes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; +import { sanitize } from "dompurify"; import type { ComputedRef } from "vue"; import { computed, ref, useAttrs } from "vue"; +import { linkify } from "@/utils/utils"; + import type { FormParameterAttributes, FormParameterTypes, FormParameterValue } from "./parameterTypes"; import FormBoolean from "./Elements/FormBoolean.vue"; @@ -181,7 +184,9 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] ! :class="{ alert: hasAlert, 'alert-info': hasAlert }">
- +
From 4c9e6cffb0c1c5e62a4cb3bf7805df8a14cd1dd4 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:24:38 +0200 Subject: [PATCH 09/12] Raise appropriate error message when user's private role is missing on purge --- lib/galaxy/managers/users.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/galaxy/managers/users.py b/lib/galaxy/managers/users.py index 210566025061..297f0f9d4eec 100644 --- a/lib/galaxy/managers/users.py +++ b/lib/galaxy/managers/users.py @@ -197,6 +197,10 @@ def purge(self, user, flush=True): if not user.deleted: raise exceptions.MessageException("User '%s' has not been deleted, so they cannot be purged." % user.email) private_role = self.app.security_agent.get_private_user_role(user) + if private_role is None: + raise exceptions.InconsistentDatabase( + "User '%s' private role is missing while attempting to purge deleted user." % user.email + ) # Delete History for active_history in user.active_histories: self.session().refresh(active_history) From b89cb420917dd2d80bb3f3c832854c193173ac80 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:25:49 +0200 Subject: [PATCH 10/12] Commit user and private role in a single DB transaction --- lib/galaxy/managers/users.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/galaxy/managers/users.py b/lib/galaxy/managers/users.py index 297f0f9d4eec..cebfe6f4d6ca 100644 --- a/lib/galaxy/managers/users.py +++ b/lib/galaxy/managers/users.py @@ -133,7 +133,7 @@ def create(self, email=None, username=None, password=None, **kwargs): Create a new user. """ self._error_on_duplicate_email(email) - user = self.model_class(email=email) + user = User(email=email) if password: user.set_password_cleartext(password) else: @@ -144,15 +144,13 @@ def create(self, email=None, username=None, password=None, **kwargs): else: # Activation is off, every new user is active by default. user.active = True - self.session().add(user) + session = self.session() + session.add(user) try: - session = self.session() - with transaction(session): - session.commit() - # TODO:?? flush needed for permissions below? If not, make optional + # Creating a private role will commit the session + user.attempt_create_private_role() except exc.IntegrityError as db_err: raise exceptions.Conflict(str(db_err)) - self.app.security_agent.create_user_role(user, self.app) return user def delete(self, user, flush=True): From 0258be8856b66095726ab3a5fa1d9fa4e1d81063 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:46:55 +0200 Subject: [PATCH 11/12] Restore model_class for creating user as this code is shared with Toolshed --- lib/galaxy/managers/users.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/managers/users.py b/lib/galaxy/managers/users.py index cebfe6f4d6ca..56872047cdc6 100644 --- a/lib/galaxy/managers/users.py +++ b/lib/galaxy/managers/users.py @@ -133,7 +133,7 @@ def create(self, email=None, username=None, password=None, **kwargs): Create a new user. """ self._error_on_duplicate_email(email) - user = User(email=email) + user = self.model_class(email=email) if password: user.set_password_cleartext(password) else: @@ -148,7 +148,7 @@ def create(self, email=None, username=None, password=None, **kwargs): session.add(user) try: # Creating a private role will commit the session - user.attempt_create_private_role() + self.app.security_agent.create_user_role(user, self.app) except exc.IntegrityError as db_err: raise exceptions.Conflict(str(db_err)) return user From cc931e1d624f5df5b6aebe2fd79abe3bab5d9814 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:54:01 +0200 Subject: [PATCH 12/12] Fix auto-creation of private role for new users --- lib/galaxy/model/security.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/galaxy/model/security.py b/lib/galaxy/model/security.py index d8dbde46c44b..a8278d69557f 100644 --- a/lib/galaxy/model/security.py +++ b/lib/galaxy/model/security.py @@ -765,6 +765,9 @@ def create_private_user_role(self, user): return self.get_private_user_role(user) def get_private_user_role(self, user, auto_create=False): + if auto_create and user.id is None: + # New user, directly create private role + return self.create_private_user_role(user) stmt = ( select(Role) .where(