Skip to content

Commit 877755b

Browse files
LawnGnomeTurbo87
authored andcommitted
auth: Add AuthCheck::require_admin() fn
This function can be used to require that the current cookie or token belong to an admin user.
1 parent 4253d00 commit 877755b

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

src/auth.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use std::collections::HashSet;
2+
13
use crate::controllers;
24
use crate::controllers::util::RequestPartsExt;
5+
use crate::middleware::app::RequestApp;
36
use crate::middleware::log_request::RequestLogExt;
47
use crate::middleware::session::RequestSession;
58
use crate::models::token::{CrateScope, EndpointScope};
@@ -16,6 +19,7 @@ pub struct AuthCheck {
1619
allow_token: bool,
1720
endpoint_scope: Option<EndpointScope>,
1821
crate_name: Option<String>,
22+
require_admin: bool,
1923
}
2024

2125
impl AuthCheck {
@@ -27,6 +31,7 @@ impl AuthCheck {
2731
allow_token: true,
2832
endpoint_scope: None,
2933
crate_name: None,
34+
require_admin: false,
3035
}
3136
}
3237

@@ -36,6 +41,7 @@ impl AuthCheck {
3641
allow_token: false,
3742
endpoint_scope: None,
3843
crate_name: None,
44+
require_admin: false,
3945
}
4046
}
4147

@@ -44,6 +50,7 @@ impl AuthCheck {
4450
allow_token: self.allow_token,
4551
endpoint_scope: Some(endpoint_scope),
4652
crate_name: self.crate_name.clone(),
53+
require_admin: self.require_admin,
4754
}
4855
}
4956

@@ -52,6 +59,16 @@ impl AuthCheck {
5259
allow_token: self.allow_token,
5360
endpoint_scope: self.endpoint_scope,
5461
crate_name: Some(crate_name.to_string()),
62+
require_admin: self.require_admin,
63+
}
64+
}
65+
66+
pub fn require_admin(&self) -> Self {
67+
Self {
68+
allow_token: self.allow_token,
69+
endpoint_scope: self.endpoint_scope,
70+
crate_name: self.crate_name.clone(),
71+
require_admin: true,
5572
}
5673
}
5774

@@ -62,10 +79,14 @@ impl AuthCheck {
6279
conn: &mut PgConnection,
6380
) -> AppResult<Authentication> {
6481
let auth = authenticate(request, conn)?;
65-
self.check_authentication(auth)
82+
self.check_authentication(auth, &request.app().config.admin_user_github_ids)
6683
}
6784

68-
pub fn check_authentication(&self, auth: Authentication) -> AppResult<Authentication> {
85+
fn check_authentication(
86+
&self,
87+
auth: Authentication,
88+
gh_admin_user_ids: &HashSet<i32>,
89+
) -> AppResult<Authentication> {
6990
if let Some(token) = auth.api_token() {
7091
if !self.allow_token {
7192
let error_message =
@@ -84,6 +105,11 @@ impl AuthCheck {
84105
}
85106
}
86107

108+
if self.require_admin && !gh_admin_user_ids.contains(&auth.user().gh_id) {
109+
let error_message = "User is unauthorized";
110+
return Err(internal(error_message).chain(forbidden()));
111+
}
112+
87113
Ok(auth)
88114
}
89115

@@ -350,4 +376,41 @@ mod tests {
350376
assert!(!auth_check.crate_scope_matches(Some(&vec![cs("anyhow")])));
351377
assert!(!auth_check.crate_scope_matches(Some(&vec![cs("actix-*")])));
352378
}
379+
380+
#[test]
381+
fn require_admin() {
382+
let auth_check = AuthCheck::default().require_admin();
383+
let gh_admin_user_ids = [42, 43].into_iter().collect();
384+
385+
assert_ok!(auth_check.check_authentication(mock_cookie(42), &gh_admin_user_ids));
386+
assert_err!(auth_check.check_authentication(mock_cookie(44), &gh_admin_user_ids));
387+
assert_ok!(auth_check.check_authentication(mock_token(43), &gh_admin_user_ids));
388+
assert_err!(auth_check.check_authentication(mock_token(45), &gh_admin_user_ids));
389+
}
390+
391+
fn mock_user(gh_id: i32) -> User {
392+
User {
393+
id: 3,
394+
gh_access_token: "arbitrary".into(),
395+
gh_login: "literally_anything".into(),
396+
name: None,
397+
gh_avatar: None,
398+
gh_id,
399+
account_lock_reason: None,
400+
account_lock_until: None,
401+
}
402+
}
403+
404+
fn mock_cookie(gh_id: i32) -> Authentication {
405+
Authentication::Cookie(CookieAuthentication {
406+
user: mock_user(gh_id),
407+
})
408+
}
409+
410+
fn mock_token(gh_id: i32) -> Authentication {
411+
Authentication::Token(TokenAuthentication {
412+
token: ApiToken::mock_builder().user_id(gh_id).build().unwrap(),
413+
user: mock_user(gh_id),
414+
})
415+
}
353416
}

0 commit comments

Comments
 (0)