From ebbdd35a95a2612258612308a88d2096969db41a Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 1 Dec 2022 15:20:57 +0100 Subject: [PATCH 1/3] crate::owners: Use token scope restrictions --- src/controllers/krate/owners.rs | 8 +++++++- src/tests/owners.rs | 20 ++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index d7e4c737a35..4a9d9b33967 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -2,6 +2,7 @@ use crate::auth::AuthCheck; use crate::controllers::prelude::*; +use crate::models::token::EndpointScope; use crate::models::{Crate, Owner, Rights, Team, User}; use crate::views::EncodableOwner; @@ -80,7 +81,12 @@ fn parse_owners_request(req: &mut dyn RequestExt) -> AppResult> { } fn modify_owners(req: &mut dyn RequestExt, add: bool) -> EndpointResult { - let auth = AuthCheck::default().check(req)?; + let crate_name = &req.params()["crate_id"]; + + let auth = AuthCheck::default() + .with_endpoint_scope(EndpointScope::ChangeOwners) + .for_crate(crate_name) + .check(req)?; let logins = parse_owners_request(req)?; let app = req.app(); diff --git a/src/tests/owners.rs b/src/tests/owners.rs index 13b709d5463..a4279c32a70 100644 --- a/src/tests/owners.rs +++ b/src/tests/owners.rs @@ -321,17 +321,11 @@ fn owner_change_via_change_owner_token() { let body = json!({ "owners": [user2.gh_login] }); let body = serde_json::to_vec(&body).unwrap(); let response = token.put::<()>(&url, &body); - assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response.status(), StatusCode::OK); assert_eq!( response.into_json(), - json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + json!({ "ok": true, "msg": "user user-2 has been invited to be an owner of crate foo_crate" }) ); - // TODO swap these assertions once token scopes are activated for this endpoint - // assert_eq!(response.status(), StatusCode::OK); - // assert_eq!( - // response.into_json(), - // json!({ "ok": true, "msg": "user user-2 has been invited to be an owner of crate foo_crate" }) - // ); } #[test] @@ -350,17 +344,11 @@ fn owner_change_via_change_owner_token_with_matching_crate_scope() { let body = json!({ "owners": [user2.gh_login] }); let body = serde_json::to_vec(&body).unwrap(); let response = token.put::<()>(&url, &body); - assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response.status(), StatusCode::OK); assert_eq!( response.into_json(), - json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + json!({ "ok": true, "msg": "user user-2 has been invited to be an owner of crate foo_crate" }) ); - // TODO swap these assertions once token scopes are activated for this endpoint - // assert_eq!(response.status(), StatusCode::OK); - // assert_eq!( - // response.into_json(), - // json!({ "ok": true, "msg": "user user-2 has been invited to be an owner of crate foo_crate" }) - // ); } #[test] From 8fe83cc870310bc274222b1143a700df4305718d Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 1 Dec 2022 17:01:24 +0100 Subject: [PATCH 2/3] version::yank: Use token scope restrictions --- src/controllers/version/yank.rs | 8 +- ...h_token_user_with_correct_crate_scope.json | 122 +++++++++++++++++ ...oken_user_with_correct_endpoint_scope.json | 122 +++++++++++++++++ ...ser_with_correct_wildcard_crate_scope.json | 122 +++++++++++++++++ ...token_user_with_incorrect_crate_scope.json | 62 +++++++++ ...en_user_with_incorrect_endpoint_scope.json | 62 +++++++++ ...r_with_incorrect_wildcard_crate_scope.json | 62 +++++++++ .../routes/crates/versions/yank_unyank.rs | 125 ++++++++++++++++++ 8 files changed, 684 insertions(+), 1 deletion(-) create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_crate_scope.json create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_endpoint_scope.json create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_wildcard_crate_scope.json create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_crate_scope.json create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_endpoint_scope.json create mode 100644 src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_wildcard_crate_scope.json diff --git a/src/controllers/version/yank.rs b/src/controllers/version/yank.rs index 5e7278d01b2..b87c384d345 100644 --- a/src/controllers/version/yank.rs +++ b/src/controllers/version/yank.rs @@ -5,6 +5,7 @@ use swirl::Job; use super::{extract_crate_name_and_semver, version_and_crate}; use crate::controllers::cargo_prelude::*; +use crate::models::token::EndpointScope; use crate::models::Rights; use crate::models::{insert_version_owner_action, VersionAction}; use crate::schema::versions; @@ -32,9 +33,14 @@ pub fn unyank(req: &mut dyn RequestExt) -> EndpointResult { fn modify_yank(req: &mut dyn RequestExt, yanked: bool) -> EndpointResult { // FIXME: Should reject bad requests before authentication, but can't due to // lifetime issues with `req`. - let auth = AuthCheck::default().check(req)?; + let (crate_name, semver) = extract_crate_name_and_semver(req)?; + let auth = AuthCheck::default() + .with_endpoint_scope(EndpointScope::Yank) + .for_crate(crate_name) + .check(req)?; + let conn = req.db_write()?; let (version, krate) = version_and_crate(&conn, crate_name, semver)?; let api_token_id = auth.api_token_id(); diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_crate_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_crate_scope.json new file mode 100644 index 00000000000..95ea6e6e88e --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_crate_scope.json @@ -0,0 +1,122 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "156" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjp0cnVlLCJsaW5rcyI6bnVsbH0K" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_endpoint_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_endpoint_scope.json new file mode 100644 index 00000000000..95ea6e6e88e --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_endpoint_scope.json @@ -0,0 +1,122 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "156" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjp0cnVlLCJsaW5rcyI6bnVsbH0K" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_wildcard_crate_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_wildcard_crate_scope.json new file mode 100644 index 00000000000..95ea6e6e88e --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_correct_wildcard_crate_scope.json @@ -0,0 +1,122 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "156" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjp0cnVlLCJsaW5rcyI6bnVsbH0K" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_crate_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_crate_scope.json new file mode 100644 index 00000000000..8c9445c2f5a --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_crate_scope.json @@ -0,0 +1,62 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_endpoint_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_endpoint_scope.json new file mode 100644 index 00000000000..8c9445c2f5a --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_endpoint_scope.json @@ -0,0 +1,62 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_wildcard_crate_scope.json b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_wildcard_crate_scope.json new file mode 100644 index 00000000000..8c9445c2f5a --- /dev/null +++ b/src/tests/http-data/routes_crates_versions_yank_unyank_auth_token_user_with_incorrect_wildcard_crate_scope.json @@ -0,0 +1,62 @@ +[ + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/crates/fyk/fyk-1.0.0.crate", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "35" + ], + [ + "content-type", + "application/gzip" + ] + ], + "body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA=" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + }, + { + "request": { + "uri": "http://alexcrichton-test.s3.amazonaws.com/3/f/fyk", + "method": "PUT", + "headers": [ + [ + "accept", + "*/*" + ], + [ + "accept-encoding", + "gzip" + ], + [ + "content-length", + "157" + ], + [ + "content-type", + "text/plain" + ] + ], + "body": "eyJuYW1lIjoiZnlrIiwidmVycyI6IjEuMC4wIiwiZGVwcyI6W10sImNrc3VtIjoiYWNiNTYwNGIxMjZhYzg5NGMxZWIxMWM0NTc1YmYyMDcyZmVhNjEyMzJhODg4ZTQ1Mzc3MGM3OWQ3ZWQ1NjQxOSIsImZlYXR1cmVzIjp7fSwieWFua2VkIjpmYWxzZSwibGlua3MiOm51bGx9Cg==" + }, + "response": { + "status": 200, + "headers": [], + "body": "" + } + } +] diff --git a/src/tests/routes/crates/versions/yank_unyank.rs b/src/tests/routes/crates/versions/yank_unyank.rs index bd5e4282a5d..ea1813911a0 100644 --- a/src/tests/routes/crates/versions/yank_unyank.rs +++ b/src/tests/routes/crates/versions/yank_unyank.rs @@ -95,6 +95,7 @@ fn unyank_records_an_audit_action() { mod auth { use super::*; use crate::util::{MockAnonymousUser, MockCookieUser}; + use cargo_registry::models::token::{CrateScope, EndpointScope}; const CRATE_NAME: &str = "fyk"; const CRATE_VERSION: &str = "1.0.0"; @@ -153,4 +154,128 @@ mod auth { assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.into_json(), json!({ "ok": true })); } + + #[test] + fn token_user_with_correct_endpoint_scope() { + let (_, _, client) = prepare(); + let client = + client.db_new_scoped_token("test-token", None, Some(vec![EndpointScope::Yank])); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + } + + #[test] + fn token_user_with_incorrect_endpoint_scope() { + let (_, _, client) = prepare(); + let client = client.db_new_scoped_token( + "test-token", + None, + Some(vec![EndpointScope::PublishUpdate]), + ); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + } + + #[test] + fn token_user_with_correct_crate_scope() { + let (_, _, client) = prepare(); + let client = client.db_new_scoped_token( + "test-token", + Some(vec![CrateScope::try_from(CRATE_NAME).unwrap()]), + None, + ); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + } + + #[test] + fn token_user_with_correct_wildcard_crate_scope() { + let (_, _, client) = prepare(); + let wildcard = format!("{}*", CRATE_NAME.chars().next().unwrap()); + let client = client.db_new_scoped_token( + "test-token", + Some(vec![CrateScope::try_from(wildcard).unwrap()]), + None, + ); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.into_json(), json!({ "ok": true })); + } + + #[test] + fn token_user_with_incorrect_crate_scope() { + let (_, _, client) = prepare(); + let client = client.db_new_scoped_token( + "test-token", + Some(vec![CrateScope::try_from("foo").unwrap()]), + None, + ); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + } + + #[test] + fn token_user_with_incorrect_wildcard_crate_scope() { + let (_, _, client) = prepare(); + let client = client.db_new_scoped_token( + "test-token", + Some(vec![CrateScope::try_from("foo*").unwrap()]), + None, + ); + + let response = client.yank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + + let response = client.unyank(CRATE_NAME, CRATE_VERSION); + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.into_json(), + json!({ "errors": [{ "detail": "must be logged in to perform that action" }] }) + ); + } } From 339987523b8972382508fefc548a92682190fd91 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 1 Dec 2022 17:11:15 +0100 Subject: [PATCH 3/3] crate::publish: Use token scope restrictions --- src/controllers/krate/publish.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index 570f5058ba1..cc18cd8ee98 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -18,6 +18,7 @@ use crate::models::{ use crate::worker; use crate::middleware::log_request::add_custom_metadata; +use crate::models::token::EndpointScope; use crate::schema::*; use crate::util::errors::{cargo_err, AppResult}; use crate::util::{read_fill, read_le_u32, CargoVcsInfo, LimitErrorReader, Maximums}; @@ -65,7 +66,24 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult { add_custom_metadata("crate_version", new_crate.vers.to_string()); let conn = app.primary_database.get()?; - let auth = AuthCheck::default().check(req)?; + + // this query should only be used for the endpoint scope calculation + // since a race condition there would only cause `publish-new` instead of + // `publish-update` to be used. + let existing_crate = Crate::by_name(&new_crate.name) + .first::(&*conn) + .optional()?; + + let endpoint_scope = match existing_crate { + Some(_) => EndpointScope::PublishUpdate, + None => EndpointScope::PublishNew, + }; + + let auth = AuthCheck::default() + .with_endpoint_scope(endpoint_scope) + .for_crate(&new_crate.name) + .check(req)?; + let api_token_id = auth.api_token_id(); let user = auth.user();