rucio 32.8.6__py3-none-any.whl → 35.8.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 (502) hide show
  1. rucio/__init__.py +0 -1
  2. rucio/alembicrevision.py +1 -2
  3. rucio/client/__init__.py +0 -1
  4. rucio/client/accountclient.py +45 -25
  5. rucio/client/accountlimitclient.py +37 -9
  6. rucio/client/baseclient.py +199 -154
  7. rucio/client/client.py +2 -3
  8. rucio/client/configclient.py +19 -6
  9. rucio/client/credentialclient.py +9 -4
  10. rucio/client/didclient.py +238 -63
  11. rucio/client/diracclient.py +13 -5
  12. rucio/client/downloadclient.py +162 -51
  13. rucio/client/exportclient.py +4 -4
  14. rucio/client/fileclient.py +3 -4
  15. rucio/client/importclient.py +4 -4
  16. rucio/client/lifetimeclient.py +21 -5
  17. rucio/client/lockclient.py +18 -8
  18. rucio/client/{metaclient.py → metaconventionsclient.py} +18 -15
  19. rucio/client/pingclient.py +0 -1
  20. rucio/client/replicaclient.py +15 -5
  21. rucio/client/requestclient.py +35 -19
  22. rucio/client/rseclient.py +133 -51
  23. rucio/client/ruleclient.py +29 -22
  24. rucio/client/scopeclient.py +8 -6
  25. rucio/client/subscriptionclient.py +47 -35
  26. rucio/client/touchclient.py +8 -4
  27. rucio/client/uploadclient.py +166 -82
  28. rucio/common/__init__.py +0 -1
  29. rucio/common/cache.py +4 -4
  30. rucio/common/config.py +52 -47
  31. rucio/common/constants.py +69 -2
  32. rucio/common/constraints.py +0 -1
  33. rucio/common/didtype.py +24 -22
  34. rucio/common/dumper/__init__.py +70 -41
  35. rucio/common/dumper/consistency.py +26 -22
  36. rucio/common/dumper/data_models.py +16 -23
  37. rucio/common/dumper/path_parsing.py +0 -1
  38. rucio/common/exception.py +281 -222
  39. rucio/common/extra.py +0 -1
  40. rucio/common/logging.py +54 -38
  41. rucio/common/pcache.py +122 -101
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +4 -4
  44. rucio/common/schema/__init__.py +17 -10
  45. rucio/common/schema/atlas.py +7 -5
  46. rucio/common/schema/belleii.py +7 -5
  47. rucio/common/schema/domatpc.py +7 -5
  48. rucio/common/schema/escape.py +7 -5
  49. rucio/common/schema/generic.py +8 -6
  50. rucio/common/schema/generic_multi_vo.py +7 -5
  51. rucio/common/schema/icecube.py +7 -5
  52. rucio/common/stomp_utils.py +0 -1
  53. rucio/common/stopwatch.py +0 -1
  54. rucio/common/test_rucio_server.py +2 -2
  55. rucio/common/types.py +262 -17
  56. rucio/common/utils.py +743 -451
  57. rucio/core/__init__.py +0 -1
  58. rucio/core/account.py +99 -29
  59. rucio/core/account_counter.py +89 -24
  60. rucio/core/account_limit.py +90 -24
  61. rucio/core/authentication.py +86 -29
  62. rucio/core/config.py +108 -38
  63. rucio/core/credential.py +14 -7
  64. rucio/core/did.py +680 -782
  65. rucio/core/did_meta_plugins/__init__.py +8 -6
  66. rucio/core/did_meta_plugins/did_column_meta.py +17 -12
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +60 -11
  68. rucio/core/did_meta_plugins/filter_engine.py +90 -50
  69. rucio/core/did_meta_plugins/json_meta.py +41 -16
  70. rucio/core/did_meta_plugins/mongo_meta.py +25 -8
  71. rucio/core/did_meta_plugins/postgres_meta.py +3 -4
  72. rucio/core/dirac.py +46 -17
  73. rucio/core/distance.py +66 -43
  74. rucio/core/exporter.py +5 -5
  75. rucio/core/heartbeat.py +181 -81
  76. rucio/core/identity.py +22 -12
  77. rucio/core/importer.py +23 -12
  78. rucio/core/lifetime_exception.py +32 -32
  79. rucio/core/lock.py +244 -142
  80. rucio/core/message.py +79 -38
  81. rucio/core/{meta.py → meta_conventions.py} +57 -44
  82. rucio/core/monitor.py +19 -13
  83. rucio/core/naming_convention.py +68 -27
  84. rucio/core/nongrid_trace.py +17 -5
  85. rucio/core/oidc.py +151 -29
  86. rucio/core/permission/__init__.py +18 -6
  87. rucio/core/permission/atlas.py +50 -35
  88. rucio/core/permission/belleii.py +6 -5
  89. rucio/core/permission/escape.py +8 -6
  90. rucio/core/permission/generic.py +82 -80
  91. rucio/core/permission/generic_multi_vo.py +9 -7
  92. rucio/core/quarantined_replica.py +91 -58
  93. rucio/core/replica.py +1303 -772
  94. rucio/core/replica_sorter.py +10 -12
  95. rucio/core/request.py +1133 -285
  96. rucio/core/rse.py +142 -102
  97. rucio/core/rse_counter.py +49 -18
  98. rucio/core/rse_expression_parser.py +6 -7
  99. rucio/core/rse_selector.py +41 -16
  100. rucio/core/rule.py +1538 -474
  101. rucio/core/rule_grouping.py +213 -68
  102. rucio/core/scope.py +50 -22
  103. rucio/core/subscription.py +92 -44
  104. rucio/core/topology.py +66 -24
  105. rucio/core/trace.py +42 -28
  106. rucio/core/transfer.py +543 -259
  107. rucio/core/vo.py +36 -18
  108. rucio/core/volatile_replica.py +59 -32
  109. rucio/daemons/__init__.py +0 -1
  110. rucio/daemons/abacus/__init__.py +0 -1
  111. rucio/daemons/abacus/account.py +29 -19
  112. rucio/daemons/abacus/collection_replica.py +21 -10
  113. rucio/daemons/abacus/rse.py +22 -12
  114. rucio/daemons/atropos/__init__.py +0 -1
  115. rucio/daemons/atropos/atropos.py +1 -2
  116. rucio/daemons/auditor/__init__.py +56 -28
  117. rucio/daemons/auditor/hdfs.py +17 -6
  118. rucio/daemons/auditor/srmdumps.py +116 -45
  119. rucio/daemons/automatix/__init__.py +0 -1
  120. rucio/daemons/automatix/automatix.py +30 -18
  121. rucio/daemons/badreplicas/__init__.py +0 -1
  122. rucio/daemons/badreplicas/minos.py +29 -18
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +5 -7
  124. rucio/daemons/badreplicas/necromancer.py +9 -13
  125. rucio/daemons/bb8/__init__.py +0 -1
  126. rucio/daemons/bb8/bb8.py +10 -13
  127. rucio/daemons/bb8/common.py +151 -154
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +15 -9
  129. rucio/daemons/bb8/t2_background_rebalance.py +15 -8
  130. rucio/daemons/c3po/__init__.py +0 -1
  131. rucio/daemons/c3po/algorithms/__init__.py +0 -1
  132. rucio/daemons/c3po/algorithms/simple.py +8 -5
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +10 -7
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +10 -7
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +30 -15
  136. rucio/daemons/c3po/c3po.py +81 -52
  137. rucio/daemons/c3po/collectors/__init__.py +0 -1
  138. rucio/daemons/c3po/collectors/agis.py +17 -17
  139. rucio/daemons/c3po/collectors/free_space.py +32 -13
  140. rucio/daemons/c3po/collectors/jedi_did.py +14 -5
  141. rucio/daemons/c3po/collectors/mock_did.py +11 -6
  142. rucio/daemons/c3po/collectors/network_metrics.py +12 -4
  143. rucio/daemons/c3po/collectors/workload.py +21 -19
  144. rucio/daemons/c3po/utils/__init__.py +0 -1
  145. rucio/daemons/c3po/utils/dataset_cache.py +15 -5
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +16 -5
  147. rucio/daemons/c3po/utils/expiring_list.py +6 -7
  148. rucio/daemons/c3po/utils/popularity.py +5 -2
  149. rucio/daemons/c3po/utils/timeseries.py +25 -12
  150. rucio/daemons/cache/__init__.py +0 -1
  151. rucio/daemons/cache/consumer.py +21 -15
  152. rucio/daemons/common.py +42 -18
  153. rucio/daemons/conveyor/__init__.py +0 -1
  154. rucio/daemons/conveyor/common.py +69 -37
  155. rucio/daemons/conveyor/finisher.py +83 -46
  156. rucio/daemons/conveyor/poller.py +101 -69
  157. rucio/daemons/conveyor/preparer.py +35 -28
  158. rucio/daemons/conveyor/receiver.py +64 -21
  159. rucio/daemons/conveyor/stager.py +33 -28
  160. rucio/daemons/conveyor/submitter.py +71 -47
  161. rucio/daemons/conveyor/throttler.py +99 -35
  162. rucio/daemons/follower/__init__.py +0 -1
  163. rucio/daemons/follower/follower.py +12 -8
  164. rucio/daemons/hermes/__init__.py +0 -1
  165. rucio/daemons/hermes/hermes.py +57 -21
  166. rucio/daemons/judge/__init__.py +0 -1
  167. rucio/daemons/judge/cleaner.py +27 -17
  168. rucio/daemons/judge/evaluator.py +31 -18
  169. rucio/daemons/judge/injector.py +31 -23
  170. rucio/daemons/judge/repairer.py +28 -18
  171. rucio/daemons/oauthmanager/__init__.py +0 -1
  172. rucio/daemons/oauthmanager/oauthmanager.py +7 -8
  173. rucio/daemons/reaper/__init__.py +0 -1
  174. rucio/daemons/reaper/dark_reaper.py +15 -9
  175. rucio/daemons/reaper/reaper.py +109 -67
  176. rucio/daemons/replicarecoverer/__init__.py +0 -1
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +255 -116
  178. rucio/{api → daemons/rsedecommissioner}/__init__.py +0 -1
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +0 -1
  186. rucio/daemons/storage/consistency/__init__.py +0 -1
  187. rucio/daemons/storage/consistency/actions.py +152 -59
  188. rucio/daemons/tracer/__init__.py +0 -1
  189. rucio/daemons/tracer/kronos.py +47 -24
  190. rucio/daemons/transmogrifier/__init__.py +0 -1
  191. rucio/daemons/transmogrifier/transmogrifier.py +35 -26
  192. rucio/daemons/undertaker/__init__.py +0 -1
  193. rucio/daemons/undertaker/undertaker.py +10 -10
  194. rucio/db/__init__.py +0 -1
  195. rucio/db/sqla/__init__.py +16 -2
  196. rucio/db/sqla/constants.py +10 -1
  197. rucio/db/sqla/migrate_repo/__init__.py +0 -1
  198. rucio/db/sqla/migrate_repo/env.py +0 -1
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +0 -1
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +0 -3
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +1 -3
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +0 -3
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +1 -3
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +1 -3
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +0 -3
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +1 -4
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +0 -1
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +0 -2
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +0 -1
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +0 -1
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +0 -2
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +0 -1
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +1 -3
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +0 -1
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +0 -3
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +0 -1
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +1 -2
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +0 -1
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +0 -3
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +1 -3
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +1 -4
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +0 -2
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +0 -3
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +0 -3
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +1 -2
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +0 -1
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +0 -1
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +0 -2
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +0 -3
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +1 -3
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +0 -2
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +1 -4
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +0 -3
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +1 -4
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +0 -1
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +1 -3
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +0 -2
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +1 -3
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +1 -3
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +1 -2
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +1 -3
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +1 -3
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +0 -2
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +1 -3
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +2 -3
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +0 -3
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +1 -4
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +0 -1
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +0 -1
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +0 -3
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +0 -1
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +0 -2
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +0 -3
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +0 -2
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +2 -4
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +0 -2
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +1 -3
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +1 -4
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +0 -3
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +0 -3
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +0 -2
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +1 -3
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +0 -3
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +0 -2
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +0 -2
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +0 -3
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +0 -3
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +0 -3
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +1 -2
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +0 -3
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +1 -3
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +1 -3
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +1 -2
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +0 -3
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +0 -3
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +1 -2
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +2 -4
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +0 -1
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +1 -4
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +0 -2
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +0 -3
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +1 -2
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +0 -3
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +1 -3
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +0 -3
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +0 -1
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +1 -2
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +0 -2
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +1 -3
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +0 -2
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +1 -4
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +0 -1
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +0 -1
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +1 -3
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +1 -4
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +0 -1
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +0 -3
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +1 -2
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +1 -3
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +0 -3
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +1 -5
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +1 -3
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +0 -3
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +1 -3
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +1 -2
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +0 -3
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +1 -4
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +1 -2
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +1 -4
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +1 -3
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +1 -4
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +0 -2
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +1 -3
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +0 -3
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +1 -3
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +0 -1
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +1 -2
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +1 -3
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +0 -2
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +0 -1
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +1 -2
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +0 -3
  327. rucio/db/sqla/models.py +122 -216
  328. rucio/db/sqla/sautils.py +12 -5
  329. rucio/db/sqla/session.py +71 -43
  330. rucio/db/sqla/types.py +3 -4
  331. rucio/db/sqla/util.py +91 -69
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/{api → gateway}/account.py +119 -46
  334. rucio/{api → gateway}/account_limit.py +12 -13
  335. rucio/{api → gateway}/authentication.py +106 -33
  336. rucio/{api → gateway}/config.py +12 -13
  337. rucio/{api → gateway}/credential.py +15 -4
  338. rucio/{api → gateway}/did.py +384 -140
  339. rucio/{api → gateway}/dirac.py +16 -6
  340. rucio/{api → gateway}/exporter.py +3 -4
  341. rucio/{api → gateway}/heartbeat.py +17 -5
  342. rucio/{api → gateway}/identity.py +63 -19
  343. rucio/{api → gateway}/importer.py +3 -4
  344. rucio/{api → gateway}/lifetime_exception.py +35 -10
  345. rucio/{api → gateway}/lock.py +34 -12
  346. rucio/{api/meta.py → gateway/meta_conventions.py} +18 -16
  347. rucio/{api → gateway}/permission.py +4 -5
  348. rucio/{api → gateway}/quarantined_replica.py +13 -4
  349. rucio/{api → gateway}/replica.py +12 -11
  350. rucio/{api → gateway}/request.py +129 -28
  351. rucio/{api → gateway}/rse.py +11 -12
  352. rucio/{api → gateway}/rule.py +117 -35
  353. rucio/{api → gateway}/scope.py +24 -14
  354. rucio/{api → gateway}/subscription.py +65 -43
  355. rucio/{api → gateway}/vo.py +17 -7
  356. rucio/rse/__init__.py +3 -4
  357. rucio/rse/protocols/__init__.py +0 -1
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +1 -2
  360. rucio/rse/protocols/dummy.py +1 -2
  361. rucio/rse/protocols/gfal.py +12 -10
  362. rucio/rse/protocols/globus.py +7 -7
  363. rucio/rse/protocols/gsiftp.py +2 -3
  364. rucio/rse/protocols/http_cache.py +1 -2
  365. rucio/rse/protocols/mock.py +1 -2
  366. rucio/rse/protocols/ngarc.py +1 -2
  367. rucio/rse/protocols/posix.py +12 -13
  368. rucio/rse/protocols/protocol.py +116 -52
  369. rucio/rse/protocols/rclone.py +6 -7
  370. rucio/rse/protocols/rfio.py +4 -5
  371. rucio/rse/protocols/srm.py +9 -10
  372. rucio/rse/protocols/ssh.py +8 -9
  373. rucio/rse/protocols/storm.py +2 -3
  374. rucio/rse/protocols/webdav.py +17 -14
  375. rucio/rse/protocols/xrootd.py +23 -17
  376. rucio/rse/rsemanager.py +19 -7
  377. rucio/tests/__init__.py +0 -1
  378. rucio/tests/common.py +43 -17
  379. rucio/tests/common_server.py +3 -3
  380. rucio/transfertool/__init__.py +0 -1
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +250 -138
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +9 -8
  387. rucio/transfertool/globus_library.py +1 -2
  388. rucio/transfertool/mock.py +21 -12
  389. rucio/transfertool/transfertool.py +33 -24
  390. rucio/vcsversion.py +4 -4
  391. rucio/version.py +5 -13
  392. rucio/web/__init__.py +0 -1
  393. rucio/web/rest/__init__.py +0 -1
  394. rucio/web/rest/flaskapi/__init__.py +0 -1
  395. rucio/web/rest/flaskapi/authenticated_bp.py +0 -1
  396. rucio/web/rest/flaskapi/v1/__init__.py +0 -1
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +15 -13
  398. rucio/web/rest/flaskapi/v1/accounts.py +49 -48
  399. rucio/web/rest/flaskapi/v1/archives.py +12 -10
  400. rucio/web/rest/flaskapi/v1/auth.py +146 -144
  401. rucio/web/rest/flaskapi/v1/common.py +82 -41
  402. rucio/web/rest/flaskapi/v1/config.py +5 -6
  403. rucio/web/rest/flaskapi/v1/credentials.py +7 -8
  404. rucio/web/rest/flaskapi/v1/dids.py +158 -28
  405. rucio/web/rest/flaskapi/v1/dirac.py +8 -8
  406. rucio/web/rest/flaskapi/v1/export.py +3 -5
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +3 -5
  408. rucio/web/rest/flaskapi/v1/identities.py +3 -5
  409. rucio/web/rest/flaskapi/v1/import.py +3 -4
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +6 -9
  411. rucio/web/rest/flaskapi/v1/locks.py +2 -4
  412. rucio/web/rest/flaskapi/v1/main.py +10 -2
  413. rucio/web/rest/flaskapi/v1/{meta.py → meta_conventions.py} +26 -11
  414. rucio/web/rest/flaskapi/v1/metrics.py +1 -2
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +4 -4
  416. rucio/web/rest/flaskapi/v1/ping.py +6 -7
  417. rucio/web/rest/flaskapi/v1/redirect.py +8 -9
  418. rucio/web/rest/flaskapi/v1/replicas.py +43 -19
  419. rucio/web/rest/flaskapi/v1/requests.py +178 -21
  420. rucio/web/rest/flaskapi/v1/rses.py +61 -26
  421. rucio/web/rest/flaskapi/v1/rules.py +48 -18
  422. rucio/web/rest/flaskapi/v1/scopes.py +3 -5
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +22 -18
  424. rucio/web/rest/flaskapi/v1/traces.py +4 -4
  425. rucio/web/rest/flaskapi/v1/types.py +20 -0
  426. rucio/web/rest/flaskapi/v1/vos.py +3 -5
  427. rucio/web/rest/main.py +0 -1
  428. rucio/web/rest/metrics.py +0 -1
  429. rucio/web/rest/ping.py +27 -0
  430. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/ldap.cfg.template +1 -1
  431. rucio-35.8.0.data/data/rucio/requirements.server.txt +268 -0
  432. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/bootstrap.py +3 -3
  433. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/merge_rucio_configs.py +2 -5
  434. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/reset_database.py +3 -3
  435. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio +87 -85
  436. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-account +0 -1
  437. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-collection-replica +0 -1
  438. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-rse +0 -1
  439. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-admin +45 -32
  440. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-atropos +0 -1
  441. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-auditor +13 -7
  442. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-automatix +1 -2
  443. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-bb8 +0 -1
  444. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-c3po +0 -1
  445. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-client +2 -3
  446. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-consumer +0 -1
  447. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-finisher +1 -2
  448. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-poller +0 -1
  449. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-preparer +0 -1
  450. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-receiver +0 -1
  451. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-stager +0 -1
  452. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-submitter +2 -3
  453. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-throttler +0 -1
  454. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dark-reaper +0 -1
  455. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dumper +11 -10
  456. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-follower +0 -1
  457. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-hermes +0 -1
  458. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-cleaner +0 -1
  459. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-evaluator +2 -3
  460. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-injector +0 -1
  461. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-repairer +0 -1
  462. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-kronos +1 -3
  463. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos +0 -1
  464. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos-temporary-expiration +0 -1
  465. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-necromancer +1 -2
  466. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-oauth-manager +2 -3
  467. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-reaper +0 -1
  468. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-replica-recoverer +6 -7
  469. rucio-35.8.0.data/scripts/rucio-rse-decommissioner +66 -0
  470. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-storage-consistency-actions +0 -1
  471. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-transmogrifier +0 -1
  472. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-undertaker +1 -2
  473. rucio-35.8.0.dist-info/METADATA +72 -0
  474. rucio-35.8.0.dist-info/RECORD +493 -0
  475. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/WHEEL +1 -1
  476. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/AUTHORS.rst +3 -0
  477. rucio/api/temporary_did.py +0 -49
  478. rucio/common/schema/cms.py +0 -478
  479. rucio/common/schema/lsst.py +0 -423
  480. rucio/core/permission/cms.py +0 -1166
  481. rucio/core/temporary_did.py +0 -188
  482. rucio/daemons/reaper/light_reaper.py +0 -255
  483. rucio/web/rest/flaskapi/v1/tmp_dids.py +0 -115
  484. rucio-32.8.6.data/data/rucio/requirements.txt +0 -55
  485. rucio-32.8.6.data/scripts/rucio-light-reaper +0 -53
  486. rucio-32.8.6.dist-info/METADATA +0 -83
  487. rucio-32.8.6.dist-info/RECORD +0 -481
  488. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  489. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  490. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  491. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  492. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  493. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  494. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  495. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  496. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  497. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  498. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  499. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
  500. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
  501. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/LICENSE +0 -0
  502. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +12,42 @@
