Skip to content

Commit

Permalink
[pocketbase#1240] added dedicated before/after auth hooks and refacto…
Browse files Browse the repository at this point in the history
…red the submit interceptors
  • Loading branch information
ganigeorgiev committed Jan 15, 2023
1 parent 8f6f879 commit 36ab3fd
Show file tree
Hide file tree
Showing 46 changed files with 1,124 additions and 294 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@

- Added LiveChat OAuth2 provider ([#1573](https://github.com/pocketbase/pocketbase/pull/1573); thanks @mariosant).

- Added new event hooks:

```go
OnRecordBeforeAuthWithPasswordRequest()
OnRecordAfterAuthWithPasswordRequest()
OnRecordBeforeAuthWithOAuth2Request()
OnRecordAfterAuthWithOAuth2Request()
OnRecordBeforeAuthRefreshRequest()
OnRecordAfterAuthRefreshRequest()
OnAdminBeforeAuthWithPasswordRequest()
OnAdminAfterAuthWithPasswordRequest()
OnAdminBeforeAuthRefreshRequest()
OnAdminAfterAuthRefreshRequest()
OnAdminBeforeRequestPasswordResetRequest()
OnAdminAfterRequestPasswordResetRequest()
OnAdminBeforeConfirmPasswordResetRequest()
OnAdminAfterConfirmPasswordResetRequest()
```

- Refactored all `forms` Submit interceptors to use a Generic data type as their payload.


## v0.11.2

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ To build the minimal standalone executable, like the prebuilt ones in the releas
2. Navigate to `examples/base`
3. Run `GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build`
(_https://go.dev/doc/install/source#environment_)
4. Start the generated executable by running `./base serve`.
4. Start the created executable by running `./base serve`.

The supported build targets by the non-cgo driver at the moment are:
```
Expand Down
143 changes: 117 additions & 26 deletions apis/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,57 @@ func (api *adminApi) authRefresh(c echo.Context) error {
return NewNotFoundError("Missing auth admin context.", nil)
}

return api.authResponse(c, admin)
event := &core.AdminAuthRefreshEvent{
HttpContext: c,
Admin: admin,
}

handlerErr := api.app.OnAdminBeforeAuthRefreshRequest().Trigger(event, func(e *core.AdminAuthRefreshEvent) error {
return api.authResponse(e.HttpContext, e.Admin)
})

if handlerErr == nil {
if err := api.app.OnAdminAfterAuthRefreshRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return handlerErr
}

func (api *adminApi) authWithPassword(c echo.Context) error {
form := forms.NewAdminLogin(api.app)
if readErr := c.Bind(form); readErr != nil {
return NewBadRequestError("An error occurred while loading the submitted data.", readErr)
if err := c.Bind(form); err != nil {
return NewBadRequestError("An error occurred while loading the submitted data.", err)
}

admin, submitErr := form.Submit()
if submitErr != nil {
return NewBadRequestError("Failed to authenticate.", submitErr)
event := &core.AdminAuthWithPasswordEvent{
HttpContext: c,
Password: form.Password,
Identity: form.Identity,
}

return api.authResponse(c, admin)
_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] {
return func(admin *models.Admin) error {
event.Admin = admin

return api.app.OnAdminBeforeAuthWithPasswordRequest().Trigger(event, func(e *core.AdminAuthWithPasswordEvent) error {
if err := next(e.Admin); err != nil {
return NewBadRequestError("Failed to authenticate.", err)
}

return api.authResponse(e.HttpContext, e.Admin)
})
}
})

if submitErr == nil {
if err := api.app.OnAdminAfterAuthWithPasswordRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return submitErr
}

func (api *adminApi) requestPasswordReset(c echo.Context) error {
Expand All @@ -86,15 +122,41 @@ func (api *adminApi) requestPasswordReset(c echo.Context) error {
return NewBadRequestError("An error occurred while validating the form.", err)
}

// run in background because we don't need to show the result
// (prevents admins enumeration)
routine.FireAndForget(func() {
if err := form.Submit(); err != nil && api.app.IsDebug() {
log.Println(err)
event := &core.AdminRequestPasswordResetEvent{
HttpContext: c,
}

submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] {
return func(Admin *models.Admin) error {
event.Admin = Admin

return api.app.OnAdminBeforeRequestPasswordResetRequest().Trigger(event, func(e *core.AdminRequestPasswordResetEvent) error {
// run in background because we don't need to show the result to the client
routine.FireAndForget(func() {
if err := next(e.Admin); err != nil && api.app.IsDebug() {
log.Println(err)
}
})

return e.HttpContext.NoContent(http.StatusNoContent)
})
}
})

return c.NoContent(http.StatusNoContent)
if submitErr == nil {
if err := api.app.OnAdminAfterRequestPasswordResetRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
} else if api.app.IsDebug() {
log.Println(submitErr)
}

// don't return the response error to prevent emails enumeration
if !c.Response().Committed {
c.NoContent(http.StatusNoContent)
}

return nil
}

func (api *adminApi) confirmPasswordReset(c echo.Context) error {
Expand All @@ -103,12 +165,31 @@ func (api *adminApi) confirmPasswordReset(c echo.Context) error {
return NewBadRequestError("An error occurred while loading the submitted data.", readErr)
}

_, submitErr := form.Submit()
if submitErr != nil {
return NewBadRequestError("Failed to set new password.", submitErr)
event := &core.AdminConfirmPasswordResetEvent{
HttpContext: c,
}

return c.NoContent(http.StatusNoContent)
_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] {
return func(admin *models.Admin) error {
event.Admin = admin

return api.app.OnAdminBeforeConfirmPasswordResetRequest().Trigger(event, func(e *core.AdminConfirmPasswordResetEvent) error {
if err := next(e.Admin); err != nil {
return NewBadRequestError("Failed to set new password.", err)
}

return e.HttpContext.NoContent(http.StatusNoContent)
})
}
})

if submitErr == nil {
if err := api.app.OnAdminAfterConfirmPasswordResetRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return submitErr
}

func (api *adminApi) list(c echo.Context) error {
Expand Down Expand Up @@ -174,10 +255,12 @@ func (api *adminApi) create(c echo.Context) error {
}

// create the admin
submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc {
return func() error {
submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] {
return func(m *models.Admin) error {
event.Admin = m

return api.app.OnAdminBeforeCreateRequest().Trigger(event, func(e *core.AdminCreateEvent) error {
if err := next(); err != nil {
if err := next(e.Admin); err != nil {
return NewBadRequestError("Failed to create admin.", err)
}

Expand All @@ -187,7 +270,9 @@ func (api *adminApi) create(c echo.Context) error {
})

if submitErr == nil {
api.app.OnAdminAfterCreateRequest().Trigger(event)
if err := api.app.OnAdminAfterCreateRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return submitErr
Expand Down Expand Up @@ -217,10 +302,12 @@ func (api *adminApi) update(c echo.Context) error {
}

// update the admin
submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc {
return func() error {
submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] {
return func(m *models.Admin) error {
event.Admin = m

return api.app.OnAdminBeforeUpdateRequest().Trigger(event, func(e *core.AdminUpdateEvent) error {
if err := next(); err != nil {
if err := next(e.Admin); err != nil {
return NewBadRequestError("Failed to update admin.", err)
}

Expand All @@ -230,7 +317,9 @@ func (api *adminApi) update(c echo.Context) error {
})

if submitErr == nil {
api.app.OnAdminAfterUpdateRequest().Trigger(event)
if err := api.app.OnAdminAfterUpdateRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return submitErr
Expand Down Expand Up @@ -261,7 +350,9 @@ func (api *adminApi) delete(c echo.Context) error {
})

if handlerErr == nil {
api.app.OnAdminAfterDeleteRequest().Trigger(event)
if err := api.app.OnAdminAfterDeleteRequest().Trigger(event); err != nil && api.app.IsDebug() {
log.Println(err)
}
}

return handlerErr
Expand Down
36 changes: 26 additions & 10 deletions apis/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/pocketbase/pocketbase/tools/types"
)

func TestAdminAuthWithEmail(t *testing.T) {
func TestAdminAuthWithPassword(t *testing.T) {
scenarios := []tests.ApiScenario{
{
Name: "empty data",
Expand All @@ -39,6 +39,9 @@ func TestAdminAuthWithEmail(t *testing.T) {
Body: strings.NewReader(`{"identity":"[email protected]","password":"1234567890"}`),
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{
"OnAdminBeforeAuthWithPasswordRequest": 1,
},
},
{
Name: "wrong password",
Expand All @@ -47,6 +50,9 @@ func TestAdminAuthWithEmail(t *testing.T) {
Body: strings.NewReader(`{"identity":"[email protected]","password":"invalid"}`),
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{
"OnAdminBeforeAuthWithPasswordRequest": 1,
},
},
{
Name: "valid email/password (guest)",
Expand All @@ -59,7 +65,9 @@ func TestAdminAuthWithEmail(t *testing.T) {
`"token":`,
},
ExpectedEvents: map[string]int{
"OnAdminAuthRequest": 1,
"OnAdminBeforeAuthWithPasswordRequest": 1,
"OnAdminAfterAuthWithPasswordRequest": 1,
"OnAdminAuthRequest": 1,
},
},
{
Expand All @@ -76,7 +84,9 @@ func TestAdminAuthWithEmail(t *testing.T) {
`"token":`,
},
ExpectedEvents: map[string]int{
"OnAdminAuthRequest": 1,
"OnAdminBeforeAuthWithPasswordRequest": 1,
"OnAdminAfterAuthWithPasswordRequest": 1,
"OnAdminAuthRequest": 1,
},
},
}
Expand Down Expand Up @@ -120,10 +130,12 @@ func TestAdminRequestPasswordReset(t *testing.T) {
Delay: 100 * time.Millisecond,
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
"OnMailerBeforeAdminResetPasswordSend": 1,
"OnMailerAfterAdminResetPasswordSend": 1,
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
"OnMailerBeforeAdminResetPasswordSend": 1,
"OnMailerAfterAdminResetPasswordSend": 1,
"OnAdminBeforeRequestPasswordResetRequest": 1,
"OnAdminAfterRequestPasswordResetRequest": 1,
},
},
{
Expand Down Expand Up @@ -206,8 +218,10 @@ func TestAdminConfirmPasswordReset(t *testing.T) {
}`),
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
"OnAdminBeforeConfirmPasswordResetRequest": 1,
"OnAdminAfterConfirmPasswordResetRequest": 1,
},
},
}
Expand Down Expand Up @@ -259,7 +273,9 @@ func TestAdminRefresh(t *testing.T) {
`"token":`,
},
ExpectedEvents: map[string]int{
"OnAdminAuthRequest": 1,
"OnAdminAuthRequest": 1,
"OnAdminBeforeAuthRefreshRequest": 1,
"OnAdminAfterAuthRefreshRequest": 1,
},
},
}
Expand Down
Loading

0 comments on commit 36ab3fd

Please sign in to comment.