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