ydb_unofficial/auth/
sa.rs1use std::sync::{Arc, RwLock};
2use std::time::{UNIX_EPOCH, SystemTime, Duration};
3
4use jwt_simple::prelude::{Claims, RSAKeyPairLike};
5pub use jwt_simple::prelude::PS256KeyPair;
6use serde::Deserialize;
7use tonic::transport::Uri;
8use yandex_cloud::yandex::cloud::iam::v1::CreateIamTokenResponse;
9
10use crate::AsciiValue;
11use super::{Credentials, UpdatableToken};
12
13#[derive(Debug, Clone, Deserialize)]
14pub struct ServiceAccountKey {
15 pub id: String,
16 pub service_account_id: String,
17 #[serde(with="ps256_private_key")]
18 pub private_key: PS256KeyPair,
19}
20
21#[derive(Debug, Clone)]
22pub struct UpdateConfig {
23 pub endpoint: Uri,
25 pub audience: String,
27 pub update_period: Duration,
29 pub update_time_reserve: Duration,
31 pub token_request_claim_time: Duration,
33}
34
35impl Default for UpdateConfig {
36 fn default() -> Self {
37 Self {
38 endpoint: "grpcs://iam.api.cloud.yandex.net:443".parse().unwrap(),
39 audience: "https://iam.api.cloud.yandex.net/iam/v1/tokens".into(),
40 update_period: Duration::from_secs(50*60),
41 update_time_reserve: Duration::from_secs(60),
42 token_request_claim_time: Duration::from_secs(60),
43 }
44 }
45}
46
47#[derive(Clone)]
48pub struct ServiceAccountCredentials {
49 token: Arc<RwLock<AsciiValue>>,
50}
51
52impl Credentials for ServiceAccountCredentials {
53 fn token(&self) -> crate::AsciiValue {
54 self.token.read().unwrap().clone()
55 }
56}
57impl Into<UpdatableToken> for ServiceAccountCredentials {
58 fn into(self) -> UpdatableToken {
59 let Self{token} = self;
60 UpdatableToken { token }
61 }
62}
63
64impl ServiceAccountCredentials {
65 pub async fn create(key: ServiceAccountKey) -> Result<Self, tonic::Status> {
66 Self::create_with_config(Default::default(), key).await
67 }
68 pub async fn create_with_config(conf: UpdateConfig, key: ServiceAccountKey) -> Result<Self, tonic::Status> {
69 let response = conf.request_iam_token(&key).await?;
71 let mut sleep_duration = conf.invoke_sleep_duration(&response);
72 let token = Arc::new(RwLock::new(response.iam_token.clone().try_into().unwrap()));
73 let update_me = Arc::downgrade(&token);
74 tokio::spawn(async move {
75 loop {
76 tokio::time::sleep(sleep_duration).await;
77 if let Some(token) = update_me.upgrade() {
78 match conf.request_iam_token(&key).await {
79 Ok(response) => {
80 sleep_duration = conf.invoke_sleep_duration(&response);
81 *token.write().unwrap() = response.iam_token.clone().try_into().unwrap();
82 log::info!("Iam token updated");
83 }
84 Err(e) => {
85 log::error!("Cannot update iam token: {:?}", e);
86 sleep_duration = Duration::from_secs(5);
87 }
88 }
89 } else {
90 log::info!("ServiceAccountCredentials removed");
91 break;
92 }
93 }
94 });
95 Ok(Self {token})
96 }
97}
98
99
100impl UpdateConfig {
101 pub async fn request_iam_token(&self, key: &ServiceAccountKey) -> Result<CreateIamTokenResponse, tonic::Status> {
102 let jwt = self.make_jwt(key);
103 let endpoint = crate::client::create_endpoint(self.endpoint.clone());
104 let mut client = yandex_cloud::yandex::cloud::iam::v1::iam_token_service_client::IamTokenServiceClient::new(endpoint.connect_lazy());
105 let request = yandex_cloud::yandex::cloud::iam::v1::CreateIamTokenRequest {
106 identity: Some(yandex_cloud::yandex::cloud::iam::v1::create_iam_token_request::Identity::Jwt(jwt))
107 };
108 let resp = client.create(request).await?;
109 Ok(resp.into_inner())
110 }
111
112 pub fn make_jwt(&self, key: &ServiceAccountKey) -> String {
113 let claims = Claims::create(self.token_request_claim_time.into())
114 .with_issuer(&key.service_account_id)
115 .with_audience(&self.audience);
116 let pair = key.private_key.clone().with_key_id(&key.id);
117 let token = pair.sign(claims).unwrap();
118 token
119 }
120 pub fn invoke_sleep_duration(&self, response: &CreateIamTokenResponse) -> tokio::time::Duration {
121 let CreateIamTokenResponse {iam_token: _, expires_at} = response;
122 let expires = if let Some(ts) = expires_at {
123 (UNIX_EPOCH + Duration::from_secs(ts.seconds as u64)) .duration_since(SystemTime::now() + self.update_time_reserve) .unwrap_or_default()
126 } else {
127 self.update_period
128 };
129 expires.into()
130 }
131}
132
133mod ps256_private_key {
134 use jwt_simple::prelude::PS256KeyPair;
135 use serde::{self, Deserialize, Deserializer};
136 pub fn deserialize<'de, D>(
137 deserializer: D,
138 ) -> Result<PS256KeyPair, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 let s = String::deserialize(deserializer)?;
143 PS256KeyPair::from_pem(&s).map_err(serde::de::Error::custom)
144 }
145}