mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'release_23.1' into dev
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galaxyproject/galaxy-client",
|
||||
"version": "23.0.0",
|
||||
"version": "23.1.1",
|
||||
"description": "Galaxy client application build system",
|
||||
"keywords": [
|
||||
"galaxy"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
description="Add or modify the configuration that grants Galaxy to access your cloud-based resources."
|
||||
to="/user/cloud_auth" />
|
||||
<UserPreferencesElement
|
||||
v-if="isConfigLoaded && config.enable_oidc"
|
||||
v-if="isConfigLoaded && config.enable_oidc && !config.fixed_delegated_auth"
|
||||
id="manage-third-party-identities"
|
||||
icon="fa-id-card-o"
|
||||
title="Manage Third-Party Identities"
|
||||
|
||||
@@ -410,7 +410,7 @@ export function getRouter(Galaxy) {
|
||||
{
|
||||
path: "user/external_ids",
|
||||
component: ExternalIdentities,
|
||||
redirect: redirectAnon(),
|
||||
redirect: redirectIf(Galaxy.config.fixed_delegated_auth, "/") || redirectAnon(),
|
||||
},
|
||||
{
|
||||
path: "user/notifications",
|
||||
|
||||
@@ -741,6 +741,13 @@ class UniverseApplication(StructuredApp, GalaxyManagerApplication):
|
||||
self, self.config.oidc_config_file, self.config.oidc_backends_config_file
|
||||
)
|
||||
|
||||
# If there is only a single external authentication provider in use
|
||||
# TODO: Future work will expand on this and provide an interface for
|
||||
# multiple auth providers allowing explicit authenticated association.
|
||||
self.config.fixed_delegated_auth = (
|
||||
len(list(self.config.oidc)) == 1 and len(list(self.auth_manager.authenticators)) == 0
|
||||
)
|
||||
|
||||
if not self.config.enable_celery_tasks and self.config.history_audit_table_prune_interval > 0:
|
||||
self.prune_history_audit_task = IntervalTask(
|
||||
func=lambda: galaxy.model.HistoryAudit.prune(self.model.session),
|
||||
|
||||
@@ -218,17 +218,7 @@ class OIDCAuthnzBase(IdentityProvider):
|
||||
existing_user = trans.sa_session.query(User).filter_by(email=email).first()
|
||||
if not user:
|
||||
if existing_user:
|
||||
# If there is only a single external authentication
|
||||
# provider in use, trust the user provided and
|
||||
# automatically associate.
|
||||
# TODO: Future work will expand on this and provide an
|
||||
# interface for when there are multiple auth providers
|
||||
# allowing explicit authenticated association.
|
||||
if (
|
||||
trans.app.config.enable_oidc
|
||||
and len(trans.app.config.oidc) == 1
|
||||
and len(trans.app.auth_manager.authenticators) == 0
|
||||
):
|
||||
if trans.app.config.fixed_delegated_auth:
|
||||
user = existing_user
|
||||
else:
|
||||
message = f"There already exists a user with email {email}. To associate this external login, you must first be logged in as that existing account."
|
||||
@@ -260,7 +250,9 @@ class OIDCAuthnzBase(IdentityProvider):
|
||||
refresh_expiration_time=refresh_expiration_time,
|
||||
)
|
||||
label = self.config.label
|
||||
if existing_user and existing_user != user:
|
||||
if trans.app.config.fixed_delegated_auth:
|
||||
redirect_url = login_redirect_url
|
||||
elif existing_user and existing_user != user:
|
||||
redirect_url = (
|
||||
f"{login_redirect_url}user/external_ids"
|
||||
f"?email_exists={email}"
|
||||
|
||||
@@ -868,6 +868,7 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
|
||||
self.builds_file_path = os.path.join(self.tool_data_path, self.builds_file_path)
|
||||
self.len_file_path = os.path.join(self.tool_data_path, self.len_file_path)
|
||||
self.oidc: Dict[str, Dict] = {}
|
||||
self.fixed_delegated_auth: bool = False
|
||||
self.integrated_tool_panel_config = self._in_managed_config_dir(self.integrated_tool_panel_config)
|
||||
integrated_tool_panel_tracking_directory = kwargs.get("integrated_tool_panel_tracking_directory")
|
||||
if integrated_tool_panel_tracking_directory:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""Utilities to help job and tool code setup jobs."""
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from typing import (
|
||||
Any,
|
||||
cast,
|
||||
Dict,
|
||||
List,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
@@ -38,6 +40,27 @@ OutputHdasAndType = Dict[str, Tuple[DatasetInstance, DatasetPath]]
|
||||
OutputPaths = List[DatasetPath]
|
||||
|
||||
|
||||
class JobOutput(NamedTuple):
|
||||
output_name: str
|
||||
dataset: DatasetInstance
|
||||
dataset_path: DatasetPath
|
||||
|
||||
|
||||
class JobOutputs(threading.local):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.output_hdas_and_paths: Optional[OutputHdasAndType] = None
|
||||
self.output_paths: Optional[OutputPaths] = None
|
||||
|
||||
@property
|
||||
def populated(self) -> bool:
|
||||
return self.output_hdas_and_paths is not None
|
||||
|
||||
def set_job_outputs(self, job_outputs: List[JobOutput]) -> None:
|
||||
self.output_paths = [t[2] for t in job_outputs]
|
||||
self.output_hdas_and_paths = {t.output_name: (t.dataset, t.dataset_path) for t in job_outputs}
|
||||
|
||||
|
||||
class JobIO(Dictifiable):
|
||||
dict_collection_visible_keys = (
|
||||
"job_id",
|
||||
@@ -99,7 +122,6 @@ class JobIO(Dictifiable):
|
||||
user_context_instance = user_context
|
||||
self.user_context = user_context_instance
|
||||
self.sa_session = sa_session
|
||||
self.job = job
|
||||
self.job_id = job.id
|
||||
self.working_directory = working_directory
|
||||
self.outputs_directory = outputs_directory
|
||||
@@ -121,25 +143,33 @@ class JobIO(Dictifiable):
|
||||
self.is_task = is_task
|
||||
self.tool_source = tool_source
|
||||
self.tool_source_class = tool_source_class
|
||||
self._output_paths: Optional[OutputPaths] = None
|
||||
self._output_hdas_and_paths: Optional[OutputHdasAndType] = None
|
||||
self.job_outputs = JobOutputs()
|
||||
self._dataset_path_rewriter: Optional[DatasetPathRewriter] = None
|
||||
|
||||
@property
|
||||
def job(self):
|
||||
return self.sa_session.get(Job, self.job_id)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, path, sa_session):
|
||||
with open(path) as job_io_serialized:
|
||||
io_dict = json.load(job_io_serialized)
|
||||
return cls.from_dict(io_dict=io_dict, sa_session=sa_session)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, io_dict, sa_session):
|
||||
io_dict.pop("model_class")
|
||||
# Drop in 24.0
|
||||
io_dict.pop("model_class", None)
|
||||
job_id = io_dict.pop("job_id")
|
||||
job = sa_session.query(Job).get(job_id)
|
||||
return cls(sa_session=sa_session, job=job, **io_dict)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, io_dict, sa_session):
|
||||
# Drop in 24.0
|
||||
io_dict.pop("model_class", None)
|
||||
return cls(sa_session=sa_session, **io_dict)
|
||||
|
||||
def to_dict(self):
|
||||
io_dict = super().to_dict()
|
||||
# dict_for will always add `model_class`, we don't need or want it
|
||||
io_dict.pop("model_class")
|
||||
io_dict["user_context"] = self.user_context.to_dict()
|
||||
return io_dict
|
||||
|
||||
@@ -165,15 +195,15 @@ class JobIO(Dictifiable):
|
||||
|
||||
@property
|
||||
def output_paths(self) -> OutputPaths:
|
||||
if self._output_paths is None:
|
||||
if not self.job_outputs.populated:
|
||||
self.compute_outputs()
|
||||
return cast(OutputPaths, self._output_paths)
|
||||
return cast(OutputPaths, self.job_outputs.output_paths)
|
||||
|
||||
@property
|
||||
def output_hdas_and_paths(self) -> OutputHdasAndType:
|
||||
if self._output_hdas_and_paths is None:
|
||||
if not self.job_outputs.populated:
|
||||
self.compute_outputs()
|
||||
return cast(OutputHdasAndType, self._output_hdas_and_paths)
|
||||
return cast(OutputHdasAndType, self.job_outputs.output_hdas_and_paths)
|
||||
|
||||
def get_input_dataset_fnames(self, ds: DatasetInstance) -> List[str]:
|
||||
filenames = [ds.get_file_name()]
|
||||
@@ -241,22 +271,21 @@ class JobIO(Dictifiable):
|
||||
special = self.sa_session.query(JobExportHistoryArchive).filter_by(job=job).first()
|
||||
false_path = None
|
||||
|
||||
results = []
|
||||
job_outputs = []
|
||||
for da in job.output_datasets + job.output_library_datasets:
|
||||
da_false_path = dataset_path_rewriter.rewrite_dataset_path(da.dataset, "output")
|
||||
mutable = da.dataset.dataset.external_filename is None
|
||||
dataset_path = DatasetPath(
|
||||
da.dataset.dataset.id, da.dataset.get_file_name(), false_path=da_false_path, mutable=mutable
|
||||
)
|
||||
results.append((da.name, da.dataset, dataset_path))
|
||||
job_outputs.append(JobOutput(da.name, da.dataset, dataset_path))
|
||||
|
||||
self._output_paths = [t[2] for t in results]
|
||||
self._output_hdas_and_paths = {t[0]: t[1:] for t in results}
|
||||
if special:
|
||||
false_path = dataset_path_rewriter.rewrite_dataset_path(special, "output")
|
||||
dsp = DatasetPath(special.dataset.id, special.dataset.get_file_name(), false_path)
|
||||
self._output_paths.append(dsp)
|
||||
self._output_hdas_and_paths["output_file"] = (special.fda, dsp)
|
||||
job_outputs.append(JobOutput("output_file", special.fda, dsp))
|
||||
|
||||
self.job_outputs.set_job_outputs(job_outputs)
|
||||
|
||||
def get_output_file_id(self, file: str) -> Optional[int]:
|
||||
for dp in self.output_paths:
|
||||
|
||||
@@ -217,6 +217,7 @@ class ConfigSerializer(base.ModelSerializer):
|
||||
"instance_access_url": _use_config,
|
||||
"organization_name": _use_config,
|
||||
"organization_url": _use_config,
|
||||
"fixed_delegated_auth": _defaults_to(False),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1719,7 +1719,7 @@ it may be inconvenient to upload the entiry file and this can be used instead.
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
If this attribute is true then try to decompress files if needed. This applies to
|
||||
test assertions expressed with ``assert_contents`` or ``compare`` set to anything
|
||||
but ``sim_size``.
|
||||
but ``sim_size``.
|
||||
This flag is useful for testing compressed outputs that are non-deterministic
|
||||
despite having deterministic decompressed contents. By default, only files compressed
|
||||
with bz2, gzip and zip will be automatically decompressed.
|
||||
@@ -7389,7 +7389,7 @@ information according to a catalog.</xs:documentation>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="type" type="xrefType" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Type of reference - currently ``bio.tools`` and ``bioconductor`` are
|
||||
<xs:documentation xml:lang="en">Type of reference - currently ``bio.tools``, ``bioconductor``, and ``biii`` are
|
||||
the only supported options.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
@@ -7403,6 +7403,7 @@ the only supported options.</xs:documentation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="bio.tools"/>
|
||||
<xs:enumeration value="bioconductor"/>
|
||||
<xs:enumeration value="biii"/>
|
||||
<!--xs:enumeration value="whatelse"/-->
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
@@ -222,6 +222,12 @@ class WebApplication:
|
||||
rc = routes.request_config()
|
||||
rc.mapper = self.mapper
|
||||
rc.mapper_dict = map_match
|
||||
server_port = environ["SERVER_PORT"]
|
||||
if isinstance(server_port, int):
|
||||
# Workaround bug in the routes package, which would concatenate this
|
||||
# without casting to str in
|
||||
# https://github.com/bbangert/routes/blob/c4d5a5fb693ce8dc7cf5dbc591861acfc49d5c23/routes/__init__.py#L73
|
||||
environ["SERVER_PORT"] = str(server_port)
|
||||
rc.environ = environ
|
||||
# Setup the transaction
|
||||
trans = self.transaction_factory(environ)
|
||||
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galaxyproject/galaxy",
|
||||
"version": "1.0.0",
|
||||
"version": "23.1.0",
|
||||
"description": ".. figure:: https://galaxyproject.org/images/galaxy-logos/galaxy_project_logo.jpg :alt: Galaxy Logo",
|
||||
"main": "index.js",
|
||||
"devDependencies": {},
|
||||
@@ -18,8 +18,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/galaxyproject/galaxy#readme",
|
||||
"dependencies": {
|
||||
"@galaxyproject/galaxy-client": "^23.0.0",
|
||||
"cpy-cli": "^4.2.0",
|
||||
"vuedraggable": "^2.24.3"
|
||||
"@galaxyproject/galaxy-client": "^23.1.1",
|
||||
"cpy-cli": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ class TestCustosAuthnz(TestCase):
|
||||
self.trans.app.config.enable_oidc = True
|
||||
self.trans.app.config.oidc = []
|
||||
self.trans.app.auth_manager.authenticators = []
|
||||
self.trans.app.config.fixed_delegated_auth = False
|
||||
|
||||
@property
|
||||
def test_id_token(self):
|
||||
|
||||
Reference in New Issue
Block a user