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
21pub 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 Ok,
39 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 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 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 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}