├── core ├── LICENSE ├── Cargo.toml ├── README.md └── src │ └── utils.rs ├── reqsign ├── LICENSE ├── src │ ├── lib.rs │ ├── oracle.rs │ ├── tencent.rs │ ├── aliyun.rs │ ├── huaweicloud.rs │ ├── context.rs │ ├── azure.rs │ └── aws.rs ├── examples │ ├── aws.rs │ ├── azure.rs │ └── google.rs └── Cargo.toml ├── services ├── aws-v4 │ ├── LICENSE │ ├── testdata │ │ ├── .gitignore │ │ ├── default_credential │ │ └── default_config │ ├── tests │ │ ├── main.rs │ │ ├── mocks │ │ │ ├── ecs_mock_server.py │ │ │ ├── credential_process_helper.py │ │ │ ├── sso_mock_server.py │ │ │ └── README.md │ │ ├── credential_providers │ │ │ ├── imds.rs │ │ │ ├── cognito.rs │ │ │ ├── process.rs │ │ │ ├── s3_express.rs │ │ │ ├── sso.rs │ │ │ ├── profile.rs │ │ │ ├── env.rs │ │ │ ├── assume_role.rs │ │ │ ├── mod.rs │ │ │ ├── assume_role_with_web_identity.rs │ │ │ └── ecs.rs │ │ └── signing │ │ │ └── special_chars.rs │ ├── src │ │ ├── provide_credential │ │ │ └── mod.rs │ │ ├── constants.rs │ │ └── credential.rs │ └── Cargo.toml ├── google │ ├── LICENSE │ ├── testdata │ │ ├── test_impersonated_service_account.json │ │ ├── test_external_account.json │ │ ├── test_credential.json │ │ └── testbucket_credential.json │ ├── tests │ │ ├── main.rs │ │ ├── signing │ │ │ └── mod.rs │ │ └── credential_providers │ │ │ ├── mod.rs │ │ │ ├── vm_metadata.rs │ │ │ ├── default.rs │ │ │ └── static_provider.rs │ ├── src │ │ ├── lib.rs │ │ ├── provide_credential │ │ │ └── mod.rs │ │ └── constants.rs │ └── Cargo.toml ├── oracle │ ├── LICENSE │ ├── src │ │ ├── provide_credential │ │ │ ├── mod.rs │ │ │ ├── config.rs │ │ │ └── static_.rs │ │ ├── lib.rs │ │ ├── constants.rs │ │ └── credential.rs │ └── Cargo.toml ├── aliyun-oss │ ├── LICENSE │ ├── testdata │ │ └── .gitignore │ ├── src │ │ ├── provide_credential │ │ │ └── mod.rs │ │ ├── constants.rs │ │ └── credential.rs │ └── Cargo.toml ├── azure-storage │ ├── LICENSE │ ├── tests │ │ ├── main.rs │ │ ├── signing │ │ │ └── mod.rs │ │ ├── credential_providers │ │ │ ├── mod.rs │ │ │ ├── imds.rs │ │ │ ├── azure_cli.rs │ │ │ ├── client_secret.rs │ │ │ └── client_certificate.rs │ │ └── mocks │ │ │ └── imds_mock.py │ ├── src │ │ ├── constants.rs │ │ └── provide_credential │ │ │ └── mod.rs │ └── Cargo.toml ├── tencent-cos │ ├── LICENSE │ ├── testdata │ │ └── .gitignore │ ├── src │ │ ├── lib.rs │ │ ├── provide_credential │ │ │ ├── mod.rs │ │ │ └── config.rs │ │ ├── constants.rs │ │ └── credential.rs │ └── Cargo.toml └── huaweicloud-obs │ ├── LICENSE │ ├── src │ ├── lib.rs │ ├── constants.rs │ ├── provide_credential │ │ ├── mod.rs │ │ └── config.rs │ └── credential.rs │ └── Cargo.toml ├── context ├── file-read-tokio │ ├── LICENSE │ ├── Cargo.toml │ ├── README.md │ └── examples │ │ └── read_credentials.rs ├── http-send-reqwest │ ├── LICENSE │ ├── tests │ │ └── wasm.rs │ ├── Cargo.toml │ └── README.md └── command-execute-tokio │ ├── LICENSE │ └── Cargo.toml ├── .gitignore ├── .vscode └── settings.json ├── NOTICE ├── CONTRIBUTING.md ├── licenserc.toml ├── rust-toolchain.toml ├── .env.example ├── rustfmt.toml ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ ├── oracle.yml │ ├── huaweicloud_obs.yml │ ├── aliyun_oss.yml │ └── tencent_cos.yml ├── .asf.yaml ├── .taplo.toml └── Cargo.toml /core/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /reqsign/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /services/aws-v4/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/google/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/oracle/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/aliyun-oss/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/azure-storage/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/tencent-cos/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /context/file-read-tokio/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /context/http-send-reqwest/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/huaweicloud-obs/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /context/command-execute-tokio/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /services/aliyun-oss/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | oidc_token_file 2 | -------------------------------------------------------------------------------- /services/aws-v4/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | web_identity_token_file 2 | -------------------------------------------------------------------------------- /services/tencent-cos/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | web_identity_token_file 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .env 4 | .claude/*.local.json 5 | .idea 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./Cargo.toml" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /services/aws-v4/testdata/default_credential: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id = shared_access_key_id 3 | aws_secret_access_key = shared_secret_access_key 4 | -------------------------------------------------------------------------------- /services/aws-v4/testdata/default_config: -------------------------------------------------------------------------------- 1 | [default] 2 | region = test 3 | aws_access_key_id = config_access_key_id 4 | aws_secret_access_key = config_secret_access_key 5 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache OpenDAL Reqsign 2 | Copyright 2025 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /services/google/testdata/test_impersonated_service_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "delegates": [], 3 | "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/example-01-iam@example-01.iam.gserviceaccount.com:generateAccessToken", 4 | "source_credentials": { 5 | "client_id": "placeholder_client_id", 6 | "client_secret": "placeholder_client_secret", 7 | "refresh_token": "placeholder_refresh_token", 8 | "type": "authorized_user" 9 | }, 10 | "type": "impersonated_service_account" 11 | } 12 | -------------------------------------------------------------------------------- /services/google/testdata/test_external_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "external_account", 3 | "audience": "//iam.googleapis.com/projects/000000000000/locations/global/workloadIdentityPools/reqsign/providers/reqsign-provider", 4 | "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", 5 | "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-234@test.iam.gserviceaccount.com:generateAccessToken", 6 | "token_url": "https://sts.googleapis.com/v1/token", 7 | "credential_source": { 8 | "url": "http://localhost:5000/token", 9 | "format": { 10 | "type": "json", 11 | "subject_token_field_name": "id_token" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Get Started 4 | 5 | This is a Rust project, so [rustup](https://rustup.rs/) is the best place to start. 6 | 7 | This is a pure rust project, so only `cargo` is needed. 8 | 9 | - `cargo check` to analyze the current package and report errors. 10 | - `cargo build` to compile the current package. 11 | - `cargo clippy` to catch common mistakes and improve code. 12 | - `cargo test` to run unit tests. 13 | - `cargo bench` to run benchmark tests. 14 | 15 | Useful tips: 16 | 17 | - Check/Build/Test/Clippy all code: `cargo --tests --benches --examples` 18 | - Test specific function: `cargo test tests::it::services::fs` 19 | 20 | ## Test 21 | 22 | Copy `.env.example` to local, change needed test suite values. 23 | 24 | ```shell 25 | cargo test 26 | ``` 27 | -------------------------------------------------------------------------------- /services/aws-v4/tests/main.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod credential_providers; 19 | mod signing; 20 | -------------------------------------------------------------------------------- /services/google/tests/main.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod credential_providers; 19 | mod signing; 20 | -------------------------------------------------------------------------------- /services/google/tests/signing/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod signed_url; 19 | mod standard; 20 | -------------------------------------------------------------------------------- /services/azure-storage/tests/main.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod credential_providers; 19 | mod signing; 20 | -------------------------------------------------------------------------------- /services/azure-storage/tests/signing/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | pub mod sas_token; 19 | pub mod shared_key; 20 | -------------------------------------------------------------------------------- /licenserc.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | headerPath = "Apache-2.0-ASF.txt" 19 | 20 | includes = ['**/*.rs', '**/*.yml', '**/*.yaml', '**/*.toml'] 21 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [toolchain] 19 | channel = "stable" 20 | components = ["cargo", "rustfmt", "clippy", "rust-analyzer"] 21 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Azure Storage 2 | REQSIGN_AZURE_STORAGE_TEST=false 3 | REQSIGN_AZURE_STORAGE_URL= 4 | REQSIGN_AZURE_STORAGE_ACCOUNT_NAME= 5 | REQSIGN_AZURE_STORAGE_ACCOUNT_KEY= 6 | # AWS V4 7 | REQSIGN_AWS_V4_TEST=false 8 | REQSIGN_AWS_V4_SERVICE= 9 | REQSIGN_AWS_V4_URL= 10 | REQSIGN_AWS_V4_REGION= 11 | REQSIGN_AWS_V4_ACCESS_KEY= 12 | REQSIGN_AWS_V4_SECRET_KEY= 13 | REQSIGN_AWS_ROLE_ARN= 14 | REQSIGN_AWS_IDP_URL= 15 | REQSIGN_AWS_IDP_BODY= 16 | # AWS V4 S3 Express 17 | REQSIGN_AWS_V4_TEST_S3_EXPRESS=false 18 | REQSIGN_AWS_V4_S3_EXPRESS_BUCKET= 19 | # Google Cloud Storage Test 20 | REQSIGN_GOOGLE_TEST=false 21 | REQSIGN_GOOGLE_CREDENTIAL= 22 | REQSIGN_GOOGLE_CLOUD_STORAGE_SCOPE= 23 | REQSIGN_GOOGLE_CLOUD_STORAGE_URL= 24 | 25 | ## Tencent COS Test 26 | REQSIGN_TENCENT_COS_TEST=on 27 | REQSIGN_TENCENT_COS_ACCESS_KEY= 28 | REQSIGN_TENCENT_COS_SECRET_KEY= 29 | REQSIGN_TENCENT_COS_URL=http://.url -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | edition = "2024" 19 | reorder_imports = true 20 | 21 | # format_code_in_doc_comments = true 22 | # group_imports = "StdExternalCrate" 23 | # imports_granularity = "Item" 24 | # overflow_delimited_expr = true 25 | # trailing_comma = "Vertical" 26 | # where_single_line = true 27 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Signers for huaweicloud obs service. 19 | 20 | mod credential; 21 | pub use credential::Credential; 22 | 23 | mod sign_request; 24 | pub use sign_request::RequestSigner; 25 | 26 | mod provide_credential; 27 | pub use provide_credential::*; 28 | 29 | mod constants; 30 | -------------------------------------------------------------------------------- /services/tencent-cos/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Tencent Cloud service signer 19 | //! 20 | //! Only COS has been supported. 21 | 22 | mod constants; 23 | 24 | mod credential; 25 | pub use credential::Credential; 26 | 27 | mod sign_request; 28 | pub use sign_request::RequestSigner; 29 | 30 | pub mod provide_credential; 31 | pub use provide_credential::*; 32 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | version: 2 19 | updates: 20 | # Maintain dependencies for GitHub Actions 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | schedule: 24 | interval: "daily" 25 | 26 | # Maintain dependencies for rust 27 | - package-ecosystem: "cargo" 28 | directory: "/" 29 | schedule: 30 | interval: "daily" 31 | -------------------------------------------------------------------------------- /services/azure-storage/tests/credential_providers/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | pub mod default; 19 | pub mod env; 20 | pub mod static_provider; 21 | 22 | #[cfg(not(target_arch = "wasm32"))] 23 | pub mod azure_cli; 24 | 25 | #[cfg(not(target_arch = "wasm32"))] 26 | pub mod client_certificate; 27 | 28 | pub mod azure_pipelines; 29 | pub mod client_secret; 30 | pub mod imds; 31 | pub mod workload_identity; 32 | -------------------------------------------------------------------------------- /services/aliyun-oss/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod assume_role_with_oidc; 19 | pub use assume_role_with_oidc::AssumeRoleWithOidcCredentialProvider; 20 | 21 | mod default; 22 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 23 | 24 | mod env; 25 | pub use env::EnvCredentialProvider; 26 | 27 | mod r#static; 28 | pub use r#static::StaticCredentialProvider; 29 | -------------------------------------------------------------------------------- /services/google/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Google Service Signer 19 | 20 | mod constants; 21 | 22 | mod credential; 23 | pub use credential::{Credential, ServiceAccount, Token}; 24 | 25 | mod sign_request; 26 | pub use sign_request::RequestSigner; 27 | 28 | mod provide_credential; 29 | pub use provide_credential::{ 30 | DefaultCredentialProvider, StaticCredentialProvider, VmMetadataCredentialProvider, 31 | }; 32 | -------------------------------------------------------------------------------- /services/azure-storage/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use percent_encoding::{AsciiSet, NON_ALPHANUMERIC}; 19 | 20 | // Headers used in azure services. 21 | pub const X_MS_DATE: &str = "x-ms-date"; 22 | #[allow(dead_code)] 23 | pub const CONTENT_MD5: &str = "content-md5"; 24 | 25 | pub static AZURE_QUERY_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 26 | .remove(b'-') 27 | .remove(b'.') 28 | .remove(b'_') 29 | .remove(b'/') 30 | .remove(b'~'); 31 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Headers used in huawei cloud services. 19 | pub const CONTENT_MD5: &str = "Content-MD5"; // different from azure 20 | 21 | // Env values used in huawei cloud services. 22 | pub const HUAWEI_CLOUD_ACCESS_KEY_ID: &str = "HUAWEI_CLOUD_ACCESS_KEY_ID"; 23 | pub const HUAWEI_CLOUD_SECRET_ACCESS_KEY: &str = "HUAWEI_CLOUD_SECRET_ACCESS_KEY"; 24 | pub const HUAWEI_CLOUD_SECURITY_TOKEN: &str = "HUAWEI_CLOUD_SECURITY_TOKEN"; 25 | -------------------------------------------------------------------------------- /services/google/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod default; 19 | #[allow(unused_imports)] 20 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 21 | 22 | mod vm_metadata; 23 | pub use vm_metadata::VmMetadataCredentialProvider; 24 | 25 | mod static_provider; 26 | pub use static_provider::StaticCredentialProvider; 27 | 28 | // Internal providers - not exported 29 | mod authorized_user; 30 | mod external_account; 31 | mod impersonated_service_account; 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Release 19 | 20 | on: 21 | workflow_dispatch: 22 | push: 23 | # only for formal releases, not for release candidates 24 | tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] 25 | 26 | env: 27 | CARGO_TERM_COLOR: always 28 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 29 | 30 | jobs: 31 | publish: 32 | name: Publish Workspace 33 | runs-on: ubuntu-24.04 34 | steps: 35 | - uses: actions/checkout@v6 36 | - run: cargo publish --workspace 37 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod config; 19 | #[deprecated( 20 | since = "0.1.0", 21 | note = "Use StaticCredentialProvider or EnvCredentialProvider instead" 22 | )] 23 | #[allow(deprecated)] 24 | pub use config::ConfigCredentialProvider; 25 | 26 | mod default; 27 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 28 | 29 | mod env; 30 | pub use env::EnvCredentialProvider; 31 | 32 | mod r#static; 33 | pub use r#static::StaticCredentialProvider; 34 | -------------------------------------------------------------------------------- /services/oracle/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod config; 19 | #[deprecated( 20 | since = "0.1.0", 21 | note = "Use EnvCredentialProvider or StaticCredentialProvider instead" 22 | )] 23 | pub use config::ConfigCredentialProvider; 24 | 25 | mod env; 26 | pub use env::EnvCredentialProvider; 27 | 28 | mod static_; 29 | pub use static_::StaticCredentialProvider; 30 | 31 | mod config_file; 32 | pub use config_file::ConfigFileCredentialProvider; 33 | 34 | mod default; 35 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 36 | -------------------------------------------------------------------------------- /services/oracle/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Oracle Cloud Infrastructure service signer 19 | //! 20 | 21 | mod constants; 22 | 23 | mod config; 24 | #[allow(deprecated)] 25 | pub use config::Config; 26 | 27 | mod credential; 28 | pub use credential::Credential; 29 | 30 | mod sign_request; 31 | pub use sign_request::RequestSigner; 32 | 33 | pub mod provide_credential; 34 | #[allow(deprecated)] 35 | pub use provide_credential::ConfigCredentialProvider; 36 | pub use provide_credential::{ 37 | ConfigFileCredentialProvider, DefaultCredentialProvider, EnvCredentialProvider, 38 | StaticCredentialProvider, 39 | }; 40 | -------------------------------------------------------------------------------- /services/google/testdata/test_credential.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "test", 4 | "private_key_id": "test_private_key_id", 5 | "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDOy4jaJIcVlffi5ENtlNhJ0tsI1zt21BI3DMGtPq7n3Ymow24w\nBV2Z73l4dsqwRo2QVSwnCQ2bVtM2DgckMNDShfWfKe3LRcl96nnn51AtAYIfRnc+\nogstzxZi4J64f7IR3KIAFxJnzo+a6FS6MmsYMAs8/Oj68fRmCD0AbAs5ZwIDAQAB\nAoGAVpPkMeBFJgZph/alPEWq4A2FYogp/y/+iEmw9IVf2PdpYNyhTz2P2JjoNEUX\nywFe12SxXY5uwfBx8RmiZ8aARkIBWs7q9Sz6f/4fdCHAuu3GAv5hmMO4dLQsGcKl\nXAQW4QxZM5/x5IXlDh4KdcUP65P0ZNS3deqDlsq/vVfY9EECQQD9I/6KNmlSrbnf\nFa/5ybF+IV8mOkEfkslQT4a9pWbA1FF53Vk4e7B+Faow3uUGHYs/HUwrd3vIVP84\nS+4Jeuc3AkEA0SGF5l3BrWWTok1Wr/UE+oPOUp2L4AV6kH8co11ZyxSQkRloLdMd\nbNzNXShuhwgvNjvgkseNSeQPJKxFRn73UQJACacMtrJ6c6eiNcp66lhxhzC4kxmX\nkB+lw4U0yxh6gZHXBYGWPFwjD7u9wJ1POFt6Cs8QL3wf4TS0gq4KhpwEIwJACIA8\nWSjmfo3qemZ6Z5ymHyjMcj9FOE4AtW71Uw6wX7juR3eo7HPwdkRjdK34EDUc9i9o\n6Y6DB8Xld7ApALyYgQJBAPTMFpKpCRNvYH5VrdObid5+T7OwDrJFHGWdbDGiT++O\nV08rl535r74rMilnQ37X1/zaKBYyxpfhnd2XXgoCgTM=\n-----END RSA PRIVATE KEY-----\n", 6 | "client_email": "test-234@test.iam.gserviceaccount.com", 7 | "client_id": "111942493510252143344", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-234%40test.iam.gserviceaccount.com" 12 | } 13 | -------------------------------------------------------------------------------- /services/tencent-cos/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod config; 19 | #[deprecated( 20 | since = "0.1.0", 21 | note = "Use StaticCredentialProvider or EnvCredentialProvider instead" 22 | )] 23 | #[allow(deprecated)] 24 | pub use config::ConfigCredentialProvider; 25 | 26 | mod default; 27 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 28 | 29 | mod assume_role_with_web_identity; 30 | pub use assume_role_with_web_identity::AssumeRoleWithWebIdentityCredentialProvider; 31 | 32 | mod env; 33 | pub use env::EnvCredentialProvider; 34 | 35 | mod r#static; 36 | pub use r#static::StaticCredentialProvider; 37 | -------------------------------------------------------------------------------- /services/aliyun-oss/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Env values used in aliyun services. 19 | pub const ALIBABA_CLOUD_ACCESS_KEY_ID: &str = "ALIBABA_CLOUD_ACCESS_KEY_ID"; 20 | pub const ALIBABA_CLOUD_ACCESS_KEY_SECRET: &str = "ALIBABA_CLOUD_ACCESS_KEY_SECRET"; 21 | pub const ALIBABA_CLOUD_SECURITY_TOKEN: &str = "ALIBABA_CLOUD_SECURITY_TOKEN"; 22 | pub const ALIBABA_CLOUD_ROLE_ARN: &str = "ALIBABA_CLOUD_ROLE_ARN"; 23 | pub const ALIBABA_CLOUD_OIDC_PROVIDER_ARN: &str = "ALIBABA_CLOUD_OIDC_PROVIDER_ARN"; 24 | pub const ALIBABA_CLOUD_OIDC_TOKEN_FILE: &str = "ALIBABA_CLOUD_OIDC_TOKEN_FILE"; 25 | pub const ALIBABA_CLOUD_STS_ENDPOINT: &str = "ALIBABA_CLOUD_STS_ENDPOINT"; 26 | -------------------------------------------------------------------------------- /.github/workflows/oracle.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Oracle Test 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | pull_request: 25 | branches: 26 | - main 27 | 28 | concurrency: 29 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | unit_test: 34 | runs-on: ubuntu-latest 35 | permissions: 36 | id-token: write 37 | steps: 38 | - uses: actions/checkout@v6 39 | - name: Test 40 | working-directory: ./services/oracle 41 | run: cargo test --no-fail-fast 42 | env: 43 | RUST_LOG: DEBUG 44 | RUST_BACKTRACE: full 45 | -------------------------------------------------------------------------------- /services/oracle/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | /// Default config path for oracle services. 19 | pub const ORACLE_CONFIG_PATH: &str = "~/.oci/config"; 20 | /// Default profile name 21 | pub const ORACLE_DEFAULT_PROFILE: &str = "DEFAULT"; 22 | 23 | /// Environment variables for Oracle Cloud Infrastructure 24 | pub const ORACLE_USER: &str = "OCI_USER"; 25 | pub const ORACLE_TENANCY: &str = "OCI_TENANCY"; 26 | pub const ORACLE_REGION: &str = "OCI_REGION"; 27 | pub const ORACLE_KEY_FILE: &str = "OCI_KEY_FILE"; 28 | pub const ORACLE_FINGERPRINT: &str = "OCI_FINGERPRINT"; 29 | pub const ORACLE_CONFIG_FILE: &str = "OCI_CONFIG_FILE"; 30 | pub const ORACLE_PROFILE: &str = "OCI_PROFILE"; 31 | -------------------------------------------------------------------------------- /.github/workflows/huaweicloud_obs.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Huaweicloud OBS Test 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | pull_request: 25 | branches: 26 | - main 27 | 28 | concurrency: 29 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | unit_test: 34 | runs-on: ubuntu-latest 35 | permissions: 36 | id-token: write 37 | steps: 38 | - uses: actions/checkout@v6 39 | - name: Test 40 | working-directory: ./services/huaweicloud-obs 41 | run: cargo test --no-fail-fast 42 | env: 43 | RUST_LOG: DEBUG 44 | RUST_BACKTRACE: full -------------------------------------------------------------------------------- /context/command-execute-tokio/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-command-execute-tokio" 20 | version = "2.0.1" 21 | 22 | categories = ["asynchronous"] 23 | description = "Tokio-based command execution implementation for reqsign" 24 | keywords = ["command", "tokio", "reqsign", "async"] 25 | 26 | edition.workspace = true 27 | license.workspace = true 28 | repository.workspace = true 29 | rust-version.workspace = true 30 | 31 | [dependencies] 32 | async-trait = { workspace = true } 33 | reqsign-core = { workspace = true } 34 | tokio = { version = "1", features = ["process", "io-util"] } 35 | 36 | [dev-dependencies] 37 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 38 | -------------------------------------------------------------------------------- /context/file-read-tokio/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-file-read-tokio" 20 | version = "2.0.1" 21 | 22 | categories = ["asynchronous"] 23 | description = "Tokio-based file reader implementation for reqsign" 24 | keywords = ["command", "tokio", "reqsign", "async"] 25 | 26 | edition.workspace = true 27 | license.workspace = true 28 | repository.workspace = true 29 | rust-version.workspace = true 30 | 31 | [dependencies] 32 | anyhow = { workspace = true } 33 | async-trait = { workspace = true } 34 | reqsign-core = { workspace = true } 35 | 36 | [target.'cfg(not(target_family = "wasm"))'.dependencies] 37 | tokio = { version = "1", features = ["fs"] } 38 | 39 | [dev-dependencies] 40 | tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread"] } 41 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-huaweicloud-obs" 20 | version = "2.0.1" 21 | 22 | description = "Huawei Cloud OBS signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | anyhow = { workspace = true } 31 | async-trait = { workspace = true } 32 | http = { workspace = true } 33 | log = { workspace = true } 34 | percent-encoding = { workspace = true } 35 | reqsign-core = { workspace = true } 36 | 37 | [dev-dependencies] 38 | env_logger = { workspace = true } 39 | reqsign-file-read-tokio = { workspace = true } 40 | reqsign-http-send-reqwest = { workspace = true } 41 | tokio = { workspace = true, features = ["full"] } 42 | -------------------------------------------------------------------------------- /reqsign/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![doc = include_str!("../README.md")] 19 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 20 | 21 | // Re-export core types 22 | pub use reqsign_core::*; 23 | 24 | // Context utilities 25 | #[cfg(feature = "default-context")] 26 | mod context; 27 | #[cfg(feature = "default-context")] 28 | pub use context::default_context; 29 | 30 | // Service modules with convenience APIs 31 | #[cfg(feature = "aliyun")] 32 | pub mod aliyun; 33 | 34 | #[cfg(feature = "aws")] 35 | pub mod aws; 36 | 37 | #[cfg(feature = "azure")] 38 | pub mod azure; 39 | 40 | #[cfg(feature = "google")] 41 | pub mod google; 42 | 43 | #[cfg(feature = "huaweicloud")] 44 | pub mod huaweicloud; 45 | 46 | #[cfg(feature = "oracle")] 47 | pub mod oracle; 48 | 49 | #[cfg(feature = "tencent")] 50 | pub mod tencent; 51 | -------------------------------------------------------------------------------- /context/http-send-reqwest/tests/wasm.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![cfg(target_arch = "wasm32")] 19 | 20 | use bytes::Bytes; 21 | use http::Request; 22 | use reqsign_core::HttpSend; 23 | use reqsign_http_send_reqwest::ReqwestHttpSend; 24 | use wasm_bindgen_test::*; 25 | 26 | #[wasm_bindgen_test] 27 | async fn http_send_returns_error_for_unreachable_host() { 28 | let client = reqwest::Client::new(); 29 | let http_send = ReqwestHttpSend::new(client); 30 | 31 | let req = Request::builder() 32 | .method("GET") 33 | .uri("https://nonexistent.invalid") 34 | .body(Bytes::new()) 35 | .expect("request builds"); 36 | 37 | let result = http_send.http_send(req).await; 38 | assert!( 39 | result.is_err(), 40 | "expected unreachable host to produce error" 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /services/oracle/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-oracle" 20 | version = "2.0.1" 21 | 22 | description = "Oracle Cloud signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | anyhow = { workspace = true } 31 | async-trait = { workspace = true } 32 | base64 = { workspace = true } 33 | http = { workspace = true } 34 | log = { workspace = true } 35 | reqsign-core = { workspace = true } 36 | rsa = { workspace = true } 37 | rust-ini = { workspace = true } 38 | 39 | [dev-dependencies] 40 | env_logger = "0.11" 41 | reqsign-file-read-tokio = { workspace = true } 42 | reqsign-http-send-reqwest = { workspace = true } 43 | tokio = { version = "1", features = ["test-util", "macros", "rt-multi-thread"] } 44 | -------------------------------------------------------------------------------- /reqsign/examples/aws.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use anyhow::Result; 19 | use reqsign::aws; 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | env_logger::init(); 24 | 25 | // Create a default signer for S3 in us-east-1 26 | let signer = aws::default_signer("s3", "us-east-1"); 27 | 28 | // Build a request 29 | let mut req = http::Request::builder() 30 | .method(http::Method::GET) 31 | .uri("https://s3.amazonaws.com/my-bucket/my-object") 32 | .body(()) 33 | .unwrap() 34 | .into_parts() 35 | .0; 36 | 37 | // Sign the request 38 | signer.sign(&mut req, None).await?; 39 | 40 | // Execute the request would require rebuilding with body 41 | // In real usage, you'd use your HTTP client here 42 | println!("Request signed successfully!"); 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /reqsign/examples/azure.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use anyhow::Result; 19 | use reqsign::azure; 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | env_logger::init(); 24 | 25 | // Create a default signer for Azure Storage 26 | let signer = azure::default_signer(); 27 | 28 | // Build a request 29 | let mut req = http::Request::builder() 30 | .method(http::Method::GET) 31 | .uri("https://myaccount.blob.core.windows.net/mycontainer/myblob") 32 | .body(()) 33 | .unwrap() 34 | .into_parts() 35 | .0; 36 | 37 | // Sign the request 38 | signer.sign(&mut req, None).await?; 39 | 40 | // Execute the request would require rebuilding with body 41 | // In real usage, you'd use your HTTP client here 42 | println!("Request signed successfully!"); 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /reqsign/examples/google.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use anyhow::Result; 19 | use reqsign::google; 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | env_logger::init(); 24 | 25 | // Create a default signer for Google Cloud Storage 26 | let signer = google::default_signer("storage.googleapis.com"); 27 | 28 | // Build a request 29 | let mut req = http::Request::builder() 30 | .method(http::Method::GET) 31 | .uri("https://storage.googleapis.com/my-bucket/my-object") 32 | .body(()) 33 | .unwrap() 34 | .into_parts() 35 | .0; 36 | 37 | // Sign the request 38 | signer.sign(&mut req, None).await?; 39 | 40 | // Execute the request would require rebuilding with body 41 | // In real usage, you'd use your HTTP client here 42 | println!("Request signed successfully!"); 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /services/aws-v4/tests/mocks/ecs_mock_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Mock ECS Credentials Server 4 | 5 | This server simulates the ECS Task IAM Roles endpoint for testing purposes. 6 | It responds to credential requests at the /creds endpoint. 7 | """ 8 | 9 | from http.server import HTTPServer, BaseHTTPRequestHandler 10 | import json 11 | from datetime import datetime, timedelta 12 | 13 | 14 | class ECSHandler(BaseHTTPRequestHandler): 15 | def do_GET(self): 16 | if self.path == '/creds': 17 | # Return mock credentials 18 | expiration = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%M:%SZ') 19 | response = { 20 | 'AccessKeyId': 'AKIAIOSFODNN7EXAMPLE', 21 | 'SecretAccessKey': 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', 22 | 'Token': 'IQoJb3JpZ2luX2VjEJv//////////wEaCXVzLXdlc3QtMiJGMEQCIDyJl0YXJwU8iBG4gLVxiNJTYfLp3oFxEOpGGHmQuWmFAiBHEK/GkClQFb0aQ/+kOZkzHKVAPItVJW/VEXAMPLE=', 23 | 'Expiration': expiration 24 | } 25 | self.send_response(200) 26 | self.send_header('Content-Type', 'application/json') 27 | self.end_headers() 28 | self.wfile.write(json.dumps(response).encode()) 29 | else: 30 | self.send_response(404) 31 | self.end_headers() 32 | 33 | def log_message(self, format, *args): 34 | # Suppress logs to stderr 35 | pass 36 | 37 | 38 | if __name__ == '__main__': 39 | import sys 40 | port = int(sys.argv[1]) if len(sys.argv) > 1 else 51679 41 | server = HTTPServer(('0.0.0.0', port), ECSHandler) 42 | print(f'Mock ECS server running on port {port}') 43 | server.serve_forever() -------------------------------------------------------------------------------- /services/aliyun-oss/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-aliyun-oss" 20 | version = "2.0.1" 21 | 22 | description = "Aliyun OSS signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | anyhow = { workspace = true } 31 | async-trait = { workspace = true } 32 | http = { workspace = true } 33 | log = { workspace = true } 34 | percent-encoding = { workspace = true } 35 | reqsign-core = { workspace = true } 36 | serde = { workspace = true } 37 | serde_json = { workspace = true } 38 | 39 | [dev-dependencies] 40 | dotenv = { workspace = true } 41 | env_logger = { workspace = true } 42 | reqsign-file-read-tokio = { workspace = true } 43 | reqsign-http-send-reqwest = { workspace = true } 44 | reqwest = { workspace = true, features = ["rustls-tls"] } 45 | tokio = { workspace = true, features = ["full"] } 46 | -------------------------------------------------------------------------------- /.asf.yaml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # All configurations could be found here: https://github.com/apache/infrastructure-asfyaml/ 19 | 20 | github: 21 | description: "Signing HTTP requests without heavy SDKs." 22 | homepage: https://docs.rs/reqsign 23 | labels: 24 | - rust 25 | - s3 26 | - gcs 27 | - azblob 28 | enabled_merge_buttons: 29 | squash: true 30 | merge: false 31 | rebase: false 32 | custom_subjects: 33 | new_discussion: "{title}" 34 | edit_discussion: "Re: {title}" 35 | close_discussion: "Re: {title}" 36 | close_discussion_with_comment: "Re: {title}" 37 | reopen_discussion: "Re: {title}" 38 | new_comment_discussion: "Re: {title}" 39 | edit_comment_discussion: "Re: {title}" 40 | delete_comment_discussion: "Re: {title}" 41 | 42 | notifications: 43 | commits: commits@opendal.apache.org 44 | issues: commits@opendal.apache.org 45 | pullrequests: commits@opendal.apache.org 46 | discussions: dev@opendal.apache.org 47 | -------------------------------------------------------------------------------- /services/tencent-cos/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-tencent-cos" 20 | version = "2.0.1" 21 | 22 | description = "Tencent Cloud COS signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | anyhow = { workspace = true } 31 | async-trait = { workspace = true } 32 | http = { workspace = true } 33 | log = { workspace = true } 34 | percent-encoding = { workspace = true } 35 | reqsign-core = { workspace = true } 36 | serde = { workspace = true } 37 | serde_json = { workspace = true } 38 | 39 | [dev-dependencies] 40 | dotenv = { workspace = true } 41 | env_logger = { workspace = true } 42 | reqsign-core = { workspace = true } 43 | reqsign-file-read-tokio = { workspace = true } 44 | reqsign-http-send-reqwest = { workspace = true } 45 | reqwest = { workspace = true, features = ["rustls-tls"] } 46 | tokio = { workspace = true, features = ["full"] } 47 | -------------------------------------------------------------------------------- /services/azure-storage/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod env; 19 | pub use env::EnvCredentialProvider; 20 | 21 | mod static_provider; 22 | pub use static_provider::StaticCredentialProvider; 23 | 24 | mod default; 25 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 26 | 27 | mod imds; 28 | pub use imds::ImdsCredentialProvider; 29 | 30 | mod workload_identity; 31 | pub use workload_identity::WorkloadIdentityCredentialProvider; 32 | 33 | mod client_secret; 34 | pub use client_secret::ClientSecretCredentialProvider; 35 | 36 | #[cfg(not(target_arch = "wasm32"))] 37 | mod azure_cli; 38 | #[cfg(not(target_arch = "wasm32"))] 39 | pub use azure_cli::AzureCliCredentialProvider; 40 | 41 | #[cfg(not(target_arch = "wasm32"))] 42 | mod client_certificate; 43 | #[cfg(not(target_arch = "wasm32"))] 44 | pub use client_certificate::ClientCertificateCredentialProvider; 45 | 46 | mod azure_pipelines; 47 | pub use azure_pipelines::AzurePipelinesCredentialProvider; 48 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-core" 20 | version = "2.0.1" 21 | 22 | categories = ["command-line-utilities", "web-programming"] 23 | description = "Signing API requests without effort." 24 | 25 | edition.workspace = true 26 | license.workspace = true 27 | repository.workspace = true 28 | rust-version.workspace = true 29 | 30 | [dependencies] 31 | anyhow = { workspace = true } 32 | async-trait = { workspace = true } 33 | base64 = { workspace = true } 34 | bytes = { workspace = true } 35 | form_urlencoded = { workspace = true } 36 | hex = { workspace = true } 37 | hmac = { workspace = true } 38 | http = { workspace = true } 39 | jiff = { workspace = true } 40 | log = { workspace = true } 41 | percent-encoding = { workspace = true } 42 | sha1 = { workspace = true } 43 | sha2 = { workspace = true } 44 | 45 | [target.'cfg(target_os = "windows")'.dependencies] 46 | windows-sys = { version = "0.61.0", features = [ 47 | "Win32_Foundation", 48 | "Win32_UI_Shell", 49 | "Win32_System_Com", 50 | ] } 51 | -------------------------------------------------------------------------------- /services/aws-v4/tests/mocks/credential_process_helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Mock credential process helper for testing ProcessCredentialProvider 4 | 5 | This script outputs credentials in the format expected by AWS credential_process. 6 | It can be used in ~/.aws/config as: 7 | credential_process = /path/to/credential_process_helper.py 8 | """ 9 | 10 | import json 11 | import sys 12 | from datetime import datetime, timedelta 13 | 14 | 15 | def generate_credentials(profile=None): 16 | """Generate mock AWS credentials in the credential_process format""" 17 | 18 | # Calculate expiration time (1 hour from now) 19 | expiration = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%M:%SZ') 20 | 21 | # Different credentials based on profile (for testing multiple profiles) 22 | if profile == "test": 23 | access_key = "ASIAPROCESSTEST" 24 | secret_key = "process/test/secret/key/EXAMPLE" 25 | else: 26 | access_key = "ASIAPROCESSEXAMPLE" 27 | secret_key = "process/secret/key/EXAMPLE" 28 | 29 | credentials = { 30 | "Version": 1, 31 | "AccessKeyId": access_key, 32 | "SecretAccessKey": secret_key, 33 | "SessionToken": "FwoGZXIvYXdzEPROCESS//////////wEaDEXAMPLETOKEN", 34 | "Expiration": expiration 35 | } 36 | 37 | return credentials 38 | 39 | 40 | def main(): 41 | """Main entry point for the credential process helper""" 42 | 43 | # Check if a profile argument was provided 44 | profile = None 45 | if len(sys.argv) > 2 and sys.argv[1] == "--profile": 46 | profile = sys.argv[2] 47 | 48 | # Generate and output credentials 49 | credentials = generate_credentials(profile) 50 | print(json.dumps(credentials, indent=2)) 51 | 52 | return 0 53 | 54 | 55 | if __name__ == "__main__": 56 | sys.exit(main()) -------------------------------------------------------------------------------- /services/google/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-google" 20 | version = "2.0.1" 21 | 22 | description = "Google Cloud Platform signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | async-trait = { workspace = true } 31 | http = { workspace = true } 32 | jsonwebtoken = "9.2" 33 | log = { workspace = true } 34 | percent-encoding = { workspace = true } 35 | rand = { workspace = true } 36 | reqsign-core = { workspace = true } 37 | rsa = { workspace = true } 38 | serde = { workspace = true } 39 | serde_json = { workspace = true } 40 | sha2 = { workspace = true } 41 | 42 | [dev-dependencies] 43 | dotenv = { workspace = true } 44 | env_logger = { workspace = true } 45 | reqsign-file-read-tokio = { workspace = true } 46 | reqsign-http-send-reqwest = { workspace = true } 47 | reqwest = { workspace = true, features = ["rustls-tls"] } 48 | sha2 = { workspace = true } 49 | tokio = { workspace = true, features = ["full"] } 50 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/imds.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::info; 20 | use reqsign_aws_v4::IMDSv2CredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_imds_v2_credential_provider() { 26 | if env::var("REQSIGN_AWS_V4_TEST_IMDS").unwrap_or_default() != "on" { 27 | info!("REQSIGN_AWS_V4_TEST_IMDS not set, skipping"); 28 | return; 29 | } 30 | 31 | let ctx = create_test_context(); 32 | let provider = IMDSv2CredentialProvider::new(); 33 | 34 | let cred = provider 35 | .provide_credential(&ctx) 36 | .await 37 | .expect("IMDSv2CredentialProvider should succeed on EC2"); 38 | 39 | assert!( 40 | cred.is_some(), 41 | "Should load credentials from EC2 instance metadata" 42 | ); 43 | let cred = cred.unwrap(); 44 | assert!(!cred.access_key_id.is_empty()); 45 | assert!(!cred.secret_access_key.is_empty()); 46 | assert!( 47 | cred.session_token.is_some(), 48 | "IMDS should return session token" 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /context/http-send-reqwest/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-http-send-reqwest" 20 | version = "2.0.1" 21 | 22 | categories = ["asynchronous"] 23 | description = "Reqwest-based HTTP client implementation for reqsign." 24 | keywords = ["command", "http", "reqsign", "async"] 25 | 26 | edition.workspace = true 27 | license.workspace = true 28 | repository.workspace = true 29 | rust-version.workspace = true 30 | 31 | [dependencies] 32 | anyhow = { workspace = true } 33 | async-trait = { workspace = true } 34 | bytes = { workspace = true } 35 | http = { workspace = true } 36 | http-body-util = { version = "0.1.3" } 37 | reqsign-core = { workspace = true } 38 | reqwest = { workspace = true, default-features = false } 39 | 40 | [dev-dependencies] 41 | reqwest = { workspace = true, default-features = false, features = ["default-tls"] } 42 | wasm-bindgen-test = { version = "0.3" } 43 | 44 | [target.'cfg(target_arch = "wasm32")'.dependencies] 45 | futures-channel = { version = "0.3" } 46 | wasm-bindgen-futures = { version = "0.4" } 47 | 48 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 49 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 50 | -------------------------------------------------------------------------------- /services/aws-v4/src/provide_credential/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod assume_role; 19 | pub use assume_role::AssumeRoleCredentialProvider; 20 | 21 | mod assume_role_with_web_identity; 22 | pub use assume_role_with_web_identity::AssumeRoleWithWebIdentityCredentialProvider; 23 | 24 | mod cognito; 25 | pub use cognito::CognitoIdentityCredentialProvider; 26 | 27 | mod default; 28 | pub use default::{DefaultCredentialProvider, DefaultCredentialProviderBuilder}; 29 | 30 | mod ecs; 31 | pub use ecs::ECSCredentialProvider; 32 | 33 | mod env; 34 | pub use env::EnvCredentialProvider; 35 | 36 | mod imds; 37 | pub use imds::IMDSv2CredentialProvider; 38 | 39 | #[cfg(not(target_arch = "wasm32"))] 40 | mod process; 41 | #[cfg(not(target_arch = "wasm32"))] 42 | pub use process::ProcessCredentialProvider; 43 | 44 | mod profile; 45 | pub use profile::ProfileCredentialProvider; 46 | 47 | #[cfg(not(target_arch = "wasm32"))] 48 | mod sso; 49 | #[cfg(not(target_arch = "wasm32"))] 50 | pub use sso::SSOCredentialProvider; 51 | 52 | mod r#static; 53 | pub use r#static::StaticCredentialProvider; 54 | 55 | mod s3_express_session; 56 | pub use s3_express_session::S3ExpressSessionProvider; 57 | 58 | mod utils; 59 | -------------------------------------------------------------------------------- /services/google/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use percent_encoding::AsciiSet; 19 | use percent_encoding::NON_ALPHANUMERIC; 20 | 21 | // Env values used in google services. 22 | pub const GOOGLE_APPLICATION_CREDENTIALS: &str = "GOOGLE_APPLICATION_CREDENTIALS"; 23 | pub const GOOGLE_SCOPE: &str = "GOOGLE_SCOPE"; 24 | 25 | // Default OAuth2 scope for Google Cloud services 26 | pub const DEFAULT_SCOPE: &str = "https://www.googleapis.com/auth/cloud-platform"; 27 | 28 | /// AsciiSet for [Google UriEncode](https://cloud.google.com/storage/docs/authentication/canonical-requests) 29 | /// 30 | /// - URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'. 31 | pub static GOOG_URI_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 32 | .remove(b'/') 33 | .remove(b'-') 34 | .remove(b'.') 35 | .remove(b'_') 36 | .remove(b'~'); 37 | 38 | /// AsciiSet for [Google UriEncode](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) 39 | /// 40 | /// But used in query. 41 | pub static GOOG_QUERY_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 42 | .remove(b'-') 43 | .remove(b'.') 44 | .remove(b'_') 45 | .remove(b'~'); 46 | -------------------------------------------------------------------------------- /services/google/tests/credential_providers/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod authorized_user; 19 | mod default; 20 | mod external_account; 21 | mod impersonated_service_account; 22 | mod static_provider; 23 | mod vm_metadata; 24 | 25 | use reqsign_core::{Context, OsEnv, StaticEnv}; 26 | use reqsign_file_read_tokio::TokioFileRead; 27 | use reqsign_http_send_reqwest::ReqwestHttpSend; 28 | use std::collections::HashMap; 29 | 30 | pub fn create_test_context() -> Context { 31 | let _ = env_logger::builder().is_test(true).try_init(); 32 | let _ = dotenv::dotenv(); 33 | 34 | Context::new() 35 | .with_file_read(TokioFileRead) 36 | .with_http_send(ReqwestHttpSend::default()) 37 | .with_env(OsEnv) 38 | } 39 | 40 | pub fn create_test_context_with_env(envs: HashMap) -> Context { 41 | let _ = env_logger::builder().is_test(true).try_init(); 42 | let _ = dotenv::dotenv(); 43 | 44 | let home_dir = std::env::var("HOME").ok().map(std::path::PathBuf::from); 45 | 46 | let ctx = Context::new() 47 | .with_file_read(TokioFileRead) 48 | .with_http_send(ReqwestHttpSend::default()) 49 | .with_env(OsEnv); 50 | 51 | ctx.with_env(StaticEnv { home_dir, envs }) 52 | } 53 | -------------------------------------------------------------------------------- /services/azure-storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-azure-storage" 20 | version = "2.0.1" 21 | 22 | description = "Azure Storage signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [dependencies] 30 | anyhow = { workspace = true } 31 | async-trait = { workspace = true } 32 | base64 = { workspace = true } 33 | bytes = { workspace = true } 34 | form_urlencoded = { workspace = true } 35 | http = { workspace = true } 36 | log = { workspace = true } 37 | percent-encoding = { workspace = true } 38 | reqsign-core = { workspace = true } 39 | serde = { workspace = true } 40 | serde_json = { workspace = true } 41 | sha1 = { workspace = true } 42 | 43 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 44 | jsonwebtoken = "9.2" 45 | pem = "3.0" 46 | rsa = { workspace = true } 47 | 48 | [dev-dependencies] 49 | async-trait = { workspace = true } 50 | env_logger = { workspace = true } 51 | reqsign-command-execute-tokio = { workspace = true } 52 | reqsign-file-read-tokio = { workspace = true } 53 | reqsign-http-send-reqwest = { workspace = true } 54 | reqwest = { workspace = true, features = ["rustls-tls"] } 55 | tokio = { workspace = true, features = ["full"] } 56 | -------------------------------------------------------------------------------- /context/file-read-tokio/README.md: -------------------------------------------------------------------------------- 1 | # reqsign-file-read-tokio 2 | 3 | Tokio-based file reading implementation for reqsign. 4 | 5 | --- 6 | 7 | This crate provides `TokioFileRead`, an async file reader that implements the `FileRead` trait from `reqsign_core` using Tokio's file system operations. 8 | 9 | ## Quick Start 10 | 11 | ```rust 12 | use reqsign_core::Context; 13 | use reqsign_file_read_tokio::TokioFileRead; 14 | 15 | // Create a context with Tokio file reader 16 | let ctx = Context::new( 17 | TokioFileRead::default(), 18 | http_client, // Your HTTP client 19 | ); 20 | 21 | // Read files asynchronously 22 | let content = ctx.file_read("/path/to/file").await?; 23 | ``` 24 | 25 | ## Features 26 | 27 | - **Async File I/O**: Leverages Tokio's async file system operations 28 | - **Zero Configuration**: Works out of the box with sensible defaults 29 | - **Lightweight**: Minimal dependencies, only what's needed 30 | 31 | ## Use Cases 32 | 33 | This crate is essential when: 34 | - Loading credentials from file system (e.g., `~/.aws/credentials`) 35 | - Reading service account keys (e.g., Google Cloud service account JSON) 36 | - Accessing configuration files for various cloud providers 37 | 38 | ## Examples 39 | 40 | ### Reading Credentials 41 | 42 | Check out the [read_credentials example](examples/read_credentials.rs) to see how to read credential files: 43 | 44 | ```bash 45 | cargo run --example read_credentials -- ~/.aws/credentials 46 | ``` 47 | 48 | ### Integration with Services 49 | 50 | ```rust 51 | use reqsign_core::{Context, Signer}; 52 | use reqsign_file_read_tokio::TokioFileRead; 53 | use reqsign_http_send_reqwest::ReqwestHttpSend; 54 | 55 | // Create context with Tokio file reader 56 | let ctx = Context::new( 57 | TokioFileRead::default(), 58 | ReqwestHttpSend::default(), 59 | ); 60 | 61 | // Use with any service that needs file access 62 | let signer = Signer::new(ctx, loader, builder); 63 | ``` 64 | 65 | ## Requirements 66 | 67 | - Tokio runtime with `fs` feature enabled 68 | - Compatible with all reqsign service implementations 69 | 70 | ## License 71 | 72 | Licensed under [Apache License, Version 2.0](./LICENSE). -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/cognito.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::info; 20 | use reqsign_aws_v4::CognitoIdentityCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_cognito_identity_credential_provider() { 26 | if env::var("REQSIGN_AWS_V4_TEST_COGNITO").unwrap_or_default() != "on" { 27 | info!("REQSIGN_AWS_V4_TEST_COGNITO not set, skipping"); 28 | return; 29 | } 30 | 31 | // Provider will read configuration from environment variables: 32 | // - AWS_COGNITO_IDENTITY_POOL_ID 33 | // - AWS_REGION or AWS_DEFAULT_REGION 34 | // - AWS_COGNITO_ENDPOINT (for mock server) 35 | let ctx = create_test_context(); 36 | let provider = CognitoIdentityCredentialProvider::new(); 37 | 38 | let cred = provider 39 | .provide_credential(&ctx) 40 | .await 41 | .expect("CognitoIdentity should succeed"); 42 | 43 | assert!(cred.is_some(), "Should load credentials from Cognito"); 44 | let cred = cred.unwrap(); 45 | assert!(!cred.access_key_id.is_empty()); 46 | assert!(!cred.secret_access_key.is_empty()); 47 | assert!( 48 | cred.session_token.is_some(), 49 | "Cognito should return session token" 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /services/tencent-cos/src/provide_credential/config.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use async_trait::async_trait; 19 | use reqsign_core::Result; 20 | use reqsign_core::{Context, ProvideCredential}; 21 | 22 | use crate::Credential; 23 | 24 | /// ConfigCredentialProvider will load credential from config. 25 | /// 26 | /// # Deprecated 27 | /// 28 | /// This provider is deprecated and will be removed in a future version. 29 | /// Use `StaticCredentialProvider` for static credentials or `EnvCredentialProvider` 30 | /// for environment-based credentials instead. 31 | #[deprecated( 32 | since = "0.1.0", 33 | note = "Use StaticCredentialProvider or EnvCredentialProvider instead" 34 | )] 35 | #[derive(Debug)] 36 | pub struct ConfigCredentialProvider; 37 | 38 | #[allow(deprecated)] 39 | impl ConfigCredentialProvider { 40 | /// Create a new ConfigCredentialProvider - this is a no-op and deprecated 41 | pub fn new(_: std::sync::Arc<()>) -> Self { 42 | Self 43 | } 44 | } 45 | 46 | #[async_trait] 47 | #[allow(deprecated)] 48 | impl ProvideCredential for ConfigCredentialProvider { 49 | type Credential = Credential; 50 | 51 | async fn provide_credential(&self, _ctx: &Context) -> Result> { 52 | // Always return None since Config is removed 53 | Ok(None) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/src/provide_credential/config.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use async_trait::async_trait; 19 | use reqsign_core::Result; 20 | use reqsign_core::{Context, ProvideCredential}; 21 | 22 | use crate::credential::Credential; 23 | 24 | /// ConfigCredentialProvider will load credential from config. 25 | /// 26 | /// # Deprecated 27 | /// 28 | /// This provider is deprecated and will be removed in a future version. 29 | /// Use `StaticCredentialProvider` for static credentials or `EnvCredentialProvider` 30 | /// for environment-based credentials instead. 31 | #[deprecated( 32 | since = "0.1.0", 33 | note = "Use StaticCredentialProvider or EnvCredentialProvider instead" 34 | )] 35 | #[derive(Debug)] 36 | pub struct ConfigCredentialProvider; 37 | 38 | #[allow(deprecated)] 39 | impl ConfigCredentialProvider { 40 | /// Create a new ConfigCredentialProvider - this is a no-op and deprecated 41 | pub fn new(_: std::sync::Arc<()>) -> Self { 42 | Self 43 | } 44 | } 45 | 46 | #[async_trait] 47 | #[allow(deprecated)] 48 | impl ProvideCredential for ConfigCredentialProvider { 49 | type Credential = Credential; 50 | 51 | async fn provide_credential(&self, _ctx: &Context) -> Result> { 52 | // Always return None since Config is removed 53 | Ok(None) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/aliyun_oss.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Aliyun OSS Test 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | pull_request: 25 | branches: 26 | - main 27 | 28 | concurrency: 29 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | unit_test: 34 | runs-on: ubuntu-latest 35 | permissions: 36 | id-token: write 37 | steps: 38 | - uses: actions/checkout@v6 39 | - name: Test 40 | working-directory: ./services/aliyun-oss 41 | run: cargo test --no-fail-fast 42 | env: 43 | RUST_LOG: DEBUG 44 | RUST_BACKTRACE: full 45 | # Aliyun OSS Test 46 | REQSIGN_ALIYUN_OSS_TEST: ${{ secrets.REQSIGN_ALIYUN_OSS_TEST }} 47 | REQSIGN_ALIYUN_OSS_BUCKET: ${{ secrets.REQSIGN_ALIYUN_OSS_BUCKET }} 48 | REQSIGN_ALIYUN_OSS_URL: ${{ secrets.REQSIGN_ALIYUN_OSS_URL }} 49 | REQSIGN_ALIYUN_OSS_ACCESS_KEY: ${{ secrets.REQSIGN_ALIYUN_OSS_ACCESS_KEY }} 50 | REQSIGN_ALIYUN_OSS_SECRET_KEY: ${{ secrets.REQSIGN_ALIYUN_OSS_SECRET_KEY }} 51 | REQSIGN_ALIYUN_PROVIDER_ARN: ${{ secrets.REQSIGN_ALIYUN_PROVIDER_ARN }} 52 | REQSIGN_ALIYUN_ROLE_ARN: ${{ secrets.REQSIGN_ALIYUN_ROLE_ARN }} 53 | REQSIGN_ALIYUN_IDP_URL: ${{ secrets.REQSIGN_ALIYUN_IDP_URL }} 54 | REQSIGN_ALIYUN_IDP_BODY: ${{ secrets.REQSIGN_ALIYUN_IDP_BODY }} 55 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/process.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::ProcessCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_process_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_PROCESS").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_PROCESS not set, skipping"); 29 | return; 30 | } 31 | 32 | let mut envs = HashMap::new(); 33 | 34 | // Process credentials are configured in AWS profile 35 | if let Ok(profile) = env::var("AWS_PROFILE") { 36 | envs.insert("AWS_PROFILE".to_string(), profile); 37 | } 38 | 39 | if let Ok(config_file) = env::var("AWS_CONFIG_FILE") { 40 | envs.insert("AWS_CONFIG_FILE".to_string(), config_file); 41 | } 42 | 43 | let ctx = create_test_context_with_env(envs); 44 | let provider = ProcessCredentialProvider::new(); 45 | 46 | let cred = provider 47 | .provide_credential(&ctx) 48 | .await 49 | .expect("ProcessCredentialProvider should succeed"); 50 | 51 | assert!( 52 | cred.is_some(), 53 | "Should load credentials from external process" 54 | ); 55 | let cred = cred.unwrap(); 56 | assert!(!cred.access_key_id.is_empty()); 57 | assert!(!cred.secret_access_key.is_empty()); 58 | } 59 | -------------------------------------------------------------------------------- /services/huaweicloud-obs/src/credential.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use std::fmt::{Debug, Formatter}; 19 | 20 | use reqsign_core::{SigningCredential, utils::Redact}; 21 | 22 | /// Credential for obs. 23 | #[derive(Clone)] 24 | pub struct Credential { 25 | /// Access key id for obs 26 | pub access_key_id: String, 27 | /// Secret access key for obs 28 | pub secret_access_key: String, 29 | /// security_token for obs. 30 | pub security_token: Option, 31 | } 32 | 33 | impl Credential { 34 | /// Create a new credential. 35 | pub fn new( 36 | access_key_id: String, 37 | secret_access_key: String, 38 | security_token: Option, 39 | ) -> Self { 40 | Self { 41 | access_key_id, 42 | secret_access_key, 43 | security_token, 44 | } 45 | } 46 | } 47 | 48 | impl Debug for Credential { 49 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 50 | f.debug_struct("Credential") 51 | .field("access_key_id", &Redact::from(&self.access_key_id)) 52 | .field("secret_access_key", &Redact::from(&self.secret_access_key)) 53 | .field( 54 | "security_token", 55 | &self.security_token.as_ref().map(Redact::from), 56 | ) 57 | .finish() 58 | } 59 | } 60 | 61 | impl SigningCredential for Credential { 62 | fn is_valid(&self) -> bool { 63 | true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /services/tencent-cos/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use percent_encoding::AsciiSet; 19 | use percent_encoding::NON_ALPHANUMERIC; 20 | 21 | pub const TENCENTCLOUD_REGION: &str = "TENCENTCLOUD_REGION"; 22 | pub const TKE_REGION: &str = "TKE_REGION"; 23 | pub const TENCENTCLOUD_SECRET_ID: &str = "TENCENTCLOUD_SECRET_ID"; 24 | pub const TKE_SECRET_ID: &str = "TKE_SECRET_ID"; 25 | pub const TENCENTCLOUD_SECRET_KEY: &str = "TENCENTCLOUD_SECRET_KEY"; 26 | pub const TKE_SECRET_KEY: &str = "TKE_SECRET_KEY"; 27 | pub const TENCENTCLOUD_TOKEN: &str = "TENCENTCLOUD_TOKEN"; 28 | pub const TENCENTCLOUD_SECURITY_TOKEN: &str = "TENCENTCLOUD_SECURITY_TOKEN"; 29 | pub const TENCENTCLOUD_ROLE_ARN: &str = "TENCENTCLOUD_ROLE_ARN"; 30 | pub const TKE_ROLE_ARN: &str = "TKE_ROLE_ARN"; 31 | pub const TENCENTCLOUD_ROLE_SESSSION_NAME: &str = "TENCENTCLOUD_ROLE_SESSSION_NAME"; 32 | pub const TKE_ROLE_SESSSION_NAME: &str = "TKE_ROLE_SESSSION_NAME"; 33 | pub const TENCENTCLOUD_PROVIDER_ID: &str = "TENCENTCLOUD_PROVIDER_ID"; 34 | pub const TKE_PROVIDER_ID: &str = "TKE_PROVIDER_ID"; 35 | pub const TENCENTCLOUD_WEB_IDENTITY_TOKEN_FILE: &str = "TENCENTCLOUD_WEB_IDENTITY_TOKEN_FILE"; 36 | pub const TKE_IDENTITY_TOKEN_FILE: &str = "TKE_IDENTITY_TOKEN_FILE"; 37 | 38 | /// AsciiSet for [Tencent UriEncode](https://cloud.tencent.com/document/product/436/7778) 39 | pub static TENCENT_URI_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 40 | .remove(b'-') 41 | .remove(b'_') 42 | .remove(b'.') 43 | .remove(b'~'); 44 | -------------------------------------------------------------------------------- /services/aws-v4/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign-aws-v4" 20 | version = "2.0.1" 21 | 22 | description = "AWS SigV4 signing implementation for reqsign." 23 | 24 | edition.workspace = true 25 | license.workspace = true 26 | repository.workspace = true 27 | rust-version.workspace = true 28 | 29 | [[bench]] 30 | harness = false 31 | name = "aws" 32 | 33 | [dependencies] 34 | anyhow = { workspace = true } 35 | async-trait = { workspace = true } 36 | bytes = "1.7.2" 37 | form_urlencoded = { workspace = true } 38 | http = { workspace = true } 39 | log = { workspace = true } 40 | percent-encoding = { workspace = true } 41 | quick-xml = { workspace = true } 42 | reqsign-core = { workspace = true } 43 | rust-ini = { workspace = true } 44 | serde = { workspace = true } 45 | serde_json = { workspace = true } 46 | serde_urlencoded = "0.7" 47 | sha1 = "0.10" 48 | 49 | [dev-dependencies] 50 | aws-credential-types = "1.1.8" 51 | aws-sigv4 = "1.2.0" 52 | criterion = { workspace = true } 53 | dotenv = { workspace = true } 54 | env_logger = { workspace = true } 55 | hex = { workspace = true } 56 | pretty_assertions = { workspace = true } 57 | reqsign-http-send-reqwest = { workspace = true } 58 | reqwest = { workspace = true, features = ["rustls-tls"] } 59 | sha2 = { workspace = true } 60 | tempfile = { workspace = true } 61 | tokio = { workspace = true, features = ["full"] } 62 | 63 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 64 | reqsign-command-execute-tokio.workspace = true 65 | reqsign-file-read-tokio.workspace = true 66 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | include = ["Cargo.toml", "**/*.toml"] 19 | 20 | [formatting] 21 | # Align consecutive entries vertically. 22 | align_entries = false 23 | # Append trailing commas for multi-line arrays. 24 | array_trailing_comma = true 25 | # Expand arrays to multiple lines that exceed the maximum column width. 26 | array_auto_expand = true 27 | # Collapse arrays that don't exceed the maximum column width and don't contain comments. 28 | array_auto_collapse = true 29 | # Omit white space padding from single-line arrays 30 | compact_arrays = true 31 | # Omit white space padding from the start and end of inline tables. 32 | compact_inline_tables = false 33 | # Maximum column width in characters, affects array expansion and collapse, this doesn't take whitespace into account. 34 | # Note that this is not set in stone, and works on a best-effort basis. 35 | column_width = 80 36 | # Indent based on tables and arrays of tables and their subtables, subtables out of order are not indented. 37 | indent_tables = false 38 | # The substring that is used for indentation, should be tabs or spaces (but technically can be anything). 39 | indent_string = ' ' 40 | # Add trailing newline at the end of the file if not present. 41 | trailing_newline = true 42 | # Alphabetically reorder keys that are not separated by empty lines. 43 | reorder_keys = true 44 | # Maximum amount of allowed consecutive blank lines. This does not affect the whitespace at the end of the document, as it is always stripped. 45 | allowed_blank_lines = 2 46 | # Use CRLF for line endings. 47 | crlf = false 48 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/s3_express.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::info; 20 | use reqsign_aws_v4::{DefaultCredentialProvider, S3ExpressSessionProvider}; 21 | use reqsign_core::ProvideCredential; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_s3_express_session_provider() { 26 | if env::var("REQSIGN_AWS_V4_TEST_S3_EXPRESS").unwrap_or_default() != "on" { 27 | info!("REQSIGN_AWS_V4_TEST_S3_EXPRESS not set, skipping"); 28 | return; 29 | } 30 | 31 | let bucket = env::var("REQSIGN_AWS_V4_S3_EXPRESS_BUCKET") 32 | .expect("REQSIGN_AWS_V4_S3_EXPRESS_BUCKET must be set for S3 Express test"); 33 | 34 | let ctx = create_test_context(); 35 | let base_provider = DefaultCredentialProvider::new(); 36 | let provider = S3ExpressSessionProvider::new(bucket, base_provider); 37 | 38 | let cred = provider 39 | .provide_credential(&ctx) 40 | .await 41 | .expect("S3ExpressSessionProvider should not fail"); 42 | 43 | assert!( 44 | cred.is_some(), 45 | "S3ExpressSessionProvider should return credentials" 46 | ); 47 | let cred = cred.unwrap(); 48 | assert!(!cred.access_key_id.is_empty()); 49 | assert!(!cred.secret_access_key.is_empty()); 50 | assert!( 51 | cred.session_token.is_some(), 52 | "S3 Express session should include session token" 53 | ); 54 | assert!( 55 | cred.expires_in.is_some(), 56 | "S3 Express session should have expiration" 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/sso.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::SSOCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_sso_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_SSO").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_SSO not set, skipping"); 29 | return; 30 | } 31 | 32 | let mut envs = HashMap::new(); 33 | 34 | // SSO credentials are configured in AWS profile 35 | if let Ok(profile) = env::var("AWS_PROFILE") { 36 | envs.insert("AWS_PROFILE".to_string(), profile); 37 | } 38 | 39 | if let Ok(config_file) = env::var("AWS_CONFIG_FILE") { 40 | envs.insert("AWS_CONFIG_FILE".to_string(), config_file); 41 | } 42 | 43 | // Allow custom SSO endpoint for testing 44 | if let Ok(endpoint) = env::var("AWS_SSO_ENDPOINT") { 45 | envs.insert("AWS_SSO_ENDPOINT".to_string(), endpoint); 46 | } 47 | 48 | let ctx = create_test_context_with_env(envs); 49 | let provider = SSOCredentialProvider::new(); 50 | 51 | let cred = provider 52 | .provide_credential(&ctx) 53 | .await 54 | .expect("SSOCredentialProvider should succeed"); 55 | 56 | assert!(cred.is_some(), "Should load credentials from SSO"); 57 | let cred = cred.unwrap(); 58 | assert!(!cred.access_key_id.is_empty()); 59 | assert!(!cred.secret_access_key.is_empty()); 60 | } 61 | -------------------------------------------------------------------------------- /services/aws-v4/src/constants.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use percent_encoding::AsciiSet; 19 | use percent_encoding::NON_ALPHANUMERIC; 20 | 21 | // Headers used in aws services. 22 | pub const X_AMZ_CONTENT_SHA_256: &str = "x-amz-content-sha256"; 23 | pub const X_AMZ_DATE: &str = "x-amz-date"; 24 | pub const X_AMZ_SECURITY_TOKEN: &str = "x-amz-security-token"; 25 | pub const X_AMZ_S3_SESSION_TOKEN: &str = "x-amz-s3session-token"; 26 | 27 | // Env values used in aws services. 28 | pub const AWS_ACCESS_KEY_ID: &str = "AWS_ACCESS_KEY_ID"; 29 | pub const AWS_SECRET_ACCESS_KEY: &str = "AWS_SECRET_ACCESS_KEY"; 30 | pub const AWS_SESSION_TOKEN: &str = "AWS_SESSION_TOKEN"; 31 | pub const AWS_PROFILE: &str = "AWS_PROFILE"; 32 | pub const AWS_CONFIG_FILE: &str = "AWS_CONFIG_FILE"; 33 | pub const AWS_SHARED_CREDENTIALS_FILE: &str = "AWS_SHARED_CREDENTIALS_FILE"; 34 | 35 | /// AsciiSet for [AWS UriEncode](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) 36 | /// 37 | /// - URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'. 38 | pub static AWS_URI_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 39 | .remove(b'/') 40 | .remove(b'-') 41 | .remove(b'.') 42 | .remove(b'_') 43 | .remove(b'~'); 44 | 45 | /// AsciiSet for [AWS UriEncode](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) 46 | /// 47 | /// But used in query. 48 | pub static AWS_QUERY_ENCODE_SET: AsciiSet = NON_ALPHANUMERIC 49 | .remove(b'-') 50 | .remove(b'.') 51 | .remove(b'_') 52 | .remove(b'~'); 53 | -------------------------------------------------------------------------------- /services/tencent-cos/src/credential.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_core::SigningCredential; 19 | use reqsign_core::time::Timestamp; 20 | use reqsign_core::utils::Redact; 21 | use std::fmt::{Debug, Formatter}; 22 | use std::time::Duration; 23 | 24 | /// Credential for Tencent COS. 25 | #[derive(Default, Clone)] 26 | pub struct Credential { 27 | /// Secret ID 28 | pub secret_id: String, 29 | /// Secret Key 30 | pub secret_key: String, 31 | /// Security token for temporary credentials 32 | pub security_token: Option, 33 | /// Expiration time for this credential 34 | pub expires_in: Option, 35 | } 36 | 37 | impl Debug for Credential { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 39 | f.debug_struct("Credential") 40 | .field("secret_id", &Redact::from(&self.secret_id)) 41 | .field("secret_key", &Redact::from(&self.secret_key)) 42 | .field("security_token", &Redact::from(&self.security_token)) 43 | .field("expires_in", &self.expires_in) 44 | .finish() 45 | } 46 | } 47 | 48 | impl SigningCredential for Credential { 49 | fn is_valid(&self) -> bool { 50 | if self.secret_id.is_empty() || self.secret_key.is_empty() { 51 | return false; 52 | } 53 | // Take 120s as buffer to avoid edge cases. 54 | if let Some(valid) = self 55 | .expires_in 56 | .map(|v| v > Timestamp::now() + Duration::from_secs(120)) 57 | { 58 | return valid; 59 | } 60 | 61 | true 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /services/google/testdata/testbucket_credential.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "iam-testbucket-reqsign-project", 4 | "private_key_id": "abcdefabcdef4f96c0c0ed6cffe88a6413e91cec", 5 | "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMXQUceY0V5Id3\nhT4Xu/eUOkj7vYMxmcM7u03g/R5d1Jj+5WdpeLu/Kjh2EhXuCfodCSawEoU1O66h\nfL9A2EMefHRE3tPMYDCYJgUqes/6of4QcSJKev29blbryKrNfQEp4xzRX9oY68bs\nRNVoDN/tIKovZle7VvOPjK83HeTv/R9l93dbKS1lTIYrNI86aRhvvnfbDZGTq/iI\nMHreTD8ICc06qsH774uVMBhTH3bxRQ8NTrZP70PS8RfCCTVNwiwSPE146aU6XUWU\nh+29rqbMNHHQsWLCLeLTY565kD+a2DtNIU0H89wPxnirDouZBYwCjtivvlE0mN5y\nj1qkPrwfAgMBAAECggEBAMO6k5qiEC5XoicmxkGVFZox+JSi/XQUAJjE2+IQi3Ty\nmVYIAPNTXv3IQitTRw2lIJeOnC8mjc5eSvL/t20zs5UPPYx4ngGwXtpaD7iPx4IU\nhHDa6izLfxpfA4DvwCbvAp5Llt4xH4Gez/aaNophSlaiYlzjeENFFCD4bRgs2Ye+\n/pyF0akNf7RO2PXC0u0KbP3rf/tqY+tGnNg7Ykx3+4yEetwFW2RgL3IlLU4PM2Xg\nRrjdiLHNOjS1VXCBMaEYuoP1HFCR+JzyBdidD++/kTSHVCab9Dz4HemyKxr8gpYK\nZUREirN8GFZwVt+SysWzIh788adhhy7Tscyy6SoQ2AECgYEA+76McDcxlEGTkRuO\nXh/dEeFDnSGEqOC3HfYJbzlo+K5bD9ged3DtwSN+bxDizHtcQnhqMPOhrD0tgiCn\nmNsEPXAhj3eoRd050R6THEXwE5Az84SR9gvm27Q8JoAUWXWIkrpVKPwDDjq7XoIF\nY1lTld84UGwg8Hgux05KcMZROd8CgYEAz9FszBaX1OgmRcOklxmDgFsWsdiepFpL\nrTj6oNpbHiJYYDJ0DQgvty8dI+1aWfqgHh1TczjYRx3Cf77qUGpYzY/Vkc2lC+fU\naxXz3qmaKIYZH/IA5+EZshXjMSwTTaGzpmzoqXZ57J9uwmQeUsLeXnlZd1habk7O\nD3CZhuK3RcECgYBGWUdRjHr0XSbpo/Oy5eCXQIXugRFbSACkBL86L6bf54lW8iQB\naLNoB40raGKYldiAUroKF+sUALyY4pszIfEbYhxexSdm7p1bjNm7Suf974w0/tTz\nFvxaZRFyCNSm8ytJJXzqyRHphgwaKudqjenHtes8vhquWEdqNryiqyjDrQKBgCV7\nNBArEv9HT3/NpWXLKDiCNTmmRBaIYpW/bRSNzVlGAIJ5Fw0yqMh1KuBL8ru/xBkq\nWN6zJe7No0K/ACu4woNwqag+WsIm8dzOfMlv9WnRpb5pO1iW9Ld10yAPPvwFag1e\nHyhRQfQ3XRaaUA3FL64CXOx1dvnmJKwMNuRpB30BAoGBAJ+DhBf9CM6ndMBJaOZa\nD8Let93+NYu/iJEn2whzN1cGBcnsOw9uAWiOkFUOuNqYVzSih11vLvUIw9Q9LKnI\n8MLsFLjvierQ1h+V1mxhNO8bEv9tTO+O8h1GMNmCy4S0wZjfsbrJzPL+pKrpgEJS\nqjDDLD9IP7jvoKHSODDsecNq\n-----END PRIVATE KEY-----\n", 6 | "client_email": "testbucket-reqsign-account@iam-testbucket-reqsign-project.iam.gserviceaccount.com", 7 | "client_id": "101000000000000000000", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/testbucket-reqsign-account%iam-testbucket-reqsign-project.iam.gserviceaccount.iam.gserviceaccount.com" 12 | } 13 | -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | # reqsign-core 2 | 3 | Core components for signing API requests. 4 | 5 | --- 6 | 7 | This crate provides the foundational types and traits for the reqsign ecosystem. It defines the core abstractions that enable flexible and extensible request signing. 8 | 9 | ## Quick Start 10 | 11 | ```rust 12 | use reqsign_core::{Context, Signer, ProvideCredential, SignRequest}; 13 | 14 | // Create a context with your implementations 15 | let ctx = Context::default(); 16 | 17 | // Create a signer with credential loader and request builder 18 | let signer = Signer::new(ctx, credential_loader, request_builder); 19 | 20 | // Sign your requests 21 | let mut parts = /* your request parts */; 22 | signer.sign(&mut parts, None).await?; 23 | ``` 24 | 25 | ## Features 26 | 27 | - **Flexible Architecture**: Define your own credential types and signing logic 28 | - **Async Support**: Built with async/await for modern Rust applications 29 | - **Environment Integration**: Access environment variables through the Context 30 | - **Type Safety**: Strong typing ensures compile-time correctness 31 | 32 | ## Core Concepts 33 | 34 | ### Context 35 | 36 | The `Context` struct serves as a container for runtime dependencies: 37 | - File system access via `FileRead` trait 38 | - HTTP client via `HttpSend` trait 39 | - Environment variables via `Env` trait 40 | 41 | ### Traits 42 | 43 | - **`ProvideCredential`**: Load credentials from various sources 44 | - **`SignRequest`**: Build service-specific signing requests 45 | - **`SigningCredential`**: Validate credential validity 46 | - **`FileRead`**: Async file reading operations 47 | - **`HttpSend`**: HTTP request execution 48 | - **`Env`**: Environment variable access 49 | 50 | ### Signer 51 | 52 | The `Signer` orchestrates the signing process by: 53 | 1. Loading credentials using the provided loader 54 | 2. Building signing requests with the builder 55 | 3. Applying signatures to HTTP requests 56 | 57 | ## Examples 58 | 59 | Check out the [custom_signer example](examples/custom_signer.rs) to see how to implement your own signing logic. 60 | 61 | ```bash 62 | cargo run --example custom_signer 63 | ``` 64 | 65 | ## Integration 66 | 67 | This crate is typically used with service-specific implementations: 68 | - `reqsign-aws-v4` for AWS services 69 | - `reqsign-aliyun-oss` for Aliyun OSS 70 | - `reqsign-azure-storage` for Azure Storage 71 | - And more... 72 | 73 | ## License 74 | 75 | Licensed under [Apache License, Version 2.0](./LICENSE). -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/profile.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::ProfileCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_profile_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_PROFILE").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_PROFILE not set, skipping"); 29 | return; 30 | } 31 | 32 | let mut envs = HashMap::new(); 33 | 34 | // Optional AWS profile name 35 | if let Ok(profile) = env::var("AWS_PROFILE") { 36 | envs.insert("AWS_PROFILE".to_string(), profile); 37 | } 38 | 39 | // Optional config file paths 40 | if let Ok(config_file) = env::var("AWS_CONFIG_FILE") { 41 | envs.insert("AWS_CONFIG_FILE".to_string(), config_file); 42 | } 43 | 44 | if let Ok(creds_file) = env::var("AWS_SHARED_CREDENTIALS_FILE") { 45 | envs.insert("AWS_SHARED_CREDENTIALS_FILE".to_string(), creds_file); 46 | } 47 | 48 | let ctx = create_test_context_with_env(envs); 49 | let provider = ProfileCredentialProvider::new(); 50 | 51 | let cred = provider 52 | .provide_credential(&ctx) 53 | .await 54 | .expect("ProfileCredentialProvider should not fail"); 55 | 56 | assert!( 57 | cred.is_some(), 58 | "Should load credentials from AWS profile files" 59 | ); 60 | let cred = cred.unwrap(); 61 | assert!(!cred.access_key_id.is_empty()); 62 | assert!(!cred.secret_access_key.is_empty()); 63 | } 64 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/env.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::EnvCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_env_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_ENV").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_ENV not set, skipping"); 29 | return; 30 | } 31 | 32 | // Use AWS native environment variables 33 | let mut envs = HashMap::from_iter([ 34 | ( 35 | "AWS_ACCESS_KEY_ID".to_string(), 36 | env::var("AWS_ACCESS_KEY_ID").expect("AWS_ACCESS_KEY_ID must be set"), 37 | ), 38 | ( 39 | "AWS_SECRET_ACCESS_KEY".to_string(), 40 | env::var("AWS_SECRET_ACCESS_KEY").expect("AWS_SECRET_ACCESS_KEY must be set"), 41 | ), 42 | ]); 43 | 44 | // Optional session token 45 | if let Ok(token) = env::var("AWS_SESSION_TOKEN") { 46 | envs.insert("AWS_SESSION_TOKEN".to_string(), token); 47 | } 48 | 49 | let ctx = create_test_context_with_env(envs); 50 | let provider = EnvCredentialProvider::new(); 51 | 52 | let cred = provider 53 | .provide_credential(&ctx) 54 | .await 55 | .expect("EnvCredentialProvider should not fail"); 56 | 57 | assert!( 58 | cred.is_some(), 59 | "Should load credentials from AWS_* env vars" 60 | ); 61 | let cred = cred.unwrap(); 62 | assert!(!cred.access_key_id.is_empty()); 63 | assert!(!cred.secret_access_key.is_empty()); 64 | } 65 | -------------------------------------------------------------------------------- /services/aws-v4/src/credential.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_core::SigningCredential; 19 | use reqsign_core::time::Timestamp; 20 | use reqsign_core::utils::Redact; 21 | use std::fmt::{Debug, Formatter}; 22 | use std::time::Duration; 23 | 24 | /// Credential that holds the access_key and secret_key. 25 | #[derive(Default, Clone)] 26 | pub struct Credential { 27 | /// Access key id for aws services. 28 | pub access_key_id: String, 29 | /// Secret access key for aws services. 30 | pub secret_access_key: String, 31 | /// Session token for aws services. 32 | pub session_token: Option, 33 | /// Expiration time for this credential. 34 | pub expires_in: Option, 35 | } 36 | 37 | impl Debug for Credential { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 39 | f.debug_struct("Credential") 40 | .field("access_key_id", &Redact::from(&self.access_key_id)) 41 | .field("secret_access_key", &Redact::from(&self.secret_access_key)) 42 | .field("session_token", &Redact::from(&self.session_token)) 43 | .field("expires_in", &self.expires_in) 44 | .finish() 45 | } 46 | } 47 | 48 | impl SigningCredential for Credential { 49 | fn is_valid(&self) -> bool { 50 | if (self.access_key_id.is_empty() || self.secret_access_key.is_empty()) 51 | && self.session_token.is_none() 52 | { 53 | return false; 54 | } 55 | // Take 120s as buffer to avoid edge cases. 56 | if let Some(valid) = self 57 | .expires_in 58 | .map(|v| v > Timestamp::now() + Duration::from_secs(120)) 59 | { 60 | return valid; 61 | } 62 | 63 | true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /services/azure-storage/tests/credential_providers/imds.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_azure_storage::{Credential, ImdsCredentialProvider}; 19 | use reqsign_core::{Context, OsEnv, ProvideCredential}; 20 | use reqsign_file_read_tokio::TokioFileRead; 21 | use reqsign_http_send_reqwest::ReqwestHttpSend; 22 | 23 | fn is_test_enabled() -> bool { 24 | std::env::var("REQSIGN_AZURE_STORAGE_TEST_IMDS").unwrap_or_default() == "on" 25 | } 26 | 27 | #[tokio::test] 28 | async fn test_imds_provider() { 29 | if !is_test_enabled() { 30 | eprintln!("Skipping test: REQSIGN_AZURE_STORAGE_TEST_IMDS is not enabled"); 31 | return; 32 | } 33 | 34 | let ctx = Context::new() 35 | .with_file_read(TokioFileRead) 36 | .with_http_send(ReqwestHttpSend::default()) 37 | .with_env(OsEnv); 38 | 39 | let _client_id = std::env::var("AZURE_CLIENT_ID").ok(); 40 | let _object_id = std::env::var("AZURE_OBJECT_ID").ok(); 41 | let _msi_res_id = std::env::var("AZURE_MSI_RESOURCE_ID").ok(); 42 | 43 | let loader = ImdsCredentialProvider::new(); 44 | 45 | // This test will only succeed when running on Azure VM with managed identity 46 | let result = loader.provide_credential(&ctx).await; 47 | 48 | let cred = result 49 | .expect("IMDS provider should succeed when test is enabled") 50 | .expect("IMDS provider should return credentials when test is enabled"); 51 | 52 | match cred { 53 | Credential::BearerToken { 54 | token, 55 | expires_in: _, 56 | } => { 57 | assert!(!token.is_empty()); 58 | eprintln!("Successfully obtained bearer token from IMDS"); 59 | } 60 | _ => panic!("Expected BearerToken credential from IMDS"), 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /services/aliyun-oss/src/credential.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_core::SigningCredential; 19 | use reqsign_core::time::Timestamp; 20 | use reqsign_core::utils::Redact; 21 | use std::fmt::{Debug, Formatter}; 22 | use std::time::Duration; 23 | 24 | /// Credential that holds the access_key and secret_key. 25 | #[derive(Default, Clone)] 26 | pub struct Credential { 27 | /// Access key id for aliyun services. 28 | pub access_key_id: String, 29 | /// Access key secret for aliyun services. 30 | pub access_key_secret: String, 31 | /// Security token for aliyun services. 32 | pub security_token: Option, 33 | /// Expiration time for this credential. 34 | pub expires_in: Option, 35 | } 36 | 37 | impl Debug for Credential { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 39 | f.debug_struct("Credential") 40 | .field("access_key_id", &Redact::from(&self.access_key_id)) 41 | .field("access_key_secret", &Redact::from(&self.access_key_secret)) 42 | .field("security_token", &Redact::from(&self.security_token)) 43 | .field("expires_in", &self.expires_in) 44 | .finish() 45 | } 46 | } 47 | 48 | impl SigningCredential for Credential { 49 | fn is_valid(&self) -> bool { 50 | if (self.access_key_id.is_empty() || self.access_key_secret.is_empty()) 51 | && self.security_token.is_none() 52 | { 53 | return false; 54 | } 55 | // Take 120s as buffer to avoid edge cases. 56 | if let Some(valid) = self 57 | .expires_in 58 | .map(|v| v > Timestamp::now() + Duration::from_secs(120)) 59 | { 60 | return valid; 61 | } 62 | 63 | true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/assume_role.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::info; 20 | use reqsign_aws_v4::{AssumeRoleCredentialProvider, DefaultCredentialProvider, RequestSigner}; 21 | use reqsign_core::{ProvideCredential, Signer}; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_assume_role_credential_provider() { 26 | if env::var("REQSIGN_AWS_V4_TEST_ASSUME_ROLE").unwrap_or_default() != "on" { 27 | info!("REQSIGN_AWS_V4_TEST_ASSUME_ROLE not set, skipping"); 28 | return; 29 | } 30 | 31 | let role_arn = env::var("REQSIGN_AWS_V4_ASSUME_ROLE_ARN") 32 | .expect("REQSIGN_AWS_V4_ASSUME_ROLE_ARN must be set for assume_role test"); 33 | 34 | let ctx = create_test_context(); 35 | let base_provider = DefaultCredentialProvider::new(); 36 | let region = env::var("AWS_REGION").unwrap_or_else(|_| "us-east-1".to_string()); 37 | 38 | // Create STS signer with base credentials 39 | let sts_signer = Signer::new( 40 | ctx.clone(), 41 | base_provider, 42 | RequestSigner::new("sts", ®ion), 43 | ); 44 | 45 | let provider = AssumeRoleCredentialProvider::new(role_arn, sts_signer) 46 | .with_region(region.clone()) 47 | .with_regional_sts_endpoint(); 48 | 49 | let cred = provider 50 | .provide_credential(&ctx) 51 | .await 52 | .expect("AssumeRole should succeed"); 53 | 54 | assert!(cred.is_some(), "AssumeRole should return credentials"); 55 | let cred = cred.unwrap(); 56 | assert!(!cred.access_key_id.is_empty()); 57 | assert!(!cred.secret_access_key.is_empty()); 58 | assert!( 59 | cred.session_token.is_some(), 60 | "AssumeRole should return session token" 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /services/azure-storage/tests/mocks/imds_mock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Minimal IMDS mock server for testing 4 | """ 5 | import json 6 | import time 7 | from http.server import HTTPServer, BaseHTTPRequestHandler 8 | from urllib.parse import urlparse, parse_qs 9 | 10 | class IMDSHandler(BaseHTTPRequestHandler): 11 | def do_GET(self): 12 | parsed = urlparse(self.path) 13 | query = parse_qs(parsed.query) 14 | 15 | # Check Metadata header 16 | if self.headers.get('Metadata') != 'true': 17 | self.send_error(400, "Metadata header required") 18 | return 19 | 20 | if parsed.path == '/metadata/identity/oauth2/token': 21 | # Accept both API versions (2018-02-01 and 2019-08-01) 22 | # Mock token response 23 | token_response = { 24 | "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJaUXBKTTBWYmJQWGhzMWpvT1ljYjh0WXhPXyIsImtpZCI6IjJaUXBKTTBWYmJQWGhzMWpvT1ljYjh0WXhPXyJ9.eyJhdWQiOiJodHRwczovL3N0b3JhZ2UuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzEyMzQ1Njc4LTEyMzQtMTIzNC0xMjM0LTEyMzQ1Njc4OTAxMi8iLCJpYXQiOjE2MzM1MzY0MjIsIm5iZiI6MTYzMzUzNjQyMiwiZXhwIjoxNjMzNjIzMTIyLCJhaW8iOiJFMlpnWUxqL3Y3Ly9kWitQL0JBQSIsImFwcGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyLyIsIm9pZCI6IjEyMzQ1Njc4LTEyMzQtMTIzNC0xMjM0LTEyMzQ1Njc4OTAxMiIsInJoIjoiMC5BUm9BZUhZME5HRHlORWFTVGh0bUZZaVNFZ2dBQUFBQUFBQUF3QUFBQUFBQUFBQ2NBQUEuIiwic3ViIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidmVyIjoiMS4wIn0.mock_signature", 25 | "expires_in": "3600", 26 | "expires_on": str(int(time.time()) + 3600), 27 | "resource": query.get('resource', [''])[0], 28 | "token_type": "Bearer" 29 | } 30 | self.send_response(200) 31 | self.send_header('Content-Type', 'application/json') 32 | self.end_headers() 33 | self.wfile.write(json.dumps(token_response).encode()) 34 | else: 35 | self.send_error(404, "Not Found") 36 | 37 | def log_message(self, format, *args): 38 | pass # Suppress logs 39 | 40 | if __name__ == '__main__': 41 | import sys 42 | port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 43 | server = HTTPServer(('', port), IMDSHandler) 44 | print(f"IMDS mock server started on port {port}") 45 | server.serve_forever() -------------------------------------------------------------------------------- /reqsign/src/oracle.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Oracle Cloud service support with convenience APIs 19 | //! 20 | //! This module provides Oracle Cloud signing functionality along with convenience 21 | //! functions for common use cases. 22 | 23 | // Re-export all Oracle Cloud signing types 24 | pub use reqsign_oracle::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default Oracle Cloud Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default Oracle Cloud signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from config file) 38 | /// - Request signer for Oracle Cloud 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for Oracle Cloud 46 | /// let signer = reqsign::oracle::default_signer(); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://objectstorage.us-phoenix-1.oraclecloud.com/n/namespace/b/bucket/o/object") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | #[cfg(feature = "default-context")] 62 | pub fn default_signer() -> DefaultSigner { 63 | let ctx = default_context(); 64 | let provider = DefaultCredentialProvider::new(); 65 | let signer = RequestSigner::new(); 66 | Signer::new(ctx, provider, signer) 67 | } 68 | -------------------------------------------------------------------------------- /reqsign/src/tencent.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Tencent Cloud COS service support with convenience APIs 19 | //! 20 | //! This module provides Tencent Cloud COS signing functionality along with convenience 21 | //! functions for common use cases. 22 | 23 | // Re-export all Tencent Cloud COS signing types 24 | pub use reqsign_tencent_cos::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default Tencent Cloud COS Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default Tencent Cloud COS signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from env vars) 38 | /// - Request signer for Tencent Cloud COS 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for Tencent Cloud COS 46 | /// let signer = reqsign::tencent::default_signer(); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://mybucket-1234567890.cos.ap-beijing.myqcloud.com/myobject") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | #[cfg(feature = "default-context")] 62 | pub fn default_signer() -> DefaultSigner { 63 | let ctx = default_context(); 64 | let provider = DefaultCredentialProvider::new(); 65 | let signer = RequestSigner::new(); 66 | Signer::new(ctx, provider, signer) 67 | } 68 | -------------------------------------------------------------------------------- /reqsign/src/aliyun.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Aliyun OSS service support with convenience APIs 19 | //! 20 | //! This module provides Aliyun OSS signing functionality along with convenience 21 | //! functions for common use cases. 22 | 23 | // Re-export all Aliyun OSS signing types 24 | pub use reqsign_aliyun_oss::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default Aliyun OSS Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default Aliyun OSS signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from env vars, config files, ECS metadata, etc.) 38 | /// - Request signer for the specified bucket 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for Aliyun OSS bucket 46 | /// let signer = reqsign::aliyun::default_signer("mybucket"); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://mybucket.oss-cn-hangzhou.aliyuncs.com/myobject") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | #[cfg(feature = "default-context")] 62 | pub fn default_signer(bucket: &str) -> DefaultSigner { 63 | let ctx = default_context(); 64 | let provider = DefaultCredentialProvider::new(); 65 | let signer = RequestSigner::new(bucket); 66 | Signer::new(ctx, provider, signer) 67 | } 68 | -------------------------------------------------------------------------------- /services/oracle/src/credential.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_core::SigningCredential; 19 | use reqsign_core::time::Timestamp; 20 | use reqsign_core::utils::Redact; 21 | use std::fmt::{Debug, Formatter}; 22 | use std::time::Duration; 23 | 24 | /// Credential that holds the API private key information. 25 | #[derive(Default, Clone)] 26 | pub struct Credential { 27 | /// TenantID for Oracle Cloud Infrastructure. 28 | pub tenancy: String, 29 | /// UserID for Oracle Cloud Infrastructure. 30 | pub user: String, 31 | /// API Private Key file path for credential. 32 | pub key_file: String, 33 | /// Fingerprint of the API Key. 34 | pub fingerprint: String, 35 | /// Expiration time for this credential. 36 | pub expires_in: Option, 37 | } 38 | 39 | impl Debug for Credential { 40 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 41 | f.debug_struct("Credential") 42 | .field("tenancy", &self.tenancy) 43 | .field("user", &self.user) 44 | .field("key_file", &Redact::from(&self.key_file)) 45 | .field("fingerprint", &self.fingerprint) 46 | .field("expires_in", &self.expires_in) 47 | .finish() 48 | } 49 | } 50 | 51 | impl SigningCredential for Credential { 52 | fn is_valid(&self) -> bool { 53 | if self.tenancy.is_empty() 54 | || self.user.is_empty() 55 | || self.key_file.is_empty() 56 | || self.fingerprint.is_empty() 57 | { 58 | return false; 59 | } 60 | // Take 120s as buffer to avoid edge cases. 61 | if let Some(valid) = self 62 | .expires_in 63 | .map(|v| v > Timestamp::now() + Duration::from_secs(120)) 64 | { 65 | return valid; 66 | } 67 | 68 | true 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /reqsign/src/huaweicloud.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Huawei Cloud OBS service support with convenience APIs 19 | //! 20 | //! This module provides Huawei Cloud OBS signing functionality along with convenience 21 | //! functions for common use cases. 22 | 23 | // Re-export all Huawei Cloud OBS signing types 24 | pub use reqsign_huaweicloud_obs::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default Huawei Cloud OBS Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default Huawei Cloud OBS signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from env vars) 38 | /// - Request signer for the specified bucket 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for Huawei Cloud OBS bucket 46 | /// let signer = reqsign::huaweicloud::default_signer("mybucket"); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://mybucket.obs.cn-north-1.myhuaweicloud.com/myobject") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | #[cfg(feature = "default-context")] 62 | pub fn default_signer(bucket: &str) -> DefaultSigner { 63 | let ctx = default_context(); 64 | let provider = DefaultCredentialProvider::new(); 65 | let signer = RequestSigner::new(bucket); 66 | Signer::new(ctx, provider, signer) 67 | } 68 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | mod assume_role; 19 | mod assume_role_with_web_identity; 20 | mod cognito; 21 | mod ecs; 22 | mod env; 23 | mod imds; 24 | #[cfg(not(target_arch = "wasm32"))] 25 | mod process; 26 | mod profile; 27 | mod s3_express; 28 | #[cfg(not(target_arch = "wasm32"))] 29 | mod sso; 30 | 31 | #[cfg(not(target_arch = "wasm32"))] 32 | use reqsign_command_execute_tokio::TokioCommandExecute; 33 | use reqsign_core::{Context, OsEnv, StaticEnv}; 34 | use reqsign_file_read_tokio::TokioFileRead; 35 | use reqsign_http_send_reqwest::ReqwestHttpSend; 36 | use std::collections::HashMap; 37 | 38 | pub fn create_test_context() -> Context { 39 | let _ = env_logger::builder().is_test(true).try_init(); 40 | let _ = dotenv::dotenv(); 41 | 42 | let mut ctx = Context::new() 43 | .with_file_read(TokioFileRead) 44 | .with_http_send(ReqwestHttpSend::default()) 45 | .with_env(OsEnv); 46 | 47 | #[cfg(not(target_arch = "wasm32"))] 48 | { 49 | ctx = ctx.with_command_execute(TokioCommandExecute); 50 | } 51 | 52 | ctx 53 | } 54 | 55 | pub fn create_test_context_with_env(envs: HashMap) -> Context { 56 | let _ = env_logger::builder().is_test(true).try_init(); 57 | let _ = dotenv::dotenv(); 58 | 59 | // Get home directory from HOME environment variable if set 60 | let home_dir = std::env::var("HOME").ok().map(std::path::PathBuf::from); 61 | 62 | let mut ctx = Context::new() 63 | .with_file_read(TokioFileRead) 64 | .with_http_send(ReqwestHttpSend::default()) 65 | .with_env(OsEnv); 66 | 67 | #[cfg(not(target_arch = "wasm32"))] 68 | { 69 | ctx = ctx.with_command_execute(TokioCommandExecute); 70 | } 71 | 72 | // StaticEnv overrides specific environment variables on top of OsEnv 73 | ctx.with_env(StaticEnv { home_dir, envs }) 74 | } 75 | -------------------------------------------------------------------------------- /context/file-read-tokio/examples/read_credentials.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use anyhow::Result; 19 | use reqsign_core::{Context, OsEnv}; 20 | use reqsign_file_read_tokio::TokioFileRead; 21 | use std::env; 22 | 23 | #[tokio::main] 24 | async fn main() -> Result<()> { 25 | // Create a context with Tokio file reader 26 | let ctx = Context::new().with_file_read(TokioFileRead).with_env(OsEnv); 27 | 28 | // Get the path from command line arguments or use a demo file 29 | let path = env::args().nth(1).unwrap_or_else(|| { 30 | // Create a temporary demo file for the example 31 | let demo_content = 32 | "[default]\naws_access_key_id = DEMO_KEY\naws_secret_access_key = DEMO_SECRET\n"; 33 | if let Some(temp_dir) = std::env::temp_dir().to_str() { 34 | let demo_path = format!("{temp_dir}/reqsign_demo_credentials"); 35 | let _ = std::fs::write(&demo_path, demo_content); 36 | return demo_path; 37 | } 38 | "demo_credentials".to_string() 39 | }); 40 | 41 | println!("Attempting to read file: {path}"); 42 | 43 | // Read the file asynchronously 44 | match ctx.file_read(&path).await { 45 | Ok(content) => { 46 | println!("Successfully read {} bytes from {}", content.len(), path); 47 | 48 | // Try to parse as UTF-8 and show a preview 49 | if let Ok(text) = String::from_utf8(content.clone()) { 50 | let preview: String = text.lines().take(5).collect::>().join("\n"); 51 | println!("\nFirst few lines:"); 52 | println!("{preview}"); 53 | if text.lines().count() > 5 { 54 | println!("... ({} more lines)", text.lines().count() - 5); 55 | } 56 | } 57 | } 58 | Err(e) => { 59 | eprintln!("Failed to read file: {e}"); 60 | eprintln!("Make sure the file exists and you have permission to read it."); 61 | } 62 | } 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/assume_role_with_web_identity.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::AssumeRoleWithWebIdentityCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_assume_role_with_web_identity_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_WEB_IDENTITY").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_WEB_IDENTITY not set, skipping"); 29 | return; 30 | } 31 | 32 | // Use AWS native environment variables 33 | let mut envs = HashMap::new(); 34 | 35 | // Required variables 36 | envs.insert( 37 | "AWS_ROLE_ARN".to_string(), 38 | env::var("AWS_ROLE_ARN").expect("AWS_ROLE_ARN must be set"), 39 | ); 40 | envs.insert( 41 | "AWS_WEB_IDENTITY_TOKEN_FILE".to_string(), 42 | env::var("AWS_WEB_IDENTITY_TOKEN_FILE").expect("AWS_WEB_IDENTITY_TOKEN_FILE must be set"), 43 | ); 44 | 45 | // Optional variables 46 | if let Ok(session_name) = env::var("AWS_ROLE_SESSION_NAME") { 47 | envs.insert("AWS_ROLE_SESSION_NAME".to_string(), session_name); 48 | } 49 | if let Ok(region) = env::var("AWS_REGION") { 50 | envs.insert("AWS_REGION".to_string(), region); 51 | } 52 | 53 | let ctx = create_test_context_with_env(envs); 54 | let provider = AssumeRoleWithWebIdentityCredentialProvider::new(); 55 | 56 | let cred = provider 57 | .provide_credential(&ctx) 58 | .await 59 | .expect("AssumeRoleWithWebIdentity should succeed"); 60 | 61 | assert!( 62 | cred.is_some(), 63 | "Should load credentials via web identity token" 64 | ); 65 | let cred = cred.unwrap(); 66 | assert!(!cred.access_key_id.is_empty()); 67 | assert!(!cred.secret_access_key.is_empty()); 68 | assert!( 69 | cred.session_token.is_some(), 70 | "Web identity should return session token" 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /services/azure-storage/tests/credential_providers/azure_cli.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #[cfg(not(target_arch = "wasm32"))] 19 | use reqsign_azure_storage::{AzureCliCredentialProvider, Credential}; 20 | #[cfg(not(target_arch = "wasm32"))] 21 | use reqsign_command_execute_tokio::TokioCommandExecute; 22 | #[cfg(not(target_arch = "wasm32"))] 23 | use reqsign_core::{Context, OsEnv, ProvideCredential}; 24 | #[cfg(not(target_arch = "wasm32"))] 25 | use reqsign_file_read_tokio::TokioFileRead; 26 | #[cfg(not(target_arch = "wasm32"))] 27 | use reqsign_http_send_reqwest::ReqwestHttpSend; 28 | 29 | #[cfg(not(target_arch = "wasm32"))] 30 | fn is_test_enabled() -> bool { 31 | std::env::var("REQSIGN_AZURE_STORAGE_TEST_CLI").unwrap_or_default() == "on" 32 | } 33 | 34 | #[cfg(not(target_arch = "wasm32"))] 35 | #[tokio::test] 36 | async fn test_azure_cli_provider() { 37 | if !is_test_enabled() { 38 | eprintln!("Skipping test: REQSIGN_AZURE_STORAGE_TEST_CLI is not enabled"); 39 | return; 40 | } 41 | 42 | let ctx = Context::new() 43 | .with_file_read(TokioFileRead) 44 | .with_http_send(ReqwestHttpSend::default()) 45 | .with_command_execute(TokioCommandExecute) 46 | .with_env(OsEnv); 47 | 48 | let loader = AzureCliCredentialProvider::new(); 49 | 50 | // This test requires Azure CLI to be installed and logged in 51 | let result = loader.provide_credential(&ctx).await; 52 | 53 | // Better error reporting 54 | let cred = match result { 55 | Ok(Some(cred)) => cred, 56 | Ok(None) => panic!("Azure CLI provider returned None when test is enabled"), 57 | Err(e) => panic!("Azure CLI provider failed with error: {e:?}"), 58 | }; 59 | 60 | match cred { 61 | Credential::BearerToken { 62 | token, 63 | expires_in: _, 64 | } => { 65 | assert!(!token.is_empty()); 66 | eprintln!("Successfully obtained bearer token from Azure CLI"); 67 | } 68 | _ => panic!("Expected BearerToken credential from Azure CLI"), 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [workspace] 19 | members = ["core", "context/*", "services/*", "reqsign"] 20 | resolver = "2" 21 | 22 | [workspace.package] 23 | edition = "2024" 24 | license = "Apache-2.0" 25 | repository = "https://github.com/apache/opendal-reqsign" 26 | rust-version = "1.85.0" 27 | 28 | [workspace.dependencies] 29 | # Workspace dependencies 30 | reqsign-aliyun-oss = { version = "2.0.1", path = "services/aliyun-oss" } 31 | reqsign-aws-v4 = { version = "2.0.1", path = "services/aws-v4" } 32 | reqsign-azure-storage = { version = "2.0.1", path = "services/azure-storage" } 33 | reqsign-command-execute-tokio = { version = "2.0.1", path = "context/command-execute-tokio" } 34 | reqsign-core = { version = "2.0.1", path = "core" } 35 | reqsign-file-read-tokio = { version = "2.0.1", path = "context/file-read-tokio" } 36 | reqsign-google = { version = "2.0.1", path = "services/google" } 37 | reqsign-http-send-reqwest = { version = "2.0.1", path = "context/http-send-reqwest" } 38 | reqsign-huaweicloud-obs = { version = "2.0.1", path = "services/huaweicloud-obs" } 39 | reqsign-oracle = { version = "2.0.1", path = "services/oracle" } 40 | reqsign-tencent-cos = { version = "2.0.1", path = "services/tencent-cos" } 41 | 42 | # Crates.io dependencies 43 | anyhow = "1" 44 | async-trait = "0.1" 45 | base64 = "0.22" 46 | bytes = "1" 47 | criterion = { version = "0.7.0", features = ["async_tokio", "html_reports"] } 48 | dotenv = "0.15" 49 | env_logger = "0.11" 50 | form_urlencoded = "1" 51 | hex = "0.4" 52 | hmac = "0.12" 53 | http = "1" 54 | jiff = "0.2" 55 | log = "0.4" 56 | percent-encoding = "2" 57 | pretty_assertions = "1.3" 58 | quick-xml = { version = "0.38.1", features = ["serialize"] } 59 | rand = { version = "0.8.5" } 60 | reqwest = { version = "0.12", default-features = false } 61 | rsa = { version = "0.9.2", features = ["pkcs5", "sha2"] } 62 | rust-ini = { version = "0.21" } 63 | serde = { version = "1", features = ["derive"] } 64 | serde_json = { version = "1" } 65 | sha1 = "0.10" 66 | sha2 = { version = "0.10", features = ["oid"] } 67 | tempfile = "3.8" 68 | tokio = { version = "1", default-features = false } 69 | -------------------------------------------------------------------------------- /services/google/tests/credential_providers/vm_metadata.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::warn; 20 | use reqsign_core::{ProvideCredential, Result}; 21 | use reqsign_google::VmMetadataCredentialProvider; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_vm_metadata_credential_provider() -> Result<()> { 26 | if env::var("REQSIGN_GOOGLE_TEST_VM_METADATA").unwrap_or_default() != "on" { 27 | warn!("REQSIGN_GOOGLE_TEST_VM_METADATA is not set, skipped"); 28 | return Ok(()); 29 | } 30 | 31 | // This test should only run on actual GCP VMs 32 | let ctx = create_test_context(); 33 | 34 | let provider = VmMetadataCredentialProvider::new(); 35 | let credential = provider 36 | .provide_credential(&ctx) 37 | .await? 38 | .expect("credential must be provided on GCP VM"); 39 | 40 | assert!(credential.has_token(), "Must have access token"); 41 | assert!(credential.has_valid_token(), "Token must be valid"); 42 | let token = credential.token.as_ref().unwrap(); 43 | assert!(!token.access_token.is_empty(), "Token must not be empty"); 44 | 45 | Ok(()) 46 | } 47 | 48 | #[tokio::test] 49 | async fn test_vm_metadata_credential_provider_with_scope() -> Result<()> { 50 | if env::var("REQSIGN_GOOGLE_TEST_VM_METADATA").unwrap_or_default() != "on" { 51 | warn!("REQSIGN_GOOGLE_TEST_VM_METADATA is not set, skipped"); 52 | return Ok(()); 53 | } 54 | 55 | // This test allows specifying a custom scope 56 | let scope = env::var("REQSIGN_GOOGLE_SCOPE") 57 | .unwrap_or_else(|_| "https://www.googleapis.com/auth/devstorage.read_write".to_string()); 58 | 59 | let ctx = create_test_context(); 60 | 61 | let provider = VmMetadataCredentialProvider::new().with_scope(&scope); 62 | let credential = provider 63 | .provide_credential(&ctx) 64 | .await? 65 | .expect("credential must be provided on GCP VM"); 66 | 67 | assert!(credential.has_token(), "Must have access token"); 68 | assert!(credential.has_valid_token(), "Token must be valid"); 69 | 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /services/aws-v4/tests/credential_providers/ecs.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::info; 20 | use reqsign_aws_v4::ECSCredentialProvider; 21 | use reqsign_core::ProvideCredential; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_ecs_credential_provider() { 27 | if env::var("REQSIGN_AWS_V4_TEST_ECS").unwrap_or_default() != "on" { 28 | info!("REQSIGN_AWS_V4_TEST_ECS not set, skipping"); 29 | return; 30 | } 31 | 32 | let mut envs = HashMap::new(); 33 | 34 | // Add custom metadata endpoint if set (for testing) 35 | if let Ok(metadata_uri) = env::var("ECS_CONTAINER_METADATA_URI") { 36 | envs.insert("ECS_CONTAINER_METADATA_URI".to_string(), metadata_uri); 37 | } 38 | 39 | // ECS can use either relative or full URI 40 | if let Ok(relative_uri) = env::var("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") { 41 | envs.insert( 42 | "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI".to_string(), 43 | relative_uri, 44 | ); 45 | } else if let Ok(full_uri) = env::var("AWS_CONTAINER_CREDENTIALS_FULL_URI") { 46 | envs.insert("AWS_CONTAINER_CREDENTIALS_FULL_URI".to_string(), full_uri); 47 | // Full URI also requires authorization token 48 | if let Ok(token) = env::var("AWS_CONTAINER_AUTHORIZATION_TOKEN") { 49 | envs.insert("AWS_CONTAINER_AUTHORIZATION_TOKEN".to_string(), token); 50 | } 51 | } else { 52 | panic!( 53 | "Either AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or AWS_CONTAINER_CREDENTIALS_FULL_URI must be set" 54 | ); 55 | } 56 | 57 | let ctx = create_test_context_with_env(envs); 58 | let provider = ECSCredentialProvider::new(); 59 | 60 | let cred = provider 61 | .provide_credential(&ctx) 62 | .await 63 | .expect("ECSCredentialProvider should succeed"); 64 | 65 | assert!(cred.is_some(), "Should load credentials from ECS"); 66 | let cred = cred.unwrap(); 67 | assert!(!cred.access_key_id.is_empty()); 68 | assert!(!cred.secret_access_key.is_empty()); 69 | } 70 | -------------------------------------------------------------------------------- /services/azure-storage/tests/credential_providers/client_secret.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_azure_storage::{ClientSecretCredentialProvider, Credential}; 19 | use reqsign_core::{Context, OsEnv, ProvideCredential}; 20 | use reqsign_file_read_tokio::TokioFileRead; 21 | use reqsign_http_send_reqwest::ReqwestHttpSend; 22 | 23 | fn is_test_enabled() -> bool { 24 | std::env::var("REQSIGN_AZURE_STORAGE_TEST_CLIENT_SECRET").unwrap_or_default() == "on" 25 | } 26 | 27 | #[tokio::test] 28 | async fn test_client_secret_provider() { 29 | if !is_test_enabled() { 30 | eprintln!("Skipping test: REQSIGN_AZURE_STORAGE_TEST_CLIENT_SECRET is not enabled"); 31 | return; 32 | } 33 | 34 | let _tenant_id = std::env::var("AZURE_TENANT_ID") 35 | .expect("AZURE_TENANT_ID must be set for client secret test"); 36 | let _client_id = std::env::var("AZURE_CLIENT_ID") 37 | .expect("AZURE_CLIENT_ID must be set for client secret test"); 38 | let _client_secret = std::env::var("AZURE_CLIENT_SECRET") 39 | .expect("AZURE_CLIENT_SECRET must be set for client secret test"); 40 | 41 | let ctx = Context::new() 42 | .with_file_read(TokioFileRead) 43 | .with_http_send(ReqwestHttpSend::default()) 44 | .with_env(OsEnv); 45 | 46 | let loader = ClientSecretCredentialProvider::new(); 47 | let result = loader.provide_credential(&ctx).await; 48 | 49 | // Better error reporting 50 | let cred = match result { 51 | Ok(Some(cred)) => cred, 52 | Ok(None) => panic!("Client secret provider returned None when test is enabled"), 53 | Err(e) => panic!("Client secret provider failed with error: {e:?}\nError details: {e}"), 54 | }; 55 | 56 | match cred { 57 | Credential::BearerToken { 58 | token, 59 | expires_in: _, 60 | } => { 61 | assert!(!token.is_empty()); 62 | // Token should be a valid JWT 63 | assert!(token.starts_with("eyJ")); 64 | eprintln!("Successfully obtained bearer token using client secret"); 65 | } 66 | _ => panic!("Expected BearerToken credential from client secret provider"), 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /reqsign/src/context.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use reqsign_core::{Context, OsEnv}; 19 | 20 | #[cfg(not(target_arch = "wasm32"))] 21 | use reqsign_command_execute_tokio::TokioCommandExecute; 22 | #[cfg(not(target_arch = "wasm32"))] 23 | use reqsign_file_read_tokio::TokioFileRead; 24 | use reqsign_http_send_reqwest::ReqwestHttpSend; 25 | 26 | /// Create a Context with default implementations. 27 | /// 28 | /// This function returns a Context configured with: 29 | /// - `TokioCommandExecute` for command execution (non-WASM only) 30 | /// - `TokioFileRead` for file reading (non-WASM only) 31 | /// - `ReqwestHttpSend` for HTTP requests 32 | /// - `OsEnv` for environment variable access 33 | /// 34 | /// # Example 35 | /// 36 | /// ```no_run 37 | /// # async fn example() -> reqsign_core::Result<()> { 38 | /// let ctx = reqsign::default_context(); 39 | /// 40 | /// // Use the context directly 41 | /// let response = ctx.http_send(http::Request::builder() 42 | /// .uri("https://api.example.com") 43 | /// .body(bytes::Bytes::new())?) 44 | /// .await?; 45 | /// # Ok(()) 46 | /// # } 47 | /// ``` 48 | /// 49 | /// # Customization 50 | /// 51 | /// You can replace any component by chaining method calls: 52 | /// 53 | /// ```no_run 54 | /// # async fn example() -> reqsign_core::Result<()> { 55 | /// // Example: Replace with a custom environment implementation 56 | /// use reqsign_core::StaticEnv; 57 | /// use std::collections::HashMap; 58 | /// 59 | /// let mut envs = HashMap::new(); 60 | /// envs.insert("AWS_ACCESS_KEY_ID".to_string(), "my-key".to_string()); 61 | /// 62 | /// let ctx = reqsign::default_context() 63 | /// .with_env(StaticEnv { envs, home_dir: None }); 64 | /// # Ok(()) 65 | /// # } 66 | /// ``` 67 | pub fn default_context() -> Context { 68 | #[cfg(not(target_arch = "wasm32"))] 69 | { 70 | Context::new() 71 | .with_command_execute(TokioCommandExecute) 72 | .with_file_read(TokioFileRead) 73 | .with_http_send(ReqwestHttpSend::default()) 74 | .with_env(OsEnv) 75 | } 76 | 77 | #[cfg(target_arch = "wasm32")] 78 | { 79 | Context::new() 80 | .with_http_send(ReqwestHttpSend::default()) 81 | .with_env(OsEnv) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Utility functions and types. 19 | 20 | use std::fmt::Debug; 21 | 22 | /// Redacts a string by replacing all but the first and last three characters with asterisks. 23 | /// 24 | /// - If the input string has fewer than 12 characters, it should be entirely redacted. 25 | /// - If the input string has 12 or more characters, only the first three and the last three. 26 | /// 27 | /// This design is to allow users to distinguish between different redacted strings but avoid 28 | /// leaking sensitive information. 29 | pub struct Redact<'a>(&'a str); 30 | 31 | impl<'a> From<&'a str> for Redact<'a> { 32 | fn from(value: &'a str) -> Self { 33 | Redact(value) 34 | } 35 | } 36 | 37 | impl<'a> From<&'a String> for Redact<'a> { 38 | fn from(value: &'a String) -> Self { 39 | Redact(value.as_str()) 40 | } 41 | } 42 | 43 | impl<'a> From<&'a Option> for Redact<'a> { 44 | fn from(value: &'a Option) -> Self { 45 | match value { 46 | None => Redact(""), 47 | Some(v) => Redact(v), 48 | } 49 | } 50 | } 51 | 52 | impl Debug for Redact<'_> { 53 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 54 | let length = self.0.len(); 55 | if length == 0 { 56 | f.write_str("EMPTY") 57 | } else if length < 12 { 58 | f.write_str("***") 59 | } else { 60 | f.write_str(&self.0[..3])?; 61 | f.write_str("***")?; 62 | f.write_str(&self.0[length - 3..]) 63 | } 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | 71 | #[test] 72 | fn test_redact() { 73 | let cases = vec![ 74 | ("Short", "***"), 75 | ("Hello World!", "Hel***ld!"), 76 | ("This is a longer string", "Thi***ing"), 77 | ("", "EMPTY"), 78 | ("HelloWorld", "***"), 79 | ]; 80 | 81 | for (input, expected) in cases { 82 | assert_eq!( 83 | format!("{:?}", Redact(input)), 84 | expected, 85 | "Failed on input: {}", 86 | input 87 | ); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /services/oracle/src/provide_credential/config.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![allow(deprecated)] 19 | 20 | use crate::{Config, Credential}; 21 | use async_trait::async_trait; 22 | use log::debug; 23 | use reqsign_core::time::Timestamp; 24 | use reqsign_core::{Context, ProvideCredential, Result}; 25 | use std::sync::Arc; 26 | use std::time::Duration; 27 | 28 | /// Static configuration based loader. 29 | #[derive(Debug)] 30 | pub struct ConfigCredentialProvider { 31 | config: Arc, 32 | } 33 | 34 | impl ConfigCredentialProvider { 35 | /// Create a new ConfigCredentialProvider 36 | pub fn new(config: Arc) -> Self { 37 | Self { config } 38 | } 39 | } 40 | 41 | #[async_trait] 42 | impl ProvideCredential for ConfigCredentialProvider { 43 | type Credential = Credential; 44 | 45 | async fn provide_credential(&self, ctx: &Context) -> Result> { 46 | // Merge with environment config 47 | let env_config = Config::from_env(ctx); 48 | let config = self.config.as_ref(); 49 | 50 | // Use environment values if available, otherwise fall back to config 51 | let tenancy = env_config.tenancy.or_else(|| config.tenancy.clone()); 52 | let user = env_config.user.or_else(|| config.user.clone()); 53 | let key_file = env_config.key_file.or_else(|| config.key_file.clone()); 54 | let fingerprint = env_config 55 | .fingerprint 56 | .or_else(|| config.fingerprint.clone()); 57 | 58 | match (&tenancy, &user, &key_file, &fingerprint) { 59 | (Some(tenancy), Some(user), Some(key_file), Some(fingerprint)) => { 60 | debug!("loading credential from config"); 61 | Ok(Some(Credential { 62 | tenancy: tenancy.clone(), 63 | user: user.clone(), 64 | key_file: key_file.clone(), 65 | fingerprint: fingerprint.clone(), 66 | // Set expires_in to 10 minutes to enforce re-read 67 | expires_in: Some(Timestamp::now() + Duration::from_secs(600)), 68 | })) 69 | } 70 | _ => { 71 | debug!("incomplete config, skipping"); 72 | Ok(None) 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /reqsign/src/azure.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Azure Storage service support with convenience APIs 19 | //! 20 | //! This module provides Azure Storage signing functionality along with convenience 21 | //! functions for common use cases. 22 | 23 | // Re-export all Azure Storage signing types 24 | pub use reqsign_azure_storage::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default Azure Storage Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default Azure Storage signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from env vars, managed identity, CLI, etc.) 38 | /// - Request signer for Azure Storage 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for Azure Storage 46 | /// let signer = reqsign::azure::default_signer(); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://myaccount.blob.core.windows.net/mycontainer/myblob") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | /// 62 | /// # Customization 63 | /// 64 | /// You can customize the signer using the `with_*` methods: 65 | /// 66 | /// ```no_run 67 | /// # async fn example() -> reqsign_core::Result<()> { 68 | /// use reqsign::azure::{default_signer, StaticCredentialProvider}; 69 | /// 70 | /// let signer = default_signer() 71 | /// .with_credential_provider(StaticCredentialProvider::new_shared_key( 72 | /// "myaccount", 73 | /// "my-account-key", 74 | /// )); 75 | /// # Ok(()) 76 | /// # } 77 | /// ``` 78 | #[cfg(feature = "default-context")] 79 | pub fn default_signer() -> DefaultSigner { 80 | let ctx = default_context(); 81 | let provider = DefaultCredentialProvider::new(); 82 | let signer = RequestSigner::new(); 83 | Signer::new(ctx, provider, signer) 84 | } 85 | -------------------------------------------------------------------------------- /reqsign/src/aws.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! AWS service support with convenience APIs 19 | //! 20 | //! This module provides AWS signing functionality along with convenience functions 21 | //! for common use cases. 22 | 23 | // Re-export all AWS signing types 24 | pub use reqsign_aws_v4::*; 25 | 26 | #[cfg(feature = "default-context")] 27 | use crate::{Signer, default_context}; 28 | 29 | /// Default AWS Signer type with commonly used components 30 | #[cfg(feature = "default-context")] 31 | pub type DefaultSigner = Signer; 32 | 33 | /// Create a default AWS signer with standard configuration 34 | /// 35 | /// This function creates a signer with: 36 | /// - Default context (with Tokio file reader, reqwest HTTP client, OS environment) 37 | /// - Default credential provider (reads from env vars, config files, IMDS, etc.) 38 | /// - Request signer for the specified service and region 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # #[tokio::main] 44 | /// # async fn main() -> reqsign_core::Result<()> { 45 | /// // Create a signer for S3 in us-east-1 46 | /// let signer = reqsign::aws::default_signer("s3", "us-east-1"); 47 | /// 48 | /// // Sign a request 49 | /// let mut req = http::Request::builder() 50 | /// .method("GET") 51 | /// .uri("https://s3.amazonaws.com/my-bucket/my-object") 52 | /// .body(()) 53 | /// .unwrap() 54 | /// .into_parts() 55 | /// .0; 56 | /// 57 | /// signer.sign(&mut req, None).await?; 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | /// 62 | /// # Customization 63 | /// 64 | /// You can customize the signer using the `with_*` methods: 65 | /// 66 | /// ```no_run 67 | /// # async fn example() -> reqsign_core::Result<()> { 68 | /// use reqsign::aws::{default_signer, StaticCredentialProvider}; 69 | /// 70 | /// let signer = default_signer("s3", "us-east-1") 71 | /// .with_credential_provider(StaticCredentialProvider::new( 72 | /// "my-access-key", 73 | /// "my-secret-key", 74 | /// )); 75 | /// # Ok(()) 76 | /// # } 77 | /// ``` 78 | #[cfg(feature = "default-context")] 79 | pub fn default_signer(service: &str, region: &str) -> DefaultSigner { 80 | let ctx = default_context(); 81 | let provider = DefaultCredentialProvider::new(); 82 | let signer = RequestSigner::new(service, region); 83 | Signer::new(ctx, provider, signer) 84 | } 85 | -------------------------------------------------------------------------------- /services/aws-v4/tests/mocks/sso_mock_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Mock SSO Credentials Server 4 | 5 | This server simulates the AWS SSO (IAM Identity Center) API for testing purposes. 6 | It responds to credential requests at the /federation/credentials endpoint. 7 | """ 8 | 9 | from http.server import HTTPServer, BaseHTTPRequestHandler 10 | import json 11 | import time 12 | from urllib.parse import urlparse, parse_qs 13 | 14 | 15 | class SSOHandler(BaseHTTPRequestHandler): 16 | def do_GET(self): 17 | parsed_url = urlparse(self.path) 18 | params = parse_qs(parsed_url.query) 19 | 20 | # Check if this is the SSO credentials endpoint 21 | if parsed_url.path == '/federation/credentials': 22 | # Verify required parameters 23 | if 'role_name' in params and 'account_id' in params: 24 | # Check for authorization header 25 | auth_header = self.headers.get('x-amz-sso_bearer_token') 26 | if auth_header != 'test-access-token-for-sso': 27 | self.send_response(401) 28 | self.send_header('Content-Type', 'application/json') 29 | self.end_headers() 30 | error = {'message': 'Unauthorized: Invalid or missing bearer token'} 31 | self.wfile.write(json.dumps(error).encode()) 32 | return 33 | 34 | # Return mock credentials 35 | expiration = int((time.time() + 3600) * 1000) # 1 hour in milliseconds 36 | response = { 37 | 'roleCredentials': { 38 | 'accessKeyId': 'ASIASSOEXAMPLE', 39 | 'secretAccessKey': 'sso/secret/key/EXAMPLE', 40 | 'sessionToken': 'FwoGZXIvYXdzEJv//////////wEaDEXAMPLETOKEN', 41 | 'expiration': expiration 42 | } 43 | } 44 | self.send_response(200) 45 | self.send_header('Content-Type', 'application/json') 46 | self.end_headers() 47 | self.wfile.write(json.dumps(response).encode()) 48 | else: 49 | self.send_response(400) 50 | self.send_header('Content-Type', 'application/json') 51 | self.end_headers() 52 | error = {'message': 'Bad Request: Missing required parameters'} 53 | self.wfile.write(json.dumps(error).encode()) 54 | else: 55 | self.send_response(404) 56 | self.send_header('Content-Type', 'application/json') 57 | self.end_headers() 58 | error = {'message': 'Not Found'} 59 | self.wfile.write(json.dumps(error).encode()) 60 | 61 | def log_message(self, format, *args): 62 | # Log to stdout for debugging 63 | print(f"SSO Mock Server: {format % args}") 64 | 65 | 66 | if __name__ == '__main__': 67 | import sys 68 | port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 69 | server = HTTPServer(('0.0.0.0', port), SSOHandler) 70 | print(f'Mock SSO server running on port {port}') 71 | server.serve_forever() -------------------------------------------------------------------------------- /services/oracle/src/provide_credential/static_.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::Credential; 19 | use async_trait::async_trait; 20 | use reqsign_core::{Context, ProvideCredential, Result}; 21 | 22 | /// StaticCredentialProvider provides static credentials that are provided at initialization time. 23 | #[derive(Debug)] 24 | pub struct StaticCredentialProvider { 25 | credential: Credential, 26 | } 27 | 28 | impl StaticCredentialProvider { 29 | /// Create a new StaticCredentialProvider with the given credentials. 30 | pub fn new(user: &str, tenancy: &str, key_file: &str, fingerprint: &str) -> Self { 31 | Self { 32 | credential: Credential { 33 | user: user.to_string(), 34 | tenancy: tenancy.to_string(), 35 | key_file: key_file.to_string(), 36 | fingerprint: fingerprint.to_string(), 37 | expires_in: None, 38 | }, 39 | } 40 | } 41 | } 42 | 43 | #[async_trait] 44 | impl ProvideCredential for StaticCredentialProvider { 45 | type Credential = Credential; 46 | 47 | async fn provide_credential(&self, _ctx: &Context) -> Result> { 48 | Ok(Some(self.credential.clone())) 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::*; 55 | use reqsign_core::OsEnv; 56 | use reqsign_file_read_tokio::TokioFileRead; 57 | use reqsign_http_send_reqwest::ReqwestHttpSend; 58 | 59 | #[tokio::test] 60 | async fn test_static_credential_provider() -> anyhow::Result<()> { 61 | let ctx = Context::new() 62 | .with_file_read(TokioFileRead) 63 | .with_http_send(ReqwestHttpSend::default()) 64 | .with_env(OsEnv); 65 | 66 | let provider = StaticCredentialProvider::new( 67 | "test_user", 68 | "test_tenancy", 69 | "/path/to/key", 70 | "test_fingerprint", 71 | ); 72 | let cred = provider.provide_credential(&ctx).await?; 73 | assert!(cred.is_some()); 74 | let cred = cred.unwrap(); 75 | assert_eq!(cred.user, "test_user"); 76 | assert_eq!(cred.tenancy, "test_tenancy"); 77 | assert_eq!(cred.key_file, "/path/to/key"); 78 | assert_eq!(cred.fingerprint, "test_fingerprint"); 79 | assert!(cred.expires_in.is_none()); 80 | 81 | Ok(()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /services/google/tests/credential_providers/default.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context_with_env; 19 | use log::warn; 20 | use reqsign_core::{ProvideCredential, Result}; 21 | use reqsign_google::DefaultCredentialProvider; 22 | use std::collections::HashMap; 23 | use std::env; 24 | 25 | #[tokio::test] 26 | async fn test_default_credential_provider() -> Result<()> { 27 | if env::var("REQSIGN_GOOGLE_TEST_DEFAULT").unwrap_or_default() != "on" { 28 | warn!("REQSIGN_GOOGLE_TEST_DEFAULT is not set, skipped"); 29 | return Ok(()); 30 | } 31 | 32 | let cred_path = env::var("GOOGLE_APPLICATION_CREDENTIALS") 33 | .expect("GOOGLE_APPLICATION_CREDENTIALS must be set"); 34 | 35 | let ctx = create_test_context_with_env(HashMap::from_iter([( 36 | "GOOGLE_APPLICATION_CREDENTIALS".to_string(), 37 | cred_path, 38 | )])); 39 | 40 | let provider = DefaultCredentialProvider::new(); 41 | let credential = provider 42 | .provide_credential(&ctx) 43 | .await? 44 | .expect("credential must be provided"); 45 | 46 | assert!(credential.has_service_account()); 47 | let sa = credential.service_account.as_ref().unwrap(); 48 | assert!(!sa.client_email.is_empty()); 49 | assert!(!sa.private_key.is_empty()); 50 | 51 | Ok(()) 52 | } 53 | 54 | #[tokio::test] 55 | async fn test_default_credential_provider_with_json_file() -> Result<()> { 56 | if env::var("REQSIGN_GOOGLE_TEST_DEFAULT").unwrap_or_default() != "on" { 57 | warn!("REQSIGN_GOOGLE_TEST_DEFAULT is not set, skipped"); 58 | return Ok(()); 59 | } 60 | 61 | // Test with test data file 62 | let test_file_path = format!( 63 | "{}/services/google/testdata/test_credential.json", 64 | env::current_dir()?.to_string_lossy() 65 | ); 66 | 67 | let ctx = create_test_context_with_env(HashMap::from_iter([( 68 | "GOOGLE_APPLICATION_CREDENTIALS".to_string(), 69 | test_file_path, 70 | )])); 71 | 72 | let provider = DefaultCredentialProvider::new(); 73 | let credential = provider 74 | .provide_credential(&ctx) 75 | .await? 76 | .expect("credential must be provided"); 77 | 78 | assert!(credential.has_service_account()); 79 | let sa = credential.service_account.as_ref().unwrap(); 80 | assert_eq!(sa.client_email, "test-234@test.iam.gserviceaccount.com"); 81 | 82 | Ok(()) 83 | } 84 | -------------------------------------------------------------------------------- /context/http-send-reqwest/README.md: -------------------------------------------------------------------------------- 1 | # reqsign-http-send-reqwest 2 | 3 | Reqwest-based HTTP client implementation for reqsign. 4 | 5 | --- 6 | 7 | This crate provides `ReqwestHttpSend`, an HTTP client that implements the `HttpSend` trait from `reqsign_core` using the popular reqwest library. 8 | 9 | ## Quick Start 10 | 11 | ```rust 12 | use reqsign_core::Context; 13 | use reqsign_http_send_reqwest::ReqwestHttpSend; 14 | 15 | // Use with default configuration 16 | let ctx = Context::new( 17 | file_reader, 18 | ReqwestHttpSend::default(), 19 | ); 20 | 21 | // Or with custom client configuration 22 | let client = reqwest::Client::builder() 23 | .timeout(std::time::Duration::from_secs(30)) 24 | .build() 25 | .unwrap(); 26 | 27 | let ctx = Context::new( 28 | file_reader, 29 | ReqwestHttpSend::new(client), 30 | ); 31 | ``` 32 | 33 | ## Features 34 | 35 | - **Full reqwest compatibility**: Use all of reqwest's powerful features 36 | - **Seamless integration**: Automatic conversion between `http` and `reqwest` types 37 | - **Customizable**: Configure timeouts, proxies, TLS settings, and more 38 | - **Async/await**: Built for modern async Rust applications 39 | 40 | ## Configuration Options 41 | 42 | ```rust 43 | use reqwest::Client; 44 | use reqsign_http_send_reqwest::ReqwestHttpSend; 45 | 46 | let client = Client::builder() 47 | // Timeouts 48 | .timeout(Duration::from_secs(30)) 49 | .connect_timeout(Duration::from_secs(10)) 50 | 51 | // Connection pooling 52 | .pool_max_idle_per_host(10) 53 | .pool_idle_timeout(Duration::from_secs(90)) 54 | 55 | // HTTP settings 56 | .user_agent("my-app/1.0") 57 | .default_headers(headers) 58 | 59 | // Proxy configuration 60 | .proxy(reqwest::Proxy::https("https://proxy.example.com")?) 61 | 62 | // TLS configuration 63 | .danger_accept_invalid_certs(false) 64 | .min_tls_version(reqwest::tls::Version::TLS_1_2) 65 | 66 | .build()?; 67 | 68 | let http_send = ReqwestHttpSend::new(client); 69 | ``` 70 | 71 | ## Examples 72 | 73 | ### Custom Client Configuration 74 | 75 | Check out the [custom_client example](examples/custom_client.rs) to see various configuration options: 76 | 77 | ```bash 78 | cargo run --example custom_client 79 | ``` 80 | 81 | ### Integration with Services 82 | 83 | ```rust 84 | use reqsign_core::{Context, Signer}; 85 | use reqsign_file_read_tokio::TokioFileRead; 86 | use reqsign_http_send_reqwest::ReqwestHttpSend; 87 | 88 | // Create context for cloud service clients 89 | let ctx = Context::new( 90 | TokioFileRead::default(), 91 | ReqwestHttpSend::default(), 92 | ); 93 | 94 | // Use with any reqsign service 95 | let signer = Signer::new(ctx, loader, builder); 96 | ``` 97 | 98 | ## Why reqwest? 99 | 100 | - **Mature and stable**: One of the most popular HTTP clients in the Rust ecosystem 101 | - **Feature-rich**: Supports proxies, cookies, redirect policies, and more 102 | - **Well-maintained**: Regular updates and security patches 103 | - **Extensive ecosystem**: Compatible with many Rust libraries and frameworks 104 | 105 | ## License 106 | 107 | Licensed under [Apache License, Version 2.0](./LICENSE). -------------------------------------------------------------------------------- /services/google/tests/credential_providers/static_provider.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::create_test_context; 19 | use log::warn; 20 | use reqsign_core::{ProvideCredential, Result}; 21 | use reqsign_google::StaticCredentialProvider; 22 | use std::env; 23 | 24 | #[tokio::test] 25 | async fn test_static_credential_provider() -> Result<()> { 26 | if env::var("REQSIGN_GOOGLE_TEST_STATIC").unwrap_or_default() != "on" { 27 | warn!("REQSIGN_GOOGLE_TEST_STATIC is not set, skipped"); 28 | return Ok(()); 29 | } 30 | 31 | let credential_content = 32 | env::var("REQSIGN_GOOGLE_CREDENTIAL").expect("REQSIGN_GOOGLE_CREDENTIAL must be set"); 33 | 34 | let ctx = create_test_context(); 35 | 36 | let provider = StaticCredentialProvider::from_base64(credential_content) 37 | .expect("credential must be valid base64"); 38 | let credential = provider 39 | .provide_credential(&ctx) 40 | .await? 41 | .expect("credential must be provided"); 42 | 43 | assert!(credential.has_service_account()); 44 | let sa = credential.service_account.as_ref().unwrap(); 45 | assert!(!sa.client_email.is_empty()); 46 | assert!(!sa.private_key.is_empty()); 47 | 48 | Ok(()) 49 | } 50 | 51 | #[tokio::test] 52 | async fn test_static_credential_provider_with_scope() -> Result<()> { 53 | if env::var("REQSIGN_GOOGLE_TEST_STATIC").unwrap_or_default() != "on" { 54 | warn!("REQSIGN_GOOGLE_TEST_STATIC is not set, skipped"); 55 | return Ok(()); 56 | } 57 | 58 | let credential_content = 59 | env::var("REQSIGN_GOOGLE_CREDENTIAL").expect("REQSIGN_GOOGLE_CREDENTIAL must be set"); 60 | let scope = env::var("REQSIGN_GOOGLE_CLOUD_STORAGE_SCOPE") 61 | .unwrap_or_else(|_| "https://www.googleapis.com/auth/devstorage.read_write".to_string()); 62 | 63 | let ctx = create_test_context(); 64 | 65 | let provider = StaticCredentialProvider::from_base64(credential_content) 66 | .expect("credential must be valid base64") 67 | .with_scope(&scope); 68 | let credential = provider 69 | .provide_credential(&ctx) 70 | .await? 71 | .expect("credential must be provided"); 72 | 73 | assert!(credential.has_service_account()); 74 | // When a scope is provided, the provider may also fetch a token 75 | if credential.has_token() { 76 | assert!(credential.has_valid_token()); 77 | } 78 | 79 | Ok(()) 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/tencent_cos.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Tencent COS Test 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | pull_request: 25 | branches: 26 | - main 27 | 28 | concurrency: 29 | group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | unit_test: 34 | runs-on: ubuntu-latest 35 | permissions: 36 | id-token: write 37 | steps: 38 | - uses: actions/checkout@v6 39 | - name: Test 40 | working-directory: ./services/tencent-cos 41 | run: cargo test --no-fail-fast 42 | env: 43 | RUST_LOG: DEBUG 44 | RUST_BACKTRACE: full 45 | # Tencent COS Test 46 | REQSIGN_TENCENT_COS_TEST: ${{ secrets.REQSIGN_TENCENT_COS_TEST }} 47 | REQSIGN_TENCENT_COS_ACCESS_KEY: ${{ secrets.REQSIGN_TENCENT_COS_ACCESS_KEY }} 48 | REQSIGN_TENCENT_COS_SECRET_KEY: ${{ secrets.REQSIGN_TENCENT_COS_SECRET_KEY }} 49 | REQSIGN_TENCENT_COS_URL: ${{ secrets.REQSIGN_TENCENT_COS_URL }} 50 | 51 | tencent_cloud_web_identify_test: 52 | runs-on: ubuntu-22.04 53 | permissions: 54 | contents: "read" 55 | id-token: "write" 56 | if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork 57 | steps: 58 | - uses: actions/checkout@v6 59 | 60 | - name: Get Id Token 61 | uses: actions/github-script@v8 62 | id: idtoken 63 | with: 64 | script: | 65 | let id_token = await core.getIDToken('sts.tencentcloudapi.com') 66 | core.exportVariable('GITHUB_ID_TOKEN', id_token) 67 | core.setSecret(id_token) 68 | 69 | - name: Test 70 | working-directory: ./services/tencent-cos 71 | run: cargo test --no-fail-fast 72 | env: 73 | RUST_LOG: DEBUG 74 | RUST_BACKTRACE: full 75 | REQSIGN_TENCENT_COS_TEST: ${{ secrets.REQSIGN_TENCENT_COS_TEST }} 76 | REQSIGN_TENCENT_COS_ACCESS_KEY: ${{ secrets.REQSIGN_TENCENT_COS_ACCESS_KEY }} 77 | REQSIGN_TENCENT_COS_SECRET_KEY: ${{ secrets.REQSIGN_TENCENT_COS_SECRET_KEY }} 78 | REQSIGN_TENCENT_COS_URL: ${{ secrets.REQSIGN_TENCENT_COS_URL }} 79 | REQSIGN_TENCENT_COS_ROLE_ARN: ${{ secrets.REQSIGN_TENCENT_COS_ROLE_ARN }} 80 | REQSIGN_TENCENT_COS_PROVIDER_ID: ${{ secrets.REQSIGN_TENCENT_COS_PROVIDER_ID }} 81 | REQSIGN_TENCENT_COS_REGION: ${{ secrets.REQSIGN_TENCENT_COS_REGION }} 82 | -------------------------------------------------------------------------------- /reqsign/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "reqsign" 20 | version = "0.18.1" 21 | 22 | categories = ["authentication", "web-programming::http-client"] 23 | description = "Signing HTTP requests for AWS, Azure, Google, Huawei, Aliyun, Tencent and Oracle services" 24 | documentation = "https://docs.rs/reqsign" 25 | keywords = ["http", "requests", "signing", "aws", "azure"] 26 | 27 | edition.workspace = true 28 | license.workspace = true 29 | repository.workspace = true 30 | rust-version.workspace = true 31 | 32 | [package.metadata.docs.rs] 33 | all-features = true 34 | 35 | [dependencies] 36 | # Core functionality (always included) 37 | reqsign-core = { workspace = true } 38 | 39 | # Service implementations (optional) 40 | reqsign-aliyun-oss = { workspace = true, optional = true } 41 | reqsign-aws-v4 = { workspace = true, optional = true } 42 | reqsign-azure-storage = { workspace = true, optional = true } 43 | reqsign-google = { workspace = true, optional = true } 44 | reqsign-huaweicloud-obs = { workspace = true, optional = true } 45 | reqsign-oracle = { workspace = true, optional = true } 46 | reqsign-tencent-cos = { workspace = true, optional = true } 47 | 48 | # Context implementations (optional but included by default) 49 | reqsign-command-execute-tokio = { workspace = true, optional = true } 50 | reqsign-file-read-tokio = { workspace = true, optional = true } 51 | reqsign-http-send-reqwest = { workspace = true, optional = true } 52 | 53 | [features] 54 | default = ["default-context"] 55 | default-context = [ 56 | "dep:reqsign-command-execute-tokio", 57 | "dep:reqsign-file-read-tokio", 58 | "dep:reqsign-http-send-reqwest", 59 | ] 60 | 61 | # Service features 62 | aliyun = ["dep:reqsign-aliyun-oss"] 63 | aws = ["dep:reqsign-aws-v4"] 64 | azure = ["dep:reqsign-azure-storage"] 65 | google = ["dep:reqsign-google"] 66 | huaweicloud = ["dep:reqsign-huaweicloud-obs"] 67 | oracle = ["dep:reqsign-oracle"] 68 | tencent = ["dep:reqsign-tencent-cos"] 69 | 70 | # Full feature set 71 | full = ["aliyun", "aws", "azure", "google", "huaweicloud", "oracle", "tencent"] 72 | 73 | [dev-dependencies] 74 | anyhow = "1" 75 | bytes = "1" 76 | env_logger = "0.11" 77 | http = "1" 78 | tokio = { version = "1", features = ["full"] } 79 | 80 | [[example]] 81 | name = "aws" 82 | required-features = ["aws", "default-context"] 83 | 84 | [[example]] 85 | name = "azure" 86 | required-features = ["azure", "default-context"] 87 | 88 | [[example]] 89 | name = "google" 90 | required-features = ["google", "default-context"] 91 | -------------------------------------------------------------------------------- /services/aws-v4/tests/signing/special_chars.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use super::{init_signing_test, load_static_credential, send_signed_request}; 19 | use anyhow::Result; 20 | use http::{Method, Request, StatusCode}; 21 | use log::warn; 22 | use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; 23 | use std::str::FromStr; 24 | 25 | #[tokio::test] 26 | async fn test_head_object_with_special_characters() -> Result<()> { 27 | let Some((ctx, signer, url)) = init_signing_test() else { 28 | warn!("REQSIGN_AWS_V4_TEST is not set, skipped"); 29 | return Ok(()); 30 | }; 31 | 32 | let cred = load_static_credential()?; 33 | 34 | let mut req = Request::new(String::new()); 35 | *req.method_mut() = Method::HEAD; 36 | *req.uri_mut() = http::Uri::from_str(&format!( 37 | "{}/{}", 38 | url, 39 | utf8_percent_encode("!@#$%^&*()_+-=;:'><,/?.txt", NON_ALPHANUMERIC) 40 | ))?; 41 | 42 | let (status, _body) = send_signed_request(&ctx, &signer, req, &cred).await?; 43 | assert_eq!(StatusCode::NOT_FOUND, status); 44 | Ok(()) 45 | } 46 | 47 | #[tokio::test] 48 | async fn test_head_object_with_encoded_characters() -> Result<()> { 49 | let Some((ctx, signer, url)) = init_signing_test() else { 50 | warn!("REQSIGN_AWS_V4_TEST is not set, skipped"); 51 | return Ok(()); 52 | }; 53 | 54 | let cred = load_static_credential()?; 55 | 56 | let mut req = Request::new(String::new()); 57 | *req.method_mut() = Method::HEAD; 58 | *req.uri_mut() = http::Uri::from_str(&format!( 59 | "{}/{}", 60 | url, 61 | utf8_percent_encode("test file with spaces.txt", NON_ALPHANUMERIC) 62 | ))?; 63 | 64 | let (status, _body) = send_signed_request(&ctx, &signer, req, &cred).await?; 65 | assert_eq!(StatusCode::NOT_FOUND, status); 66 | Ok(()) 67 | } 68 | 69 | #[tokio::test] 70 | async fn test_object_with_unicode_characters() -> Result<()> { 71 | let Some((ctx, signer, url)) = init_signing_test() else { 72 | warn!("REQSIGN_AWS_V4_TEST is not set, skipped"); 73 | return Ok(()); 74 | }; 75 | 76 | let cred = load_static_credential()?; 77 | 78 | let mut req = Request::new(String::new()); 79 | *req.method_mut() = Method::HEAD; 80 | *req.uri_mut() = http::Uri::from_str(&format!( 81 | "{}/{}", 82 | url, 83 | utf8_percent_encode("文件名.txt", NON_ALPHANUMERIC) 84 | ))?; 85 | 86 | let (status, _body) = send_signed_request(&ctx, &signer, req, &cred).await?; 87 | assert_eq!(StatusCode::NOT_FOUND, status); 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /services/aws-v4/tests/mocks/README.md: -------------------------------------------------------------------------------- 1 | # AWS V4 Mock Servers 2 | 3 | This directory contains mock servers used for testing AWS credential providers in CI/CD environments. 4 | 5 | ## Available Mock Servers 6 | 7 | ### ECS Mock Server (`ecs_mock_server.py`) 8 | Simulates the ECS Task IAM Roles endpoint for testing `ECSCredentialProvider`. 9 | 10 | **Usage:** 11 | ```bash 12 | python3 ecs_mock_server.py [port] 13 | # Default port: 51679 14 | ``` 15 | 16 | **Endpoints:** 17 | - `/creds` - Returns mock IAM credentials 18 | 19 | ### SSO Mock Server (`sso_mock_server.py`) 20 | Simulates the AWS SSO (IAM Identity Center) API for testing `SSOCredentialProvider`. 21 | 22 | **Usage:** 23 | ```bash 24 | python3 sso_mock_server.py [port] 25 | # Default port: 8080 26 | ``` 27 | 28 | **Endpoints:** 29 | - `/federation/credentials` - Returns mock role credentials 30 | - Requires header: `x-amz-sso_bearer_token: test-access-token-for-sso` 31 | - Query params: `role_name` and `account_id` 32 | 33 | ### IMDS Mock Server (`imds_mock_server.py`) 34 | Simulates the EC2 Instance Metadata Service (IMDSv2) for testing `IMDSv2CredentialProvider`. 35 | 36 | **Usage:** 37 | ```bash 38 | python3 imds_mock_server.py [port] 39 | # Default port: 1338 40 | ``` 41 | 42 | **Endpoints:** 43 | - `PUT /latest/api/token` - Get IMDSv2 session token 44 | - `GET /latest/meta-data/iam/security-credentials/` - List available roles 45 | - `GET /latest/meta-data/iam/security-credentials/{role}` - Get role credentials 46 | 47 | **Note:** The IMDS test in CI currently uses the official `amazon-ec2-metadata-mock` tool instead of this Python mock. 48 | 49 | ### Credential Process Helper (`credential_process_helper.py`) 50 | A Python script that outputs credentials in the format expected by AWS `credential_process`. 51 | 52 | **Usage:** 53 | ```bash 54 | python3 credential_process_helper.py [--profile ] 55 | ``` 56 | 57 | **Output:** 58 | Returns JSON with `Version`, `AccessKeyId`, `SecretAccessKey`, `SessionToken`, and `Expiration`. 59 | 60 | **Example AWS Config:** 61 | ```ini 62 | [default] 63 | credential_process = python3 /path/to/credential_process_helper.py 64 | 65 | [profile custom] 66 | credential_process = python3 /path/to/credential_process_helper.py --profile test 67 | ``` 68 | 69 | ### Cognito Identity Mock Server (`cognito_mock_server.py`) 70 | Simulates the Amazon Cognito Identity service for testing `CognitoIdentityCredentialProvider`. 71 | 72 | **Usage:** 73 | ```bash 74 | python3 cognito_mock_server.py [port] 75 | # Default port: 8443 76 | ``` 77 | 78 | **Endpoints:** 79 | - `POST /` with `x-amz-target: AWSCognitoIdentityService.GetId` - Get or create identity ID 80 | - `POST /` with `x-amz-target: AWSCognitoIdentityService.GetCredentialsForIdentity` - Get credentials for identity 81 | 82 | **Features:** 83 | - Supports both authenticated (with logins) and unauthenticated identities 84 | - Returns different credentials based on authentication state 85 | - Generates unique identity IDs for each request 86 | 87 | ## Testing 88 | 89 | These mock servers and helpers are automatically used in GitHub Actions workflows. See `.github/workflows/aws_v4.yml` for usage examples. 90 | 91 | ## Development 92 | 93 | When adding new mock servers: 94 | 1. Create a new Python file following the existing pattern 95 | 2. Include clear documentation in the file header 96 | 3. Support configurable port via command-line argument 97 | 4. Update this README with the new server information -------------------------------------------------------------------------------- /services/azure-storage/tests/credential_providers/client_certificate.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #[cfg(not(target_arch = "wasm32"))] 19 | use reqsign_azure_storage::{ClientCertificateCredentialProvider, Credential}; 20 | #[cfg(not(target_arch = "wasm32"))] 21 | use reqsign_core::{Context, OsEnv, ProvideCredential}; 22 | #[cfg(not(target_arch = "wasm32"))] 23 | use reqsign_file_read_tokio::TokioFileRead; 24 | #[cfg(not(target_arch = "wasm32"))] 25 | use reqsign_http_send_reqwest::ReqwestHttpSend; 26 | 27 | #[cfg(not(target_arch = "wasm32"))] 28 | fn is_test_enabled() -> bool { 29 | std::env::var("REQSIGN_AZURE_STORAGE_TEST_CLIENT_CERTIFICATE").unwrap_or_default() == "on" 30 | } 31 | 32 | #[cfg(not(target_arch = "wasm32"))] 33 | #[tokio::test] 34 | async fn test_client_certificate_provider() { 35 | if !is_test_enabled() { 36 | eprintln!("Skipping test: REQSIGN_AZURE_STORAGE_TEST_CLIENT_CERTIFICATE is not enabled"); 37 | return; 38 | } 39 | 40 | let _tenant_id = std::env::var("AZURE_TENANT_ID") 41 | .expect("AZURE_TENANT_ID must be set for client certificate test"); 42 | let _client_id = std::env::var("AZURE_CLIENT_ID") 43 | .expect("AZURE_CLIENT_ID must be set for client certificate test"); 44 | let _cert_path = std::env::var("AZURE_CLIENT_CERTIFICATE_PATH") 45 | .expect("AZURE_CLIENT_CERTIFICATE_PATH must be set for client certificate test"); 46 | let _cert_password = std::env::var("AZURE_CLIENT_CERTIFICATE_PASSWORD").ok(); 47 | 48 | let ctx = Context::new() 49 | .with_file_read(TokioFileRead) 50 | .with_http_send(ReqwestHttpSend::default()) 51 | .with_env(OsEnv); 52 | 53 | let loader = ClientCertificateCredentialProvider::new(); 54 | 55 | let result = loader.provide_credential(&ctx).await; 56 | 57 | // Better error reporting 58 | let cred = match result { 59 | Ok(Some(cred)) => cred, 60 | Ok(None) => panic!("Client certificate provider returned None when test is enabled"), 61 | Err(e) => { 62 | panic!("Client certificate provider failed with error: {e:?}\nError details: {e}") 63 | } 64 | }; 65 | 66 | match cred { 67 | Credential::BearerToken { 68 | token, 69 | expires_in: _, 70 | } => { 71 | assert!(!token.is_empty()); 72 | // Token should be a valid JWT 73 | assert!(token.starts_with("eyJ")); 74 | eprintln!("Successfully obtained bearer token using client certificate"); 75 | } 76 | _ => panic!("Expected BearerToken credential from client certificate provider"), 77 | } 78 | } 79 | --------------------------------------------------------------------------------