mini_rust_auth/
session.rs

1//! Provides function wrappers around the `auth` module which are ready to be used as endpoints in actix web.
2//! Made to interact with a actix_session session. Mainly this is only needed for session management. User creation is not effected.
3//! This should be more easily used inside other projects. As long as a session is present. This saves you from needing to build
4//! your own wrappers around actix session or managing cookies yourself.
5//!
6//! **SECURITY NOTE**: The session token will be stored on the client side in a cookie. The cookies should have the max security possible.
7//! This can be configured when creating the actix_session wrapper.
8//!
9//! Note that this only provides functions relating to sessions. These functions can be used at the top of your own functions to check if a session
10//! is valid. However note that for the most part if you need to for example add a user (dose not relate to sessions) you should use `auth::add_user()` or
11//! `wrappers::add_user()` if you want a endpoint you can tie to.
12use actix_session;
13use actix_web::{cookie::time::format_description::modifier::End, HttpRequest, HttpResponse, Responder};
14use argon2::password_hash::rand_core::impls;
15use sqlx::{self, Postgres};
16
17use crate::auth;
18
19pub enum GenerateSessionReturn {
20    SessionGeneratedAndSaved(),
21    FailedToGenerate(String),
22    ClientSessionNotSetCorrectly(),
23}
24
25pub enum ValidatedSessionReturn<'a> {
26    ValidSession(),
27    SessionOutOfDate(),
28    /// Could not exists in DB or user may be wrong.
29    SessionInvalid(),
30    ClientSessionNotSetCorrectly(&'a str),
31}
32
33pub enum EndSessionsReturn<'a> {
34    RemovedSessions(),
35    SessionInvalid(),
36    ClientSessionNotSetCorrectly(&'a str),
37    DatabaseError(String),
38}
39
40///`generate_session` will create a auth session based on the provided credentials
41/// and add it to the actix_session. This auth session can now be checked on
42/// subsequent calls to for example `validate_session`
43pub async fn generate_session(
44    session: actix_session::Session,
45    creds: auth::Credentials,
46    pool: &sqlx::Pool<Postgres>,
47) -> GenerateSessionReturn {
48    let local_session = match auth::generate_session(&creds, pool, auth::SESSION_VALID_FOR_SECONDS).await {
49        Ok(session) => session,
50        Err(err) => {
51            return GenerateSessionReturn::FailedToGenerate(format!("Failed with {:?}", err))
52        }
53    };
54
55    let _user_name_insert = match session.insert("user_name", &local_session.user_name) {
56        Ok(_) => (),
57        Err(_) => return GenerateSessionReturn::ClientSessionNotSetCorrectly(),
58    };
59
60    let _session_token_insert = match session.insert("session_token", &local_session.session_token)
61    {
62        Ok(_) => (),
63        Err(_) => return GenerateSessionReturn::ClientSessionNotSetCorrectly(),
64    };
65
66    let _time_to_die_insert =
67        match session.insert("time_to_die", &local_session.time_to_die.to_rfc3339()) {
68            Ok(time_to_die) => time_to_die,
69            Err(_) => return GenerateSessionReturn::ClientSessionNotSetCorrectly(),
70        };
71
72    return GenerateSessionReturn::SessionGeneratedAndSaved();
73}
74
75/// Validate session using the given session but will return a type
76/// compatible with actix web responder.
77pub async fn generate_session_web_resp(
78    client_session: actix_session::Session,
79    json_creds: actix_web::web::Json<auth::Credentials>,
80    pool: actix_web::web::Data<sqlx::Pool<Postgres>>,
81) -> impl Responder {
82    let creds = auth::Credentials {
83        password: json_creds.password.to_string(),
84        user_name: json_creds.user_name.to_string(),
85        realm: json_creds.realm.to_string(),
86    };
87
88    match generate_session(client_session, creds, &pool).await {
89        GenerateSessionReturn::ClientSessionNotSetCorrectly() => {
90            actix_web::HttpResponse::InternalServerError().body("Session failed to be set")
91        }
92        GenerateSessionReturn::FailedToGenerate(err) => {
93            actix_web::HttpResponse::InternalServerError()
94                .body(format!("Session failed to generate: {:?}", err))
95        }
96        GenerateSessionReturn::SessionGeneratedAndSaved() => {
97            actix_web::HttpResponse::Accepted().body("Session Good")
98        }
99    }
100}
101
102/// `validate_session` will check the session stored in the current `actix_session`
103/// this will be valid if `generate_session` was call prior and set up a auth session.
104/// Assuming the auth session is not invalid
105pub async fn validate_session(
106    client_session: actix_session::Session,
107    pool: &sqlx::Pool<Postgres>,
108) -> ValidatedSessionReturn {
109    let user_name = match client_session.get::<String>("user_name") {
110        Ok(user_name_option) => match user_name_option {
111            Some(user_name) => user_name,
112            None => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("user_name"),
113        },
114        Err(_) => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("user_name"),
115    };
116
117    let session_token = match client_session.get::<String>("session_token") {
118        Ok(session_token_option) => match session_token_option {
119            Some(session_token) => session_token,
120            None => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("session_token"),
121        },
122        Err(_) => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("session_token"),
123    };
124
125    let time_to_die = match client_session.get::<String>("time_to_die") {
126        Ok(session_token_option) => match session_token_option {
127            Some(session_token) => session_token,
128            None => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("time_to_die"),
129        },
130        Err(_) => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("time_to_die"),
131    };
132
133    let time = match chrono::DateTime::parse_from_rfc3339(&time_to_die) {
134        Ok(time) => time,
135        Err(_) => return ValidatedSessionReturn::ClientSessionNotSetCorrectly("time set wrong in time_to_die"),
136    };
137
138    let local_session = auth::Session {
139        user_name: user_name,
140        session_token: session_token,
141        time_to_die: time.into(),
142    };
143
144    match auth::validate_session(&local_session, &pool).await {
145        auth::SessionValidated::ValidSession() => ValidatedSessionReturn::ValidSession(),
146        auth::SessionValidated::InvalidSession() => {
147            return ValidatedSessionReturn::SessionInvalid()
148        }
149    }
150}
151
152/// Validate session using the given session but will return a type
153/// compatible with actix web responder.
154pub async fn validate_session_web_resp(
155    client_session: actix_session::Session,
156    pool: actix_web::web::Data<sqlx::Pool<Postgres>>,
157) -> impl Responder {
158    match validate_session(client_session, &pool).await {
159        ValidatedSessionReturn::ClientSessionNotSetCorrectly(err) => {
160            HttpResponse::InternalServerError().body(format!("Client Session not set correctly\nGot err: {:?}", err))
161        }
162        ValidatedSessionReturn::SessionInvalid() => {
163            HttpResponse::InternalServerError().body("Session invalid")
164        }
165        ValidatedSessionReturn::SessionOutOfDate() => {
166            HttpResponse::InternalServerError().body("Session out of date")
167        }
168        ValidatedSessionReturn::ValidSession() => HttpResponse::Accepted().body("Session Good"),
169    }
170}
171
172pub async fn end_sessions<'a> (
173    client_session: actix_session::Session,
174    pool: actix_web::web::Data<sqlx::Pool<Postgres>>,
175) -> EndSessionsReturn<'a> {
176    let user_name = match client_session.get::<String>("user_name") {
177        Ok(user_name_option) => match user_name_option {
178            Some(user_name) => user_name,
179            None => return EndSessionsReturn::ClientSessionNotSetCorrectly("user_name"),
180        },
181        Err(_) => return EndSessionsReturn::ClientSessionNotSetCorrectly("user_name"),
182    };
183
184    let session_token = match client_session.get::<String>("session_token") {
185        Ok(session_token_option) => match session_token_option {
186            Some(session_token) => session_token,
187            None => return EndSessionsReturn::ClientSessionNotSetCorrectly("session_token"),
188        },
189        Err(_) => return EndSessionsReturn::ClientSessionNotSetCorrectly("session_token"),
190    };
191
192    let time_to_die = match client_session.get::<String>("time_to_die") {
193        Ok(session_token_option) => match session_token_option {
194            Some(session_token) => session_token,
195            None => return EndSessionsReturn::ClientSessionNotSetCorrectly("time_to_die"),
196        },
197        Err(_) => return EndSessionsReturn::ClientSessionNotSetCorrectly("time_to_die"),
198    };
199
200    let time = match chrono::DateTime::parse_from_rfc3339(&time_to_die) {
201        Ok(time) => time,
202        Err(_) => return EndSessionsReturn::ClientSessionNotSetCorrectly("time set wrong in time_to_die"),
203    };
204
205    let local_session = auth::Session {
206        user_name: user_name,
207        session_token: session_token,
208        time_to_die: time.into(),
209    };
210
211    match auth::end_sessions(&local_session, &pool).await {
212        auth::EndSessionReturn::BadSession() => EndSessionsReturn::SessionInvalid(),
213        auth::EndSessionReturn::DataBaseError(err) => EndSessionsReturn::DatabaseError(err.to_string()),
214        auth::EndSessionReturn::Ended() => EndSessionsReturn::RemovedSessions(),
215
216    }
217}
218
219pub async fn end_sessions_web_resp(
220    client_session: actix_session::Session,
221    pool: actix_web::web::Data<sqlx::Pool<Postgres>>,   
222) -> impl Responder {
223    match end_sessions(client_session, pool).await {
224        EndSessionsReturn::RemovedSessions() => HttpResponse::Accepted().body("Removed All Sessions"),
225        EndSessionsReturn::ClientSessionNotSetCorrectly(msg) => HttpResponse::BadRequest().body(msg),
226        EndSessionsReturn::SessionInvalid() => HttpResponse::Forbidden().body("Invalid Session"),
227        EndSessionsReturn::DatabaseError(msg) => HttpResponse::InternalServerError().body(format!("Database Issue: {msg}"))
228    }
229}