openstack_keystone_core/application_credential/
service.rs1use std::collections::BTreeMap;
16use std::sync::Arc;
17
18use async_trait::async_trait;
19use base64::{Engine as _, engine::general_purpose};
20use rand::{RngExt, rng};
21use secrecy::SecretString;
22use uuid::Uuid;
23use validator::Validate;
24
25use crate::application_credential::{
26 ApplicationCredentialProviderError, backend::ApplicationCredentialBackend, types::*,
27};
28use crate::config::Config;
29use crate::keystone::ServiceState;
30use crate::plugin_manager::PluginManagerApi;
31use crate::role::{
32 RoleApi,
33 types::{Role, RoleListParameters},
34};
35
36pub struct ApplicationCredentialService {
38 backend_driver: Arc<dyn ApplicationCredentialBackend>,
39}
40
41impl ApplicationCredentialService {
42 pub fn new<P: PluginManagerApi>(
43 config: &Config,
44 plugin_manager: &P,
45 ) -> Result<Self, ApplicationCredentialProviderError> {
46 let backend_driver = plugin_manager
47 .get_application_credential_backend(config.application_credential.driver.clone())?
48 .clone();
49 Ok(Self { backend_driver })
50 }
51}
52
53#[async_trait]
54impl ApplicationCredentialApi for ApplicationCredentialService {
55 async fn create_application_credential(
57 &self,
58 state: &ServiceState,
59 rec: ApplicationCredentialCreate,
60 ) -> Result<ApplicationCredentialCreateResponse, ApplicationCredentialProviderError> {
61 rec.validate()?;
62 let mut new_rec = rec;
64 if new_rec.id.is_none() {
65 new_rec.id = Some(Uuid::new_v4().simple().to_string());
66 }
67 if let Some(ref mut rules) = new_rec.access_rules {
68 for rule in rules {
69 if rule.id.is_none() {
70 rule.id = Some(Uuid::new_v4().simple().to_string());
71 }
72 }
73 }
74 if new_rec.secret.is_none() {
75 new_rec.secret = Some(generate_secret());
76 }
77 self.backend_driver
78 .create_application_credential(state, new_rec)
79 .await
80 }
81
82 #[tracing::instrument(level = "info", skip(self, state))]
84 async fn get_application_credential<'a>(
85 &self,
86 state: &ServiceState,
87 id: &'a str,
88 ) -> Result<Option<ApplicationCredential>, ApplicationCredentialProviderError> {
89 if let Some(mut app_cred) = self
90 .backend_driver
91 .get_application_credential(state, id)
92 .await?
93 {
94 let roles: BTreeMap<String, Role> = state
95 .provider
96 .get_role_provider()
97 .list_roles(state, &RoleListParameters::default())
98 .await?
99 .into_iter()
100 .map(|x| (x.id.clone(), x))
101 .collect();
102 for cred_role in app_cred.roles.iter_mut() {
103 if let Some(role) = roles.get(&cred_role.id) {
104 cred_role.name = Some(role.name.clone());
105 cred_role.domain_id = role.domain_id.clone();
106 }
107 }
108 Ok(Some(app_cred))
109 } else {
110 Ok(None)
111 }
112 }
113
114 #[tracing::instrument(level = "info", skip(self, state))]
116 async fn list_application_credentials(
117 &self,
118 state: &ServiceState,
119 params: &ApplicationCredentialListParameters,
120 ) -> Result<Vec<ApplicationCredential>, ApplicationCredentialProviderError> {
121 params.validate()?;
122 let mut creds = self
123 .backend_driver
124 .list_application_credentials(state, params)
125 .await?;
126
127 let roles: BTreeMap<String, Role> = state
128 .provider
129 .get_role_provider()
130 .list_roles(state, &RoleListParameters::default())
131 .await?
132 .into_iter()
133 .map(|x| (x.id.clone(), x))
134 .collect();
135 for cred in creds.iter_mut() {
136 for cred_role in cred.roles.iter_mut() {
137 if let Some(role) = roles.get(&cred_role.id) {
138 cred_role.name = Some(role.name.clone());
139 cred_role.domain_id = role.domain_id.clone();
140 }
141 }
142 }
143 Ok(creds)
144 }
145}
146
147pub fn generate_secret() -> SecretString {
154 const LENGTH: usize = 64;
155
156 let mut secret_bytes = [0u8; LENGTH];
159 rng().fill(&mut secret_bytes[..]);
160
161 let encoded_secret = general_purpose::URL_SAFE_NO_PAD.encode(secret_bytes);
164
165 SecretString::new(encoded_secret.into())
166}