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,1644 @@
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 json
16
+ import logging
17
+ import time
18
+ from typing import TYPE_CHECKING
19
+ from urllib.parse import urlparse
20
+
21
+ from flask import Blueprint, Flask, Response, redirect, render_template, request
22
+ from werkzeug.datastructures import Headers
23
+
24
+ from rucio.common.config import config_get
25
+ from rucio.common.exception import AccessDenied, CannotAuthenticate, CannotAuthorize, IdentityError, IdentityNotFound
26
+ from rucio.common.extra import import_extras
27
+ from rucio.common.utils import date_to_str
28
+ from rucio.core.authentication import strip_x509_proxy_attributes
29
+ from rucio.gateway.authentication import (
30
+ get_auth_oidc,
31
+ get_auth_token_gss,
32
+ get_auth_token_saml,
33
+ get_auth_token_ssh,
34
+ get_auth_token_user_pass,
35
+ get_auth_token_x509,
36
+ get_ssh_challenge_token,
37
+ get_token_oidc,
38
+ redirect_auth_oidc,
39
+ refresh_cli_auth_token,
40
+ validate_auth_token,
41
+ )
42
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, error_headers, extract_vo, generate_http_error_flask, get_account_from_verified_identity
43
+
44
+ if TYPE_CHECKING:
45
+
46
+ from flask.typing import ResponseReturnValue
47
+
48
+ EXTRA_MODULES = import_extras(['onelogin'])
49
+
50
+ if EXTRA_MODULES['onelogin']:
51
+ from onelogin.saml2.auth import OneLogin_Saml2_Auth # pylint: disable=import-error
52
+
53
+ from rucio.web.ui.flask.common.utils import prepare_saml_request
54
+
55
+
56
+ class UserPass(ErrorHandlingMethodView):
57
+ """
58
+ Authenticate a Rucio account temporarily via username and password.
59
+ """
60
+
61
+ def get_headers(self) -> Headers:
62
+ headers = Headers()
63
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
64
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
65
+ headers['Access-Control-Allow-Methods'] = '*'
66
+ headers['Access-Control-Allow-Credentials'] = 'true'
67
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token, X-Rucio-Auth-Token-Expires, X-Rucio-Auth-Account, X-Rucio-Auth-Accounts'
68
+ return headers
69
+
70
+ def options(self) -> 'ResponseReturnValue':
71
+ """
72
+ ---
73
+ summary: UserPass Allow cross-site scripting
74
+ description: UserPass Allow cross-site scripting. Explicit for Authentication.
75
+ tags:
76
+ - Auth
77
+ responses:
78
+ 200:
79
+ description: OK
80
+ headers:
81
+ Access-Control-Allow-Origin:
82
+ schema:
83
+ type: string
84
+ Access-Control-Allow-Headers:
85
+ schema:
86
+ type: string
87
+ Access-Control-Allow-Methods:
88
+ schema:
89
+ type: string
90
+ enum: ['*']
91
+ Access-Control-Allow-Credentials:
92
+ schema:
93
+ type: string
94
+ enum: ['true']
95
+ Access-Control-Expose-Headers:
96
+ schema:
97
+ type: string
98
+ enum: ['X-Rucio-Auth-Token']
99
+ 404:
100
+ description: Not found
101
+ """
102
+
103
+ return '', 200, self.get_headers()
104
+
105
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
106
+ def get(self) -> 'ResponseReturnValue':
107
+ """
108
+ ---
109
+ summary: UserPass
110
+ description: Authenticate a Rucio account temporarily via username and password.
111
+ tags:
112
+ - Auth
113
+ parameters:
114
+ - name: X-Rucio-Account
115
+ in: header
116
+ description: Account identifier as a string.
117
+ schema:
118
+ type: string
119
+ required: true
120
+ - name: X-Rucio-Username
121
+ in: header
122
+ description: Username as a string.
123
+ schema:
124
+ type: string
125
+ required: true
126
+ - name: X-Rucio-Password
127
+ in: header
128
+ description: password as a text-plain string.
129
+ schema:
130
+ type: string
131
+ required: true
132
+ - name: X-Rucio-AppID
133
+ in: header
134
+ description: Application identifier as a string.
135
+ schema:
136
+ type: string
137
+ - name: X-Forwarded-For
138
+ in: header
139
+ description: The forward ip address.
140
+ schema:
141
+ type: string
142
+ responses:
143
+ 200:
144
+ description: OK
145
+ headers:
146
+ Access-Control-Allow-Origin:
147
+ schema:
148
+ type: string
149
+ Access-Control-Allow-Headers:
150
+ schema:
151
+ type: string
152
+ Access-Control-Allow-Methods:
153
+ schema:
154
+ type: string
155
+ Access-Control-Allow-Credentials:
156
+ schema:
157
+ type: string
158
+ Access-Control-Expose-Headers:
159
+ schema:
160
+ type: string
161
+ X-Rucio-Auth-Token:
162
+ schema:
163
+ type: string
164
+ X-Rucio-Auth-Token-Expires:
165
+ schema:
166
+ type: string
167
+ X-Rucio-Auth-Account:
168
+ schema:
169
+ type: string
170
+ description: The rucio account used for authentication
171
+ 206:
172
+ description: Partial content containing X-Rucio-Auth-Accounts header
173
+ headers:
174
+ X-Rucio-Auth-Accounts:
175
+ schema:
176
+ type: string
177
+ description: The rucio accounts corresponding to the provided identity as a csv string
178
+
179
+ 401:
180
+ description: Cannot authenticate
181
+ """
182
+ headers = self.get_headers()
183
+
184
+ headers['Content-Type'] = 'application/octet-stream'
185
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
186
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
187
+ headers['Pragma'] = 'no-cache'
188
+ vo = extract_vo(request.headers)
189
+ account = request.headers.get('X-Rucio-Account', default=None)
190
+ username = request.headers.get('X-Rucio-Username', default=None)
191
+ password = request.headers.get('X-Rucio-Password', default=None)
192
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
193
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
194
+ if not username or not password:
195
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, 'Cannot authenticate without passing all required arguments', headers=headers)
196
+
197
+ accounts: list[str] = []
198
+ if not account:
199
+ try:
200
+ accounts = get_account_from_verified_identity(identity_key=username, id_type='USERPASS', password=password)
201
+ except IdentityNotFound:
202
+ return generate_http_error_flask(401, IdentityNotFound.__name__, 'Cannot authenticate. Username/Password pair does not exist.', headers=headers)
203
+ except IdentityError:
204
+ return generate_http_error_flask(401, IdentityError.__name__, 'Cannot authenticate. The identity does not exist.', headers=headers)
205
+ else:
206
+ accounts = [account]
207
+
208
+ if len(accounts) > 1:
209
+ account_names: list[str] = []
210
+ for account in accounts:
211
+ if isinstance(account, str):
212
+ account_names.append(account)
213
+ else:
214
+ account_names.append(account.external)
215
+ headers['X-Rucio-Auth-Accounts'] = ','.join(accounts)
216
+ return json.dumps(account_names), 206, headers
217
+
218
+ account = accounts[0]
219
+ account_name = account if isinstance(account, str) else account.external
220
+ try:
221
+ result = get_auth_token_user_pass(account_name, username, password, appid, ip, vo=vo)
222
+ if not result:
223
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, f'Cannot authenticate to account {account} with given credentials', headers=headers)
224
+ headers['X-Rucio-Auth-Account'] = account_name
225
+ headers['X-Rucio-Auth-Token'] = result['token']
226
+ headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result['expires_at']) # type: ignore (value could be None)
227
+ return '', 200, headers
228
+ except AccessDenied:
229
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, f'Cannot authenticate to account {account} with given credentials', headers=headers)
230
+
231
+
232
+ class OIDC(ErrorHandlingMethodView):
233
+ """
234
+ Requests a user specific Authorization URL (assigning a user session state,
235
+ nonce, Rucio OIDC Client ID with the correct issuers authentication endpoint).
236
+ """
237
+
238
+ def get_headers(self) -> Headers:
239
+ headers = Headers()
240
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
241
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
242
+ headers['Access-Control-Allow-Methods'] = '*'
243
+ headers['Access-Control-Allow-Credentials'] = 'true'
244
+ return headers
245
+
246
+ def options(self) -> 'ResponseReturnValue':
247
+ """
248
+ ---
249
+ summary: OIDC Allow cross-site scripting
250
+ description: OIDC Allow cross-site scripting. Explicit for Authentication.
251
+ tags:
252
+ - Auth
253
+ responses:
254
+ 200:
255
+ description: OK
256
+ headers:
257
+ Access-Control-Allow-Origin:
258
+ schema:
259
+ type: string
260
+ Access-Control-Allow-Headers:
261
+ schema:
262
+ type: string
263
+ Access-Control-Allow-Methods:
264
+ schema:
265
+ type: string
266
+ enum: ['*']
267
+ Access-Control-Allow-Credentials:
268
+ schema:
269
+ type: string
270
+ enum: ['true']
271
+ 404:
272
+ description: Not found
273
+ """
274
+
275
+ return '', 200, self.get_headers()
276
+
277
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
278
+ def get(self) -> 'ResponseReturnValue':
279
+ """
280
+ ---
281
+ summary: OIDC
282
+ description: Authenticate a Rucio account via OIDC.
283
+ tags:
284
+ - Auth
285
+ parameters:
286
+ - name: HTTP_X_RUCIO_ACCOUNT
287
+ in: header
288
+ description: Account identifier as a string.
289
+ schema:
290
+ type: string
291
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_SCOPE
292
+ in: header
293
+ schema:
294
+ type: string
295
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUDIENCE
296
+ in: header
297
+ schema:
298
+ type: string
299
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUTO
300
+ in: header
301
+ schema:
302
+ type: string
303
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_ISSUER
304
+ in: header
305
+ schema:
306
+ type: string
307
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_POLLING
308
+ in: header
309
+ schema:
310
+ type: string
311
+ - name: HTTP_X_RUCIO_CLIENT_AUTHORIZE_REFRESH_LIFETIME
312
+ in: header
313
+ schema:
314
+ type: string
315
+ - name: X-Forwarded-For
316
+ in: header
317
+ schema:
318
+ type: string
319
+ responses:
320
+ 200:
321
+ description: OK
322
+ headers:
323
+ X-Rucio-OIDC-Auth-URL:
324
+ description: User & Rucio OIDC Client specific Authorization URL
325
+ schema:
326
+ type: string
327
+ 401:
328
+ description: Cannot authenticate
329
+ """
330
+ headers = self.get_headers()
331
+
332
+ headers.set('Content-Type', 'application/octet-stream')
333
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
334
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
335
+ headers.set('Pragma', 'no-cache')
336
+
337
+ vo = extract_vo(request.headers)
338
+ account = request.environ.get('HTTP_X_RUCIO_ACCOUNT', 'webui')
339
+ auth_scope = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_SCOPE', "")
340
+ audience = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUDIENCE', "")
341
+ auto = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_AUTO', False)
342
+ issuer = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_ISSUER', None)
343
+ polling = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_POLLING', False)
344
+ refresh_lifetime = request.environ.get('HTTP_X_RUCIO_CLIENT_AUTHORIZE_REFRESH_LIFETIME', None)
345
+ auto = (auto == 'True' or auto == 'true')
346
+ polling = (polling == 'True' or polling == 'true')
347
+ if refresh_lifetime == 'None':
348
+ refresh_lifetime = None
349
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
350
+ try:
351
+ kwargs = {'auth_scope': auth_scope,
352
+ 'audience': audience,
353
+ 'issuer': issuer,
354
+ 'auto': auto,
355
+ 'polling': polling,
356
+ 'refresh_lifetime': refresh_lifetime,
357
+ 'ip': ip}
358
+ result = get_auth_oidc(account, vo=vo, **kwargs)
359
+ except AccessDenied:
360
+ return generate_http_error_flask(
361
+ status_code=401,
362
+ exc=CannotAuthenticate.__name__,
363
+ exc_msg=f'Cannot get authentication URL from Rucio Authentication Server for account {account}',
364
+ headers=headers
365
+ )
366
+
367
+ if not result:
368
+ return generate_http_error_flask(
369
+ status_code=401,
370
+ exc=CannotAuthenticate.__name__,
371
+ exc_msg=f'Cannot get authentication URL from Rucio Authentication Server for account {account}',
372
+ headers=headers
373
+ )
374
+
375
+ headers.set('X-Rucio-OIDC-Auth-URL', result)
376
+ return '', 200, headers
377
+
378
+
379
+ class RedirectOIDC(ErrorHandlingMethodView):
380
+ """
381
+ Authenticate a Rucio account via
382
+ an Identity Provider (XDC IAM as of June 2019).
383
+ """
384
+
385
+ def get_headers(self) -> Headers:
386
+ headers = Headers()
387
+ headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN')) # type: ignore (value could be None)
388
+ headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) # type: ignore (value could be None)
389
+ headers.set('Access-Control-Allow-Methods', '*')
390
+ headers.set('Access-Control-Allow-Credentials', 'true')
391
+ return headers
392
+
393
+ def options(self) -> 'ResponseReturnValue':
394
+ """
395
+ ---
396
+ summary: RedirectOIDC Allow cross-site scripting
397
+ description: RedirectOIDC Allow cross-site scripting. Explicit for Authentication.
398
+ tags:
399
+ - Auth
400
+ responses:
401
+ 200:
402
+ description: OK
403
+ headers:
404
+ Access-Control-Allow-Origin:
405
+ schema:
406
+ type: string
407
+ Access-Control-Allow-Headers:
408
+ schema:
409
+ type: string
410
+ Access-Control-Allow-Methods:
411
+ schema:
412
+ type: string
413
+ enum: ['*']
414
+ Access-Control-Allow-Credentials:
415
+ schema:
416
+ type: string
417
+ enum: ['true']
418
+ 404:
419
+ description: Not found
420
+ """
421
+ return '', 200, self.get_headers()
422
+
423
+ @check_accept_header_wrapper_flask(['application/octet-stream', 'text/html'])
424
+ def get(self) -> 'ResponseReturnValue':
425
+ """
426
+ ---
427
+ summary: RedirectOIDC
428
+ description: Authenticate a Rucio account via RedirectOIDC.
429
+ tags:
430
+ - Auth
431
+ parameters:
432
+ - name: X-Rucio-Client-Fetch-Token
433
+ in: header
434
+ schema:
435
+ type: string
436
+ responses:
437
+ 200:
438
+ description: OK
439
+ headers:
440
+ X-Rucio-Auth-Token:
441
+ description: The authentication token
442
+ schema:
443
+ type: string
444
+ Content-Type:
445
+ schema:
446
+ type: string
447
+ enum: ['application/octet-stream']
448
+ 303:
449
+ description: Redirect
450
+ 401:
451
+ description: Cannot authenticate
452
+ """
453
+ headers = self.get_headers()
454
+
455
+ # interaction with web browser - display response in html format
456
+ headers.set('Content-Type', 'text/html')
457
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
458
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
459
+ headers.set('Pragma', 'no-cache')
460
+
461
+ try:
462
+ fetchtoken = (request.headers.get('X-Rucio-Client-Fetch-Token', default=None) == 'True')
463
+ query_string = request.query_string.decode(encoding='utf-8')
464
+ result = redirect_auth_oidc(query_string, fetchtoken)
465
+ except AccessDenied:
466
+ headers.extend(error_headers(CannotAuthenticate.__name__, 'Cannot authorize your access, please check your access credentials'))
467
+ return render_template('auth_crash.html', crashtype='contact'), 401, headers
468
+ except Exception as error:
469
+ logging.exception("Internal Error")
470
+ headers.extend(error_headers(error.__class__.__name__, str(error.args[0])))
471
+ return render_template('auth_crash.html', crashtype='internal_error'), 500, headers
472
+
473
+ if not result:
474
+ headers.extend(error_headers(CannotAuthenticate.__name__, 'Cannot finalize your token request, no authorization content returned from the auth server'))
475
+ return render_template('auth_crash.html', crashtype='no_result'), 401, headers
476
+
477
+ if fetchtoken:
478
+ # this is only a case of returning the final token to the Rucio Client polling
479
+ # or requesting token after copy-pasting the Rucio code from the web page page
480
+ headers.set('Content-Type', 'application/octet-stream')
481
+ headers.set('X-Rucio-Auth-Token', result)
482
+ return '', 200, headers
483
+ else:
484
+ response = redirect(result, code=303)
485
+ response.headers.extend(headers)
486
+ return response
487
+
488
+
489
+ class CodeOIDC(ErrorHandlingMethodView):
490
+ """
491
+ IdP redirects to this endpoint with the AuthZ code
492
+ Rucio Auth server will request new token. This endpoint should be reached
493
+ only if the request/ IdP login has been made through web browser. Then the response
494
+ content will be in html (including the potential errors displayed).
495
+ The token will be saved in the Rucio DB, but only Rucio code will
496
+ be returned on the web page, or, in case of polling is True, successful
497
+ operation is confirmed waiting for the Rucio client to get the token automatically.
498
+ """
499
+
500
+ def get_headers(self) -> Headers:
501
+ headers = Headers()
502
+ headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN')) # type: ignore (value could be None)
503
+ headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) # type: ignore (value could be None)
504
+ headers.set('Access-Control-Allow-Methods', '*')
505
+ headers.set('Access-Control-Allow-Credentials', 'true')
506
+ return headers
507
+
508
+ def options(self) -> 'ResponseReturnValue':
509
+ """
510
+ ---
511
+ summary: CodeOIDC Allow cross-site scripting
512
+ description: CodeOIDC Allow cross-site scripting. Explicit for Authentication.
513
+ tags:
514
+ - Auth
515
+ responses:
516
+ 200:
517
+ description: OK
518
+ headers:
519
+ Access-Control-Allow-Origin:
520
+ schema:
521
+ type: string
522
+ Access-Control-Allow-Headers:
523
+ schema:
524
+ type: string
525
+ Access-Control-Allow-Methods:
526
+ schema:
527
+ type: string
528
+ enum: ['*']
529
+ Access-Control-Allow-Credentials:
530
+ schema:
531
+ type: string
532
+ enum: ['true']
533
+ 404:
534
+ description: Not found
535
+ """
536
+ return '', 200, self.get_headers()
537
+
538
+ @check_accept_header_wrapper_flask(['application/octet-stream', 'text/html'])
539
+ def get(self) -> 'ResponseReturnValue':
540
+ """
541
+ ---
542
+ summary: CodeOIDC
543
+ description: Authenticate a Rucio account via CodeOIDC.
544
+ tags:
545
+ - Auth
546
+ parameters:
547
+ - name: X-Forwarded-For
548
+ in: header
549
+ schema:
550
+ type: string
551
+ responses:
552
+ 200:
553
+ description: OK
554
+ 400:
555
+ description: Invalid request
556
+ """
557
+ headers = self.get_headers()
558
+
559
+ headers.set('Content-Type', 'text/html')
560
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
561
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
562
+ headers.set('Pragma', 'no-cache')
563
+
564
+ query_string = request.query_string.decode(encoding='utf-8')
565
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
566
+
567
+ try:
568
+ result = get_token_oidc(query_string, ip)
569
+ except AccessDenied:
570
+ headers.extend(error_headers(CannotAuthenticate.__name__, 'Cannot authorize your access, please check your access credentials'))
571
+ return render_template('auth_crash.html', crashtype='contact'), 401, headers
572
+ except Exception as error:
573
+ logging.exception("Internal Error")
574
+ headers.extend(error_headers(error.__class__.__name__, str(error.args[0])))
575
+ return render_template('auth_crash.html', crashtype='internal_error'), 500, headers
576
+
577
+ if not result:
578
+ headers.extend(error_headers(CannotAuthenticate.__name__, 'Cannot finalize your token request, no authorization content returned from the auth server'))
579
+ return render_template('auth_crash.html', crashtype='no_result'), 401, headers
580
+
581
+ if 'fetchcode' in result:
582
+ return render_template('auth_granted.html', authcode=result['fetchcode']), 200, headers
583
+ elif 'polling' in result and result['polling'] is True:
584
+ return render_template('auth_granted.html', authcode='allok'), 200, headers
585
+ else:
586
+ headers.extend(error_headers('InvalidRequest', 'Cannot recognize and process your request'))
587
+ return render_template('auth_crash.html', crashtype='bad_request'), 400, headers
588
+
589
+
590
+ class TokenOIDC(ErrorHandlingMethodView):
591
+ """
592
+ Authenticate a Rucio account temporarily via ID,
593
+ access (eventually save new refresh token)
594
+ received from an Identity Provider.
595
+ """
596
+
597
+ def get_headers(self) -> Headers:
598
+ headers = Headers()
599
+ headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN')) # type: ignore (value could be None)
600
+ headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) # type: ignore (value could be None)
601
+ headers.set('Access-Control-Allow-Methods', '*')
602
+ headers.set('Access-Control-Allow-Credentials', 'true')
603
+ return headers
604
+
605
+ def options(self) -> 'ResponseReturnValue':
606
+ """
607
+ ---
608
+ summary: TokenOIDC Allow cross-site scripting
609
+ description: TokenOIDC Allow cross-site scripting. Explicit for Authentication.
610
+ tags:
611
+ - Auth
612
+ responses:
613
+ 200:
614
+ description: OK
615
+ headers:
616
+ Access-Control-Allow-Origin:
617
+ schema:
618
+ type: string
619
+ Access-Control-Allow-Headers:
620
+ schema:
621
+ type: string
622
+ Access-Control-Allow-Methods:
623
+ schema:
624
+ type: string
625
+ enum: ['*']
626
+ Access-Control-Allow-Credentials:
627
+ schema:
628
+ type: string
629
+ enum: ['true']
630
+ 404:
631
+ description: Not found
632
+ """
633
+ return '', 200, self.get_headers()
634
+
635
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
636
+ def get(self) -> 'ResponseReturnValue':
637
+ """
638
+ ---
639
+ summary: TokenOIDC
640
+ description: Authenticate a Rucio account via TokenOIDC.
641
+ tags:
642
+ - Auth
643
+ parameters:
644
+ - name: X-Forwarded-For
645
+ in: header
646
+ schema:
647
+ type: string
648
+ responses:
649
+ 200:
650
+ description: OK
651
+ headers:
652
+ X-Rucio-Auth-Token:
653
+ description: The authentication token
654
+ schema:
655
+ type: string
656
+ X-Rucio-Auth-Token-Expires:
657
+ description: The time when the token expires
658
+ schema:
659
+ type: string
660
+ 401:
661
+ description: Cannot authenticate
662
+ """
663
+ headers = self.get_headers()
664
+
665
+ headers.set('Content-Type', 'application/octet-stream')
666
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
667
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
668
+ headers.set('Pragma', 'no-cache')
669
+
670
+ query_string = request.query_string.decode(encoding='utf-8')
671
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
672
+
673
+ try:
674
+ result = get_token_oidc(query_string, ip)
675
+ except AccessDenied:
676
+ return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers)
677
+
678
+ if not result:
679
+ return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers)
680
+ if 'token' in result and 'webhome' not in result:
681
+ headers.set('X-Rucio-Auth-Token', result['token']['token'])
682
+ headers.set('X-Rucio-Auth-Token-Expires', date_to_str(result['token']['expires_at'])) # type: ignore (value could be None)
683
+ return '', 200, headers
684
+ elif 'webhome' in result:
685
+ webhome = result['webhome']
686
+ if webhome is None:
687
+ headers.extend(error_headers(CannotAuthenticate.__name__, 'Cannot find your OIDC identity linked to any Rucio account'))
688
+ headers.set('Content-Type', 'text/html')
689
+ return render_template('auth_crash.html', crashtype='unknown_identity'), 401, headers
690
+ # domain setting is necessary so that the token gets distributed also to the webui server
691
+ domain = '.'.join(urlparse(webhome).netloc.split('.')[1:])
692
+ response = redirect(webhome, code=303)
693
+ response.headers.extend(headers)
694
+ response.set_cookie('x-rucio-auth-token', value=result['token']['token'], domain=domain, path='/')
695
+ response.set_cookie('rucio-auth-token-created-at', value=str(time.time()), domain=domain, path='/')
696
+ # response.set_cookie('x-rucio-auth-token', value=result['token']['token'])
697
+ # response.set_cookie('rucio-auth-token-created-at', value=str(time.time()))
698
+ return response
699
+ else:
700
+ return '', 400, headers
701
+
702
+
703
+ class RefreshOIDC(ErrorHandlingMethodView):
704
+ """
705
+ For a presented and access token which has equivalent in Rucio DB
706
+ (and also has refrech token in the Rucio DB) the class will attempt
707
+ token refresh and return a user a new refreshed token. If the presented token
708
+ is a result of a previous refresh happening in the last 10 min, the same token will be returned.
709
+ """
710
+
711
+ def get_headers(self) -> Headers:
712
+ headers = Headers()
713
+ headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN')) # type: ignore (value could be None)
714
+ headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) # type: ignore (value could be None)
715
+ headers.set('Access-Control-Allow-Methods', '*')
716
+ headers.set('Access-Control-Allow-Credentials', 'true')
717
+ headers.set('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')
718
+ return headers
719
+
720
+ def options(self) -> 'ResponseReturnValue':
721
+ """
722
+ ---
723
+ summary: RefreshOIDC Allow cross-site scripting
724
+ description: RefreshOIDC Allow cross-site scripting. Explicit for Authentication.
725
+ tags:
726
+ - Auth
727
+ responses:
728
+ 200:
729
+ description: OK
730
+ headers:
731
+ Access-Control-Allow-Origin:
732
+ schema:
733
+ type: string
734
+ Access-Control-Allow-Headers:
735
+ schema:
736
+ type: string
737
+ Access-Control-Allow-Methods:
738
+ schema:
739
+ type: string
740
+ enum: ['*']
741
+ Access-Control-Allow-Credentials:
742
+ schema:
743
+ type: string
744
+ enum: ['true']
745
+ Access-Control-Expose-Headers:
746
+ schema:
747
+ type: string
748
+ enum: ['X-Rucio-Auth-Token']
749
+ 404:
750
+ description: Not found
751
+ """
752
+ return '', 200, self.get_headers()
753
+
754
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
755
+ def get(self) -> 'ResponseReturnValue':
756
+ """
757
+ ---
758
+ summary: RefreshOIDC
759
+ description: Authenticate a Rucio account via RefreshOIDC.
760
+ tags:
761
+ - Auth
762
+ parameters:
763
+ - name: X-Rucio-Account
764
+ in: header
765
+ schema:
766
+ type: string
767
+ required: true
768
+ - name: X-Rucio-Auth-Token
769
+ in: header
770
+ schema:
771
+ type: string
772
+ required: true
773
+ responses:
774
+ 200:
775
+ description: OK
776
+ headers:
777
+ X-Rucio-Auth-Token:
778
+ description: The authentication token
779
+ schema:
780
+ type: string
781
+ X-Rucio-Auth-Token-Expires:
782
+ description: The time when the token expires
783
+ schema:
784
+ type: string
785
+ 401:
786
+ description: Cannot authenticate
787
+ """
788
+ headers = self.get_headers()
789
+
790
+ headers.set('Content-Type', 'application/octet-stream')
791
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
792
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
793
+ headers.set('Pragma', 'no-cache')
794
+
795
+ vo = extract_vo(request.headers)
796
+ account = request.headers.get('X-Rucio-Account', default=None)
797
+ token = request.headers.get('X-Rucio-Auth-Token', default=None)
798
+ if token is None or account is None:
799
+ return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers)
800
+
801
+ try:
802
+ result = refresh_cli_auth_token(token, account, vo=vo)
803
+ except AccessDenied:
804
+ return generate_http_error_flask(401, CannotAuthorize.__name__, 'Cannot authorize token request.', headers=headers)
805
+
806
+ if result is not None and len(result) > 1:
807
+ headers.set('X-Rucio-Auth-Token', str(result[0]))
808
+ headers.set('X-Rucio-Auth-Token-Expires', str(result[1]))
809
+ else:
810
+ headers.set('X-Rucio-Auth-Token', '')
811
+ headers.set('X-Rucio-Auth-Token-Expires', '')
812
+ return '', 200, headers
813
+
814
+
815
+ class GSS(ErrorHandlingMethodView):
816
+ """
817
+ Authenticate a Rucio account temporarily via a GSS token.
818
+ """
819
+
820
+ def get_headers(self) -> Headers:
821
+ headers = Headers()
822
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
823
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
824
+ headers['Access-Control-Allow-Methods'] = '*'
825
+ headers['Access-Control-Allow-Credentials'] = 'true'
826
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'
827
+ return headers
828
+
829
+ def options(self) -> 'ResponseReturnValue':
830
+ """
831
+ ---
832
+ summary: GSS Allow cross-site scripting
833
+ description: GSS Allow cross-site scripting. Explicit for Authentication.
834
+ tags:
835
+ - Auth
836
+ responses:
837
+ 200:
838
+ description: OK
839
+ headers:
840
+ Access-Control-Allow-Origin:
841
+ schema:
842
+ type: string
843
+ Access-Control-Allow-Headers:
844
+ schema:
845
+ type: string
846
+ Access-Control-Allow-Methods:
847
+ schema:
848
+ type: string
849
+ enum: ['*']
850
+ Access-Control-Allow-Credentials:
851
+ schema:
852
+ type: string
853
+ enum: ['true']
854
+ Access-Control-Expose-Headers:
855
+ schema:
856
+ type: string
857
+ enum: ['X-Rucio-Auth-Token']
858
+ 404:
859
+ description: Not found
860
+ """
861
+ return '', 200, self.get_headers()
862
+
863
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
864
+ def get(self) -> 'ResponseReturnValue':
865
+ """
866
+ ---
867
+ summary: GSS
868
+ description: Authenticate a Rucio account via GSS.
869
+ tags:
870
+ - Auth
871
+ parameters:
872
+ - name: X-Rucio-Account
873
+ in: header
874
+ schema:
875
+ type: string
876
+ required: true
877
+ - name: REMOTE_USER
878
+ in: header
879
+ schema:
880
+ type: string
881
+ required: true
882
+ - name: X-Rucio-AppID
883
+ in: header
884
+ schema:
885
+ type: string
886
+ - name: X-Forwarded-For
887
+ in: header
888
+ schema:
889
+ type: string
890
+ responses:
891
+ 200:
892
+ description: OK
893
+ headers:
894
+ X-Rucio-Auth-Token:
895
+ description: The authentication token
896
+ schema:
897
+ type: string
898
+ X-Rucio-Auth-Token-Expires:
899
+ description: The time when the token expires
900
+ schema:
901
+ type: string
902
+ 401:
903
+ description: Cannot authenticate
904
+ """
905
+
906
+ headers = self.get_headers()
907
+
908
+ headers['Content-Type'] = 'application/octet-stream'
909
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
910
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
911
+ headers['Pragma'] = 'no-cache'
912
+
913
+ vo = extract_vo(request.headers)
914
+ account = request.headers.get('X-Rucio-Account', default=None)
915
+ gsscred = request.environ.get('REMOTE_USER')
916
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
917
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
918
+
919
+ try:
920
+ result = get_auth_token_gss(account, gsscred, appid, ip, vo=vo)
921
+ except AccessDenied:
922
+ return generate_http_error_flask(
923
+ status_code=401,
924
+ exc=CannotAuthenticate.__name__,
925
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
926
+ headers=headers
927
+ )
928
+
929
+ if result is None:
930
+ return generate_http_error_flask(
931
+ status_code=401,
932
+ exc=CannotAuthenticate.__name__,
933
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
934
+ headers=headers
935
+ )
936
+
937
+ headers['X-Rucio-Auth-Token'] = result['token']
938
+ headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result['expires_at']) # type: ignore (value could be None)
939
+ return '', 200, headers
940
+
941
+
942
+ class x509(ErrorHandlingMethodView): # noqa: N801
943
+ """
944
+ Authenticate a Rucio account temporarily via an x509 certificate.
945
+ """
946
+
947
+ def get_headers(self) -> Headers:
948
+ headers = Headers()
949
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
950
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
951
+ headers['Access-Control-Allow-Methods'] = '*'
952
+ headers['Access-Control-Allow-Credentials'] = 'true'
953
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token, X-Rucio-Auth-Token-Expires, X-Rucio-Auth-Account, X-Rucio-Auth-Accounts'
954
+ return headers
955
+
956
+ def options(self) -> 'ResponseReturnValue':
957
+ """
958
+ ---
959
+ summary: x509 Allow cross-site scripting
960
+ description: x509 Allow cross-site scripting. Explicit for Authentication.
961
+ tags:
962
+ - Auth
963
+ responses:
964
+ 200:
965
+ description: OK
966
+ headers:
967
+ Access-Control-Allow-Origin:
968
+ schema:
969
+ type: string
970
+ Access-Control-Allow-Headers:
971
+ schema:
972
+ type: string
973
+ Access-Control-Allow-Methods:
974
+ schema:
975
+ type: string
976
+ enum: ['*']
977
+ Access-Control-Allow-Credentials:
978
+ schema:
979
+ type: string
980
+ enum: ['true']
981
+ Access-Control-Expose-Headers:
982
+ schema:
983
+ type: string
984
+ enum: ['X-Rucio-Auth-Token']
985
+ 404:
986
+ description: Not found
987
+ """
988
+ return '', 200, self.get_headers()
989
+
990
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
991
+ def get(self) -> 'ResponseReturnValue':
992
+ """
993
+ ---
994
+ summary: x509
995
+ description: Authenticate a Rucio account via x509.
996
+ tags:
997
+ - Auth
998
+ parameters:
999
+ - name: X-Rucio-Account
1000
+ in: header
1001
+ schema:
1002
+ type: string
1003
+ required: true
1004
+ - name: X-Rucio-AppID
1005
+ in: header
1006
+ schema:
1007
+ type: string
1008
+ - name: X-Forwarded-For
1009
+ in: header
1010
+ schema:
1011
+ type: string
1012
+ - name: X-Rucio-Allow-Return-Multiple-Accounts
1013
+ in: header
1014
+ schema:
1015
+ type: boolean
1016
+ description: If set to true, a HTTP 206 response will be returned if the identity is associated with multiple accounts.
1017
+ responses:
1018
+ 200:
1019
+ description: OK
1020
+ headers:
1021
+ X-Rucio-Auth-Token:
1022
+ description: The authentication token
1023
+ schema:
1024
+ type: string
1025
+ X-Rucio-Auth-Token-Expires:
1026
+ description: The time when the token expires
1027
+ schema:
1028
+ type: string
1029
+ X-Rucio-Auth-Account:
1030
+ description: The rucio account corresponding to the provided identity
1031
+ schema:
1032
+ type: string
1033
+ 206:
1034
+ description: Partial content containing X-Rucio-Auth-Accounts header
1035
+ headers:
1036
+ X-Rucio-Auth-Accounts:
1037
+ schema:
1038
+ type: string
1039
+ description: The rucio accounts corresponding to the provided identity as a csv string
1040
+ 401:
1041
+ description: Cannot authenticate
1042
+ """
1043
+ headers = self.get_headers()
1044
+ headers['Content-Type'] = 'application/octet-stream'
1045
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
1046
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
1047
+ headers['Pragma'] = 'no-cache'
1048
+
1049
+ vo = extract_vo(request.headers)
1050
+ account = request.headers.get('X-Rucio-Account', default=None)
1051
+ dn = request.environ.get('SSL_CLIENT_S_DN')
1052
+ if not dn:
1053
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, 'Cannot get DN', headers=headers)
1054
+ dn = strip_x509_proxy_attributes(dn)
1055
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
1056
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1057
+ return_multiple_accounts = request.headers.get('X-Rucio-Allow-Return-Multiple-Accounts', default=None)
1058
+
1059
+ accounts: list[str] = []
1060
+ if not account:
1061
+ try:
1062
+ accounts = get_account_from_verified_identity(identity_key=dn, id_type='X509')
1063
+ except IdentityError as e:
1064
+ return generate_http_error_flask(401, IdentityError.__name__, str(e), headers=headers)
1065
+ else:
1066
+ accounts = [account]
1067
+
1068
+ if len(accounts) > 1:
1069
+ if return_multiple_accounts is None or return_multiple_accounts.lower() != 'true':
1070
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, 'Multiple accounts associated with the provided identity', headers=headers)
1071
+ account_names: list[str] = []
1072
+ for account in accounts:
1073
+ if isinstance(account, str):
1074
+ account_names.append(account)
1075
+ else:
1076
+ account_names.append(account.external)
1077
+ headers['X-Rucio-Auth-Accounts'] = ','.join(accounts)
1078
+ return json.dumps(account_names), 206, headers
1079
+ account = accounts[0]
1080
+ account_name = account if isinstance(account, str) else account.external
1081
+ result = None
1082
+ try:
1083
+ result = get_auth_token_x509(account_name, dn, appid, ip, vo=vo)
1084
+ except AccessDenied:
1085
+ return generate_http_error_flask(
1086
+ status_code=401,
1087
+ exc=CannotAuthenticate.__name__,
1088
+ exc_msg=f'Cannot authenticate to account {account_name} with given credentials',
1089
+ headers=headers
1090
+ )
1091
+ except IdentityError as e:
1092
+ return generate_http_error_flask(
1093
+ status_code=401,
1094
+ exc=CannotAuthenticate.__name__,
1095
+ exc_msg=str(e),
1096
+ headers=headers
1097
+ )
1098
+
1099
+ if not result:
1100
+ return generate_http_error_flask(
1101
+ status_code=401,
1102
+ exc=CannotAuthenticate.__name__,
1103
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
1104
+ headers=headers
1105
+ )
1106
+ headers['X-Rucio-Auth-Token'] = result['token']
1107
+ headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result['expires_at']) # type: ignore (value could be None)
1108
+ headers['X-Rucio-Auth-Account'] = account
1109
+ return '', 200, headers
1110
+
1111
+
1112
+ class SSH(ErrorHandlingMethodView):
1113
+ """
1114
+ Authenticate a Rucio account temporarily via SSH key exchange.
1115
+ """
1116
+
1117
+ def get_headers(self) -> Headers:
1118
+ headers = Headers()
1119
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
1120
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
1121
+ headers['Access-Control-Allow-Methods'] = '*'
1122
+ headers['Access-Control-Allow-Credentials'] = 'true'
1123
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'
1124
+ return headers
1125
+
1126
+ def options(self) -> 'ResponseReturnValue':
1127
+ """
1128
+ ---
1129
+ summary: SSH Allow cross-site scripting
1130
+ description: SSH Allow cross-site scripting. Explicit for Authentication.
1131
+ tags:
1132
+ - Auth
1133
+ responses:
1134
+ 200:
1135
+ description: OK
1136
+ headers:
1137
+ Access-Control-Allow-Origin:
1138
+ schema:
1139
+ type: string
1140
+ Access-Control-Allow-Headers:
1141
+ schema:
1142
+ type: string
1143
+ Access-Control-Allow-Methods:
1144
+ schema:
1145
+ type: string
1146
+ enum: ['*']
1147
+ Access-Control-Allow-Credentials:
1148
+ schema:
1149
+ type: string
1150
+ enum: ['true']
1151
+ Access-Control-Expose-Headers:
1152
+ schema:
1153
+ type: string
1154
+ enum: ['X-Rucio-Auth-Token']
1155
+ 404:
1156
+ description: Not found
1157
+ """
1158
+ return '', 200, self.get_headers()
1159
+
1160
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
1161
+ def get(self) -> 'ResponseReturnValue':
1162
+ """
1163
+ ---
1164
+ summary: SSH
1165
+ description: Authenticate a Rucio account via SSH.
1166
+ tags:
1167
+ - Auth
1168
+ parameters:
1169
+ - name: X-Rucio-Account
1170
+ in: header
1171
+ schema:
1172
+ type: string
1173
+ required: true
1174
+ - name: X-Rucio-SSH-Signature
1175
+ in: header
1176
+ schema:
1177
+ type: string
1178
+ required: true
1179
+ - name: X-Rucio-AppID
1180
+ in: header
1181
+ schema:
1182
+ type: string
1183
+ - name: X-Forwarded-For
1184
+ in: header
1185
+ schema:
1186
+ type: string
1187
+ responses:
1188
+ 200:
1189
+ description: OK
1190
+ headers:
1191
+ X-Rucio-Auth-Token:
1192
+ description: The authentication token
1193
+ schema:
1194
+ type: string
1195
+ X-Rucio-Auth-Token-Expires:
1196
+ description: The time when the token expires
1197
+ schema:
1198
+ type: string
1199
+ 401:
1200
+ description: Cannot authenticate
1201
+ """
1202
+ headers = self.get_headers()
1203
+
1204
+ headers['Content-Type'] = 'application/octet-stream'
1205
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
1206
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
1207
+ headers['Pragma'] = 'no-cache'
1208
+
1209
+ vo = extract_vo(request.headers)
1210
+ account = request.headers.get('X-Rucio-Account', default=None)
1211
+ signature = request.headers.get('X-Rucio-SSH-Signature', default=None)
1212
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
1213
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1214
+
1215
+ try:
1216
+ result = get_auth_token_ssh(account, signature, appid, ip, vo=vo)
1217
+ except AccessDenied:
1218
+ return generate_http_error_flask(
1219
+ status_code=401,
1220
+ exc=CannotAuthenticate.__name__,
1221
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
1222
+ headers=headers
1223
+ )
1224
+
1225
+ if not result:
1226
+ return generate_http_error_flask(
1227
+ status_code=401,
1228
+ exc=CannotAuthenticate.__name__,
1229
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
1230
+ headers=headers
1231
+ )
1232
+
1233
+ headers['X-Rucio-Auth-Token'] = result['token']
1234
+ headers['X-Rucio-Auth-Token-Expires'] = date_to_str(result['expires_at']) # type: ignore (value could be None)
1235
+ return '', 200, headers
1236
+
1237
+
1238
+ class SSHChallengeToken(ErrorHandlingMethodView):
1239
+ """
1240
+ Request a challenge token for SSH authentication
1241
+ """
1242
+
1243
+ def get_headers(self) -> Headers:
1244
+ headers = Headers()
1245
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
1246
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
1247
+ headers['Access-Control-Allow-Methods'] = '*'
1248
+ headers['Access-Control-Allow-Credentials'] = 'true'
1249
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'
1250
+ return headers
1251
+
1252
+ def options(self) -> 'ResponseReturnValue':
1253
+ """
1254
+ ---
1255
+ summary: SSHChallengeToken Allow cross-site scripting
1256
+ description: SSHChallengeToken Allow cross-site scripting. Explicit for Authentication.
1257
+ tags:
1258
+ - Auth
1259
+ responses:
1260
+ 200:
1261
+ description: OK
1262
+ headers:
1263
+ Access-Control-Allow-Origin:
1264
+ schema:
1265
+ type: string
1266
+ Access-Control-Allow-Headers:
1267
+ schema:
1268
+ type: string
1269
+ Access-Control-Allow-Methods:
1270
+ schema:
1271
+ type: string
1272
+ enum: ['*']
1273
+ Access-Control-Allow-Credentials:
1274
+ schema:
1275
+ type: string
1276
+ enum: ['true']
1277
+ Access-Control-Expose-Headers:
1278
+ schema:
1279
+ type: string
1280
+ enum: ['X-Rucio-Auth-Token']
1281
+ 404:
1282
+ description: Not found
1283
+ """
1284
+ return '', 200, self.get_headers()
1285
+
1286
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
1287
+ def get(self) -> 'ResponseReturnValue':
1288
+ """
1289
+ ---
1290
+ summary: SSHChallengeToken
1291
+ description: Authenticate a Rucio account via SSHChallengeToken.
1292
+ tags:
1293
+ - Auth
1294
+ parameters:
1295
+ - name: X-Rucio-Account
1296
+ in: header
1297
+ schema:
1298
+ type: string
1299
+ required: true
1300
+ - name: X-Rucio-AppID
1301
+ in: header
1302
+ schema:
1303
+ type: string
1304
+ - name: X-Forwarded-For
1305
+ in: header
1306
+ schema:
1307
+ type: string
1308
+ responses:
1309
+ 200:
1310
+ description: OK
1311
+ headers:
1312
+ X-Rucio-SSH-Challenge-Token:
1313
+ description: The authentication token
1314
+ schema:
1315
+ type: string
1316
+ X-Rucio-SSH-Challenge-Token-Expires:
1317
+ description: The time when the token expires
1318
+ schema:
1319
+ type: string
1320
+ 401:
1321
+ description: Cannot authenticate
1322
+ """
1323
+ headers = self.get_headers()
1324
+
1325
+ headers['Content-Type'] = 'application/octet-stream'
1326
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
1327
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
1328
+ headers['Pragma'] = 'no-cache'
1329
+
1330
+ vo = extract_vo(request.headers)
1331
+ account = request.headers.get('X-Rucio-Account', default=None)
1332
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
1333
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1334
+
1335
+ result = get_ssh_challenge_token(account, appid, ip, vo=vo)
1336
+
1337
+ if not result:
1338
+ return generate_http_error_flask(
1339
+ status_code=401,
1340
+ exc=CannotAuthenticate.__name__,
1341
+ exc_msg=f'Cannot generate challenge for account {account}',
1342
+ headers=headers
1343
+ )
1344
+
1345
+ headers['X-Rucio-SSH-Challenge-Token'] = result['token']
1346
+ headers['X-Rucio-SSH-Challenge-Token-Expires'] = date_to_str(result['expires_at']) # type: ignore (value could be None)
1347
+ return '', 200, headers
1348
+
1349
+
1350
+ class SAML(ErrorHandlingMethodView):
1351
+ """
1352
+ Authenticate a Rucio account temporarily via CERN SSO.
1353
+ """
1354
+
1355
+ def get_headers(self) -> Headers:
1356
+ headers = Headers()
1357
+ headers.set('Access-Control-Allow-Origin', request.environ.get('HTTP_ORIGIN')) # type: ignore (value could be None)
1358
+ headers.set('Access-Control-Allow-Headers', request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) # type: ignore (value could be None)
1359
+ headers.set('Access-Control-Allow-Methods', '*')
1360
+ headers.set('Access-Control-Allow-Credentials', 'true')
1361
+ headers.set('Access-Control-Expose-Headers', 'X-Rucio-Auth-Token')
1362
+ return headers
1363
+
1364
+ def options(self) -> 'ResponseReturnValue':
1365
+ """
1366
+ ---
1367
+ summary: SAML Allow cross-site scripting
1368
+ description: SAML Allow cross-site scripting. Explicit for Authentication.
1369
+ tags:
1370
+ - Auth
1371
+ responses:
1372
+ 200:
1373
+ description: OK
1374
+ headers:
1375
+ Access-Control-Allow-Origin:
1376
+ schema:
1377
+ type: string
1378
+ Access-Control-Allow-Headers:
1379
+ schema:
1380
+ type: string
1381
+ Access-Control-Allow-Methods:
1382
+ schema:
1383
+ type: string
1384
+ enum: ['*']
1385
+ Access-Control-Allow-Credentials:
1386
+ schema:
1387
+ type: string
1388
+ enum: ['true']
1389
+ Access-Control-Expose-Headers:
1390
+ schema:
1391
+ type: string
1392
+ enum: ['X-Rucio-Auth-Token']
1393
+ 404:
1394
+ description: Not found
1395
+ """
1396
+ return '', 200, self.get_headers()
1397
+
1398
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
1399
+ def get(self) -> 'ResponseReturnValue':
1400
+ """
1401
+ ---
1402
+ summary: SAML
1403
+ description: Authenticate a Rucio account via SAML.
1404
+ tags:
1405
+ - Auth
1406
+ parameters:
1407
+ - name: X-Rucio-Account
1408
+ in: header
1409
+ schema:
1410
+ type: string
1411
+ required: true
1412
+ - name: X-Rucio-AppID
1413
+ in: header
1414
+ schema:
1415
+ type: string
1416
+ - name: X-Forwarded-For
1417
+ in: header
1418
+ schema:
1419
+ type: string
1420
+ responses:
1421
+ 200:
1422
+ description: OK
1423
+ headers:
1424
+ X-Rucio-Auth-Token:
1425
+ description: The authentication token
1426
+ schema:
1427
+ type: string
1428
+ X-Rucio-Auth-Token-Expires:
1429
+ description: The time when the token expires
1430
+ schema:
1431
+ type: string
1432
+ X-Rucio-SAML-Auth-URL:
1433
+ description: The time when the token expires
1434
+ schema:
1435
+ type: string
1436
+ 401:
1437
+ description: Cannot authenticate
1438
+ """
1439
+ headers = self.get_headers()
1440
+
1441
+ headers.set('Content-Type', 'application/octet-stream')
1442
+ headers.set('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
1443
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
1444
+ headers.set('Pragma', 'no-cache')
1445
+
1446
+ if not EXTRA_MODULES['onelogin']:
1447
+ return "SAML not configured on the server side.", 400, headers
1448
+
1449
+ saml_nameid = request.cookies.get('saml-nameid', default=None)
1450
+ vo = extract_vo(request.headers)
1451
+ account = request.headers.get('X-Rucio-Account', default=None)
1452
+ appid = request.headers.get('X-Rucio-AppID', default='unknown')
1453
+ ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1454
+
1455
+ if saml_nameid:
1456
+ try:
1457
+ result = get_auth_token_saml(account, saml_nameid, appid, ip, vo=vo)
1458
+ except AccessDenied:
1459
+ return generate_http_error_flask(
1460
+ status_code=401,
1461
+ exc=CannotAuthenticate.__name__,
1462
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
1463
+ headers=headers
1464
+ )
1465
+
1466
+ if not result:
1467
+ return generate_http_error_flask(
1468
+ status_code=401,
1469
+ exc=CannotAuthenticate.__name__,
1470
+ exc_msg=f'Cannot authenticate to account {account} with given credentials',
1471
+ headers=headers
1472
+ )
1473
+
1474
+ headers.set('X-Rucio-Auth-Token', result['token'])
1475
+ headers.set('X-Rucio-Auth-Token-Expires', date_to_str(result['expires_at'])) # type: ignore (value could be None)
1476
+ return '', 200, headers
1477
+
1478
+ # Path to the SAML config folder
1479
+ saml_path = config_get('saml', 'config_path')
1480
+
1481
+ req = prepare_saml_request(request.environ, dict(request.args.items(multi=False)))
1482
+ auth = OneLogin_Saml2_Auth(req, custom_base_path=saml_path)
1483
+
1484
+ headers.set('X-Rucio-SAML-Auth-URL', auth.login())
1485
+ return '', 200, headers
1486
+
1487
+ def post(self) -> 'ResponseReturnValue':
1488
+ """
1489
+ ---
1490
+ summary: Post a SAML request
1491
+ description: Post a SAML request
1492
+ tags:
1493
+ - Auth
1494
+ responses:
1495
+ 200:
1496
+ description: OK
1497
+ 401:
1498
+ description: Invalid Auth Token
1499
+ """
1500
+ if not EXTRA_MODULES['onelogin']:
1501
+ return "SAML not configured on the server side.", 200, [('X-Rucio-Auth-Token', '')]
1502
+
1503
+ saml_path = config_get('saml', 'config_path')
1504
+ req = prepare_saml_request(request.environ, dict(request.args.items(multi=False)))
1505
+ auth = OneLogin_Saml2_Auth(req, custom_base_path=saml_path)
1506
+
1507
+ auth.process_response()
1508
+ errors = auth.get_errors()
1509
+ if not errors:
1510
+ if auth.is_authenticated():
1511
+ response = Response()
1512
+ response.set_cookie('saml-nameid', value=auth.get_nameid(), path='/')
1513
+ return response
1514
+ return '', 200
1515
+
1516
+
1517
+ class Validate(ErrorHandlingMethodView):
1518
+ """
1519
+ Validate a Rucio Auth Token.
1520
+ """
1521
+
1522
+ def get_headers(self) -> Headers:
1523
+ headers = Headers()
1524
+ headers['Access-Control-Allow-Origin'] = request.environ.get('HTTP_ORIGIN') # type: ignore (value could be None)
1525
+ headers['Access-Control-Allow-Headers'] = request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') # type: ignore (value could be None)
1526
+ headers['Access-Control-Allow-Methods'] = '*'
1527
+ headers['Access-Control-Allow-Credentials'] = 'true'
1528
+ headers['Access-Control-Expose-Headers'] = 'X-Rucio-Auth-Token'
1529
+ return headers
1530
+
1531
+ def options(self) -> 'ResponseReturnValue':
1532
+ """
1533
+ ---
1534
+ summary: Validate Allow cross-site scripting
1535
+ description: Validate Allow cross-site scripting. Explicit for Authentication.
1536
+ tags:
1537
+ - Auth
1538
+ responses:
1539
+ 200:
1540
+ description: OK
1541
+ headers:
1542
+ Access-Control-Allow-Origin:
1543
+ schema:
1544
+ type: string
1545
+ Access-Control-Allow-Headers:
1546
+ schema:
1547
+ type: string
1548
+ Access-Control-Allow-Methods:
1549
+ schema:
1550
+ type: string
1551
+ enum: ['*']
1552
+ Access-Control-Allow-Credentials:
1553
+ schema:
1554
+ type: string
1555
+ enum: ['true']
1556
+ Access-Control-Expose-Headers:
1557
+ schema:
1558
+ type: string
1559
+ enum: ['X-Rucio-Auth-Token']
1560
+ 404:
1561
+ description: Not found
1562
+ """
1563
+ return '', 200, self.get_headers()
1564
+
1565
+ @check_accept_header_wrapper_flask(['application/octet-stream'])
1566
+ def get(self) -> 'ResponseReturnValue':
1567
+ """
1568
+ ---
1569
+ summary: Validate
1570
+ description: Validate a Rucio auth token.
1571
+ tags:
1572
+ - Auth
1573
+ parameters:
1574
+ - name: X-Rucio-Account
1575
+ in: header
1576
+ schema:
1577
+ type: string
1578
+ required: true
1579
+ responses:
1580
+ 200:
1581
+ description: OK
1582
+ 401:
1583
+ description: Cannot authenticate
1584
+ """
1585
+
1586
+ headers = self.get_headers()
1587
+
1588
+ headers['Content-Type'] = 'application/octet-stream'
1589
+ headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
1590
+ headers.add('Cache-Control', 'post-check=0, pre-check=0')
1591
+ headers['Pragma'] = 'no-cache'
1592
+
1593
+ token = request.headers.get('X-Rucio-Auth-Token', default=None)
1594
+
1595
+ result = validate_auth_token(token)
1596
+ if not result:
1597
+ return generate_http_error_flask(
1598
+ status_code=401,
1599
+ exc=CannotAuthenticate.__name__,
1600
+ exc_msg='Cannot authenticate with given credentials',
1601
+ headers=headers
1602
+ )
1603
+
1604
+ return str(result), 200, headers
1605
+
1606
+
1607
+ def blueprint() -> Blueprint:
1608
+ bp = Blueprint('auth', __name__, url_prefix='/auth')
1609
+
1610
+ user_pass_view = UserPass.as_view('user_pass')
1611
+ bp.add_url_rule('/userpass', view_func=user_pass_view, methods=['get', 'options'])
1612
+ gss_view = GSS.as_view('gss')
1613
+ bp.add_url_rule('/gss', view_func=gss_view, methods=['get', 'options'])
1614
+ x509_view = x509.as_view('x509')
1615
+ bp.add_url_rule('/x509', view_func=x509_view, methods=['get', 'options'])
1616
+ bp.add_url_rule('/x509/webui', view_func=x509_view, methods=['get', 'options'])
1617
+ bp.add_url_rule('/x509_proxy', view_func=x509_view, methods=['get', 'options'])
1618
+ ssh_view = SSH.as_view('ssh')
1619
+ bp.add_url_rule('/ssh', view_func=ssh_view, methods=['get', 'options'])
1620
+ ssh_challenge_token_view = SSHChallengeToken.as_view('ssh_challenge_token')
1621
+ bp.add_url_rule('/ssh_challenge_token', view_func=ssh_challenge_token_view, methods=['get', 'options'])
1622
+ saml_view = SAML.as_view('saml')
1623
+ bp.add_url_rule('/saml', view_func=saml_view, methods=['get', 'post', 'options'])
1624
+ validate_view = Validate.as_view('validate')
1625
+ bp.add_url_rule('/validate', view_func=validate_view, methods=['get', 'options'])
1626
+ oidc_view = OIDC.as_view('oidc_view')
1627
+ bp.add_url_rule('/oidc', view_func=oidc_view, methods=['get', 'options'])
1628
+ token_oidc_view = TokenOIDC.as_view('token_oidc_view')
1629
+ bp.add_url_rule('/oidc_token', view_func=token_oidc_view, methods=['get', 'options'])
1630
+ code_oidc_view = CodeOIDC.as_view('code_oidc_view')
1631
+ bp.add_url_rule('/oidc_code', view_func=code_oidc_view, methods=['get', 'options'])
1632
+ redirect_oidc_view = RedirectOIDC.as_view('redirect_oidc_view')
1633
+ bp.add_url_rule('/oidc_redirect', view_func=redirect_oidc_view, methods=['get', 'options'])
1634
+ refresh_oidc_view = RefreshOIDC.as_view('refresh_oidc_view')
1635
+ bp.add_url_rule('/oidc_refresh', view_func=refresh_oidc_view, methods=['get', 'options'])
1636
+
1637
+ return bp
1638
+
1639
+
1640
+ def make_doc() -> Flask:
1641
+ """ Only used for sphinx documentation """
1642
+ doc_app = Flask(__name__)
1643
+ doc_app.register_blueprint(blueprint())
1644
+ return doc_app