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