Skip to main content

kanidmd_lib/server/
identity.rs

1//! Contains structures related to the Identity that initiated an `Event` in the
2//! server. Generally this Identity is what will have access controls applied to
3//! and this provides the set of `Limits` to confine how many resources that the
4//! identity may consume during operations to prevent denial-of-service.
5
6use crate::be::Limits;
7use crate::prelude::*;
8use crate::value::Session;
9use kanidm_proto::internal::{ApiTokenPurpose, UatPurpose};
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeSet;
12use std::hash::Hash;
13use std::net::IpAddr;
14use std::sync::Arc;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum Source {
18    Internal,
19    Https(IpAddr),
20    Ldaps(IpAddr),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum AccessScope {
25    ReadOnly,
26    ReadWrite,
27    Synchronise,
28}
29
30impl std::fmt::Display for AccessScope {
31    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
32        match self {
33            AccessScope::ReadOnly => write!(f, "read only"),
34            AccessScope::ReadWrite => write!(f, "read write"),
35            AccessScope::Synchronise => write!(f, "synchronise"),
36        }
37    }
38}
39
40impl From<&ApiTokenPurpose> for AccessScope {
41    fn from(purpose: &ApiTokenPurpose) -> Self {
42        match purpose {
43            ApiTokenPurpose::ReadOnly => AccessScope::ReadOnly,
44            ApiTokenPurpose::ReadWrite => AccessScope::ReadWrite,
45            ApiTokenPurpose::Synchronise => AccessScope::Synchronise,
46        }
47    }
48}
49
50impl From<&UatPurpose> for AccessScope {
51    fn from(purpose: &UatPurpose) -> Self {
52        match purpose {
53            UatPurpose::ReadOnly => AccessScope::ReadOnly,
54            UatPurpose::ReadWrite { .. } => AccessScope::ReadWrite,
55        }
56    }
57}
58
59#[derive(Debug, Clone)]
60/// Metadata and the entry of the current Identity which is an external account/user.
61pub struct IdentUser {
62    pub entry: Arc<EntrySealedCommitted>,
63    // IpAddr?
64    // Other metadata?
65}
66
67#[derive(Debug, Clone)]
68/// The internal role being used for this operation.
69pub enum InternalRole {
70    /// The internal database system. This has unlimited crab power.
71    System,
72    /// A migration operation being performed on the system.
73    Migration,
74
75    /// An anonymous account action - this could be a credential reset
76    /// request, or a request to create a new account.
77    AccountRequest,
78
79    /// An internal role than can manage the outbound message queue.
80    MessageQueue,
81}
82
83impl std::fmt::Display for InternalRole {
84    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
85        match self {
86            Self::System => write!(f, "System"),
87            Self::Migration => write!(f, "Migration"),
88            Self::AccountRequest => write!(f, "AccountRequest"),
89            Self::MessageQueue => write!(f, "MessageQueue"),
90        }
91    }
92}
93
94impl InternalRole {
95    pub fn get_uuid(&self) -> Uuid {
96        match self {
97            Self::System => UUID_SYSTEM,
98            Self::Migration => UUID_INTERNAL_MIGRATION,
99            Self::AccountRequest => UUID_INTERNAL_ACCOUNT_REQUEST,
100            Self::MessageQueue => UUID_INTERNAL_MESSAGE_QUEUE,
101        }
102    }
103}
104
105#[derive(Debug, Clone)]
106/// The type of Identity that is related to this session.
107pub enum IdentType {
108    User(IdentUser),
109    Synch(Uuid),
110    Internal(InternalRole),
111}
112
113#[derive(Debug, Clone, PartialEq, Hash, Ord, PartialOrd, Eq, Serialize, Deserialize)]
114/// A unique identifier of this Identity, that can be associated to various
115/// caching components.
116pub enum IdentityId {
117    // Time stamp of the originating event.
118    // The uuid of the originating user
119    User(Uuid),
120    Synch(Uuid),
121    Internal(Uuid),
122}
123
124impl From<&IdentityId> for Uuid {
125    fn from(ident: &IdentityId) -> Uuid {
126        match ident {
127            IdentityId::User(uuid) | IdentityId::Synch(uuid) | IdentityId::Internal(uuid) => *uuid,
128        }
129    }
130}
131
132impl From<&IdentType> for IdentityId {
133    fn from(idt: &IdentType) -> Self {
134        match idt {
135            IdentType::Internal(role) => IdentityId::Internal(role.get_uuid()),
136            IdentType::User(u) => IdentityId::User(u.entry.get_uuid()),
137            IdentType::Synch(u) => IdentityId::Synch(*u),
138        }
139    }
140}
141
142#[derive(Debug, Clone)]
143/// An identity that initiated an `Event`. Contains extra details about the session
144/// and other info that can assist with server decision making.
145pub struct Identity {
146    pub origin: IdentType,
147    #[allow(dead_code)]
148    source: Source,
149    pub(crate) session_id: Uuid,
150    pub(crate) scope: AccessScope,
151    limits: Limits,
152}
153
154impl std::fmt::Display for Identity {
155    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
156        match &self.origin {
157            IdentType::Internal(u) => write!(f, "Internal ({}) ({})", u, self.scope),
158            IdentType::Synch(u) => write!(f, "Synchronise ({}) ({})", u, self.scope),
159            IdentType::User(u) => {
160                let nv = u.entry.get_uuid2spn();
161                write!(
162                    f,
163                    "User( {}, {} ) ({}, {})",
164                    nv.to_proto_string_clone(),
165                    u.entry.get_uuid().as_hyphenated(),
166                    self.session_id,
167                    self.scope
168                )
169            }
170        }
171    }
172}
173
174impl Identity {
175    pub(crate) fn new(
176        origin: IdentType,
177        source: Source,
178        session_id: Uuid,
179        scope: AccessScope,
180        limits: Limits,
181    ) -> Self {
182        Self {
183            origin,
184            source,
185            session_id,
186            scope,
187            limits,
188        }
189    }
190
191    #[allow(dead_code)]
192    pub(crate) fn source(&self) -> &Source {
193        &self.source
194    }
195
196    pub(crate) fn limits(&self) -> &Limits {
197        &self.limits
198    }
199
200    #[cfg(test)]
201    pub(crate) fn limits_mut(&mut self) -> &mut Limits {
202        &mut self.limits
203    }
204
205    pub(crate) fn migration() -> Self {
206        Identity {
207            origin: IdentType::Internal(InternalRole::Migration),
208            source: Source::Internal,
209            session_id: UUID_INTERNAL_SESSION_ID,
210            scope: AccessScope::ReadWrite,
211            limits: Limits::unlimited(),
212        }
213    }
214
215    pub(crate) fn account_request() -> Self {
216        Identity {
217            origin: IdentType::Internal(InternalRole::AccountRequest),
218            source: Source::Internal,
219            session_id: UUID_INTERNAL_SESSION_ID,
220            scope: AccessScope::ReadOnly,
221            limits: Limits::unlimited(),
222        }
223    }
224
225    pub(crate) fn message_queue() -> Self {
226        Identity {
227            origin: IdentType::Internal(InternalRole::MessageQueue),
228            source: Source::Internal,
229            session_id: UUID_INTERNAL_SESSION_ID,
230            scope: AccessScope::ReadWrite,
231            limits: Limits::unlimited(),
232        }
233    }
234
235    pub(crate) fn from_internal() -> Self {
236        Identity {
237            origin: IdentType::Internal(InternalRole::System),
238            source: Source::Internal,
239            session_id: UUID_INTERNAL_SESSION_ID,
240            scope: AccessScope::ReadWrite,
241            limits: Limits::unlimited(),
242        }
243    }
244
245    #[cfg(test)]
246    pub(crate) fn from_impersonate_entry_readonly(
247        entry: Arc<Entry<EntrySealed, EntryCommitted>>,
248    ) -> Self {
249        Identity {
250            origin: IdentType::User(IdentUser { entry }),
251            source: Source::Internal,
252            session_id: UUID_INTERNAL_SESSION_ID,
253            scope: AccessScope::ReadOnly,
254            limits: Limits::unlimited(),
255        }
256    }
257
258    pub fn from_impersonate_entry_readwrite(
259        entry: Arc<Entry<EntrySealed, EntryCommitted>>,
260    ) -> Self {
261        Identity {
262            origin: IdentType::User(IdentUser { entry }),
263            source: Source::Internal,
264            session_id: UUID_INTERNAL_SESSION_ID,
265            scope: AccessScope::ReadWrite,
266            limits: Limits::unlimited(),
267        }
268    }
269
270    pub fn access_scope(&self) -> AccessScope {
271        self.scope
272    }
273
274    pub fn project_with_scope(&self, scope: AccessScope) -> Self {
275        let mut new = self.clone();
276        new.scope = scope;
277        new
278    }
279
280    pub fn get_session_id(&self) -> Uuid {
281        self.session_id
282    }
283
284    pub fn get_session(&self) -> Option<&Session> {
285        match &self.origin {
286            IdentType::Internal(_) | IdentType::Synch(_) => None,
287            IdentType::User(u) => u
288                .entry
289                .get_ava_as_session_map(Attribute::UserAuthTokenSession)
290                .and_then(|sessions| sessions.get(&self.session_id)),
291        }
292    }
293
294    pub fn get_user_entry(&self) -> Option<Arc<EntrySealedCommitted>> {
295        match &self.origin {
296            IdentType::Internal(_) | IdentType::Synch(_) => None,
297            IdentType::User(u) => Some(u.entry.clone()),
298        }
299    }
300
301    pub fn from_impersonate(ident: &Self) -> Self {
302        // TODO #64 ?: In the future, we could change some of this data
303        // to reflect the fact we are in fact impersonating the action
304        // rather than the user explicitly requesting it. Could matter
305        // to audits and logs to determine what happened.
306        ident.clone()
307    }
308
309    pub fn is_internal(&self) -> bool {
310        matches!(self.origin, IdentType::Internal(_))
311    }
312
313    pub fn get_uuid(&self) -> Uuid {
314        match &self.origin {
315            IdentType::Internal(role) => role.get_uuid(),
316            IdentType::User(u) => u.entry.get_uuid(),
317            IdentType::Synch(u) => *u,
318        }
319    }
320
321    /// Indicate if the session associated with this identity has a session
322    /// that can logout. Examples of sessions that *can not* logout are anonymous,
323    /// tokens, or PIV sessions.
324    pub fn can_logout(&self) -> bool {
325        match &self.origin {
326            IdentType::Internal(_) => false,
327            IdentType::User(u) => u.entry.get_uuid() != UUID_ANONYMOUS,
328            IdentType::Synch(_) => false,
329        }
330    }
331
332    pub fn get_event_origin_id(&self) -> IdentityId {
333        IdentityId::from(&self.origin)
334    }
335
336    #[cfg(test)]
337    pub fn has_claim(&self, claim: &str) -> bool {
338        match &self.origin {
339            IdentType::Internal(_) | IdentType::Synch(_) => false,
340            IdentType::User(u) => u
341                .entry
342                .attribute_equality(Attribute::Claim, &PartialValue::new_iutf8(claim)),
343        }
344    }
345
346    pub fn is_memberof(&self, group: Uuid) -> bool {
347        match &self.origin {
348            IdentType::Internal(_) | IdentType::Synch(_) => false,
349            IdentType::User(u) => u
350                .entry
351                .attribute_equality(Attribute::MemberOf, &PartialValue::Refer(group)),
352        }
353    }
354
355    pub fn get_memberof(&self) -> Option<&BTreeSet<Uuid>> {
356        match &self.origin {
357            IdentType::Internal(_) | IdentType::Synch(_) => None,
358            IdentType::User(u) => u.entry.get_ava_refer(Attribute::MemberOf),
359        }
360    }
361
362    pub fn get_oauth2_consent_scopes(&self, oauth2_rs: Uuid) -> Option<&BTreeSet<String>> {
363        match &self.origin {
364            IdentType::Internal(_) | IdentType::Synch(_) => None,
365            IdentType::User(u) => u
366                .entry
367                .get_ava_as_oauthscopemaps(Attribute::OAuth2ConsentScopeMap)
368                .and_then(|scope_map| scope_map.get(&oauth2_rs)),
369        }
370    }
371}