mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-03 23:55:05 +02:00
101 lines
3.1 KiB
Rust
101 lines
3.1 KiB
Rust
use crate::model::{self, ActorModel, Transition, TransitionAction, TransitionResult};
|
|
|
|
use crate::error::Error;
|
|
use crate::run::EventRecord;
|
|
use crate::state::*;
|
|
use kanidm_client::KanidmClient;
|
|
|
|
use async_trait::async_trait;
|
|
use rand::Rng;
|
|
use rand_chacha::ChaCha8Rng;
|
|
|
|
use std::time::Duration;
|
|
|
|
enum State {
|
|
Unauthenticated,
|
|
Authenticated,
|
|
}
|
|
|
|
pub struct ActorReader {
|
|
state: State,
|
|
randomised_backoff_time: Duration,
|
|
}
|
|
|
|
impl ActorReader {
|
|
pub fn new(mut cha_rng: ChaCha8Rng, warmup_time_ms: u64) -> Self {
|
|
let max_backoff_time_in_ms = warmup_time_ms - 1000;
|
|
let randomised_backoff_time =
|
|
Duration::from_millis(cha_rng.gen_range(0..max_backoff_time_in_ms));
|
|
ActorReader {
|
|
state: State::Unauthenticated,
|
|
randomised_backoff_time,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ActorModel for ActorReader {
|
|
async fn transition(
|
|
&mut self,
|
|
client: &KanidmClient,
|
|
person: &Person,
|
|
) -> Result<Vec<EventRecord>, Error> {
|
|
let transition = self.next_transition();
|
|
|
|
if let Some(delay) = transition.delay {
|
|
tokio::time::sleep(delay).await;
|
|
}
|
|
|
|
// Once we get to here, we want the transition to go ahead.
|
|
let (result, event) = match transition.action {
|
|
TransitionAction::Login => model::login(client, person).await,
|
|
TransitionAction::Logout => model::logout(client, person).await,
|
|
TransitionAction::PrivilegeReauth
|
|
| TransitionAction::WriteAttributePersonMail
|
|
| TransitionAction::ReadSelfAccount
|
|
| TransitionAction::WriteSelfPassword => return Err(Error::InvalidState),
|
|
TransitionAction::ReadSelfMemberOf => {
|
|
model::person_get_self_memberof(client, person).await
|
|
}
|
|
}?;
|
|
|
|
self.next_state(transition.action, result);
|
|
|
|
Ok(event)
|
|
}
|
|
}
|
|
|
|
impl ActorReader {
|
|
fn next_transition(&mut self) -> Transition {
|
|
match self.state {
|
|
State::Unauthenticated => Transition {
|
|
delay: Some(self.randomised_backoff_time),
|
|
action: TransitionAction::Login,
|
|
},
|
|
State::Authenticated => Transition {
|
|
delay: Some(Duration::from_secs(1)),
|
|
action: TransitionAction::ReadSelfMemberOf,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn next_state(&mut self, action: TransitionAction, result: TransitionResult) {
|
|
// Is this a design flaw? We probably need to know what the state was that we
|
|
// requested to move to?
|
|
match (&self.state, action, result) {
|
|
(State::Unauthenticated, TransitionAction::Login, TransitionResult::Ok) => {
|
|
self.state = State::Authenticated;
|
|
}
|
|
(State::Authenticated, TransitionAction::ReadSelfMemberOf, TransitionResult::Ok) => {
|
|
self.state = State::Authenticated;
|
|
}
|
|
#[allow(clippy::unreachable)]
|
|
(_, _, TransitionResult::Ok) => unreachable!(),
|
|
|
|
(_, _, TransitionResult::Error) => {
|
|
self.state = State::Unauthenticated {};
|
|
}
|
|
}
|
|
}
|
|
}
|