haste_server/auth_n/session/
user.rs1use crate::auth_n::error::AuthError;
2use haste_fhir_model::r4::generated::terminology::IssueType;
3use haste_fhir_operation_error::OperationOutcomeError;
4use haste_repository::{
5 Repository,
6 admin::TenantModelAdmin,
7 types::{
8 mfa::{UserMFACredentialCreate, UserMFASearchClaims},
9 scope::UserId,
10 user::User,
11 },
12};
13use serde::{Deserialize, Serialize};
14use tower_sessions::Session;
15
16static AUTHORIZATION_STATE_KEY: &str = "user_authorization_state";
17
18#[derive(Deserialize, Serialize)]
19pub struct AuthorizationStateCompleted {
20 pub user: User,
21}
22
23#[derive(Deserialize, Serialize)]
24pub enum SessionAuthorizationState {
25 Complete(AuthorizationStateCompleted),
26 MFARequired { user: User },
27 }
31
32pub async fn get_completed_authorization_state(
33 session: &Session,
34) -> Result<AuthorizationStateCompleted, OperationOutcomeError> {
35 let authorization_state = get_authorization_state(session).await?;
36
37 match authorization_state {
38 Some(SessionAuthorizationState::Complete(completed_state)) => Ok(completed_state),
39 _ => Err(OperationOutcomeError::error(
40 IssueType::invalid(),
41 "Authorization state is not complete.".to_string(),
42 )),
43 }
44}
45
46pub async fn get_authorization_state(
47 session: &Session,
48) -> Result<Option<SessionAuthorizationState>, OperationOutcomeError> {
49 let authorization_state = session
50 .get::<SessionAuthorizationState>(AUTHORIZATION_STATE_KEY)
51 .await
52 .map_err(|_e| {
53 OperationOutcomeError::fatal(
54 IssueType::exception(),
55 "Session returned an error when retrieving current user.".to_string(),
56 )
57 })?;
58
59 Ok(authorization_state)
60}
61
62pub async fn set_initial_authorization_state<Repo: Repository>(
63 repo: &Repo,
64 session: &Session,
65 user: User,
66) -> Result<(), OperationOutcomeError> {
67 let active_mfa_credentials = TenantModelAdmin::<UserMFACredentialCreate, _, _, _, _>::search(
68 repo,
69 &user.tenant,
70 &UserMFASearchClaims {
71 tenant: user.tenant.clone(),
72 user_id: UserId::new(user.id.clone()),
73 is_active: Some(true),
74 },
75 )
76 .await?;
77
78 let initial_state = if active_mfa_credentials.is_empty() {
79 SessionAuthorizationState::Complete(AuthorizationStateCompleted { user })
81 } else {
82 SessionAuthorizationState::MFARequired { user }
83 };
84
85 session
86 .insert(AUTHORIZATION_STATE_KEY, initial_state)
87 .await
88 .map_err(|e| AuthError::SessionWriteFailed(e).into())
89}
90
91pub async fn set_completed_authorization_state(
92 session: &Session,
93 user: User,
94) -> Result<(), OperationOutcomeError> {
95 session
96 .insert(
97 AUTHORIZATION_STATE_KEY,
98 SessionAuthorizationState::Complete(AuthorizationStateCompleted { user }),
99 )
100 .await
101 .map_err(|e| AuthError::SessionWriteFailed(e).into())
102}
103
104pub async fn rotate_session_id(session: &Session) -> Result<(), OperationOutcomeError> {
111 session.cycle_id().await.map_err(|_e| {
112 OperationOutcomeError::fatal(
113 IssueType::exception(),
114 "Failed to rotate the session id.".to_string(),
115 )
116 })
117}
118
119pub async fn clear_authorization_state(session: &Session) -> Result<(), OperationOutcomeError> {
120 session
121 .remove::<SessionAuthorizationState>(AUTHORIZATION_STATE_KEY)
122 .await
123 .map_err(|_e| {
124 OperationOutcomeError::fatal(
125 IssueType::exception(),
126 "Failed to clear user from session.".to_string(),
127 )
128 })?;
129
130 Ok(())
131}