Skip to main content

openstack_keystone_core/application_credential/
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//     http://www.apache.org/licenses/LICENSE-2.0
5//
6// Unless required by applicable law or agreed to in writing, software
7// distributed under the License is distributed on an "AS IS" BASIS,
8// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9// See the License for the specific language governing permissions and
10// limitations under the License.
11//
12// SPDX-License-Identifier: Apache-2.0
13
14//! # Application credentials provider
15//!
16//! Application credentials provide a way to delegate a user's authorization to
17//! an application without sharing the user's password authentication. This is a
18//! useful security measure, especially for situations where the user's
19//! identification is provided by an external source, such as LDAP or a
20//! single-sign-on service. Instead of storing user passwords in config files, a
21//! user creates an application credential for a specific project, with all or a
22//! subset of the role assignments they have on that project, and then stores
23//! the application credential identifier and secret in the config file.
24//!
25//! Multiple application credentials may be active at once, so you can easily
26//! rotate application credentials by creating a second one, converting your
27//! applications to use it one by one, and finally deleting the first one.
28//!
29//! Application credentials are limited by the lifespan of the user that created
30//! them. If the user is deleted, disabled, or loses a role assignment on a
31//! project, the application credential is deleted.
32//!
33//! Application credentials can have their privileges limited in two ways.
34//! First, the owner may specify a subset of their own roles that the
35//! application credential may assume when getting a token for a project. For
36//! example, if a user has the member role on a project, they also have the
37//! implied role reader and can grant the application credential only the reader
38//! role for the project:
39//!
40//! ```yaml
41//! "roles": [
42//!     {"name": "reader"}
43//! ]
44//! ```
45//!
46//! Users also have the option of delegating more fine-grained access control to
47//! their application credentials by using access rules. For example, to create
48//! an application credential that is constricted to creating servers in nova,
49//! the user can add the following access rules:
50//!
51//! ```yaml
52//! "access_rules": [
53//!     {
54//!         "path": "/v2.1/servers",
55//!         "method": "POST",
56//!         "service": "compute"
57//!     }
58//! ]
59//! ```
60//!
61//! The "path" attribute of application credential access rules uses a wildcard
62//! syntax to make it more flexible. For example, to create an application
63//! credential that is constricted to listing server IP addresses, you could use
64//! either of the following access rules:
65//!
66//! ```yaml
67//! "access_rules": [
68//!     {
69//!         "path": "/v2.1/servers/*/ips",
70//!         "method": "GET",
71//!         "service": "compute"
72//!     }
73//! ]
74//! ```
75//!
76//! or equivalently:
77//!
78//! ```yaml
79//! "access_rules": [
80//!     {
81//!         "path": "/v2.1/servers/{server_id}/ips",
82//!         "method": "GET",
83//!         "service": "compute"
84//!     }
85//! ]
86//! ```
87//!
88//! In both cases, a request path containing any server ID will match the access
89//! rule. For even more flexibility, the recursive wildcard ** indicates that
90//! request paths containing any number of / will be matched. For example:
91//!
92//! ```yaml
93//! "access_rules": [
94//!     {
95//!         "path": "/v2.1/**",
96//!         "method": "GET",
97//!         "service": "compute"
98//!     }
99//! ]
100//! ```
101//!
102//! will match any nova API for version 2.1.
103//!
104//! An access rule created for one application credential can be re-used by
105//! providing its ID to another application credential, for example:
106//!
107//! ```yaml
108//! "access_rules": [
109//!     {
110//!         "id": "abcdef"
111//!     }
112//! ]
113//! ```
114use async_trait::async_trait;
115
116pub mod backend;
117pub mod error;
118#[cfg(any(test, feature = "mock"))]
119mod mock;
120pub mod service;
121pub mod types;
122
123use crate::config::Config;
124use crate::keystone::ServiceState;
125use crate::plugin_manager::PluginManagerApi;
126use service::ApplicationCredentialService;
127use types::*;
128
129pub use error::ApplicationCredentialProviderError;
130#[cfg(any(test, feature = "mock"))]
131pub use mock::MockApplicationCredentialProvider;
132pub use types::ApplicationCredentialApi;
133
134/// Application Credential Provider.
135pub enum ApplicationCredentialProvider {
136    Service(ApplicationCredentialService),
137    #[cfg(any(test, feature = "mock"))]
138    Mock(MockApplicationCredentialProvider),
139}
140
141impl ApplicationCredentialProvider {
142    pub fn new<P: PluginManagerApi>(
143        config: &Config,
144        plugin_manager: &P,
145    ) -> Result<Self, ApplicationCredentialProviderError> {
146        Ok(Self::Service(ApplicationCredentialService::new(
147            config,
148            plugin_manager,
149        )?))
150    }
151}
152
153#[async_trait]
154impl ApplicationCredentialApi for ApplicationCredentialProvider {
155    /// Create a new application credential.
156    async fn create_application_credential(
157        &self,
158        state: &ServiceState,
159        rec: ApplicationCredentialCreate,
160    ) -> Result<ApplicationCredentialCreateResponse, ApplicationCredentialProviderError> {
161        match self {
162            Self::Service(provider) => provider.create_application_credential(state, rec).await,
163            #[cfg(any(test, feature = "mock"))]
164            Self::Mock(provider) => provider.create_application_credential(state, rec).await,
165        }
166    }
167
168    /// Get a single application credential by ID.
169    #[tracing::instrument(level = "info", skip(self, state))]
170    async fn get_application_credential<'a>(
171        &self,
172        state: &ServiceState,
173        id: &'a str,
174    ) -> Result<Option<ApplicationCredential>, ApplicationCredentialProviderError> {
175        match self {
176            Self::Service(provider) => provider.get_application_credential(state, id).await,
177            #[cfg(any(test, feature = "mock"))]
178            Self::Mock(provider) => provider.get_application_credential(state, id).await,
179        }
180    }
181
182    /// List application credentials.
183    #[tracing::instrument(level = "info", skip(self, state))]
184    async fn list_application_credentials(
185        &self,
186        state: &ServiceState,
187        params: &ApplicationCredentialListParameters,
188    ) -> Result<Vec<ApplicationCredential>, ApplicationCredentialProviderError> {
189        match self {
190            Self::Service(provider) => provider.list_application_credentials(state, params).await,
191            #[cfg(any(test, feature = "mock"))]
192            Self::Mock(provider) => provider.list_application_credentials(state, params).await,
193        }
194    }
195}