@@ -19,14 +19,17 @@ use mas_data_model::{Clock, Device, SystemClock, TokenType, Ulid, UpstreamOAuthP
19
19
use mas_email:: Address ;
20
20
use mas_matrix:: HomeserverConnection ;
21
21
use mas_storage:: {
22
- RepositoryAccess ,
22
+ Pagination , RepositoryAccess ,
23
23
compat:: { CompatAccessTokenRepository , CompatSessionFilter , CompatSessionRepository } ,
24
24
oauth2:: OAuth2SessionFilter ,
25
25
queue:: {
26
26
DeactivateUserJob , ProvisionUserJob , QueueJobRepositoryExt as _, ReactivateUserJob ,
27
27
SyncDevicesJob ,
28
28
} ,
29
- user:: { BrowserSessionFilter , UserEmailRepository , UserPasswordRepository , UserRepository } ,
29
+ user:: {
30
+ BrowserSessionFilter , UserEmailRepository , UserFilter , UserPasswordRepository ,
31
+ UserRepository ,
32
+ } ,
30
33
} ;
31
34
use mas_storage_pg:: { DatabaseError , PgRepository } ;
32
35
use rand:: {
@@ -85,6 +88,15 @@ enum Subcommand {
85
88
ignore_complexity : bool ,
86
89
} ,
87
90
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
+
88
100
/// Issue a compatibility token
89
101
IssueCompatibilityToken {
90
102
/// User for which to issue the token
@@ -315,6 +327,82 @@ impl Options {
315
327
Ok ( ExitCode :: SUCCESS )
316
328
}
317
329
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
+
318
406
SC :: IssueCompatibilityToken {
319
407
username,
320
408
admin,
0 commit comments