13
12
  # See the License for the specific language governing permissions and
14
13
  # limitations under the License.
15
14
 
16
- from re import match, compile, error
15
+ from re import compile, error, match
17
16
  from traceback import format_exc
18
- from typing import TYPE_CHECKING
17
+ from typing import TYPE_CHECKING, Any, Optional, cast
19
18
 
20
19
  from dogpile.cache.api import NO_VALUE
20
+ from sqlalchemy import and_, delete, select
21
21
  from sqlalchemy.exc import IntegrityError
22
22
 
23
23
  from rucio.common.cache import make_region_memcached
24
- from rucio.common.exception import Duplicate, RucioException, InvalidObject
24
+ from rucio.common.exception import Duplicate, InvalidObject, RucioException
25
25
  from rucio.db.sqla import models
26
26
  from rucio.db.sqla.constants import KeyType
27
27
  from rucio.db.sqla.session import read_session, transactional_session
28
28
 
29
29
  if TYPE_CHECKING:
30
+ from typing import TypedDict
31
+
30
32
  from sqlalchemy.orm import Session
31
33
 
34
+ from rucio.common.types import InternalScope
35
+
36
+ class NamingConventionDict(TypedDict):
37
+ scope: InternalScope
38
+ regexp: str
39
+
32
40
  REGION = make_region_memcached(expiration_time=900)
