From e4cd6810abbbbcc4745cd465a2da5295f200dfda Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Wed, 6 Nov 2024 14:22:57 +0200 Subject: [PATCH] always register the installer hooks in case the superuser is created by a console command --- apis/installer.go | 41 +++------------------------------ apis/serve.go | 7 +++++- core/record_model_superusers.go | 40 +++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/apis/installer.go b/apis/installer.go index 35291a87..d8b1a218 100644 --- a/apis/installer.go +++ b/apis/installer.go @@ -13,11 +13,9 @@ import ( "github.com/go-ozzo/ozzo-validation/v4/is" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" - "github.com/pocketbase/pocketbase/tools/hook" "github.com/pocketbase/pocketbase/tools/security" ) -const installerEmail = "__pbinstaller@example.com" const installerHookId = "__pbinstallerHook" func loadInstaller(app core.App, hostURL string) error { @@ -35,39 +33,6 @@ func loadInstaller(app core.App, hostURL string) error { return err } - // prevent sending password reset emails to the installer address - app.OnMailerRecordPasswordResetSend(core.CollectionNameSuperusers).Bind(&hook.Handler[*core.MailerRecordEvent]{ - Id: installerHookId, - Func: func(e *core.MailerRecordEvent) error { - if e.Record.Email() == installerEmail { - return errors.New("cannot reset the password for the installer account") - } - - return e.Next() - }, - }) - - // cleanup the installer account after the first superuser creation - app.OnRecordCreate(core.CollectionNameSuperusers).Bind(&hook.Handler[*core.RecordEvent]{ - Id: installerHookId, - Func: func(e *core.RecordEvent) error { - if err := e.Next(); err != nil { - return err - } - - color.Green("Successfully created superuser %s! This message will no longer show on the next startup.\n\n", e.Record.Email()) - - if err = e.App.Delete(installerRecord); err != nil { - e.App.Logger().Error("Failed to remove installer superuser", "error", err) - } - - app.OnRecordCreate().Unbind(installerHookId) - app.OnMailerRecordPasswordResetSend().Unbind(installerHookId) - - return nil - }, - }) - // launch url (ignore errors and always print a help text as fallback) url := fmt.Sprintf("%s/_/#/pbinstal/%s", hostURL, token) _ = launchURL(url) @@ -80,7 +45,7 @@ func loadInstaller(app core.App, hostURL string) error { func needInstallerSuperuser(app core.App) bool { total, err := app.CountRecords(core.CollectionNameSuperusers, dbx.Not(dbx.HashExp{ - "email": installerEmail, + "email": core.DefaultInstallerEmail, })) return err == nil && total == 0 } @@ -91,14 +56,14 @@ func findOrCreateInstallerSuperuser(app core.App) (*core.Record, error) { return nil, err } - record, err := app.FindAuthRecordByEmail(col, installerEmail) + record, err := app.FindAuthRecordByEmail(col, core.DefaultInstallerEmail) if err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, err } record = core.NewRecord(col) - record.SetEmail(installerEmail) + record.SetEmail(core.DefaultInstallerEmail) record.SetPassword(security.RandomString(30)) err = app.Save(record) diff --git a/apis/serve.go b/apis/serve.go index 6c8fb5e6..ff47f32a 100644 --- a/apis/serve.go +++ b/apis/serve.go @@ -270,7 +270,12 @@ func Serve(app core.App, config ServeConfig) error { regular.Printf("└─ Dashboard: %s\n", color.CyanString("%s/_/", fullAddr)) } - go loadInstaller(app, fullAddr) + go func() { + installerErr := loadInstaller(app, fullAddr) + if installerErr != nil { + app.Logger().Warn("Failed to initialize installer", "error", installerErr) + } + }() var serveErr error if config.HttpsAddr != "" { diff --git a/core/record_model_superusers.go b/core/record_model_superusers.go index 256d6aee..8b1da48c 100644 --- a/core/record_model_superusers.go +++ b/core/record_model_superusers.go @@ -1,6 +1,8 @@ package core import ( + "database/sql" + "errors" "fmt" "github.com/pocketbase/pocketbase/tools/hook" @@ -9,6 +11,10 @@ import ( const CollectionNameSuperusers = "_superusers" +// DefaultInstallerEmail is the default superuser email address +// for the initial autogenerated superuser account. +const DefaultInstallerEmail = "__pbinstaller@example.com" + func (app *BaseApp) registerSuperuserHooks() { app.OnRecordDelete(CollectionNameSuperusers).Bind(&hook.Handler[*RecordEvent]{ Id: "pbSuperusersRecordDelete", @@ -39,13 +45,45 @@ func (app *BaseApp) registerSuperuserHooks() { Id: "pbSuperusersRecordSaveExec", Func: func(e *RecordEvent) error { e.Record.SetVerified(true) // always mark superusers as verified - return e.Next() + + if err := e.Next(); err != nil { + return err + } + + // ensure that the installer superuser is deleted + if e.Type == ModelEventTypeCreate && e.Record.Email() != DefaultInstallerEmail { + record, err := app.FindAuthRecordByEmail(CollectionNameSuperusers, DefaultInstallerEmail) + if errors.Is(err, sql.ErrNoRows) { + // already deleted + } else if err != nil { + e.App.Logger().Warn("Failed to fetch installer superuser", "error", err) + } else { + err = e.App.Delete(record) + if err != nil { + e.App.Logger().Warn("Failed to delete installer superuser", "error", err) + } + } + } + + return nil }, Priority: -99, } app.OnRecordCreateExecute(CollectionNameSuperusers).Bind(recordSaveHandler) app.OnRecordUpdateExecute(CollectionNameSuperusers).Bind(recordSaveHandler) + // prevent sending password reset emails to the installer address + app.OnMailerRecordPasswordResetSend(CollectionNameSuperusers).Bind(&hook.Handler[*MailerRecordEvent]{ + Id: "pbSuperusersInstallerPasswordReset", + Func: func(e *MailerRecordEvent) error { + if e.Record.Email() == DefaultInstallerEmail { + return errors.New("cannot reset the password for the installer superuser") + } + + return e.Next() + }, + }) + collectionSaveHandler := &hook.Handler[*CollectionEvent]{ Id: "pbSuperusersCollectionSaveExec", Func: func(e *CollectionEvent) error {