Skip to content

Commit ebd3851

Browse files
committed
Holding on to the authorisation token.
1 parent eb39de6 commit ebd3851

File tree

3 files changed

+47
-34
lines changed

3 files changed

+47
-34
lines changed

src/middleware/current_user.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ use diesel::prelude::*;
66
use crate::db::RequestTransaction;
77
use crate::util::errors::{std_error, CargoResult, ChainError, Unauthorized};
88

9+
use crate::models::ApiToken;
910
use crate::models::User;
1011
use crate::schema::users;
1112

1213
#[derive(Debug, Clone, Copy)]
1314
pub struct CurrentUser;
1415

15-
#[derive(Debug, Clone, Eq, PartialEq)]
16+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1617
pub enum AuthenticationSource {
1718
SessionCookie,
18-
ApiToken { auth_header: String },
19+
ApiToken { api_token_id: i32 },
1920
}
2021

2122
impl Middleware for CurrentUser {
@@ -42,19 +43,23 @@ impl Middleware for CurrentUser {
4243
} else {
4344
// Otherwise, look for an `Authorization` header on the request
4445
// and try to find a user in the database with a matching API token
45-
let user_auth = req.headers().find("Authorization").and_then(|headers| {
46-
let auth_header = headers[0].to_string();
47-
48-
User::find_by_api_token(&conn, &auth_header)
49-
.ok()
50-
.map(|user| (AuthenticationSource::ApiToken { auth_header }, user))
51-
});
46+
let user_and_token_id = if let Some(headers) = req.headers().find("Authorization") {
47+
ApiToken::find_by_api_token_and_revoked(&conn, headers[0], false)
48+
.and_then(|api_token| {
49+
User::find(&conn, api_token.user_id).map(|user| (user, api_token.id))
50+
})
51+
.optional()
52+
.map_err(|e| Box::new(e) as Box<dyn Error + Send>)?
53+
} else {
54+
None
55+
};
5256
drop(conn);
5357

54-
if let Some((api_token, user)) = user_auth {
58+
if let Some((user, api_token_id)) = user_and_token_id {
5559
// Attach the `User` model from the database to the request
5660
req.mut_extensions().insert(user);
57-
req.mut_extensions().insert(api_token);
61+
req.mut_extensions()
62+
.insert(AuthenticationSource::ApiToken { api_token_id });
5863
}
5964
}
6065

src/models/token.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,28 @@ impl ApiToken {
4646
last_used_at: self.last_used_at,
4747
}
4848
}
49+
50+
pub fn find_by_api_token_and_revoked(
51+
conn: &PgConnection,
52+
token_: &str,
53+
revoked_: bool,
54+
) -> QueryResult<ApiToken> {
55+
use crate::schema::api_tokens::dsl::{api_tokens, last_used_at, revoked, token};
56+
use diesel::{dsl::now, update};
57+
58+
let tokens = api_tokens
59+
.filter(token.eq(token_))
60+
.filter(revoked.eq(revoked_));
61+
62+
// If the database is in read only mode, we can't update last_used_at.
63+
// Try updating in a new transaction, if that fails, fall back to reading
64+
conn.transaction(|| {
65+
update(tokens)
66+
.set(last_used_at.eq(now.nullable()))
67+
.get_result::<ApiToken>(conn)
68+
})
69+
.or_else(|_| tokens.first(conn))
70+
}
4971
}
5072

5173
#[cfg(test)]

src/models/user.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use diesel::dsl::now;
21
use diesel::prelude::*;
32
use std::borrow::Cow;
43

54
use crate::app::App;
65
use crate::util::CargoResult;
76

8-
use crate::models::{Crate, CrateOwner, Email, NewEmail, Owner, OwnerKind, Rights};
7+
use crate::models::{ApiToken, Crate, CrateOwner, Email, NewEmail, Owner, OwnerKind, Rights};
98
use crate::schema::{crate_owners, emails, users};
109
use crate::views::{EncodablePrivateUser, EncodablePublicUser};
1110

@@ -105,27 +104,14 @@ impl<'a> NewUser<'a> {
105104
}
106105

107106
impl User {
108-
/// Queries the database for a user with a certain `api_token` value.
109-
pub fn find_by_api_token(conn: &PgConnection, token_: &str) -> QueryResult<User> {
110-
use crate::schema::api_tokens::dsl::{api_tokens, last_used_at, revoked, token, user_id};
111-
use diesel::update;
112-
113-
let tokens = api_tokens
114-
.filter(token.eq(token_))
115-
.filter(revoked.eq(false));
116-
117-
// If the database is in read only mode, we can't update last_used_at.
118-
// Try updating in a new transaction, if that fails, fall back to reading
119-
let user_id_ = conn
120-
.transaction(|| {
121-
update(tokens)
122-
.set(last_used_at.eq(now.nullable()))
123-
.returning(user_id)
124-
.get_result::<i32>(conn)
125-
})
126-
.or_else(|_| tokens.select(user_id).first(conn))?;
127-
128-
users::table.find(user_id_).first(conn)
107+
pub fn find(conn: &PgConnection, id: i32) -> QueryResult<User> {
108+
users::table.find(id).first(conn)
109+
}
110+
111+
pub fn find_by_api_token(conn: &PgConnection, token: &str) -> QueryResult<User> {
112+
let api_token = ApiToken::find_by_api_token_and_revoked(conn, token, false)?;
113+
114+
Self::find(conn, api_token.user_id)
129115
}
130116

131117
pub fn owning(krate: &Crate, conn: &PgConnection) -> CargoResult<Vec<Owner>> {

0 commit comments

Comments
 (0)