33
41
 
34
42
 
35
43
  @transactional_session
36
- def add_naming_convention(scope, regexp, convention_type, *, session: "Session"):
44
+ def add_naming_convention(
45
+ scope: "InternalScope",
46
+ regexp: str,
47
+ convention_type: KeyType,
48
+ *,
49
+ session: "Session"
50
+ ) -> None:
37
51
  """
38
52
  add a naming convention for a given scope
39
53
 
@@ -60,7 +74,12 @@ def add_naming_convention(scope, regexp, convention_type, *, session: "Session")
60
74
 
61
75
 
62
76
  @read_session
63
- def get_naming_convention(scope, convention_type, *, session: "Session"):
77
+ def get_naming_convention(
78
+ scope: "InternalScope",
79
+ convention_type: KeyType,
80
+ *,
81
+ session: "Session"
82
+ ) -> Optional[str]:
64
83
  """
65
84
  Get the naming convention for a given scope
66
85
 
@@ -70,15 +89,22 @@ def get_naming_convention(scope, convention_type, *, session: "Session"):
70
89
 
71
90
  :returns: the regular expression.
72
91
  """
73
- query = session.query(models.NamingConvention.regexp).\
74
- filter(models.NamingConvention.scope == scope).\
75
- filter(models.NamingConvention.convention_type == convention_type)
76
- for row in query:
77
- return row[0]
92
+ stmt = select(
93
+ models.NamingConvention.regexp
94
+ ).where(
95
+ and_(models.NamingConvention.scope == scope,
96
+ models.NamingConvention.convention_type == convention_type)
97
+ )
98
+ return session.execute(stmt).scalar()
78
99
 
79
100
 
80
101
  @transactional_session
81
- def delete_naming_convention(scope, convention_type, *, session: "Session"):
102
+ def delete_naming_convention(
103
+ scope: "InternalScope",
104
+ convention_type: KeyType,
105
+ *,
106
+ session: "Session"
107
+ ) -> int:
82
108
  """
