Skip to content

Commit f22c576

Browse files
committed
Added documentation and comments.
1 parent 62626e6 commit f22c576

File tree

3 files changed

+49
-14
lines changed

3 files changed

+49
-14
lines changed

src/controllers/user/session.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ use crate::util::token::NewSecureToken;
1414
use crate::util::token::SecureToken;
1515
use crate::Env;
1616

17+
/// Name of the cookie used for session-based authentication.
1718
pub const SESSION_COOKIE_NAME: &str = "crates_auth";
1819

20+
/// Creates a session cookie with the given token.
1921
pub fn session_cookie(token: &NewSecureToken, secure: bool) -> Cookie<'static> {
2022
Cookie::build(SESSION_COOKIE_NAME, token.plaintext().to_string())
2123
.http_only(true)
@@ -112,24 +114,24 @@ pub fn authorize(req: &mut dyn RequestExt) -> EndpointResult {
112114
let ghuser = req.app().github.current_user(token)?;
113115
let user = save_user_to_database(&ghuser, token.secret(), &req.app().emails, &*req.db_conn()?)?;
114116

117+
// Setup a persistent session for the newly logged in user.
115118
let user_agent = req
116119
.headers()
117120
.get(header::USER_AGENT)
118121
.and_then(|value| value.to_str().ok())
119122
.map(|value| value.to_string())
120123
.unwrap_or_default();
121124

122-
// Setup a session for the newly logged in user.
123125
let token = SecureToken::generate(crate::util::token::SecureTokenKind::Session);
124-
let session = PersistentSession::create(user.id, &token, req.remote_addr().ip(), &user_agent);
125-
session.insert(&*req.db_conn()?)?;
126+
PersistentSession::create(user.id, &token, req.remote_addr().ip(), &user_agent)
127+
.insert(&*req.db_conn()?)?;
126128

127-
// Setup session cookie.
129+
// Setup persistent session cookie.
128130
let secure = req.app().config.env() == Env::Production;
129131
req.cookies_mut().add(session_cookie(&token, secure));
130132

131-
// Log in by setting a cookie and the middleware authentication
132-
// TODO(adsnaider): Remove.
133+
// TODO(adsnaider): Remove as part of https://github.com/rust-lang/crates.io/issues/2630.
134+
// Log in by setting a cookie and the middleware authentication.
133135
req.session_mut()
134136
.insert("user_id".to_string(), user.id.to_string());
135137

@@ -168,8 +170,10 @@ fn save_user_to_database(
168170

169171
/// Handles the `DELETE /api/private/session` route.
170172
pub fn logout(req: &mut dyn RequestExt) -> EndpointResult {
171-
// TODO(adsnaider): Remove this.
173+
// TODO(adsnaider): Remove as part of https://github.com/rust-lang/crates.io/issues/2630.
172174
req.session_mut().remove(&"user_id".to_string());
175+
176+
// Remove persistent session from database.
173177
if let Some(session_token) = req
174178
.cookies()
175179
.get(SESSION_COOKIE_NAME)
@@ -178,12 +182,8 @@ pub fn logout(req: &mut dyn RequestExt) -> EndpointResult {
178182
req.cookies_mut().remove(Cookie::named(SESSION_COOKIE_NAME));
179183

180184
if let Ok(conn) = req.db_conn() {
181-
match PersistentSession::revoke_from_token(&conn, &session_token) {
182-
Ok(0) => {}
183-
Ok(1) => {}
184-
Ok(_count) => {}
185-
Err(_e) => {}
186-
}
185+
// TODO(adsnaider): Maybe log errors somehow?
186+
let _ = PersistentSession::revoke_from_token(&conn, &session_token);
187187
}
188188
}
189189
Ok(req.json(&true))

src/controllers/util.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ fn verify_origin(req: &dyn RequestExt) -> AppResult<()> {
6868
fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
6969
let conn = req.db_conn()?;
7070

71-
// TODO(adsnaider): Remove this.
71+
// TODO(adsnaider): Remove as part of https://github.com/rust-lang/crates.io/issues/2630.
72+
// Log in with the session id.
7273
let session = req.session();
7374
let user_id_from_session = session.get("user_id").and_then(|s| s.parse::<i32>().ok());
7475

@@ -82,6 +83,7 @@ fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
8283
});
8384
}
8485

86+
// Log in with persistent session token.
8587
if let Some(session_token) = req
8688
.cookies()
8789
.get(SESSION_COOKIE_NAME)

src/models/persistent_session.rs

+33
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,34 @@ use crate::schema::persistent_sessions;
88
use crate::util::token::SecureToken;
99
use 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"]
1319
pub 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)]
2540
pub enum SessionError {
2641
#[error("token prefix doesn't match session tokens")]
@@ -30,6 +45,7 @@ pub enum SessionError {
3045
}
3146

3247
impl 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"]
89121
pub struct NewPersistentSession<'a, 'b> {
@@ -94,6 +126,7 @@ pub struct NewPersistentSession<'a, 'b> {
94126
}
95127

96128
impl 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

Comments
 (0)