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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/api-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ serde.workspace = true
serde_json.workspace = true
serde_urlencoded = { workspace = true, optional = true }
thiserror.workspace = true
utoipa = { workspace = true, features = ["chrono"], optional = true }
utoipa = { workspace = true, features = ["chrono", "url"], optional = true }
validator = { workspace = true, features = ["derive"], optional = true }
webauthn-rs-proto.workspace = true
url = { workspace = true, optional = true }
Expand Down
11 changes: 11 additions & 0 deletions crates/api-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ impl KeystoneApiError {
//}
}

pub fn not_found<R, I>(resource: R, identifier: I) -> Self
where
R: Into<String>,
I: Into<String>,
{
Self::NotFound {
resource: resource.into(),
identifier: identifier.into(),
}
}

pub fn unauthorized<E, C>(error: E, context: Option<C>) -> Self
where
E: std::error::Error + Send + Sync + 'static,
Expand Down
4 changes: 4 additions & 0 deletions crates/api-types/src/error_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ impl From<SpiffeProviderError> for KeystoneApiError {
fn from(value: SpiffeProviderError) -> Self {
match value {
ref err @ SpiffeProviderError::Conflict(..) => Self::BadRequest(err.to_string()),
SpiffeProviderError::BindingNotFound(svid) => Self::NotFound {
resource: "spiffe binding".into(),
identifier: svid,
},
other => Self::InternalError(other.to_string()),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/api-types/src/v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// SPDX-License-Identifier: Apache-2.0
//! # V4 API types
pub mod auth;
pub mod spiffe;
pub mod token_restriction;
pub mod user;

Expand Down
21 changes: 21 additions & 0 deletions crates/api-types/src/v4/spiffe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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
//! SPIFFE API types.

pub mod binding;

pub use binding::*;

#[cfg(feature = "conv")]
mod binding_conv;
176 changes: 176 additions & 0 deletions crates/api-types/src/v4/spiffe/binding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// 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
//! SPIFFE binding API types.

use serde::{Deserialize, Serialize};
#[cfg(feature = "validate")]
use validator::Validate;

use crate::Link;

/// SPIFFE authorization information.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum SpiffeAuthorization {
/// Domain scope authorization.
Domain {
/// Domain ID.
domain_id: String,
/// Role IDs to authorize.
role_ids: Option<Vec<String>>,
},
/// Project scope authorization.
Project {
/// Project ID.
project_id: String,
/// Role IDs to authorize.
role_ids: Option<Vec<String>>,
},
/// System scope authorization.
System {
/// System ID.
system_id: String,
/// Role IDs to authorize.
role_ids: Option<Vec<String>>,
},
}

/// SPIFFE binding information.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBinding {
/// Domain ID the binding belongs to.
#[cfg_attr(feature = "openapi", schema(nullable = false, max_length = 64))]
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
pub domain_id: String,

/// Flag indicating system-wide identity (system scope).
pub is_system: bool,

/// SPIFFE SVID identifier.
#[cfg_attr(feature = "openapi", schema(max_length = 255, format = Uri))]
#[cfg_attr(feature = "validate", validate(length(max = 255)))]
pub svid: String,

/// Optional user ID the binding maps to.
#[cfg_attr(feature = "openapi", schema(nullable = false, max_length = 64))]
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,

/// List of authorizations bound to this identity.
#[serde(skip_serializing_if = "Option::is_none")]
pub authorizations: Option<Vec<SpiffeAuthorization>>,
}

/// SPIFFE binding response.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingResponse {
/// Binding object.
#[cfg_attr(feature = "validate", validate(nested))]
pub binding: SpiffeBinding,
}

/// New SPIFFE binding data.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingCreate {
/// Domain ID the binding belongs to.
#[cfg_attr(feature = "openapi", schema(nullable = false, max_length = 64))]
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
pub domain_id: String,

/// Flag indicating system-wide identity (system scope).
pub is_system: bool,

/// SPIFFE SVID identifier.
#[cfg_attr(feature = "openapi", schema(max_length = 255, format = Uri))]
#[cfg_attr(feature = "validate", validate(length(max = 255)))]
pub svid: String,

/// Optional user ID the binding maps to.
#[cfg_attr(feature = "openapi", schema(nullable = false, max_length = 64))]
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,

/// List of authorizations to bind.
#[serde(skip_serializing_if = "Option::is_none")]
pub authorizations: Option<Vec<SpiffeAuthorization>>,
}

/// SPIFFE binding create request.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingCreateRequest {
/// Binding object.
#[cfg_attr(feature = "validate", validate(nested))]
pub binding: SpiffeBindingCreate,
}

