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
76impl std::fmt::Display for InternalRole {
77    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
78        match self {
79            Self::System => write!(f, "System"),
80            Self::Migration => write!(f, "Migration"),
81        }
82    }
83}
84
85impl InternalRole {
86    pub fn get_uuid(&self) -> Uuid {
87        match self {
88            Self::System => UUID_SYSTEM,
89            Self::Migration => UUID_INTERNAL_MIGRATION,
90        }
91    }
92}
93
94#[derive(Debug, Clone)]
95/// The type of Identity that is related to this session.
96pub enum IdentType {
97    User(IdentUser),
98    Synch(Uuid),
99    Internal(InternalRole),
100}
101
102#[derive(Debug, Clone, PartialEq, Hash, Ord, PartialOrd, Eq, Serialize, Deserialize)]
103/// A unique identifier of this Identity, that can be associated to various
104/// caching components.
105pub enum IdentityId {
106    // Time stamp of the originating event.
107    // The uuid of the originating user
108    User(Uuid),
109    Synch(Uuid),
110    Internal(Uuid),
111}
112
113impl From<&IdentityId> for Uuid {
114    fn from(ident: &IdentityId) -> Uuid {
115        match ident {
116            IdentityId::User(uuid) | IdentityId::Synch(uuid) | IdentityId::Internal(uuid) => *uuid,
117        }
118    }
119}
120
121impl From<&IdentType> for IdentityId {
122    fn from(idt: &IdentType) -> Self {
123        match idt {
124            IdentType::Internal(role) => IdentityId::Internal(role.get_uuid()),
125            IdentType::User(u) => IdentityId::User(u.entry.get_uuid()),
126            IdentType::Synch(u) => IdentityId::Synch(*u),
127        }
128    }
129}
130
131#[derive(Debug, Clone)]
132/// An identity that initiated an `Event`. Contains extra details about the session
133/// and other info that can assist with server decision making.
134pub struct Identity {
135    pub origin: IdentType,
136    #[allow(dead_code)]
137    source: Source,
138    pub(crate) session_id: Uuid,
139    pub(crate) scope: AccessScope,
140    limits: Limits,
141}
142
143impl std::fmt::Display for Identity {
144    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
145        match &self.origin {
146            IdentType::Internal(u) => write!(f, "Internal ({}) ({})", u, self.scope),
147            IdentType::Synch(u) => write!(f, "Synchronise ({}) ({})", u, self.scope),
148            IdentType::User(u) => {
149                let nv = u.entry.get_uuid2spn();
150                write!(
151                    f,
152                    "User( {}, {} ) ({}, {})",
153                    nv.to_proto_string_clone(),
154                    u.entry.get_uuid().as_hyphenated(),
155                    self.session_id,
156                    self.scope
157                )
158            }
159        }
160    }
161}
162
163impl Identity {
164    pub(crate) fn new(
165        origin: IdentType,
166        source: Source,
167        session_id: Uuid,
168        scope: AccessScope,
169        limits: Limits,
170    ) -> Self {
171        Self {
172            origin,
173            source,
174            session_id,
175            scope,
176            limits,
177        }
178    }
179
180    #[allow(dead_code)]
181    pub(crate) fn source(&self) -> &Source {
182        &self.source
183    }
184
185    pub(crate) fn limits(&self) -> &Limits {
186        &self.limits
187    }
188
189    #[cfg(test)]
190    pub(crate) fn limits_mut(&mut self) -> &mut Limits {
191        &mut self.limits
192    }
193
194    pub(crate) fn migration() -> Self {
195        Identity {
196            origin: IdentType::Internal(InternalRole::Migration),
197            source: Source::Internal,
198            session_id: UUID_INTERNAL_SESSION_ID,
199            scope: AccessScope::ReadWrite,
200            limits: Limits::unlimited(),
201        }
202    }
203
204    pub(crate) fn from_internal() -> Self {
205        Identity {
206            origin: IdentType::Internal(InternalRole::System),
207            source: Source::Internal,
208            session_id: UUID_INTERNAL_SESSION_ID,
209            scope: AccessScope::ReadWrite,
210            limits: Limits::unlimited(),
211        }
212    }
213
214    #[cfg(test)]
215    pub(crate) fn from_impersonate_entry_readonly(
216        entry: Arc<Entry<EntrySealed, EntryCommitted>>,
217    ) -> Self {
218        Identity {
219            origin: IdentType::User(IdentUser { entry }),
220            source: Source::Internal,
221            session_id: UUID_INTERNAL_SESSION_ID,
222            scope: AccessScope::ReadOnly,
223            limits: Limits::unlimited(),
224        }
225    }
226
227    #[cfg(test)]
228    pub fn from_impersonate_entry_readwrite(
229        entry: Arc<Entry<EntrySealed, EntryCommitted>>,
230    ) -> Self {
231        Identity {
232            origin: IdentType::User(IdentUser { entry }),
233            source: Source::Internal,
234            session_id: UUID_INTERNAL_SESSION_ID,
235            scope: AccessScope::ReadWrite,
236            limits: Limits::unlimited(),
237        }
238    }
239
240    pub fn access_scope(&self) -> AccessScope {
241        self.scope
242    }
243
244    pub fn project_with_scope(&self, scope: AccessScope) -> Self {
245        let mut new = self.clone();
246        new.scope = scope;
247        new
248    }
249
250    pub fn get_session_id(&self) -> Uuid {
251        self.session_id
252    }
253
254    pub fn get_session(&self) -> Option<&Session> {
255        match &self.origin {
256            IdentType::Internal(_) | IdentType::Synch(_) => None,
257            IdentType::User(u) => u
258                .entry
259                .get_ava_as_session_map(Attribute::UserAuthTokenSession)
260                .and_then(|sessions| sessions.get(&self.session_id)),
261        }
262    }
263
264    pub fn get_user_entry(&self) -> Option<Arc<EntrySealedCommitted>> {
265        match &self.origin {
266            IdentType::Internal(_) | IdentType::Synch(_) => None,
267            IdentType::User(u) => Some(u.entry.clone()),
268        }
269    }
270
271    pub fn from_impersonate(ident: &Self) -> Self {
272        // TODO #64 ?: In the future, we could change some of this data
273        // to reflect the fact we are in fact impersonating the action
274        // rather than the user explicitly requesting it. Could matter
275        // to audits and logs to determine what happened.
276        ident.clone()
277    }
278
279    pub fn is_internal(&self) -> bool {
280        matches!(self.origin, IdentType::Internal(_))
281    }
282
283    pub fn get_uuid(&self) -> Option<Uuid> {
284        match &self.origin {
285            IdentType::Internal(role) => Some(role.get_uuid()),
286            IdentType::User(u) => Some(u.entry.get_uuid()),
287            IdentType::Synch(u) => Some(*u),
288        }
289    }
290
291    /// Indicate if the session associated with this identity has a session
292    /// that can logout. Examples of sessions that *can not* logout are anonymous,
293    /// tokens, or PIV sessions.
294    pub fn can_logout(&self) -> bool {
295        match &self.origin {
296            IdentType::Internal(_) => false,
297            IdentType::User(u) => u.entry.get_uuid() != UUID_ANONYMOUS,
298            IdentType::Synch(_) => false,
299        }
300    }
301
302    pub fn get_event_origin_id(&self) -> IdentityId {
303        IdentityId::from(&self.origin)
304    }
305
306    #[cfg(test)]
307    pub fn has_claim(&self, claim: &str) -> bool {
308        match &self.origin {
309            IdentType::Internal(_) | IdentType::Synch(_) => false,
310            IdentType::User(u) => u
311                .entry
312                .attribute_equality(Attribute::Claim, &PartialValue::new_iutf8(claim)),
313        }
314    }
315
316    pub fn is_memberof(&self, group: Uuid) -> bool {
317        match &self.origin {
318            IdentType::Internal(_) | IdentType::Synch(_) => false,
319            IdentType::User(u) => u
320                .entry
321                .attribute_equality(Attribute::MemberOf, &PartialValue::Refer(group)),
322        }
323    }
324
325    pub fn get_memberof(&self) -> Option<&BTreeSet<Uuid>> {
326        match &self.origin {
327            IdentType::Internal(_) | IdentType::Synch(_) => None,
328            IdentType::User(u) => u.entry.get_ava_refer(Attribute::MemberOf),
329        }
330    }
331
332    pub fn get_oauth2_consent_scopes(&self, oauth2_rs: Uuid) -> Option<&BTreeSet<String>> {
333        match &self.origin {
334            IdentType::Internal(_) | IdentType::Synch(_) => None,
335            IdentType::User(u) => u
336                .entry
337                .get_ava_as_oauthscopemaps(Attribute::OAuth2ConsentScopeMap)
338                .and_then(|scope_map| scope_map.get(&oauth2_rs)),
339        }
340    }
341}