Skip to main content

openstack_keystone_core/token/
mod.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;
23
24pub mod backend;
25pub mod error;
26#[cfg(any(test, feature = "mock"))]
27mod mock;
28pub mod service;
29//mod token_restriction;
30pub mod types;
31
32use crate::auth::{AuthenticatedInfo, AuthzInfo};
33use crate::config::Config;
34use crate::keystone::ServiceState;
35use crate::plugin_manager::PluginManagerApi;
36use crate::token::service::TokenService;
37pub use error::TokenProviderError;
38
39pub use crate::token::types::*;
40#[cfg(any(test, feature = "mock"))]
41pub use mock::MockTokenProvider;
42
43pub enum TokenProvider {
44    Service(TokenService),
45    #[cfg(any(test, feature = "mock"))]
46    Mock(MockTokenProvider),
47}
48
49impl TokenProvider {
50    pub fn new<P: PluginManagerApi>(
51        config: &Config,
52        plugin_manager: &P,
53    ) -> Result<Self, TokenProviderError> {
54        Ok(Self::Service(TokenService::new(config, plugin_manager)?))
55    }
56}
57
58#[async_trait]
59impl TokenApi for TokenProvider {
60    /// Authenticate by token.
61    async fn authenticate_by_token<'a>(
62        &self,
63        state: &ServiceState,
64        credential: &'a str,
65        allow_expired: Option<bool>,
66        window_seconds: Option<i64>,
67    ) -> Result<AuthenticatedInfo, TokenProviderError> {
68        match self {
69            Self::Service(provider) => {
70                provider
71                    .authenticate_by_token(state, credential, allow_expired, window_seconds)
72                    .await
73            }
74            #[cfg(any(test, feature = "mock"))]
75            Self::Mock(provider) => {
76                provider
77                    .authenticate_by_token(state, credential, allow_expired, window_seconds)
78                    .await
79            }
80        }
81    }
82
83    /// Validate token.
84    async fn validate_token<'a>(
85        &self,
86        state: &ServiceState,
87        credential: &'a str,
88        allow_expired: Option<bool>,
89        window_seconds: Option<i64>,
90    ) -> Result<Token, TokenProviderError> {
91        match self {
92            Self::Service(provider) => {
93                provider
94                    .validate_token(state, credential, allow_expired, window_seconds)
95                    .await
96            }
97            #[cfg(any(test, feature = "mock"))]
98            Self::Mock(provider) => {
99                provider
100                    .validate_token(state, credential, allow_expired, window_seconds)
101                    .await
102            }
103        }
104    }
105
106    /// Issue the Keystone token.
107    fn issue_token(
108        &self,
109        authentication_info: AuthenticatedInfo,
110        authz_info: AuthzInfo,
111        token_restrictions: Option<&TokenRestriction>,
112    ) -> Result<Token, TokenProviderError> {
113        match self {
114            Self::Service(provider) => {
115                provider.issue_token(authentication_info, authz_info, token_restrictions)
116            }
117            #[cfg(any(test, feature = "mock"))]
118            Self::Mock(provider) => {
119                provider.issue_token(authentication_info, authz_info, token_restrictions)
120            }
121        }
122    }
123
124    /// Encode the token into a `String` representation.
125    ///
126    /// Encode the [`Token`] into the `String` to be used as a http header.
127    fn encode_token(&self, token: &Token) -> Result<String, TokenProviderError> {
128        match self {
129            Self::Service(provider) => provider.encode_token(token),
130            #[cfg(any(test, feature = "mock"))]
131            Self::Mock(provider) => provider.encode_token(token),
132        }
133    }
134
135    /// Populate role assignments in the token that support that information.
136    async fn populate_role_assignments(
137        &self,
138        state: &ServiceState,
139        token: &mut Token,
140    ) -> Result<(), TokenProviderError> {
141        match self {
142            Self::Service(provider) => provider.populate_role_assignments(state, token).await,
143            #[cfg(any(test, feature = "mock"))]
144            Self::Mock(provider) => provider.populate_role_assignments(state, token).await,
145        }
146    }
147
148    /// Expand the token information.
149    ///
150    /// Query and expand information about the user, scope and the role
151    /// assignments into the token.
152    async fn expand_token_information(
153        &self,
154        state: &ServiceState,
155        token: &Token,
156    ) -> Result<Token, TokenProviderError> {
157        match self {
158            Self::Service(provider) => provider.expand_token_information(state, token).await,
159            #[cfg(any(test, feature = "mock"))]
160            Self::Mock(provider) => provider.expand_token_information(state, token).await,
161        }
162    }
163
164    /// Get the token restriction by the ID.
165    async fn get_token_restriction<'a>(
166        &self,
167        state: &ServiceState,
168        id: &'a str,
169        expand_roles: bool,
170    ) -> Result<Option<TokenRestriction>, TokenProviderError> {
171        match self {
172            Self::Service(provider) => {
173                provider
174                    .get_token_restriction(state, id, expand_roles)
175                    .await
176            }
177            #[cfg(any(test, feature = "mock"))]
178            Self::Mock(provider) => {
179                provider
180                    .get_token_restriction(state, id, expand_roles)
181                    .await
182            }
183        }
184    }
185
186    /// Create new token restriction.
187    async fn create_token_restriction<'a>(
188        &self,
189        state: &ServiceState,
190        restriction: TokenRestrictionCreate,
191    ) -> Result<TokenRestriction, TokenProviderError> {
192        match self {
193            Self::Service(provider) => provider.create_token_restriction(state, restriction).await,
194            #[cfg(any(test, feature = "mock"))]
195            Self::Mock(provider) => provider.create_token_restriction(state, restriction).await,
196        }
197    }
198
199    /// List token restrictions.
200    async fn list_token_restrictions<'a>(
201        &self,
202        state: &ServiceState,
203        params: &TokenRestrictionListParameters,
204    ) -> Result<Vec<TokenRestriction>, TokenProviderError> {
205        match self {
206            Self::Service(provider) => provider.list_token_restrictions(state, params).await,
207            #[cfg(any(test, feature = "mock"))]
208            Self::Mock(provider) => provider.list_token_restrictions(state, params).await,
209        }
210    }
211
212    /// Update existing token restriction.
213    async fn update_token_restriction<'a>(
214        &self,
215        state: &ServiceState,
216        id: &'a str,
217        restriction: TokenRestrictionUpdate,
218    ) -> Result<TokenRestriction, TokenProviderError> {
219        match self {
220            Self::Service(provider) => {
221                provider
222                    .update_token_restriction(state, id, restriction)
223                    .await
224            }
225            #[cfg(any(test, feature = "mock"))]
226            Self::Mock(provider) => {
227                provider
228                    .update_token_restriction(state, id, restriction)
229                    .await
230            }
231        }
232    }
233
234    /// Delete token restriction by the ID.
235    async fn delete_token_restriction<'a>(
236        &self,
237        state: &ServiceState,
238        id: &'a str,
239    ) -> Result<(), TokenProviderError> {
240        match self {
241            Self::Service(provider) => provider.delete_token_restriction(state, id).await,
242            #[cfg(any(test, feature = "mock"))]
243            Self::Mock(provider) => provider.delete_token_restriction(state, id).await,
244        }
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use std::fs::File;
251    use std::io::Write;
252    use tempfile::tempdir;
253
254    use crate::config::Config;
255
256    pub(super) fn setup_config() -> Config {
257        let keys_dir = tempdir().unwrap();
258        // write fernet key used to generate tokens in python
259        let file_path = keys_dir.path().join("0");
260        let mut tmp_file = File::create(file_path).unwrap();
261        write!(tmp_file, "BFTs1CIVIBLTP4GOrQ26VETrJ7Zwz1O4wbEcCQ966eM=").unwrap();
262
263        let builder = config::Config::builder()
264            .set_override(
265                "auth.methods",
266                "password,token,openid,application_credential",
267            )
268            .unwrap()
269            .set_override("database.connection", "dummy")
270            .unwrap();
271        let mut config: Config = Config::try_from(builder).expect("can build a valid config");
272        config.fernet_tokens.key_repository = keys_dir.keep();
273        config
274    }
275}