mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-03 15:45:06 +02:00
Removes the protected plugin into an access control module so that it's outputs can be properly represented in effective access checks.
495 lines
13 KiB
Rust
495 lines
13 KiB
Rust
use super::ScimMail;
|
|
use super::ScimOauth2ClaimMapJoinChar;
|
|
use super::ScimSshPublicKey;
|
|
use crate::attribute::Attribute;
|
|
use crate::internal::UiHint;
|
|
use scim_proto::ScimEntryHeader;
|
|
use serde::Serialize;
|
|
use serde_with::{base64, formats, hex::Hex, serde_as, skip_serializing_none};
|
|
use std::collections::{BTreeMap, BTreeSet};
|
|
use time::format_description::well_known::Rfc3339;
|
|
use time::OffsetDateTime;
|
|
use url::Url;
|
|
use utoipa::ToSchema;
|
|
use uuid::Uuid;
|
|
|
|
/// A strongly typed ScimEntry that is for transmission to clients. This uses
|
|
/// Kanidm internal strong types for values allowing direct serialisation and
|
|
/// transmission.
|
|
#[serde_as]
|
|
#[skip_serializing_none]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
pub struct ScimEntryKanidm {
|
|
#[serde(flatten)]
|
|
pub header: ScimEntryHeader,
|
|
|
|
pub ext_access_check: Option<ScimEffectiveAccess>,
|
|
#[serde(flatten)]
|
|
pub attrs: BTreeMap<Attribute, ScimValueKanidm>,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
pub enum ScimAttributeEffectiveAccess {
|
|
/// All attributes on the entry have this permission granted
|
|
Grant,
|
|
/// All attributes on the entry have this permission denied
|
|
Deny,
|
|
/// The following attributes on the entry have this permission granted
|
|
Allow(BTreeSet<Attribute>),
|
|
}
|
|
|
|
impl ScimAttributeEffectiveAccess {
|
|
/// Check if the effective access allows or denies this attribute
|
|
pub fn check(&self, attr: &Attribute) -> bool {
|
|
match self {
|
|
Self::Grant => true,
|
|
Self::Deny => false,
|
|
Self::Allow(set) => set.contains(attr),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimEffectiveAccess {
|
|
/// The identity that inherits the effective permission
|
|
pub ident: Uuid,
|
|
/// If the ident may delete the target entry
|
|
pub delete: bool,
|
|
/// The set of effective access over search events
|
|
pub search: ScimAttributeEffectiveAccess,
|
|
/// The set of effective access over modify present events
|
|
pub modify_present: ScimAttributeEffectiveAccess,
|
|
/// The set of effective access over modify remove events
|
|
pub modify_remove: ScimAttributeEffectiveAccess,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimAddress {
|
|
pub formatted: String,
|
|
pub street_address: String,
|
|
pub locality: String,
|
|
pub region: String,
|
|
pub postal_code: String,
|
|
pub country: String,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimApplicationPassword {
|
|
pub uuid: Uuid,
|
|
pub application_uuid: Uuid,
|
|
pub label: String,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimBinary {
|
|
pub label: String,
|
|
#[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
|
|
pub value: Vec<u8>,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimCertificate {
|
|
#[serde_as(as = "Hex")]
|
|
pub s256: Vec<u8>,
|
|
#[serde_as(as = "base64::Base64<base64::UrlSafe, formats::Unpadded>")]
|
|
pub der: Vec<u8>,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimAuditString {
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub date_time: OffsetDateTime,
|
|
pub value: String,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum ScimIntentTokenState {
|
|
Valid,
|
|
InProgress,
|
|
Consumed,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimIntentToken {
|
|
pub token_id: String,
|
|
pub state: ScimIntentTokenState,
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub expires: OffsetDateTime,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimKeyInternal {
|
|
pub key_id: String,
|
|
pub status: String,
|
|
pub usage: String,
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub valid_from: OffsetDateTime,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[skip_serializing_none]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimAuthSession {
|
|
pub id: Uuid,
|
|
#[serde_as(as = "Option<Rfc3339>")]
|
|
pub expires: Option<OffsetDateTime>,
|
|
#[serde_as(as = "Option<Rfc3339>")]
|
|
pub revoked: Option<OffsetDateTime>,
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub issued_at: OffsetDateTime,
|
|
pub issued_by: Uuid,
|
|
pub credential_id: Uuid,
|
|
pub auth_type: String,
|
|
pub session_scope: String,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[skip_serializing_none]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimOAuth2Session {
|
|
pub id: Uuid,
|
|
pub parent_id: Option<Uuid>,
|
|
pub client_id: Uuid,
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub issued_at: OffsetDateTime,
|
|
#[serde_as(as = "Option<Rfc3339>")]
|
|
pub expires: Option<OffsetDateTime>,
|
|
#[serde_as(as = "Option<Rfc3339>")]
|
|
pub revoked: Option<OffsetDateTime>,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[skip_serializing_none]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimApiToken {
|
|
pub id: Uuid,
|
|
pub label: String,
|
|
#[serde_as(as = "Option<Rfc3339>")]
|
|
pub expires: Option<OffsetDateTime>,
|
|
#[serde_as(as = "Rfc3339")]
|
|
pub issued_at: OffsetDateTime,
|
|
pub issued_by: Uuid,
|
|
pub scope: String,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimOAuth2ScopeMap {
|
|
pub group: String,
|
|
pub group_uuid: Uuid,
|
|
pub scopes: BTreeSet<String>,
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimOAuth2ClaimMap {
|
|
pub group: String,
|
|
pub group_uuid: Uuid,
|
|
pub claim: String,
|
|
pub join_char: ScimOauth2ClaimMapJoinChar,
|
|
pub values: BTreeSet<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScimReference {
|
|
pub uuid: Uuid,
|
|
pub value: String,
|
|
}
|
|
|
|
/// This is a strongly typed ScimValue for Kanidm. It is for serialisation only
|
|
/// since on a deserialisation path we can not know the intent of the sender
|
|
/// to how we deserialise strings. Additionally during deserialisation we need
|
|
/// to accept optional or partial types too.
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
#[serde(untagged)]
|
|
pub enum ScimValueKanidm {
|
|
Bool(bool),
|
|
Uint32(u32),
|
|
Integer(i64),
|
|
Decimal(f64),
|
|
String(String),
|
|
DateTime(#[serde_as(as = "Rfc3339")] OffsetDateTime),
|
|
Reference(Url),
|
|
Uuid(Uuid),
|
|
EntryReference(ScimReference),
|
|
EntryReferences(Vec<ScimReference>),
|
|
|
|
// Other strong outbound types.
|
|
ArrayString(Vec<String>),
|
|
ArrayDateTime(#[serde_as(as = "Vec<Rfc3339>")] Vec<OffsetDateTime>),
|
|
ArrayUuid(Vec<Uuid>),
|
|
ArrayBinary(Vec<ScimBinary>),
|
|
ArrayCertificate(Vec<ScimCertificate>),
|
|
|
|
Address(Vec<ScimAddress>),
|
|
Mail(Vec<ScimMail>),
|
|
ApplicationPassword(Vec<ScimApplicationPassword>),
|
|
AuditString(Vec<ScimAuditString>),
|
|
SshPublicKey(Vec<ScimSshPublicKey>),
|
|
AuthSession(Vec<ScimAuthSession>),
|
|
OAuth2Session(Vec<ScimOAuth2Session>),
|
|
ApiToken(Vec<ScimApiToken>),
|
|
IntentToken(Vec<ScimIntentToken>),
|
|
OAuth2ScopeMap(Vec<ScimOAuth2ScopeMap>),
|
|
OAuth2ClaimMap(Vec<ScimOAuth2ClaimMap>),
|
|
KeyInternal(Vec<ScimKeyInternal>),
|
|
UiHints(Vec<UiHint>),
|
|
}
|
|
|
|
#[serde_as]
|
|
#[derive(Serialize, Debug, Clone, ToSchema)]
|
|
pub struct ScimPerson {
|
|
pub uuid: Uuid,
|
|
pub name: String,
|
|
pub displayname: String,
|
|
pub spn: String,
|
|
pub description: Option<String>,
|
|
pub mails: Vec<ScimMail>,
|
|
pub managed_by: Option<ScimReference>,
|
|
pub groups: Vec<ScimReference>,
|
|
}
|
|
|
|
impl TryFrom<ScimEntryKanidm> for ScimPerson {
|
|
type Error = ();
|
|
|
|
fn try_from(scim_entry: ScimEntryKanidm) -> Result<Self, Self::Error> {
|
|
let uuid = scim_entry.header.id;
|
|
let name = scim_entry
|
|
.attrs
|
|
.get(&Attribute::Name)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::String(s) => Some(s.clone()),
|
|
_ => None,
|
|
})
|
|
.ok_or(())?;
|
|
|
|
let displayname = scim_entry
|
|
.attrs
|
|
.get(&Attribute::DisplayName)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::String(s) => Some(s.clone()),
|
|
_ => None,
|
|
})
|
|
.ok_or(())?;
|
|
|
|
let spn = scim_entry
|
|
.attrs
|
|
.get(&Attribute::Spn)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::String(s) => Some(s.clone()),
|
|
_ => None,
|
|
})
|
|
.ok_or(())?;
|
|
|
|
let description = scim_entry
|
|
.attrs
|
|
.get(&Attribute::Description)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::String(s) => Some(s.clone()),
|
|
_ => None,
|
|
});
|
|
|
|
let mails = scim_entry
|
|
.attrs
|
|
.get(&Attribute::Mail)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::Mail(m) => Some(m.clone()),
|
|
_ => None,
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
let groups = scim_entry
|
|
.attrs
|
|
.get(&Attribute::DirectMemberOf)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::EntryReferences(v) => Some(v.clone()),
|
|
_ => None,
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
let managed_by = scim_entry
|
|
.attrs
|
|
.get(&Attribute::EntryManagedBy)
|
|
.and_then(|v| match v {
|
|
ScimValueKanidm::EntryReference(v) => Some(v.clone()),
|
|
_ => None,
|
|
});
|
|
|
|
Ok(ScimPerson {
|
|
uuid,
|
|
name,
|
|
displayname,
|
|
spn,
|
|
description,
|
|
mails,
|
|
managed_by,
|
|
groups,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<bool> for ScimValueKanidm {
|
|
fn from(b: bool) -> Self {
|
|
Self::Bool(b)
|
|
}
|
|
}
|
|
|
|
impl From<OffsetDateTime> for ScimValueKanidm {
|
|
fn from(odt: OffsetDateTime) -> Self {
|
|
Self::DateTime(odt)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<UiHint>> for ScimValueKanidm {
|
|
fn from(set: Vec<UiHint>) -> Self {
|
|
Self::UiHints(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<OffsetDateTime>> for ScimValueKanidm {
|
|
fn from(set: Vec<OffsetDateTime>) -> Self {
|
|
Self::ArrayDateTime(set)
|
|
}
|
|
}
|
|
|
|
impl From<String> for ScimValueKanidm {
|
|
fn from(s: String) -> Self {
|
|
Self::String(s)
|
|
}
|
|
}
|
|
|
|
impl From<&str> for ScimValueKanidm {
|
|
fn from(s: &str) -> Self {
|
|
Self::String(s.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<Vec<String>> for ScimValueKanidm {
|
|
fn from(set: Vec<String>) -> Self {
|
|
Self::ArrayString(set)
|
|
}
|
|
}
|
|
|
|
impl From<Uuid> for ScimValueKanidm {
|
|
fn from(u: Uuid) -> Self {
|
|
Self::Uuid(u)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<Uuid>> for ScimValueKanidm {
|
|
fn from(set: Vec<Uuid>) -> Self {
|
|
Self::ArrayUuid(set)
|
|
}
|
|
}
|
|
|
|
impl From<u32> for ScimValueKanidm {
|
|
fn from(u: u32) -> Self {
|
|
Self::Uint32(u)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimAddress>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimAddress>) -> Self {
|
|
Self::Address(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimMail>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimMail>) -> Self {
|
|
Self::Mail(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimApplicationPassword>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimApplicationPassword>) -> Self {
|
|
Self::ApplicationPassword(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimAuditString>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimAuditString>) -> Self {
|
|
Self::AuditString(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimBinary>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimBinary>) -> Self {
|
|
Self::ArrayBinary(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimCertificate>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimCertificate>) -> Self {
|
|
Self::ArrayCertificate(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimSshPublicKey>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimSshPublicKey>) -> Self {
|
|
Self::SshPublicKey(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimAuthSession>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimAuthSession>) -> Self {
|
|
Self::AuthSession(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimOAuth2Session>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimOAuth2Session>) -> Self {
|
|
Self::OAuth2Session(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimApiToken>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimApiToken>) -> Self {
|
|
Self::ApiToken(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimIntentToken>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimIntentToken>) -> Self {
|
|
Self::IntentToken(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimOAuth2ScopeMap>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimOAuth2ScopeMap>) -> Self {
|
|
Self::OAuth2ScopeMap(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimOAuth2ClaimMap>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimOAuth2ClaimMap>) -> Self {
|
|
Self::OAuth2ClaimMap(set)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<ScimKeyInternal>> for ScimValueKanidm {
|
|
fn from(set: Vec<ScimKeyInternal>) -> Self {
|
|
Self::KeyInternal(set)
|
|
}
|
|
}
|