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.

Files changed (106) hide show
  1. rucio/common/plugins.py +1 -1
  2. rucio/common/utils.py +17 -10
  3. rucio/core/did.py +32 -4
  4. rucio/core/did_meta_plugins/did_column_meta.py +1 -1
  5. rucio/core/replica.py +1 -1
  6. rucio/db/sqla/util.py +1 -1
  7. rucio/gateway/authentication.py +58 -88
  8. rucio/gateway/config.py +63 -75
  9. rucio/gateway/credential.py +11 -17
  10. rucio/gateway/did.py +245 -329
  11. rucio/gateway/dirac.py +33 -34
  12. rucio/gateway/exporter.py +27 -30
  13. rucio/gateway/heartbeat.py +15 -19
  14. rucio/gateway/importer.py +12 -14
  15. rucio/gateway/lifetime_exception.py +16 -24
  16. rucio/gateway/lock.py +27 -40
  17. rucio/gateway/meta_conventions.py +19 -28
  18. rucio/gateway/quarantined_replica.py +24 -27
  19. rucio/gateway/replica.py +223 -226
  20. rucio/gateway/rse.py +191 -218
  21. rucio/gateway/rule.py +115 -146
  22. rucio/gateway/scope.py +18 -25
  23. rucio/gateway/trace.py +48 -0
  24. rucio/gateway/vo.py +32 -37
  25. rucio/vcsversion.py +3 -3
  26. rucio/web/rest/flaskapi/v1/accounts.py +2 -2
  27. rucio/web/rest/flaskapi/v1/auth.py +15 -0
  28. rucio/web/rest/flaskapi/v1/common.py +20 -0
  29. rucio/web/rest/flaskapi/v1/config.py +7 -7
  30. rucio/web/rest/flaskapi/v1/credentials.py +20 -13
  31. rucio/web/rest/flaskapi/v1/dids.py +55 -55
  32. rucio/web/rest/flaskapi/v1/dirac.py +2 -2
  33. rucio/web/rest/flaskapi/v1/export.py +1 -1
  34. rucio/web/rest/flaskapi/v1/heartbeats.py +3 -3
  35. rucio/web/rest/flaskapi/v1/import.py +1 -1
  36. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +5 -5
  37. rucio/web/rest/flaskapi/v1/locks.py +4 -4
  38. rucio/web/rest/flaskapi/v1/main.py +17 -10
  39. rucio/web/rest/flaskapi/v1/meta_conventions.py +3 -3
  40. rucio/web/rest/flaskapi/v1/replicas.py +31 -30
  41. rucio/web/rest/flaskapi/v1/rses.py +37 -37
  42. rucio/web/rest/flaskapi/v1/rules.py +15 -15
  43. rucio/web/rest/flaskapi/v1/scopes.py +3 -3
  44. rucio/web/rest/flaskapi/v1/traces.py +75 -77
  45. rucio/web/rest/flaskapi/v1/vos.py +5 -7
  46. {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/METADATA +1 -2
  47. {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/RECORD +106 -105
  48. {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/WHEEL +1 -1
  49. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  50. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  51. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  52. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  53. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  54. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  55. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  56. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  57. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  58. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  59. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  60. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  61. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
  62. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
  63. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/requirements.server.txt +0 -0
  64. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/bootstrap.py +0 -0
  65. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  66. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/data/rucio/tools/reset_database.py +0 -0
  67. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio +0 -0
  68. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-account +0 -0
  69. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  70. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-abacus-rse +0 -0
  71. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-admin +0 -0
  72. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-atropos +0 -0
  73. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-auditor +0 -0
  74. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-automatix +0 -0
  75. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-bb8 +0 -0
  76. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-cache-client +0 -0
  77. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-cache-consumer +0 -0
  78. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-finisher +0 -0
  79. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-poller +0 -0
  80. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-preparer +0 -0
  81. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-receiver +0 -0
  82. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-stager +0 -0
  83. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-submitter +0 -0
  84. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-conveyor-throttler +0 -0
  85. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-dark-reaper +0 -0
  86. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-dumper +0 -0
  87. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-follower +0 -0
  88. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-hermes +0 -0
  89. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-cleaner +0 -0
  90. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-evaluator +0 -0
  91. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-injector +0 -0
  92. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-judge-repairer +0 -0
  93. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-kronos +0 -0
  94. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-minos +0 -0
  95. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
  96. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-necromancer +0 -0
  97. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-oauth-manager +0 -0
  98. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-reaper +0 -0
  99. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-replica-recoverer +0 -0
  100. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-rse-decommissioner +0 -0
  101. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  102. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-transmogrifier +0 -0
  103. {rucio-37.1.0.post1.data → rucio-37.3.0.data}/scripts/rucio-undertaker +0 -0
  104. {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
  105. {rucio-37.1.0.post1.dist-info → rucio-37.3.0.dist-info}/licenses/LICENSE +0 -0
  106. {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 = getattr(os.environ, env_name, "")
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 fallback scope extraction algorithm, based on the ATLAS scope extraction algorithm.
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 DID.
603
+ :returns: A tuple containing the extracted scope and the name.
603
604
  """
604
- if did.find(':') > -1:
605
- if len(did.split(':')) > 2:
606
- raise RucioException('Too many colons. Cannot extract scope and name')
607
- scope, name = did.split(':')[0], did.split(':')[1]
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, Enum):
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: "Mapping[Any, Any]",
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,
@@ -234,7 +234,7 @@ class DidColumnMeta(DidMetaPlugin):
234
234
  )
235
235
  stmt = stmt.with_hint(
236
236
  models.DataIdentifier,
237
- 'NO_EXPAND',
237
+ 'USE_CONCAT INDEX_RS_ASC(DIDS)',
238
238
  'oracle'
239
239
  )
240
240
 
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('SELECT UPPER(table_name) '
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())
@@ -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 TYPE_CHECKING, Any, Optional, Union
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 transactional_session
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
- return oidc.refresh_cli_auth_token(token_string, internal_account, session=session)
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
- return authentication.redirect_auth_oidc(authn_code, fetchtoken, session=session)
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
- return oidc.get_auth_oidc(internal_account, session=session, **kwargs)
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
- return oidc.get_token_oidc(auth_query_string, ip, session=session)
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
- auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_user_pass', kwargs=kwargs, session=session)
174
- if not auth_result.allowed:
175
- raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (username, account, auth_result.message))
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
- internal_account = InternalAccount(account, vo=vo)
160
+ internal_account = InternalAccount(account, vo=vo)
178
161
 
179
- return authentication.get_auth_token_user_pass(internal_account, username, password, appid, ip, session=session)
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
- auth_result = permission.has_permission(issuer=account, vo=vo, action='get_auth_token_gss', kwargs=kwargs, session=session)
209
- if not auth_result.allowed:
210
- raise exception.AccessDenied('User with identity %s can not log to account %s. %s' % (gsscred, account, auth_result.message))
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
- internal_account = InternalAccount(account, vo=vo)
192
+ internal_account = InternalAccount(account, vo=vo)
213
193
 
214
- return authentication.get_auth_token_gss(internal_account, gsscred, appid, ip, session=session)
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
- internal_account = InternalAccount(account, vo=vo)
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
- return authentication.get_auth_token_x509(internal_account, dn, appid, ip, session=session)
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
- internal_account = InternalAccount(account, vo=vo)
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
- return authentication.get_auth_token_ssh(internal_account, signature, appid, ip, session=session)
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
- internal_account = InternalAccount(account, vo=vo)
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
- return authentication.get_ssh_challenge_token(internal_account, appid, ip, session=session)
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
- internal_account = InternalAccount(account, vo=vo)
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
- return authentication.get_auth_token_saml(internal_account, saml_nameid, appid, ip, session=session)
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
- auth = authentication.validate_auth_token(token, session=session)
378
- vo = auth['account'].vo
379
- auth = gateway_update_return_dict(auth, session=session)
380
- auth['vo'] = vo
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