orca/
model.rs

1use crate::error::Error;
2use crate::run::{EventDetail, EventRecord};
3use crate::state::*;
4use std::time::{Duration, Instant};
5
6use kanidm_client::{ClientError, KanidmClient};
7
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10
11pub enum TransitionAction {
12    Login,
13    Logout,
14    PrivilegeReauth,
15    WriteAttributePersonMail,
16    ReadSelfAccount,
17    ReadSelfMemberOf,
18    WriteSelfPassword,
19}
20
21// Is this the right way? Should transitions/delay be part of the actor model? Should
22// they be responsible.
23pub struct Transition {
24    pub delay: Option<Duration>,
25    pub action: TransitionAction,
26}
27
28impl Transition {
29    #[allow(dead_code)]
30    pub fn delay(&self) -> Option<Duration> {
31        self.delay
32    }
33}
34
35#[derive(Eq, PartialEq, Ord, PartialOrd)]
36pub enum TransitionResult {
37    // Success
38    Ok,
39    // We need to re-authenticate, the session expired.
40    // AuthenticationNeeded,
41    // An error occurred.
42    Error,
43}
44
45#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
46pub enum ActorRole {
47    #[default]
48    None,
49    PeoplePiiReader,
50    PeopleSelfMailWrite,
51    PeopleSelfReadProfile,
52    PeopleSelfReadMemberOf,
53    PeopleSelfSetPassword,
54    PeopleGroupAdmin,
55}
56
57impl ActorRole {
58    pub fn requires_membership_to(&self) -> Option<&[&str]> {
59        match self {
60            ActorRole::None
61            | ActorRole::PeopleSelfReadProfile
62            | ActorRole::PeopleSelfReadMemberOf
63            | ActorRole::PeopleSelfSetPassword => None,
64            ActorRole::PeoplePiiReader => Some(&["idm_people_pii_read"]),
65            ActorRole::PeopleSelfMailWrite => Some(&["idm_people_self_mail_write"]),
66            ActorRole::PeopleGroupAdmin => Some(&["idm_group_admins"]),
67        }
68    }
69}
70
71#[async_trait]
72pub trait ActorModel {
73    async fn transition(
74        &mut self,
75        client: &KanidmClient,
76        person: &Person,
77    ) -> Result<Vec<EventRecord>, Error>;
78}
79
80pub async fn login(
81    client: &KanidmClient,
82    person: &Person,
83) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
84    // Should we measure the time of each call rather than the time with multiple calls?
85    let start = Instant::now();
86    let result = match &person.credential {
87        Credential::Password { plain } => {
88            client
89                .auth_simple_password(person.username.as_str(), plain.as_str())
90                .await
91        }
92    };
93
94    let duration = Instant::now().duration_since(start);
95    Ok(parse_call_result_into_transition_result_and_event_record(
96        result,
97        EventDetail::Login,
98        start,
99        duration,
100    ))
101}
102
103pub async fn person_set_self_mail(
104    client: &KanidmClient,
105    person: &Person,
106    values: &[&str],
107) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
108    // Should we measure the time of each call rather than the time with multiple calls?
109    let person_username = person.username.as_str();
110
111    let start = Instant::now();
112    let result = client
113        .idm_person_account_set_attr(person_username, "mail", values)
114        .await;
115
116    let duration = Instant::now().duration_since(start);
117    let parsed_result = parse_call_result_into_transition_result_and_event_record(
118        result,
119        EventDetail::PersonSetSelfMail,
120        start,
121        duration,
122    );
123
124    Ok(parsed_result)
125}
126
127pub async fn person_create_group(
128    client: &KanidmClient,
129    group_name: &str,
130) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
131    let start = Instant::now();
132    let result = client.idm_group_create(group_name, None).await;
133
134    let duration = Instant::now().duration_since(start);
135    let parsed_result = parse_call_result_into_transition_result_and_event_record(
136        result,
137        EventDetail::PersonCreateGroup,
138        start,
139        duration,
140    );
141
142    Ok(parsed_result)
143}
144
145pub async fn person_add_group_members(
146    client: &KanidmClient,
147    group_name: &str,
148    group_members: &[&str],
149) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
150    let start = Instant::now();
151    let result = client
152        .idm_group_add_members(group_name, group_members)
153        .await;
154
155    let duration = Instant::now().duration_since(start);
156    let parsed_result = parse_call_result_into_transition_result_and_event_record(
157        result,
158        EventDetail::PersonAddGroupMembers,
159        start,
160        duration,
161    );
162
163    Ok(parsed_result)
164}
165
166pub async fn person_set_self_password(
167    client: &KanidmClient,
168    person: &Person,
169    pw: &str,
170) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
171    // Should we measure the time of each call rather than the time with multiple calls?
172    let person_username = person.username.as_str();
173
174    let start = Instant::now();
175    let result = client
176        .idm_person_account_primary_credential_set_password(person_username, pw)
177        .await;
178
179    let duration = Instant::now().duration_since(start);
180    let parsed_result = parse_call_result_into_transition_result_and_event_record(
181        result,
182        EventDetail::PersonSetSelfPassword,
183        start,
184        duration,
185    );
186
187    Ok(parsed_result)
188}
189
190pub async fn privilege_reauth(
191    client: &KanidmClient,
192    person: &Person,
193) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
194    let start = Instant::now();
195
196    let result = match &person.credential {
197        Credential::Password { plain } => client.reauth_simple_password(plain.as_str()).await,
198    };
199
200    let duration = Instant::now().duration_since(start);
201
202    let parsed_result = parse_call_result_into_transition_result_and_event_record(
203        result,
204        EventDetail::PersonReauth,
205        start,
206        duration,
207    );
208    Ok(parsed_result)
209}
210
211pub async fn logout(
212    client: &KanidmClient,
213    _person: &Person,
214) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
215    let start = Instant::now();
216    let result = client.logout().await;
217    let duration = Instant::now().duration_since(start);
218
219    Ok(parse_call_result_into_transition_result_and_event_record(
220        result,
221        EventDetail::Logout,
222        start,
223        duration,
224    ))
225}
226
227pub async fn person_get_self_account(
228    client: &KanidmClient,
229    person: &Person,
230) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
231    let start = Instant::now();
232    let result = client.idm_person_account_get(&person.username).await;
233    let duration = Instant::now().duration_since(start);
234    Ok(parse_call_result_into_transition_result_and_event_record(
235        result,
236        EventDetail::PersonGetSelfAccount,
237        start,
238        duration,
239    ))
240}
241
242pub async fn person_get_self_memberof(
243    client: &KanidmClient,
244    person: &Person,
245) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
246    let start = Instant::now();
247    let result = client
248        .idm_person_account_get_attr(&person.username, "memberof")
249        .await;
250    let duration = Instant::now().duration_since(start);
251    Ok(parse_call_result_into_transition_result_and_event_record(
252        result,
253        EventDetail::PersonGetSelfMemberOf,
254        start,
255        duration,
256    ))
257}
258
259fn parse_call_result_into_transition_result_and_event_record<T>(
260    result: Result<T, ClientError>,
261    details: EventDetail,
262    start: Instant,
263    duration: Duration,
264) -> (TransitionResult, Vec<EventRecord>) {
265    match result {
266        Ok(_) => (
267            TransitionResult::Ok,
268            vec![EventRecord {
269                start,
270                duration,
271                details,
272            }],
273        ),
274        Err(client_err) => {
275            debug!(?client_err);
276            (
277                TransitionResult::Error,
278                vec![EventRecord {
279                    start,
280                    duration,
281                    details: EventDetail::Error,
282                }],
283            )
284        }
285    }
286}