axum_security/cookie/
session.rs1use std::{convert::Infallible, hash::Hash};
2
3use axum::{
4 extract::{FromRequestParts, OptionalFromRequestParts},
5 http::{Extensions, StatusCode, request::Parts},
6};
7
8use crate::cookie::SessionId;
9
10#[derive(Clone, Debug)]
15pub struct CookieSession<S> {
16 pub session_id: SessionId,
18 pub created_at: u64,
20 pub state: S,
22}
23
24impl<S> CookieSession<S> {
25 pub fn new(id: SessionId, created_at: u64, value: S) -> Self {
26 Self {
27 session_id: id,
28 created_at,
29 state: value,
30 }
31 }
32
33 pub fn from_extensions(extensions: &mut Extensions) -> Option<Self>
34 where
35 S: Send + Sync + 'static,
36 {
37 extensions.remove()
38 }
39}
40
41impl<S> Hash for CookieSession<S> {
42 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
43 self.session_id.hash(state)
44 }
45}
46
47impl<S> Eq for CookieSession<S> {}
48
49impl<S> PartialEq for CookieSession<S> {
50 fn eq(&self, other: &Self) -> bool {
51 self.session_id == other.session_id
52 }
53}
54
55impl<S, T> FromRequestParts<S> for CookieSession<T>
56where
57 S: Send + Sync,
58 T: Send + Sync + 'static,
59{
60 type Rejection = StatusCode;
61
62 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, StatusCode> {
63 if let Some(session) = parts.extensions.remove() {
64 Ok(session)
65 } else {
66 Err(StatusCode::UNAUTHORIZED)
67 }
68 }
69}
70
71impl<S, T> OptionalFromRequestParts<S> for CookieSession<T>
72where
73 S: Send + Sync,
74 T: Send + Sync + 'static,
75{
76 type Rejection = Infallible;
77
78 async fn from_request_parts(
79 parts: &mut Parts,
80 _state: &S,
81 ) -> Result<Option<Self>, Self::Rejection> {
82 Ok(parts.extensions.remove())
83 }
84}
85
86#[cfg(test)]
87mod extract_cookie {
88 use axum::{
89 extract::FromRequestParts,
90 http::{Request, StatusCode},
91 };
92
93 use crate::cookie::{CookieSession, SessionId};
94
95 #[tokio::test]
96 async fn extract() {
97 let cookie = CookieSession::new(SessionId::new(), 0, ());
98
99 let (mut parts, _) = Request::builder()
100 .extension(cookie.clone())
101 .body(())
102 .unwrap()
103 .into_parts();
104
105 let extracted_cookie = CookieSession::<()>::from_request_parts(&mut parts, &())
106 .await
107 .unwrap();
108
109 assert!(cookie.session_id == extracted_cookie.session_id);
110 assert!(cookie.created_at == extracted_cookie.created_at);
111 }
112
113 #[tokio::test]
114 async fn extract_rejection() {
115 let (mut parts, _) = Request::builder().body(()).unwrap().into_parts();
116
117 let rejection = CookieSession::<()>::from_request_parts(&mut parts, &())
118 .await
119 .unwrap_err();
120
121 assert!(rejection == StatusCode::UNAUTHORIZED);
122 }
123}