use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum AwsPermissionEffect {
#[default]
Allow,
Deny,
}
impl AwsPermissionEffect {
pub fn as_str(&self) -> &'static str {
match self {
Self::Allow => "Allow",
Self::Deny => "Deny",
}
}
pub fn is_allow(&self) -> bool {
matches!(self, Self::Allow)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionGrant {
#[serde(skip_serializing_if = "Option::is_none")]
pub actions: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub predefined_roles: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub residual_permissions: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data_actions: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AwsBindingSpec {
pub resources: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<IndexMap<String, IndexMap<String, String>>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GcpBindingSpec {
pub scope: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<GcpCondition>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AzureBindingSpec {
pub scope: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BindingConfiguration<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub stack: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resource: Option<T>,
}
impl<T> BindingConfiguration<T> {
pub fn is_empty(&self) -> bool {
self.stack.is_none() && self.resource.is_none()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GcpCondition {
pub title: String,
pub expression: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AwsPlatformPermission {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "AwsPermissionEffect::is_allow")]
pub effect: AwsPermissionEffect,
pub grant: PermissionGrant,
pub binding: BindingConfiguration<AwsBindingSpec>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GcpPlatformPermission {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub grant: PermissionGrant,
pub binding: BindingConfiguration<GcpBindingSpec>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AzurePlatformPermission {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub grant: PermissionGrant,
pub binding: BindingConfiguration<AzureBindingSpec>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PlatformPermissions {
#[serde(skip_serializing_if = "Option::is_none")]
pub aws: Option<Vec<AwsPlatformPermission>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gcp: Option<Vec<GcpPlatformPermission>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub azure: Option<Vec<AzurePlatformPermission>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionSet {
pub id: String,
pub description: String,
pub platforms: PlatformPermissions,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(untagged)]
pub enum PermissionSetReference {
Name(String),
Inline(PermissionSet),
}
impl PermissionSetReference {
pub fn id(&self) -> &str {
match self {
PermissionSetReference::Name(name) => name,
PermissionSetReference::Inline(permission_set) => &permission_set.id,
}
}
pub fn from_name(name: impl Into<String>) -> Self {
PermissionSetReference::Name(name.into())
}
pub fn from_inline(permission_set: PermissionSet) -> Self {
PermissionSetReference::Inline(permission_set)
}
pub fn resolve(
&self,
resolver: impl Fn(&str) -> Option<PermissionSet>,
) -> Option<PermissionSet> {
match self {
PermissionSetReference::Name(name) => resolver(name),
PermissionSetReference::Inline(permission_set) => Some(permission_set.clone()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(transparent)]
pub struct PermissionProfile(pub IndexMap<String, Vec<PermissionSetReference>>);
impl PermissionProfile {
pub fn new() -> Self {
Self(IndexMap::new())
}
pub fn global<I>(mut self, permission_sets: I) -> Self
where
I: IntoIterator,
I::Item: Into<PermissionSetReference>,
{
let permission_list: Vec<PermissionSetReference> =
permission_sets.into_iter().map(|s| s.into()).collect();
self.0.insert("*".to_string(), permission_list);
self
}
pub fn resource<I>(mut self, resource_name: impl Into<String>, permission_sets: I) -> Self
where
I: IntoIterator,
I::Item: Into<PermissionSetReference>,
{
let permission_list: Vec<PermissionSetReference> =
permission_sets.into_iter().map(|s| s.into()).collect();
self.0.insert(resource_name.into(), permission_list);
self
}
}
impl Default for PermissionProfile {
fn default() -> Self {
Self::new()
}
}
impl From<String> for PermissionSetReference {
fn from(name: String) -> Self {
PermissionSetReference::Name(name)
}
}
impl From<&str> for PermissionSetReference {
fn from(name: &str) -> Self {
PermissionSetReference::Name(name.to_string())
}
}
impl From<PermissionSet> for PermissionSetReference {
fn from(permission_set: PermissionSet) -> Self {
PermissionSetReference::Inline(permission_set)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum ManagementPermissions {
Auto,
Extend(PermissionProfile),
Override(PermissionProfile),
}
impl Default for ManagementPermissions {
fn default() -> Self {
ManagementPermissions::Auto
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionsConfig {
pub profiles: IndexMap<String, PermissionProfile>,
#[serde(default)]
pub management: ManagementPermissions,
}
impl PermissionsConfig {
pub fn new() -> Self {
Self {
profiles: IndexMap::new(),
management: ManagementPermissions::Auto,
}
}
pub fn with_profile(mut self, name: impl Into<String>, profile: PermissionProfile) -> Self {
self.profiles.insert(name.into(), profile);
self
}
pub fn with_management(mut self, management: ManagementPermissions) -> Self {
self.management = management;
self
}
}
impl Default for PermissionsConfig {
fn default() -> Self {
Self::new()
}
}
impl ManagementPermissions {
pub fn auto() -> Self {
ManagementPermissions::Auto
}
pub fn extend(profile: PermissionProfile) -> Self {
ManagementPermissions::Extend(profile)
}
pub fn override_(profile: PermissionProfile) -> Self {
ManagementPermissions::Override(profile)
}
pub fn profile(&self) -> Option<&PermissionProfile> {
match self {
ManagementPermissions::Auto => None,
ManagementPermissions::Extend(profile) => Some(profile),
ManagementPermissions::Override(profile) => Some(profile),
}
}
pub fn is_auto(&self) -> bool {
matches!(self, ManagementPermissions::Auto)
}
pub fn is_extend(&self) -> bool {
matches!(self, ManagementPermissions::Extend(_))
}
pub fn is_override(&self) -> bool {
matches!(self, ManagementPermissions::Override(_))
}
}