83
109
  delete a naming convention for a given scope
84
110
 
@@ -87,14 +113,19 @@ def delete_naming_convention(scope, convention_type, *, session: "Session"):
87
113
  :param convention_type: the did_type on which the regexp should apply.
88
114
  :param session: The database session in use.
89
115
  """
90
- REGION.delete(scope.internal)
91
- return session.query(models.NamingConvention) \
92
- .filter_by(scope=scope, convention_type=convention_type) \
93
- .delete()
116
+ if scope.internal is not None:
117
+ REGION.delete(scope.internal)
118
+ stmt = delete(
119
+ models.NamingConvention
120
+ ).where(
121
+ and_(models.NamingConvention.scope == scope,
122
+ models.NamingConvention.convention_type == convention_type)
123
+ )
124
+ return session.execute(stmt).rowcount
94
125
 
95
126
 
96
127
  @read_session
97
- def list_naming_conventions(*, session: "Session"):
128
+ def list_naming_conventions(*, session: "Session") -> list["NamingConventionDict"]:
98
129
  """
99
130
  List all naming conventions.
100
131
 
@@ -102,13 +133,21 @@ def list_naming_conventions(*, session: "Session"):
102
133
 
103
134
  :returns: a list of dictionaries.
104
135
  """
105
- query = session.query(models.NamingConvention.scope,
106
- models.NamingConvention.regexp)
107
- return [row._asdict() for row in query]
136
+ stmt = select(
137
+ models.NamingConvention.scope,
138
+ models.NamingConvention.regexp
139
+ )
140
+ return [cast("NamingConventionDict", row._asdict()) for row in session.execute(stmt).all()]
108
141
 
109
142
 
110
143
  @read_session
111
- def validate_name(scope, name, did_type, *, session: "Session"):
144
+ def validate_name(
145
+ scope: "InternalScope",
146
+ name: str,
147
+ did_type: str,
148
+ *,
149
+ session: "Session"
150
+ ) -> Optional[dict[str, Any]]:
112
151
  """
