11use chrono:: NaiveDateTime ;
22use cookie:: { Cookie , SameSite } ;
33use diesel:: prelude:: * ;
4- use ipnetwork:: IpNetwork ;
5- use std:: net:: IpAddr ;
64use std:: num:: ParseIntError ;
75use std:: str:: FromStr ;
86use thiserror:: Error ;
@@ -28,106 +26,69 @@ pub struct PersistentSession {
2826 pub hashed_token : SecureToken ,
2927 /// Datetime the session was created.
3028 pub created_at : NaiveDateTime ,
31- /// Datetime the session was last used.
32- pub last_used_at : NaiveDateTime ,
3329 /// Whether the session is revoked.
3430 pub revoked : bool ,
35- /// Last IP address that used the session.
36- pub last_ip_address : IpNetwork ,
37- /// Last user agent that used the session.
38- pub last_user_agent : String ,
39- }
40-
41- /// Session-related errors.
42- #[ derive( Error , Debug , PartialEq ) ]
43- pub enum SessionError {
44- #[ error( "token prefix doesn't match session tokens" ) ]
45- InvalidSessionToken ,
46- #[ error( "database manipulation error" ) ]
47- DieselError ( #[ from] diesel:: result:: Error ) ,
4831}
4932
5033impl PersistentSession {
5134 /// Creates a `NewPersistentSession` that can be inserted into the database.
52- pub fn create < ' a , ' b > (
53- user_id : i32 ,
54- token : & ' a SecureToken ,
55- last_ip_address : IpAddr ,
56- last_user_agent : & ' b str ,
57- ) -> NewPersistentSession < ' a , ' b > {
35+ pub fn create < ' a > ( user_id : i32 , token : & ' a SecureToken ) -> NewPersistentSession < ' a > {
5836 NewPersistentSession {
5937 user_id,
6038 hashed_token : token,
61- last_ip_address : last_ip_address. into ( ) ,
62- last_user_agent,
6339 }
6440 }
6541
66- /// Finds an unrevoked session that matches `token` from the database and returns it .
42+ /// Finds the session with the ID .
6743 ///
6844 /// # Returns
6945 ///
70- /// * `Ok(Some(...))` if a session matches the `token`.
71- /// * `Ok(None)` if no session matches the `token`.
72- /// * `Err(...)` for other errors such as invalid tokens or diesel errors.
73- pub fn find_and_update (
74- conn : & PgConnection ,
75- session_cookie : & SessionCookie ,
76- ip_address : IpAddr ,
77- user_agent : & str ,
78- ) -> Result < Option < Self > , SessionError > {
79- let hashed_token = SecureToken :: parse ( SecureTokenKind :: Session , & session_cookie. token )
80- . ok_or ( SessionError :: InvalidSessionToken ) ?;
81- let sessions = persistent_sessions:: table
82- . filter ( persistent_sessions:: id. eq ( session_cookie. id ) )
83- . filter ( persistent_sessions:: revoked. eq ( false ) )
84- . filter ( persistent_sessions:: hashed_token. eq ( hashed_token) ) ;
85-
86- conn. transaction ( || {
87- diesel:: update ( sessions. clone ( ) )
88- . set ( (
89- persistent_sessions:: last_used_at. eq ( diesel:: dsl:: now) ,
90- persistent_sessions:: last_ip_address. eq ( IpNetwork :: from ( ip_address) ) ,
91- persistent_sessions:: last_user_agent. eq ( user_agent) ,
92- ) )
93- . get_result ( conn)
94- . optional ( )
95- } )
96- . or_else ( |_| sessions. first ( conn) . optional ( ) )
97- . map_err ( SessionError :: DieselError )
46+ /// * `Ok(Some(...))` if a session matches the id.
47+ /// * `Ok(None)` if no session matches the id.
48+ /// * `Err(...)` for other errors..
49+ pub fn find ( id : i64 , conn : & PgConnection ) -> Result < Option < Self > , diesel:: result:: Error > {
50+ persistent_sessions:: table
51+ . find ( id)
52+ . get_result ( conn)
53+ . optional ( )
9854 }
9955
100- /// Revokes the `token` in the database.
101- ///
102- /// # Returns
103- ///
104- /// The number of sessions that were revoked or an error if the `token` isn't valid or there
105- /// was an issue with the database connection.
106- pub fn revoke_from_token ( conn : & PgConnection , token : & str ) -> Result < usize , SessionError > {
107- let hashed_token = SecureToken :: parse ( SecureTokenKind :: Session , token)
108- . ok_or ( SessionError :: InvalidSessionToken ) ?;
109- let sessions = persistent_sessions:: table
110- . filter ( persistent_sessions:: hashed_token. eq ( hashed_token) )
111- . filter ( persistent_sessions:: revoked. eq ( false ) ) ;
112-
113- diesel:: update ( sessions)
114- . set ( persistent_sessions:: revoked. eq ( true ) )
115- . execute ( conn)
116- . map_err ( SessionError :: DieselError )
56+ /// Updates the session in the database.
57+ pub fn update ( & self , conn : & PgConnection ) -> Result < ( ) , diesel:: result:: Error > {
58+ diesel:: update ( persistent_sessions:: table. find ( self . id ) )
59+ . set ( (
60+ persistent_sessions:: user_id. eq ( & self . user_id ) ,
61+ persistent_sessions:: hashed_token. eq ( & self . hashed_token ) ,
62+ persistent_sessions:: revoked. eq ( & self . revoked ) ,
63+ ) )
64+ . get_result :: < Self > ( conn)
65+ . map ( |_| ( ) )
66+ }
67+
68+ pub fn is_authorized ( & self , token : & str ) -> bool {
69+ if let Some ( hashed_token) = SecureToken :: parse ( SecureTokenKind :: Session , token) {
70+ !self . revoked && self . hashed_token == hashed_token
71+ } else {
72+ false
73+ }
74+ }
75+
76+ /// Revokes the session (needs update).
77+ pub fn revoke ( & mut self ) -> & mut Self {
78+ self . revoked = true ;
79+ self
11780 }
11881}
11982
12083/// A new, insertable persistent session.
12184#[ derive( Clone , Debug , PartialEq , Eq , Insertable ) ]
12285#[ table_name = "persistent_sessions" ]
123- pub struct NewPersistentSession < ' a , ' b > {
86+ pub struct NewPersistentSession < ' a > {
12487 user_id : i32 ,
12588 hashed_token : & ' a SecureToken ,
126- last_ip_address : IpNetwork ,
127- last_user_agent : & ' b str ,
12889}
12990
130- impl NewPersistentSession < ' _ , ' _ > {
91+ impl NewPersistentSession < ' _ > {
13192 /// Inserts the session into the database.
13293 pub fn insert ( self , conn : & PgConnection ) -> Result < PersistentSession , diesel:: result:: Error > {
13394 diesel:: insert_into ( persistent_sessions:: table)
@@ -166,6 +127,14 @@ impl SessionCookie {
166127 . path ( "/" )
167128 . finish ( )
168129 }
130+
131+ pub fn session_id ( & self ) -> i64 {
132+ self . id
133+ }
134+
135+ pub fn token ( & self ) -> & str {
136+ & self . token
137+ }
169138}
170139
171140/// Error returned when the session cookie couldn't be parsed.
0 commit comments