openstack_keystone_core/k8s_auth/
service.rs1use std::collections::HashMap;
17use std::sync::Arc;
18
19use async_trait::async_trait;
20use reqwest::Client;
21use tokio::sync::RwLock;
22
23use crate::k8s_auth::{K8sAuthProviderError, backend::K8sAuthBackend, types::*};
24use crate::keystone::ServiceState;
25use crate::plugin_manager::PluginManagerApi;
26use crate::token::types::TokenRestriction;
27use crate::{auth::AuthenticatedInfo, config::Config};
28
29pub struct K8sAuthService {
31 pub(super) backend_driver: Arc<dyn K8sAuthBackend>,
33
34 pub(super) http_clients: RwLock<HashMap<String, Arc<Client>>>,
36}
37
38impl K8sAuthService {
39 pub fn new<P: PluginManagerApi>(
40 config: &Config,
41 plugin_manager: &P,
42 ) -> Result<Self, K8sAuthProviderError> {
43 let backend_driver = plugin_manager
44 .get_k8s_auth_backend(config.k8s_auth.driver.clone())?
45 .clone();
46 Ok(Self {
47 backend_driver,
48 http_clients: RwLock::new(HashMap::new()),
49 })
50 }
51}
52
53#[async_trait]
54impl K8sAuthApi for K8sAuthService {
55 async fn authenticate_by_k8s_sa_token(
57 &self,
58 state: &ServiceState,
59 req: &K8sAuthRequest,
60 ) -> Result<(AuthenticatedInfo, TokenRestriction), K8sAuthProviderError> {
61 self.authenticate(state, req).await
62 }
63
64 #[tracing::instrument(skip(self, state))]
66 async fn create_auth_instance(
67 &self,
68 state: &ServiceState,
69 instance: K8sAuthInstanceCreate,
70 ) -> Result<K8sAuthInstance, K8sAuthProviderError> {
71 let mut new = instance;
72 if new.id.is_none() {
73 new.id = Some(uuid::Uuid::new_v4().simple().to_string());
74 }
75 self.backend_driver.create_auth_instance(state, new).await
76 }
77
78 #[tracing::instrument(skip(self, state))]
80 async fn create_auth_role(
81 &self,
82 state: &ServiceState,
83 role: K8sAuthRoleCreate,
84 ) -> Result<K8sAuthRole, K8sAuthProviderError> {
85 let mut new = role;
86 if new.id.is_none() {
87 new.id = Some(uuid::Uuid::new_v4().simple().to_string());
88 }
89 self.backend_driver.create_auth_role(state, new).await
90 }
91
92 #[tracing::instrument(skip(self, state))]
94 async fn delete_auth_instance<'a>(
95 &self,
96 state: &ServiceState,
97 id: &'a str,
98 ) -> Result<(), K8sAuthProviderError> {
99 self.backend_driver.delete_auth_instance(state, id).await
100 }
101
102 #[tracing::instrument(skip(self, state))]
104 async fn delete_auth_role<'a>(
105 &self,
106 state: &ServiceState,
107 id: &'a str,
108 ) -> Result<(), K8sAuthProviderError> {
109 self.backend_driver.delete_auth_role(state, id).await
110 }
111
112 #[tracing::instrument(skip(self, state))]
114 async fn get_auth_instance<'a>(
115 &self,
116 state: &ServiceState,
117 id: &'a str,
118 ) -> Result<Option<K8sAuthInstance>, K8sAuthProviderError> {
119 self.backend_driver.get_auth_instance(state, id).await
120 }
121
122 #[tracing::instrument(skip(self, state))]
124 async fn get_auth_role<'a>(
125 &self,
126 state: &ServiceState,
127 id: &'a str,
128 ) -> Result<Option<K8sAuthRole>, K8sAuthProviderError> {
129 self.backend_driver.get_auth_role(state, id).await
130 }
131
132 #[tracing::instrument(skip(self, state))]
134 async fn list_auth_instances(
135 &self,
136 state: &ServiceState,
137 params: &K8sAuthInstanceListParameters,
138 ) -> Result<Vec<K8sAuthInstance>, K8sAuthProviderError> {
139 self.backend_driver.list_auth_instances(state, params).await
140 }
141
142 #[tracing::instrument(skip(self, state))]
144 async fn list_auth_roles(
145 &self,
146 state: &ServiceState,
147 params: &K8sAuthRoleListParameters,
148 ) -> Result<Vec<K8sAuthRole>, K8sAuthProviderError> {
149 self.backend_driver.list_auth_roles(state, params).await
150 }
151
152 #[tracing::instrument(skip(self, state))]
154 async fn update_auth_instance<'a>(
155 &self,
156 state: &ServiceState,
157 id: &'a str,
158 data: K8sAuthInstanceUpdate,
159 ) -> Result<K8sAuthInstance, K8sAuthProviderError> {
160 self.backend_driver
161 .update_auth_instance(state, id, data)
162 .await
163 }
164
165 #[tracing::instrument(skip(self, state))]
167 async fn update_auth_role<'a>(
168 &self,
169 state: &ServiceState,
170 id: &'a str,
171 data: K8sAuthRoleUpdate,
172 ) -> Result<K8sAuthRole, K8sAuthProviderError> {
173 self.backend_driver.update_auth_role(state, id, data).await
174 }
175}
176
177#[cfg(test)]
178pub(crate) mod tests {
179 use std::sync::Arc;
180
181 use super::*;
182 use crate::k8s_auth::backend::MockK8sAuthBackend;
183 use crate::tests::get_mocked_state;
184
185 #[tokio::test]
186 async fn test_create_auth_instance() {
187 let state = get_mocked_state(None, None);
188 let mut backend = MockK8sAuthBackend::default();
189 backend
190 .expect_create_auth_instance()
191 .returning(|_, _| Ok(K8sAuthInstance::default()));
192 let provider = K8sAuthService {
193 backend_driver: Arc::new(backend),
194 http_clients: RwLock::new(HashMap::new()),
195 };
196
197 assert!(
198 provider
199 .create_auth_instance(
200 &state,
201 K8sAuthInstanceCreate {
202 ca_cert: Some("ca".into()),
203 disable_local_ca_jwt: Some(true),
204 domain_id: "did".into(),
205 enabled: true,
206 host: "host".into(),
207 id: Some("id".into()),
208 name: Some("name".into()),
209 }
210 )
211 .await
212 .is_ok()
213 );
214 }
215
216 #[tokio::test]
217 async fn test_create_auth_role() {
218 let state = get_mocked_state(None, None);
219 let mut backend = MockK8sAuthBackend::default();
220 backend
221 .expect_create_auth_role()
222 .returning(|_, _| Ok(K8sAuthRole::default()));
223 let provider = K8sAuthService {
224 backend_driver: Arc::new(backend),
225 http_clients: RwLock::new(HashMap::new()),
226 };
227
228 assert!(
229 provider
230 .create_auth_role(
231 &state,
232 K8sAuthRoleCreate {
233 auth_instance_id: "cid".into(),
234 bound_audience: Some("aud".into()),
235 bound_service_account_names: vec!["a".into(), "b".into()],
236 bound_service_account_namespaces: vec!["na".into(), "nb".into()],
237 domain_id: "did".into(),
238 enabled: true,
239 id: Some("id".into()),
240 name: "name".into(),
241 token_restriction_id: "trid".into(),
242 }
243 )
244 .await
245 .is_ok()
246 );
247 }
248}