From 93ff02802889e798d2cb1fbb1380e7d31582209b Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 25 Mar 2024 17:38:27 -0400 Subject: [PATCH 1/3] Fix error in notes --- doc/source/releases/23.2_announce.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/releases/23.2_announce.rst b/doc/source/releases/23.2_announce.rst index 8b07b2ec1f95..9a6aa84198ea 100644 --- a/doc/source/releases/23.2_announce.rst +++ b/doc/source/releases/23.2_announce.rst @@ -42,7 +42,6 @@ Administration Notes * Configuration options are now available for carbon emissions reporting. (`#16307 `__) * The pgcleanup script has been modified to allow periodically deleting old datasets; operations can now be restricted to specific object store ids. (`#16340 `__) * Tool memory usage has been improved. (`#16536 `__) -* Documentation added for tool panel and panel view administration. (`#17078 `__) * Support added for cgroups-v2 (`#17169 `__) * Support added for configuring job metrics inline; documentation improved (`#17178 `__) From 2ce26862052a44c9ffe4045b15fc9dc156d69356 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 26 Mar 2024 10:54:29 +0100 Subject: [PATCH 2/3] Proxy Access-Control-* headers when using x-accel-redirect --- doc/source/admin/nginx.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/admin/nginx.md b/doc/source/admin/nginx.md index a943832d81e7..a76b8a6e564d 100644 --- a/doc/source/admin/nginx.md +++ b/doc/source/admin/nginx.md @@ -248,6 +248,9 @@ To enable it, add the following to your Galaxy's `server {}` block: location /_x_accel_redirect/ { internal; alias /; + # Add upstream response headers that would otherwise be omitted + add_header Access-Control-Allow-Origin $upstream_http_access_control_allow_origin; + add_header Access-Control-Allow-Methods $upstream_http_access_control_allow_methods; } ``` From 50d9d1f440139be0002c7eff619b22613c5a73d9 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 27 Mar 2024 19:15:37 +0100 Subject: [PATCH 3/3] Fix user login when duplicate UserRoleAssociation exists In https://github.com/galaxyproject/galaxy/commit/3fd5b0252f840dbb92aaebf31b9d21034b21c578 we changed the sqlalchemy query construct `one_or_none` method to use the core statement `.scalar_one_or_none`, which bypasses the identity map. Since there are a handful of legacy accounts with duplicate UserRoleAssociation rows on usegalaxy.{org,eu} the deduplication that occured via the entity map meant that no exception was raised, while for the core level construct this happened. Fortunately it's easy enough to simply add a distinct statement to return to the old behavior. Here's an ipython session to prove this: """ In [4]: ura = sa_session.query(UserRoleAssociation).filter_by(user_id=1, role_id=1).all() In [5]: ura Out[5]: [] In [6]: new_ura = UserRoleAssociation(sa_session.get(User, 1), sa_session.get(Role, 1)) In [7]: sa_session.add(new_ura) In [8]: sa_session.flush() In [9]: role = ( ...: sa_session.query(Role) ...: .filter( ...: and_( ...: UserRoleAssociation.table.c.user_id == 1, ...: Role.id == UserRoleAssociation.table.c.role_id, ...: Role.type == Role.types.PRIVATE, ...: ) ...: ) ...: .one_or_none() ...: ) In [10]: stmt = select(Role).where( ...: and_( ...: UserRoleAssociation.user_id == 1, ...: Role.id == UserRoleAssociation.role_id, ...: Role.type == Role.types.PRIVATE, ...: ) ...: ) ...: role = sa_session.execute(stmt).scalar_one_or_none() --------------------------------------------------------------------------- MultipleResultsFound Traceback (most recent call last) Cell In[10], line 8 1 stmt = select(Role).where( 2 and_( 3 UserRoleAssociation.user_id == 1, (...) 6 ) 7 ) ----> 8 role = sa_session.execute(stmt).scalar_one_or_none() File ~/src/galaxy/.venv/lib/python3.11/site-packages/sqlalchemy/engine/result.py:1225, in Result.scalar_one_or_none(self) 1212 def scalar_one_or_none(self): 1213 """Return exactly one scalar result or ``None``. 1214 1215 This is equivalent to calling :meth:`_engine.Result.scalars` and (...) 1223 1224 """ -> 1225 return self._only_one_row( 1226 raise_for_second_row=True, raise_for_none=False, scalar=True 1227 ) File ~/src/galaxy/.venv/lib/python3.11/site-packages/sqlalchemy/engine/result.py:614, in ResultInternal._only_one_row(self, raise_for_second_row, raise_for_none, scalar) 612 if next_row is not _NO_ROW: 613 self._soft_close(hard=True) --> 614 raise exc.MultipleResultsFound( 615 "Multiple rows were found when exactly one was required" 616 if raise_for_none 617 else "Multiple rows were found when one or none " 618 "was required" 619 ) 620 else: 621 next_row = _NO_ROW MultipleResultsFound: Multiple rows were found when one or none was required In [11]: stmt = select(Role).where( ...: and_( ...: UserRoleAssociation.user_id == 1, ...: Role.id == UserRoleAssociation.role_id, ...: Role.type == Role.types.PRIVATE, ...: ) ...: ).distinct() ...: role = sa_session.execute(stmt).scalar_one_or_none() """ I think that's what the sqlalchemy docs for [Query.one_or_none](https://docs.sqlalchemy.org/en/14/orm/query.html#sqlalchemy.orm.Query.one_or_none) mean to communicate with > Returns None if the query selects no rows. Raises sqlalchemy.orm.exc.MultipleResultsFound if multiple object identities are returned, or if multiple rows are returned for a query that returns only scalar values as opposed to full identity-mapped entities. Fixes https://github.com/galaxyproject/galaxy/issues/17848 --- lib/galaxy/model/security.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/galaxy/model/security.py b/lib/galaxy/model/security.py index dc5ed7f32592..837f2be86b26 100644 --- a/lib/galaxy/model/security.py +++ b/lib/galaxy/model/security.py @@ -746,12 +746,16 @@ def create_private_user_role(self, user): return self.get_private_user_role(user) def get_private_user_role(self, user, auto_create=False): - stmt = select(Role).where( - and_( - UserRoleAssociation.user_id == user.id, - Role.id == UserRoleAssociation.role_id, - Role.type == Role.types.PRIVATE, + stmt = ( + select(Role) + .where( + and_( + UserRoleAssociation.user_id == user.id, + Role.id == UserRoleAssociation.role_id, + Role.type == Role.types.PRIVATE, + ) ) + .distinct() ) role = self.sa_session.execute(stmt).scalar_one_or_none() if not role: