@@ -4,6 +4,7 @@ use crate::bors::make_text_ignored_by_bors;
44use crate :: github:: api:: client:: GithubRepositoryClient ;
55use crate :: github:: api:: operations:: { ForcePush , MergeError } ;
66use crate :: github:: oauth:: { OAuthClient , UserGitHubClient } ;
7+ use crate :: permissions:: PermissionType ;
78use crate :: server:: ServerStateRef ;
89use anyhow:: Context ;
910use axum:: extract:: { Query , State } ;
@@ -47,6 +48,7 @@ pub enum RollupError {
4748 PullRequestNotFound { pr : PullRequestNumber } ,
4849 NoPullRequestsSelected ,
4950 TooManyPullRequests ,
51+ NotAuthenticated ,
5052}
5153
5254impl IntoResponse for RollupError {
@@ -77,6 +79,11 @@ impl IntoResponse for RollupError {
7779 StatusCode :: BAD_REQUEST ,
7880 format ! ( "Rolling up too many pull requests, at most {ROLLUP_PR_LIMIT} is allowed" ) ,
7981 ) ,
82+ RollupError :: NotAuthenticated => (
83+ StatusCode :: FORBIDDEN ,
84+ "You are not allowed to create rollups. Review permissions are required."
85+ . to_string ( ) ,
86+ ) ,
8087 }
8188 . into_response ( )
8289 }
@@ -116,6 +123,16 @@ pub async fn oauth_callback_handler(
116123 . get_authenticated_client ( repo_name. clone ( ) , & callback. code )
117124 . await ?;
118125
126+ // The rollup author is expected to r+ the rollup, so they must have review permissions to
127+ // create it in the first place.
128+ if !repo_state
129+ . permissions
130+ . load ( )
131+ . has_permission ( user_client. user . id , PermissionType :: Review )
132+ {
133+ return Err ( RollupError :: NotAuthenticated ) ;
134+ }
135+
119136 let span = tracing:: info_span!(
120137 "create_rollup" ,
121138 repo = %repo_name,
@@ -165,7 +182,7 @@ async fn create_rollup(
165182 mut pr_nums,
166183 } = rollup_state;
167184
168- let username = user_client. username ;
185+ let username = user_client. user . username ;
169186
170187 tracing:: info!( "User {username} is creating a rollup with PRs: {pr_nums:?}" ) ;
171188
@@ -360,6 +377,7 @@ async fn create_rollup(
360377mod tests {
361378 use crate :: github:: rollup:: OAuthRollupState ;
362379 use crate :: github:: { GithubRepoName , PullRequestNumber } ;
380+ use crate :: permissions:: PermissionType ;
363381 use crate :: tests:: {
364382 ApiRequest , ApiResponse , BorsTester , Comment , GitHub , MergeBehavior , PullRequest , Repo ,
365383 User , default_repo_name, run_test,
@@ -368,7 +386,15 @@ mod tests {
368386
369387 #[ sqlx:: test]
370388 async fn rollup_missing_fork ( pool : sqlx:: PgPool ) {
371- run_test ( pool, async |ctx : & mut BorsTester | {
389+ let mut gh = GitHub :: default ( ) ;
390+ gh. add_user ( rollup_user ( ) ) ;
391+ gh. get_repo ( ( ) )
392+ . lock ( )
393+ . permissions
394+ . users
395+ . insert ( rollup_user ( ) , vec ! [ PermissionType :: Review ] ) ;
396+
397+ run_test ( ( pool, gh) , async |ctx : & mut BorsTester | {
372398 let pr1 = ctx. open_pr ( ( ) , |_| { } ) . await ?;
373399 let pr2 = ctx. open_pr ( ( ) , |_| { } ) . await ?;
374400
@@ -435,6 +461,24 @@ mod tests {
435461 . await ;
436462 }
437463
464+ #[ sqlx:: test]
465+ async fn rollup_missing_review_permissions ( pool : sqlx:: PgPool ) {
466+ let mut gh = GitHub :: default ( ) ;
467+ gh. add_user ( rollup_user ( ) ) ;
468+ run_test ( ( pool, gh) , async |ctx : & mut BorsTester | {
469+ let pr1 = ctx. open_pr ( ( ) , |_| { } ) . await ?;
470+
471+ make_rollup ( ctx, & [ & pr1] )
472+ . await ?
473+ . assert_status ( StatusCode :: FORBIDDEN )
474+ . assert_body (
475+ "You are not allowed to create rollups. Review permissions are required." ,
476+ ) ;
477+ Ok ( ( ) )
478+ } )
479+ . await ;
480+ }
481+
438482 #[ sqlx:: test]
439483 async fn rollup_merge_conflict ( pool : sqlx:: PgPool ) {
440484 let gh = run_test ( ( pool, rollup_state ( ) ) , async |ctx : & mut BorsTester | {
@@ -640,6 +684,12 @@ mod tests {
640684 let mut gh = GitHub :: default ( ) ;
641685 let rolluper = rollup_user ( ) ;
642686 gh. add_user ( rolluper. clone ( ) ) ;
687+ gh. get_repo ( ( ) )
688+ . lock ( )
689+ . permissions
690+ . users
691+ . insert ( rolluper. clone ( ) , vec ! [ PermissionType :: Review ] ) ;
692+
643693 // Create fork
644694 let mut repo = Repo :: new ( rolluper, fork_repo ( ) . name ( ) ) ;
645695 repo. fork = true ;
0 commit comments