rucio 32.8.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rucio might be problematic. Click here for more details.

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