photoncloud-monorepo/iam/crates/iam-api/src/conversions.rs

478 lines
17 KiB
Rust

//! Proto <-> types conversions
//!
//! Converts between protobuf types and IAM domain types.
use iam_types::{
Condition as TypesCondition, ConditionExpr as TypesConditionExpr,
Organization as TypesOrganization, Permission as TypesPermission,
PolicyBinding as TypesBinding, Principal as TypesPrincipal,
PrincipalKind as TypesPrincipalKind, PrincipalRef as TypesPrincipalRef,
Project as TypesProject, Role as TypesRole, Scope as TypesScope,
};
use crate::proto::{
self, condition_expr, scope, Condition, ConditionExpr, Organization, Permission, PolicyBinding,
Principal, PrincipalKind, PrincipalRef, Project, Role, Scope,
};
// ============================================================================
// PrincipalKind conversions
// ============================================================================
/// Convert types PrincipalKind to proto enum value
pub fn principal_kind_to_proto(kind: &TypesPrincipalKind) -> i32 {
match kind {
TypesPrincipalKind::User => PrincipalKind::User as i32,
TypesPrincipalKind::ServiceAccount => PrincipalKind::ServiceAccount as i32,
TypesPrincipalKind::Group => PrincipalKind::Group as i32,
}
}
/// Convert proto enum value to types PrincipalKind
pub fn proto_to_principal_kind(value: i32) -> Result<TypesPrincipalKind, &'static str> {
match PrincipalKind::try_from(value) {
Ok(PrincipalKind::User) => Ok(TypesPrincipalKind::User),
Ok(PrincipalKind::ServiceAccount) => Ok(TypesPrincipalKind::ServiceAccount),
Ok(PrincipalKind::Group) => Ok(TypesPrincipalKind::Group),
_ => Err("invalid principal kind"),
}
}
// ============================================================================
// PrincipalRef conversions
// ============================================================================
impl From<TypesPrincipalRef> for PrincipalRef {
fn from(r: TypesPrincipalRef) -> Self {
PrincipalRef {
kind: principal_kind_to_proto(&r.kind),
id: r.id,
}
}
}
/// Convert proto PrincipalRef to types PrincipalRef
pub fn proto_to_principal_ref(r: &PrincipalRef) -> Result<TypesPrincipalRef, &'static str> {
let kind = proto_to_principal_kind(r.kind)?;
Ok(TypesPrincipalRef::new(kind, &r.id))
}
// ============================================================================
// Principal conversions
// ============================================================================
impl From<TypesPrincipal> for Principal {
fn from(p: TypesPrincipal) -> Self {
Principal {
id: p.id,
kind: principal_kind_to_proto(&p.kind),
name: p.name,
org_id: p.org_id,
project_id: p.project_id,
email: p.email,
oidc_sub: p.oidc_sub,
node_id: p.node_id,
metadata: p.metadata,
created_at: p.created_at,
updated_at: p.updated_at,
enabled: p.enabled,
}
}
}
impl From<Principal> for TypesPrincipal {
fn from(p: Principal) -> Self {
TypesPrincipal {
id: p.id,
kind: proto_to_principal_kind(p.kind).unwrap_or(TypesPrincipalKind::User),
name: p.name,
org_id: p.org_id,
project_id: p.project_id,
email: p.email,
oidc_sub: p.oidc_sub,
node_id: p.node_id,
metadata: p.metadata,
created_at: p.created_at,
updated_at: p.updated_at,
enabled: p.enabled,
}
}
}
// ============================================================================
// Organization / Project conversions
// ============================================================================
impl From<TypesOrganization> for Organization {
fn from(org: TypesOrganization) -> Self {
Organization {
id: org.id,
name: org.name,
description: org.description,
metadata: org.metadata,
created_at: org.created_at,
updated_at: org.updated_at,
enabled: org.enabled,
}
}
}
impl From<Organization> for TypesOrganization {
fn from(org: Organization) -> Self {
TypesOrganization {
id: org.id,
name: org.name,
description: org.description,
metadata: org.metadata,
created_at: org.created_at,
updated_at: org.updated_at,
enabled: org.enabled,
}
}
}
impl From<TypesProject> for Project {
fn from(project: TypesProject) -> Self {
Project {
id: project.id,
org_id: project.org_id,
name: project.name,
description: project.description,
metadata: project.metadata,
created_at: project.created_at,
updated_at: project.updated_at,
enabled: project.enabled,
}
}
}
impl From<Project> for TypesProject {
fn from(project: Project) -> Self {
TypesProject {
id: project.id,
org_id: project.org_id,
name: project.name,
description: project.description,
metadata: project.metadata,
created_at: project.created_at,
updated_at: project.updated_at,
enabled: project.enabled,
}
}
}
// ============================================================================
// Scope conversions
// ============================================================================
impl From<TypesScope> for Scope {
fn from(s: TypesScope) -> Self {
Scope {
scope: Some(match s {
TypesScope::System => scope::Scope::System(true),
TypesScope::Org { id } => scope::Scope::Org(proto::OrgScope { id }),
TypesScope::Project { id, org_id } => {
scope::Scope::Project(proto::ProjectScope { id, org_id })
}
TypesScope::Resource {
id,
project_id,
org_id,
} => scope::Scope::Resource(proto::ResourceScope {
id,
project_id,
org_id,
}),
}),
}
}
}
impl From<Scope> for TypesScope {
fn from(s: Scope) -> Self {
match s.scope {
Some(scope::Scope::System(true)) => TypesScope::System,
Some(scope::Scope::Org(org)) => TypesScope::org(org.id),
Some(scope::Scope::Project(proj)) => TypesScope::project(proj.id, proj.org_id),
Some(scope::Scope::Resource(res)) => {
TypesScope::resource(res.id, res.project_id, res.org_id)
}
_ => TypesScope::System,
}
}
}
// ============================================================================
// Permission conversions
// ============================================================================
impl From<TypesPermission> for Permission {
fn from(p: TypesPermission) -> Self {
Permission {
action: p.action,
resource_pattern: p.resource_pattern,
condition: p.condition.map(|c| c.into()),
}
}
}
impl From<Permission> for TypesPermission {
fn from(p: Permission) -> Self {
TypesPermission {
action: p.action,
resource_pattern: p.resource_pattern,
condition: p.condition.map(|c| c.into()),
}
}
}
// ============================================================================
// Role conversions
// ============================================================================
impl From<TypesRole> for Role {
fn from(r: TypesRole) -> Self {
Role {
name: r.name,
display_name: r.display_name,
description: r.description,
scope: Some(r.scope.into()),
permissions: r.permissions.into_iter().map(|p| p.into()).collect(),
builtin: r.builtin,
created_at: r.created_at,
updated_at: r.updated_at,
}
}
}
impl From<Role> for TypesRole {
fn from(r: Role) -> Self {
TypesRole {
name: r.name,
display_name: r.display_name,
description: r.description,
scope: r
.scope
.unwrap_or(Scope {
scope: Some(scope::Scope::System(true)),
})
.into(),
permissions: r.permissions.into_iter().map(Into::into).collect(),
builtin: r.builtin,
created_at: r.created_at,
updated_at: r.updated_at,
}
}
}
// ============================================================================
// PolicyBinding conversions
// ============================================================================
impl From<TypesBinding> for PolicyBinding {
fn from(b: TypesBinding) -> Self {
PolicyBinding {
id: b.id,
principal: Some(b.principal_ref.into()),
role: b.role_ref,
scope: Some(b.scope.into()),
condition: b.condition.map(|c| c.into()),
created_at: b.created_at,
updated_at: b.updated_at,
created_by: b.created_by,
expires_at: b.expires_at,
enabled: b.enabled,
}
}
}
impl From<PolicyBinding> for TypesBinding {
fn from(b: PolicyBinding) -> Self {
TypesBinding {
id: b.id,
principal_ref: proto_to_principal_ref(&b.principal.unwrap_or_else(|| PrincipalRef {
kind: PrincipalKind::User as i32,
id: String::new(),
}))
.unwrap_or_else(|_| TypesPrincipalRef::user("")),
role_ref: b.role,
scope: b
.scope
.unwrap_or(Scope {
scope: Some(scope::Scope::System(true)),
})
.into(),
condition: b.condition.map(|c| c.into()),
created_at: b.created_at,
updated_at: b.updated_at,
created_by: b.created_by,
expires_at: b.expires_at,
enabled: b.enabled,
}
}
}
// ============================================================================
// Condition conversions
// ============================================================================
impl From<TypesCondition> for Condition {
fn from(c: TypesCondition) -> Self {
Condition {
expression: Some(c.expression.into()),
}
}
}
impl From<Condition> for TypesCondition {
fn from(c: Condition) -> Self {
TypesCondition {
expression: c
.expression
.map(|e| e.into())
.unwrap_or(TypesConditionExpr::And(vec![])),
}
}
}
impl From<TypesConditionExpr> for ConditionExpr {
fn from(e: TypesConditionExpr) -> Self {
let expr = match e {
TypesConditionExpr::StringEquals { key, value } => {
condition_expr::Expr::StringEquals(proto::StringEqualsExpr { key, value })
}
TypesConditionExpr::StringNotEquals { key, value } => {
condition_expr::Expr::StringNotEquals(proto::StringNotEqualsExpr { key, value })
}
TypesConditionExpr::StringLike { key, pattern } => {
condition_expr::Expr::StringLike(proto::StringLikeExpr { key, pattern })
}
TypesConditionExpr::StringNotLike { key, pattern } => {
condition_expr::Expr::StringNotLike(proto::StringNotLikeExpr { key, pattern })
}
TypesConditionExpr::NumericEquals { key, value } => {
condition_expr::Expr::NumericEquals(proto::NumericEqualsExpr { key, value })
}
TypesConditionExpr::NumericLessThan { key, value } => {
condition_expr::Expr::NumericLessThan(proto::NumericLessThanExpr { key, value })
}
TypesConditionExpr::NumericLessThanEquals { key, value } => {
condition_expr::Expr::NumericLessThan(proto::NumericLessThanExpr { key, value })
}
TypesConditionExpr::NumericGreaterThan { key, value } => {
condition_expr::Expr::NumericGreaterThan(proto::NumericGreaterThanExpr {
key,
value,
})
}
TypesConditionExpr::NumericGreaterThanEquals { key, value } => {
condition_expr::Expr::NumericGreaterThan(proto::NumericGreaterThanExpr {
key,
value,
})
}
TypesConditionExpr::IpAddress { key, cidr } => {
condition_expr::Expr::IpAddress(proto::IpAddressExpr { key, cidr })
}
TypesConditionExpr::NotIpAddress { key, cidr } => {
condition_expr::Expr::NotIpAddress(proto::NotIpAddressExpr { key, cidr })
}
TypesConditionExpr::TimeBetween { start, end } => {
condition_expr::Expr::TimeBetween(proto::TimeBetweenExpr { start, end })
}
TypesConditionExpr::Exists { key } => {
condition_expr::Expr::Exists(proto::ExistsExpr { key })
}
TypesConditionExpr::StringEqualsAny { key, values } => {
condition_expr::Expr::StringEqualsAny(proto::StringEqualsAnyExpr { key, values })
}
TypesConditionExpr::Bool { key, value } => {
condition_expr::Expr::BoolExpr(proto::BoolExpr { key, value })
}
TypesConditionExpr::And(exprs) => condition_expr::Expr::AndExpr(proto::AndExpr {
expressions: exprs.into_iter().map(|e| e.into()).collect(),
}),
TypesConditionExpr::Or(exprs) => condition_expr::Expr::OrExpr(proto::OrExpr {
expressions: exprs.into_iter().map(|e| e.into()).collect(),
}),
TypesConditionExpr::Not(expr) => {
condition_expr::Expr::NotExpr(Box::new(proto::NotExpr {
expression: Some(Box::new((*expr).into())),
}))
}
};
ConditionExpr { expr: Some(expr) }
}
}
impl From<ConditionExpr> for TypesConditionExpr {
fn from(e: ConditionExpr) -> Self {
match e.expr {
Some(condition_expr::Expr::StringEquals(e)) => TypesConditionExpr::StringEquals {
key: e.key,
value: e.value,
},
Some(condition_expr::Expr::StringNotEquals(e)) => TypesConditionExpr::StringNotEquals {
key: e.key,
value: e.value,
},
Some(condition_expr::Expr::StringLike(e)) => TypesConditionExpr::StringLike {
key: e.key,
pattern: e.pattern,
},
Some(condition_expr::Expr::StringNotLike(e)) => TypesConditionExpr::StringNotLike {
key: e.key,
pattern: e.pattern,
},
Some(condition_expr::Expr::NumericEquals(e)) => TypesConditionExpr::NumericEquals {
key: e.key,
value: e.value,
},
Some(condition_expr::Expr::NumericLessThan(e)) => TypesConditionExpr::NumericLessThan {
key: e.key,
value: e.value,
},
Some(condition_expr::Expr::NumericGreaterThan(e)) => {
TypesConditionExpr::NumericGreaterThan {
key: e.key,
value: e.value,
}
}
Some(condition_expr::Expr::IpAddress(e)) => TypesConditionExpr::IpAddress {
key: e.key,
cidr: e.cidr,
},
Some(condition_expr::Expr::NotIpAddress(e)) => TypesConditionExpr::NotIpAddress {
key: e.key,
cidr: e.cidr,
},
Some(condition_expr::Expr::TimeBetween(e)) => TypesConditionExpr::TimeBetween {
start: e.start,
end: e.end,
},
Some(condition_expr::Expr::Exists(e)) => TypesConditionExpr::Exists { key: e.key },
Some(condition_expr::Expr::StringEqualsAny(e)) => TypesConditionExpr::StringEqualsAny {
key: e.key,
values: e.values,
},
Some(condition_expr::Expr::BoolExpr(e)) => TypesConditionExpr::Bool {
key: e.key,
value: e.value,
},
Some(condition_expr::Expr::AndExpr(e)) => {
TypesConditionExpr::And(e.expressions.into_iter().map(|x| x.into()).collect())
}
Some(condition_expr::Expr::OrExpr(e)) => {
TypesConditionExpr::Or(e.expressions.into_iter().map(|x| x.into()).collect())
}
Some(condition_expr::Expr::NotExpr(e)) => {
let inner = e
.expression
.map(|x| (*x).into())
.unwrap_or(TypesConditionExpr::And(vec![]));
TypesConditionExpr::Not(Box::new(inner))
}
None => TypesConditionExpr::And(vec![]),
}
}
}