diff --git a/sdk/security_keyvault/Cargo.toml b/sdk/security_keyvault/Cargo.toml index e9d8eb61ce8..8b02e942153 100644 --- a/sdk/security_keyvault/Cargo.toml +++ b/sdk/security_keyvault/Cargo.toml @@ -13,15 +13,15 @@ license = "MIT" edition = "2021" [dependencies] +async-trait = "0.1" futures = "0.3" base64 = "0.13" -reqwest = { version = "0.11", features = ["json"] } -time = "0.3.10" +time = "0.3" const_format = "0.2.13" serde_json = "1.0" url = "2.2" serde = { version = "1.0", features = ["derive"] } -azure_core = { path = "../core", version = "0.4"} +azure_core = { path = "../core", version = "0.4", no-default-features = true } [dev-dependencies] azure_identity = { path = "../identity", default_features = false } @@ -30,4 +30,6 @@ async-trait = "0.1" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } [features] -default = ["azure_core/enable_reqwest"] +default = ["enable_reqwest"] +enable_reqwest = ["azure_core/enable_reqwest"] +enable_reqwest_rustls = ["azure_core/enable_reqwest_rustls"] \ No newline at end of file diff --git a/sdk/security_keyvault/examples/backup_secret.rs b/sdk/security_keyvault/examples/backup_secret.rs index 224f7a41ee5..dd43707f64f 100644 --- a/sdk/security_keyvault/examples/backup_secret.rs +++ b/sdk/security_keyvault/examples/backup_secret.rs @@ -1,25 +1,19 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; let backup_response = client.backup(secret_name).into_future().await?; dbg!(&backup_response); diff --git a/sdk/security_keyvault/examples/delete_secret.rs b/sdk/security_keyvault/examples/delete_secret.rs index 54cfdfb567c..c134781bee0 100644 --- a/sdk/security_keyvault/examples/delete_secret.rs +++ b/sdk/security_keyvault/examples/delete_secret.rs @@ -1,25 +1,19 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; client.delete(secret_name).into_future().await?; diff --git a/sdk/security_keyvault/examples/get_secret.rs b/sdk/security_keyvault/examples/get_secret.rs index b5b33478e0e..f96bc5551f1 100644 --- a/sdk/security_keyvault/examples/get_secret.rs +++ b/sdk/security_keyvault/examples/get_secret.rs @@ -1,26 +1,27 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; +use futures::StreamExt; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); - let creds = std::sync::Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; + let mut versions = client.get_versions(&secret_name).into_stream(); + while let Some(version) = versions.next().await { + println!("{:?}", version?); + } + let secret = client.get(secret_name).into_future().await?; dbg!(secret.value); diff --git a/sdk/security_keyvault/examples/get_secret_versions.rs b/sdk/security_keyvault/examples/get_secret_versions.rs index 0774040743f..567fef2c9b3 100644 --- a/sdk/security_keyvault/examples/get_secret_versions.rs +++ b/sdk/security_keyvault/examples/get_secret_versions.rs @@ -1,28 +1,27 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use futures::StreamExt; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; - let secrets = client.get_versions(secret_name).into_future().await?; + let mut secrets = client.get_versions(secret_name).into_stream(); + while let Some(secret) = secrets.next().await { + println!("{:?}", secret?); + } + dbg!(&secrets); Ok(()) diff --git a/sdk/security_keyvault/examples/list_secrets.rs b/sdk/security_keyvault/examples/list_secrets.rs index 4a11f0f51c6..fb08979816d 100644 --- a/sdk/security_keyvault/examples/list_secrets.rs +++ b/sdk/security_keyvault/examples/list_secrets.rs @@ -1,28 +1,25 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use futures::stream::StreamExt; +use std::{env, sync::Arc}; #[tokio::main] -async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); +async fn main() -> azure_core::Result<()> { let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; - let secrets = client.list_secrets().into_future().await?; - dbg!(&secrets); + let mut stream = client.list_secrets().into_stream(); + while let Some(response) = stream.next().await { + dbg!(&response?); + } Ok(()) } diff --git a/sdk/security_keyvault/examples/pass_client.rs b/sdk/security_keyvault/examples/pass_client.rs index 331ecea9254..ce0753c014e 100644 --- a/sdk/security_keyvault/examples/pass_client.rs +++ b/sdk/security_keyvault/examples/pass_client.rs @@ -1,24 +1,18 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; get_secret(&client).await?; diff --git a/sdk/security_keyvault/examples/restore_secret.rs b/sdk/security_keyvault/examples/restore_secret.rs index 8f7c1fda073..d181f7a8f76 100644 --- a/sdk/security_keyvault/examples/restore_secret.rs +++ b/sdk/security_keyvault/examples/restore_secret.rs @@ -1,25 +1,19 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let backup_blob = env::var("BACKUP_BLOB").expect("Missing BACKUP_BLOB environment variable."); - let creds = Arc::new(ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), - )); + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), + ); + let client = SecretClient::new(&keyvault_url, creds)?; client.restore_secret(&backup_blob).into_future().await?; diff --git a/sdk/security_keyvault/examples/set_secret.rs b/sdk/security_keyvault/examples/set_secret.rs index 11139cafac9..2c02f4a821f 100644 --- a/sdk/security_keyvault/examples/set_secret.rs +++ b/sdk/security_keyvault/examples/set_secret.rs @@ -1,28 +1,22 @@ -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); let secret_value = env::var("SECRET_VALUE").expect("Missing SECRET_VALUE environment variable."); - let creds = ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), ); - let client = SecretClient::new(&keyvault_url, Arc::new(creds))?; + + let client = SecretClient::new(&keyvault_url, creds)?; client.set(&secret_name, secret_value).into_future().await?; diff --git a/sdk/security_keyvault/examples/update_secret.rs b/sdk/security_keyvault/examples/update_secret.rs index 47269a5c3c8..7097bba36f6 100644 --- a/sdk/security_keyvault/examples/update_secret.rs +++ b/sdk/security_keyvault/examples/update_secret.rs @@ -1,30 +1,24 @@ use azure_core::date; -use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; +use azure_identity::DefaultAzureCredentialBuilder; use azure_security_keyvault::SecretClient; -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; use time::OffsetDateTime; #[tokio::main] async fn main() -> Result<(), Box> { - let client_id = env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."); - let client_secret = - env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable."); - let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable."); let keyvault_url = env::var("KEYVAULT_URL").expect("Missing KEYVAULT_URL environment variable."); let secret_name = env::var("SECRET_NAME").expect("Missing SECRET_NAME environment variable."); let secret_version = env::var("SECRET_VERSION").expect("Missing SECRET_VERSION environment variable."); - let creds = ClientSecretCredential::new( - azure_core::new_http_client(), - tenant_id, - client_id, - client_secret, - TokenCredentialOptions::default(), + let creds = Arc::new( + DefaultAzureCredentialBuilder::new() + .exclude_managed_identity_credential() + .build(), ); - let client = SecretClient::new(&keyvault_url, Arc::new(creds))?; + + let client = SecretClient::new(&keyvault_url, creds)?; // Disable secret. client diff --git a/sdk/security_keyvault/src/account/operations/list_certificates.rs b/sdk/security_keyvault/src/account/operations/list_certificates.rs index 6c1ced45570..2874c718b2e 100644 --- a/sdk/security_keyvault/src/account/operations/list_certificates.rs +++ b/sdk/security_keyvault/src/account/operations/list_certificates.rs @@ -1,55 +1,59 @@ use crate::prelude::*; +use azure_core::{ + error::Error, headers::Headers, CollectedResponse, Continuable, Method, Pageable, +}; use url::Url; operation! { + #[stream] ListCertificates, client: CertificateClient, } impl ListCertificatesBuilder { - pub fn into_future(mut self) -> ListCertificates { - Box::pin(async move { - let mut certificates = Vec::::new(); + pub fn into_stream(self) -> Pageable { + let make_request = move |continuation: Option| { + let this = self.clone(); + let mut ctx = self.context.clone(); + async move { + let mut uri = this.client.keyvault_client.vault_url.clone(); + uri.set_path("certificates"); + + if let Some(continuation) = continuation { + uri = Url::parse(&continuation)?; + } - let mut uri = self.client.client.vault_url.clone(); - uri.set_path("certificates"); - uri.set_query(Some(API_VERSION_PARAM)); + let headers = Headers::new(); + let mut request = this.client.keyvault_client.finalize_request( + uri, + Method::Get, + headers, + None, + )?; - loop { - let resp_body = self - .client + let response = this .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut ctx, &mut request) .await?; - let response = - serde_json::from_str::(&resp_body).unwrap(); - - certificates.extend( - response - .value - .into_iter() - .map(|s| CertificateProperties { - id: s.id.to_owned(), - name: s.id.split('/').collect::>()[4].to_string(), - version: s.id.split('/').collect::>()[5].to_string(), - enabled: s.attributes.enabled, - created_on: s.attributes.created, - updated_on: s.attributes.updated, - not_before: s.attributes.nbf, - expires_on: s.attributes.exp, - }) - .collect::>(), - ); - - match response.next_link { - None => break, - Some(u) => uri = Url::parse(&u).unwrap(), - } - } - Ok(certificates) - }) + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let response = serde_json::from_slice::(body)?; + Ok(response) + } + }; + Pageable::new(make_request) } } -type ListCertificatesResponse = Vec; +type ListCertificatesResponse = KeyVaultGetCertificatesResponse; + +impl Continuable for ListCertificatesResponse { + type Continuation = String; + + fn continuation(&self) -> Option { + self.next_link.clone() + } +} diff --git a/sdk/security_keyvault/src/account/operations/list_secrets.rs b/sdk/security_keyvault/src/account/operations/list_secrets.rs index 7f8b323291b..c3e5823a778 100644 --- a/sdk/security_keyvault/src/account/operations/list_secrets.rs +++ b/sdk/security_keyvault/src/account/operations/list_secrets.rs @@ -1,56 +1,59 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{ + error::Error, headers::Headers, CollectedResponse, Continuable, Method, Pageable, +}; use url::Url; operation! { + #[stream] ListSecrets, client: SecretClient, } impl ListSecretsBuilder { - pub fn into_future(mut self) -> ListSecrets { - Box::pin(async move { - let mut secrets = Vec::::new(); + pub fn into_stream(self) -> Pageable { + let make_request = move |continuation: Option| { + let this = self.clone(); + let mut ctx = self.context.clone(); + async move { + let mut uri = this.client.keyvault_client.vault_url.clone(); + uri.set_path("secrets"); + + if let Some(continuation) = continuation { + uri = Url::parse(&continuation)?; + } - let mut uri = self.client.client.vault_url.clone(); - uri.set_path("secrets"); + let headers = Headers::new(); + let mut request = this.client.keyvault_client.finalize_request( + uri, + Method::Get, + headers, + None, + )?; - loop { - let resp_body = self - .client + let response = this .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut ctx, &mut request) .await?; - let response = serde_json::from_str::(&resp_body) - .with_context(ErrorKind::DataConversion, || { - format!( - "failed to parse KeyVaultGetSecretsResponse. resp_body: {resp_body}" - ) - })?; - - secrets.extend( - response - .value - .into_iter() - .map(|s| KeyVaultSecretBaseIdentifier { - id: s.id.clone(), - name: s.id.split('/').last().unwrap().to_owned(), - enabled: s.attributes.enabled, - created_on: s.attributes.created, - updated_on: s.attributes.updated, - }) - .collect::>(), - ); - - match response.next_link { - None => break, - Some(u) => uri = Url::parse(&u).unwrap(), - } - } - Ok(secrets) - }) + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let response = serde_json::from_slice::(body)?; + Ok(response) + } + }; + Pageable::new(make_request) } } -type ListSecretsResponse = Vec; +type ListSecretsResponse = KeyVaultGetSecretsResponse; + +impl Continuable for ListSecretsResponse { + type Continuation = String; + + fn continuation(&self) -> Option { + self.next_link.clone() + } +} diff --git a/sdk/security_keyvault/src/account/operations/restore_certificate.rs b/sdk/security_keyvault/src/account/operations/restore_certificate.rs index e47599e7ffc..3ed216fb4fd 100644 --- a/sdk/security_keyvault/src/account/operations/restore_certificate.rs +++ b/sdk/security_keyvault/src/account/operations/restore_certificate.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, Method}; operation! { RestoreCertificate, @@ -9,20 +10,23 @@ operation! { impl RestoreCertificateBuilder { pub fn into_future(mut self) -> RestoreCertificate { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path("certificates/restore"); - uri.set_query(Some(API_VERSION_PARAM)); let mut request_body = serde_json::Map::new(); request_body.insert("value".to_owned(), self.backup_blob.into()); + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Post, + headers, + Some(serde_json::Value::Object(request_body).to_string().into()), + )?; + self.client - .client - .request( - reqwest::Method::POST, - uri.to_string(), - Some(serde_json::Value::Object(request_body).to_string()), - ) + .keyvault_client + .send(&mut self.context, &mut request) .await?; Ok(()) diff --git a/sdk/security_keyvault/src/account/operations/restore_secret.rs b/sdk/security_keyvault/src/account/operations/restore_secret.rs index 7317b42d976..2d762025503 100644 --- a/sdk/security_keyvault/src/account/operations/restore_secret.rs +++ b/sdk/security_keyvault/src/account/operations/restore_secret.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, Method}; operation! { RestoreSecret, @@ -9,20 +10,23 @@ operation! { impl RestoreSecretBuilder { pub fn into_future(mut self) -> RestoreSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path("secrets/restore"); - uri.set_query(Some(API_VERSION_PARAM)); let mut request_body = serde_json::Map::new(); request_body.insert("value".to_owned(), self.backup_blob.into()); + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Post, + headers, + Some(serde_json::Value::Object(request_body).to_string().into()), + )?; + self.client - .client - .request( - reqwest::Method::POST, - uri.to_string(), - Some(serde_json::Value::Object(request_body).to_string()), - ) + .keyvault_client + .send(&mut self.context, &mut request) .await?; Ok(()) diff --git a/sdk/security_keyvault/src/certificates/models.rs b/sdk/security_keyvault/src/certificates/models.rs index 975c79bf735..055e04167d6 100644 --- a/sdk/security_keyvault/src/certificates/models.rs +++ b/sdk/security_keyvault/src/certificates/models.rs @@ -2,37 +2,39 @@ use serde::Deserialize; use time::OffsetDateTime; #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultCertificateBaseIdentifierAttributedRaw { +pub struct KeyVaultCertificateBaseIdentifierAttributes { pub enabled: bool, - #[serde(default, with = "azure_core::date::timestamp::option")] - pub exp: Option, - #[serde(default, with = "azure_core::date::timestamp::option")] - pub nbf: Option, - #[serde(with = "azure_core::date::timestamp")] - pub created: OffsetDateTime, - #[serde(with = "azure_core::date::timestamp")] - pub updated: OffsetDateTime, + #[serde(default, with = "azure_core::date::timestamp::option", rename = "exp")] + pub expires_on: Option, + #[serde(default, with = "azure_core::date::timestamp::option", rename = "nbf")] + pub not_before: Option, + #[serde(with = "azure_core::date::timestamp", rename = "created")] + pub created_on: OffsetDateTime, + #[serde(with = "azure_core::date::timestamp", rename = "updated")] + pub updated_on: OffsetDateTime, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultCertificateBaseIdentifierRaw { +pub struct KeyVaultCertificateBaseIdentifier { pub id: String, #[allow(unused)] pub x5t: String, - pub attributes: KeyVaultCertificateBaseIdentifierAttributedRaw, + pub attributes: KeyVaultCertificateBaseIdentifierAttributes, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetCertificatesResponse { - pub value: Vec, +pub struct KeyVaultGetCertificatesResponse { + pub value: Vec, #[serde(rename = "nextLink")] pub next_link: Option, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetCertificateResponse { - pub kid: String, - pub sid: String, +pub struct KeyVaultGetCertificateResponse { + #[serde(rename = "kid")] + pub key_id: String, + #[serde(rename = "sid")] + pub secret_id: String, pub x5t: String, pub cer: String, pub id: String, @@ -41,16 +43,16 @@ pub(crate) struct KeyVaultGetCertificateResponse { } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetCertificateResponseAttributes { +pub struct KeyVaultGetCertificateResponseAttributes { pub enabled: bool, - #[serde(default, with = "azure_core::date::timestamp::option")] - pub exp: Option, - #[serde(default, with = "azure_core::date::timestamp::option")] - pub nbf: Option, - #[serde(with = "azure_core::date::timestamp")] - pub created: OffsetDateTime, - #[serde(with = "azure_core::date::timestamp")] - pub updated: OffsetDateTime, + #[serde(default, with = "azure_core::date::timestamp::option", rename = "exp")] + pub expires_on: Option, + #[serde(default, with = "azure_core::date::timestamp::option", rename = "nbf")] + pub not_before: Option, + #[serde(with = "azure_core::date::timestamp", rename = "created")] + pub created_on: OffsetDateTime, + #[serde(with = "azure_core::date::timestamp", rename = "updated")] + pub updated_on: OffsetDateTime, #[serde(rename = "recoveryLevel")] #[allow(unused)] pub recovery_level: String, @@ -58,7 +60,7 @@ pub(crate) struct KeyVaultGetCertificateResponseAttributes { #[derive(Deserialize, Debug)] #[allow(unused)] -pub(crate) struct KeyVaultGetCertificateResponsePolicy { +pub struct KeyVaultGetCertificateResponsePolicy { pub id: String, pub key_props: KeyVaultGetCertificateResponsePolicyKeyProperties, pub secret_props: KeyVaultGetCertificateResponsePolicySecretProperties, @@ -69,7 +71,7 @@ pub(crate) struct KeyVaultGetCertificateResponsePolicy { #[derive(Deserialize, Debug)] #[allow(unused)] -pub(crate) struct KeyVaultGetCertificateResponsePolicyKeyProperties { +pub struct KeyVaultGetCertificateResponsePolicyKeyProperties { pub exportable: bool, pub kty: String, pub key_size: u64, @@ -77,32 +79,32 @@ pub(crate) struct KeyVaultGetCertificateResponsePolicyKeyProperties { } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetCertificateResponsePolicySecretProperties { +pub struct KeyVaultGetCertificateResponsePolicySecretProperties { #[serde(rename = "contentType")] pub content_type: String, } #[derive(Deserialize, Debug)] #[allow(unused)] -pub(crate) struct KeyVaultGetCertificateResponsePolicyX509Properties { +pub struct KeyVaultGetCertificateResponsePolicyX509Properties { pub subject: String, pub validity_months: u64, } #[derive(Deserialize, Debug)] #[allow(unused)] -pub(crate) struct KeyVaultGetCertificateResponsePolicyIssuer { +pub struct KeyVaultGetCertificateResponsePolicyIssuer { pub name: String, } #[derive(Deserialize, Debug)] #[allow(unused)] -pub(crate) struct KeyVaultGetCertificateResponsePolicyAttributes { +pub struct KeyVaultGetCertificateResponsePolicyAttributes { pub enabled: bool, - #[serde(with = "azure_core::date::timestamp")] - pub created: OffsetDateTime, - #[serde(with = "azure_core::date::timestamp")] - pub updated: OffsetDateTime, + #[serde(with = "azure_core::date::timestamp", rename = "created")] + pub created_on: OffsetDateTime, + #[serde(with = "azure_core::date::timestamp", rename = "updated")] + pub updated_on: OffsetDateTime, } #[derive(Deserialize, Debug)] @@ -110,16 +112,6 @@ pub struct CertificateBackupResponse { pub value: String, } -#[derive(Debug)] -pub struct KeyVaultCertificate { - pub key_id: String, - pub secret_id: String, - pub x5t: String, - pub cer: String, - pub content_type: String, - pub properties: CertificateProperties, -} - #[derive(Debug)] pub struct CertificateProperties { pub id: String, diff --git a/sdk/security_keyvault/src/certificates/operations/backup.rs b/sdk/security_keyvault/src/certificates/operations/backup.rs index 2fb6be58d09..52d18679bef 100644 --- a/sdk/security_keyvault/src/certificates/operations/backup.rs +++ b/sdk/security_keyvault/src/certificates/operations/backup.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, CollectedResponse, Method}; operation! { CertificateBackup, @@ -10,21 +10,25 @@ operation! { impl CertificateBackupBuilder { pub fn into_future(mut self) -> CertificateBackup { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path(&format!("certificates/{}/backup", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); - let response_body = self - .client + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Post, headers, None)?; + + let response = self .client - .request(reqwest::Method::POST, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let backup_blob = serde_json::from_str::(&response_body) - .with_context(ErrorKind::DataConversion, || { - format!("failed to parse certificate backup response. uri: {uri}") - })?; + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + let backup_blob = serde_json::from_slice(body)?; Ok(backup_blob) }) } diff --git a/sdk/security_keyvault/src/certificates/operations/get_certificate.rs b/sdk/security_keyvault/src/certificates/operations/get_certificate.rs index 02d2d5f7ae9..c2226baad51 100644 --- a/sdk/security_keyvault/src/certificates/operations/get_certificate.rs +++ b/sdk/security_keyvault/src/certificates/operations/get_certificate.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, CollectedResponse, Method}; operation! { GetCertificate, @@ -11,39 +11,30 @@ operation! { impl GetCertificateBuilder { pub fn into_future(mut self) -> GetCertificate { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let version = self.version.unwrap_or_default(); uri.set_path(&format!("certificates/{}/{}", self.name, version)); - uri.set_query(Some(API_VERSION_PARAM)); - let response_body = self - .client + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Get, headers, None)?; + + let response = self .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let response = serde_json::from_str::(&response_body) - .with_context(ErrorKind::DataConversion, || { - format!("failed to parse get certificate response. uri: {uri} certificate_name: {} response_body: {response_body}", self.name) - })?; - Ok(KeyVaultCertificate { - key_id: response.kid, - secret_id: response.sid, - x5t: response.x5t, - cer: response.cer, - content_type: response.policy.secret_props.content_type, - properties: CertificateProperties { - id: response.id, - name: self.name.to_string(), - version: version.to_string(), - enabled: response.attributes.enabled, - not_before: response.attributes.nbf, - expires_on: response.attributes.exp, - created_on: response.attributes.created, - updated_on: response.attributes.updated, - }, - }) + + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let response: KeyVaultGetCertificateResponse = serde_json::from_slice(body)?; + + Ok(response) }) } } -type GetCertificateResponse = KeyVaultCertificate; +type GetCertificateResponse = KeyVaultGetCertificateResponse; diff --git a/sdk/security_keyvault/src/certificates/operations/get_versions.rs b/sdk/security_keyvault/src/certificates/operations/get_versions.rs index 3cef5aad30e..416a028b69a 100644 --- a/sdk/security_keyvault/src/certificates/operations/get_versions.rs +++ b/sdk/security_keyvault/src/certificates/operations/get_versions.rs @@ -1,64 +1,48 @@ use crate::prelude::*; +use azure_core::{error::Error, headers::Headers, CollectedResponse, Method, Pageable}; use url::Url; operation! { + #[stream] GetCertificateVersions, client: CertificateClient, name: String, } impl GetCertificateVersionsBuilder { - pub fn into_future(mut self) -> GetCertificateVersions { - Box::pin(async move { - let mut versions = Vec::::new(); + pub fn into_stream(self) -> Pageable { + let make_request = move |continuation: Option| { + let this = self.clone(); + let mut ctx = self.context.clone(); + async move { + let mut uri = this.client.keyvault_client.vault_url.clone(); + uri.set_path(&format!("certificates/{}/versions", this.name)); + + if let Some(continuation) = continuation { + uri = Url::parse(&continuation)?; + } - let mut uri = self.client.client.vault_url.clone(); - uri.set_path(&format!("certificates/{}/versions", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); + let headers = Headers::new(); + let mut request = this.client.keyvault_client.finalize_request( + uri, + Method::Get, + headers, + None, + )?; - loop { - let resp_body = self - .client + let response = this .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut ctx, &mut request) .await?; - let response = - serde_json::from_str::(&resp_body).unwrap(); + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); - versions.extend( - response - .value - .into_iter() - .map(|s| CertificateProperties { - id: s.id.to_owned(), - name: self.name.to_string(), - version: s.id.split('/').collect::>()[5].to_string(), - enabled: s.attributes.enabled, - created_on: s.attributes.created, - updated_on: s.attributes.updated, - not_before: s.attributes.nbf, - expires_on: s.attributes.exp, - }) - .collect::>(), - ); - match response.next_link { - None => break, - Some(u) => uri = Url::parse(&u).unwrap(), - } + let response = serde_json::from_slice::(body)?; + Ok(response) } - - // Return the certificate versions sorted by the time modified in descending order. - versions.sort_by(|a, b| { - if a.updated_on > b.updated_on { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - } - }); - Ok(versions) - }) + }; + Pageable::new(make_request) } } - -type GetCertificateVersionsResponse = Vec; diff --git a/sdk/security_keyvault/src/certificates/operations/update_properties.rs b/sdk/security_keyvault/src/certificates/operations/update_properties.rs index 6b2b6bbc5ab..494dfa9c2fd 100644 --- a/sdk/security_keyvault/src/certificates/operations/update_properties.rs +++ b/sdk/security_keyvault/src/certificates/operations/update_properties.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, Method}; use serde::Serialize; use time::OffsetDateTime; @@ -32,10 +32,9 @@ struct UpdateRequest { impl UpdateCertificatePropertiesBuilder { pub fn into_future(mut self) -> UpdateCertificateProperties { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let version = self.version.unwrap_or_default(); uri.set_path(&format!("certificates/{}/{}", self.name, version)); - uri.set_query(Some(API_VERSION_PARAM)); let request = UpdateRequest { attributes: Attributes { @@ -45,17 +44,19 @@ impl UpdateCertificatePropertiesBuilder { }, }; - let body = serde_json::to_string(&request) - .with_context(ErrorKind::Other, || { - format!( - "failed to serialize UpdateRequest. secret_name: {} secret_version_name: {version}", - self.name - ) - })?; + let body = serde_json::to_string(&request)?; + + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Patch, + headers, + Some(body.into()), + )?; self.client - .client - .request(reqwest::Method::PATCH, uri.to_string(), Some(body)) + .keyvault_client + .send(&mut self.context, &mut request) .await?; Ok(()) diff --git a/sdk/security_keyvault/src/clients/certificate_client.rs b/sdk/security_keyvault/src/clients/certificate_client.rs index 7c55c1bea03..d86039a9892 100644 --- a/sdk/security_keyvault/src/clients/certificate_client.rs +++ b/sdk/security_keyvault/src/clients/certificate_client.rs @@ -4,7 +4,7 @@ use std::sync::Arc; #[derive(Clone, Debug)] pub struct CertificateClient { - pub(crate) client: KeyvaultClient, + pub(crate) keyvault_client: KeyvaultClient, } impl CertificateClient { @@ -12,12 +12,12 @@ impl CertificateClient { vault_url: &str, token_credential: Arc, ) -> azure_core::Result { - let client = KeyvaultClient::new(vault_url, token_credential)?; - Ok(Self { client }) + let keyvault_client = KeyvaultClient::new(vault_url, token_credential)?; + Ok(Self::new_with_client(keyvault_client)) } - pub(crate) fn new_with_client(client: KeyvaultClient) -> Self { - Self { client } + pub(crate) fn new_with_client(keyvault_client: KeyvaultClient) -> Self { + Self { keyvault_client } } /// Gets a certificate from the Key Vault. @@ -57,6 +57,7 @@ impl CertificateClient { /// use azure_security_keyvault::KeyvaultClient; /// use azure_identity::DefaultAzureCredential; /// use tokio::runtime::Runtime; + /// use futures::StreamExt; /// use std::sync::Arc; /// /// async fn example() { @@ -65,7 +66,7 @@ impl CertificateClient { /// &"KEYVAULT_URL", /// Arc::new(creds), /// ).unwrap().certificate_client(); - /// let certificate_versions = client.get_versions("NAME").into_future().await.unwrap(); + /// let certificate_versions = client.get_versions("NAME").into_stream().next().await.unwrap(); /// dbg!(&certificate_versions); /// } /// @@ -145,7 +146,6 @@ impl CertificateClient { { // let mut uri = self.vault_url.clone(); // uri.set_path(&format!("certificates/{}", certificate_name)); - // uri.set_query(Some(API_VERSION_PARAM)); // self.delete_authed(uri.to_string()).await?; @@ -161,6 +161,7 @@ impl CertificateClient { /// ```no_run /// use azure_security_keyvault::KeyvaultClient; /// use azure_identity::DefaultAzureCredential; + /// use futures::StreamExt; /// use tokio::runtime::Runtime; /// use std::sync::Arc; /// @@ -170,7 +171,7 @@ impl CertificateClient { /// &"KEYVAULT_URL", /// Arc::new(creds), /// ).unwrap().certificate_client(); - /// let certificates = client.list_certificates().into_future().await.unwrap(); + /// let certificates = client.list_certificates().into_stream().next().await.unwrap(); /// dbg!(&certificates); /// } /// @@ -209,173 +210,3 @@ impl CertificateClient { RestoreCertificateBuilder::new(self.clone(), backup_blob.into()) } } - -#[cfg(test)] -#[allow(unused_must_use)] -mod tests { - use crate::mock_client; - use crate::prelude::*; - use crate::tests::MockCredential; - use azure_core::date; - use mockito::{mock, Matcher}; - use serde_json::json; - use std::time::Duration; - use time::OffsetDateTime; - - #[tokio::test] - async fn get_certificate() { - let time_created = OffsetDateTime::now_utc() - date::duration_from_days(7); - let time_updated = OffsetDateTime::now_utc(); - let _m = mock("GET", "/certificates/test-certificate/") - .match_query(Matcher::UrlEncoded("api-version".into(), API_VERSION.into())) - .with_header("content-type", "application/json") - .with_body( - json!({ - "id": "https://test-keyvault.vault.azure.net/certificates/test-certificate/002ade539442463aba45c0efb42e3e84", - "x5t": "fLi3U52HunIVNXubkEnf8tP6Wbo", - "kid": "https://test-keyvault.vault.azure.net/keys/test-certificate/002ade539442463aba45c0efb42e3e84", - "sid": "https://test-keyvault.vault.azure.net/secrets/test-certificate/002ade539442463aba45c0efb42e3e84", - "cer": "MIICODCCAeagAwIBAgIQqHmpBAv+CY9IJFoUhlbziTAJBgUrDgMCHQUAMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTE1MDQyOTIxNTM0MVoXDTM5MTIzMTIzNTk1OVowFzEVMBMGA1UEAxMMS2V5VmF1bHRUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5bVAT73zr4+N4WVv2+SvTunAw08ksS4BrJW/nNliz3S9XuzMBMXvmYzU5HJ8TtEgluBiZZYd5qsMJD+OXHSNbsLdmMhni0jYX09h3XlC2VJw2sGKeYF+xEaavXm337aZZaZyjrFBrrUl51UePaN+kVFXNlBb3N3TYpqa7KokXenJQuR+i9Gv9a77c0UsSsDSryxppYhKK7HvTZCpKrhVtulF5iPMswWe9np3uggfMamyIsK/0L7X9w9B2qN7993RR0A00nOk4H6CnkuwO77dSsD0KJsk6FyAoZBzRXDZh9+d9R76zCL506NcQy/jl0lCiQYwsUX73PG5pxOh02OwKwIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwCQYFKw4DAh0FAANBAGqIjo2geVagzuzaZOe1ClGKhZeiCKfWAxklaGN+qlGUbVS4IN4V1lot3VKnzabasmkEHeNxPwLn1qvSD0cX9CE=", - "attributes": { - "enabled": true, - "created": time_created.unix_timestamp(), - "updated": time_updated.unix_timestamp(), - "recoveryLevel": "Recoverable+Purgeable" - }, - "policy": { - "id": "https://test-keyvault.vault.azure.net/certificates/selfSignedCert01/policy", - "key_props": { - "exportable": true, - "kty": "RSA", - "key_size": 2048, - "reuse_key": false - }, - "secret_props": { - "contentType": "application/x-pkcs12" - }, - "x509_props": { - "subject": "CN=KeyVaultTest", - "ekus": [], - "key_usage": [], - "validity_months": 297 - }, - "issuer": { - "name": "Unknown" - }, - "attributes": { - "enabled": true, - "created": 1493938289, - "updated": 1493938291 - } - } - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - dbg!(mockito::server_url()); - let client = mock_client!(&"test-keyvault", creds); - - let certificate = client - .certificate_client() - .get("test-certificate") - .into_future() - .await - .unwrap(); - - assert_eq!( - "https://test-keyvault.vault.azure.net/keys/test-certificate/002ade539442463aba45c0efb42e3e84", - certificate.key_id - ); - assert!( - date::diff(time_created, certificate.properties.created_on) < Duration::from_secs(1) - ); - assert!( - date::diff(time_updated, certificate.properties.updated_on) < Duration::from_secs(1) - ); - } - - #[tokio::test] - async fn get_certificate_versions() { - let time_created_1 = OffsetDateTime::now_utc() - date::duration_from_days(7); - let time_updated_1 = OffsetDateTime::now_utc(); - let time_created_2 = OffsetDateTime::now_utc() - date::duration_from_days(9); - let time_updated_2 = OffsetDateTime::now_utc() - date::duration_from_days(2); - - let _m1 = mock("GET", "/certificates/test-certificate/versions") - .match_query(Matcher::AllOf(vec![ - Matcher::UrlEncoded("api-version".into(), API_VERSION.into()), - ])) - .with_header("content-type", "application/json") - .with_body( - json!({ - "value": [{ - "id": "https://test-keyvault.vault.azure.net/certificates/test-certificate/VERSION_1", - "x5t": "fLi3U52HunIVNXubkEnf8tP6Wbo", - "attributes": { - "enabled": true, - "created": time_created_1.unix_timestamp(), - "updated": time_updated_1.unix_timestamp(), - } - }], - "nextLink": format!("{}/certificates/text-certificate/versions?api-version={}&maxresults=1&$skiptoken=SKIP_TOKEN_MOCK", mockito::server_url(), API_VERSION) - }) - .to_string(), - ) - .with_status(200) - .create(); - - let _m2 = mock("GET", "/certificates/text-certificate/versions") - .match_query(Matcher::AllOf(vec![ - Matcher::UrlEncoded("api-version".into(), API_VERSION.into()), - Matcher::UrlEncoded("maxresults".into(), "1".into()), - Matcher::UrlEncoded("$skiptoken".into(), "SKIP_TOKEN_MOCK".into()), - ])) - .with_header("content-type", "application/json") - .with_body( - json!({ - "value": [{ - "id": "https://test-keyvault.vault.azure.net/certificates/test-certificate/VERSION_2", - "x5t": "fLi3U52HunIVNXubkEnf8tP6Wbo", - "attributes": { - "enabled": true, - "created": time_created_2.unix_timestamp(), - "updated": time_updated_2.unix_timestamp(), - } - }], - "nextLink": null - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - let client = mock_client!(&"test-keyvault", creds); - - let certificate_versions = client - .certificate_client() - .get_versions("test-certificate") - .into_future() - .await - .unwrap(); - - let certificate_1 = &certificate_versions[0]; - assert_eq!( - "https://test-keyvault.vault.azure.net/certificates/test-certificate/VERSION_1", - certificate_1.id - ); - assert!(date::diff(time_created_1, certificate_1.created_on) < Duration::from_secs(1)); - assert!(date::diff(time_updated_1, certificate_1.updated_on) < Duration::from_secs(1)); - - let certificate_2 = &certificate_versions[1]; - assert_eq!( - "https://test-keyvault.vault.azure.net/certificates/test-certificate/VERSION_2", - certificate_2.id - ); - assert!(date::diff(time_created_2, certificate_2.created_on) < Duration::from_secs(1)); - assert!(date::diff(time_updated_2, certificate_2.updated_on) < Duration::from_secs(1)); - } -} diff --git a/sdk/security_keyvault/src/clients/key_client.rs b/sdk/security_keyvault/src/clients/key_client.rs index da15cf80f94..d5f1b1d6289 100644 --- a/sdk/security_keyvault/src/clients/key_client.rs +++ b/sdk/security_keyvault/src/clients/key_client.rs @@ -4,7 +4,7 @@ use std::sync::Arc; #[derive(Clone, Debug)] pub struct KeyClient { - pub(crate) client: KeyvaultClient, + pub(crate) keyvault_client: KeyvaultClient, } impl KeyClient { @@ -12,12 +12,12 @@ impl KeyClient { vault_url: &str, token_credential: Arc, ) -> azure_core::Result { - let client = KeyvaultClient::new(vault_url, token_credential)?; - Ok(Self { client }) + let keyvault_client = KeyvaultClient::new(vault_url, token_credential)?; + Ok(Self::new_with_client(keyvault_client)) } - pub(crate) fn new_with_client(client: KeyvaultClient) -> Self { - Self { client } + pub(crate) fn new_with_client(keyvault_client: KeyvaultClient) -> Self { + Self { keyvault_client } } /// Gets the public part of a stored key. @@ -67,176 +67,3 @@ impl KeyClient { DecryptBuilder::new(self.clone(), name.into(), decrypt_parameters) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock_client; - use crate::tests::MockCredential; - use azure_core::date; - use mockito::{mock, Matcher}; - use serde_json::json; - use std::time::Duration; - use time::OffsetDateTime; - - #[tokio::test] - async fn can_get_key() -> azure_core::Result<()> { - let time_created = OffsetDateTime::now_utc() - date::duration_from_days(7); - let time_updated = OffsetDateTime::now_utc(); - let _m = mock("GET", "/keys/test-key/78deebed173b48e48f55abf87ed4cf71") - .match_query(Matcher::UrlEncoded("api-version".into(), API_VERSION.into())) - .with_header("content-type", "application/json") - .with_body( - json!({ - "key": { - "kid": "https://test-keyvault.vault.azure.net/keys/test-key/78deebed173b48e48f55abf87ed4cf71", - "kty": "RSA", - "key_ops": [ - "encrypt", - "decrypt", - "sign", - "verify", - "wrapKey", - "unwrapKey", - "destroy!" - ], - "n": "2HJAE5fU3Cw2Rt9hEuq-F6XjINKGa-zskfISVqopqUy60GOs2eyhxbWbJBeUXNor_gf-tXtNeuqeBgitLeVa640UDvnEjYTKWjCniTxZRaU7ewY8BfTSk-7KxoDdLsPSpX_MX4rwlAx-_1UGk5t4sQgTbm9T6Fm2oqFd37dsz5-Gj27UP2GTAShfJPFD7MqU_zIgOI0pfqsbNL5xTQVM29K6rX4jSPtylZV3uWJtkoQIQnrIHhk1d0SC0KwlBV3V7R_LVYjiXLyIXsFzSNYgQ68ZjAwt8iL7I8Osa-ehQLM13DVvLASaf7Jnu3sC3CWl3Gyirgded6cfMmswJzY87w", - "e": "AQAB" - }, - "attributes": { - "enabled": true, - "created": time_created.unix_timestamp(), - "updated": time_updated.unix_timestamp(), - "recoveryLevel": "Recoverable+Purgeable" - }, - "tags": { - "purpose": "unit test", - "test name ": "CreateGetDeleteKeyTest" - } - }) - .to_string(), - ) - .with_status(200) - .create(); - - let client = mock_client!(&"test-keyvault", MockCredential::new()); - - let key = client - .key_client() - .get("test-key") - .version("78deebed173b48e48f55abf87ed4cf71") - .into_future() - .await?; - - let JsonWebKey { id, n, .. } = key.key; - let KeyProperties { - attributes, - managed, - tags, - } = key.properties; - let KeyAttributes { - created_on, - enabled, - updated_on, - .. - } = attributes; - let expected_n = base64::decode_config("2HJAE5fU3Cw2Rt9hEuq-F6XjINKGa-zskfISVqopqUy60GOs2eyhxbWbJBeUXNor_gf-tXtNeuqeBgitLeVa640UDvnEjYTKWjCniTxZRaU7ewY8BfTSk-7KxoDdLsPSpX_MX4rwlAx-_1UGk5t4sQgTbm9T6Fm2oqFd37dsz5-Gj27UP2GTAShfJPFD7MqU_zIgOI0pfqsbNL5xTQVM29K6rX4jSPtylZV3uWJtkoQIQnrIHhk1d0SC0KwlBV3V7R_LVYjiXLyIXsFzSNYgQ68ZjAwt8iL7I8Osa-ehQLM13DVvLASaf7Jnu3sC3CWl3Gyirgded6cfMmswJzY87w", BASE64_URL_SAFE).unwrap(); - assert_eq!(expected_n, n.unwrap()); - assert_eq!( - "https://test-keyvault.vault.azure.net/keys/test-key/78deebed173b48e48f55abf87ed4cf71", - id.unwrap() - ); - - assert!(managed.is_none()); - assert_eq!(tags.unwrap().get("purpose").unwrap(), "unit test"); - assert!(enabled.unwrap()); - assert!(date::diff(time_created, created_on.unwrap()) < Duration::from_secs(1)); - assert!(date::diff(time_updated, updated_on.unwrap()) < Duration::from_secs(1)); - Ok(()) - } - - #[tokio::test] - async fn can_sign() { - let _m = mock("POST", "/keys/test-key/78deebed173b48e48f55abf87ed4cf71/sign") - .match_query(Matcher::UrlEncoded("api-version".into(), API_VERSION.into())) - .with_header("content-type", "application/json") - .with_body( - json!({ - "kid": "https://myvault.vault.azure.net/keys/testkey/9885aa558e8d448789683188f8c194b0", - "value": "aKFG8NXcfTzqyR44rW42484K_zZI_T7zZuebvWuNgAoEI1gXYmxrshp42CunSmmu4oqo4-IrCikPkNIBkHXnAW2cv03Ad0UpwXhVfepK8zzDBaJPMKVGS-ZRz8CshEyGDKaLlb3J3zEkXpM3RrSEr0mdV6hndHD_mznLB5RmFui5DsKAhez4vUqajgtkgcPfCekMqeSwp6r9ItVL-gEoAohx8XMDsPedqu-7BuZcBcdayaPuBRL4wWoTDULA11P-UN_sJ5qMj3BbiRYhIlBWGR04wIGfZ3pkJjHJUpOvgH2QajdYPzUBauOCewMYbq9XkLRSzI_A7HkkDVycugSeAA" - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - let client = mock_client!(&"test-keyvault", creds).key_client(); - - let res = client - .sign("test-key", SignatureAlgorithm::RS512, "base64msg2sign") - .version("78deebed173b48e48f55abf87ed4cf71") - .into_future() - .await - .unwrap(); - - let kid = res.key_id; - let sig = res.signature; - let alg = res.algorithm; - - assert_eq!( - kid, - "https://myvault.vault.azure.net/keys/testkey/9885aa558e8d448789683188f8c194b0" - ); - let expected_sig = base64::decode_config("aKFG8NXcfTzqyR44rW42484K_zZI_T7zZuebvWuNgAoEI1gXYmxrshp42CunSmmu4oqo4-IrCikPkNIBkHXnAW2cv03Ad0UpwXhVfepK8zzDBaJPMKVGS-ZRz8CshEyGDKaLlb3J3zEkXpM3RrSEr0mdV6hndHD_mznLB5RmFui5DsKAhez4vUqajgtkgcPfCekMqeSwp6r9ItVL-gEoAohx8XMDsPedqu-7BuZcBcdayaPuBRL4wWoTDULA11P-UN_sJ5qMj3BbiRYhIlBWGR04wIGfZ3pkJjHJUpOvgH2QajdYPzUBauOCewMYbq9XkLRSzI_A7HkkDVycugSeAA", BASE64_URL_SAFE).unwrap(); - assert_eq!(expected_sig, sig); - assert!(matches!(alg, SignatureAlgorithm::RS512)); - } - - #[tokio::test] - async fn can_decrypt() { - let _m = mock("POST", "/keys/test-key/78deebed173b48e48f55abf87ed4cf71/decrypt") - .match_query(Matcher::UrlEncoded("api-version".into(), API_VERSION.into())) - .with_header("content-type", "application/json") - .with_body( - json!({ - "kid": "https://myvault.vault.azure.net/keys/test-key/78deebed173b48e48f55abf87ed4cf71", - "value": "dvDmrSBpjRjtYg" - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - let client = mock_client!(&"test-keyvault", creds); - - let decrypt_parameters = DecryptParameters { - ciphertext: base64::decode("dvDmrSBpjRjtYg").unwrap(), - decrypt_parameters_encryption: DecryptParametersEncryption::Rsa( - RsaDecryptParameters::new(EncryptionAlgorithm::RsaOaep256).unwrap(), - ), - }; - - let res: DecryptResult = client - .key_client() - .decrypt("test-key", decrypt_parameters) - .version("78deebed173b48e48f55abf87ed4cf71") - .into_future() - .await - .unwrap(); - - let kid = res.key_id; - let val = res.result; - let alg = res.algorithm; - - assert_eq!( - kid, - "https://myvault.vault.azure.net/keys/test-key/78deebed173b48e48f55abf87ed4cf71" - ); - let expected_val = base64::decode_config("dvDmrSBpjRjtYg", BASE64_URL_SAFE).unwrap(); - assert_eq!(expected_val, val); - - assert!(matches!(alg, EncryptionAlgorithm::RsaOaep256)); - } -} diff --git a/sdk/security_keyvault/src/clients/keyvault_client.rs b/sdk/security_keyvault/src/clients/keyvault_client.rs index 7a578c3cf73..e0817429ee5 100644 --- a/sdk/security_keyvault/src/clients/keyvault_client.rs +++ b/sdk/security_keyvault/src/clients/keyvault_client.rs @@ -1,13 +1,18 @@ -use crate::prelude::*; -use azure_core::auth::{TokenCredential, TokenResponse}; -use azure_core::error::{Error, ErrorKind, ResultExt}; +use crate::{clients::pipeline::new_pipeline_from_options, prelude::*}; +use azure_core::{ + auth::TokenCredential, + date, + error::{Error, ErrorKind}, + headers::*, + Body, Context, Method, Pipeline, Request, Response, +}; use const_format::formatcp; use std::sync::Arc; use time::OffsetDateTime; use url::Url; pub const API_VERSION: &str = "7.0"; -pub(crate) const API_VERSION_PARAM: &str = formatcp!("api-version={}", API_VERSION); +const API_VERSION_PARAM: &str = formatcp!("api-version={}", API_VERSION); /// Client for Key Vault operations - getting a secret, listing secrets, etc. /// @@ -22,16 +27,13 @@ pub(crate) const API_VERSION_PARAM: &str = formatcp!("api-version={}", API_VERSI #[derive(Clone)] pub struct KeyvaultClient { pub(crate) vault_url: Url, - pub(crate) endpoint: String, - pub(crate) token_credential: Arc, - pub(crate) token: Option, + pub(crate) pipeline: Pipeline, } impl std::fmt::Debug for KeyvaultClient { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("KeyvaultClient") .field("vault_url", &self.vault_url) - .field("endpoint", &self.endpoint) .finish_non_exhaustive() } } @@ -54,63 +56,50 @@ impl KeyvaultClient { ) -> azure_core::Result { let vault_url = Url::parse(vault_url)?; let endpoint = extract_endpoint(&vault_url)?; + let pipeline = new_pipeline_from_options(token_credential.clone(), endpoint); let client = Self { vault_url, - endpoint, - token_credential, - token: None, + pipeline, }; Ok(client) } - pub(crate) async fn request( - &mut self, - method: reqwest::Method, - uri: String, - body: Option, - ) -> azure_core::Result { - self.get_token().await?; + pub(crate) fn finalize_request( + &self, + mut url: Url, + method: Method, + headers: Headers, + request_body: Option, + ) -> azure_core::Result { + let dt = OffsetDateTime::now_utc(); + let time = date::to_rfc1123(&dt); + + url.set_query(Some(API_VERSION_PARAM)); + + let mut request = Request::new(url, method); + for (k, v) in headers { + request.insert_header(k, v); + } - let mut req = reqwest::Client::new() - .request(method, &uri) - .bearer_auth(self.token.as_ref().unwrap().token.secret()); + request.insert_header(MS_DATE, time); - if let Some(body) = body { - req = req.header("content-type", "application/json").body(body); + if let Some(request_body) = request_body { + request.insert_header(CONTENT_LENGTH, request_body.len().to_string()); + request.set_body(request_body); } else { - req = req.header("content-length", 0); - } + request.insert_header(CONTENT_LENGTH, "0"); + request.set_body(azure_core::EMPTY_BODY); + }; - let resp = req - .send() - .await - .with_context(ErrorKind::Io, || { - format!("failed to send request. uri: {uri}") - })? - .error_for_status() - .with_context(ErrorKind::Io, || { - format!("failed to read response body text. uri: {uri}") - })?; - - let body = resp.text().await.with_context(ErrorKind::Io, || { - format!("failed to read response body text. uri: {uri}") - })?; - Ok(body) + Ok(request) } - pub(crate) async fn get_token(&mut self) -> azure_core::Result<&str> { - if self.token.is_none() - || matches!(&self.token, Some(token) if token.expires_on < OffsetDateTime::now_utc()) - { - let token = self - .token_credential - .get_token(&self.endpoint) - .await - .context(ErrorKind::Credential, "get token failed")?; - - self.token = Some(token); - } - Ok(self.token.as_ref().unwrap().token.secret()) + pub(crate) async fn send( + &self, + context: &mut Context, + request: &mut Request, + ) -> azure_core::Result { + self.pipeline.send(context, request).await } pub fn secret_client(&self) -> SecretClient { diff --git a/sdk/security_keyvault/src/clients/mod.rs b/sdk/security_keyvault/src/clients/mod.rs index 85744b9a31d..1c07bb36b7e 100644 --- a/sdk/security_keyvault/src/clients/mod.rs +++ b/sdk/security_keyvault/src/clients/mod.rs @@ -1,9 +1,10 @@ mod certificate_client; mod key_client; mod keyvault_client; +mod pipeline; +mod policy; mod secret_client; pub use certificate_client::CertificateClient; pub use key_client::KeyClient; -pub(crate) use keyvault_client::API_VERSION_PARAM; pub use keyvault_client::{KeyvaultClient, API_VERSION}; pub use secret_client::SecretClient; diff --git a/sdk/security_keyvault/src/clients/pipeline.rs b/sdk/security_keyvault/src/clients/pipeline.rs new file mode 100644 index 00000000000..f2eaa382ae2 --- /dev/null +++ b/sdk/security_keyvault/src/clients/pipeline.rs @@ -0,0 +1,32 @@ +use crate::clients::policy::AuthorizationPolicy; +use azure_core::{auth::TokenCredential, ClientOptions, Pipeline, TimeoutPolicy}; +use std::sync::Arc; + +pub(crate) fn new_pipeline_from_options( + credentials: Arc, + scope: String, +) -> Pipeline { + let auth_policy: Arc = + Arc::new(AuthorizationPolicy::new(credentials, scope)); + + // TODO: as we move to the builder pattern for the clients, these should be + // set there. + let client_options = ClientOptions::default(); + let timeout_policy = TimeoutPolicy::new(None); + + // The `AuthorizationPolicy` must be the **last** retry policy. + // Policies can change the url and/or the headers, and the `AuthorizationPolicy` + // must be able to inspect them or the resulting token will be invalid. + let per_retry_policies = vec![ + Arc::new(timeout_policy) as Arc, + auth_policy, + ]; + + Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + client_options, + Vec::new(), + per_retry_policies, + ) +} diff --git a/sdk/security_keyvault/src/clients/policy.rs b/sdk/security_keyvault/src/clients/policy.rs new file mode 100644 index 00000000000..5ca3346b28a --- /dev/null +++ b/sdk/security_keyvault/src/clients/policy.rs @@ -0,0 +1,57 @@ +use azure_core::{ + auth::TokenCredential, + error::{ErrorKind, ResultExt}, + headers::*, + Context, Policy, PolicyResult, Request, +}; +use std::sync::Arc; + +#[derive(Clone)] +pub struct AuthorizationPolicy { + credentials: Arc, + scope: String, +} + +impl std::fmt::Debug for AuthorizationPolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AuthorizationPolicy") + .field("credentials", &"...") + .field("scope", &self.scope) + .finish() + } +} + +impl AuthorizationPolicy { + pub(crate) fn new(credentials: Arc, scope: String) -> Self { + Self { credentials, scope } + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl Policy for AuthorizationPolicy { + async fn send( + &self, + ctx: &Context, + request: &mut Request, + next: &[Arc], + ) -> PolicyResult { + assert!( + !next.is_empty(), + "Authorization policies cannot be the last policy of a pipeline" + ); + + let bearer_token = self + .credentials + .get_token(&self.scope) + .await + .context(ErrorKind::Credential, "failed to get bearer token")?; + + request.insert_header( + AUTHORIZATION, + format!("Bearer {}", bearer_token.token.secret()), + ); + + next[0].send(ctx, request, &next[1..]).await + } +} diff --git a/sdk/security_keyvault/src/clients/secret_client.rs b/sdk/security_keyvault/src/clients/secret_client.rs index 6821edc4f6b..b7080ae1369 100644 --- a/sdk/security_keyvault/src/clients/secret_client.rs +++ b/sdk/security_keyvault/src/clients/secret_client.rs @@ -4,7 +4,7 @@ use std::sync::Arc; #[derive(Clone, Debug)] pub struct SecretClient { - pub(crate) client: KeyvaultClient, + pub(crate) keyvault_client: KeyvaultClient, } impl SecretClient { @@ -12,12 +12,12 @@ impl SecretClient { vault_url: &str, token_credential: Arc, ) -> azure_core::Result { - let client = KeyvaultClient::new(vault_url, token_credential)?; - Ok(Self { client }) + let keyvault_client = KeyvaultClient::new(vault_url, token_credential)?; + Ok(Self::new_with_client(keyvault_client)) } - pub(crate) fn new_with_client(client: KeyvaultClient) -> Self { - Self { client } + pub(crate) fn new_with_client(keyvault_client: KeyvaultClient) -> Self { + Self { keyvault_client } } /// Gets a secret from the Key Vault. @@ -113,6 +113,7 @@ impl SecretClient { /// ```no_run /// use azure_security_keyvault::KeyvaultClient; /// use azure_identity::DefaultAzureCredential; + /// use futures::StreamExt; /// use tokio::runtime::Runtime; /// use std::sync::Arc; /// @@ -122,7 +123,7 @@ impl SecretClient { /// &"KEYVAULT_URL", /// Arc::new(creds), /// ).unwrap().secret_client(); - /// let secret_versions = client.get_versions("SECRET_NAME").into_future().await.unwrap(); + /// let secret_versions = client.get_versions("SECRET_NAME").into_stream().next().await.unwrap(); /// dbg!(&secret_versions); /// } /// @@ -198,6 +199,7 @@ impl SecretClient { /// use azure_security_keyvault::KeyvaultClient; /// use azure_identity::DefaultAzureCredential; /// use tokio::runtime::Runtime; + /// use futures::stream::StreamExt; /// /// async fn example() { /// let creds = DefaultAzureCredential::default(); @@ -205,7 +207,7 @@ impl SecretClient { /// &"KEYVAULT_URL", /// std::sync::Arc::new(creds), /// ).unwrap().secret_client(); - /// let secrets = client.list_secrets().into_future().await.unwrap(); + /// let secrets = client.list_secrets().into_stream().next().await; /// dbg!(&secrets); /// } /// @@ -243,136 +245,3 @@ impl SecretClient { RestoreSecretBuilder::new(self.clone(), backup_blob.into()) } } - -#[cfg(test)] -#[allow(unused_must_use)] -mod tests { - use crate::mock_client; - use crate::prelude::API_VERSION; - use crate::tests::MockCredential; - use azure_core::date; - use mockito::{mock, Matcher}; - use serde_json::json; - use std::time::Duration; - use time::OffsetDateTime; - - #[tokio::test] - async fn get_secret() -> azure_core::Result<()> { - let time_created = OffsetDateTime::now_utc() - date::duration_from_days(7); - let time_updated = OffsetDateTime::now_utc(); - let _m = mock("GET", "/secrets/test-secret/") - .match_query(Matcher::UrlEncoded("api-version".into(), API_VERSION.into())) - .with_header("content-type", "application/json") - .with_body( - json!({ - "value": "secret-value", - "id": "https://test-keyvault.vault.azure.net/secrets/test-secret/4387e9f3d6e14c459867679a90fd0f79", - "attributes": { - "enabled": true, - "created": time_created.unix_timestamp(), - "updated": time_updated.unix_timestamp(), - "recoveryLevel": "Recoverable+Purgeable" - } - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - dbg!(mockito::server_url()); - let client = mock_client!(&"test-keyvault", creds); - let secret_client = client.secret_client(); - - let secret = secret_client.get("test-secret").into_future().await?; - - assert_eq!("secret-value", secret.value); - assert_eq!( - "https://test-keyvault.vault.azure.net/secrets/test-secret/4387e9f3d6e14c459867679a90fd0f79", - secret.id - ); - assert!(date::diff(time_created, secret.created_on) < Duration::from_secs(1)); - assert!(date::diff(time_updated, secret.updated_on) < Duration::from_secs(1)); - Ok(()) - } - - #[tokio::test] - async fn get_secret_versions() -> azure_core::Result<()> { - let time_created_1 = OffsetDateTime::now_utc() - date::duration_from_days(7); - let time_updated_1 = OffsetDateTime::now_utc(); - let time_created_2 = OffsetDateTime::now_utc() - date::duration_from_days(9); - let time_updated_2 = OffsetDateTime::now_utc() - date::duration_from_days(2); - - let _m1 = mock("GET", "/secrets/test-secret/versions") - .match_query(Matcher::AllOf(vec![ - Matcher::UrlEncoded("api-version".into(), API_VERSION.into()), - // Matcher::UrlEncoded("maxresults".into(), DEFAULT_MAX_RESULTS.to_string()), - ])) - .with_header("content-type", "application/json") - .with_body( - json!({ - "value": [{ - "id": "https://test-keyvault.vault.azure.net/secrets/test-secret/VERSION_1", - "attributes": { - "enabled": true, - "created": time_created_1.unix_timestamp(), - "updated": time_updated_1.unix_timestamp(), - } - }], - "nextLink": format!("{}/secrets/text-secret/versions?api-version={}&maxresults=1&$skiptoken=SKIP_TOKEN_MOCK", mockito::server_url(), API_VERSION) - }) - .to_string(), - ) - .with_status(200) - .create(); - - let _m2 = mock("GET", "/secrets/text-secret/versions") - .match_query(Matcher::AllOf(vec![ - Matcher::UrlEncoded("api-version".into(), API_VERSION.into()), - Matcher::UrlEncoded("maxresults".into(), "1".into()), - Matcher::UrlEncoded("$skiptoken".into(), "SKIP_TOKEN_MOCK".into()), - ])) - .with_header("content-type", "application/json") - .with_body( - json!({ - "value": [{ - "id": "https://test-keyvault.vault.azure.net/secrets/test-secret/VERSION_2", - "attributes": { - "enabled": true, - "created": time_created_2.unix_timestamp(), - "updated": time_updated_2.unix_timestamp(), - } - }], - "nextLink": null - }) - .to_string(), - ) - .with_status(200) - .create(); - - let creds = MockCredential::new(); - let secret_client = mock_client!(&"test-keyvault", creds).secret_client(); - - let secret_versions = secret_client - .get_versions("test-secret") - .into_future() - .await?; - - let secret_1 = &secret_versions[0]; - assert_eq!( - "https://test-keyvault.vault.azure.net/secrets/test-secret/VERSION_1", - secret_1.id - ); - assert!(date::diff(time_created_1, secret_1.created_on) < Duration::from_secs(1)); - assert!(date::diff(time_updated_1, secret_1.updated_on) < Duration::from_secs(1)); - - let secret_2 = &secret_versions[1]; - assert_eq!( - "https://test-keyvault.vault.azure.net/secrets/test-secret/VERSION_2", - secret_2.id - ); - assert!(date::diff(time_created_2, secret_2.created_on) < Duration::from_secs(1)); - assert!(date::diff(time_updated_2, secret_2.updated_on) < Duration::from_secs(1)); - Ok(()) - } -} diff --git a/sdk/security_keyvault/src/keys/operations/decrypt.rs b/sdk/security_keyvault/src/keys/operations/decrypt.rs index 2c02e2b0a3b..f438d87c996 100644 --- a/sdk/security_keyvault/src/keys/operations/decrypt.rs +++ b/sdk/security_keyvault/src/keys/operations/decrypt.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, CollectedResponse, Method}; use serde_json::{Map, Value}; operation! { @@ -14,11 +15,10 @@ impl DecryptBuilder { Box::pin(async move { // POST {vaultBaseUrl}/keys/{key-name}/{key-version}/decrypt?api-version=7.2 let version = self.version.unwrap_or_default(); - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let path = format!("keys/{}/{}/decrypt", self.name, version); uri.set_path(&path); - uri.set_query(Some(API_VERSION_PARAM)); let mut request_body = Map::new(); request_body.insert( @@ -58,17 +58,24 @@ impl DecryptBuilder { } }; + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Post, + headers, + Some(Value::Object(request_body).to_string().into()), + )?; + let response = self .client - .client - .request( - reqwest::Method::POST, - uri.to_string(), - Some(Value::Object(request_body).to_string()), - ) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let mut result = serde_json::from_str::(&response)?; + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let mut result = serde_json::from_slice::(body)?; result.algorithm = algorithm; Ok(result) }) diff --git a/sdk/security_keyvault/src/keys/operations/get_key.rs b/sdk/security_keyvault/src/keys/operations/get_key.rs index fbab5ff7a85..faee829dccb 100644 --- a/sdk/security_keyvault/src/keys/operations/get_key.rs +++ b/sdk/security_keyvault/src/keys/operations/get_key.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, CollectedResponse, Method}; operation! { GetKey, @@ -10,18 +11,27 @@ operation! { impl GetKeyBuilder { pub fn into_future(mut self) -> GetKey { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let version = self.version.unwrap_or_default(); let path = format!("keys/{}/{}", self.name, version); uri.set_path(&path); - uri.set_query(Some(API_VERSION_PARAM)); - let resp_body = self - .client + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Get, headers, None)?; + + let response = self .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let response = serde_json::from_str::(&resp_body)?; + + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + let response = serde_json::from_slice::(body)?; + Ok(response) }) } diff --git a/sdk/security_keyvault/src/keys/operations/sign.rs b/sdk/security_keyvault/src/keys/operations/sign.rs index ed339ead967..d008966d1b0 100644 --- a/sdk/security_keyvault/src/keys/operations/sign.rs +++ b/sdk/security_keyvault/src/keys/operations/sign.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, CollectedResponse, Method}; use serde_json::{Map, Value}; operation! { @@ -15,25 +16,31 @@ impl SignBuilder { Box::pin(async move { // POST {vaultBaseUrl}/keys/{key-name}/{key-version}/sign?api-version=7.1 let version = self.version.unwrap_or_default(); - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path(&format!("keys/{}/{}/sign", self.name, version)); - uri.set_query(Some(API_VERSION_PARAM)); let mut request_body = Map::new(); request_body.insert("alg".to_owned(), Value::String(self.algorithm.to_string())); request_body.insert("value".to_owned(), Value::String(self.digest.to_owned())); + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Post, + headers, + Some(Value::Object(request_body).to_string().into()), + )?; + let response = self .client - .client - .request( - reqwest::Method::POST, - uri.to_string(), - Some(Value::Object(request_body).to_string()), - ) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let mut result = serde_json::from_str::(&response)?; + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let mut result = serde_json::from_slice::(body)?; result.algorithm = self.algorithm; Ok(result) }) diff --git a/sdk/security_keyvault/src/lib.rs b/sdk/security_keyvault/src/lib.rs index 94cc7985ab8..9a67971ec53 100644 --- a/sdk/security_keyvault/src/lib.rs +++ b/sdk/security_keyvault/src/lib.rs @@ -9,44 +9,3 @@ pub mod prelude; mod secrets; pub use clients::*; - -#[cfg(test)] -mod tests { - use azure_core::auth::AccessToken; - use azure_core::auth::{TokenCredential, TokenResponse}; - use azure_core::date; - use time::OffsetDateTime; - - #[macro_export] - macro_rules! mock_client { - ($keyvault_name:expr, $creds:expr) => {{ - $crate::KeyvaultClient { - vault_url: url::Url::parse(&mockito::server_url()).unwrap(), - endpoint: "".to_string(), - token_credential: $creds, - token: None, - } - }}; - } - - pub(crate) struct MockCredential(()); - impl MockCredential { - pub(crate) fn new() -> std::sync::Arc { - std::sync::Arc::new(Self(())) - } - } - - #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] - impl TokenCredential for MockCredential { - async fn get_token( - &self, - _resource: &str, - ) -> Result { - Ok(TokenResponse::new( - AccessToken::new("TOKEN".to_owned()), - OffsetDateTime::now_utc() + date::duration_from_days(14), - )) - } - } -} diff --git a/sdk/security_keyvault/src/secrets/models.rs b/sdk/security_keyvault/src/secrets/models.rs index 514732d7cff..95301eb6387 100644 --- a/sdk/security_keyvault/src/secrets/models.rs +++ b/sdk/security_keyvault/src/secrets/models.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use time::OffsetDateTime; #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultSecretBaseIdentifierAttributedRaw { +pub struct KeyVaultSecretBaseIdentifierAttributedRaw { pub enabled: bool, #[serde(with = "azure_core::date::timestamp")] pub created: OffsetDateTime, @@ -11,34 +11,35 @@ pub(crate) struct KeyVaultSecretBaseIdentifierAttributedRaw { } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultSecretBaseIdentifierRaw { +pub struct KeyVaultSecretBaseIdentifierRaw { pub id: String, pub attributes: KeyVaultSecretBaseIdentifierAttributedRaw, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetSecretsResponse { +pub struct KeyVaultGetSecretsResponse { pub value: Vec, #[serde(rename = "nextLink")] pub next_link: Option, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetSecretResponse { +pub struct KeyVaultGetSecretResponse { pub value: String, pub id: String, pub attributes: KeyVaultGetSecretResponseAttributes, } #[derive(Deserialize, Debug)] -pub(crate) struct KeyVaultGetSecretResponseAttributes { +pub struct KeyVaultGetSecretResponseAttributes { pub enabled: bool, - #[serde(default, with = "azure_core::date::timestamp::option")] - pub exp: Option, - #[serde(with = "azure_core::date::timestamp")] - pub created: OffsetDateTime, - #[serde(with = "azure_core::date::timestamp")] - pub updated: OffsetDateTime, + #[serde(default)] + #[serde(with = "azure_core::date::timestamp::option", rename = "exp")] + pub expires_on: Option, + #[serde(with = "azure_core::date::timestamp", rename = "created")] + pub created_on: OffsetDateTime, + #[serde(with = "azure_core::date::timestamp", rename = "updated")] + pub updated_on: OffsetDateTime, #[serde(rename = "recoveryLevel")] #[allow(unused)] pub recovery_level: String, @@ -57,13 +58,3 @@ pub struct KeyVaultSecretBaseIdentifier { pub created_on: OffsetDateTime, pub updated_on: OffsetDateTime, } - -#[derive(Debug)] -pub struct KeyVaultSecret { - pub id: String, - pub value: String, - pub enabled: bool, - pub expires_on: Option, - pub created_on: OffsetDateTime, - pub updated_on: OffsetDateTime, -} diff --git a/sdk/security_keyvault/src/secrets/operations/backup_secret.rs b/sdk/security_keyvault/src/secrets/operations/backup_secret.rs index 78b57828b87..369ce77a38c 100644 --- a/sdk/security_keyvault/src/secrets/operations/backup_secret.rs +++ b/sdk/security_keyvault/src/secrets/operations/backup_secret.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, CollectedResponse, Method}; use serde::Deserialize; operation! { @@ -11,25 +11,26 @@ operation! { impl BackupSecretBuilder { pub fn into_future(mut self) -> BackupSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path(&format!("secrets/{}/backup", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); - let response_body = self - .client + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Post, headers, None)?; + + let response = self .client - .request(reqwest::Method::POST, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - serde_json::from_str::(&response_body).with_context( - ErrorKind::DataConversion, - || { - format!( - "failed to parse BackupSecretResponse. secret_name: {}", - self.name - ) - }, - ) + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + + let response = serde_json::from_slice::(body)?; + Ok(response) }) } } diff --git a/sdk/security_keyvault/src/secrets/operations/delete_secret.rs b/sdk/security_keyvault/src/secrets/operations/delete_secret.rs index c675081867c..052f575ff4d 100644 --- a/sdk/security_keyvault/src/secrets/operations/delete_secret.rs +++ b/sdk/security_keyvault/src/secrets/operations/delete_secret.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use azure_core::{headers::Headers, Method}; operation! { DeleteSecret, @@ -9,13 +10,18 @@ operation! { impl DeleteSecretBuilder { pub fn into_future(mut self) -> DeleteSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path(&format!("secrets/{}", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); + + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Delete, headers, None)?; self.client - .client - .request(reqwest::Method::DELETE, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; Ok(()) diff --git a/sdk/security_keyvault/src/secrets/operations/get_secret.rs b/sdk/security_keyvault/src/secrets/operations/get_secret.rs index 9d541e3234a..fd8cf21bcef 100644 --- a/sdk/security_keyvault/src/secrets/operations/get_secret.rs +++ b/sdk/security_keyvault/src/secrets/operations/get_secret.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, CollectedResponse, Method}; operation! { GetSecret, @@ -11,32 +11,28 @@ operation! { impl GetSecretBuilder { pub fn into_future(mut self) -> GetSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let version = self.version.unwrap_or_default(); uri.set_path(&format!("secrets/{}/{}", self.name, version)); - uri.set_query(Some(API_VERSION_PARAM)); + let headers = Headers::new(); - let response_body = self - .client + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Get, headers, None)?; + + let response = self .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut self.context, &mut request) .await?; - let response = serde_json::from_str::(&response_body) - .with_context(ErrorKind::DataConversion, || { - format!( - "failed to parse KeyVaultGetSecretResponse. secret_name: {} secret_version_name: {version} response_body: {response_body}", self.name - ) - })?; - Ok(KeyVaultSecret { - expires_on: response.attributes.exp, - enabled: response.attributes.enabled, - value: response.value, - created_on: response.attributes.created, - updated_on: response.attributes.updated, - id: response.id, - }) + + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); + let response = serde_json::from_slice::(body)?; + Ok(response) }) } } -type GetSecretResponse = KeyVaultSecret; +type GetSecretResponse = KeyVaultGetSecretResponse; diff --git a/sdk/security_keyvault/src/secrets/operations/get_versions.rs b/sdk/security_keyvault/src/secrets/operations/get_versions.rs index 8e3dac24c4e..12935b14af0 100644 --- a/sdk/security_keyvault/src/secrets/operations/get_versions.rs +++ b/sdk/security_keyvault/src/secrets/operations/get_versions.rs @@ -1,66 +1,48 @@ use crate::prelude::*; -use crate::secrets::models::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{error::Error, headers::Headers, CollectedResponse, Method, Pageable}; use url::Url; operation! { + #[stream] GetSecretVersions, client: SecretClient, name: String, } impl GetSecretVersionsBuilder { - pub fn into_future(mut self) -> GetSecretVersions { - Box::pin(async move { - let mut secret_versions = Vec::::new(); + pub fn into_stream(self) -> Pageable { + let make_request = move |continuation: Option| { + let this = self.clone(); + let mut ctx = self.context.clone(); + async move { + let mut uri = this.client.keyvault_client.vault_url.clone(); + uri.set_path(&format!("secrets/{}/versions", this.name)); - let mut uri = self.client.client.vault_url.clone(); - uri.set_path(&format!("secrets/{}/versions", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); + if let Some(continuation) = continuation { + uri = Url::parse(&continuation)?; + } - loop { - let resp_body = self - .client + let headers = Headers::new(); + let mut request = this.client.keyvault_client.finalize_request( + uri, + Method::Get, + headers, + None, + )?; + + let response = this .client - .request(reqwest::Method::GET, uri.to_string(), None) + .keyvault_client + .send(&mut ctx, &mut request) .await?; - let response = serde_json::from_str::(&resp_body) - .with_context(ErrorKind::DataConversion, || { - format!( - "failed to parse KeyVaultGetSecretsResponse. resp_body: {resp_body}" - ) - })?; - secret_versions.extend( - response - .value - .into_iter() - .map(|s| KeyVaultSecretBaseIdentifier { - id: s.id.to_owned(), - name: s.id.split('/').last().unwrap().to_owned(), - enabled: s.attributes.enabled, - created_on: s.attributes.created, - updated_on: s.attributes.updated, - }) - .collect::>(), - ); - match response.next_link { - None => break, - Some(u) => uri = Url::parse(&u)?, - } - } + let response = CollectedResponse::from_response(response).await?; + let body = response.body(); - // Return the secret versions sorted by the time modified in descending order. - secret_versions.sort_by(|a, b| { - if a.updated_on > b.updated_on { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - } - }); - Ok(secret_versions) - }) + let response = serde_json::from_slice::(body)?; + Ok(response) + } + }; + Pageable::new(make_request) } } - -type GetSecretVersionsResponse = Vec; diff --git a/sdk/security_keyvault/src/secrets/operations/set_secret.rs b/sdk/security_keyvault/src/secrets/operations/set_secret.rs index 0b63133457b..4f56e5bbe1e 100644 --- a/sdk/security_keyvault/src/secrets/operations/set_secret.rs +++ b/sdk/security_keyvault/src/secrets/operations/set_secret.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, Method}; use serde_json::{Map, Value}; operation! { @@ -12,25 +12,25 @@ operation! { impl SetSecretBuilder { pub fn into_future(mut self) -> SetSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); uri.set_path(&format!("secrets/{}", self.name)); - uri.set_query(Some(API_VERSION_PARAM)); let mut request_body = Map::new(); request_body.insert("value".to_string(), Value::String(self.value)); + let body = Some(Value::Object(request_body).to_string().into()); + + let headers = Headers::new(); + let mut request = + self.client + .keyvault_client + .finalize_request(uri, Method::Put, headers, body)?; + self.client - .client - .request( - reqwest::Method::PUT, - uri.to_string(), - Some(Value::Object(request_body).to_string()), - ) - .await - .with_context(ErrorKind::Other, || { - format!("failed to set secret. secret_name: {}", self.name) - })?; + .keyvault_client + .send(&mut self.context, &mut request) + .await?; Ok(()) }) diff --git a/sdk/security_keyvault/src/secrets/operations/update_secret.rs b/sdk/security_keyvault/src/secrets/operations/update_secret.rs index 1e732324f75..e1028339742 100644 --- a/sdk/security_keyvault/src/secrets/operations/update_secret.rs +++ b/sdk/security_keyvault/src/secrets/operations/update_secret.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use azure_core::error::{ErrorKind, ResultExt}; +use azure_core::{headers::Headers, Method}; use serde::Serialize; use std::collections::HashMap; use time::OffsetDateTime; @@ -40,10 +40,9 @@ struct UpdateRequest { impl UpdateSecretBuilder { pub fn into_future(mut self) -> UpdateSecret { Box::pin(async move { - let mut uri = self.client.client.vault_url.clone(); + let mut uri = self.client.keyvault_client.vault_url.clone(); let version = self.version.unwrap_or_default(); uri.set_path(&format!("secrets/{}/{}", self.name, version)); - uri.set_query(Some(API_VERSION_PARAM)); let request = UpdateRequest { content_type: self.content_type, @@ -56,21 +55,20 @@ impl UpdateSecretBuilder { tags: self.tags, }; - let body = serde_json::to_string(&request) - .with_context(ErrorKind::Other, || { - format!( - "failed to serialize UpdateRequest. secret_name: {} secret_version_name: {version}", - self.name - ) - })?; + let body = serde_json::to_string(&request)?; + + let headers = Headers::new(); + let mut request = self.client.keyvault_client.finalize_request( + uri, + Method::Patch, + headers, + Some(body.into()), + )?; self.client - .client - .request(reqwest::Method::PATCH, uri.to_string(), Some(body)) - .await - .with_context(ErrorKind::Other, || { - format!("failed to set secret. secret_name: {}", self.name) - })?; + .keyvault_client + .send(&mut self.context, &mut request) + .await?; Ok(()) })