113
152
  Validate a name according to a naming convention.
114
153
 
@@ -120,10 +159,11 @@ def validate_name(scope, name, did_type, *, session: "Session"):
120
159
 
121
160
  :returns: a dictionary with metadata.
122
161
  """
123
- if scope.external.startswith('user'):
124
- return {'project': 'user'}
125
- elif scope.external.startswith('group'):
126
- return {'project': 'group'}
162
+ if scope.external is not None:
163
+ if scope.external.startswith('user'):
164
+ return {'project': 'user'}
165
+ elif scope.external.startswith('group'):
166
+ return {'project': 'group'}
127
167
 
128
168
  # Check if naming convention can be found in cache region
129
169
  regexp = REGION.get(scope.internal)
@@ -131,13 +171,14 @@ def validate_name(scope, name, did_type, *, session: "Session"):
131
171
  regexp = get_naming_convention(scope=scope,
132
172
  convention_type=KeyType.DATASET,
133
173
  session=session)
134
- regexp and REGION.set(scope.internal, regexp)
174
+ if scope.internal is not None:
175
+ regexp and REGION.set(scope.internal, regexp)
135
176
 
136
177
  if not regexp:
137
178
  return
138
179
 
139
180
  # Validate with regexp
140
- groups = match(regexp, str(name))
181
+ groups = match(regexp, str(name)) # type: ignore
141
182
  if groups:
142
183
  meta = groups.groupdict()
143
184
  # Hack to get task_id from version
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +16,7 @@ import json
17
16
  import logging.handlers
18
17
  import random
19
18
  import socket
19
+ from typing import TYPE_CHECKING, Any, Union, overload
20
20
 
21
21
  import stomp
22
22
 
@@ -24,6 +24,9 @@ from rucio.common.config import config_get, config_get_int
24
24
  from rucio.common.logging import rucio_log_formatter
25
25
  from rucio.core.monitor import MetricManager
26
26
 
27
+ if TYPE_CHECKING:
28
+ from datetime import datetime
29
+
27
30
  METRICS = MetricManager(module=__name__)
28
31
 
29
32
  CONFIG_COMMON_LOGLEVEL = getattr(logging, config_get('common', 'loglevel', raise_exception=False, default='DEBUG').upper())
@@ -80,14 +83,23 @@ for broker in BROKERS_RESOLVED:
80
83
  CONNS.append(stomp.Connection(host_and_ports=[(broker, PORT)], vhost=VHOST, reconnect_attempts_max=3))
81
84
 
82
85
 
83
- def date_handler(obj):
84
- '''
85
- '''
86
+ @overload
87
+ def date_handler(obj: "datetime") -> str:
88
+ ...
89
+
90
+
91
+ @overload
92
+ def date_handler(obj: object) -> object:
93
+ ...
94
+
95
+
96
+ def date_handler(obj: Any) -> Union[str, object]:
97
+ """ Format dates to ISO format. """
86
98
  return obj.isoformat() if hasattr(obj, 'isoformat') else obj
87
99
 
88
100
 
89
101
  @METRICS.count_it
90
- def trace(payload):
102
+ def trace(payload: dict[str, Any]) -> None:
91
103
  """
92
104
  Write a trace to the buffer log file and send it to active mq.
93
105
 
rucio/core/oidc.py CHANGED
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,46 +12,52 @@
13
12
  # See the License for the specific language governing permissions and
14
13
  # limitations under the License.
15
14
 
15
+ import hashlib
16
16
  import json
17
17
  import logging
18
- import random
19
18
  import subprocess
20
19
  import traceback
21
20
  from datetime import datetime, timedelta
22
- from typing import Any, TYPE_CHECKING, Optional
23
- from urllib.parse import urlparse, parse_qs
21
+ from math import floor
22
+ from secrets import choice
23
+ from typing import TYPE_CHECKING, Any, Final, Optional, Union
24
+ from urllib.parse import parse_qs, urljoin, urlparse
24
25
 
