use crate::be::Limits;
use std::collections::BTreeSet;
use std::hash::Hash;
use std::net::IpAddr;
use std::sync::Arc;
use uuid::uuid;
use kanidm_proto::internal::{ApiTokenPurpose, UatPurpose};
use serde::{Deserialize, Serialize};
use crate::prelude::*;
use crate::value::Session;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
Internal,
Https(IpAddr),
Ldaps(IpAddr),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessScope {
ReadOnly,
ReadWrite,
Synchronise,
}
impl std::fmt::Display for AccessScope {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
AccessScope::ReadOnly => write!(f, "read only"),
AccessScope::ReadWrite => write!(f, "read write"),
AccessScope::Synchronise => write!(f, "synchronise"),
}
}
}
impl From<&ApiTokenPurpose> for AccessScope {
fn from(purpose: &ApiTokenPurpose) -> Self {
match purpose {
ApiTokenPurpose::ReadOnly => AccessScope::ReadOnly,
ApiTokenPurpose::ReadWrite => AccessScope::ReadWrite,
ApiTokenPurpose::Synchronise => AccessScope::Synchronise,
}
}
}
impl From<&UatPurpose> for AccessScope {
fn from(purpose: &UatPurpose) -> Self {
match purpose {
UatPurpose::ReadOnly => AccessScope::ReadOnly,
UatPurpose::ReadWrite { .. } => AccessScope::ReadWrite,
}
}
}
#[derive(Debug, Clone)]
pub struct IdentUser {
pub entry: Arc<EntrySealedCommitted>,
}
#[derive(Debug, Clone)]
pub enum IdentType {
User(IdentUser),
Synch(Uuid),
Internal,
}
#[derive(Debug, Clone, PartialEq, Hash, Ord, PartialOrd, Eq, Serialize, Deserialize)]
pub enum IdentityId {
User(Uuid),
Synch(Uuid),
Internal,
}
impl From<&IdentityId> for Uuid {
fn from(ident: &IdentityId) -> Uuid {
match ident {
IdentityId::User(uuid) | IdentityId::Synch(uuid) => *uuid,
IdentityId::Internal => UUID_SYSTEM,
}
}
}
impl From<&IdentType> for IdentityId {
fn from(idt: &IdentType) -> Self {
match idt {
IdentType::Internal => IdentityId::Internal,
IdentType::User(u) => IdentityId::User(u.entry.get_uuid()),
IdentType::Synch(u) => IdentityId::Synch(*u),
}
}
}
#[derive(Debug, Clone)]
pub struct Identity {
pub origin: IdentType,
#[allow(dead_code)]
source: Source,
pub(crate) session_id: Uuid,
pub(crate) scope: AccessScope,
limits: Limits,
}
impl std::fmt::Display for Identity {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.origin {
IdentType::Internal => write!(f, "Internal ({})", self.scope),
IdentType::Synch(u) => write!(f, "Synchronise ({}) ({})", u, self.scope),
IdentType::User(u) => {
let nv = u.entry.get_uuid2spn();
write!(
f,
"User( {}, {} ) ({}, {})",
nv.to_proto_string_clone(),
u.entry.get_uuid().as_hyphenated(),
self.session_id,
self.scope
)
}
}
}
}
impl Identity {
pub(crate) fn new(
origin: IdentType,
source: Source,
session_id: Uuid,
scope: AccessScope,
limits: Limits,
) -> Self {
Self {
origin,
source,
session_id,
scope,
limits,
}
}
#[allow(dead_code)]
pub(crate) fn source(&self) -> &Source {
&self.source
}
pub(crate) fn limits(&self) -> &Limits {
&self.limits
}
#[cfg(test)]
pub(crate) fn limits_mut(&mut self) -> &mut Limits {
&mut self.limits
}
pub(crate) fn from_internal() -> Self {
Identity {
origin: IdentType::Internal,
source: Source::Internal,
session_id: uuid!("00000000-0000-0000-0000-000000000000"),
scope: AccessScope::ReadWrite,
limits: Limits::unlimited(),
}
}
#[cfg(test)]
pub(crate) fn from_impersonate_entry_readonly(
entry: Arc<Entry<EntrySealed, EntryCommitted>>,
) -> Self {
Identity {
origin: IdentType::User(IdentUser { entry }),
source: Source::Internal,
session_id: uuid!("00000000-0000-0000-0000-000000000000"),
scope: AccessScope::ReadOnly,
limits: Limits::unlimited(),
}
}
#[cfg(test)]
pub fn from_impersonate_entry_readwrite(
entry: Arc<Entry<EntrySealed, EntryCommitted>>,
) -> Self {
Identity {
origin: IdentType::User(IdentUser { entry }),
source: Source::Internal,
session_id: uuid!("00000000-0000-0000-0000-000000000000"),
scope: AccessScope::ReadWrite,
limits: Limits::unlimited(),
}
}
pub fn access_scope(&self) -> AccessScope {
self.scope
}
pub fn project_with_scope(&self, scope: AccessScope) -> Self {
let mut new = self.clone();
new.scope = scope;
new
}
pub fn get_session_id(&self) -> Uuid {
self.session_id
}
pub fn get_session(&self) -> Option<&Session> {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => None,
IdentType::User(u) => u
.entry
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
.and_then(|sessions| sessions.get(&self.session_id)),
}
}
pub fn get_user_entry(&self) -> Option<Arc<EntrySealedCommitted>> {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => None,
IdentType::User(u) => Some(u.entry.clone()),
}
}
pub fn from_impersonate(ident: &Self) -> Self {
ident.clone()
}
pub fn is_internal(&self) -> bool {
matches!(self.origin, IdentType::Internal)
}
pub fn get_uuid(&self) -> Option<Uuid> {
match &self.origin {
IdentType::Internal => None,
IdentType::User(u) => Some(u.entry.get_uuid()),
IdentType::Synch(u) => Some(*u),
}
}
pub fn can_logout(&self) -> bool {
match &self.origin {
IdentType::Internal => false,
IdentType::User(u) => u.entry.get_uuid() != UUID_ANONYMOUS,
IdentType::Synch(_) => false,
}
}
pub fn get_event_origin_id(&self) -> IdentityId {
IdentityId::from(&self.origin)
}
#[cfg(test)]
pub fn has_claim(&self, claim: &str) -> bool {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => false,
IdentType::User(u) => u
.entry
.attribute_equality(Attribute::Claim, &PartialValue::new_iutf8(claim)),
}
}
pub fn is_memberof(&self, group: Uuid) -> bool {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => false,
IdentType::User(u) => u
.entry
.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(group)),
}
}
pub fn get_memberof(&self) -> Option<&BTreeSet<Uuid>> {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => None,
IdentType::User(u) => u.entry.get_ava_refer(Attribute::MemberOf),
}
}
pub fn get_oauth2_consent_scopes(&self, oauth2_rs: Uuid) -> Option<&BTreeSet<String>> {
match &self.origin {
IdentType::Internal | IdentType::Synch(_) => None,
IdentType::User(u) => u
.entry
.get_ava_as_oauthscopemaps(Attribute::OAuth2ConsentScopeMap)
.and_then(|scope_map| scope_map.get(&oauth2_rs)),
}
}
}