Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/api/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod tests {
use super::*;

use crate::assignment::MockAssignmentProvider;
use crate::catalog::MockCatalogProvider;
use crate::config::Config;
use crate::identity::MockIdentityProvider;
use crate::keystone::Service;
Expand Down Expand Up @@ -92,9 +93,11 @@ mod tests {
let identity_mock = MockIdentityProvider::default();
let token_mock = MockTokenProvider::default();
let assignment_mock = MockAssignmentProvider::default();
let catalog_mock = MockCatalogProvider::default();
let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down
7 changes: 7 additions & 0 deletions src/api/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use thiserror::Error;
use tracing::error;

use crate::assignment::error::AssignmentProviderError;
use crate::catalog::error::CatalogProviderError;
use crate::identity::error::IdentityProviderError;
use crate::resource::error::ResourceProviderError;
use crate::token::error::TokenProviderError;
Expand Down Expand Up @@ -65,6 +66,12 @@ pub enum KeystoneApiError {
source: AssignmentProviderError,
},

#[error(transparent)]
CatalogError {
#[from]
source: CatalogProviderError,
},

#[error(transparent)]
IdentityError {
#[from]
Expand Down
68 changes: 68 additions & 0 deletions src/api/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ use axum::{
response::{IntoResponse, Response},
};
use chrono::{DateTime, Utc};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::catalog::types::{Endpoint as ProviderEndpoint, Service};

#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)]
pub struct Versions {
pub versions: Values,
Expand Down Expand Up @@ -87,3 +90,68 @@ impl Default for MediaType {
}
}
}

/// A catalog object
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)]
pub struct Catalog(Vec<CatalogService>);

impl IntoResponse for Catalog {
fn into_response(self) -> Response {
(StatusCode::OK, Json(self)).into_response()
}
}

/// A catalog object
#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)]
#[builder(setter(strip_option, into))]
pub struct CatalogService {
pub r#type: Option<String>,
pub name: Option<String>,
pub id: String,
pub endpoints: Vec<Endpoint>,
}

impl From<(Service, Vec<ProviderEndpoint>)> for CatalogService {
fn from(value: (Service, Vec<ProviderEndpoint>)) -> Self {
Self {
id: value.0.id.clone(),
name: value.0.name.clone(),
r#type: value.0.r#type,
endpoints: value.1.into_iter().map(Into::into).collect(),
}
}
}

/// A Catalog Endpoint
#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)]
#[builder(setter(strip_option, into))]
pub struct Endpoint {
pub id: String,
pub url: String,
pub interface: String,
pub region: Option<String>,
pub region_id: Option<String>,
}

impl From<ProviderEndpoint> for Endpoint {
fn from(value: ProviderEndpoint) -> Self {
Self {
id: value.id.clone(),
interface: value.interface.clone(),
url: value.url.clone(),
region: value.region_id.clone(),
region_id: value.region_id.clone(),
}
}
}

