@@ -2,6 +2,8 @@ package main
22
33import (
44 "context"
5+ "crypto/rand"
6+ "encoding/base64"
57 "errors"
68 "fmt"
79 "log"
@@ -64,6 +66,7 @@ func main() {
6466 }
6567
6668 handleEncryptionKey (cfg )
69+ handleAdminPassword (cfg )
6770 handleDebugMode (cfg )
6871 runAppFunc (cfg )
6972}
@@ -300,6 +303,137 @@ func handleKeyNotFound(toolkitCrypto security.Crypto, _, _ security.Storager) st
300303 return toolkitCrypto .GenerateKey ()
301304}
302305
306+ // generateRandomPassword creates a cryptographically secure random password.
307+ func generateRandomPassword (length int ) (string , error ) {
308+ bytes := make ([]byte , length )
309+
310+ _ , err := rand .Read (bytes )
311+ if err != nil {
312+ return "" , err
313+ }
314+
315+ return base64 .URLEncoding .EncodeToString (bytes )[:length ], nil
316+ }
317+
318+ // handleAdminPassword manages the admin password - loading from keyring or generating a new one.
319+ func handleAdminPassword (cfg * config.Config ) {
320+ // If admin password is already provided via config/env, just use it
321+ if cfg .AdminPassword != "" {
322+ log .Println ("Admin password loaded from configuration" )
323+
324+ return
325+ }
326+
327+ // Try to initialize secret store client for password retrieval
328+ remoteStorage , err := handleSecretsConfig (cfg )
329+ if err != nil {
330+ remoteStorage = nil
331+ }
332+
333+ // Try remote storage first
334+ if done := tryRemoteAdminPassword (cfg , remoteStorage ); done {
335+ return
336+ }
337+
338+ // Try local keyring storage
339+ localStorage := security .NewKeyRingStorage ("device-management-toolkit" )
340+
341+ if done := tryLocalAdminPassword (cfg , localStorage , remoteStorage ); done {
342+ return
343+ }
344+
345+ // Password not found anywhere, generate a new one
346+ password , err := generateRandomPassword (16 )
347+ if err != nil {
348+ log .Fatalf ("Failed to generate admin password: %v" , err )
349+ }
350+
351+ cfg .AdminPassword = password
352+
353+ if err := saveAdminPassword (password , remoteStorage , localStorage ); err != nil {
354+ log .Printf ("Warning: Failed to save admin password: %v" , err )
355+ }
356+
357+ // Output the generated password so the user knows what to use
358+ log .Printf ("\033 [33m========================================\033 [0m" )
359+ log .Printf ("\033 [33mGenerated Admin Password: %s\033 [0m" , password )
360+ log .Printf ("\033 [33mThis password has been saved to your system keyring.\033 [0m" )
361+ log .Printf ("\033 [33m========================================\033 [0m" )
362+ }
363+
364+ // tryRemoteAdminPassword attempts to retrieve the admin password from remote storage.
365+ func tryRemoteAdminPassword (cfg * config.Config , remoteStorage security.Storager ) bool {
366+ if remoteStorage == nil {
367+ return false
368+ }
369+
370+ password , err := remoteStorage .GetKeyValue ("admin-password" )
371+ if err == nil && password != "" {
372+ cfg .AdminPassword = password
373+
374+ log .Println ("Admin password loaded from secret store" )
375+
376+ return true
377+ }
378+
379+ return false
380+ }
381+
382+ // tryLocalAdminPassword attempts to retrieve the admin password from local keyring.
383+ func tryLocalAdminPassword (cfg * config.Config , localStorage , remoteStorage security.Storager ) bool {
384+ password , err := localStorage .GetKeyValue ("admin-password" )
385+ if err == nil && password != "" {
386+ cfg .AdminPassword = password
387+
388+ log .Println ("Admin password loaded from local keyring" )
389+ syncAdminPasswordToRemote (password , remoteStorage )
390+
391+ return true
392+ }
393+
394+ // Check for unexpected errors
395+ if err != nil && ! errors .Is (err , security .ErrKeyNotFound ) {
396+ log .Printf ("Warning: Failed to read admin password from keyring: %v" , err )
397+ }
398+
399+ return false
400+ }
401+
402+ // syncAdminPasswordToRemote syncs the admin password to remote storage if available.
403+ func syncAdminPasswordToRemote (password string , remoteStorage security.Storager ) {
404+ if remoteStorage == nil {
405+ return
406+ }
407+
408+ if err := remoteStorage .SetKeyValue ("admin-password" , password ); err != nil {
409+ log .Printf ("Warning: Failed to sync admin password to secret store: %v" , err )
410+ } else {
411+ log .Println ("Admin password synced to secret store" )
412+ }
413+ }
414+
415+ func saveAdminPassword (password string , remoteStorage , localStorage security.Storager ) error {
416+ if remoteStorage != nil {
417+ err := remoteStorage .SetKeyValue ("admin-password" , password )
418+ if err == nil {
419+ log .Println ("Admin password saved to secret store" )
420+
421+ return nil
422+ }
423+
424+ return err
425+ }
426+
427+ err := localStorage .SetKeyValue ("admin-password" , password )
428+ if err == nil {
429+ log .Println ("Admin password saved to local keyring" )
430+
431+ return nil
432+ }
433+
434+ return err
435+ }
436+
303437// CommandExecutor is an interface to allow for mocking exec.Command in tests.
304438type CommandExecutor interface {
305439 Execute (name string , arg ... string ) error
0 commit comments