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}