rucio 32.8.6__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 (481) hide show
  1. rucio/__init__.py +18 -0
  2. rucio/alembicrevision.py +16 -0
  3. rucio/api/__init__.py +14 -0
  4. rucio/api/account.py +266 -0
  5. rucio/api/account_limit.py +287 -0
  6. rucio/api/authentication.py +302 -0
  7. rucio/api/config.py +218 -0
  8. rucio/api/credential.py +60 -0
  9. rucio/api/did.py +726 -0
  10. rucio/api/dirac.py +71 -0
  11. rucio/api/exporter.py +60 -0
  12. rucio/api/heartbeat.py +62 -0
  13. rucio/api/identity.py +160 -0
  14. rucio/api/importer.py +46 -0
  15. rucio/api/lifetime_exception.py +95 -0
  16. rucio/api/lock.py +131 -0
  17. rucio/api/meta.py +85 -0
  18. rucio/api/permission.py +72 -0
  19. rucio/api/quarantined_replica.py +69 -0
  20. rucio/api/replica.py +528 -0
  21. rucio/api/request.py +220 -0
  22. rucio/api/rse.py +601 -0
  23. rucio/api/rule.py +335 -0
  24. rucio/api/scope.py +89 -0
  25. rucio/api/subscription.py +255 -0
  26. rucio/api/temporary_did.py +49 -0
  27. rucio/api/vo.py +112 -0
  28. rucio/client/__init__.py +16 -0
  29. rucio/client/accountclient.py +413 -0
  30. rucio/client/accountlimitclient.py +155 -0
  31. rucio/client/baseclient.py +929 -0
  32. rucio/client/client.py +77 -0
  33. rucio/client/configclient.py +113 -0
  34. rucio/client/credentialclient.py +54 -0
  35. rucio/client/didclient.py +691 -0
  36. rucio/client/diracclient.py +48 -0
  37. rucio/client/downloadclient.py +1674 -0
  38. rucio/client/exportclient.py +44 -0
  39. rucio/client/fileclient.py +51 -0
  40. rucio/client/importclient.py +42 -0
  41. rucio/client/lifetimeclient.py +74 -0
  42. rucio/client/lockclient.py +99 -0
  43. rucio/client/metaclient.py +137 -0
  44. rucio/client/pingclient.py +45 -0
  45. rucio/client/replicaclient.py +444 -0
  46. rucio/client/requestclient.py +109 -0
  47. rucio/client/rseclient.py +664 -0
  48. rucio/client/ruleclient.py +287 -0
  49. rucio/client/scopeclient.py +88 -0
  50. rucio/client/subscriptionclient.py +161 -0
  51. rucio/client/touchclient.py +78 -0
  52. rucio/client/uploadclient.py +871 -0
  53. rucio/common/__init__.py +14 -0
  54. rucio/common/cache.py +74 -0
  55. rucio/common/config.py +796 -0
  56. rucio/common/constants.py +92 -0
  57. rucio/common/constraints.py +18 -0
  58. rucio/common/didtype.py +187 -0
  59. rucio/common/dumper/__init__.py +306 -0
  60. rucio/common/dumper/consistency.py +449 -0
  61. rucio/common/dumper/data_models.py +325 -0
  62. rucio/common/dumper/path_parsing.py +65 -0
  63. rucio/common/exception.py +1092 -0
  64. rucio/common/extra.py +37 -0
  65. rucio/common/logging.py +404 -0
  66. rucio/common/pcache.py +1387 -0
  67. rucio/common/policy.py +84 -0
  68. rucio/common/schema/__init__.py +143 -0
  69. rucio/common/schema/atlas.py +411 -0
  70. rucio/common/schema/belleii.py +406 -0
  71. rucio/common/schema/cms.py +478 -0
  72. rucio/common/schema/domatpc.py +399 -0
  73. rucio/common/schema/escape.py +424 -0
  74. rucio/common/schema/generic.py +431 -0
  75. rucio/common/schema/generic_multi_vo.py +410 -0
  76. rucio/common/schema/icecube.py +404 -0
  77. rucio/common/schema/lsst.py +423 -0
  78. rucio/common/stomp_utils.py +160 -0
  79. rucio/common/stopwatch.py +56 -0
  80. rucio/common/test_rucio_server.py +148 -0
  81. rucio/common/types.py +158 -0
  82. rucio/common/utils.py +1946 -0
  83. rucio/core/__init__.py +14 -0
  84. rucio/core/account.py +426 -0
  85. rucio/core/account_counter.py +171 -0
  86. rucio/core/account_limit.py +357 -0
  87. rucio/core/authentication.py +563 -0
  88. rucio/core/config.py +386 -0
  89. rucio/core/credential.py +218 -0
  90. rucio/core/did.py +3102 -0
  91. rucio/core/did_meta_plugins/__init__.py +250 -0
  92. rucio/core/did_meta_plugins/did_column_meta.py +326 -0
  93. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
  94. rucio/core/did_meta_plugins/filter_engine.py +573 -0
  95. rucio/core/did_meta_plugins/json_meta.py +215 -0
  96. rucio/core/did_meta_plugins/mongo_meta.py +199 -0
  97. rucio/core/did_meta_plugins/postgres_meta.py +317 -0
  98. rucio/core/dirac.py +208 -0
  99. rucio/core/distance.py +164 -0
  100. rucio/core/exporter.py +59 -0
  101. rucio/core/heartbeat.py +263 -0
  102. rucio/core/identity.py +290 -0
  103. rucio/core/importer.py +248 -0
  104. rucio/core/lifetime_exception.py +377 -0
  105. rucio/core/lock.py +474 -0
  106. rucio/core/message.py +241 -0
  107. rucio/core/meta.py +190 -0
  108. rucio/core/monitor.py +441 -0
  109. rucio/core/naming_convention.py +154 -0
  110. rucio/core/nongrid_trace.py +124 -0
  111. rucio/core/oidc.py +1339 -0
  112. rucio/core/permission/__init__.py +107 -0
  113. rucio/core/permission/atlas.py +1333 -0
  114. rucio/core/permission/belleii.py +1076 -0
  115. rucio/core/permission/cms.py +1166 -0
  116. rucio/core/permission/escape.py +1076 -0
  117. rucio/core/permission/generic.py +1128 -0
  118. rucio/core/permission/generic_multi_vo.py +1148 -0
  119. rucio/core/quarantined_replica.py +190 -0
  120. rucio/core/replica.py +3627 -0
  121. rucio/core/replica_sorter.py +368 -0
  122. rucio/core/request.py +2241 -0
  123. rucio/core/rse.py +1835 -0
  124. rucio/core/rse_counter.py +155 -0
  125. rucio/core/rse_expression_parser.py +460 -0
  126. rucio/core/rse_selector.py +277 -0
  127. rucio/core/rule.py +3419 -0
  128. rucio/core/rule_grouping.py +1473 -0
  129. rucio/core/scope.py +152 -0
  130. rucio/core/subscription.py +316 -0
  131. rucio/core/temporary_did.py +188 -0
  132. rucio/core/topology.py +448 -0
  133. rucio/core/trace.py +361 -0
  134. rucio/core/transfer.py +1233 -0
  135. rucio/core/vo.py +151 -0
  136. rucio/core/volatile_replica.py +123 -0
  137. rucio/daemons/__init__.py +14 -0
  138. rucio/daemons/abacus/__init__.py +14 -0
  139. rucio/daemons/abacus/account.py +106 -0
  140. rucio/daemons/abacus/collection_replica.py +113 -0
  141. rucio/daemons/abacus/rse.py +107 -0
  142. rucio/daemons/atropos/__init__.py +14 -0
  143. rucio/daemons/atropos/atropos.py +243 -0
  144. rucio/daemons/auditor/__init__.py +261 -0
  145. rucio/daemons/auditor/hdfs.py +86 -0
  146. rucio/daemons/auditor/srmdumps.py +284 -0
  147. rucio/daemons/automatix/__init__.py +14 -0
  148. rucio/daemons/automatix/automatix.py +281 -0
  149. rucio/daemons/badreplicas/__init__.py +14 -0
  150. rucio/daemons/badreplicas/minos.py +311 -0
  151. rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
  152. rucio/daemons/badreplicas/necromancer.py +200 -0
  153. rucio/daemons/bb8/__init__.py +14 -0
  154. rucio/daemons/bb8/bb8.py +356 -0
  155. rucio/daemons/bb8/common.py +762 -0
  156. rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
  157. rucio/daemons/bb8/t2_background_rebalance.py +146 -0
  158. rucio/daemons/c3po/__init__.py +14 -0
  159. rucio/daemons/c3po/algorithms/__init__.py +14 -0
  160. rucio/daemons/c3po/algorithms/simple.py +131 -0
  161. rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
  162. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
  163. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
  164. rucio/daemons/c3po/c3po.py +342 -0
  165. rucio/daemons/c3po/collectors/__init__.py +14 -0
  166. rucio/daemons/c3po/collectors/agis.py +108 -0
  167. rucio/daemons/c3po/collectors/free_space.py +62 -0
  168. rucio/daemons/c3po/collectors/jedi_did.py +48 -0
  169. rucio/daemons/c3po/collectors/mock_did.py +46 -0
  170. rucio/daemons/c3po/collectors/network_metrics.py +63 -0
  171. rucio/daemons/c3po/collectors/workload.py +110 -0
  172. rucio/daemons/c3po/utils/__init__.py +14 -0
  173. rucio/daemons/c3po/utils/dataset_cache.py +40 -0
  174. rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
  175. rucio/daemons/c3po/utils/expiring_list.py +63 -0
  176. rucio/daemons/c3po/utils/popularity.py +82 -0
  177. rucio/daemons/c3po/utils/timeseries.py +76 -0
  178. rucio/daemons/cache/__init__.py +14 -0
  179. rucio/daemons/cache/consumer.py +191 -0
  180. rucio/daemons/common.py +391 -0
  181. rucio/daemons/conveyor/__init__.py +14 -0
  182. rucio/daemons/conveyor/common.py +530 -0
  183. rucio/daemons/conveyor/finisher.py +492 -0
  184. rucio/daemons/conveyor/poller.py +372 -0
  185. rucio/daemons/conveyor/preparer.py +198 -0
  186. rucio/daemons/conveyor/receiver.py +206 -0
  187. rucio/daemons/conveyor/stager.py +127 -0
  188. rucio/daemons/conveyor/submitter.py +379 -0
  189. rucio/daemons/conveyor/throttler.py +468 -0
  190. rucio/daemons/follower/__init__.py +14 -0
  191. rucio/daemons/follower/follower.py +97 -0
  192. rucio/daemons/hermes/__init__.py +14 -0
  193. rucio/daemons/hermes/hermes.py +738 -0
  194. rucio/daemons/judge/__init__.py +14 -0
  195. rucio/daemons/judge/cleaner.py +149 -0
  196. rucio/daemons/judge/evaluator.py +172 -0
  197. rucio/daemons/judge/injector.py +154 -0
  198. rucio/daemons/judge/repairer.py +144 -0
  199. rucio/daemons/oauthmanager/__init__.py +14 -0
  200. rucio/daemons/oauthmanager/oauthmanager.py +199 -0
  201. rucio/daemons/reaper/__init__.py +14 -0
  202. rucio/daemons/reaper/dark_reaper.py +272 -0
  203. rucio/daemons/reaper/light_reaper.py +255 -0
  204. rucio/daemons/reaper/reaper.py +701 -0
  205. rucio/daemons/replicarecoverer/__init__.py +14 -0
  206. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
  207. rucio/daemons/storage/__init__.py +14 -0
  208. rucio/daemons/storage/consistency/__init__.py +14 -0
  209. rucio/daemons/storage/consistency/actions.py +753 -0
  210. rucio/daemons/tracer/__init__.py +14 -0
  211. rucio/daemons/tracer/kronos.py +513 -0
  212. rucio/daemons/transmogrifier/__init__.py +14 -0
  213. rucio/daemons/transmogrifier/transmogrifier.py +753 -0
  214. rucio/daemons/undertaker/__init__.py +14 -0
  215. rucio/daemons/undertaker/undertaker.py +137 -0
  216. rucio/db/__init__.py +14 -0
  217. rucio/db/sqla/__init__.py +38 -0
  218. rucio/db/sqla/constants.py +192 -0
  219. rucio/db/sqla/migrate_repo/__init__.py +14 -0
  220. rucio/db/sqla/migrate_repo/env.py +111 -0
  221. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
  222. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
  223. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
  224. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
  225. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
  226. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
  227. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
  228. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
  229. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
  230. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
  231. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
  232. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
  233. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
  234. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
  235. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
  236. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
  237. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
  238. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
  239. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
  240. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
  241. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
  242. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
  243. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
  244. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
  245. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
  246. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
  247. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
  248. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
  249. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
  250. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
  251. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
  252. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
  254. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
  255. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
  256. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
  257. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
  258. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
  259. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
  260. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
  261. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
  262. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
  263. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
  264. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
  265. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
  266. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
  267. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
  268. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
  269. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
  270. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
  271. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
  273. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
  274. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
  275. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
  276. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
  277. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
  278. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
  279. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
  280. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
  281. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
  282. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
  283. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
  284. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
  285. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
  286. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
  287. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
  288. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
  289. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
  290. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
  291. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
  292. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
  293. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
  294. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
  296. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
  297. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
  298. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
  299. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
  300. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
  301. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
  302. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
  303. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
  304. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
  305. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
  306. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
  307. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
  308. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
  309. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
  311. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
  312. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
  313. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
  314. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
  315. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
  316. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
  317. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
  318. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
  319. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
  320. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
  321. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
  322. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
  323. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
  324. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
  325. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
  326. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
  327. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
  328. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
  329. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
  330. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
  331. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
  332. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
  333. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
  334. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
  335. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
  336. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
  337. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
  338. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
  339. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
  340. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
  341. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
  342. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
  343. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
  344. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
  345. rucio/db/sqla/models.py +1834 -0
  346. rucio/db/sqla/sautils.py +48 -0
  347. rucio/db/sqla/session.py +470 -0
  348. rucio/db/sqla/types.py +207 -0
  349. rucio/db/sqla/util.py +521 -0
  350. rucio/rse/__init__.py +97 -0
  351. rucio/rse/protocols/__init__.py +14 -0
  352. rucio/rse/protocols/cache.py +123 -0
  353. rucio/rse/protocols/dummy.py +112 -0
  354. rucio/rse/protocols/gfal.py +701 -0
  355. rucio/rse/protocols/globus.py +243 -0
  356. rucio/rse/protocols/gsiftp.py +93 -0
  357. rucio/rse/protocols/http_cache.py +83 -0
  358. rucio/rse/protocols/mock.py +124 -0
  359. rucio/rse/protocols/ngarc.py +210 -0
  360. rucio/rse/protocols/posix.py +251 -0
  361. rucio/rse/protocols/protocol.py +530 -0
  362. rucio/rse/protocols/rclone.py +365 -0
  363. rucio/rse/protocols/rfio.py +137 -0
  364. rucio/rse/protocols/srm.py +339 -0
  365. rucio/rse/protocols/ssh.py +414 -0
  366. rucio/rse/protocols/storm.py +207 -0
  367. rucio/rse/protocols/webdav.py +547 -0
  368. rucio/rse/protocols/xrootd.py +295 -0
  369. rucio/rse/rsemanager.py +752 -0
  370. rucio/tests/__init__.py +14 -0
  371. rucio/tests/common.py +244 -0
  372. rucio/tests/common_server.py +132 -0
  373. rucio/transfertool/__init__.py +14 -0
  374. rucio/transfertool/fts3.py +1484 -0
  375. rucio/transfertool/globus.py +200 -0
  376. rucio/transfertool/globus_library.py +182 -0
  377. rucio/transfertool/mock.py +81 -0
  378. rucio/transfertool/transfertool.py +212 -0
  379. rucio/vcsversion.py +11 -0
  380. rucio/version.py +46 -0
  381. rucio/web/__init__.py +14 -0
  382. rucio/web/rest/__init__.py +14 -0
  383. rucio/web/rest/flaskapi/__init__.py +14 -0
  384. rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
  385. rucio/web/rest/flaskapi/v1/__init__.py +14 -0
  386. rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
  387. rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
  388. rucio/web/rest/flaskapi/v1/archives.py +100 -0
  389. rucio/web/rest/flaskapi/v1/auth.py +1642 -0
  390. rucio/web/rest/flaskapi/v1/common.py +385 -0
  391. rucio/web/rest/flaskapi/v1/config.py +305 -0
  392. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  393. rucio/web/rest/flaskapi/v1/dids.py +2204 -0
  394. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  395. rucio/web/rest/flaskapi/v1/export.py +77 -0
  396. rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
  397. rucio/web/rest/flaskapi/v1/identities.py +263 -0
  398. rucio/web/rest/flaskapi/v1/import.py +133 -0
  399. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
  400. rucio/web/rest/flaskapi/v1/locks.py +360 -0
  401. rucio/web/rest/flaskapi/v1/main.py +83 -0
  402. rucio/web/rest/flaskapi/v1/meta.py +226 -0
  403. rucio/web/rest/flaskapi/v1/metrics.py +37 -0
  404. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  405. rucio/web/rest/flaskapi/v1/ping.py +89 -0
  406. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  407. rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
  408. rucio/web/rest/flaskapi/v1/requests.py +841 -0
  409. rucio/web/rest/flaskapi/v1/rses.py +2204 -0
  410. rucio/web/rest/flaskapi/v1/rules.py +824 -0
  411. rucio/web/rest/flaskapi/v1/scopes.py +161 -0
  412. rucio/web/rest/flaskapi/v1/subscriptions.py +646 -0
  413. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  414. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  415. rucio/web/rest/flaskapi/v1/tmp_dids.py +115 -0
  416. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  417. rucio/web/rest/flaskapi/v1/vos.py +280 -0
  418. rucio/web/rest/main.py +19 -0
  419. rucio/web/rest/metrics.py +28 -0
  420. rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
  421. rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  422. rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
  423. rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
  424. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  425. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  426. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  427. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  428. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  429. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  430. rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  431. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  432. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
  433. rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  434. rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
  435. rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
  436. rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
  437. rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
  438. rucio-32.8.6.data/scripts/rucio +2540 -0
  439. rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
  440. rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
  441. rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
  442. rucio-32.8.6.data/scripts/rucio-admin +2434 -0
  443. rucio-32.8.6.data/scripts/rucio-atropos +61 -0
  444. rucio-32.8.6.data/scripts/rucio-auditor +199 -0
  445. rucio-32.8.6.data/scripts/rucio-automatix +51 -0
  446. rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
  447. rucio-32.8.6.data/scripts/rucio-c3po +86 -0
  448. rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
  449. rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
  450. rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
  451. rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
  452. rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
  453. rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
  454. rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
  455. rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
  456. rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
  457. rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
  458. rucio-32.8.6.data/scripts/rucio-dumper +159 -0
  459. rucio-32.8.6.data/scripts/rucio-follower +45 -0
  460. rucio-32.8.6.data/scripts/rucio-hermes +55 -0
  461. rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
  462. rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
  463. rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
  464. rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
  465. rucio-32.8.6.data/scripts/rucio-kronos +45 -0
  466. rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
  467. rucio-32.8.6.data/scripts/rucio-minos +54 -0
  468. rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
  469. rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
  470. rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
  471. rucio-32.8.6.data/scripts/rucio-reaper +84 -0
  472. rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
  473. rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
  474. rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
  475. rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
  476. rucio-32.8.6.dist-info/METADATA +83 -0
  477. rucio-32.8.6.dist-info/RECORD +481 -0
  478. rucio-32.8.6.dist-info/WHEEL +5 -0
  479. rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
  480. rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
  481. rucio-32.8.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,563 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import datetime