26
+ import requests
27
+ from dogpile.cache.api import NoValue
25
28
  from jwkest.jws import JWS
26
29
  from jwkest.jwt import JWT
27
- from math import floor
28
30
  from oic import rndstr
29
31
  from oic.oauth2.message import CCAccessTokenRequest
30
- from oic.oic import Client, Grant, Token, REQUEST2ENDPOINT
31
- from oic.oic.message import (AccessTokenResponse, AuthorizationResponse,
32
- Message, RegistrationResponse)
32
+ from oic.oic import REQUEST2ENDPOINT, Client, Grant, Token
33
+ from oic.oic.message import AccessTokenResponse, AuthorizationResponse, Message, RegistrationResponse
33
34
  from oic.utils import time_util
34
35
  from oic.utils.authn.client import CLIENT_AUTHN_METHOD
35
36
  from sqlalchemy import delete, select, update
36
37
  from sqlalchemy.sql.expression import true
37
38
 
38
39
  from rucio.common import types
40
+ from rucio.common.cache import make_region_memcached
39
41
  from rucio.common.config import config_get, config_get_int
40
- from rucio.common.exception import (CannotAuthenticate, CannotAuthorize,
41
- RucioException)
42
+ from rucio.common.exception import CannotAuthenticate, CannotAuthorize, RucioException
42
43
  from rucio.common.stopwatch import Stopwatch
43
44
  from rucio.common.utils import all_oidc_req_claims_present, build_url, val_to_space_sep_str
44
45
  from rucio.core.account import account_exists
45
46
  from rucio.core.identity import exist_identity_account, get_default_account
46
47
  from rucio.core.monitor import MetricManager
47
- from rucio.db.sqla import filter_thread_work
48
- from rucio.db.sqla import models
48
+ from rucio.db.sqla import filter_thread_work, models
49
49
  from rucio.db.sqla.constants import IdentityType
50
50
  from rucio.db.sqla.session import read_session, transactional_session
51
51
 
52
52
  if TYPE_CHECKING:
53
53
  from sqlalchemy.orm import Session
54
54
 
55
+ # The WLCG Common JWT Profile dictates that the lifetime of access and ID tokens
56
+ # should range from five minutes to six hours.
57
+ TOKEN_MIN_LIFETIME: Final = config_get_int('oidc', 'token_min_lifetime', default=300)
58
+ TOKEN_MAX_LIFETIME: Final = config_get_int('oidc', 'token_max_lifetime', default=21600)
55
59
 
60
+ REGION: Final = make_region_memcached(expiration_time=TOKEN_MAX_LIFETIME)
56
61
  METRICS = MetricManager(module=__name__)
57
62
 
58
63
  # worokaround for a bug in pyoidc (as of Dec 2019)
@@ -75,6 +80,81 @@ LEEWAY_SECS = 120
75
80
  # --> check 'profile' info (requested profile scope)
76
81
 
77
82
 
83
+ @METRICS.time_it
84
+ def _token_cache_get(
85
+ key: str,
86
+ min_lifetime: int = TOKEN_MIN_LIFETIME,
87
+ ) -> Optional[str]:
88
+ """Retrieve a token from the cache.
89
+
90
+ Return ``None`` if the cache backend did not return a value, the value is
91
+ not a valid JWT, or the token has a remaining lifetime less than
92
+ ``min_lifetime`` seconds.
93
+ """
94
+ value = REGION.get(key)
95
+ if isinstance(value, NoValue):
96
+ METRICS.counter('token_cache.miss').inc()
97
+ return None
98
+
99
+ if isinstance(value, str):
100
+ try:
101
+ payload = JWT().unpack(value).payload()
102
+ except Exception:
103
+ METRICS.counter('token_cache.invalid').inc()
104
+ return None
105
+ else:
106
+ METRICS.counter('token_cache.invalid').inc()
107
+ return None
108
+
109
+ now = datetime.utcnow().timestamp()
110
+ expiration = payload.get('exp', 0) # type: ignore
111
+ if now + min_lifetime > expiration:
112
+ METRICS.counter('token_cache.expired').inc()
113
+ return None
114
+
115
+ METRICS.counter('token_cache.hit').inc()
116
+ return value
117
+
118
+
119
+ def _token_cache_set(key: str, value: str) -> None:
120
+ """Store a token in the cache."""
121
+ REGION.set(key, value)
122
+
123
+
124
+ def request_token(audience: str, scope: str, use_cache: bool = True) -> Optional[str]:
125
+ """Request a token from the provider.
126
+
127
+ Return ``None`` if the configuration was not loaded properly or the request
128
+ was unsuccessful.
129
+ """
130
+ if not all([OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_PROVIDER_ENDPOINT]):
131
+ if OIDC_CONFIGURATION_RUN or not __load_oidc_configuration():
132
+ return None
133
+
134
+ key = hashlib.md5(f'audience={audience};scope={scope}'.encode()).hexdigest()
135
+
136
+ if use_cache and (token := _token_cache_get(key)):
137
+ return token
138
+
139
+ try:
140
+ response = requests.post(url=OIDC_PROVIDER_ENDPOINT,
141
+ auth=(OIDC_CLIENT_ID, OIDC_CLIENT_SECRET),
142
+ data={'grant_type': 'client_credentials',
143
+ 'audience': audience,
144
+ 'scope': scope})
145
+ response.raise_for_status()
146
+ payload = response.json()
147
+ token = payload['access_token']
148
+ except Exception:
149
+ logging.debug('Failed to procure a token', exc_info=True)
150
+ return None
151
+
152
+ if use_cache:
153
+ _token_cache_set(key, token)
154
+
155
+ return token
156
+
157
+
78
158
  def __get_rucio_oidc_clients(keytimeout: int = 43200) -> tuple[dict, dict]:
79
159
  """
80
160
  Creates a Rucio OIDC Client instances per Identity Provider (IdP)
@@ -125,6 +205,11 @@ def __get_rucio_oidc_clients(keytimeout: int = 43200) -> tuple[dict, dict]:
125
205
  # global variables to represent the IdP clients
126
206
  OIDC_CLIENTS = {}
127
207
  OIDC_ADMIN_CLIENTS = {}
208
+ # New-style token support.
209
+ OIDC_CLIENT_ID = ''
210
+ OIDC_CLIENT_SECRET = ''
211
+ OIDC_PROVIDER_ENDPOINT = ''
212
+ OIDC_CONFIGURATION_RUN = False
128
213
 
129
214
 
130
215
  def __initialize_oidc_clients() -> None:
@@ -143,8 +228,40 @@ def __initialize_oidc_clients() -> None:
143
228
  pass
144
229
 
145
230
 
146
- # try loading OIDC clients uppon module import
147
- __initialize_oidc_clients()
231
+ def __load_oidc_configuration() -> bool:
232
+ """Load the configuration for the new-style token support."""
233
+ global OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_PROVIDER_ENDPOINT, OIDC_CONFIGURATION_RUN
234
+
235
+ OIDC_CONFIGURATION_RUN = True
236
+
237
+ if not IDPSECRETS:
238
+ logging.error('Configuration option "idpsecrets" in section "oidc" is not set')
239
+ return False
240
+ if not ADMIN_ISSUER_ID:
241
+ logging.error('Configuration option "admin_issuer" in section "oidc" is not set')
242
+ return False
243
+
244
+ try:
245
+ with open(IDPSECRETS) as f:
246
+ data = json.load(f)
247
+ OIDC_CLIENT_ID = data[ADMIN_ISSUER_ID]['client_id']
248
+ OIDC_CLIENT_SECRET = data[ADMIN_ISSUER_ID]['client_secret']
249
+ issuer = data[ADMIN_ISSUER_ID]['issuer']
250
+ except Exception:
251
+ logging.error('Failed to parse configuration file "%s"', IDPSECRETS,
252
+ exc_info=True)
253
+ return False
254
+ try:
255
+ oidc_discover_url = urljoin(issuer, '.well-known/openid-configuration')
256
+ response = requests.get(oidc_discover_url)
257
+ response.raise_for_status()
258
+ payload = response.json()
259
+ OIDC_PROVIDER_ENDPOINT = payload['token_endpoint']
260
+ except (requests.HTTPError, requests.JSONDecodeError, KeyError):
261
+ logging.error('Failed to discover token endpoint', exc_info=True)
262
+ return False
263
+
264
+ return True
148
265
 
149
266
 
150
267
  def __get_init_oidc_client(token_object: models.Token = None, token_type: str = None, **kwargs) -> dict[Any, Any]:
@@ -189,11 +306,11 @@ def __get_init_oidc_client(token_object: models.Token = None, token_type: str =
189
306
  token = ''
190
307
  if not token_type:
191
308
  token_type = kwargs.get('token_type', None)
192
- if token_type == 'subject_token':
309
+ if token_type == 'subject_token': # noqa: S105
193
310
  token = token_object.token
194
311
  # do not remove - even though None, oic expects this key to exist
195
312
  auth_args["redirect_uri"] = None
196
- if token_type == 'refresh_token':
313
+ if token_type == 'refresh_token': # noqa: S105
197
314
  token = token_object.refresh_token
198
315
  # do not remove - even though None, oic expects this key to exist
199
316
  auth_args["redirect_uri"] = None
@@ -219,7 +336,7 @@ def __get_init_oidc_client(token_object: models.Token = None, token_type: str =
219
336
  if not redirect_url:
220
337
  redirect_to = kwargs.get("redirect_to", "auth/oidc_token")
221
338
  redirect_urls = [u for u in client_secret["redirect_uris"] if redirect_to in u]
222
- redirect_url = random.choice(redirect_urls)
339
+ redirect_url = choice(redirect_urls)
223
340
  if not redirect_url:
224
341
  raise CannotAuthenticate("Could not pick any redirect URL(s) from the ones defined "
225
342
  + "in Rucio OIDC Client configuration file.") # NOQA: W503
@@ -275,7 +392,7 @@ def get_auth_oidc(account: str, *, session: "Session", **kwargs) -> str:
275
392
  OR a redirection url to be used in user's browser for authentication.
276
393
  """
277
394
  # TO-DO - implement a check if that account already has a valid
278
- # token withthe required scope and audience and return such token !
395
+ # token with the required scope and audience and return such token !
279
396
  auth_scope = kwargs.get('auth_scope', EXPECTED_OIDC_SCOPE)
280
397
  if not auth_scope:
281
398
  auth_scope = EXPECTED_OIDC_SCOPE
@@ -360,7 +477,12 @@ def get_auth_oidc(account: str, *, session: "Session", **kwargs) -> str:
360
477
 
361
478
 
362
479
  @transactional_session
363
- def get_token_oidc(auth_query_string: str, ip: str = None, *, session: "Session"):
480
+ def get_token_oidc(
481
+ auth_query_string: str,
482
+ ip: Optional[str] = None,
483
+ *,
484
+ session: "Session"
485
+ ) -> Optional[dict[str, Optional[Union[str, bool]]]]:
364
486
  """
365
487
  After Rucio User got redirected to Rucio /auth/oidc_token (or /auth/oidc_code)
366
488
  REST endpoints with authz code and session state encoded within the URL.
@@ -651,8 +773,8 @@ def get_token_for_account_operation(account: str, req_audience: str = None, req_
651
773
  # supported by Rucio server (have OIDC admin client registered as well)
652
774
  # that is why we take the issuer of the account identity that has an active/valid token
653
775
  # and look for admin account identity which has this issuer assigned
654
- # requestor should always have at least one active subject token unless it is root
655
- # this is why we first discover if the requestor is root or not
776
+ # requester should always have at least one active subject token unless it is root
777
+ # this is why we first discover if the requester is root or not
656
778
  get_token_for_adminacc = False
657
779
  admin_identity = None
658
780
  admin_issuer = None
@@ -662,10 +784,10 @@ def get_token_for_account_operation(account: str, req_audience: str = None, req_
662
784
  preferred_issuer = None
663
785
  for token in account_tokens:
664
786
  preferred_issuer = token.identity.split(", ")[1].split("=")[1]
665
- # loop through all OIDC identities registerd for the account of the requestor
787
+ # loop through all OIDC identities registered for the account of the requester
666
788
  for identity in identities:
667
789
  issuer = identity.split(", ")[1].split("=")[1]
668
- # compare the account of the requestor with the account of the admin
790
+ # compare the account of the requester with the account of the admin
669
791
  if account == admin_iss_acc_idt_dict[issuer][0]:
670
792
  # take first matching case which means root is requesting OIDC authentication
671
793
  admin_identity = admin_iss_acc_idt_dict[issuer][1]
@@ -765,7 +887,7 @@ def get_token_for_account_operation(account: str, req_audience: str = None, req_
765
887
  subject_token = token
766
888
  # if not proceed with token exchange
767
889
  if not subject_token:
768
- subject_token = random.choice(account_tokens)
890
+ subject_token = choice(account_tokens)
769
891
  exchanged_token = __exchange_token_oidc(subject_token,
770
892
  scope=req_scope,
771
893
  audience=req_audience,
@@ -813,7 +935,7 @@ def __exchange_token_oidc(subject_token_object: models.Token, *, session: "Sessi
813
935
  if not grant_type:
814
936
  grant_type = EXCHANGE_GRANT_TYPE
815
937
  try:
816
- oidc_dict = __get_init_oidc_client(token_object=subject_token_object, token_type="subject_token")
938
+ oidc_dict = __get_init_oidc_client(token_object=subject_token_object, token_type="subject_token") # noqa: S106
817
939
  oidc_client = oidc_dict['client']
818
940
  args = {"subject_token": subject_token_object.token,
819
941
  "scope": jwt_row_dict['authz_scope'],
@@ -891,7 +1013,7 @@ def __change_refresh_state(token: str, refresh: bool = False, *, session: "Sessi
891
1013
 
892
1014
 
893
1015
  @transactional_session
894
- def refresh_cli_auth_token(token_string: str, account: str, *, session: "Session"):
1016
+ def refresh_cli_auth_token(token_string: str, account: str, *, session: "Session") -> Optional[tuple[str, int]]:
895
1017
  """
