Skip to content

Commit 62626e6

Browse files
committed
Added session tests.
1 parent 31b548b commit 62626e6

File tree

5 files changed

+88
-8
lines changed

5 files changed

+88
-8
lines changed

src/controllers/user/session.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::Env;
1616

1717
pub const SESSION_COOKIE_NAME: &str = "crates_auth";
1818

19-
fn session_cookie(token: &NewSecureToken, secure: bool) -> Cookie<'static> {
19+
pub fn session_cookie(token: &NewSecureToken, secure: bool) -> Cookie<'static> {
2020
Cookie::build(SESSION_COOKIE_NAME, token.plaintext().to_string())
2121
.http_only(true)
2222
.secure(secure)

src/tests/authentication.rs

+38
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,51 @@ use crate::util::{RequestHelper, Response};
22
use crate::TestApp;
33

44
use crate::util::encode_session_header;
5+
use cargo_registry::controllers::user::session::session_cookie;
6+
use cargo_registry::util::token::SecureToken;
7+
use cargo_registry::util::token::SecureTokenKind;
58
use conduit::{header, Body, Method, StatusCode};
69

710
static URL: &str = "/api/v1/me/updates";
811
static MUST_LOGIN: &[u8] = br#"{"errors":[{"detail":"must be logged in to perform that action"}]}"#;
912
static INTERNAL_ERROR_NO_USER: &str =
1013
"user_id from cookie not found in database caused by NotFound";
1114

15+
#[test]
16+
fn persistent_session_user() {
17+
let (app, _) = TestApp::init().empty();
18+
let user = app.db_new_user("user1").with_session();
19+
let request = user.request_builder(Method::GET, URL);
20+
let response: Response<Body> = user.run(request);
21+
assert_eq!(response.status(), StatusCode::OK);
22+
}
23+
24+
#[test]
25+
fn incorrect_session_is_forbidden() {
26+
let (_, anon) = TestApp::init().empty();
27+
28+
let token = SecureToken::generate(SecureTokenKind::Session);
29+
// Create a cookie that isn't in the database.
30+
let cookie = session_cookie(&token, false).to_string();
31+
let mut request = anon.request_builder(Method::GET, URL);
32+
request.header(header::COOKIE, &cookie);
33+
let response: Response<Body> = anon.run(request);
34+
assert_eq!(response.status(), StatusCode::FORBIDDEN);
35+
assert_eq!(
36+
response.into_json(),
37+
json!({"errors": [{"detail": "must be logged in to perform that action"}]})
38+
);
39+
}
40+
41+
#[test]
42+
fn cookie_user() {
43+
let (_, _, cookie_user) = TestApp::init().with_user();
44+
let request = cookie_user.request_builder(Method::GET, URL);
45+
46+
let response: Response<Body> = cookie_user.run(request);
47+
assert_eq!(response.status(), StatusCode::OK);
48+
}
49+
1250
#[test]
1351
fn anonymous_user_unauthorized() {
1452
let (_, anon) = TestApp::init().empty();

src/tests/util.rs

+42
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ use crate::{
2323
builders::PublishBuilder, CategoryListResponse, CategoryResponse, CrateList, CrateResponse,
2424
GoodCrate, OkBool, OwnersResponse, VersionResponse,
2525
};
26+
use cargo_registry::controllers::user::session::session_cookie;
27+
use cargo_registry::models::PersistentSession;
2628
use cargo_registry::models::{ApiToken, CreatedApiToken, User};
29+
use cargo_registry::util::token::NewSecureToken;
30+
use cargo_registry::util::token::SecureToken;
31+
use cargo_registry::util::token::SecureTokenKind;
2732

2833
use conduit::{BoxError, Handler, Method};
2934
use conduit_cookie::SessionMiddleware;
@@ -270,6 +275,43 @@ impl MockCookieUser {
270275
token,
271276
}
272277
}
278+
279+
pub fn with_session(&self) -> MockSessionUser {
280+
let ip_addr = "192.168.0.42";
281+
let user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36";
282+
283+
let token = SecureToken::generate(SecureTokenKind::Session);
284+
285+
self.app.db(|conn| {
286+
PersistentSession::create(self.user.id, &token, ip_addr.parse().unwrap(), user_agent)
287+
.insert(&conn)
288+
.unwrap()
289+
});
290+
291+
MockSessionUser {
292+
app: self.app.clone(),
293+
token,
294+
}
295+
}
296+
}
297+
298+
pub struct MockSessionUser {
299+
app: TestApp,
300+
token: NewSecureToken,
301+
}
302+
303+
impl RequestHelper for MockSessionUser {
304+
fn request_builder(&self, method: Method, path: &str) -> MockRequest {
305+
let cookie = session_cookie(&self.token, false).to_string();
306+
307+
let mut request = req(method, path);
308+
request.header(header::COOKIE, &cookie);
309+
request
310+
}
311+
312+
fn app(&self) -> &TestApp {
313+
&self.app
314+
}
273315
}
274316

275317
/// A type that can generate token authenticated requests

src/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mod io_util;
1212
mod request_helpers;
1313
mod request_proxy;
1414
pub mod rfc3339;
15-
pub(crate) mod token;
15+
pub mod token;
1616

1717
pub type AppResponse = Response<conduit::Body>;
1818
pub type EndpointResult = Result<AppResponse, Box<dyn errors::AppError>>;

src/util/token.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct SecureToken {
1212
}
1313

1414
impl SecureToken {
15-
pub(crate) fn generate(kind: SecureTokenKind) -> NewSecureToken {
15+
pub fn generate(kind: SecureTokenKind) -> NewSecureToken {
1616
let plaintext = format!(
1717
"{}{}",
1818
kind.prefix(),
@@ -26,7 +26,7 @@ impl SecureToken {
2626
}
2727
}
2828

29-
pub(crate) fn parse(kind: SecureTokenKind, plaintext: &str) -> Option<Self> {
29+
pub fn parse(kind: SecureTokenKind, plaintext: &str) -> Option<Self> {
3030
// This will both reject tokens without a prefix and tokens of the wrong kind.
3131
if SecureTokenKind::from_token(plaintext) != Some(kind) {
3232
return None;
@@ -60,18 +60,18 @@ impl FromSql<Bytea, Pg> for SecureToken {
6060
}
6161
}
6262

63-
pub(crate) struct NewSecureToken {
63+
pub struct NewSecureToken {
6464
plaintext: String,
6565
inner: SecureToken,
6666
}
6767

6868
impl NewSecureToken {
69-
pub(crate) fn plaintext(&self) -> &str {
69+
pub fn plaintext(&self) -> &str {
7070
&self.plaintext
7171
}
7272

7373
#[cfg(test)]
74-
pub(crate) fn into_inner(self) -> SecureToken {
74+
pub fn into_inner(self) -> SecureToken {
7575
self.inner
7676
}
7777
}
@@ -120,7 +120,7 @@ secure_token_kind! {
120120
/// NEVER CHANGE THE PREFIX OF EXISTING TOKEN TYPES!!! Doing so will implicitly revoke all the
121121
/// tokens of that kind, distrupting production users.
122122
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
123-
pub(crate) enum SecureTokenKind {
123+
pub enum SecureTokenKind {
124124
Api => "cio", // Crates.IO
125125
Session => "ses", // Session tokens.
126126
}

0 commit comments

Comments
 (0)