17
+ import hashlib
18
+ import random
19
+ import re
20
+ import sys
21
+ import traceback
22
+ from base64 import b64decode
23
+ from typing import TYPE_CHECKING
24
+
25
+ import paramiko
26
+ from dogpile.cache import make_region
27
+ from dogpile.cache.api import NO_VALUE
28
+ from sqlalchemy import delete, null, or_, select
29
+
30
+ from rucio.common.cache import make_region_memcached
31
+ from rucio.common.config import config_get_bool
32
+ from rucio.common.exception import CannotAuthenticate, RucioException
33
+ from rucio.common.utils import chunks, generate_uuid, date_to_str
34
+ from rucio.core.account import account_exists
35
+ from rucio.core.oidc import validate_jwt
36
+ from rucio.db.sqla import filter_thread_work
37
+ from rucio.db.sqla import models
38
+ from rucio.db.sqla.constants import IdentityType
39
+ from rucio.db.sqla.session import read_session, transactional_session
40
+
41
+ if TYPE_CHECKING:
42
+ from sqlalchemy.orm import Session
43
+ from typing import Any, Union
44
+
45
+
46
+ def strip_x509_proxy_attributes(dn: str) -> str:
47
+ """Strip X509 proxy attributes from a DN.
48
+
49
+ When an X509 VOMS proxy certificate is produced, an additional Common Name
50
+ attribute is added to the subject of the original certificate. Its value
51
+ can take different forms. For proxy versions 3 and later (the default), the
52
+ value is a numeric. For previous versions, the value is exclusively one of
53
+ 'limited proxy' or 'proxy', depending on how it was produced (the most
54
+ trustworthy documentation on this seems to be the VOMS source code itself;
55
+ refer to the file sslutils.c). Note that this addition might happen more
56
+ than once (e.g. if a limited proxy is used to produce a full proxy).
57
+
58
+ By default, the Apache server will return the DN in an RFC-compliant format,
59
+ which can look like this:
60
+ CN=John Doe,OU=Users,DC=example,DC=com
61
+ However, in case the LegacyDNStringFormat of mod_ssl is enabled, then it can
62
+ look like this instead:
63
+ /DC=com/DC=example/OU=Users/CN=John Doe
64
+ In the first case, the Common Name attributes added by VOMS are prepended,
65
+ whereas in the second case, they are appended.
66
+
67
+ The motivation for stripping these attributes is to avoid having to store
68
+ multiple DNs in the database (as different identities).
69
+ """
70
+ if dn.startswith('/'):
71
+ regexp = r'(/CN=(limited proxy|proxy|[0-9]+))+$'
72
+ else:
73
+ regexp = r'^(CN=(limited proxy|proxy|[0-9]+),)+'
74
+
75
+ return re.sub(regexp, '', dn)
76
+
77
+
78
+ def token_key_generator(namespace, fni, **kwargs):
79
+ """ :returns: generate key function """
80
+ def generate_key(token, *, session: "Session"):
81
+ """ :returns: token """
82
+ return token
83
+ return generate_key
84
+
85
+
86
+ if config_get_bool('cache', 'use_external_cache_for_auth_tokens', default=False):
87
+ TOKENREGION = make_region_memcached(expiration_time=900, function_key_generator=token_key_generator)
88
+ else:
89
+ TOKENREGION = make_region(function_key_generator=token_key_generator).configure('dogpile.cache.memory', expiration_time=900)
90
+
91
+
92
+ @transactional_session
93
+ def get_auth_token_user_pass(account, username, password, appid, ip=None, *, session: "Session"):
94
+ """
95
+ Authenticate a Rucio account temporarily via username and password.
96
+
97
+ The token lifetime is 1 hour.
98
+
99
+ :param account: Account identifier as a string.
100
+ :param username: Username as a string.
101
+ :param password: SHA1 hash of the password as a string.
102
+ :param appid: The application identifier as a string.
103
+ :param ip: IP address of the client a a string.
104
+ :param session: The database session in use.
105
+
106
+ :returns: A dict with token and expires_at entries.
107
+ """
108
+
109
+ # Make sure the account exists
110
+ if not account_exists(account, session=session):
111
+ return None
112
+
113
+ query = select(
114
+ models.Identity
115
+ ).where(
116
+ models.Identity.identity == username,
117
+ models.Identity.identity_type == IdentityType.USERPASS
118
+ )
119
+ result = session.execute(query).scalar()
120
+
121
+ db_salt = result['salt']
122
+ db_password = result['password']
123
+
124
+ salted_password = db_salt + password.encode()
125
+ if db_password != hashlib.sha256(salted_password).hexdigest():
126
+ return None
127
+
128
+ # get account identifier
129
+ query = select(
130
+ models.IdentityAccountAssociation
131
+ ).where(
132
+ models.IdentityAccountAssociation.identity == username,
133
+ models.IdentityAccountAssociation.identity_type == IdentityType.USERPASS,
134
+ models.IdentityAccountAssociation.account == account
135
+ )
136
+ result = session.execute(query).scalar()
137
+
138
+ db_account = result['account']
139
+
140
+ # remove expired tokens
141
+ __delete_expired_tokens_account(account=account, session=session)
142
+
143
+ # create new rucio-auth-token for account
144
+ tuid = generate_uuid() # NOQA
145
+ token = f'{account}-{username}-{appid}-{tuid}'
146
+ new_token = models.Token(account=db_account, identity=username, token=token, ip=ip)
147
+ new_token.save(session=session)
148
+
149
+ return token_dictionary(new_token)
150
+
151
+
152
+ @transactional_session
153
+ def get_auth_token_x509(account, dn, appid, ip=None, *, session: "Session"):
154
+ """
155
+ Authenticate a Rucio account temporarily via an x509 certificate.
156
+
157
+ The token lifetime is 1 hour.
158
+
159
+ :param account: Account identifier as a string.
160
+ :param dn: Client certificate distinguished name string, as extracted by Apache/mod_ssl.
161
+ :param id: The application identifier as a string.
162
+ :param ipaddr: IP address of the client as a string.
163
+ :param session: The database session in use.
164
+
165
+ :returns: A dict with token and expires_at entries.
166
+ """
167
+
168
+ # Make sure the account exists
169
+ if not account_exists(account, session=session):
170
+ return None
171
+
172
+ # remove expired tokens
173
+ __delete_expired_tokens_account(account=account, session=session)
174
+
175
+ # create new rucio-auth-token for account
176
+ tuid = generate_uuid() # NOQA
177
+ token = f'{account}-{dn}-{appid}-{tuid}'
178
+ new_token = models.Token(account=account, identity=dn, token=token, ip=ip)
179
+ new_token.save(session=session)
180
+
181
+ return token_dictionary(new_token)
182
+
183
+
184
+ @transactional_session
185
+ def get_auth_token_gss(account, gsstoken, appid, ip=None, *, session: "Session"):
186
+ """
187
+ Authenticate a Rucio account temporarily via a GSS token.
188
+
189
+ The token lifetime is 1 hour.
190
+
191
+ :param account: Account identifier as a string.
192
+ :param gsscred: GSS principal@REALM as a string.
193
+ :param appid: The application identifier as a string.
194
+ :param ip: IP address of the client as a string.
195
+ :param session: The database session in use.
196
+
197
+ :returns: A dict with token and expires_at entries.
198
+ """
199
+
200
+ # Make sure the account exists
201
+ if not account_exists(account, session=session):
202
+ return None
203
+
204
+ # remove expired tokens
205
+ __delete_expired_tokens_account(account=account, session=session)
206
+
207
+ # create new rucio-auth-token for account
208
+ tuid = generate_uuid() # NOQA
209
+ token = f'{account}-{gsstoken}-{appid}-{tuid}'
210
+ new_token = models.Token(account=account, token=token, ip=ip)
211
+ new_token.save(session=session)
212
+
213
+ return token_dictionary(new_token)
214
+
215
+
216
+ @transactional_session
217
+ def get_auth_token_ssh(account, signature, appid, ip=None, *, session: "Session"):
218
+ """
219
+ Authenticate a Rucio account temporarily via SSH key exchange.
220
+
221
+ The token lifetime is 1 hour.
222
+
223
+ :param account: Account identifier as a string.
224
+ :param signature: Response to server challenge signed with SSH private key as string.
225
+ :param appid: The application identifier as a string.
226
+ :param ip: IP address of the client as a string.
227
+ :param session: The database session in use.
228
+
229
+ :returns: A dict with token and expires_at entries.
230
+ """
231
+ if not isinstance(signature, bytes):
232
+ signature = signature.encode()
233
+
234
+ # Make sure the account exists
235
+ if not account_exists(account, session=session):
236
+ return None
237
+
238
+ # get all active challenge tokens for the requested account
239
+ query = select(
240
+ models.Token
241
+ ).where(
242
+ models.Token.expired_at >= datetime.datetime.utcnow(),
243
+ models.Token.account == account,
244
+ models.Token.token.like('challenge-%')
245
+ )
246
+ active_challenge_tokens = session.execute(query).scalars().all()
247
+
248
+ # get all identities for the requested account
249
+ query = select(
250
+ models.IdentityAccountAssociation
251
+ ).where(
252
+ models.IdentityAccountAssociation.identity_type == IdentityType.SSH,
253
+ models.IdentityAccountAssociation.account == account
254
+ )
255
+ identities = session.execute(query).scalars().all()
256
+
257
+ # no challenge tokens found
258
+ if not active_challenge_tokens:
259
+ return None
260
+
261
+ # try all available SSH identities for the account with the provided signature
262
+ match = False
263
+ for identity in identities:
264
+ data = identity['identity'].split()[1]
265
+ data += '=' * ((4 - len(data) % 4) % 4) # adding required padding
266
+ pub_k = paramiko.RSAKey(data=b64decode(data))
267
+ for challenge_token in active_challenge_tokens:
268
+ if pub_k.verify_ssh_sig(str(challenge_token['token']).encode(),
269
+ paramiko.Message(signature)):
270
+ match = True
271
+ break
272
+ if match:
273
+ break
274
+
275
+ if not match:
276
+ return None
277
+
278
+ # remove expired tokens
279
+ __delete_expired_tokens_account(account=account, session=session)
280
+
281
+ # create new rucio-auth-token for account
282
+ tuid = generate_uuid() # NOQA
283
+ token = f'{account}-ssh:pubkey-{appid}-{tuid}'
284
+ new_token = models.Token(account=account, token=token, ip=ip)
285
+ new_token.save(session=session)
286
+
287
+ return token_dictionary(new_token)
288
+
289
+
290
+ @transactional_session
291
+ def get_ssh_challenge_token(account, appid, ip=None, *, session: "Session"):
292
+ """
293
+ Prepare a challenge token for subsequent SSH public key authentication.
294
+
295
+ The challenge lifetime is fixed to 10 seconds.
296
+
297
+ :param account: Account identifier as a string.
298
+ :param appid: The application identifier as a string.
299
+ :param ip: IP address of the client as a string.
300
+
301
+ :returns: A dict with token and expires_at entries.
302
+ """
303
+
304
+ # Make sure the account exists
305
+ if not account_exists(account, session=session):
306
+ return None
307
+
308
+ # Cryptographically secure random number.
309
+ # This requires a /dev/urandom like device from the OS
310
+ rng = random.SystemRandom()
311
+ crypto_rand = rng.randint(0, sys.maxsize)
312
+
313
+ # give the client 10 seconds max to sign the challenge token
314
+ expiration = datetime.datetime.utcnow() + datetime.timedelta(seconds=10)
315
+ expiration_unix = expiration.strftime("%s")
316
+
317
+ challenge_token = f'challenge-{crypto_rand}-{account}-{expiration_unix}'
318
+
319
+ new_challenge_token = models.Token(account=account, token=challenge_token, ip=ip,
320
+ expired_at=expiration)
321
+ new_challenge_token.save(session=session)
322
+
323
+ return token_dictionary(new_challenge_token)
324
+
325
+
326
+ @transactional_session
327
+ def get_auth_token_saml(account, saml_nameid, appid, ip=None, *, session: "Session"):
328
+ """
329
+ Authenticate a Rucio account temporarily via SAML.
330
+
331
+ The token lifetime is 1 hour.
332
+
333
+ :param account: Account identifier as a string.
334
+ :param saml_nameid: SAML NameID of the client.
335
+ :param appid: The application identifier as a string.
336
+ :param ip: IP address of the client a a string.
337
+ :param session: The database session in use.
338
+
339
+ :returns: A dict with token and expires_at entries.
340
+ """
341
+
342
+ # Make sure the account exists
343
+ if not account_exists(account, session=session):
344
+ return None
345
+
346
+ # remove expired tokens
347
+ __delete_expired_tokens_account(account=account, session=session)
348
+
349
+ tuid = generate_uuid() # NOQA
350
+ token = f'{account}-{saml_nameid}-{appid}-{tuid}'
351
+ new_token = models.Token(account=account, identity=saml_nameid, token=token, ip=ip)
352
+ new_token.save(session=session)
353
+
354
+ return token_dictionary(new_token)
355
+
356
+
357
+ @transactional_session
358
+ def redirect_auth_oidc(auth_code, fetchtoken=False, *, session: "Session"):
359
+ """
360
+ Finds the Authentication URL in the Rucio DB oauth_requests table
361
+ and redirects user's browser to this URL.
362
+
363
+ :param auth_code: Rucio assigned code to redirect
364
+ authorization securely to IdP via Rucio Auth server through a browser.
365
+ :param fetchtoken: If True, valid token temporarily saved in the oauth_requests table
366
+ will be returned. If False, redirection URL is returned.
367
+ :param session: The database session in use.
368
+
369
+ :returns: result of the query (authorization URL or a
370
+ token if a user asks with the correct code) or None.
371
+ Exception thrown in case of an unexpected crash.
372
+
373
+ """
374
+ try:
375
+ query = select(
376
+ models.OAuthRequest.redirect_msg
377
+ ).where(
378
+ models.OAuthRequest.access_msg == auth_code
379
+ )
380
+ redirect_result = session.execute(query).scalar()
381
+
382
+ if not redirect_result:
383
+ return None
384
+
385
+ if 'http' not in redirect_result and fetchtoken:
386
+ # in this case the function check if the value is a valid token
387
+ vdict = validate_auth_token(redirect_result, session=session)
388
+ if vdict:
389
+ return redirect_result
390
+ return None
391
+ elif 'http' in redirect_result and not fetchtoken:
392
+ # return redirection URL
393
+ return redirect_result
394
+ except:
395
+ raise CannotAuthenticate(traceback.format_exc())
396
+
397
+
398
+ @transactional_session
399
+ def delete_expired_tokens(total_workers, worker_number, limit=1000, *, session: "Session"):
400
+ """
401
+ Delete expired tokens.
402
+
403
+ :param total_workers: Number of total workers.
404
+ :param worker_number: id of the executing worker.
405
+ :param limit: Maximum number of tokens to delete.
406
+ :param session: Database session in use.
407
+
408
+ :returns: number of deleted rows
409
+ """
410
+
411
+ # get expired tokens
412
+ try:
413
+ # delete all expired tokens except tokens which have refresh token that is still valid
414
+ query = select(
415
+ models.Token.token
416
+ ).where(
417
+ models.Token.expired_at <= datetime.datetime.utcnow(),
418
+ or_(
419
+ models.Token.refresh_expired_at == null(),
420
+ models.Token.refresh_expired_at <= datetime.datetime.utcnow()
421
+ )
422
+ ).order_by(
423
+ models.Token.expired_at
424
+ )
425
+
426
+ query = filter_thread_work(session=session, query=query, total_threads=total_workers, thread_id=worker_number, hash_variable='token')
427
+
428
+ # limiting the number of tokens deleted at once
429
+ query = query.limit(limit)
430
+ # Oracle does not support chaining order_by(), limit(), and
431
+ # with_for_update(). Use a nested query to overcome this.
432
+ if session.bind.dialect.name == 'oracle':
433
+ query = select(
434
+ models.Token.token
435
+ ).where(
436
+ models.Token.token.in_(query)
437
+ ).with_for_update(
438
+ skip_locked=True
439
+ )
440
+ else:
441
+ query = query.with_for_update(skip_locked=True)
442
+ # remove expired tokens
443
+ deleted_tokens = 0
444
+ for tokens in session.execute(query).scalars().partitions(10):
445
+ query = delete(
446
+ models.Token
447
+ ).where(
448
+ models.Token.token.in_(tokens)
449
+ )
450
+ deleted_tokens += session.execute(query).rowcount
451
+
452
+ except Exception as error:
453
+ raise RucioException(error.args)
454
+
455
+ return deleted_tokens
456
+
457
+
458
+ @read_session
459
+ def query_token(token, *, session: "Session"):
460
+ """
461
+ Validate an authentication token using the database. This method will only be called
462
+ if no entry could be found in the according cache.
463
+
464
+ :param token: Authentication token as a variable-length string.
465
+ :param session: The database session in use.
466
+
467
+ :returns: dictionary { account: <account name>,
468
+ identity: <identity>,
469
+ lifetime: <token lifetime>,
470
+ audience: <audience>,
471
+ authz_scope: <authz_scope> }
472
+ if successful, None otherwise.
473
+ """
474
+ # Query the DB to validate token
475
+ query = select(
476
+ models.Token.account,
477
+ models.Token.identity,
478
+ models.Token.expired_at.label('lifetime'),
479
+ models.Token.audience,
480
+ models.Token.oidc_scope.label('authz_scope')
481
+ ).where(
482
+ models.Token.token == token,
483
+ models.Token.expired_at > datetime.datetime.utcnow()
484
+ )
485
+ result = session.execute(query).first()
486
+ if result:
487
+ return result._asdict()
488
+ return None
489
+
490
+
491
+ @transactional_session
492
+ def validate_auth_token(token: str, *, session: "Session") -> "dict[str, Any]":
493
+ """
494
+ Validate an authentication token.
495
+
496
+ :param token: Authentication token as a variable-length string.
497
+
498
+ :returns: dictionary { account: <account name>,
499
+ identity: <identity>,
500
+ lifetime: <token lifetime>,
501
+ audience: <audience>,
502
+ authz_scope: <authz_scope> }
503
+ if successful
504
+ :raises: CannotAuthenticate if unsuccessful
505
+ """
506
+ if not token:
507
+ raise CannotAuthenticate("No token was passed!")
508
+
509
+ # Be gentle with bash variables, there can be whitespace
510
+ token = token.strip()
511
+ cache_key = token.replace(' ', '')
512
+
513
+ # Check if token ca be found in cache region
514
+ value: "Union[NO_VALUE, dict[str, Any]]" = TOKENREGION.get(cache_key)
515
+ if value is NO_VALUE: # no cached entry found
516
+ value = query_token(token, session=session)
517
+ if not value:
518
+ # identify JWT access token and validte
519
+ # & save it in Rucio if scope and audience are correct
520
+ if len(token.split(".")) == 3:
521
+ value = validate_jwt(token, session=session)
522
+ else:
523
+ raise CannotAuthenticate(traceback.format_exc())
524
+ # save token in the cache
525
+ TOKENREGION.set(cache_key, value)
526
+ lifetime = value.get('lifetime', datetime.datetime(1970, 1, 1))
527
+ if lifetime < datetime.datetime.utcnow(): # check if expired
528
+ TOKENREGION.delete(cache_key)
529
+ raise CannotAuthenticate(f"Token found but expired since {date_to_str(lifetime)}.")
530
+ return value
531
+
532
+
533
+ def token_dictionary(token: models.Token):
534
+ return {'token': token.token, 'expires_at': token.expired_at}
535
+
536
+
537
+ @transactional_session
538
+ def __delete_expired_tokens_account(account, *, session: "Session"):
539
+ """"
540
+ Deletes expired tokens from the database.
541
+
542
+ :param account: Account to delete expired tokens.
543
+ :param session: The database session in use.
544
+ """
545
+ select_query = select(
546
+ models.Token.token
547
+ ).where(
548
+ models.Token.expired_at < datetime.datetime.utcnow(),
549
+ models.Token.account == account
550
+ ).with_for_update(
551
+ skip_locked=True
552
+ )
553
+ tokens = session.execute(select_query).scalars().all()
554
+
555
+ for chunk in chunks(tokens, 100):
556
+ delete_query = delete(
557
+ models.Token
558
+ ).prefix_with(
559
+ "/*+ INDEX(TOKENS_ACCOUNT_EXPIRED_AT_IDX) */"
560
+ ).where(
561
+ models.Token.token.in_(chunk)
562
+ )
563
+ session.execute(delete_query)