use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::error;
use crate::api::identity::v3::auth::token::create as token_v3;
use crate::auth::authtoken::AuthTokenError;
use crate::config;
use crate::types::identity::v3::{self as types_v3, AuthResponse, Domain, Project};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AuthTokenScopeError {
#[error("Auth data is missing")]
MissingAuthData,
#[error("Cannot determine authorization scope from config")]
MissingScope,
#[error("Cannot construct project scope information from config: {}", source)]
ProjectBuild {
#[from]
source: token_v3::ProjectBuilderError,
},
#[error(
"Cannot construct project scope domain information from config: {}",
source
)]
ProjectDomainBuild {
#[from]
source: token_v3::ProjectDomainBuilderError,
},
#[error(
"Cannot construct project scope domain information from config: {}",
source
)]
ScopeDomainBuild {
#[from]
source: token_v3::ScopeDomainBuilderError,
},
#[error("Cannot construct auth scope information from config: {}", source)]
ScopeBuild {
#[from]
source: token_v3::ScopeBuilderError,
},
}
impl From<token_v3::ProjectBuilderError> for AuthTokenError {
fn from(source: token_v3::ProjectBuilderError) -> Self {
Self::Scope {
source: source.into(),
}
}
}
impl From<token_v3::ProjectDomainBuilderError> for AuthTokenError {
fn from(source: token_v3::ProjectDomainBuilderError) -> Self {
Self::Scope {
source: source.into(),
}
}
}
impl From<token_v3::ScopeDomainBuilderError> for AuthTokenError {
fn from(source: token_v3::ScopeDomainBuilderError) -> Self {
Self::Scope {
source: source.into(),
}
}
}
impl From<token_v3::ScopeBuilderError> for AuthTokenError {
fn from(source: token_v3::ScopeBuilderError) -> Self {
Self::Scope {
source: source.into(),
}
}
}
#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize, Debug)]
pub enum AuthTokenScope {
Project(Project),
Domain(Domain),
Unscoped,
}
impl TryFrom<&config::CloudConfig> for AuthTokenScope {
type Error = AuthTokenScopeError;
fn try_from(config: &config::CloudConfig) -> Result<Self, Self::Error> {
let auth = config.auth.clone().ok_or(Self::Error::MissingAuthData)?;
if auth.project_id.is_some() || auth.project_name.is_some() {
Ok(AuthTokenScope::Project(Project {
id: auth.project_id.clone(),
name: auth.project_name.clone(),
domain: types_v3::get_domain(auth.project_domain_id, auth.project_domain_name),
}))
} else if auth.domain_id.is_some() || auth.domain_name.is_some() {
Ok(AuthTokenScope::Domain(Domain {
id: auth.domain_id.clone(),
name: auth.domain_name.clone(),
}))
} else {
Ok(AuthTokenScope::Unscoped)
}
}
}
impl From<&AuthResponse> for AuthTokenScope {
fn from(auth: &AuthResponse) -> Self {
if let Some(project) = &auth.token.project {
Self::Project(project.clone())
} else if let Some(domain) = &auth.token.domain {
Self::Domain(domain.clone())
} else {
Self::Unscoped
}
}
}
impl TryFrom<&AuthTokenScope> for token_v3::Scope<'_> {
type Error = AuthTokenError;
fn try_from(scope: &AuthTokenScope) -> Result<Self, Self::Error> {
let mut scope_builder = token_v3::ScopeBuilder::default();
match scope {
AuthTokenScope::Project(project) => {
let mut project_builder = token_v3::ProjectBuilder::default();
if let Some(val) = &project.id {
project_builder.id(val.clone());
}
if let Some(val) = &project.name {
project_builder.name(val.clone());
}
if let Some(domain) = &project.domain {
let mut domain_builder = token_v3::ProjectDomainBuilder::default();
if let Some(val) = &domain.id {
domain_builder.id(val.clone());
}
if let Some(val) = &domain.name {
domain_builder.name(val.clone());
}
project_builder.domain(domain_builder.build()?);
}
scope_builder.project(project_builder.build()?);
}
AuthTokenScope::Domain(domain) => {
let mut domain_builder = token_v3::ScopeDomainBuilder::default();
if let Some(val) = &domain.id {
domain_builder.id(val.clone());
}
if let Some(val) = &domain.name {
domain_builder.name(val.clone());
}
scope_builder.domain(domain_builder.build()?);
}
AuthTokenScope::Unscoped => {}
}
Ok(scope_builder.build()?)
}
}
impl TryFrom<&config::CloudConfig> for token_v3::Scope<'_> {
type Error = AuthTokenError;
fn try_from(config: &config::CloudConfig) -> Result<Self, Self::Error> {
let auth = config.auth.clone().ok_or(Self::Error::MissingAuthData)?;
let mut scope = token_v3::ScopeBuilder::default();
if auth.project_id.is_some() || auth.project_name.is_some() {
let mut project_scope = token_v3::ProjectBuilder::default();
if auth.project_domain_name.is_some() || auth.project_domain_id.is_some() {
let mut project_domain = token_v3::ProjectDomainBuilder::default();
if let Some(val) = auth.project_domain_id {
project_domain.id(val);
}
if let Some(val) = auth.project_domain_name {
project_domain.name(val);
}
project_scope.domain(project_domain.build()?);
};
if let Some(val) = auth.project_id {
project_scope.id(val);
}
if let Some(val) = auth.project_name {
project_scope.name(val);
}
scope.project(project_scope.build()?);
} else if auth.domain_id.is_some() || auth.domain_name.is_some() {
let mut domain_scope = token_v3::ScopeDomainBuilder::default();
if let Some(val) = auth.domain_id {
domain_scope.id(val);
}
if let Some(val) = auth.domain_name {
domain_scope.name(val);
}
scope.domain(domain_scope.build()?);
} else {
return Err(Self::Error::Scope {
source: AuthTokenScopeError::MissingScope,
});
}
Ok(scope.build()?)
}
}