@@ -8,19 +8,34 @@ use crate::schema::persistent_sessions;
88use crate :: util:: token:: SecureToken ;
99use crate :: util:: token:: SecureTokenKind ;
1010
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.
1117#[ derive( Clone , Debug , PartialEq , Eq , Identifiable , Queryable ) ]
1218#[ table_name = "persistent_sessions" ]
1319pub struct PersistentSession {
20+ /// The id of this session.
1421 pub id : i32 ,
22+ /// The user id associated with this session.
1523 pub user_id : i32 ,
24+ /// The token (hashed) that identifies the session.
1625 pub hashed_token : SecureToken ,
26+ /// Datetime the session was created.
1727 pub created_at : NaiveDateTime ,
28+ /// Datetime the session was last used.
1829 pub last_used_at : NaiveDateTime ,
30+ /// Whether the session is revoked.
1931 pub revoked : bool ,
32+ /// Last IP address that used the session.
2033 pub last_ip_address : IpNetwork ,
34+ /// Last user agent that used the session.
2135 pub last_user_agent : String ,
2236}
2337
38+ /// Session-related errors.
2439#[ derive( Error , Debug , PartialEq ) ]
2540pub enum SessionError {
2641 #[ error( "token prefix doesn't match session tokens" ) ]
@@ -30,6 +45,7 @@ pub enum SessionError {
3045}
3146
3247impl PersistentSession {
48+ /// Creates a `NewPersistentSession` that can be inserted into the database.
3349 pub fn create < ' a , ' b > (
3450 user_id : i32 ,
3551 token : & ' a SecureToken ,
@@ -44,6 +60,13 @@ impl PersistentSession {
4460 }
4561 }
4662
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.
4770 pub fn find_from_token_and_update (
4871 conn : & PgConnection ,
4972 token : & str ,
@@ -56,6 +79,8 @@ impl PersistentSession {
5679 . filter ( persistent_sessions:: revoked. eq ( false ) )
5780 . filter ( persistent_sessions:: hashed_token. eq ( hashed_token) ) ;
5881
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?
5984 conn. transaction ( || {
6085 diesel:: update ( sessions. clone ( ) )
6186 . set ( (
@@ -70,6 +95,12 @@ impl PersistentSession {
7095 . map_err ( SessionError :: DieselError )
7196 }
7297
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.
73104 pub fn revoke_from_token ( conn : & PgConnection , token : & str ) -> Result < usize , SessionError > {
74105 let hashed_token = SecureToken :: parse ( SecureTokenKind :: Session , token)
75106 . ok_or ( SessionError :: InvalidSessionToken ) ?;
@@ -84,6 +115,7 @@ impl PersistentSession {
84115 }
85116}
86117
118+ /// A new, insertable persistent session.
87119#[ derive( Clone , Debug , PartialEq , Eq , Insertable ) ]
88120#[ table_name = "persistent_sessions" ]
89121pub struct NewPersistentSession < ' a , ' b > {
@@ -94,6 +126,7 @@ pub struct NewPersistentSession<'a, 'b> {
94126}
95127
96128impl NewPersistentSession < ' _ , ' _ > {
129+ /// Inserts the session into the database.
97130 pub fn insert ( self , conn : & PgConnection ) -> Result < PersistentSession , diesel:: result:: Error > {
98131 diesel:: insert_into ( persistent_sessions:: table)
99132 . values ( self )
0 commit comments