Skip to main content

openstack_keystone_core/token/
service.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//! # Token provider.
15//!
16//! A Keystone token is an alpha-numeric text string that enables access to
17//! OpenStack APIs and resources. A token may be revoked at any time and is
18//! valid for a finite duration. OpenStack Identity is an integration service
19//! that does not aspire to be a full-fledged identity store and management
20//! solution.
21
22use async_trait::async_trait;
23use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
24use chrono::{DateTime, TimeDelta, Utc};
25use std::collections::HashSet;
26use std::sync::Arc;
27use tracing::{debug, trace};
28use uuid::Uuid;
29
30use crate::auth::{AuthenticatedInfo, AuthenticationError, AuthzInfo};
31use crate::config::{Config, TokenProviderDriver};
32use crate::identity::IdentityApi;
33use crate::keystone::ServiceState;
34use crate::plugin_manager::PluginManagerApi;
35use crate::resource::{
36    ResourceApi,
37    types::{Domain, Project},
38};
39use crate::revoke::RevokeApi;
40use crate::token::{
41    TokenProviderError,
42    backend::{TokenBackend, TokenRestrictionBackend, fernet::FernetTokenProvider},
43};
44use crate::{
45    application_credential::ApplicationCredentialApi,
46    assignment::{
47        AssignmentApi,
48        error::AssignmentProviderError,
49        types::{RoleAssignmentListParameters, RoleAssignmentListParametersBuilder},
50    },
51    role::{RoleApi, types::RoleRef},
52    trust::{TrustApi, types::Trust},
53};
54
55pub use crate::token::types::*;
56
57pub struct TokenService {
58    config: Config,
59    backend_driver: Arc<dyn TokenBackend>,
60    tr_backend_driver: Arc<dyn TokenRestrictionBackend>,
61}
62
63impl TokenService {
64    pub fn new<P: PluginManagerApi>(
65        config: &Config,
66        plugin_manager: &P,
67    ) -> Result<Self, TokenProviderError> {
68        let backend_driver = match config.token.provider {
69            TokenProviderDriver::Fernet => FernetTokenProvider::new(config.clone()),
70        };
71        let tr_backend_driver = plugin_manager
72            .get_token_restriction_backend(&config.token_restriction.driver)?
73            .clone();
74        Ok(Self {
75            config: config.clone(),
76            backend_driver: Arc::new(backend_driver),
77            tr_backend_driver,
78        })
79    }
80
81    fn get_new_token_expiry(
82        &self,
83        auth_expiration: &Option<DateTime<Utc>>,
84    ) -> Result<DateTime<Utc>, TokenProviderError> {
85        let default_expiry = Utc::now()
86            .checked_add_signed(TimeDelta::seconds(self.config.token.expiration as i64))
87            .ok_or(TokenProviderError::ExpiryCalculation)?;
88        Ok(auth_expiration
89            .map(|x| std::cmp::min(x, default_expiry))
90            .unwrap_or(default_expiry))
91    }
92
93    /// Create unscoped token.
94    fn create_unscoped_token(
95        &self,
96        authentication_info: &AuthenticatedInfo,
97    ) -> Result<Token, TokenProviderError> {
98        Ok(Token::Unscoped(
99            UnscopedPayloadBuilder::default()
100                .user_id(authentication_info.user_id.clone())
101                .user(authentication_info.user.clone())
102                .methods(authentication_info.methods.clone().iter())
103                .audit_ids(authentication_info.audit_ids.clone().iter())
104                .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
105                .build()?,
106        ))
107    }
108
109    /// Create project scoped token.
110    fn create_project_scope_token(
111        &self,
112        authentication_info: &AuthenticatedInfo,
113        project: &Project,
114    ) -> Result<Token, TokenProviderError> {
115        let token_expiry = self.get_new_token_expiry(&authentication_info.expires_at)?;
116        if let Some(application_credential) = &authentication_info.application_credential {
117            // Token for the application credential authentication
118            Ok(Token::ApplicationCredential(
119                ApplicationCredentialPayloadBuilder::default()
120                    .application_credential_id(application_credential.id.clone())
121                    .application_credential(application_credential.clone())
122                    .user_id(authentication_info.user_id.clone())
123                    .user(authentication_info.user.clone())
124                    .methods(authentication_info.methods.clone().iter())
125                    .audit_ids(authentication_info.audit_ids.clone().iter())
126                    .expires_at(
127                        application_credential
128                            .expires_at
129                            .map(|ac_expiry| std::cmp::min(token_expiry, ac_expiry))
130                            .unwrap_or(token_expiry),
131                    )
132                    .project_id(project.id.clone())
133                    .project(project.clone())
134                    .build()?,
135            ))
136        } else {
137            // General project scoped token
138            Ok(Token::ProjectScope(
139                ProjectScopePayloadBuilder::default()
140                    .user_id(authentication_info.user_id.clone())
141                    .user(authentication_info.user.clone())
142                    .methods(authentication_info.methods.clone().iter())
143                    .audit_ids(authentication_info.audit_ids.clone().iter())
144                    .expires_at(token_expiry)
145                    .project_id(project.id.clone())
146                    .project(project.clone())
147                    .build()?,
148            ))
149        }
150    }
151
152    /// Create domain scoped token.
153    fn create_domain_scope_token(
154        &self,
155        authentication_info: &AuthenticatedInfo,
156        domain: &Domain,
157    ) -> Result<Token, TokenProviderError> {
158        Ok(Token::DomainScope(
159            DomainScopePayloadBuilder::default()
160                .user_id(authentication_info.user_id.clone())
161                .user(authentication_info.user.clone())
162                .methods(authentication_info.methods.clone().iter())
163                .audit_ids(authentication_info.audit_ids.clone().iter())
164                .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
165                .domain_id(domain.id.clone())
166                .domain(domain.clone())
167                .build()?,
168        ))
169    }
170
171    /// Create unscoped token with the identity provider bind.
172    fn create_federated_unscoped_token(
173        &self,
174        authentication_info: &AuthenticatedInfo,
175    ) -> Result<Token, TokenProviderError> {
176        if let (Some(idp_id), Some(protocol_id)) = (
177            authentication_info.idp_id.clone(),
178            authentication_info.protocol_id.clone(),
179        ) {
180            Ok(Token::FederationUnscoped(
181                FederationUnscopedPayloadBuilder::default()
182                    .user_id(authentication_info.user_id.clone())
183                    .user(authentication_info.user.clone())
184                    .methods(authentication_info.methods.clone().iter())
185                    .audit_ids(authentication_info.audit_ids.clone().iter())
186                    .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
187                    .idp_id(idp_id)
188                    .protocol_id(protocol_id)
189                    .group_ids(vec![])
190                    .build()?,
191            ))
192        } else {
193            Err(TokenProviderError::FederatedPayloadMissingData)
194        }
195    }
196
197    /// Create project scoped token with the identity provider bind.
198    fn create_federated_project_scope_token(
199        &self,
200        authentication_info: &AuthenticatedInfo,
201        project: &Project,
202    ) -> Result<Token, TokenProviderError> {
203        if let (Some(idp_id), Some(protocol_id)) = (
204            authentication_info.idp_id.clone(),
205            authentication_info.protocol_id.clone(),
206        ) {
207            Ok(Token::FederationProjectScope(
208                FederationProjectScopePayloadBuilder::default()
209                    .user_id(authentication_info.user_id.clone())
210                    .user(authentication_info.user.clone())
211                    .methods(authentication_info.methods.clone().iter())
212                    .audit_ids(authentication_info.audit_ids.clone().iter())
213                    .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
214                    .idp_id(idp_id)
215                    .protocol_id(protocol_id)
216                    .group_ids(
217                        authentication_info
218                            .user_groups
219                            .clone()
220                            .iter()
221                            .map(|grp| grp.id.clone())
222                            .collect::<Vec<_>>(),
223                    )
224                    .project_id(project.id.clone())
225                    .project(project.clone())
226                    .build()?,
227            ))
228        } else {
229            Err(TokenProviderError::FederatedPayloadMissingData)
230        }
231    }
232
233    /// Create domain scoped token with the identity provider bind.
234    fn create_federated_domain_scope_token(
235        &self,
236        authentication_info: &AuthenticatedInfo,
237        domain: &Domain,
238    ) -> Result<Token, TokenProviderError> {
239        if let (Some(idp_id), Some(protocol_id)) = (
240            authentication_info.idp_id.clone(),
241            authentication_info.protocol_id.clone(),
242        ) {
243            Ok(Token::FederationDomainScope(
244                FederationDomainScopePayloadBuilder::default()
245                    .user_id(authentication_info.user_id.clone())
246                    .user(authentication_info.user.clone())
247                    .methods(authentication_info.methods.clone().iter())
248                    .audit_ids(authentication_info.audit_ids.clone().iter())
249                    .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
250                    .idp_id(idp_id)
251                    .protocol_id(protocol_id)
252                    .group_ids(
253                        authentication_info
254                            .user_groups
255                            .clone()
256                            .iter()
257                            .map(|grp| grp.id.clone())
258                            .collect::<Vec<_>>(),
259                    )
260                    .domain_id(domain.id.clone())
261                    .domain(domain.clone())
262                    .build()?,
263            ))
264        } else {
265            Err(TokenProviderError::FederatedPayloadMissingData)
266        }
267    }
268
269    /// Create token with the specified restrictions.
270    fn create_restricted_token(
271        &self,
272        authentication_info: &AuthenticatedInfo,
273        authz_info: &AuthzInfo,
274        restriction: &TokenRestriction,
275    ) -> Result<Token, TokenProviderError> {
276        Ok(Token::Restricted(
277            RestrictedPayloadBuilder::default()
278                .user_id(
279                    restriction
280                        .user_id
281                        .as_ref()
282                        .unwrap_or(&authentication_info.user_id.clone()),
283                )
284                .user(authentication_info.user.clone())
285                .methods(authentication_info.methods.clone().iter())
286                .audit_ids(authentication_info.audit_ids.clone().iter())
287                .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
288                .token_restriction_id(restriction.id.clone())
289                .project_id(
290                    restriction
291                        .project_id
292                        .as_ref()
293                        .or(match authz_info {
294                            AuthzInfo::Project(project) => Some(&project.id),
295                            _ => None,
296                        })
297                        .ok_or_else(|| TokenProviderError::RestrictedTokenNotProjectScoped)?,
298                )
299                .allow_renew(restriction.allow_renew)
300                .allow_rescope(restriction.allow_rescope)
301                .roles(restriction.roles.clone())
302                .build()?,
303        ))
304    }
305
306    /// Create system scoped token.
307    fn create_system_scoped_token(
308        &self,
309        authentication_info: &AuthenticatedInfo,
310    ) -> Result<Token, TokenProviderError> {
311        Ok(Token::SystemScope(
312            SystemScopePayloadBuilder::default()
313                .user_id(authentication_info.user_id.clone())
314                .user(authentication_info.user.clone())
315                .methods(authentication_info.methods.clone().iter())
316                .audit_ids(authentication_info.audit_ids.clone().iter())
317                .system_id("system")
318                .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
319                .build()?,
320        ))
321    }
322
323    /// Create token based on the trust.
324    fn create_trust_token(
325        &self,
326        authentication_info: &AuthenticatedInfo,
327        trust: &Trust,
328    ) -> Result<Token, TokenProviderError> {
329        if let Some(project_id) = &trust.project_id {
330            Ok(Token::Trust(
331                TrustPayloadBuilder::default()
332                    .user_id(authentication_info.user_id.clone())
333                    .user(authentication_info.user.clone())
334                    .methods(authentication_info.methods.clone().iter())
335                    .audit_ids(authentication_info.audit_ids.clone().iter())
336                    .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
337                    .trust_id(trust.id.clone())
338                    .project_id(project_id.clone())
339                    .build()?,
340            ))
341        } else {
342            // Trust without project_id is unscoped
343            Ok(Token::Unscoped(
344                UnscopedPayloadBuilder::default()
345                    .user_id(authentication_info.user_id.clone())
346                    .user(authentication_info.user.clone())
347                    .methods(authentication_info.methods.clone().iter())
348                    .audit_ids(authentication_info.audit_ids.clone().iter())
349                    .expires_at(self.get_new_token_expiry(&authentication_info.expires_at)?)
350                    .build()?,
351            ))
352        }
353    }
354
355    /// Expand user information in the token.
356    async fn expand_user_information(
357        &self,
358        state: &ServiceState,
359        token: &mut Token,
360    ) -> Result<(), TokenProviderError> {
361        if token.user().is_none() {
362            let user = state
363                .provider
364                .get_identity_provider()
365                .get_user(state, token.user_id())
366                .await?;
367            match token {
368                Token::ApplicationCredential(data) => {
369                    data.user = user;
370                }
371                Token::Unscoped(data) => {
372                    data.user = user;
373                }
374                Token::ProjectScope(data) => {
375                    data.user = user;
376                }
377                Token::DomainScope(data) => {
378                    data.user = user;
379                }
380                Token::FederationUnscoped(data) => {
381                    data.user = user;
382                }
383                Token::FederationProjectScope(data) => {
384                    data.user = user;
385                }
386                Token::FederationDomainScope(data) => {
387                    data.user = user;
388                }
389                Token::Restricted(data) => {
390                    data.user = user;
391                }
392                Token::SystemScope(data) => {
393                    data.user = user;
394                }
395                Token::Trust(data) => {
396                    data.user = if let Some(trust) = &data.trust
397                        && trust.impersonation
398                    {
399                        state
400                            .provider
401                            .get_identity_provider()
402                            .get_user(state, &trust.trustor_user_id)
403                            .await?
404                    } else {
405                        user
406                    };
407                }
408            }
409        }
410        Ok(())
411    }
412
413    /// Expand the target scope information in the token.
414    async fn expand_scope_information(
415        &self,
416        state: &ServiceState,
417        token: &mut Token,
418    ) -> Result<(), TokenProviderError> {
419        match token {
420            Token::ProjectScope(data) => {
421                if data.project.is_none() {
422                    let project = state
423                        .provider
424                        .get_resource_provider()
425                        .get_project(state, &data.project_id)
426                        .await?;
427
428                    data.project = project;
429                }
430            }
431            Token::ApplicationCredential(data) => {
432                if data.application_credential.is_none() {
433                    data.application_credential = Some(
434                        state
435                            .provider
436                            .get_application_credential_provider()
437                            .get_application_credential(state, &data.application_credential_id)
438                            .await?
439                            .ok_or_else(|| {
440                                TokenProviderError::ApplicationCredentialNotFound(
441                                    data.application_credential_id.clone(),
442                                )
443                            })?,
444                    );
445                }
446                if data.project.is_none() {
447                    let project = state
448                        .provider
449                        .get_resource_provider()
450                        .get_project(state, &data.project_id)
451                        .await?;
452
453                    data.project = project;
454                }
455            }
456            Token::FederationProjectScope(data) => {
457                if data.project.is_none() {
458                    let project = state
459                        .provider
460                        .get_resource_provider()
461                        .get_project(state, &data.project_id)
462                        .await?;
463
464                    data.project = project;
465                }
466            }
467            Token::DomainScope(data) => {
468                if data.domain.is_none() {
469                    let domain = state
470                        .provider
471                        .get_resource_provider()
472                        .get_domain(state, &data.domain_id)
473                        .await?;
474
475                    data.domain = domain;
476                }
477            }
478            Token::FederationDomainScope(data) => {
479                if data.domain.is_none() {
480                    let domain = state
481                        .provider
482                        .get_resource_provider()
483                        .get_domain(state, &data.domain_id)
484                        .await?;
485
486                    data.domain = domain;
487                }
488            }
489            Token::Restricted(data) => {
490                if data.project.is_none() {
491                    let project = state
492                        .provider
493                        .get_resource_provider()
494                        .get_project(state, &data.project_id)
495                        .await?;
496
497                    data.project = project;
498                }
499            }
500            Token::SystemScope(_data) => {}
501            Token::Trust(data) => {
502                if data.trust.is_none() {
503                    data.trust = state
504                        .provider
505                        .get_trust_provider()
506                        .get_trust(state, &data.trust_id)
507                        .await?;
508                }
509                if data.project.is_none() {
510                    data.project = state
511                        .provider
512                        .get_resource_provider()
513                        .get_project(state, &data.project_id)
514                        .await?;
515                }
516            }
517
518            _ => {}
519        };
520        Ok(())
521    }
522
523    /// Populate role assignments in the token that support that information.
524    async fn _populate_role_assignments(
525        &self,
526        state: &ServiceState,
527        token: &mut Token,
528    ) -> Result<(), TokenProviderError> {
529        match token {
530            Token::ApplicationCredential(data) => {
531                if data.application_credential.is_none() {
532                    data.application_credential = Some(
533                        state
534                            .provider
535                            .get_application_credential_provider()
536                            .get_application_credential(state, &data.application_credential_id)
537                            .await?
538                            .ok_or_else(|| {
539                                TokenProviderError::ApplicationCredentialNotFound(
540                                    data.application_credential_id.clone(),
541                                )
542                            })?,
543                    );
544                }
545                if let Some(ref mut ac) = data.application_credential {
546                    let user_role_ids: HashSet<String> = state
547                        .provider
548                        .get_assignment_provider()
549                        .list_role_assignments(
550                            state,
551                            &RoleAssignmentListParametersBuilder::default()
552                                .user_id(&data.user_id)
553                                .project_id(&ac.project_id)
554                                .include_names(false)
555                                .effective(true)
556                                .build()
557                                .map_err(AssignmentProviderError::from)?,
558                        )
559                        .await?
560                        .into_iter()
561                        .map(|x| x.role_id.clone())
562                        .collect();
563
564                    // Gather all effective roles that the user have remaining should some of the
565                    // AppCred assigned roles be revoked in the meanwhile.
566                    let mut final_roles: Vec<RoleRef> = Vec::new();
567                    for role in ac.roles.iter() {
568                        if user_role_ids.contains(&role.id) {
569                            final_roles.push(role.clone());
570                        }
571                    }
572                    if final_roles.is_empty() {
573                        return Err(TokenProviderError::ActorHasNoRolesOnTarget);
574                    }
575                    data.roles = Some(final_roles);
576                };
577            }
578            Token::DomainScope(data) => {
579                data.roles = Some(
580                    state
581                        .provider
582                        .get_assignment_provider()
583                        .list_role_assignments(
584                            state,
585                            &RoleAssignmentListParametersBuilder::default()
586                                .user_id(&data.user_id)
587                                .domain_id(&data.domain_id)
588                                .include_names(true)
589                                .effective(true)
590                                .build()
591                                .map_err(AssignmentProviderError::from)?,
592                        )
593                        .await?
594                        .into_iter()
595                        .map(|x| RoleRef {
596                            id: x.role_id.clone(),
597                            name: x.role_name.clone(),
598                            domain_id: None,
599                        })
600                        .collect(),
601                );
602                if data.roles.as_ref().is_none_or(|roles| roles.is_empty()) {
603                    return Err(TokenProviderError::ActorHasNoRolesOnTarget);
604                }
605            }
606            Token::FederationProjectScope(data) => {
607                data.roles = Some(
608                    state
609                        .provider
610                        .get_assignment_provider()
611                        .list_role_assignments(
612                            state,
613                            &RoleAssignmentListParametersBuilder::default()
614                                .user_id(&data.user_id)
615                                .project_id(&data.project_id)
616                                .include_names(true)
617                                .effective(true)
618                                .build()
619                                .map_err(AssignmentProviderError::from)?,
620                        )
621                        .await?
622                        .into_iter()
623                        .map(|x| RoleRef {
624                            id: x.role_id.clone(),
625                            name: x.role_name.clone(),
626                            domain_id: None,
627                        })
628                        .collect(),
629                );
630                if data.roles.as_ref().is_none_or(|roles| roles.is_empty()) {
631                    return Err(TokenProviderError::ActorHasNoRolesOnTarget);
632                }
633            }
634            Token::FederationDomainScope(data) => {
635                data.roles = Some(
636                    state
637                        .provider
638                        .get_assignment_provider()
639                        .list_role_assignments(
640                            state,
641                            &RoleAssignmentListParametersBuilder::default()
642                                .user_id(&data.user_id)
643                                .domain_id(&data.domain_id)
644                                .include_names(true)
645                                .effective(true)
646                                .build()
647                                .map_err(AssignmentProviderError::from)?,
648                        )
649                        .await?
650                        .into_iter()
651                        .map(|x| RoleRef {
652                            id: x.role_id.clone(),
653                            name: x.role_name.clone(),
654                            domain_id: None,
655                        })
656                        .collect(),
657                );
658                if data.roles.as_ref().is_none_or(|roles| roles.is_empty()) {
659                    return Err(TokenProviderError::ActorHasNoRolesOnTarget);
660                }
661            }
662            Token::ProjectScope(data) => {
663                data.roles = Some(
664                    state
665                        .provider
666                        .get_assignment_provider()
667                        .list_role_assignments(
668                            state,
669                            &RoleAssignmentListParametersBuilder::default()
670                                .user_id(&data.user_id)
671                                .project_id(&data.project_id)
672                                .include_names(true)
673                                .effective(true)
674                                .build()
675                                .map_err(AssignmentProviderError::from)?,
676                        )
677                        .await?
678                        .into_iter()
679                        .map(|x| RoleRef {
680                            id: x.role_id.clone(),
681                            name: x.role_name.clone(),
682                            domain_id: None,
683                        })
684                        .collect(),
685                );
686                if data.roles.as_ref().is_none_or(|roles| roles.is_empty()) {
687                    return Err(TokenProviderError::ActorHasNoRolesOnTarget);
688                }
689            }
690            Token::Restricted(data) => {
691                if data.roles.is_none() {
692                    self.get_token_restriction(state, &data.token_restriction_id, true)
693                        .await?
694                        .inspect(|restrictions| data.roles = restrictions.roles.clone())
695                        .ok_or(TokenProviderError::TokenRestrictionNotFound(
696                            data.token_restriction_id.clone(),
697                        ))?;
698                }
699            }
700            Token::SystemScope(data) => {
701                data.roles = Some(
702                    state
703                        .provider
704                        .get_assignment_provider()
705                        .list_role_assignments(
706                            state,
707                            &RoleAssignmentListParametersBuilder::default()
708                                .user_id(&data.user_id)
709                                .system_id(&data.system_id)
710                                .include_names(true)
711                                .effective(true)
712                                .build()
713                                .map_err(AssignmentProviderError::from)?,
714                        )
715                        .await?
716                        .into_iter()
717                        .map(|x| RoleRef {
718                            id: x.role_id.clone(),
719                            name: x.role_name.clone(),
720                            domain_id: None,
721                        })
722                        .collect(),
723                );
724                if data.roles.as_ref().is_none_or(|roles| roles.is_empty()) {
725                    return Err(TokenProviderError::ActorHasNoRolesOnTarget);
726                }
727            }
728            Token::Trust(data) => {
729                // Resolve role assignments of the trust verifying that the trustor still has
730                // those roles on the scope.
731                if let Some(ref mut trust) = data.trust {
732                    let trustor_roles: HashSet<String> = state
733                        .provider
734                        .get_assignment_provider()
735                        .list_role_assignments(
736                            state,
737                            &RoleAssignmentListParameters {
738                                user_id: Some(trust.trustor_user_id.clone()),
739                                project_id: Some(data.project_id.clone()),
740                                effective: Some(true),
741                                ..Default::default()
742                            },
743                        )
744                        .await?
745                        .into_iter()
746                        .map(|x| x.role_id.clone())
747                        .collect();
748                    if let Some(ref mut trust_roles) = trust.roles {
749                        // `token_model._get_trust_roles`: Verify that the trustor still has all
750                        // roles mentioned in the trust. Return error when at least one role is not
751                        // available anymore.
752
753                        // Expand the implied roles
754                        state
755                            .provider
756                            .get_role_provider()
757                            .expand_implied_roles(state, trust_roles)
758                            .await?;
759                        if !trust_roles
760                            .iter()
761                            .all(|role| trustor_roles.contains(&role.id))
762                        {
763                            debug!(
764                                "Trust roles {:?} are missing for the trustor {:?}",
765                                trust_roles, trustor_roles
766                            );
767                            return Err(TokenProviderError::ActorHasNoRolesOnTarget);
768                        }
769                        trust_roles.retain_mut(|role| role.domain_id.is_none());
770                    }
771                }
772            }
773            _ => {}
774        }
775
776        Ok(())
777    }
778}
779
780#[async_trait]
781impl TokenApi for TokenService {
782    /// Authenticate by token.
783    #[tracing::instrument(level = "info", skip(self, state, credential))]
784    async fn authenticate_by_token<'a>(
785        &self,
786        state: &ServiceState,
787        credential: &'a str,
788        allow_expired: Option<bool>,
789        window_seconds: Option<i64>,
790    ) -> Result<AuthenticatedInfo, TokenProviderError> {
791        // TODO: is the expand really false?
792        let token = self
793            .validate_token(state, credential, allow_expired, window_seconds)
794            .await?;
795        if let Token::Restricted(restriction) = &token
796            && !restriction.allow_renew
797        {
798            return Err(AuthenticationError::TokenRenewalForbidden)?;
799        }
800        let mut auth_info_builder = AuthenticatedInfo::builder();
801        auth_info_builder.user_id(token.user_id());
802        auth_info_builder.methods(token.methods().clone());
803        auth_info_builder.audit_ids(token.audit_ids().clone());
804        auth_info_builder.expires_at(*token.expires_at());
805        if let Token::Restricted(restriction) = &token {
806            auth_info_builder.token_restriction_id(restriction.token_restriction_id.clone());
807        }
808        Ok(auth_info_builder
809            .build()
810            .map_err(AuthenticationError::from)?)
811    }
812
813    /// Validate token.
814    #[tracing::instrument(level = "info", skip(self, state, credential))]
815    async fn validate_token<'a>(
816        &self,
817        state: &ServiceState,
818        credential: &'a str,
819        allow_expired: Option<bool>,
820        window_seconds: Option<i64>,
821    ) -> Result<Token, TokenProviderError> {
822        let mut token = self.backend_driver.decode(credential)?;
823        let latest_expiration_cutof = Utc::now()
824            .checked_add_signed(TimeDelta::seconds(window_seconds.unwrap_or(0)))
825            .unwrap_or(Utc::now());
826        if !allow_expired.unwrap_or_default() && *token.expires_at() < latest_expiration_cutof {
827            trace!(
828                "Token has expired at {:?} with cutof: {:?}",
829                token.expires_at(),
830                latest_expiration_cutof
831            );
832            return Err(TokenProviderError::Expired);
833        }
834
835        // Expand the token unless `expand = Some(false)`
836        token = self.expand_token_information(state, &token).await?;
837
838        if state
839            .provider
840            .get_revoke_provider()
841            .is_token_revoked(state, &token)
842            .await?
843        {
844            return Err(TokenProviderError::TokenRevoked);
845        }
846
847        token.validate_subject(state).await?;
848        token.validate_scope(state).await?;
849
850        Ok(token)
851    }
852
853    /// Issue the Keystone token.
854    #[tracing::instrument(level = "debug", skip(self))]
855    fn issue_token(
856        &self,
857        authentication_info: AuthenticatedInfo,
858        authz_info: AuthzInfo,
859        token_restrictions: Option<&TokenRestriction>,
860    ) -> Result<Token, TokenProviderError> {
861        // This should be executed already, but let's better repeat it as last line of
862        // defence. It is also necessary to call this before to stop before we
863        // start to resolve authz info.
864        authentication_info.validate()?;
865
866        // TODO: Check whether it is allowed to change the scope of the token if
867        // AuthenticatedInfo already contains scope it was issued for.
868        let mut authentication_info = authentication_info;
869        authentication_info
870            .audit_ids
871            .push(URL_SAFE_NO_PAD.encode(Uuid::new_v4().as_bytes()));
872        if let Some(token_restrictions) = &token_restrictions {
873            self.create_restricted_token(&authentication_info, &authz_info, token_restrictions)
874        } else if authentication_info.idp_id.is_some() && authentication_info.protocol_id.is_some()
875        {
876            match &authz_info {
877                AuthzInfo::Domain(domain) => {
878                    self.create_federated_domain_scope_token(&authentication_info, domain)
879                }
880                AuthzInfo::Project(project) => {
881                    self.create_federated_project_scope_token(&authentication_info, project)
882                }
883                AuthzInfo::Trust(_trust) => Err(TokenProviderError::Conflict {
884                    message: "cannot create trust token with an identity provider in scope".into(),
885                    context: "issuing token".into(),
886                }),
887                AuthzInfo::System => Err(TokenProviderError::Conflict {
888                    message: "cannot create system scope token with an identity provider in scope"
889                        .into(),
890                    context: "issuing token".into(),
891                }),
892                AuthzInfo::Unscoped => self.create_federated_unscoped_token(&authentication_info),
893            }
894        } else {
895            match &authz_info {
896                AuthzInfo::Domain(domain) => {
897                    self.create_domain_scope_token(&authentication_info, domain)
898                }
899                AuthzInfo::Project(project) => {
900                    self.create_project_scope_token(&authentication_info, project)
901                }
902                AuthzInfo::Trust(trust) => self.create_trust_token(&authentication_info, trust),
903                AuthzInfo::System => self.create_system_scoped_token(&authentication_info),
904                AuthzInfo::Unscoped => self.create_unscoped_token(&authentication_info),
905            }
906        }
907    }
908
909    /// Encode the token into a `String` representation.
910    ///
911    /// Encode the [`Token`] into the `String` to be used as a http header.
912    fn encode_token(&self, token: &Token) -> Result<String, TokenProviderError> {
913        self.backend_driver.encode(token)
914    }
915
916    /// Populate role assignments in the token that support that information.
917    async fn populate_role_assignments(
918        &self,
919        state: &ServiceState,
920        token: &mut Token,
921    ) -> Result<(), TokenProviderError> {
922        self._populate_role_assignments(state, token).await
923    }
924
925    /// Expand the token information.
926    ///
927    /// Query and expand information about the user, scope and the role
928    /// assignments into the token.
929    async fn expand_token_information(
930        &self,
931        state: &ServiceState,
932        token: &Token,
933    ) -> Result<Token, TokenProviderError> {
934        let mut new_token = token.clone();
935        self.expand_user_information(state, &mut new_token).await?;
936        self.expand_scope_information(state, &mut new_token).await?;
937        self.populate_role_assignments(state, &mut new_token)
938            .await?;
939        Ok(new_token)
940    }
941
942    /// Get the token restriction by the ID.
943    async fn get_token_restriction<'a>(
944        &self,
945        state: &ServiceState,
946        id: &'a str,
947        expand_roles: bool,
948    ) -> Result<Option<TokenRestriction>, TokenProviderError> {
949        self.tr_backend_driver
950            .get_token_restriction(state, id, expand_roles)
951            .await
952    }
953
954    /// Create new token restriction.
955    async fn create_token_restriction<'a>(
956        &self,
957        state: &ServiceState,
958        restriction: TokenRestrictionCreate,
959    ) -> Result<TokenRestriction, TokenProviderError> {
960        let mut restriction = restriction;
961        if restriction.id.is_empty() {
962            restriction.id = Uuid::new_v4().simple().to_string();
963        }
964        self.tr_backend_driver
965            .create_token_restriction(state, restriction)
966            .await
967    }
968
969    /// List token restrictions.
970    async fn list_token_restrictions<'a>(
971        &self,
972        state: &ServiceState,
973        params: &TokenRestrictionListParameters,
974    ) -> Result<Vec<TokenRestriction>, TokenProviderError> {
975        self.tr_backend_driver
976            .list_token_restrictions(state, params)
977            .await
978    }
979
980    /// Update existing token restriction.
981    async fn update_token_restriction<'a>(
982        &self,
983        state: &ServiceState,
984        id: &'a str,
985        restriction: TokenRestrictionUpdate,
986    ) -> Result<TokenRestriction, TokenProviderError> {
987        self.tr_backend_driver
988            .update_token_restriction(state, id, restriction)
989            .await
990    }
991
992    /// Delete token restriction by the ID.
993    async fn delete_token_restriction<'a>(
994        &self,
995        state: &ServiceState,
996        id: &'a str,
997    ) -> Result<(), TokenProviderError> {
998        self.tr_backend_driver
999            .delete_token_restriction(state, id)
1000            .await
1001    }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use chrono::Utc;
1007    use eyre::{Result, eyre};
1008    use std::sync::Arc;
1009    use tracing_test::traced_test;
1010    use uuid::Uuid;
1011
1012    use super::super::tests::setup_config;
1013    use super::*;
1014    use crate::application_credential::{
1015        MockApplicationCredentialProvider, types::ApplicationCredential,
1016    };
1017    use crate::assignment::{
1018        MockAssignmentProvider,
1019        types::{Assignment, AssignmentType, RoleAssignmentListParameters},
1020    };
1021    use crate::auth::AuthenticatedInfoBuilder;
1022    use crate::config::Config;
1023    use crate::identity::{MockIdentityProvider, types::UserResponseBuilder};
1024    use crate::provider::Provider;
1025    use crate::resource::{MockResourceProvider, types::*};
1026    use crate::revoke::MockRevokeProvider;
1027    use crate::tests::get_mocked_state;
1028    use crate::token::backend::MockTokenRestrictionBackend;
1029    use crate::trust::types::*;
1030
1031    /// Generate test token to use for validation testing.
1032    fn generate_token(validity: Option<TimeDelta>) -> Result<Token> {
1033        Ok(Token::ProjectScope(ProjectScopePayload {
1034            methods: vec!["password".into()],
1035            user_id: Uuid::new_v4().simple().to_string(),
1036            project_id: Uuid::new_v4().simple().to_string(),
1037            audit_ids: vec!["Zm9vCg".into()],
1038            expires_at: Utc::now()
1039                .checked_add_signed(validity.unwrap_or_default())
1040                .ok_or(eyre!("timedelta apply failed"))?,
1041            ..Default::default()
1042        }))
1043    }
1044
1045    fn get_provider(config: &Config) -> TokenService {
1046        TokenService {
1047            config: config.clone(),
1048            backend_driver: Arc::new(FernetTokenProvider::new(config.clone())),
1049            tr_backend_driver: Arc::new(MockTokenRestrictionBackend::default()),
1050        }
1051    }
1052
1053    #[tokio::test]
1054    async fn test_populate_role_assignments() {
1055        let token_provider = get_provider(&Config::default());
1056        let mut assignment_mock = MockAssignmentProvider::default();
1057        assignment_mock
1058            .expect_list_role_assignments()
1059            .withf(|_, q: &RoleAssignmentListParameters| {
1060                q.project_id == Some("project_id".to_string())
1061            })
1062            .returning(|_, q: &RoleAssignmentListParameters| {
1063                Ok(vec![Assignment {
1064                    role_id: "rid".into(),
1065                    role_name: Some("role_name".into()),
1066                    actor_id: q.user_id.clone().unwrap(),
1067                    target_id: q.project_id.clone().unwrap(),
1068                    r#type: AssignmentType::UserProject,
1069                    inherited: false,
1070                    implied_via: None,
1071                }])
1072            });
1073        assignment_mock
1074            .expect_list_role_assignments()
1075            .withf(|_, q: &RoleAssignmentListParameters| {
1076                q.domain_id == Some("domain_id".to_string())
1077            })
1078            .returning(|_, q: &RoleAssignmentListParameters| {
1079                Ok(vec![Assignment {
1080                    role_id: "rid".into(),
1081                    role_name: Some("role_name".into()),
1082                    actor_id: q.user_id.clone().unwrap(),
1083                    target_id: q.domain_id.clone().unwrap(),
1084                    r#type: AssignmentType::UserProject,
1085                    inherited: false,
1086                    implied_via: None,
1087                }])
1088            });
1089        let provider = Provider::mocked_builder().mock_assignment(assignment_mock);
1090
1091        let state = get_mocked_state(None, Some(provider));
1092
1093        let mut ptoken = Token::ProjectScope(ProjectScopePayload {
1094            user_id: "bar".into(),
1095            project_id: "project_id".into(),
1096            ..Default::default()
1097        });
1098        token_provider
1099            .populate_role_assignments(&state, &mut ptoken)
1100            .await
1101            .unwrap();
1102
1103        if let Token::ProjectScope(data) = ptoken {
1104            assert_eq!(
1105                data.roles.unwrap(),
1106                vec![RoleRef {
1107                    id: "rid".into(),
1108                    name: Some("role_name".into()),
1109                    domain_id: None
1110                }]
1111            );
1112        } else {
1113            panic!("Not project scope");
1114        }
1115
1116        let mut dtoken = Token::DomainScope(DomainScopePayload {
1117            user_id: "bar".into(),
1118            domain_id: "domain_id".into(),
1119            ..Default::default()
1120        });
1121        token_provider
1122            .populate_role_assignments(&state, &mut dtoken)
1123            .await
1124            .unwrap();
1125
1126        if let Token::DomainScope(data) = dtoken {
1127            assert_eq!(
1128                data.roles.unwrap(),
1129                vec![RoleRef {
1130                    id: "rid".into(),
1131                    name: Some("role_name".into()),
1132                    domain_id: None
1133                }]
1134            );
1135        } else {
1136            panic!("Not domain scope");
1137        }
1138
1139        let mut utoken = Token::Unscoped(UnscopedPayload {
1140            user_id: "bar".into(),
1141            ..Default::default()
1142        });
1143        assert!(
1144            token_provider
1145                .populate_role_assignments(&state, &mut utoken)
1146                .await
1147                .is_ok()
1148        );
1149    }
1150
1151    /// Test that a valid token with revocation events fails validation.
1152    #[tokio::test]
1153    #[traced_test]
1154    async fn test_validate_token_revoked() {
1155        let token = generate_token(Some(TimeDelta::hours(1))).unwrap();
1156
1157        let config = setup_config();
1158        let token_provider = get_provider(&config);
1159        let mut revoke_mock = MockRevokeProvider::default();
1160        //let token_clone = token.clone();
1161        revoke_mock
1162            .expect_is_token_revoked()
1163            // TODO: in roundtrip the precision of expiry is reduced and issued_at is different
1164            //.withf(move |_, t: &Token| {
1165            //    *t == token_clone
1166            //})
1167            .returning(|_, _| Ok(true));
1168
1169        let mut identity_mock = MockIdentityProvider::default();
1170        let token_clone = token.clone();
1171        identity_mock
1172            .expect_get_user()
1173            .withf(move |_, id: &'_ str| id == token_clone.user_id())
1174            .returning(|_, id: &'_ str| {
1175                Ok(Some(
1176                    UserResponseBuilder::default()
1177                        .domain_id("user_domain_id")
1178                        .enabled(true)
1179                        .name("name")
1180                        .id(id)
1181                        .build()
1182                        .unwrap(),
1183                ))
1184            });
1185        let mut resource_mock = MockResourceProvider::default();
1186        let token_clone2 = token.clone();
1187        resource_mock
1188            .expect_get_project()
1189            .withf(move |_, id: &'_ str| id == token_clone2.project_id().unwrap())
1190            .returning(|_, id: &'_ str| {
1191                Ok(Some(Project {
1192                    id: id.to_string(),
1193                    name: "project".to_string(),
1194                    ..Default::default()
1195                }))
1196            });
1197
1198        let mut assignment_mock = MockAssignmentProvider::default();
1199        let token_clone3 = token.clone();
1200        assignment_mock
1201            .expect_list_role_assignments()
1202            .withf(move |_, q: &RoleAssignmentListParameters| {
1203                q.project_id == token_clone3.project_id().cloned()
1204            })
1205            .returning(|_, q: &RoleAssignmentListParameters| {
1206                Ok(vec![Assignment {
1207                    role_id: "rid".into(),
1208                    role_name: Some("role_name".into()),
1209                    actor_id: q.user_id.clone().unwrap(),
1210                    target_id: q.project_id.clone().unwrap(),
1211                    r#type: AssignmentType::UserProject,
1212                    inherited: false,
1213                    implied_via: None,
1214                }])
1215            });
1216        let provider = Provider::mocked_builder()
1217            .mock_assignment(assignment_mock)
1218            .mock_identity(identity_mock)
1219            .mock_revoke(revoke_mock)
1220            .mock_resource(resource_mock);
1221
1222        let state = get_mocked_state(Some(config), Some(provider));
1223
1224        let credential = token_provider.encode_token(&token).unwrap();
1225        match token_provider
1226            .validate_token(&state, &credential, Some(false), None)
1227            .await
1228        {
1229            Err(TokenProviderError::TokenRevoked) => {}
1230            _ => {
1231                panic!("token must be revoked")
1232            }
1233        }
1234    }
1235
1236    #[tokio::test]
1237    async fn test_populate_role_assignments_application_credential() {
1238        let token_provider = get_provider(&Config::default());
1239        let mut assignment_mock = MockAssignmentProvider::default();
1240        assignment_mock
1241            .expect_list_role_assignments()
1242            .withf(|_, q: &RoleAssignmentListParameters| {
1243                q.project_id == Some("project_id".to_string())
1244                    && q.user_id == Some("bar".to_string())
1245            })
1246            .returning(|_, q: &RoleAssignmentListParameters| {
1247                Ok(vec![Assignment {
1248                    role_id: "role_1".into(),
1249                    role_name: Some("role_name".into()),
1250                    actor_id: q.user_id.clone().unwrap(),
1251                    target_id: q.project_id.clone().unwrap(),
1252                    r#type: AssignmentType::UserProject,
1253                    inherited: false,
1254                    implied_via: None,
1255                }])
1256            });
1257        assignment_mock
1258            .expect_list_role_assignments()
1259            .withf(|_, q: &RoleAssignmentListParameters| {
1260                q.domain_id == Some("domain_id".to_string())
1261            })
1262            .returning(|_, q: &RoleAssignmentListParameters| {
1263                Ok(vec![Assignment {
1264                    role_id: "rid".into(),
1265                    role_name: Some("role_name".into()),
1266                    actor_id: q.user_id.clone().unwrap(),
1267                    target_id: q.domain_id.clone().unwrap(),
1268                    r#type: AssignmentType::UserProject,
1269                    inherited: false,
1270                    implied_via: None,
1271                }])
1272            });
1273        let mut ac_mock = MockApplicationCredentialProvider::default();
1274        ac_mock
1275            .expect_get_application_credential()
1276            .withf(|_, id: &'_ str| id == "app_cred_id")
1277            .returning(|_, id: &'_ str| {
1278                Ok(Some(ApplicationCredential {
1279                    access_rules: None,
1280                    description: None,
1281                    expires_at: None,
1282                    id: id.into(),
1283                    name: "foo".into(),
1284                    project_id: "project_id".into(),
1285                    roles: vec![
1286                        RoleRef {
1287                            id: "role_1".into(),
1288                            name: Some("role_name_1".into()),
1289                            domain_id: None,
1290                        },
1291                        RoleRef {
1292                            id: "role_2".into(),
1293                            name: Some("role_name_2".into()),
1294                            domain_id: None,
1295                        },
1296                    ],
1297                    unrestricted: false,
1298                    user_id: "bar".into(),
1299                }))
1300            });
1301        ac_mock
1302            .expect_get_application_credential()
1303            .withf(|_, id: &'_ str| id == "app_cred_bad_roles")
1304            .returning(|_, id: &'_ str| {
1305                Ok(Some(ApplicationCredential {
1306                    access_rules: None,
1307                    description: None,
1308                    expires_at: None,
1309                    id: id.into(),
1310                    name: "foo".into(),
1311                    project_id: "project_id".into(),
1312                    roles: vec![
1313                        RoleRef {
1314                            id: "-role_1".into(),
1315                            name: Some("-role_name_1".into()),
1316                            domain_id: None,
1317                        },
1318                        RoleRef {
1319                            id: "-role_2".into(),
1320                            name: Some("-role_name_2".into()),
1321                            domain_id: None,
1322                        },
1323                    ],
1324                    unrestricted: false,
1325                    user_id: "bar".into(),
1326                }))
1327            });
1328        ac_mock
1329            .expect_get_application_credential()
1330            .withf(|_, id: &'_ str| id == "missing")
1331            .returning(|_, _| Ok(None));
1332        let provider = Provider::mocked_builder()
1333            .mock_application_credential(ac_mock)
1334            .mock_assignment(assignment_mock);
1335
1336        let state = get_mocked_state(None, Some(provider));
1337
1338        let mut token = Token::ApplicationCredential(ApplicationCredentialPayload {
1339            user_id: "bar".into(),
1340            project_id: "project_id".into(),
1341            application_credential_id: "app_cred_id".into(),
1342            ..Default::default()
1343        });
1344        token_provider
1345            .populate_role_assignments(&state, &mut token)
1346            .await
1347            .unwrap();
1348
1349        if let Token::ApplicationCredential(..) = &token {
1350            assert_eq!(
1351                token.effective_roles().unwrap(),
1352                &vec![RoleRef {
1353                    id: "role_1".into(),
1354                    name: Some("role_name_1".into()),
1355                    domain_id: None,
1356                }],
1357                "only still active role assignment is returned"
1358            );
1359        } else {
1360            panic!("Not application credential scope");
1361        }
1362
1363        // Try populating role assignments for not existing appcred
1364        if let Err(TokenProviderError::ApplicationCredentialNotFound(id)) = token_provider
1365            .populate_role_assignments(
1366                &state,
1367                &mut Token::ApplicationCredential(ApplicationCredentialPayload {
1368                    user_id: "bar".into(),
1369                    project_id: "project_id".into(),
1370                    application_credential_id: "missing".into(),
1371                    ..Default::default()
1372                }),
1373            )
1374            .await
1375        {
1376            assert_eq!(id, "missing");
1377        } else {
1378            panic!("role expansion for missing application credential should fail");
1379        }
1380
1381        // No roles remain after subtracting current user roles
1382        if let Err(TokenProviderError::ActorHasNoRolesOnTarget) = token_provider
1383            .populate_role_assignments(
1384                &state,
1385                &mut Token::ApplicationCredential(ApplicationCredentialPayload {
1386                    user_id: "bar".into(),
1387                    project_id: "project_id".into(),
1388                    application_credential_id: "app_cred_bad_roles".into(),
1389                    ..Default::default()
1390                }),
1391            )
1392            .await
1393        {
1394        } else {
1395            panic!(
1396                "role expansion for application credential with roles the user does not have anymore should fail"
1397            );
1398        }
1399    }
1400
1401    #[tokio::test]
1402    async fn test_create_unscoped_token() {
1403        let token_provider = get_provider(&Config::default());
1404        let now = Utc::now();
1405        let token = token_provider
1406            .create_unscoped_token(
1407                &AuthenticatedInfoBuilder::default()
1408                    .user_id("uid")
1409                    .expires_at(now)
1410                    .build()
1411                    .unwrap(),
1412            )
1413            .unwrap();
1414        assert_eq!(*token.expires_at(), now);
1415        assert_eq!(*token.user_id(), "uid");
1416        let token = token_provider
1417            .create_unscoped_token(
1418                &AuthenticatedInfoBuilder::default()
1419                    .user_id("uid")
1420                    .build()
1421                    .unwrap(),
1422            )
1423            .unwrap();
1424        assert!(now < *token.expires_at());
1425        assert_eq!(*token.user_id(), "uid");
1426        assert!(token.project_id().is_none());
1427    }
1428
1429    #[tokio::test]
1430    async fn test_create_project_scope_token() {
1431        let token_provider = get_provider(&Config::default());
1432        let now = Utc::now();
1433        let token = token_provider
1434            .create_project_scope_token(
1435                &AuthenticatedInfoBuilder::default()
1436                    .user_id("uid")
1437                    .expires_at(now)
1438                    .build()
1439                    .unwrap(),
1440                &ProjectBuilder::default()
1441                    .id("pid")
1442                    .domain_id("did")
1443                    .name("pname")
1444                    .enabled(true)
1445                    .build()
1446                    .unwrap(),
1447            )
1448            .unwrap();
1449        assert_eq!(*token.expires_at(), now);
1450        assert_eq!(*token.user_id(), "uid");
1451        let token = token_provider
1452            .create_project_scope_token(
1453                &AuthenticatedInfoBuilder::default()
1454                    .user_id("uid")
1455                    .build()
1456                    .unwrap(),
1457                &ProjectBuilder::default()
1458                    .id("pid")
1459                    .domain_id("did")
1460                    .name("pname")
1461                    .enabled(true)
1462                    .build()
1463                    .unwrap(),
1464            )
1465            .unwrap();
1466        assert!(now < *token.expires_at());
1467        assert_eq!(*token.user_id(), "uid");
1468        assert_eq!(*token.project_id().unwrap(), "pid");
1469    }
1470
1471    #[tokio::test]
1472    async fn test_create_domain_scope_token() {
1473        let token_provider = get_provider(&Config::default());
1474        let now = Utc::now();
1475        let token = token_provider
1476            .create_domain_scope_token(
1477                &AuthenticatedInfoBuilder::default()
1478                    .user_id("uid")
1479                    .expires_at(now)
1480                    .build()
1481                    .unwrap(),
1482                &DomainBuilder::default()
1483                    .id("did")
1484                    .name("pname")
1485                    .enabled(true)
1486                    .build()
1487                    .unwrap(),
1488            )
1489            .unwrap();
1490        assert_eq!(*token.expires_at(), now);
1491        assert_eq!(*token.user_id(), "uid");
1492        let token = token_provider
1493            .create_domain_scope_token(
1494                &AuthenticatedInfoBuilder::default()
1495                    .user_id("uid")
1496                    .build()
1497                    .unwrap(),
1498                &DomainBuilder::default()
1499                    .id("did")
1500                    .name("pname")
1501                    .enabled(true)
1502                    .build()
1503                    .unwrap(),
1504            )
1505            .unwrap();
1506        assert!(now < *token.expires_at());
1507        assert_eq!(*token.user_id(), "uid");
1508        assert_eq!(token.domain().unwrap().id, "did");
1509    }
1510
1511    #[tokio::test]
1512    async fn test_create_system_token() {
1513        let token_provider = get_provider(&Config::default());
1514        let now = Utc::now();
1515        let token = token_provider
1516            .create_system_scoped_token(
1517                &AuthenticatedInfoBuilder::default()
1518                    .user_id("uid")
1519                    .expires_at(now)
1520                    .build()
1521                    .unwrap(),
1522            )
1523            .unwrap();
1524        assert_eq!(*token.expires_at(), now);
1525        assert_eq!(*token.user_id(), "uid");
1526        let token = token_provider
1527            .create_system_scoped_token(
1528                &AuthenticatedInfoBuilder::default()
1529                    .user_id("uid")
1530                    .build()
1531                    .unwrap(),
1532            )
1533            .unwrap();
1534        assert!(now < *token.expires_at());
1535        assert_eq!(*token.user_id(), "uid");
1536        if let Token::SystemScope(data) = token {
1537            assert_eq!(data.system_id, "system");
1538        } else {
1539            panic!("wrong token type");
1540        }
1541    }
1542
1543    #[tokio::test]
1544    async fn test_create_trust_token() {
1545        let token_provider = get_provider(&Config::default());
1546        let now = Utc::now();
1547        let token = token_provider
1548            .create_trust_token(
1549                &AuthenticatedInfoBuilder::default()
1550                    .user_id("uid")
1551                    .expires_at(now)
1552                    .build()
1553                    .unwrap(),
1554                &TrustBuilder::default()
1555                    .id("tid")
1556                    .impersonation(false)
1557                    .trustor_user_id("trustor_uid")
1558                    .trustee_user_id("trustor_uid")
1559                    .build()
1560                    .unwrap(),
1561            )
1562            .unwrap();
1563        assert_eq!(*token.expires_at(), now);
1564        assert_eq!(*token.user_id(), "uid");
1565        let token = token_provider
1566            .create_trust_token(
1567                &AuthenticatedInfoBuilder::default()
1568                    .user_id("uid")
1569                    .build()
1570                    .unwrap(),
1571                &TrustBuilder::default()
1572                    .id("tid")
1573                    .impersonation(false)
1574                    .trustor_user_id("trustor_uid")
1575                    .trustee_user_id("trustor_uid")
1576                    .project_id("pid")
1577                    .build()
1578                    .unwrap(),
1579            )
1580            .unwrap();
1581        assert!(now < *token.expires_at());
1582        assert_eq!(*token.user_id(), "uid");
1583        if let Token::Trust(data) = token {
1584            assert_eq!(data.trust_id, "tid");
1585        } else {
1586            panic!("wrong token type");
1587        }
1588
1589        // unscoped
1590        let token = token_provider
1591            .create_trust_token(
1592                &AuthenticatedInfoBuilder::default()
1593                    .user_id("uid")
1594                    .build()
1595                    .unwrap(),
1596                &TrustBuilder::default()
1597                    .id("tid")
1598                    .impersonation(false)
1599                    .trustor_user_id("trustor_uid")
1600                    .trustee_user_id("trustor_uid")
1601                    .build()
1602                    .unwrap(),
1603            )
1604            .unwrap();
1605        assert!(now < *token.expires_at());
1606        assert_eq!(*token.user_id(), "uid");
1607        if let Token::Unscoped(_data) = token {
1608        } else {
1609            panic!("wrong token type");
1610        }
1611    }
1612
1613    #[tokio::test]
1614    async fn test_create_restricted_token() {
1615        let token_provider = get_provider(&Config::default());
1616        let now = Utc::now();
1617        let token = token_provider
1618            .create_restricted_token(
1619                &AuthenticatedInfoBuilder::default()
1620                    .user_id("uid")
1621                    .expires_at(now)
1622                    .build()
1623                    .unwrap(),
1624                &AuthzInfo::System,
1625                &TokenRestrictionBuilder::default()
1626                    .id("rid")
1627                    .domain_id("did")
1628                    .project_id("pid")
1629                    .allow_renew(true)
1630                    .allow_rescope(true)
1631                    .role_ids([])
1632                    .build()
1633                    .unwrap(),
1634            )
1635            .unwrap();
1636        assert_eq!(*token.expires_at(), now);
1637        assert_eq!(*token.user_id(), "uid");
1638        let token = token_provider
1639            .create_restricted_token(
1640                &AuthenticatedInfoBuilder::default()
1641                    .user_id("uid")
1642                    .build()
1643                    .unwrap(),
1644                &AuthzInfo::System,
1645                &TokenRestrictionBuilder::default()
1646                    .id("rid")
1647                    .domain_id("did")
1648                    .project_id("pid")
1649                    .allow_renew(true)
1650                    .allow_rescope(true)
1651                    .role_ids([])
1652                    .build()
1653                    .unwrap(),
1654            )
1655            .unwrap();
1656        assert!(now < *token.expires_at());
1657        assert_eq!(*token.user_id(), "uid");
1658        if let Token::Restricted(data) = token {
1659            assert_eq!(data.token_restriction_id, "rid");
1660        } else {
1661            panic!("wrong token type");
1662        }
1663    }
1664}