@@ -19,14 +19,17 @@ use mas_data_model::{Clock, Device, SystemClock, TokenType, Ulid, UpstreamOAuthP
1919use mas_email:: Address ;
2020use mas_matrix:: HomeserverConnection ;
2121use mas_storage:: {
22- RepositoryAccess ,
22+ Pagination , RepositoryAccess ,
2323 compat:: { CompatAccessTokenRepository , CompatSessionFilter , CompatSessionRepository } ,
2424 oauth2:: OAuth2SessionFilter ,
2525 queue:: {
2626 DeactivateUserJob , ProvisionUserJob , QueueJobRepositoryExt as _, ReactivateUserJob ,
2727 SyncDevicesJob ,
2828 } ,
29- user:: { BrowserSessionFilter , UserEmailRepository , UserPasswordRepository , UserRepository } ,
29+ user:: {
30+ BrowserSessionFilter , UserEmailRepository , UserFilter , UserPasswordRepository ,
31+ UserRepository ,
32+ } ,
3033} ;
3134use mas_storage_pg:: { DatabaseError , PgRepository } ;
3235use rand:: {
@@ -85,6 +88,15 @@ enum Subcommand {
8588 ignore_complexity : bool ,
8689 } ,
8790
91+ /// Make a user admin
92+ PromoteAdmin { username : String } ,
93+
94+ /// Make a user non-admin
95+ DemoteAdmin { username : String } ,
96+
97+ /// List all users with admin privileges
98+ ListAdminUsers ,
99+
88100 /// Issue a compatibility token
89101 IssueCompatibilityToken {
90102 /// User for which to issue the token
@@ -315,6 +327,82 @@ impl Options {
315327 Ok ( ExitCode :: SUCCESS )
316328 }
317329
330+ SC :: PromoteAdmin { username } => {
331+ let _span =
332+ info_span ! ( "cli.manage.promote_admin" , user. username = username, ) . entered ( ) ;
333+
334+ let database_config = DatabaseConfig :: extract_or_default ( figment)
335+ . map_err ( anyhow:: Error :: from_boxed) ?;
336+ let mut conn = database_connection_from_config ( & database_config) . await ?;
337+ let txn = conn. begin ( ) . await ?;
338+ let mut repo = PgRepository :: from_conn ( txn) ;
339+
340+ let user = repo
341+ . user ( )
342+ . find_by_username ( & username)
343+ . await ?
344+ . context ( "User not found" ) ?;
345+
346+ let user = repo. user ( ) . set_can_request_admin ( user, true ) . await ?;
347+
348+ repo. into_inner ( ) . commit ( ) . await ?;
349+ info ! ( %user. id, %user. username, "User promoted to admin" ) ;
350+
351+ Ok ( ExitCode :: SUCCESS )
352+ }
353+
354+ SC :: DemoteAdmin { username } => {
355+ let _span =
356+ info_span ! ( "cli.manage.demote_admin" , user. username = username, ) . entered ( ) ;
357+
358+ let database_config = DatabaseConfig :: extract_or_default ( figment)
359+ . map_err ( anyhow:: Error :: from_boxed) ?;
360+ let mut conn = database_connection_from_config ( & database_config) . await ?;
361+ let txn = conn. begin ( ) . await ?;
362+ let mut repo = PgRepository :: from_conn ( txn) ;
363+
364+ let user = repo
365+ . user ( )
366+ . find_by_username ( & username)
367+ . await ?
368+ . context ( "User not found" ) ?;
369+
370+ let user = repo. user ( ) . set_can_request_admin ( user, false ) . await ?;
371+
372+ repo. into_inner ( ) . commit ( ) . await ?;
373+ info ! ( %user. id, %user. username, "User is no longer admin" ) ;
374+
375+ Ok ( ExitCode :: SUCCESS )
376+ }
377+
378+ SC :: ListAdminUsers => {
379+ let _span = info_span ! ( "cli.manage.list_admins" ) . entered ( ) ;
380+ let database_config = DatabaseConfig :: extract_or_default ( figment)
381+ . map_err ( anyhow:: Error :: from_boxed) ?;
382+ let mut conn = database_connection_from_config ( & database_config) . await ?;
383+ let txn = conn. begin ( ) . await ?;
384+ let mut repo = PgRepository :: from_conn ( txn) ;
385+
386+ let mut cursor = Pagination :: first ( 1000 ) ;
387+ let filter = UserFilter :: new ( ) . can_request_admin_only ( ) ;
388+ let total = repo. user ( ) . count ( filter) . await ?;
389+
390+ info ! ( "The following users can request admin privileges ({total} total):" ) ;
391+ loop {
392+ let page = repo. user ( ) . list ( filter, cursor) . await ?;
393+ for user in page. edges {
394+ info ! ( %user. id, username = %user. username) ;
395+ cursor = cursor. after ( user. id ) ;
396+ }
397+
398+ if !page. has_next_page {
399+ break ;
400+ }
401+ }
402+
403+ Ok ( ExitCode :: SUCCESS )
404+ }
405+
318406 SC :: IssueCompatibilityToken {
319407 username,
320408 admin,
0 commit comments