Skip to content

Commit 894460f

Browse files
committed
Add API Token Auth restrictions on GET APIs
1 parent 9fd121d commit 894460f

File tree

9 files changed

+97
-4
lines changed

9 files changed

+97
-4
lines changed

src/controllers/crate_owner_invitation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::collections::HashMap;
99
/// Handles the `GET /me/crate_owner_invitations` route.
1010
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
1111
// Ensure that the user is authenticated
12-
let user = req.authenticate()?.user();
12+
let user = req.authenticate()?.forbid_api_token_auth()?.user();
1313

1414
// Load all pending invitations for the user
1515
let conn = &*req.db_read_only()?;

src/controllers/krate/follow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn unfollow(req: &mut dyn RequestExt) -> EndpointResult {
4646
pub fn following(req: &mut dyn RequestExt) -> EndpointResult {
4747
use diesel::dsl::exists;
4848

49-
let user_id = req.authenticate()?.user_id();
49+
let user_id = req.authenticate()?.forbid_api_token_auth()?.user_id();
5050
let conn = req.db_read_only()?;
5151
let follow = follow_target(req, &conn, user_id)?;
5252
let following = diesel::select(exists(follows::table.find(follow.id()))).get_result(&*conn)?;

src/controllers/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use serde_json as json;
99

1010
/// Handles the `GET /me/tokens` route.
1111
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
12-
let authenticated_user = req.authenticate()?;
12+
let authenticated_user = req.authenticate()?.forbid_api_token_auth()?;
1313
let conn = req.db_conn()?;
1414
let user = authenticated_user.user();
1515

src/controllers/user/me.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn me(req: &mut dyn RequestExt) -> EndpointResult {
5454
pub fn updates(req: &mut dyn RequestExt) -> EndpointResult {
5555
use diesel::dsl::any;
5656

57-
let authenticated_user = req.authenticate()?;
57+
let authenticated_user = req.authenticate()?.forbid_api_token_auth()?;
5858
let user = authenticated_user.user();
5959

6060
let followed_crates = Follow::belonging_to(&user).select(follows::crate_id);

src/controllers/util.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ impl AuthenticatedUser {
2828
pub fn user(self) -> User {
2929
self.user
3030
}
31+
32+
/// Disallows token authenticated users
33+
pub fn forbid_api_token_auth(self) -> AppResult<Self> {
34+
if self.token_id.is_none() {
35+
Ok(self)
36+
} else {
37+
Err(
38+
internal("API Token authentication was explicitly disallowed for this API")
39+
.chain(forbidden()),
40+
)
41+
}
42+
}
3143
}
3244

3345
/// The Origin header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin)

src/tests/krate/following.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,63 @@ fn following() {
5757
assert!(!is_following());
5858
assert_eq!(user.search("following=1").crates.len(), 0);
5959
}
60+
61+
#[test]
62+
fn disallow_api_token_auth_for_get_crate_following_status() {
63+
let (app, _, _, token) = TestApp::init().with_token();
64+
let api_token = token.as_model();
65+
66+
let a_crate = "a_crate";
67+
68+
app.db(|conn| {
69+
CrateBuilder::new(a_crate, api_token.user_id).expect_build(conn);
70+
});
71+
72+
// Token auth on GET for get following status is disallowed
73+
token
74+
.get(&format!("/api/v1/crates/{}/following", a_crate))
75+
.assert_forbidden();
76+
}
77+
78+
#[test]
79+
fn getting_followed_crates_allows_api_token_auth() {
80+
let (app, _, user, token) = TestApp::init().with_token();
81+
let api_token = token.as_model();
82+
83+
let crate_to_follow = "some_crate_to_follow";
84+
let crate_not_followed = "another_crate";
85+
86+
app.db(|conn| {
87+
CrateBuilder::new(crate_to_follow, api_token.user_id).expect_build(conn);
88+
CrateBuilder::new(crate_not_followed, api_token.user_id).expect_build(conn);
89+
});
90+
91+
let is_following = |crate_name: &str| -> bool {
92+
#[derive(Deserialize)]
93+
struct F {
94+
following: bool,
95+
}
96+
97+
// Token auth on GET for get following status is disallowed
98+
user.get::<F>(&format!("/api/v1/crates/{}/following", crate_name))
99+
.good()
100+
.following
101+
};
102+
103+
let follow = |crate_name: &str| {
104+
assert!(
105+
token
106+
.put::<OkBool>(&format!("/api/v1/crates/{}/follow", crate_name), b"")
107+
.good()
108+
.ok
109+
);
110+
};
111+
112+
follow(crate_to_follow);
113+
114+
assert!(is_following(crate_to_follow));
115+
assert!(!is_following(crate_not_followed));
116+
117+
let json = token.search("following=1");
118+
assert_eq!(json.crates.len(), 1);
119+
}

src/tests/owners.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,15 @@ fn invitations_are_empty_by_default() {
320320
assert_eq!(json.crate_owner_invitations.len(), 0);
321321
}
322322

323+
#[test]
324+
fn api_token_cannot_list_invitations() {
325+
let (_, _, _, token) = TestApp::init().with_token();
326+
327+
token
328+
.get("/api/v1/me/crate_owner_invitations")
329+
.assert_forbidden();
330+
}
331+
323332
#[test]
324333
fn invitations_list() {
325334
let (app, _, owner, token) = TestApp::init().with_token();

src/tests/token.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ fn list_logged_out() {
3636
anon.get(URL).assert_forbidden();
3737
}
3838

39+
#[test]
40+
fn list_with_api_token_is_forbidden() {
41+
let (_, _, _, token) = TestApp::init().with_token();
42+
token.get(URL).assert_forbidden();
43+
}
44+
3945
#[test]
4046
fn list_empty() {
4147
let (_, _, user) = TestApp::init().with_user();

src/tests/user.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ fn crates_by_user_id_not_including_deleted_owners() {
206206
assert_eq!(response.crates.len(), 0);
207207
}
208208

209+
#[test]
210+
fn api_token_cannot_get_user_updates() {
211+
let (_, _, _, token) = TestApp::init().with_token();
212+
token.get("/api/v1/me/updates").assert_forbidden();
213+
}
214+
209215
#[test]
210216
fn following() {
211217
use cargo_registry::schema::versions;

0 commit comments

Comments
 (0)