Skip to content

Commit ae0b260

Browse files
authored
feat: Enable system_scope token support (#498)
Add support to authorize with the system scope. Corresponding payload is added, as well as token validation.
1 parent b0fe3c6 commit ae0b260

File tree

12 files changed

+398
-102
lines changed

12 files changed

+398
-102
lines changed

src/api/v3/auth/token/common.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,7 @@ pub(super) async fn get_authz_info(
131131
return Err(KeystoneApiError::Unauthorized(None));
132132
}
133133
}
134-
Some(Scope::System(_scope)) => {
135-
todo!()
136-
}
134+
Some(Scope::System(_scope)) => AuthzInfo::System,
137135
None => AuthzInfo::Unscoped,
138136
};
139137
authz_info.validate()?;

src/api/v3/auth/token/token_impl.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
use crate::api::common;
1616
use crate::api::error::KeystoneApiError;
17-
use crate::api::v3::auth::token::types::{Token, TokenBuilder, UserBuilder};
17+
use crate::api::v3::auth::token::types::{System, Token, TokenBuilder, UserBuilder};
1818
use crate::api::v3::role::types::Role;
1919
use crate::identity::IdentityApi;
2020
use crate::keystone::ServiceState;
@@ -38,6 +38,7 @@ impl Token {
3838
response.audit_ids(token.audit_ids().clone());
3939
response.methods(token.methods().clone());
4040
response.expires_at(*token.expires_at());
41+
response.issued_at(*token.issued_at());
4142

4243
let user = if let Some(user) = token.user() {
4344
user
@@ -75,15 +76,7 @@ impl Token {
7576
}
7677

7778
match token {
78-
ProviderToken::Unscoped(_token) => {}
79-
ProviderToken::DomainScope(token) => {
80-
if domain.is_none() {
81-
domain = Some(
82-
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
83-
);
84-
}
85-
}
86-
ProviderToken::ProjectScope(token) => {
79+
ProviderToken::ApplicationCredential(token) => {
8780
if project.is_none() {
8881
project = Some(
8982
state
@@ -98,7 +91,22 @@ impl Token {
9891
);
9992
}
10093
}
101-
ProviderToken::ApplicationCredential(token) => {
94+
ProviderToken::DomainScope(token) => {
95+
if domain.is_none() {
96+
domain = Some(
97+
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
98+
);
99+
}
100+
}
101+
ProviderToken::FederationUnscoped(_token) => {}
102+
ProviderToken::FederationDomainScope(token) => {
103+
if domain.is_none() {
104+
domain = Some(
105+
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
106+
);
107+
}
108+
}
109+
ProviderToken::FederationProjectScope(token) => {
102110
if project.is_none() {
103111
project = Some(
104112
state
@@ -113,15 +121,7 @@ impl Token {
113121
);
114122
}
115123
}
116-
ProviderToken::FederationUnscoped(_token) => {}
117-
ProviderToken::FederationDomainScope(token) => {
118-
if domain.is_none() {
119-
domain = Some(
120-
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
121-
);
122-
}
123-
}
124-
ProviderToken::FederationProjectScope(token) => {
124+
ProviderToken::ProjectScope(token) => {
125125
if project.is_none() {
126126
project = Some(
127127
state
@@ -151,6 +151,9 @@ impl Token {
151151
);
152152
}
153153
}
154+
ProviderToken::SystemScope(_token) => {
155+
response.system(System { all: true });
156+
}
154157
ProviderToken::Trust(token) => {
155158
if project.is_none() {
156159
project = Some(
@@ -182,6 +185,7 @@ impl Token {
182185
);
183186
}
184187
}
188+
ProviderToken::Unscoped(_token) => {}
185189
}
186190

187191
if let Some(domain) = domain {

src/api/v3/auth/token/types.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub struct Token {
6262
/// The date and time when the token expires.
6363
pub expires_at: DateTime<Utc>,
6464

65+
/// The date and time when the token was issued.
66+
pub issued_at: DateTime<Utc>,
67+
6568
// # Subject
6669
/// A user object.
6770
//#[builder(default)]
@@ -98,6 +101,12 @@ pub struct Token {
98101
#[validate(nested)]
99102
pub roles: Option<Vec<Role>>,
100103

104+
/// A system object.
105+
#[serde(skip_serializing_if = "Option::is_none")]
106+
#[builder(default)]
107+
#[validate(nested)]
108+
pub system: Option<System>,
109+
101110
/// A catalog object.
102111
#[serde(skip_serializing_if = "Option::is_none")]
103112
#[builder(default)]
@@ -286,3 +295,12 @@ pub struct ValidateTokenParameters {
286295
/// return a 404 exception.
287296
pub allow_expired: Option<bool>,
288297
}
298+
299+
/// System information.
300+
#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema, Validate)]
301+
#[builder(build_fn(error = "BuilderError"))]
302+
#[builder(setter(into, strip_option))]
303+
pub struct System {
304+
/// All
305+
pub all: bool,
306+
}

src/api/v4/auth/token/token_impl.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use crate::api::common;
1616
use crate::api::error::KeystoneApiError;
1717
use crate::api::v3::role::types::Role;
18-
use crate::api::v4::auth::token::types::{Token, TokenBuilder, UserBuilder};
18+
use crate::api::v4::auth::token::types::{System, Token, TokenBuilder, UserBuilder};
1919
use crate::identity::IdentityApi;
2020
use crate::keystone::ServiceState;
2121
use crate::resource::{
@@ -37,6 +37,7 @@ impl Token {
3737
response.audit_ids(token.audit_ids().clone());
3838
response.methods(token.methods().clone());
3939
response.expires_at(*token.expires_at());
40+
response.issued_at(*token.issued_at());
4041

4142
let user = if let Some(user) = token.user() {
4243
user
@@ -74,15 +75,7 @@ impl Token {
7475
}
7576

7677
match token {
77-
ProviderToken::Unscoped(_token) => {}
78-
ProviderToken::DomainScope(token) => {
79-
if domain.is_none() {
80-
domain = Some(
81-
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
82-
);
83-
}
84-
}
85-
ProviderToken::ProjectScope(token) => {
78+
ProviderToken::ApplicationCredential(token) => {
8679
if project.is_none() {
8780
project = Some(
8881
state
@@ -97,7 +90,22 @@ impl Token {
9790
);
9891
}
9992
}
100-
ProviderToken::ApplicationCredential(token) => {
93+
ProviderToken::DomainScope(token) => {
94+
if domain.is_none() {
95+
domain = Some(
96+
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
97+
);
98+
}
99+
}
100+
ProviderToken::FederationUnscoped(_token) => {}
101+
ProviderToken::FederationDomainScope(token) => {
102+
if domain.is_none() {
103+
domain = Some(
104+
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
105+
);
106+
}
107+
}
108+
ProviderToken::FederationProjectScope(token) => {
101109
if project.is_none() {
102110
project = Some(
103111
state
@@ -112,15 +120,7 @@ impl Token {
112120
);
113121
}
114122
}
115-
ProviderToken::FederationUnscoped(_token) => {}
116-
ProviderToken::FederationDomainScope(token) => {
117-
if domain.is_none() {
118-
domain = Some(
119-
common::get_domain(state, Some(&token.domain_id), None::<&str>).await?,
120-
);
121-
}
122-
}
123-
ProviderToken::FederationProjectScope(token) => {
123+
ProviderToken::ProjectScope(token) => {
124124
if project.is_none() {
125125
project = Some(
126126
state
@@ -150,6 +150,9 @@ impl Token {
150150
);
151151
}
152152
}
153+
ProviderToken::SystemScope(_token) => {
154+
response.system(System { all: true });
155+
}
153156
ProviderToken::Trust(token) => {
154157
if project.is_none() {
155158
project = Some(
@@ -169,6 +172,7 @@ impl Token {
169172
response.trust(trust);
170173
}
171174
}
175+
ProviderToken::Unscoped(_token) => {}
172176
}
173177

174178
if let Some(domain) = domain {

src/api/v4/auth/token/types.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,16 @@ pub struct Token {
6262
/// The date and time when the token expires.
6363
pub expires_at: DateTime<Utc>,
6464

65+
/// The date and time when the token was issued.
66+
pub issued_at: DateTime<Utc>,
67+
68+
// # Subject
6569
/// A user object.
6670
//#[builder(default)]
6771
#[validate(nested)]
6872
pub user: User,
6973

74+
// # Scope
7075
/// A domain object including the id and name representing the domain the
7176
/// token is scoped to. This is only included in tokens that are scoped
7277
/// to a domain.
@@ -83,12 +88,19 @@ pub struct Token {
8388
#[validate(nested)]
8489
pub project: Option<Project>,
8590

91+
/// A system object.
92+
#[serde(skip_serializing_if = "Option::is_none")]
93+
#[builder(default)]
94+
#[validate(nested)]
95+
pub system: Option<System>,
96+
8697
/// A trust object.
8798
#[serde(skip_serializing_if = "Option::is_none", rename = "OS-TRUST:trust")]
8899
#[builder(default)]
89100
#[validate(nested)]
90101
pub trust: Option<TokenTrustRepr>,
91102

103+
// # Roles on the scope.
92104
/// A list of role objects
93105
#[serde(skip_serializing_if = "Option::is_none")]
94106
#[builder(default)]
@@ -275,3 +287,12 @@ pub struct ValidateTokenParameters {
275287
/// return a 404 exception.
276288
pub allow_expired: Option<bool>,
277289
}
290+
291+
/// System information.
292+
#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema, Validate)]
293+
#[builder(build_fn(error = "BuilderError"))]
294+
#[builder(setter(into, strip_option))]
295+
pub struct System {
296+
/// All
297+
pub all: bool,
298+
}

src/assignment/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl AssignmentApi for AssignmentProvider {
217217
r#type: RoleAssignmentTargetType::Domain,
218218
inherited: Some(false),
219219
});
220-
} else if let Some(val) = &params.system {
220+
} else if let Some(val) = &params.system_id {
221221
targets.push(RoleAssignmentTarget {
222222
id: val.clone(),
223223
r#type: RoleAssignmentTargetType::System,

src/assignment/types/assignment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ pub struct RoleAssignmentListParameters {
230230
/// Query role assignments on the system.
231231
#[builder(default)]
232232
#[validate(length(max = 64))]
233-
pub system: Option<String>,
233+
pub system_id: Option<String>,
234234

235235
// #[builder(default)]
236236
// pub inherited: Option<bool>,

src/auth/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub enum AuthzInfo {
156156
Domain(Domain),
157157
/// Project scope.
158158
Project(Project),
159+
/// System scope.
160+
System,
159161
/// Trust scope.
160162
Trust(Trust),
161163
/// Unscoped.
@@ -180,6 +182,7 @@ impl AuthzInfo {
180182
return Err(AuthenticationError::Unauthorized);
181183
}
182184
}
185+
AuthzInfo::System => {}
183186
AuthzInfo::Trust(_) => {}
184187
AuthzInfo::Unscoped => {}
185188
}
@@ -295,6 +298,13 @@ mod tests {
295298
}
296299
}
297300

301+
#[test]
302+
#[traced_test]
303+
fn test_authz_validate_system() {
304+
let authz = AuthzInfo::System;
305+
assert!(authz.validate().is_ok());
306+
}
307+
298308
#[test]
299309
#[traced_test]
300310
fn test_authz_validate_unscoped() {

0 commit comments

Comments
 (0)