orca/models/
read.rs

1use crate::model::{self, ActorModel, Transition, TransitionAction, TransitionResult};
2
3use crate::error::Error;
4use crate::run::EventRecord;
5use crate::state::*;
6use kanidm_client::KanidmClient;
7
8use async_trait::async_trait;
9use rand::Rng;
10use rand_chacha::ChaCha8Rng;
11
12use std::time::Duration;
13
14enum State {
15    Unauthenticated,
16    Authenticated,
17}
18
19pub struct ActorReader {
20    state: State,
21    randomised_backoff_time: Duration,
22}
23
24impl ActorReader {
25    pub fn new(mut cha_rng: ChaCha8Rng, warmup_time_ms: u64) -> Self {
26        let max_backoff_time_in_ms = warmup_time_ms - 1000;
27        let randomised_backoff_time =
28            Duration::from_millis(cha_rng.random_range(0..max_backoff_time_in_ms));
29        ActorReader {
30            state: State::Unauthenticated,
31            randomised_backoff_time,
32        }
33    }
34}
35
36#[async_trait]
37impl ActorModel for ActorReader {
38    async fn transition(
39        &mut self,
40        client: &KanidmClient,
41        person: &Person,
42    ) -> Result<Vec<EventRecord>, Error> {
43        let transition = self.next_transition();
44
45        if let Some(delay) = transition.delay {
46            tokio::time::sleep(delay).await;
47        }
48
49        // Once we get to here, we want the transition to go ahead.
50        let (result, event) = match transition.action {
51            TransitionAction::Login => model::login(client, person).await,
52            TransitionAction::Logout => model::logout(client, person).await,
53            TransitionAction::PrivilegeReauth
54            | TransitionAction::WriteAttributePersonMail
55            | TransitionAction::ReadSelfAccount
56            | TransitionAction::WriteSelfPassword => return Err(Error::InvalidState),
57            TransitionAction::ReadSelfMemberOf => {
58                model::person_get_self_memberof(client, person).await
59            }
60        }?;
61
62        self.next_state(transition.action, result);
63
64        Ok(event)
65    }
66}
67
68impl ActorReader {
69    fn next_transition(&mut self) -> Transition {
70        match self.state {
71            State::Unauthenticated => Transition {
72                delay: Some(self.randomised_backoff_time),
73                action: TransitionAction::Login,
74            },
75            State::Authenticated => Transition {
76                delay: Some(Duration::from_secs(1)),
77                action: TransitionAction::ReadSelfMemberOf,
78            },
79        }
80    }
81
82    fn next_state(&mut self, action: TransitionAction, result: TransitionResult) {
83        // Is this a design flaw? We probably need to know what the state was that we
84        // requested to move to?
85        match (&self.state, action, result) {
86            (State::Unauthenticated, TransitionAction::Login, TransitionResult::Ok) => {
87                self.state = State::Authenticated;
88            }
89            (State::Authenticated, TransitionAction::ReadSelfMemberOf, TransitionResult::Ok) => {
90                self.state = State::Authenticated;
91            }
92            #[allow(clippy::unreachable)]
93            (_, _, TransitionResult::Ok) => unreachable!(),
94
95            (_, _, TransitionResult::Error) => {
96                self.state = State::Unauthenticated {};
97            }
98        }
99    }
100}