7
7
use std:: { collections:: BTreeMap , process:: ExitCode } ;
8
8
9
9
use anyhow:: Context ;
10
+ use chrono:: Duration ;
10
11
use clap:: { ArgAction , CommandFactory , Parser } ;
11
12
use console:: { Alignment , Style , Term , pad_str, style} ;
12
13
use dialoguer:: { Confirm , FuzzySelect , Input , Password , theme:: ColorfulTheme } ;
@@ -28,7 +29,10 @@ use mas_storage::{
28
29
user:: { BrowserSessionFilter , UserEmailRepository , UserPasswordRepository , UserRepository } ,
29
30
} ;
30
31
use mas_storage_pg:: { DatabaseError , PgRepository } ;
31
- use rand:: { RngCore , SeedableRng } ;
32
+ use rand:: {
33
+ RngCore , SeedableRng ,
34
+ distributions:: { Alphanumeric , DistString as _} ,
35
+ } ;
32
36
use sqlx:: { Acquire , types:: Uuid } ;
33
37
use tracing:: { error, info, info_span, warn} ;
34
38
use zeroize:: Zeroizing ;
@@ -95,6 +99,24 @@ enum Subcommand {
95
99
admin : bool ,
96
100
} ,
97
101
102
+ /// Create a new user registration token
103
+ IssueUserRegistrationToken {
104
+ /// Specific token string to use. If not provided, a random token will
105
+ /// be generated.
106
+ #[ arg( long) ]
107
+ token : Option < String > ,
108
+
109
+ /// Maximum number of times this token can be used.
110
+ /// If not provided, the token can be used an unlimited number of times.
111
+ #[ arg( long) ]
112
+ usage_limit : Option < u32 > ,
113
+
114
+ /// Time in seconds after which the token expires.
115
+ /// If not provided, the token never expires.
116
+ #[ arg( long) ]
117
+ expires_in : Option < u32 > ,
118
+ } ,
119
+
98
120
/// Trigger a provisioning job for all users
99
121
ProvisionAllUsers ,
100
122
@@ -330,6 +352,38 @@ impl Options {
330
352
Ok ( ExitCode :: SUCCESS )
331
353
}
332
354
355
+ SC :: IssueUserRegistrationToken {
356
+ token,
357
+ usage_limit,
358
+ expires_in,
359
+ } => {
360
+ let _span = info_span ! ( "cli.manage.add_user_registration_token" ) . entered ( ) ;
361
+
362
+ let database_config = DatabaseConfig :: extract_or_default ( figment) ?;
363
+ let mut conn = database_connection_from_config ( & database_config) . await ?;
364
+ let txn = conn. begin ( ) . await ?;
365
+ let mut repo = PgRepository :: from_conn ( txn) ;
366
+
367
+ // Calculate expiration time if provided
368
+ let expires_at =
369
+ expires_in. map ( |seconds| clock. now ( ) + Duration :: seconds ( seconds. into ( ) ) ) ;
370
+
371
+ // Generate a token if not provided
372
+ let token_str = token. unwrap_or_else ( || Alphanumeric . sample_string ( & mut rng, 12 ) ) ;
373
+
374
+ // Create the token
375
+ let registration_token = repo
376
+ . user_registration_token ( )
377
+ . add ( & mut rng, & clock, token_str, usage_limit, expires_at)
378
+ . await ?;
379
+
380
+ repo. into_inner ( ) . commit ( ) . await ?;
381
+
382
+ info ! ( %registration_token. id, "Created user registration token: {}" , registration_token. token) ;
383
+
384
+ Ok ( ExitCode :: SUCCESS )
385
+ }
386
+
333
387
SC :: ProvisionAllUsers => {
334
388
let _span = info_span ! ( "cli.manage.provision_all_users" ) . entered ( ) ;
335
389
let database_config = DatabaseConfig :: extract_or_default ( figment) ?;
0 commit comments