1
+ use std:: collections:: HashSet ;
2
+
1
3
use crate :: controllers;
2
4
use crate :: controllers:: util:: RequestPartsExt ;
5
+ use crate :: middleware:: app:: RequestApp ;
3
6
use crate :: middleware:: log_request:: RequestLogExt ;
4
7
use crate :: middleware:: session:: RequestSession ;
5
8
use crate :: models:: token:: { CrateScope , EndpointScope } ;
@@ -16,6 +19,7 @@ pub struct AuthCheck {
16
19
allow_token : bool ,
17
20
endpoint_scope : Option < EndpointScope > ,
18
21
crate_name : Option < String > ,
22
+ require_admin : bool ,
19
23
}
20
24
21
25
impl AuthCheck {
@@ -27,6 +31,7 @@ impl AuthCheck {
27
31
allow_token : true ,
28
32
endpoint_scope : None ,
29
33
crate_name : None ,
34
+ require_admin : false ,
30
35
}
31
36
}
32
37
@@ -36,6 +41,7 @@ impl AuthCheck {
36
41
allow_token : false ,
37
42
endpoint_scope : None ,
38
43
crate_name : None ,
44
+ require_admin : false ,
39
45
}
40
46
}
41
47
@@ -44,6 +50,7 @@ impl AuthCheck {
44
50
allow_token : self . allow_token ,
45
51
endpoint_scope : Some ( endpoint_scope) ,
46
52
crate_name : self . crate_name . clone ( ) ,
53
+ require_admin : self . require_admin ,
47
54
}
48
55
}
49
56
@@ -52,6 +59,16 @@ impl AuthCheck {
52
59
allow_token : self . allow_token ,
53
60
endpoint_scope : self . endpoint_scope ,
54
61
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 ,
55
72
}
56
73
}
57
74
@@ -61,8 +78,17 @@ impl AuthCheck {
61
78
request : & T ,
62
79
conn : & mut PgConnection ,
63
80
) -> AppResult < Authentication > {
64
- let auth = authenticate ( request, conn) ?;
81
+ self . check_authentication (
82
+ authenticate ( request, conn) ?,
83
+ & request. app ( ) . config . gh_admin_user_ids ,
84
+ )
85
+ }
65
86
87
+ fn check_authentication (
88
+ & self ,
89
+ auth : Authentication ,
90
+ gh_admin_user_ids : & HashSet < i32 > ,
91
+ ) -> AppResult < Authentication > {
66
92
if let Some ( token) = auth. api_token ( ) {
67
93
if !self . allow_token {
68
94
let error_message =
@@ -81,6 +107,11 @@ impl AuthCheck {
81
107
}
82
108
}
83
109
110
+ if self . require_admin && !gh_admin_user_ids. contains ( & auth. user ( ) . gh_id ) {
111
+ let error_message = "User is unauthorized" ;
112
+ return Err ( internal ( error_message) . chain ( forbidden ( ) ) ) ;
113
+ }
114
+
84
115
Ok ( auth)
85
116
}
86
117
@@ -347,4 +378,43 @@ mod tests {
347
378
assert ! ( !auth_check. crate_scope_matches( Some ( & vec![ cs( "anyhow" ) ] ) ) ) ;
348
379
assert ! ( !auth_check. crate_scope_matches( Some ( & vec![ cs( "actix-*" ) ] ) ) ) ;
349
380
}
381
+
382
+ #[ test]
383
+ fn require_admin ( ) {
384
+ let auth_check = AuthCheck :: default ( ) . require_admin ( ) ;
385
+ let gh_admin_user_ids = [ 42 , 43 ] . into_iter ( ) . collect ( ) ;
386
+
387
+ assert_ok ! ( auth_check. check_authentication( mock_cookie( 42 ) , & gh_admin_user_ids) ) ;
388
+ assert_err ! ( auth_check. check_authentication( mock_cookie( 44 ) , & gh_admin_user_ids) ) ;
389
+ assert_ok ! ( auth_check. check_authentication( mock_token( 43 ) , & gh_admin_user_ids) ) ;
390
+ assert_err ! ( auth_check. check_authentication( mock_token( 45 ) , & gh_admin_user_ids) ) ;
391
+ }
392
+
393
+ fn mock_user ( gh_id : i32 ) -> User {
394
+ User {
395
+ id : 3 ,
396
+ gh_access_token : "arbitrary" . into ( ) ,
397
+ gh_login : "literally_anything" . into ( ) ,
398
+ name : None ,
399
+ gh_avatar : None ,
400
+ gh_id,
401
+ account_lock_reason : None ,
402
+ account_lock_until : None ,
403
+ }
404
+ }
405
+
406
+ fn mock_cookie ( gh_id : i32 ) -> Authentication {
407
+ Authentication :: Cookie ( CookieAuthentication {
408
+ user : mock_user ( gh_id) ,
409
+ } )
410
+ }
411
+
412
+ fn mock_token ( gh_id : i32 ) -> Authentication {
413
+ Authentication :: Token ( TokenAuthentication {
414
+ token : crate :: models:: token:: tests:: build_mock ( )
415
+ . user_id ( gh_id)
416
+ . token ( ) ,
417
+ user : mock_user ( gh_id) ,
418
+ } )
419
+ }
350
420
}
0 commit comments