use crate::error::Error;
use crate::run::{EventDetail, EventRecord};
use crate::state::*;
use std::time::{Duration, Instant};
use kanidm_client::{ClientError, KanidmClient};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
pub enum TransitionAction {
Login,
Logout,
PrivilegeReauth,
WriteAttributePersonMail,
ReadSelfAccount,
ReadSelfMemberOf,
WriteSelfPassword,
}
pub struct Transition {
pub delay: Option<Duration>,
pub action: TransitionAction,
}
impl Transition {
#[allow(dead_code)]
pub fn delay(&self) -> Option<Duration> {
self.delay
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd)]
pub enum TransitionResult {
Ok,
Error,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
pub enum ActorRole {
#[default]
None,
PeoplePiiReader,
PeopleSelfMailWrite,
PeopleSelfReadProfile,
PeopleSelfReadMemberOf,
PeopleSelfSetPassword,
PeopleGroupAdmin,
}
impl ActorRole {
pub fn requires_membership_to(&self) -> Option<&[&str]> {
match self {
ActorRole::None
| ActorRole::PeopleSelfReadProfile
| ActorRole::PeopleSelfReadMemberOf
| ActorRole::PeopleSelfSetPassword => None,
ActorRole::PeoplePiiReader => Some(&["idm_people_pii_read"]),
ActorRole::PeopleSelfMailWrite => Some(&["idm_people_self_mail_write"]),
ActorRole::PeopleGroupAdmin => Some(&["idm_group_admins"]),
}
}
}
#[async_trait]
pub trait ActorModel {
async fn transition(
&mut self,
client: &KanidmClient,
person: &Person,
) -> Result<Vec<EventRecord>, Error>;
}
pub async fn login(
client: &KanidmClient,
person: &Person,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = match &person.credential {
Credential::Password { plain } => {
client
.auth_simple_password(person.username.as_str(), plain.as_str())
.await
}
};
let duration = Instant::now().duration_since(start);
Ok(parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::Login,
start,
duration,
))
}
pub async fn person_set_self_mail(
client: &KanidmClient,
person: &Person,
values: &[&str],
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let person_username = person.username.as_str();
let start = Instant::now();
let result = client
.idm_person_account_set_attr(person_username, "mail", values)
.await;
let duration = Instant::now().duration_since(start);
let parsed_result = parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonSetSelfMail,
start,
duration,
);
Ok(parsed_result)
}
pub async fn person_create_group(
client: &KanidmClient,
group_name: &str,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = client.idm_group_create(group_name, None).await;
let duration = Instant::now().duration_since(start);
let parsed_result = parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonCreateGroup,
start,
duration,
);
Ok(parsed_result)
}
pub async fn person_add_group_members(
client: &KanidmClient,
group_name: &str,
group_members: &[&str],
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = client
.idm_group_add_members(group_name, group_members)
.await;
let duration = Instant::now().duration_since(start);
let parsed_result = parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonAddGroupMembers,
start,
duration,
);
Ok(parsed_result)
}
pub async fn person_set_self_password(
client: &KanidmClient,
person: &Person,
pw: &str,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let person_username = person.username.as_str();
let start = Instant::now();
let result = client
.idm_person_account_primary_credential_set_password(person_username, pw)
.await;
let duration = Instant::now().duration_since(start);
let parsed_result = parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonSetSelfPassword,
start,
duration,
);
Ok(parsed_result)
}
pub async fn privilege_reauth(
client: &KanidmClient,
person: &Person,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = match &person.credential {
Credential::Password { plain } => client.reauth_simple_password(plain.as_str()).await,
};
let duration = Instant::now().duration_since(start);
let parsed_result = parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonReauth,
start,
duration,
);
Ok(parsed_result)
}
pub async fn logout(
client: &KanidmClient,
_person: &Person,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = client.logout().await;
let duration = Instant::now().duration_since(start);
Ok(parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::Logout,
start,
duration,
))
}
pub async fn person_get_self_account(
client: &KanidmClient,
person: &Person,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = client.idm_person_account_get(&person.username).await;
let duration = Instant::now().duration_since(start);
Ok(parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonGetSelfAccount,
start,
duration,
))
}
pub async fn person_get_self_memberof(
client: &KanidmClient,
person: &Person,
) -> Result<(TransitionResult, Vec<EventRecord>), Error> {
let start = Instant::now();
let result = client
.idm_person_account_get_attr(&person.username, "memberof")
.await;
let duration = Instant::now().duration_since(start);
Ok(parse_call_result_into_transition_result_and_event_record(
result,
EventDetail::PersonGetSelfMemberOf,
start,
duration,
))
}
fn parse_call_result_into_transition_result_and_event_record<T>(
result: Result<T, ClientError>,
details: EventDetail,
start: Instant,
duration: Duration,
) -> (TransitionResult, Vec<EventRecord>) {
match result {
Ok(_) => (
TransitionResult::Ok,
vec![EventRecord {
start,
duration,
details,
}],
),
Err(client_err) => {
debug!(?client_err);
(
TransitionResult::Error,
vec![EventRecord {
start,
duration,
details: EventDetail::Error,
}],
)
}
}
}