impl From<Vec<(Service, Vec<ProviderEndpoint>)>> for Catalog {
fn from(value: Vec<(Service, Vec<ProviderEndpoint>)>) -> Self {
Self(
value
.into_iter()
.map(|(srv, eps)| (srv, eps).into())
.collect(),
)
}
}
7 changes: 7 additions & 0 deletions src/api/v3/auth/token/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ mod tests {
MockAssignmentProvider,
types::{Assignment, AssignmentType, Role as ProviderRole, RoleAssignmentListParameters},
};
use crate::catalog::MockCatalogProvider;
use crate::config::Config;
use crate::identity::{MockIdentityProvider, types::UserResponse};
use crate::keystone::Service;
Expand Down Expand Up @@ -224,9 +225,11 @@ mod tests {
});
let token_mock = MockTokenProvider::default();
let assignment_mock = MockAssignmentProvider::default();
let catalog_mock = MockCatalogProvider::default();
let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down Expand Up @@ -277,9 +280,11 @@ mod tests {
});
let token_mock = MockTokenProvider::default();
let assignment_mock = MockAssignmentProvider::default();
let catalog_mock = MockCatalogProvider::default();
let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down Expand Up @@ -344,6 +349,7 @@ mod tests {
});
let token_mock = MockTokenProvider::default();
let mut assignment_mock = MockAssignmentProvider::default();
let catalog_mock = MockCatalogProvider::default();
assignment_mock.expect_list_role_assignments().returning(
|_, _, q: &RoleAssignmentListParameters| {
Ok(vec![Assignment {
Expand All @@ -359,6 +365,7 @@ mod tests {
let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down
35 changes: 30 additions & 5 deletions src/api/v3/auth/token/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@
//
// SPDX-License-Identifier: Apache-2.0

use axum::{Json, extract::State, http::HeaderMap, http::StatusCode, response::IntoResponse};
use axum::{
Json,
extract::{Query, State},
http::HeaderMap,
http::StatusCode,
response::IntoResponse,
};
use base64::{Engine as _, engine::general_purpose::URL_SAFE};
use utoipa_axum::{router::OpenApiRouter, routes};
use uuid::Uuid;

use crate::api::{auth::Auth, common::get_domain, error::KeystoneApiError};
use crate::api::{Catalog, auth::Auth, common::get_domain, error::KeystoneApiError};
use crate::catalog::CatalogApi;
use crate::identity::IdentityApi;
use crate::identity::types::UserResponse;
use crate::keystone::ServiceState;
Expand All @@ -26,7 +33,7 @@ use crate::resource::{
types::{Domain, Project},
};
use crate::token::TokenApi;
use types::{AuthRequest, Scope, Token as ApiResponseToken, TokenResponse};
use types::{AuthRequest, CreateTokenParameters, Scope, Token as ApiResponseToken, TokenResponse};

mod common;
pub mod types;
Expand All @@ -40,14 +47,15 @@ pub(super) fn openapi_router() -> OpenApiRouter<ServiceState> {
post,
path = "/",
description = "Issue token",
params(),
params(CreateTokenParameters),
responses(
(status = OK, description = "Token object", body = TokenResponse),
),
tag="auth"
)]
#[tracing::instrument(name = "api::token_post", level = "debug", skip(state, req))]
async fn post(
Query(query): Query<CreateTokenParameters>,
State(state): State<ServiceState>,
Json(req): Json<AuthRequest>,
) -> Result<impl IntoResponse, KeystoneApiError> {
Expand Down Expand Up @@ -160,7 +168,7 @@ async fn post(
.expand_domain_information(&mut token, &state.db, &state.provider)
.await?;

let api_token = TokenResponse {
let mut api_token = TokenResponse {
token: ApiResponseToken::from_user_auth(
&state,
&token,
Expand All @@ -170,6 +178,15 @@ async fn post(
)
.await?,
};
if !query.nocatalog.is_some_and(|x| x) {
let catalog: Catalog = state
.provider
.get_catalog_provider()
.get_catalog(&state.db, true)
.await?
.into();
api_token.token.catalog = Some(catalog);
}
return Ok((
StatusCode::OK,
[(
Expand Down Expand Up @@ -260,6 +277,7 @@ mod tests {
use super::openapi_router;
use crate::api::v3::auth::token::types::TokenResponse;
use crate::assignment::MockAssignmentProvider;
use crate::catalog::MockCatalogProvider;
use crate::config::Config;
use crate::identity::{MockIdentityProvider, types::UserResponse};
use crate::keystone::Service;
Expand All @@ -276,6 +294,7 @@ mod tests {
let db = DatabaseConnection::Disconnected;
let config = Config::default();
let assignment_mock = MockAssignmentProvider::default();
let catalog_mock = MockCatalogProvider::default();
let mut identity_mock = MockIdentityProvider::default();
identity_mock.expect_get_user().returning(|_, id: &'_ str| {
Ok(Some(UserResponse {
Expand Down Expand Up @@ -315,6 +334,7 @@ mod tests {
let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down Expand Up @@ -382,6 +402,7 @@ mod tests {
let db = DatabaseConnection::Disconnected;
let config = Config::default();
let mut assignment_mock = MockAssignmentProvider::default();
let mut catalog_mock = MockCatalogProvider::default();
assignment_mock
.expect_list_role_assignments()
.returning(|_, _, _| Ok(Vec::new()));
Expand Down Expand Up @@ -449,10 +470,14 @@ mod tests {
token_mock
.expect_encode_token()
.returning(|_| Ok("token".to_string()));
catalog_mock
.expect_get_catalog()
.returning(|_, _| Ok(Vec::new()));

let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down
15 changes: 14 additions & 1 deletion src/api/v3/auth/token/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ use axum::{
use chrono::{DateTime, Utc};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use utoipa::{IntoParams, ToSchema};

use crate::api::error::TokenError;
use crate::api::types::Catalog;
use crate::api::v3::role::types::Role;
use crate::identity::types as identity_types;
use crate::resource::types as resource_provider_types;
Expand Down Expand Up @@ -74,6 +75,11 @@ pub struct Token {
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub roles: Option<Vec<Role>>,

/// A catalog object.
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub catalog: Option<Catalog>,
}

#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)]
Expand Down Expand Up @@ -267,3 +273,10 @@ impl TryFrom<&BackendToken> for Token {
Ok(token.build()?)
}
}

#[derive(Clone, Debug, Default, Deserialize, Serialize, IntoParams)]
pub struct CreateTokenParameters {
/// The authentication response excludes the service catalog. By default, the response includes
/// the service catalog.
pub nocatalog: Option<bool>,
}
3 changes: 3 additions & 0 deletions src/api/v3/role/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mod tests {
MockAssignmentProvider,
types::{Role, RoleListParameters},
};
use crate::catalog::MockCatalogProvider;
use crate::config::Config;
use crate::identity::MockIdentityProvider;
use crate::keystone::{Service, ServiceState};
Expand All @@ -136,11 +137,13 @@ mod tests {
..Default::default()
}))
});
let catalog_mock = MockCatalogProvider::default();
let identity_mock = MockIdentityProvider::default();

let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down
3 changes: 3 additions & 0 deletions src/api/v3/role_assignment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ mod tests {
MockAssignmentProvider,
types::{Assignment, AssignmentType, RoleAssignmentListParameters},
};
use crate::catalog::MockCatalogProvider;
use crate::config::Config;
use crate::identity::MockIdentityProvider;
use crate::keystone::{Service, ServiceState};
Expand All @@ -107,10 +108,12 @@ mod tests {
}))
});
let identity_mock = MockIdentityProvider::default();
let catalog_mock = MockCatalogProvider::default();

let provider = ProviderBuilder::default()
.config(config.clone())
.assignment(assignment_mock)
.catalog(catalog_mock)
.identity(identity_mock)
.resource(resource_mock)
.token(token_mock)
Expand Down
16 changes: 16 additions & 0 deletions src/catalog/backends.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pub mod error;
pub mod sql;
Loading