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