896
1018
  Checks if there is active refresh token and if so returns
897
1019
  either active token with expiration timestamp or requests a new
@@ -1057,7 +1179,7 @@ def __refresh_token_oidc(token_object: models.Token, *, session: "Session"):
1057
1179
  if datetime.utcnow() - extra_dict['refresh_start'] > timedelta(hours=extra_dict['refresh_lifetime']):
1058
1180
  __change_refresh_state(token_object.token, refresh=False, session=session)
1059
1181
  return None
1060
- oidc_dict = __get_init_oidc_client(token_object=token_object, token_type="refresh_token")
1182
+ oidc_dict = __get_init_oidc_client(token_object=token_object, token_type="refresh_token") # noqa: S106
1061
1183
  oidc_client = oidc_dict['client']
1062
1184
  # getting a new refreshed set of tokens
1063
1185
  state = oidc_dict['state']
@@ -1169,7 +1291,7 @@ def __get_keyvalues_from_claims(token: str, keys=None):
1169
1291
  for key in keys:
1170
1292
  value = ''
1171
1293
  if key in claims:
1172
- value = val_to_space_sep_str(claims[key])
1294
+ value = val_to_space_sep_str(claims[key]) # type: ignore
1173
1295
  resdict[key] = value
1174
1296
  return resdict
1175
1297
  except Exception as error:
@@ -1293,7 +1415,7 @@ def validate_jwt(json_web_token: str, *, session: "Session") -> dict[str, Any]:
1293
1415
  # try to get it from IdP introspection endpoint
1294
1416
  # TO-BE-REMOVED - once all IdPs support scope and audience in token claims !!!
1295
1417
  if not token_dict['authz_scope'] or not token_dict['audience']:
1296
- clprocess = subprocess.Popen(['curl', '-s', '-L', '-u', '%s:%s'
1418
+ clprocess = subprocess.Popen(['curl', '-s', '-L', '-u', '%s:%s' # noqa: S607
1297
1419
  % (oidc_client.client_id, oidc_client.client_secret),
1298
1420
  '-d', 'token=%s' % (json_web_token),
1299
1421
  oidc_client.introspection_endpoint],
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +15,18 @@
16
15
  import importlib
17
16
  from configparser import NoOptionError, NoSectionError
18
17
  from os import environ
19
- from typing import TYPE_CHECKING
18
+ from typing import TYPE_CHECKING, Any
20
19
 
21
20
  from rucio.common import config, exception
22
21
  from rucio.common.utils import check_policy_package_version
23
22
 
24
23
  if TYPE_CHECKING:
25
24
  from typing import Optional
25
+
26
26
  from sqlalchemy.orm import Session
27
27
 
28
+ from rucio.common.types import InternalAccount
29
+
28
30
  # dictionary of permission modules for each VO
29
31
  permission_modules = {}
30
32
 
@@ -66,13 +68,15 @@ if not multivo:
66
68
 
67
69
  try:
68
70
  module = importlib.import_module(POLICY)
71
+ except ModuleNotFoundError:
72
+ raise exception.PolicyPackageNotFound(POLICY)
69
73
  except ImportError:
70
- raise exception.PolicyPackageNotFound('Module ' + POLICY + ' not found')
74
+ raise exception.ErrorLoadingPolicyPackage(POLICY)
71
75
 
72
76
  permission_modules["def"] = module
73
77
 
74
78
 
75
- def load_permission_for_vo(vo):
79
+ def load_permission_for_vo(vo: str) -> None:
76
80
  GENERIC_FALLBACK = 'generic_multi_vo'
77
81
  if config.config_has_section('policy'):
78
82
  try:
@@ -95,13 +99,21 @@ def load_permission_for_vo(vo):
95
99
 
96
100
  try:
97
101
  module = importlib.import_module(POLICY)
102
+ except ModuleNotFoundError:
103
+ raise exception.PolicyPackageNotFound(POLICY)
98
104
  except ImportError:
99
- raise exception.PolicyPackageNotFound('Module ' + POLICY + ' not found')
105
+ raise exception.ErrorLoadingPolicyPackage(POLICY)
100
106
 
101
107
  permission_modules[vo] = module
102
108
 
103
109
 
104
- def has_permission(issuer, action, kwargs, *, session: "Optional[Session]" = None):
110
+ def has_permission(
111
+ issuer: "InternalAccount",
112
+ action: str,
113
+ kwargs: dict[str, Any],
114
+ *,
115
+ session: "Optional[Session]" = None
116
+ ) -> bool:
105
117
  if issuer.vo not in permission_modules:
106
118
  load_permission_for_vo(issuer.vo)
107
119
  return permission_modules[issuer.vo].has_permission(issuer, action, kwargs, session=session)