From 9ebe092b9d93d77d229e1da9a642f305f7ebaff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxi=20Quo=C3=9F?= Date: Sun, 29 Dec 2024 12:00:12 +0100 Subject: [PATCH] fix: create custom superuser setup to change new pocketbase superuser behavior --- backend/pb/handlers.go | 40 ++++++++++++++++++++++++ backend/pb/pb.go | 25 ++++++++++----- frontend/src/routes/+layout.svelte | 11 ++++--- frontend/src/routes/account/+page.svelte | 6 ++-- frontend/src/routes/login/+page.svelte | 3 +- frontend/src/routes/welcome/+page.svelte | 16 ++++++---- 6 files changed, 80 insertions(+), 21 deletions(-) diff --git a/backend/pb/handlers.go b/backend/pb/handlers.go index e5dc9637..226f56f3 100644 --- a/backend/pb/handlers.go +++ b/backend/pb/handlers.go @@ -216,3 +216,43 @@ func HandlerScan(e *core.RequestEvent) error { return e.JSON(http.StatusOK, res) } + +func HandlerInitSuperuser(e *core.RequestEvent) error { + superusersCollection, err := e.App.FindCollectionByNameOrId(core.CollectionNameSuperusers) + if err != nil { + return e.NotFoundError("Failed to retrieve superusers collection", err) + } + + totalSuperusers, err := e.App.CountRecords(superusersCollection) + if err != nil { + return e.InternalServerError("Failed to retrieve superusers count", err) + } + + if totalSuperusers > 0 { + return e.BadRequestError("An initial superuser already exists", nil) + } + + data := struct { + Email string `json:"email" form:"email"` + Password string `json:"password" form:"password"` + PasswordConfirm string `json:"password_confirm" form:"password-confirm"` + }{} + err = e.BindBody(&data) + if err != nil { + return e.BadRequestError("Failed to read request data", err) + } + + if data.Password != data.PasswordConfirm { + return e.BadRequestError("Password don't match", err) + } + + record := core.NewRecord(superusersCollection) + record.SetEmail(data.Email) + record.SetPassword(data.Password) + err = App.Save(record) + if err != nil { + return e.BadRequestError("Failed to create initial superuser", err) + } + + return apis.RecordAuthResponse(e, record, "", nil) +} diff --git a/backend/pb/pb.go b/backend/pb/pb.go index e7370183..5db9266c 100644 --- a/backend/pb/pb.go +++ b/backend/pb/pb.go @@ -1,6 +1,7 @@ package pb import ( + "errors" "fmt" "io/fs" "os" @@ -66,6 +67,7 @@ func StartPocketBase(distDirFS fs.FS) { se.Router.GET("/api/upsnap/reboot/{id}", HandlerReboot).Bind(RequireUpSnapPermission()) se.Router.GET("/api/upsnap/shutdown/{id}", HandlerShutdown).Bind(RequireUpSnapPermission()) se.Router.GET("/api/upsnap/scan", HandlerScan).Bind(apis.RequireSuperuserAuth()) + se.Router.POST("/api/upsnap/init-superuser", HandlerInitSuperuser) // https://github.com/pocketbase/pocketbase/discussions/6198 if err := importSettings(); err != nil { return err @@ -115,18 +117,18 @@ func StartPocketBase(distDirFS fs.FS) { cronjobs.SetWakeShutdownJobs(App) } } - return nil + return e.Next() }) return se.Next() }) App.OnModelAfterCreateSuccess().BindFunc(func(e *core.ModelEvent) error { - if e.Model.TableName() == "_admins" { + if e.Model.TableName() == "_superusers" { if err := setSetupCompleted(); err != nil { logger.Error.Println(err) return err } - return nil + return e.Next() } else if e.Model.TableName() == "devices" { // when a device is created, give the user all rights to the device he just created deviceRec := e.Model.(*core.Record) @@ -149,21 +151,30 @@ func StartPocketBase(distDirFS fs.FS) { } } } - return nil + return e.Next() }) + App.OnModelAfterDeleteSuccess().BindFunc(func(e *core.ModelEvent) error { - if e.Model.TableName() == "_admins" { + if e.Model.TableName() == "_superusers" { if err := setSetupCompleted(); err != nil { logger.Error.Println(err) return err } } - return nil + return e.Next() + }) + + // prevent new superuser bahavior introduced in pocketbase 0.23 + App.OnRecordCreate(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordEvent) error { + if e.Record.Email() == core.DefaultInstallerEmail { + return errors.New("skip default PocketBase installer") + } + return e.Next() }) App.OnTerminate().BindFunc(func(e *core.TerminateEvent) error { cronjobs.StopAll() - return nil + return e.Next() }) if err := App.Start(); err != nil { diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 95743217..93f2b643 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -65,10 +65,13 @@ // refresh auth token if ($pocketbase.authStore.isSuperuser) { - await $pocketbase.admins.authRefresh().catch(() => { - $pocketbase.authStore.clear(); - goto('/login'); - }); + await $pocketbase + .collection('_superusers') + .authRefresh() + .catch(() => { + $pocketbase.authStore.clear(); + goto('/login'); + }); } else { await $pocketbase .collection('users') diff --git a/frontend/src/routes/account/+page.svelte b/frontend/src/routes/account/+page.svelte index 4092b76c..0e04a80f 100644 --- a/frontend/src/routes/account/+page.svelte +++ b/frontend/src/routes/account/+page.svelte @@ -95,9 +95,9 @@ function changePassword() { fetch( - `${backendUrl}api/${$pocketbase.authStore.isSuperuser ? 'admins' : `collections/users/records`}/${ - $pocketbase.authStore.record?.id - }`, + `${backendUrl}api/collections/${ + $pocketbase.authStore.isSuperuser ? '_superusers' : `users` + }/records/${$pocketbase.authStore.record?.id}`, { method: 'PATCH', headers: { diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index 0ff08321..51d6a31f 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -23,7 +23,8 @@ }); function tryAdminThenUser() { - $pocketbase.admins + $pocketbase + .collection('_superusers') .authWithPassword(form.email, form.password) .then(() => { goto('/'); diff --git a/frontend/src/routes/welcome/+page.svelte b/frontend/src/routes/welcome/+page.svelte index b4c00664..e2fb3428 100644 --- a/frontend/src/routes/welcome/+page.svelte +++ b/frontend/src/routes/welcome/+page.svelte @@ -26,11 +26,14 @@ }); async function register() { - $pocketbase.admins - .create({ - email: form.email, - password: form.password, - passwordConfirm: form.confirm + $pocketbase + .send('/api/upsnap/init-superuser', { + method: 'POST', + body: { + email: form.email, + password: form.password, + password_confirm: form.confirm + } }) .then(() => { $pocketbase @@ -39,7 +42,8 @@ .then((data) => { settingsPub.set(data as SettingsPublic); }); - $pocketbase.admins + $pocketbase + .collection('_superusers') .authWithPassword(form.email, form.password) .then(() => { stepsCompleted = 2;