/// Update SPIFFE binding data.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingUpdate {
/// List of authorizations to update.
#[serde(skip_serializing_if = "Option::is_none")]
pub authorizations: Option<Vec<SpiffeAuthorization>>,
}

/// SPIFFE binding update request.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingUpdateRequest {
/// Binding update object.
#[cfg_attr(feature = "validate", validate(nested))]
pub binding: SpiffeBindingUpdate,
}

/// SPIFFE binding list parameters.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::IntoParams))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingListParameters {
/// Domain ID to filter bindings.
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
#[serde(skip_serializing_if = "Option::is_none")]
pub domain_id: Option<String>,

/// User ID to filter bindings.
#[cfg_attr(feature = "validate", validate(length(max = 64)))]
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
}

/// SPIFFE binding list response.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "validate", derive(validator::Validate))]
pub struct SpiffeBindingList {
/// Collection of binding objects.
#[cfg_attr(feature = "validate", validate(nested))]
pub bindings: Vec<SpiffeBinding>,

/// Pagination links.
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<Vec<Link>>,
}
123 changes: 123 additions & 0 deletions crates/api-types/src/v4/spiffe/binding_conv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// 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
//! SPIFFE binding conversion implementations.

use openstack_keystone_core_types::spiffe as provider_types;

use crate::v4::spiffe::binding as api_types;

impl From<api_types::SpiffeAuthorization> for provider_types::SpiffeAuthorization {
fn from(value: api_types::SpiffeAuthorization) -> Self {
match value {
api_types::SpiffeAuthorization::Domain {
domain_id,
role_ids,
} => Self::Domain {
domain_id,
role_ids,
},
api_types::SpiffeAuthorization::Project {
project_id,
role_ids,
} => Self::Project {
project_id,
role_ids,
},
api_types::SpiffeAuthorization::System {
system_id,
role_ids,
} => Self::System {
system_id,
role_ids,
},
}
}
}

impl From<provider_types::SpiffeAuthorization> for api_types::SpiffeAuthorization {
fn from(value: provider_types::SpiffeAuthorization) -> Self {
match value {
provider_types::SpiffeAuthorization::Domain {
domain_id,
role_ids,
} => Self::Domain {
domain_id,
role_ids,
},
provider_types::SpiffeAuthorization::Project {
project_id,
role_ids,
} => Self::Project {
project_id,
role_ids,
},
provider_types::SpiffeAuthorization::System {
system_id,
role_ids,
} => Self::System {
system_id,
role_ids,
},
}
}
}

impl From<provider_types::SpiffeBinding> for api_types::SpiffeBinding {
fn from(value: provider_types::SpiffeBinding) -> Self {
Self {
domain_id: value.domain_id,
is_system: value.is_system,
svid: value.svid,
user_id: value.user_id,
authorizations: value
.authorizations
.map(|auths| auths.into_iter().map(Into::into).collect()),
}
}
}

impl From<api_types::SpiffeBindingListParameters> for provider_types::SpiffeBindingListParameters {
fn from(value: api_types::SpiffeBindingListParameters) -> Self {
Self {
domain_id: value.domain_id,
user_id: value.user_id,
}
}
}

impl From<api_types::SpiffeBindingCreateRequest> for provider_types::SpiffeBindingCreate {
fn from(value: api_types::SpiffeBindingCreateRequest) -> Self {
Self {
domain_id: value.binding.domain_id,
is_system: value.binding.is_system,
svid: value.binding.svid,
user_id: value.binding.user_id,
authorizations: value
.binding
.authorizations
.map(|auths| auths.into_iter().map(Into::into).collect()),
}
}
}

impl From<api_types::SpiffeBindingUpdateRequest> for provider_types::SpiffeBindingUpdate {
fn from(value: api_types::SpiffeBindingUpdateRequest) -> Self {
Self {
authorizations: value
.binding
.authorizations
.map(|auths| auths.into_iter().map(Into::into).collect()),
}
}
}
1 change: 1 addition & 0 deletions crates/core-types/src/spiffe/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::role::RoleRef;

/// Authorization information.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SpiffeAuthorization {
/// Domain scope.
Domain {
Expand Down
2 changes: 1 addition & 1 deletion crates/core-types/src/spiffe/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub enum SpiffeProviderError {
RaftNotAvailable,

/// Raft storage error.
#[error("raft storage error in the spiffe provider")]
#[error("raft storage error in the spiffe provider: {source}")]
RaftStoreError {
/// The source of the error.
#[from]
Expand Down
Loading
Loading