@@ -8,19 +8,34 @@ use crate::schema::persistent_sessions;
8
8
use crate :: util:: token:: SecureToken ;
9
9
use crate :: util:: token:: SecureTokenKind ;
10
10
11
+ /// A persistent session model (as is stored in the database).
12
+ ///
13
+ /// The sessions table works by maintaining a `hashed_token`. In order for a user to securely
14
+ /// demonstrate authenticity, the user provides us with the token (stored as part of a cookie). We
15
+ /// hash the token and search in the database for matches. If we find one and the token hasn't
16
+ /// been revoked, then we update the session with the latest values and authorize the user.
11
17
#[ derive( Clone , Debug , PartialEq , Eq , Identifiable , Queryable ) ]
12
18
#[ table_name = "persistent_sessions" ]
13
19
pub struct PersistentSession {
20
+ /// The id of this session.
14
21
pub id : i32 ,
22
+ /// The user id associated with this session.
15
23
pub user_id : i32 ,
24
+ /// The token (hashed) that identifies the session.
16
25
pub hashed_token : SecureToken ,
26
+ /// Datetime the session was created.
17
27
pub created_at : NaiveDateTime ,
28
+ /// Datetime the session was last used.
18
29
pub last_used_at : NaiveDateTime ,
30
+ /// Whether the session is revoked.
19
31
pub revoked : bool ,
32
+ /// Last IP address that used the session.
20
33
pub last_ip_address : IpNetwork ,
34
+ /// Last user agent that used the session.
21
35
pub last_user_agent : String ,
22
36
}
23
37
38
+ /// Session-related errors.
24
39
#[ derive( Error , Debug , PartialEq ) ]
25
40
pub enum SessionError {
26
41
#[ error( "token prefix doesn't match session tokens" ) ]
@@ -30,6 +45,7 @@ pub enum SessionError {
30
45
}
31
46
32
47
impl PersistentSession {
48
+ /// Creates a `NewPersistentSession` that can be inserted into the database.
33
49
pub fn create < ' a , ' b > (
34
50
user_id : i32 ,
35
51
token : & ' a SecureToken ,
@@ -44,6 +60,13 @@ impl PersistentSession {
44
60
}
45
61
}
46
62
63
+ /// Finds an unrevoked session that matches `token` from the database and returns it.
64
+ ///
65
+ /// # Returns
66
+ ///
67
+ /// * `Ok(Some(...))` if a session matches the `token`.
68
+ /// * `Ok(None)` if no session matches the `token`.
69
+ /// * `Err(...)` for other errors such as invalid tokens or diesel errors.
47
70
pub fn find_from_token_and_update (
48
71
conn : & PgConnection ,
49
72
token : & str ,
@@ -56,6 +79,8 @@ impl PersistentSession {
56
79
. filter ( persistent_sessions:: revoked. eq ( false ) )
57
80
. filter ( persistent_sessions:: hashed_token. eq ( hashed_token) ) ;
58
81
82
+ // TODO: Do we want to check if the user agent or IP address don't match? What about the
83
+ // created_at/last_user_at times, do we want to expire the tokens?
59
84
conn. transaction ( || {
60
85
diesel:: update ( sessions. clone ( ) )
61
86
. set ( (
@@ -70,6 +95,12 @@ impl PersistentSession {
70
95
. map_err ( SessionError :: DieselError )
71
96
}
72
97
98
+ /// Revokes the `token` in the database.
99
+ ///
100
+ /// # Returns
101
+ ///
102
+ /// The number of sessions that were revoked or an error if the `token` isn't valid or there
103
+ /// was an issue with the database connection.
73
104
pub fn revoke_from_token ( conn : & PgConnection , token : & str ) -> Result < usize , SessionError > {
74
105
let hashed_token = SecureToken :: parse ( SecureTokenKind :: Session , token)
75
106
. ok_or ( SessionError :: InvalidSessionToken ) ?;
@@ -84,6 +115,7 @@ impl PersistentSession {
84
115
}
85
116
}
86
117
118
+ /// A new, insertable persistent session.
87
119
#[ derive( Clone , Debug , PartialEq , Eq , Insertable ) ]
88
120
#[ table_name = "persistent_sessions" ]
89
121
pub struct NewPersistentSession < ' a , ' b > {
@@ -94,6 +126,7 @@ pub struct NewPersistentSession<'a, 'b> {
94
126
}
95
127
96
128
impl NewPersistentSession < ' _ , ' _ > {
129
+ /// Inserts the session into the database.
97
130
pub fn insert ( self , conn : & PgConnection ) -> Result < PersistentSession , diesel:: result:: Error > {
98
131
diesel:: insert_into ( persistent_sessions:: table)
99
132
. values ( self )
0 commit comments