rucio 37.1.0.post1__py3-none-any.whl → 37.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rucio might be problematic. Click here for more details.
- rucio/common/plugins.py +1 -1
- rucio/common/utils.py +17 -10
- rucio/core/did.py +32 -4
- rucio/core/did_meta_plugins/did_column_meta.py +1 -1
- rucio/core/replica.py +1 -1
- rucio/db/sqla/util.py +1 -1
- rucio/gateway/authentication.py +58 -88
- rucio/gateway/config.py +63 -75
- rucio/gateway/credential.py +11 -17
- rucio/gateway/did.py +245 -329
- rucio/gateway/dirac.py +33 -34
- rucio/gateway/exporter.py +27 -30
- rucio/gateway/heartbeat.py +15 -19
- rucio/gateway/importer.py +12 -14
- rucio/gateway/lifetime_exception.py +16 -24
- rucio/gateway/lock.py +27 -40
- rucio/gateway/meta_conventions.py +19 -28
- rucio/gateway/quarantined_replica.py +24 -27
- rucio/gateway/replica.py +223 -226
- rucio/gateway/rse.py +191 -218
- rucio/gateway/rule.py +115 -146
- rucio/gateway/scope.py +18 -25
- rucio/gateway/trace.py +48 -0
- rucio/gateway/vo.py +32 -37
- rucio/vcsversion.py +3 -3
- rucio/web/rest/flaskapi/v1/accounts.py +2 -2
- rucio/web/rest/flaskapi/v1/auth.py +15 -0
- rucio/web/rest/flaskapi/v1/common.py +20 -0
- rucio/web/rest/flaskapi/v1/config.py +7 -7
- rucio/web/rest/flaskapi/v1/credentials.py +20 -13
- rucio/web/rest/flaskapi/v1/dids.py +55 -55
- rucio/web/rest/flaskapi/v1/dirac.py +2 -2
- rucio/web/rest/flaskapi/v1/export.py +1 -1
- rucio/web/rest/flaskapi/v1/heartbeats.py +3 -3
- rucio/web/rest/flaskapi/v1/import.py +1 -1
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +5 -5
- rucio/web/rest/flaskapi/v1/locks.py +4 -4
- rucio/web/rest/flaskapi/v1/main.py +17 -10
- rucio/web/rest/flaskapi/v1/meta_conventions.py +3 -3
- rucio/web/rest/flaskapi/v1/replicas.py +31 -30
- rucio/web/rest/flaskapi/v1/rses.py +37 -37
- rucio/web/rest/flaskapi/v1/rules.py +15 -15
- rucio/web/rest/flaskapi/v1/scopes.py +3 -3
- rucio/web/rest/flaskapi/v1/traces.py +75 -77
- rucio/web/rest/flaskapi/v1/vos.py +5 -7
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/METADATA +1 -2
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/RECORD +106 -105
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/WHEEL +1 -1
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/requirements.server.txt +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-admin +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-atropos +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-auditor +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-automatix +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-cache-client +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-receiver +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-dumper +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-follower +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-hermes +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-kronos +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-minos +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-necromancer +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-reaper +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-transmogrifier +0 -0
- {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/top_level.txt +0 -0
rucio/common/plugins.py
CHANGED
|
@@ -156,7 +156,7 @@ class PolicyPackageAlgorithms:
|
|
|
156
156
|
# import from utils here to avoid circular import
|
|
157
157
|
|
|
158
158
|
env_name = 'RUCIO_POLICY_PACKAGE' + ('' if not vo else '_' + vo.upper())
|
|
159
|
-
package =
|
|
159
|
+
package = os.getenv(env_name, "")
|
|
160
160
|
if not package:
|
|
161
161
|
package = str(config.config_get('policy', 'package' + ('' if not vo else '-' + vo)))
|
|
162
162
|
|
rucio/common/utils.py
CHANGED
|
@@ -595,20 +595,17 @@ class ScopeExtractionAlgorithms(PolicyPackageAlgorithms):
|
|
|
595
595
|
@staticmethod
|
|
596
596
|
def extract_scope_default(did: str, scopes: Optional['Sequence[str]']) -> 'Sequence[str]':
|
|
597
597
|
"""
|
|
598
|
-
Default
|
|
598
|
+
Default scope extraction algorithm. Extracts the scope from the DID.
|
|
599
599
|
|
|
600
600
|
:param did: The DID to extract the scope from.
|
|
601
|
+
:param scopes: Not used in the default algorithm.
|
|
601
602
|
|
|
602
|
-
:returns: A tuple containing the extracted scope and the
|
|
603
|
+
:returns: A tuple containing the extracted scope and the name.
|
|
603
604
|
"""
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
if name.endswith('/'):
|
|
609
|
-
name = name[:-1]
|
|
610
|
-
return scope, name
|
|
611
|
-
else:
|
|
605
|
+
|
|
606
|
+
# This block is ATLAS specific, to be removed in the future.
|
|
607
|
+
# More info at https://github.com/rucio/rucio/pull/7521
|
|
608
|
+
if did.find(':') == -1:
|
|
612
609
|
scope = did.split('.')[0]
|
|
613
610
|
if did.startswith('user') or did.startswith('group'):
|
|
614
611
|
scope = ".".join(did.split('.')[0:2])
|
|
@@ -616,6 +613,16 @@ class ScopeExtractionAlgorithms(PolicyPackageAlgorithms):
|
|
|
616
613
|
did = did[:-1]
|
|
617
614
|
return scope, did
|
|
618
615
|
|
|
616
|
+
parts = did.split(':')
|
|
617
|
+
if len(parts) != 2:
|
|
618
|
+
msg = f"Cannot extract scope and name from DID {did}. The DID should have exactly one colon but found {len(parts)} colons."
|
|
619
|
+
raise RucioException(msg)
|
|
620
|
+
scope, name = parts
|
|
621
|
+
if not scope or not name:
|
|
622
|
+
msg = f"Cannot extract scope and name from DID {did}. Found empty scope or name."
|
|
623
|
+
raise RucioException(msg)
|
|
624
|
+
return scope, name
|
|
625
|
+
|
|
619
626
|
@staticmethod
|
|
620
627
|
def extract_scope_dirac(did: str, scopes: Optional['Sequence[str]']) -> 'Sequence[str]':
|
|
621
628
|
# Default dirac scope extract algorithm. Scope is the second element in the LFN or the first one (VO name)
|
rucio/core/did.py
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import logging
|
|
16
16
|
import random
|
|
17
17
|
from datetime import datetime, timedelta
|
|
18
|
-
from enum import Enum
|
|
19
18
|
from hashlib import md5
|
|
20
19
|
from re import match
|
|
21
20
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
@@ -254,6 +253,7 @@ def attach_dids(
|
|
|
254
253
|
name: str,
|
|
255
254
|
dids: "Sequence[Mapping[str, Any]]",
|
|
256
255
|
account: "InternalAccount",
|
|
256
|
+
ignore_duplicate: bool = False,
|
|
257
257
|
rse_id: Optional[str] = None,
|
|
258
258
|
*,
|
|
259
259
|
session: "Session",
|
|
@@ -265,10 +265,11 @@ def attach_dids(
|
|
|
265
265
|
:param name: The data identifier name.
|
|
266
266
|
:param dids: The content.
|
|
267
267
|
:param account: The account owner.
|
|
268
|
+
:param ignore_duplicate: If True, ignore duplicate entries.
|
|
268
269
|
:param rse_id: The RSE id for the replicas.
|
|
269
270
|
:param session: The database session in use.
|
|
270
271
|
"""
|
|
271
|
-
return attach_dids_to_dids(attachments=[{'scope': scope, 'name': name, 'dids': dids, 'rse_id': rse_id}], account=account, session=session)
|
|
272
|
+
return attach_dids_to_dids(attachments=[{'scope': scope, 'name': name, 'dids': dids, 'rse_id': rse_id}], account=account, ignore_duplicate=ignore_duplicate, session=session)
|
|
272
273
|
|
|
273
274
|
|
|
274
275
|
@transactional_session
|
|
@@ -340,6 +341,7 @@ def attach_dids_to_dids(
|
|
|
340
341
|
collections_temp_table=children_temp_table,
|
|
341
342
|
collections=children,
|
|
342
343
|
account=account,
|
|
344
|
+
ignore_duplicate=ignore_duplicate,
|
|
343
345
|
session=session)
|
|
344
346
|
update_parent = True
|
|
345
347
|
|
|
@@ -669,6 +671,7 @@ def __add_collections_to_container(
|
|
|
669
671
|
collections_temp_table: Any,
|
|
670
672
|
collections: "Mapping[tuple[InternalScope, str], Mapping[str, Any]]",
|
|
671
673
|
account: "InternalAccount",
|
|
674
|
+
ignore_duplicate: bool = False,
|
|
672
675
|
*,
|
|
673
676
|
session: "Session"
|
|
674
677
|
) -> None:
|
|
@@ -678,6 +681,7 @@ def __add_collections_to_container(
|
|
|
678
681
|
:param parent_did: the DataIdentifier object of the parent did
|
|
679
682
|
:param collections: .
|
|
680
683
|
:param account: The account owner.
|
|
684
|
+
:param ignore_duplicate: If True, ignore duplicate entries.
|
|
681
685
|
:param session: The database session in use.
|
|
682
686
|
"""
|
|
683
687
|
|
|
@@ -695,9 +699,27 @@ def __add_collections_to_container(
|
|
|
695
699
|
and_(models.DataIdentifier.scope == collections_temp_table.scope,
|
|
696
700
|
models.DataIdentifier.name == collections_temp_table.name),
|
|
697
701
|
)
|
|
702
|
+
if ignore_duplicate:
|
|
703
|
+
stmt = stmt.add_columns(
|
|
704
|
+
models.DataIdentifierAssociation.scope.label("ignore_duplicate_parent_scope"),
|
|
705
|
+
models.DataIdentifierAssociation.name.label("ignore_duplicate_parent_name"),
|
|
706
|
+
collections_temp_table.scope.label("ignore_duplicate_child_scope"),
|
|
707
|
+
collections_temp_table.name.label("ignore_duplicate_child_name"),
|
|
708
|
+
).outerjoin_from(
|
|
709
|
+
collections_temp_table,
|
|
710
|
+
models.DataIdentifierAssociation,
|
|
711
|
+
and_(
|
|
712
|
+
models.DataIdentifierAssociation.scope == parent_did.scope,
|
|
713
|
+
models.DataIdentifierAssociation.name == parent_did.name,
|
|
714
|
+
models.DataIdentifierAssociation.child_scope == collections_temp_table.scope,
|
|
715
|
+
models.DataIdentifierAssociation.child_name == collections_temp_table.name,
|
|
716
|
+
),
|
|
717
|
+
)
|
|
698
718
|
|
|
699
719
|
container_parents = None
|
|
700
720
|
child_type = None
|
|
721
|
+
ignore_duplicate_existing_attachments = set()
|
|
722
|
+
|
|
701
723
|
for row in session.execute(stmt):
|
|
702
724
|
|
|
703
725
|
if row.did_scope is None:
|
|
@@ -719,8 +741,14 @@ def __add_collections_to_container(
|
|
|
719
741
|
if (row.scope, row.name) in container_parents:
|
|
720
742
|
raise exception.UnsupportedOperation('Circular attachment detected. %s:%s is already a parent of %s:%s' % (row.scope, row.name, parent_did.scope, parent_did.name))
|
|
721
743
|
|
|
744
|
+
if ignore_duplicate and row.ignore_duplicate_parent_scope is not None:
|
|
745
|
+
ignore_duplicate_existing_attachments.add((row.ignore_duplicate_parent_scope, row.ignore_duplicate_parent_name, row.ignore_duplicate_child_scope, row.ignore_duplicate_child_name))
|
|
746
|
+
|
|
722
747
|
messages = []
|
|
723
748
|
for c in collections.values():
|
|
749
|
+
if ignore_duplicate and (parent_did.scope, parent_did.name, c['scope'], c['name']) in ignore_duplicate_existing_attachments:
|
|
750
|
+
continue
|
|
751
|
+
|
|
724
752
|
did_asso = models.DataIdentifierAssociation(
|
|
725
753
|
scope=parent_did.scope,
|
|
726
754
|
name=parent_did.name,
|
|
@@ -1320,7 +1348,7 @@ def list_new_dids(
|
|
|
1320
1348
|
select_stmt = select_stmt.where(
|
|
1321
1349
|
models.DataIdentifier.did_type == DIDType[did_type]
|
|
1322
1350
|
)
|
|
1323
|
-
elif isinstance(did_type,
|
|
1351
|
+
elif isinstance(did_type, DIDType):
|
|
1324
1352
|
select_stmt = select_stmt.where(
|
|
1325
1353
|
models.DataIdentifier.did_type == did_type
|
|
1326
1354
|
)
|
|
@@ -2268,7 +2296,7 @@ def set_status(
|
|
|
2268
2296
|
@read_session
|
|
2269
2297
|
def list_dids(
|
|
2270
2298
|
scope: "InternalScope",
|
|
2271
|
-
filters: "
|
|
2299
|
+
filters: "Iterable[dict[Any, Any]]",
|
|
2272
2300
|
did_type: Literal['all', 'collection', 'dataset', 'container', 'file'] = 'collection',
|
|
2273
2301
|
ignore_case: bool = False,
|
|
2274
2302
|
limit: Optional[int] = None,
|
rucio/core/replica.py
CHANGED
|
@@ -243,7 +243,7 @@ def __exist_replicas(
|
|
|
243
243
|
|
|
244
244
|
@read_session
|
|
245
245
|
def list_bad_replicas_status(
|
|
246
|
-
state: BadFilesStatus = BadFilesStatus.BAD,
|
|
246
|
+
state: Optional[BadFilesStatus] = BadFilesStatus.BAD,
|
|
247
247
|
rse_id: Optional[str] = None,
|
|
248
248
|
younger_than: Optional[datetime] = None,
|
|
249
249
|
older_than: Optional[datetime] = None,
|
rucio/db/sqla/util.py
CHANGED
|
@@ -337,7 +337,7 @@ def list_oracle_global_temp_tables(session: "Session") -> list[str]:
|
|
|
337
337
|
global_temp_tables = [
|
|
338
338
|
str(t[0]).upper()
|
|
339
339
|
for t in session.execute(
|
|
340
|
-
text(
|
|
340
|
+
text("SELECT /*+ OPT_PARAM('OPTIMIZER_FEATURES_ENABLE', '11.2.0.4') */ UPPER(table_name) "
|
|
341
341
|
'FROM all_tables '
|
|
342
342
|
'WHERE OWNER = :owner AND IOT_NAME IS NULL AND DURATION IS NOT NULL'),
|
|
343
343
|
dict(owner=models.BASE.metadata.schema.upper())
|
rucio/gateway/authentication.py
CHANGED
|
@@ -12,27 +12,21 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from typing import
|
|
15
|
+
from typing import Any, Optional
|
|
16
16
|
|
|
17
17
|
from rucio.common import exception
|
|
18
18
|
from rucio.common.types import InternalAccount, TokenDict
|
|
19
19
|
from rucio.common.utils import gateway_update_return_dict
|
|
20
20
|
from rucio.core import authentication, identity, oidc
|
|
21
|
-
from rucio.db.sqla.constants import IdentityType
|
|
22
|
-
from rucio.db.sqla.session import
|
|
21
|
+
from rucio.db.sqla.constants import DatabaseOperationType, IdentityType
|
|
22
|
+
from rucio.db.sqla.session import db_session
|
|
23
23
|
from rucio.gateway import permission
|
|
24
24
|
|
|
25
|
-
if TYPE_CHECKING:
|
|
26
|
-
from sqlalchemy.orm import Session
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
@transactional_session
|
|
30
26
|
def refresh_cli_auth_token(
|
|
31
27
|
token_string: str,
|
|
32
28
|
account: str,
|
|
33
29
|
vo: str = 'def',
|
|
34
|
-
*,
|
|
35
|
-
session: "Session"
|
|
36
30
|
) -> Optional[tuple[str, int]]:
|
|
37
31
|
"""
|
|
38
32
|
Checks if there is active refresh token and if so returns
|
|
@@ -40,20 +34,18 @@ def refresh_cli_auth_token(
|
|
|
40
34
|
refresh and returns new access token.
|
|
41
35
|
:param token_string: token string
|
|
42
36
|
:param account: Rucio account for which token refresh should be considered
|
|
43
|
-
:param session: The database session in use.
|
|
44
37
|
|
|
45
38
|
:return: tuple of (access token, expiration epoch), None otherswise
|
|
46
39
|
"""
|
|
47
40
|
internal_account = InternalAccount(account, vo=vo)
|
|
48
|
-
|
|
41
|
+
|
|
42
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
43
|
+
return oidc.refresh_cli_auth_token(token_string, internal_account, session=session)
|
|
49
44
|
|
|
50
45
|
|
|
51
|
-
@transactional_session
|
|
52
46
|
def redirect_auth_oidc(
|
|
53
47
|
authn_code: str,
|
|
54
48
|
fetchtoken: bool = False,
|
|
55
|
-
*,
|
|
56
|
-
session: "Session"
|
|
57
49
|
) -> Optional[str]:
|
|
58
50
|
"""
|
|
59
51
|
Finds the Authentication URL in the Rucio DB oauth_requests table
|
|
@@ -63,21 +55,18 @@ def redirect_auth_oidc(
|
|
|
63
55
|
authorization securely to IdP via Rucio Auth server through a browser.
|
|
64
56
|
:param fetchtoken: If True, valid token temporarily saved in the oauth_requests table
|
|
65
57
|
will be returned. If False, redirection URL is returned.
|
|
66
|
-
:param session: The database session in use.
|
|
67
58
|
|
|
68
59
|
:returns: result of the query (authorization URL or a
|
|
69
60
|
token if a user asks with the correct code) or None.
|
|
70
61
|
Exception thrown in case of an unexpected crash.
|
|
71
62
|
"""
|
|
72
|
-
|
|
63
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
64
|
+
return authentication.redirect_auth_oidc(authn_code, fetchtoken, session=session)
|
|
73
65
|
|
|
74
66
|
|
|
75
|
-
@transactional_session
|
|
76
67
|
def get_auth_oidc(
|
|
77
68
|
account: str,
|
|
78
69
|
vo: str = 'def',
|
|
79
|
-
*,
|
|
80
|
-
session: "Session",
|
|
81
70
|
**kwargs
|
|
82
71
|
) -> str:
|
|
83
72
|
"""
|
|
@@ -107,7 +96,6 @@ def get_auth_oidc(
|
|
|
107
96
|
:param refresh_lifetime: specifies how long the OAuth daemon should
|
|
108
97
|
be refreshing this token. Default is 96 hours.
|
|
109
98
|
:param ip: IP address of the client as a string.
|
|
110
|
-
:param session: The database session in use.
|
|
111
99
|
|
|
112
100
|
:returns: User & Rucio OIDC Client specific Authorization or Redirection URL as a string
|
|
113
101
|
OR a redirection url to be used in user's browser for authentication.
|
|
@@ -115,16 +103,14 @@ def get_auth_oidc(
|
|
|
115
103
|
# no permission layer for the moment !
|
|
116
104
|
|
|
117
105
|
internal_account = InternalAccount(account, vo=vo)
|
|
118
|
-
|
|
106
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
107
|
+
return oidc.get_auth_oidc(internal_account, session=session, **kwargs)
|
|
119
108
|
|
|
120
109
|
|
|
121
|
-
@transactional_session
|
|
122
110
|
def get_token_oidc(
|
|
123
111
|
auth_query_string: str,
|
|
124
112
|
ip: Optional[str] = None,
|
|
125
|
-
|
|
126
|
-
session: "Session"
|
|
127
|
-
) -> Optional[dict[str, Optional[Union[str, bool]]]]:
|
|
113
|
+
) -> Optional[dict[str, Any]]:
|
|
128
114
|
"""
|
|
129
115
|
After Rucio User got redirected to Rucio /auth/oidc_token (or /auth/oidc_code)
|
|
130
116
|
REST endpoints with authz code and session state encoded within the URL.
|
|
@@ -132,17 +118,16 @@ def get_token_oidc(
|
|
|
132
118
|
|
|
133
119
|
:param auth_query_string: IdP redirection URL query string (AuthZ code & user session state).
|
|
134
120
|
:param ip: IP address of the client as a string.
|
|
135
|
-
:param session: The database session in use.
|
|
136
121
|
|
|
137
122
|
:returns: One of the following tuples: ("fetchcode", <code>); ("token", <token>);
|
|
138
123
|
("polling", True); The result depends on the authentication strategy being used
|
|
139
124
|
(no auto, auto, polling).
|
|
140
125
|
"""
|
|
141
126
|
# no permission layer for the moment !
|
|
142
|
-
|
|
127
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
128
|
+
return oidc.get_token_oidc(auth_query_string, ip, session=session)
|
|
143
129
|
|
|
144
130
|
|
|
145
|
-
@transactional_session
|
|
146
131
|
def get_auth_token_user_pass(
|
|
147
132
|
account: str,
|
|
148
133
|
username: str,
|
|
@@ -150,8 +135,6 @@ def get_auth_token_user_pass(
|
|
|
150
135
|
appid: str,
|
|
151
136
|
ip: Optional[str] = None,
|
|
152
137
|
vo: str = 'def',
|
|
153
|
-
*,
|
|
154
|
-
session: "Session"
|
|
155
138
|
) -> Optional[TokenDict]:
|
|
156
139
|
"""
|
|
157
140
|
Authenticate a Rucio account temporarily via username and password.
|
|
@@ -164,30 +147,27 @@ def get_auth_token_user_pass(
|
|
|
164
147
|
:param appid: The application identifier as a string.
|
|
165
148
|
:param ip: IP address of the client as a string.
|
|
166
149
|
:param vo: The VO to act on.
|
|
167
|
-
:param session: The database session in use.
|
|
168
150
|
|
|
169
151
|
:returns: A dict with token and expires_at entries.
|
|
170
152
|
"""
|
|
171
153
|
|
|
172
154
|
kwargs = {'account': account, 'username': username, 'password': password}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
155
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
156
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_user_pass', kwargs=kwargs, session=session)
|
|
157
|
+
if not auth_result.allowed:
|
|
158
|
+
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (username, account, auth_result.message))
|
|
176
159
|
|
|
177
|
-
|
|
160
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
178
161
|
|
|
179
|
-
|
|
162
|
+
return authentication.get_auth_token_user_pass(internal_account, username, password, appid, ip, session=session)
|
|
180
163
|
|
|
181
164
|
|
|
182
|
-
@transactional_session
|
|
183
165
|
def get_auth_token_gss(
|
|
184
166
|
account: str,
|
|
185
167
|
gsscred: str,
|
|
186
168
|
appid: str,
|
|
187
169
|
ip: Optional[str] = None,
|
|
188
170
|
vo: str = 'def',
|
|
189
|
-
*,
|
|
190
|
-
session: "Session"
|
|
191
171
|
) -> Optional[TokenDict]:
|
|
192
172
|
"""
|
|
193
173
|
Authenticate a Rucio account temporarily via a GSS token.
|
|
@@ -199,30 +179,27 @@ def get_auth_token_gss(
|
|
|
199
179
|
:param appid: The application identifier as a string.
|
|
200
180
|
:param ip: IP address of the client as a string.
|
|
201
181
|
:param vo: The VO to act on.
|
|
202
|
-
:param session: The database session in use.
|
|
203
182
|
|
|
204
183
|
:returns: A dict with token and expires_at entries.
|
|
205
184
|
"""
|
|
206
185
|
|
|
207
186
|
kwargs = {'account': account, 'gsscred': gsscred}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
187
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
188
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_gss', kwargs=kwargs, session=session)
|
|
189
|
+
if not auth_result.allowed:
|
|
190
|
+
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (gsscred, account, auth_result.message))
|
|
211
191
|
|
|
212
|
-
|
|
192
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
213
193
|
|
|
214
|
-
|
|
194
|
+
return authentication.get_auth_token_gss(internal_account, gsscred, appid, ip, session=session)
|
|
215
195
|
|
|
216
196
|
|
|
217
|
-
@transactional_session
|
|
218
197
|
def get_auth_token_x509(
|
|
219
198
|
account: Optional[str],
|
|
220
199
|
dn: str,
|
|
221
200
|
appid: str,
|
|
222
201
|
ip: Optional[str] = None,
|
|
223
202
|
vo: str = 'def',
|
|
224
|
-
*,
|
|
225
|
-
session: "Session"
|
|
226
203
|
) -> Optional[TokenDict]:
|
|
227
204
|
"""
|
|
228
205
|
Authenticate a Rucio account temporarily via an x509 certificate.
|
|
@@ -234,7 +211,6 @@ def get_auth_token_x509(
|
|
|
234
211
|
:param appid: The application identifier as a string.
|
|
235
212
|
:param ip: IP address of the client as a string.
|
|
236
213
|
:param vo: The VO to act on.
|
|
237
|
-
:param session: The database session in use.
|
|
238
214
|
|
|
239
215
|
:returns: A dict with token and expires_at entries.
|
|
240
216
|
"""
|
|
@@ -243,24 +219,23 @@ def get_auth_token_x509(
|
|
|
243
219
|
account = identity.get_default_account(dn, IdentityType.X509).external
|
|
244
220
|
|
|
245
221
|
kwargs = {'account': account, 'dn': dn}
|
|
246
|
-
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_x509', kwargs=kwargs, session=session)
|
|
247
|
-
if not auth_result.allowed:
|
|
248
|
-
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (dn, account, auth_result.message))
|
|
249
222
|
|
|
250
|
-
|
|
223
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
224
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_x509', kwargs=kwargs, session=session)
|
|
225
|
+
if not auth_result.allowed:
|
|
226
|
+
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (dn, account, auth_result.message))
|
|
227
|
+
|
|
228
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
251
229
|
|
|
252
|
-
|
|
230
|
+
return authentication.get_auth_token_x509(internal_account, dn, appid, ip, session=session)
|
|
253
231
|
|
|
254
232
|
|
|
255
|
-
@transactional_session
|
|
256
233
|
def get_auth_token_ssh(
|
|
257
234
|
account: str,
|
|
258
235
|
signature: str,
|
|
259
236
|
appid: str,
|
|
260
237
|
ip: Optional[str] = None,
|
|
261
238
|
vo: str = 'def',
|
|
262
|
-
*,
|
|
263
|
-
session: "Session"
|
|
264
239
|
) -> Optional[TokenDict]:
|
|
265
240
|
"""
|
|
266
241
|
Authenticate a Rucio account temporarily via SSH key exchange.
|
|
@@ -272,29 +247,27 @@ def get_auth_token_ssh(
|
|
|
272
247
|
:param appid: The application identifier as a string.
|
|
273
248
|
:param ip: IP address of the client as a string.
|
|
274
249
|
:param vo: The VO to act on.
|
|
275
|
-
:param session: The database session in use.
|
|
276
250
|
|
|
277
251
|
:returns: A dict with token and expires_at entries.
|
|
278
252
|
"""
|
|
279
253
|
|
|
280
254
|
kwargs = {'account': account, 'signature': signature}
|
|
281
|
-
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_ssh', kwargs=kwargs, session=session)
|
|
282
|
-
if not auth_result.allowed:
|
|
283
|
-
raise exception.AccessDenied('User with provided signature can not log to account %s. %s' % (account, auth_result.message))
|
|
284
255
|
|
|
285
|
-
|
|
256
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
257
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_ssh', kwargs=kwargs, session=session)
|
|
258
|
+
if not auth_result.allowed:
|
|
259
|
+
raise exception.AccessDenied('User with provided signature can not log to account %s. %s' % (account, auth_result.message))
|
|
260
|
+
|
|
261
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
286
262
|
|
|
287
|
-
|
|
263
|
+
return authentication.get_auth_token_ssh(internal_account, signature, appid, ip, session=session)
|
|
288
264
|
|
|
289
265
|
|
|
290
|
-
@transactional_session
|
|
291
266
|
def get_ssh_challenge_token(
|
|
292
267
|
account: str,
|
|
293
268
|
appid: str,
|
|
294
269
|
ip: Optional[str] = None,
|
|
295
270
|
vo: str = 'def',
|
|
296
|
-
*,
|
|
297
|
-
session: "Session"
|
|
298
271
|
) -> Optional[TokenDict]:
|
|
299
272
|
"""
|
|
300
273
|
Get a challenge token for subsequent SSH public key authentication.
|
|
@@ -305,30 +278,28 @@ def get_ssh_challenge_token(
|
|
|
305
278
|
:param appid: The application identifier as a string.
|
|
306
279
|
:param ip: IP address of the client as a string.
|
|
307
280
|
:param vo: The VO to act on.
|
|
308
|
-
:param session: The database session in use.
|
|
309
281
|
|
|
310
282
|
:returns: A dict with token and expires_at entries.
|
|
311
283
|
"""
|
|
312
284
|
|
|
313
285
|
kwargs = {'account': account}
|
|
314
|
-
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_ssh', kwargs=kwargs, session=session)
|
|
315
|
-
if not auth_result.allowed:
|
|
316
|
-
raise exception.AccessDenied('User can not get challenge token for account %s. %s' % (account, auth_result.message))
|
|
317
286
|
|
|
318
|
-
|
|
287
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
288
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_ssh', kwargs=kwargs, session=session)
|
|
289
|
+
if not auth_result.allowed:
|
|
290
|
+
raise exception.AccessDenied('User can not get challenge token for account %s. %s' % (account, auth_result.message))
|
|
291
|
+
|
|
292
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
319
293
|
|
|
320
|
-
|
|
294
|
+
return authentication.get_ssh_challenge_token(internal_account, appid, ip, session=session)
|
|
321
295
|
|
|
322
296
|
|
|
323
|
-
@transactional_session
|
|
324
297
|
def get_auth_token_saml(
|
|
325
298
|
account: str,
|
|
326
299
|
saml_nameid: str,
|
|
327
300
|
appid: str,
|
|
328
301
|
ip: Optional[str] = None,
|
|
329
302
|
vo: str = 'def',
|
|
330
|
-
*,
|
|
331
|
-
session: "Session"
|
|
332
303
|
) -> Optional[TokenDict]:
|
|
333
304
|
"""
|
|
334
305
|
Authenticate a Rucio account temporarily via SSO.
|
|
@@ -339,26 +310,24 @@ def get_auth_token_saml(
|
|
|
339
310
|
:param saml_nameid: NameId returned in SAML response as a string.
|
|
340
311
|
:param appid: The application identifier as a string.
|
|
341
312
|
:param ip: IP address of the client as a string.
|
|
342
|
-
:param session: The database session in use.
|
|
343
313
|
|
|
344
314
|
:returns: A dict with token and expires_at entries.
|
|
345
315
|
"""
|
|
346
316
|
|
|
347
317
|
kwargs = {'account': account, 'saml_nameid': saml_nameid}
|
|
348
|
-
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_saml', kwargs=kwargs, session=session)
|
|
349
|
-
if not auth_result.allowed:
|
|
350
|
-
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (saml_nameid, account, auth_result.message))
|
|
351
318
|
|
|
352
|
-
|
|
319
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
320
|
+
auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_saml', kwargs=kwargs, session=session)
|
|
321
|
+
if not auth_result.allowed:
|
|
322
|
+
raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (saml_nameid, account, auth_result.message))
|
|
323
|
+
|
|
324
|
+
internal_account = InternalAccount(account, vo=vo)
|
|
353
325
|
|
|
354
|
-
|
|
326
|
+
return authentication.get_auth_token_saml(internal_account, saml_nameid, appid, ip, session=session)
|
|
355
327
|
|
|
356
328
|
|
|
357
|
-
@transactional_session
|
|
358
329
|
def validate_auth_token(
|
|
359
330
|
token: str,
|
|
360
|
-
*,
|
|
361
|
-
session: "Session"
|
|
362
331
|
) -> dict[str, Any]:
|
|
363
332
|
"""
|
|
364
333
|
Validate an authentication token.
|
|
@@ -374,8 +343,9 @@ def validate_auth_token(
|
|
|
374
343
|
vo: <vo> }
|
|
375
344
|
"""
|
|
376
345
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
346
|
+
with db_session(DatabaseOperationType.WRITE) as session:
|
|
347
|
+
auth = authentication.validate_auth_token(token, session=session)
|
|
348
|
+
vo = auth['account'].vo
|
|
349
|
+
auth = gateway_update_return_dict(auth, session=session)
|
|
350
|
+
auth['vo'] = vo
|
|
381
351
|
return auth
|