1use async_trait::async_trait;
37use chrono::{DateTime, Utc};
38use std::collections::HashSet;
39
40pub mod backend;
41pub mod error;
42#[cfg(any(test, feature = "mock"))]
43pub mod mock;
44pub mod types;
45#[cfg(any(test, feature = "mock"))]
46pub use mock::MockIdentityProvider;
47pub mod service;
48
49use crate::auth::AuthenticatedInfo;
50use crate::config::Config;
51use crate::identity::types::*;
52use crate::keystone::ServiceState;
53use crate::plugin_manager::PluginManagerApi;
54use service::IdentityService;
55
56pub use error::IdentityProviderError;
57pub use types::IdentityApi;
58
59pub enum IdentityProvider {
61 Service(IdentityService),
62 #[cfg(any(test, feature = "mock"))]
63 Mock(MockIdentityProvider),
64}
65
66impl IdentityProvider {
67 pub fn new<P: PluginManagerApi>(
68 config: &Config,
69 plugin_manager: &P,
70 ) -> Result<Self, IdentityProviderError> {
71 Ok(Self::Service(IdentityService::new(config, plugin_manager)?))
72 }
73}
74
75#[async_trait]
76impl IdentityApi for IdentityProvider {
77 #[tracing::instrument(skip(self, state))]
78 async fn add_user_to_group<'a>(
79 &self,
80 state: &ServiceState,
81 user_id: &'a str,
82 group_id: &'a str,
83 ) -> Result<(), IdentityProviderError> {
84 match self {
85 Self::Service(provider) => provider.add_user_to_group(state, user_id, group_id).await,
86 #[cfg(any(test, feature = "mock"))]
87 Self::Mock(provider) => provider.add_user_to_group(state, user_id, group_id).await,
88 }
89 }
90
91 #[tracing::instrument(skip(self, state))]
92 async fn add_user_to_group_expiring<'a>(
93 &self,
94 state: &ServiceState,
95 user_id: &'a str,
96 group_id: &'a str,
97 idp_id: &'a str,
98 ) -> Result<(), IdentityProviderError> {
99 match self {
100 Self::Service(provider) => {
101 provider
102 .add_user_to_group_expiring(state, user_id, group_id, idp_id)
103 .await
104 }
105 #[cfg(any(test, feature = "mock"))]
106 Self::Mock(provider) => {
107 provider
108 .add_user_to_group_expiring(state, user_id, group_id, idp_id)
109 .await
110 }
111 }
112 }
113
114 #[tracing::instrument(skip(self, state))]
115 async fn add_users_to_groups<'a>(
116 &self,
117 state: &ServiceState,
118 memberships: Vec<(&'a str, &'a str)>,
119 ) -> Result<(), IdentityProviderError> {
120 match self {
121 Self::Service(provider) => provider.add_users_to_groups(state, memberships).await,
122 #[cfg(any(test, feature = "mock"))]
123 Self::Mock(provider) => provider.add_users_to_groups(state, memberships).await,
124 }
125 }
126
127 #[tracing::instrument(skip(self, state))]
128 async fn add_users_to_groups_expiring<'a>(
129 &self,
130 state: &ServiceState,
131 memberships: Vec<(&'a str, &'a str)>,
132 idp_id: &'a str,
133 ) -> Result<(), IdentityProviderError> {
134 match self {
135 Self::Service(provider) => {
136 provider
137 .add_users_to_groups_expiring(state, memberships, idp_id)
138 .await
139 }
140 #[cfg(any(test, feature = "mock"))]
141 Self::Mock(provider) => {
142 provider
143 .add_users_to_groups_expiring(state, memberships, idp_id)
144 .await
145 }
146 }
147 }
148
149 #[tracing::instrument(skip(self, state, auth))]
151 async fn authenticate_by_password(
152 &self,
153 state: &ServiceState,
154 auth: &UserPasswordAuthRequest,
155 ) -> Result<AuthenticatedInfo, IdentityProviderError> {
156 match self {
157 Self::Service(provider) => provider.authenticate_by_password(state, auth).await,
158 #[cfg(any(test, feature = "mock"))]
159 Self::Mock(provider) => provider.authenticate_by_password(state, auth).await,
160 }
161 }
162
163 #[tracing::instrument(skip(self, state))]
165 async fn create_group(
166 &self,
167 state: &ServiceState,
168 group: GroupCreate,
169 ) -> Result<Group, IdentityProviderError> {
170 match self {
171 Self::Service(provider) => provider.create_group(state, group).await,
172 #[cfg(any(test, feature = "mock"))]
173 Self::Mock(provider) => provider.create_group(state, group).await,
174 }
175 }
176
177 #[tracing::instrument(skip(self, state))]
179 async fn create_service_account(
180 &self,
181 state: &ServiceState,
182 sa: ServiceAccountCreate,
183 ) -> Result<ServiceAccount, IdentityProviderError> {
184 match self {
185 Self::Service(provider) => provider.create_service_account(state, sa).await,
186 #[cfg(any(test, feature = "mock"))]
187 Self::Mock(provider) => provider.create_service_account(state, sa).await,
188 }
189 }
190
191 #[tracing::instrument(skip(self, state))]
193 async fn create_user(
194 &self,
195 state: &ServiceState,
196 user: UserCreate,
197 ) -> Result<UserResponse, IdentityProviderError> {
198 match self {
199 Self::Service(provider) => provider.create_user(state, user).await,
200 #[cfg(any(test, feature = "mock"))]
201 Self::Mock(provider) => provider.create_user(state, user).await,
202 }
203 }
204
205 #[tracing::instrument(skip(self, state))]
207 async fn delete_group<'a>(
208 &self,
209 state: &ServiceState,
210 group_id: &'a str,
211 ) -> Result<(), IdentityProviderError> {
212 match self {
213 Self::Service(provider) => provider.delete_group(state, group_id).await,
214 #[cfg(any(test, feature = "mock"))]
215 Self::Mock(provider) => provider.delete_group(state, group_id).await,
216 }
217 }
218
219 #[tracing::instrument(skip(self, state))]
221 async fn delete_user<'a>(
222 &self,
223 state: &ServiceState,
224 user_id: &'a str,
225 ) -> Result<(), IdentityProviderError> {
226 match self {
227 Self::Service(provider) => provider.delete_user(state, user_id).await,
228 #[cfg(any(test, feature = "mock"))]
229 Self::Mock(provider) => provider.delete_user(state, user_id).await,
230 }
231 }
232
233 #[tracing::instrument(skip(self, state))]
235 async fn get_service_account<'a>(
236 &self,
237 state: &ServiceState,
238 user_id: &'a str,
239 ) -> Result<Option<ServiceAccount>, IdentityProviderError> {
240 match self {
241 Self::Service(provider) => provider.get_service_account(state, user_id).await,
242 #[cfg(any(test, feature = "mock"))]
243 Self::Mock(provider) => provider.get_service_account(state, user_id).await,
244 }
245 }
246
247 #[tracing::instrument(skip(self, state))]
249 async fn get_user<'a>(
250 &self,
251 state: &ServiceState,
252 user_id: &'a str,
253 ) -> Result<Option<UserResponse>, IdentityProviderError> {
254 match self {
255 Self::Service(provider) => provider.get_user(state, user_id).await,
256 #[cfg(any(test, feature = "mock"))]
257 Self::Mock(provider) => provider.get_user(state, user_id).await,
258 }
259 }
260
261 async fn get_user_domain_id<'a>(
269 &self,
270 state: &ServiceState,
271 user_id: &'a str,
272 ) -> Result<String, IdentityProviderError> {
273 match self {
274 Self::Service(provider) => provider.get_user_domain_id(state, user_id).await,
275 #[cfg(any(test, feature = "mock"))]
276 Self::Mock(provider) => provider.get_user_domain_id(state, user_id).await,
277 }
278 }
279
280 #[tracing::instrument(skip(self, state))]
282 async fn find_federated_user<'a>(
283 &self,
284 state: &ServiceState,
285 idp_id: &'a str,
286 unique_id: &'a str,
287 ) -> Result<Option<UserResponse>, IdentityProviderError> {
288 match self {
289 Self::Service(provider) => provider.find_federated_user(state, idp_id, unique_id).await,
290 #[cfg(any(test, feature = "mock"))]
291 Self::Mock(provider) => provider.find_federated_user(state, idp_id, unique_id).await,
292 }
293 }
294
295 #[tracing::instrument(skip(self, state))]
297 async fn list_users(
298 &self,
299 state: &ServiceState,
300 params: &UserListParameters,
301 ) -> Result<Vec<UserResponse>, IdentityProviderError> {
302 match self {
303 Self::Service(provider) => provider.list_users(state, params).await,
304 #[cfg(any(test, feature = "mock"))]
305 Self::Mock(provider) => provider.list_users(state, params).await,
306 }
307 }
308
309 #[tracing::instrument(skip(self, state))]
311 async fn list_groups(
312 &self,
313 state: &ServiceState,
314 params: &GroupListParameters,
315 ) -> Result<Vec<Group>, IdentityProviderError> {
316 match self {
317 Self::Service(provider) => provider.list_groups(state, params).await,
318 #[cfg(any(test, feature = "mock"))]
319 Self::Mock(provider) => provider.list_groups(state, params).await,
320 }
321 }
322
323 #[tracing::instrument(skip(self, state))]
325 async fn get_group<'a>(
326 &self,
327 state: &ServiceState,
328 group_id: &'a str,
329 ) -> Result<Option<Group>, IdentityProviderError> {
330 match self {
331 Self::Service(provider) => provider.get_group(state, group_id).await,
332 #[cfg(any(test, feature = "mock"))]
333 Self::Mock(provider) => provider.get_group(state, group_id).await,
334 }
335 }
336
337 #[tracing::instrument(skip(self, state))]
339 async fn list_groups_of_user<'a>(
340 &self,
341 state: &ServiceState,
342 user_id: &'a str,
343 ) -> Result<Vec<Group>, IdentityProviderError> {
344 match self {
345 Self::Service(provider) => provider.list_groups_of_user(state, user_id).await,
346 #[cfg(any(test, feature = "mock"))]
347 Self::Mock(provider) => provider.list_groups_of_user(state, user_id).await,
348 }
349 }
350
351 #[tracing::instrument(skip(self, state))]
352 async fn remove_user_from_group<'a>(
353 &self,
354 state: &ServiceState,
355 user_id: &'a str,
356 group_id: &'a str,
357 ) -> Result<(), IdentityProviderError> {
358 match self {
359 Self::Service(provider) => {
360 provider
361 .remove_user_from_group(state, user_id, group_id)
362 .await
363 }
364 #[cfg(any(test, feature = "mock"))]
365 Self::Mock(provider) => {
366 provider
367 .remove_user_from_group(state, user_id, group_id)
368 .await
369 }
370 }
371 }
372
373 #[tracing::instrument(skip(self, state))]
374 async fn remove_user_from_group_expiring<'a>(
375 &self,
376 state: &ServiceState,
377 user_id: &'a str,
378 group_id: &'a str,
379 idp_id: &'a str,
380 ) -> Result<(), IdentityProviderError> {
381 match self {
382 Self::Service(provider) => {
383 provider
384 .remove_user_from_group_expiring(state, user_id, group_id, idp_id)
385 .await
386 }
387 #[cfg(any(test, feature = "mock"))]
388 Self::Mock(provider) => {
389 provider
390 .remove_user_from_group_expiring(state, user_id, group_id, idp_id)
391 .await
392 }
393 }
394 }
395
396 #[tracing::instrument(skip(self, state))]
397 async fn remove_user_from_groups<'a>(
398 &self,
399 state: &ServiceState,
400 user_id: &'a str,
401 group_ids: HashSet<&'a str>,
402 ) -> Result<(), IdentityProviderError> {
403 match self {
404 Self::Service(provider) => {
405 provider
406 .remove_user_from_groups(state, user_id, group_ids)
407 .await
408 }
409 #[cfg(any(test, feature = "mock"))]
410 Self::Mock(provider) => {
411 provider
412 .remove_user_from_groups(state, user_id, group_ids)
413 .await
414 }
415 }
416 }
417
418 #[tracing::instrument(skip(self, state))]
419 async fn remove_user_from_groups_expiring<'a>(
420 &self,
421 state: &ServiceState,
422 user_id: &'a str,
423 group_ids: HashSet<&'a str>,
424 idp_id: &'a str,
425 ) -> Result<(), IdentityProviderError> {
426 match self {
427 Self::Service(provider) => {
428 provider
429 .remove_user_from_groups_expiring(state, user_id, group_ids, idp_id)
430 .await
431 }
432 #[cfg(any(test, feature = "mock"))]
433 Self::Mock(provider) => {
434 provider
435 .remove_user_from_groups_expiring(state, user_id, group_ids, idp_id)
436 .await
437 }
438 }
439 }
440
441 #[tracing::instrument(skip(self, state))]
442 async fn set_user_groups<'a>(
443 &self,
444 state: &ServiceState,
445 user_id: &'a str,
446 group_ids: HashSet<&'a str>,
447 ) -> Result<(), IdentityProviderError> {
448 match self {
449 Self::Service(provider) => provider.set_user_groups(state, user_id, group_ids).await,
450 #[cfg(any(test, feature = "mock"))]
451 Self::Mock(provider) => provider.set_user_groups(state, user_id, group_ids).await,
452 }
453 }
454
455 #[tracing::instrument(skip(self, state))]
456 async fn set_user_groups_expiring<'a>(
457 &self,
458 state: &ServiceState,
459 user_id: &'a str,
460 group_ids: HashSet<&'a str>,
461 idp_id: &'a str,
462 last_verified: Option<&'a DateTime<Utc>>,
463 ) -> Result<(), IdentityProviderError> {
464 match self {
465 Self::Service(provider) => {
466 provider
467 .set_user_groups_expiring(state, user_id, group_ids, idp_id, last_verified)
468 .await
469 }
470 #[cfg(any(test, feature = "mock"))]
471 Self::Mock(provider) => {
472 provider
473 .set_user_groups_expiring(state, user_id, group_ids, idp_id, last_verified)
474 .await
475 }
476 }
477 }
478}