mirror of
https://github.com/teamhanko/hanko.git
synced 2025-11-01 19:18:53 +08:00
fix(review): fix review findings
* admin api: make email primary when user has no emails * utils: move get updated user and webhook trigger to utils to reduce duplicated code * events: remove unused user and email event - Check is replaced with string variant * remove unused dtos * fix tests after changes * webhook tests: switch to test.Suite instead of TestPersister -> added deprecation annotation to test.NewPersister * Email Verification: Fix trigger of webhook when email verification is enabled and a email is created but not validated Closes: #692, #1051
This commit is contained in:
@ -1,6 +0,0 @@
|
||||
package dto
|
||||
|
||||
type UpdateCredentialDto struct {
|
||||
Id string `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package dto
|
||||
|
||||
type TokenDto struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
@ -10,7 +10,6 @@ import (
|
||||
auditlog "github.com/teamhanko/hanko/backend/audit_log"
|
||||
"github.com/teamhanko/hanko/backend/config"
|
||||
"github.com/teamhanko/hanko/backend/dto"
|
||||
"github.com/teamhanko/hanko/backend/dto/admin"
|
||||
"github.com/teamhanko/hanko/backend/persistence"
|
||||
"github.com/teamhanko/hanko/backend/persistence/models"
|
||||
"github.com/teamhanko/hanko/backend/session"
|
||||
@ -20,10 +19,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
UpdatedUserErrorMessage = "failed to fetch updated user: %w"
|
||||
)
|
||||
|
||||
type EmailHandler struct {
|
||||
persister persistence.Persister
|
||||
cfg *config.Config
|
||||
@ -31,13 +26,13 @@ type EmailHandler struct {
|
||||
auditLogger auditlog.Logger
|
||||
}
|
||||
|
||||
func NewEmailHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) (*EmailHandler, error) {
|
||||
func NewEmailHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) *EmailHandler {
|
||||
return &EmailHandler{
|
||||
persister: persister,
|
||||
cfg: cfg,
|
||||
sessionManager: sessionManager,
|
||||
auditLogger: auditLogger,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *EmailHandler) List(c echo.Context) error {
|
||||
@ -145,15 +140,8 @@ func (h *EmailHandler) Create(c echo.Context) error {
|
||||
return fmt.Errorf("failed to create audit log: %w", err)
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(c, events.EmailCreate, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
if !h.cfg.Emails.RequireVerification {
|
||||
utils.NotifyUserChange(c, tx, h.persister, events.EmailCreate, userId)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, email)
|
||||
@ -215,16 +203,7 @@ func (h *EmailHandler) SetPrimaryEmail(c echo.Context) error {
|
||||
return fmt.Errorf("failed to create audit log: %w", err)
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(c, events.EmailPrimary, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
}
|
||||
utils.NotifyUserChange(c, tx, h.persister, events.EmailPrimary, userId)
|
||||
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
})
|
||||
@ -268,16 +247,7 @@ func (h *EmailHandler) Delete(c echo.Context) error {
|
||||
return fmt.Errorf("failed to create audit log: %w", err)
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(c, events.EmailDelete, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
}
|
||||
utils.NotifyUserChange(c, tx, h.persister, events.EmailDelete, userId)
|
||||
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
})
|
||||
|
||||
@ -144,17 +144,14 @@ func (h *emailAdminHandler) Create(ctx echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(ctx, events.EmailCreate, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
ctx.Logger().Warn(err)
|
||||
// make email primary if user had no emails prior to email creation
|
||||
if len(user.Emails) == 0 {
|
||||
primaryEmail := models.NewPrimaryEmail(email.ID, user.ID)
|
||||
err = h.persister.GetPrimaryEmailPersisterWithConnection(tx).Create(*primaryEmail)
|
||||
}
|
||||
|
||||
utils.NotifyUserChange(ctx, tx, h.persister, events.EmailCreate, userId)
|
||||
|
||||
return ctx.JSON(http.StatusCreated, admin.FromEmailModel(email))
|
||||
})
|
||||
}
|
||||
@ -232,16 +229,7 @@ func (h *emailAdminHandler) Delete(ctx echo.Context) error {
|
||||
return fmt.Errorf("failed to delete email from db: %w", err)
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(ctx, events.EmailDelete, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
ctx.Logger().Warn(err)
|
||||
}
|
||||
utils.NotifyUserChange(ctx, tx, h.persister, events.EmailDelete, userId)
|
||||
|
||||
return ctx.NoContent(http.StatusNoContent)
|
||||
})
|
||||
@ -287,16 +275,7 @@ func (h *emailAdminHandler) SetPrimaryEmail(ctx echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
updatedUser, err := h.persister.GetUserPersisterWithConnection(tx).Get(user.ID)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return fmt.Errorf(UpdatedUserErrorMessage, err)
|
||||
}
|
||||
}
|
||||
err = utils.TriggerWebhooks(ctx, events.EmailPrimary, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
ctx.Logger().Warn(err)
|
||||
}
|
||||
utils.NotifyUserChange(ctx, tx, h.persister, events.EmailPrimary, userId)
|
||||
|
||||
return ctx.NoContent(http.StatusNoContent)
|
||||
})
|
||||
|
||||
@ -26,8 +26,7 @@ type emailSuite struct {
|
||||
}
|
||||
|
||||
func (s *emailSuite) TestEmailHandler_New() {
|
||||
emailHandler, err := NewEmailHandler(&config.Config{}, s.Storage, sessionManager{}, test.NewAuditLogger())
|
||||
s.NoError(err)
|
||||
emailHandler := NewEmailHandler(&config.Config{}, s.Storage, sessionManager{}, test.NewAuditLogger())
|
||||
s.NotEmpty(emailHandler)
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@ import (
|
||||
"github.com/teamhanko/hanko/backend/persistence/models"
|
||||
"github.com/teamhanko/hanko/backend/rate_limiter"
|
||||
"github.com/teamhanko/hanko/backend/session"
|
||||
"github.com/teamhanko/hanko/backend/webhooks/events"
|
||||
"github.com/teamhanko/hanko/backend/webhooks/utils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/gomail.v2"
|
||||
"net/http"
|
||||
@ -323,7 +325,10 @@ func (h *PasscodeHandler) Finish(c echo.Context) error {
|
||||
return echo.NewHTTPError(http.StatusForbidden).SetInternal(errors.New("passcode finalization not allowed"))
|
||||
}
|
||||
|
||||
wasUnverified := false
|
||||
if !passcode.Email.Verified {
|
||||
wasUnverified = true
|
||||
|
||||
// Update email verified status and assign the email address to the user.
|
||||
passcode.Email.Verified = true
|
||||
passcode.Email.UserID = &user.ID
|
||||
@ -377,6 +382,11 @@ func (h *PasscodeHandler) Finish(c echo.Context) error {
|
||||
return fmt.Errorf("failed to create audit log: %w", err)
|
||||
}
|
||||
|
||||
// notify about email verification result. Last step to prevent a trigger and rollback scenario
|
||||
if h.cfg.Emails.RequireVerification && wasUnverified {
|
||||
utils.NotifyUserChange(c, tx, h.persister, events.EmailCreate, user.ID)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, dto.PasscodeReturn{
|
||||
Id: passcode.ID.String(),
|
||||
TTL: passcode.Ttl,
|
||||
|
||||
@ -125,10 +125,7 @@ func NewPublicRouter(cfg *config.Config, persister persistence.Persister, promet
|
||||
wellKnown.GET("/jwks.json", wellKnownHandler.GetPublicKeys)
|
||||
wellKnown.GET("/config", wellKnownHandler.GetConfig)
|
||||
|
||||
emailHandler, err := NewEmailHandler(cfg, persister, sessionManager, auditLogger)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create public email handler: %w", err))
|
||||
}
|
||||
emailHandler := NewEmailHandler(cfg, persister, sessionManager, auditLogger)
|
||||
|
||||
webauthn := g.Group("/webauthn")
|
||||
webauthnRegistration := webauthn.Group("/registration", sessionMiddleware)
|
||||
@ -147,7 +144,7 @@ func NewPublicRouter(cfg *config.Config, persister persistence.Persister, promet
|
||||
passcode := g.Group("/passcode")
|
||||
passcodeLogin := passcode.Group("/login")
|
||||
passcodeLogin.POST("/initialize", passcodeHandler.Init)
|
||||
passcodeLogin.POST("/finalize", passcodeHandler.Finish)
|
||||
passcodeLogin.POST("/finalize", passcodeHandler.Finish, webhookMiddlware)
|
||||
|
||||
email := g.Group("/emails", sessionMiddleware, webhookMiddlware)
|
||||
email.GET("", emailHandler.List)
|
||||
|
||||
@ -145,10 +145,12 @@ func (h *UserHandler) Create(c echo.Context) error {
|
||||
EmailID: email.ID,
|
||||
}
|
||||
|
||||
if !h.cfg.Emails.RequireVerification {
|
||||
err = utils.TriggerWebhooks(c, events.UserCreate, admin.FromUserModel(newUser))
|
||||
if err != nil {
|
||||
c.Logger().Warn(err)
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, newUserDto)
|
||||
})
|
||||
|
||||
@ -40,7 +40,7 @@ func (s *webhookSuite) TestWebhookHandler_List() {
|
||||
Hooks: config.Webhooks{
|
||||
config.Webhook{
|
||||
Callback: "http://lorem",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserDelete},
|
||||
},
|
||||
config.Webhook{
|
||||
Callback: "http://ipsum",
|
||||
@ -62,7 +62,7 @@ func (s *webhookSuite) TestWebhookHandler_List() {
|
||||
err = json.Unmarshal(rec.Body.Bytes(), &dto)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Equal(3, len(dto.Database))
|
||||
s.Equal(5, len(dto.Database))
|
||||
s.Equal(2, len(dto.Config))
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ func (s *webhookSuite) TestWebhookHandler_Create() {
|
||||
testBody := admin.CreateWebhookRequestDto{
|
||||
Callback: "http://lorem",
|
||||
Events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
}
|
||||
testBodyJson, err := json.Marshal(testBody)
|
||||
@ -121,24 +121,24 @@ func (s *webhookSuite) TestWebhookHandler_CreateWithParams() {
|
||||
{
|
||||
name: "success",
|
||||
callback: "http://lorem.ipsum",
|
||||
events: events.Events{events.User},
|
||||
events: events.Events{events.UserDelete},
|
||||
expectedStatus: http.StatusCreated,
|
||||
},
|
||||
{
|
||||
name: "empty callback",
|
||||
callback: "",
|
||||
events: events.Events{events.User},
|
||||
events: events.Events{events.UserDelete},
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing callback",
|
||||
events: events.Events{events.User},
|
||||
events: events.Events{events.UserDelete},
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "wrong callback",
|
||||
callback: "lorem",
|
||||
events: events.Events{events.User},
|
||||
events: events.Events{events.UserDelete},
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
@ -223,7 +223,7 @@ func (s *webhookSuite) TestWebhookHandler_Delete() {
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Nil(entry)
|
||||
s.Equal(2, len(list))
|
||||
s.Equal(4, len(list))
|
||||
|
||||
err = e.Close()
|
||||
s.Require().NoError(err)
|
||||
@ -409,7 +409,7 @@ func (s *webhookSuite) TestWebhookHandler_Update() {
|
||||
CreateWebhookRequestDto: admin.CreateWebhookRequestDto{
|
||||
Callback: "https://ipsum.magna/lorem",
|
||||
Events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
},
|
||||
Enabled: true,
|
||||
@ -470,7 +470,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusOK,
|
||||
@ -480,7 +480,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da7",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusNotFound,
|
||||
@ -490,7 +490,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusNotFound,
|
||||
@ -500,7 +500,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "lorem",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
@ -509,7 +509,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
name: "missing ID",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusNotFound,
|
||||
@ -519,7 +519,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4",
|
||||
callback: "",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
@ -529,7 +529,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4",
|
||||
callback: "lorem",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
@ -538,7 +538,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
name: "missing Callback",
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
enabled: true,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
@ -563,7 +563,7 @@ func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {
|
||||
testId: "8b00da9a-cacf-45ea-b25d-c1ce0f0d7da4",
|
||||
callback: "https://lorem.ipsum.et",
|
||||
events: events.Events{
|
||||
events.User,
|
||||
events.UserDelete,
|
||||
},
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
@ -31,13 +31,6 @@ func (w *Webhook) Validate(tx *pop.Connection) (*validate.Errors, error) {
|
||||
&validators.UUIDIsPresent{Name: "ID", Field: w.ID},
|
||||
&validators.StringIsPresent{Name: "Callback", Field: w.Callback},
|
||||
&validators.TimeIsPresent{Name: "ExpiresAt", Field: w.ExpiresAt},
|
||||
&validators.TimeAfterTime{
|
||||
FirstName: "Expires At",
|
||||
FirstTime: w.ExpiresAt,
|
||||
SecondName: "Now",
|
||||
SecondTime: time.Now(),
|
||||
},
|
||||
|
||||
&validators.TimeIsPresent{Name: "UpdatedAt", Field: w.UpdatedAt},
|
||||
&validators.TimeIsPresent{Name: "CreatedAt", Field: w.CreatedAt},
|
||||
), nil
|
||||
|
||||
14
backend/test/fixtures/webhooks/webhooks.yaml
vendored
14
backend/test/fixtures/webhooks/webhooks.yaml
vendored
@ -19,3 +19,17 @@
|
||||
expires_at: 2220-12-31 23:59:59
|
||||
created_at: 2020-12-31 23:59:59
|
||||
updated_at: 2020-12-31 23:59:59
|
||||
- id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3
|
||||
callback: http://localhost
|
||||
enabled: 1
|
||||
failures: 0
|
||||
expires_at: 2020-12-31 23:59:59
|
||||
created_at: 2020-12-31 23:59:59
|
||||
updated_at: 2020-12-31 23:59:59
|
||||
- id: 8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2
|
||||
callback: http://localhost
|
||||
enabled: 1
|
||||
failures: 5
|
||||
expires_at: 2020-12-31 23:59:59
|
||||
created_at: 2020-12-31 23:59:59
|
||||
updated_at: 2020-12-31 23:59:59
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"github.com/teamhanko/hanko/backend/persistence/models"
|
||||
)
|
||||
|
||||
// Deprecated: NewPersister is deprecated. User test.Suite instead
|
||||
func NewPersister(
|
||||
user []models.User,
|
||||
passcodes []models.Passcode,
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
func TestNewConfigHook(t *testing.T) {
|
||||
hook := config.Webhook{
|
||||
Callback: "http://lorem.ipsum",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
cfgHook := NewConfigHook(hook, nil)
|
||||
@ -23,7 +23,7 @@ func TestConfigHook_DisableOnExpiryDate(t *testing.T) {
|
||||
now := time.Now()
|
||||
hook := config.Webhook{
|
||||
Callback: "http://lorem.ipsum",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
dbHook := NewConfigHook(hook, nil)
|
||||
@ -34,7 +34,7 @@ func TestConfigHook_DisableOnExpiryDate(t *testing.T) {
|
||||
func TestConfigHook_DisableOnFailure(t *testing.T) {
|
||||
hook := config.Webhook{
|
||||
Callback: "http://lorem.ipsum",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
dbHook := NewConfigHook(hook, nil)
|
||||
@ -45,7 +45,7 @@ func TestConfigHook_DisableOnFailure(t *testing.T) {
|
||||
func TestConfigHook_Reset(t *testing.T) {
|
||||
hook := config.Webhook{
|
||||
Callback: "http://lorem.ipsum",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
dbHook := NewConfigHook(hook, nil)
|
||||
@ -56,7 +56,7 @@ func TestConfigHook_Reset(t *testing.T) {
|
||||
func TestConfigHook_IsEnabled(t *testing.T) {
|
||||
hook := config.Webhook{
|
||||
Callback: "http://lorem.ipsum",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
dbHook := NewConfigHook(hook, nil)
|
||||
|
||||
@ -2,322 +2,134 @@ package webhooks
|
||||
|
||||
import (
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/teamhanko/hanko/backend/persistence"
|
||||
"github.com/teamhanko/hanko/backend/persistence/models"
|
||||
"github.com/teamhanko/hanko/backend/test"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewDatabaseHook(t *testing.T) {
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
func TestDatabaseHookSuite(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, new(databaseHookSuite))
|
||||
}
|
||||
|
||||
type databaseHookSuite struct {
|
||||
test.Suite
|
||||
}
|
||||
|
||||
func (s *databaseHookSuite) TestNewDatabaseHook() {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: false,
|
||||
Failures: 0,
|
||||
ExpiresAt: time.Now().Add(24 * -1 * time.Hour),
|
||||
ExpiresAt: time.Now().Add(WebhookExpireDuration),
|
||||
}
|
||||
|
||||
dbHook := NewDatabaseHook(hook, persister.GetWebhookPersister(nil), nil)
|
||||
require.NotEmpty(t, dbHook)
|
||||
dbHook := NewDatabaseHook(hook, s.Storage.GetWebhookPersister(nil), nil)
|
||||
s.NotEmpty(dbHook)
|
||||
}
|
||||
|
||||
func TestDatabaseHook_DisableOnExpiryDate(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_DisableOnExpiryDate() {
|
||||
hook, whPersister := s.loadWebhook("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3")
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
Failures: 0,
|
||||
ExpiresAt: now.Add(24 * -1 * time.Hour),
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err = dbHook.DisableOnExpiryDate(now)
|
||||
assert.NoError(t, err)
|
||||
err := dbHook.DisableOnExpiryDate(now)
|
||||
s.NoError(err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
assert.NoError(t, err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
require.False(t, updatedHook.Enabled)
|
||||
s.False(updatedHook.Enabled)
|
||||
}
|
||||
func TestDatabaseHook_DoNotDisableOnExpiryDate(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_DoNotDisableOnExpiryDate() {
|
||||
hook, whPersister := s.loadWebhook("a47fe92a-1e4b-4119-8653-55ad82737c88")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
Failures: 0,
|
||||
ExpiresAt: now.Add(24 * 1 * time.Hour),
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err = dbHook.DisableOnExpiryDate(now)
|
||||
assert.NoError(t, err)
|
||||
err := dbHook.DisableOnExpiryDate(now)
|
||||
s.NoError(err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
assert.NoError(t, err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
require.True(t, updatedHook.Enabled)
|
||||
s.True(updatedHook.Enabled)
|
||||
}
|
||||
|
||||
func TestDatabaseHook_DisableOnFailure(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
Failures: FailureExpireRate,
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_DisableOnFailure() {
|
||||
hook, whPersister := s.loadWebhook("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err = dbHook.DisableOnFailure()
|
||||
assert.NoError(t, err)
|
||||
err := dbHook.DisableOnFailure()
|
||||
s.Require().NoError(err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
assert.NoError(t, err)
|
||||
s.NoError(err)
|
||||
|
||||
require.False(t, updatedHook.Enabled)
|
||||
s.False(updatedHook.Enabled)
|
||||
}
|
||||
|
||||
func TestDatabaseHook_DoNotDisableOnFailure(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
Failures: 0,
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_DoNotDisableOnFailure() {
|
||||
hook, whPersister := s.loadWebhook("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da3")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err = dbHook.DisableOnFailure()
|
||||
assert.NoError(t, err)
|
||||
err := dbHook.DisableOnFailure()
|
||||
s.NoError(err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
assert.NoError(t, err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
require.True(t, updatedHook.Enabled)
|
||||
s.True(updatedHook.Enabled)
|
||||
}
|
||||
|
||||
func TestDatabaseHook_Reset(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_Reset() {
|
||||
hook, whPersister := s.loadWebhook("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da2")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err := dbHook.Reset()
|
||||
s.NoError(err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Less(updatedHook.Failures, hook.Failures, "Failures should be reset to 0")
|
||||
s.Equal(0, updatedHook.Failures)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
Failures: 3,
|
||||
s.True(updatedHook.ExpiresAt.After(now))
|
||||
s.True(updatedHook.UpdatedAt.After(now))
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
err = dbHook.Reset()
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedHook, err := whPersister.Get(hook.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
require.Less(t, updatedHook.Failures, hook.Failures, "Failures should be reset to 0")
|
||||
require.Equal(t, 0, updatedHook.Failures)
|
||||
|
||||
require.True(t, updatedHook.ExpiresAt.After(now))
|
||||
require.True(t, updatedHook.UpdatedAt.After(now))
|
||||
}
|
||||
|
||||
func TestDatabaseHook_IsEnabled(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_IsEnabled() {
|
||||
hook, whPersister := s.loadWebhook("a47fe92a-1e4b-4119-8653-55ad82737c88")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
|
||||
require.True(t, dbHook.IsEnabled())
|
||||
s.True(dbHook.IsEnabled())
|
||||
}
|
||||
|
||||
func TestDatabaseHook_IsDisabled(t *testing.T) {
|
||||
hookId, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
hook := models.Webhook{
|
||||
ID: hookId,
|
||||
Enabled: false,
|
||||
}
|
||||
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{hook},
|
||||
nil,
|
||||
)
|
||||
|
||||
whPersister := persister.GetWebhookPersister(nil)
|
||||
func (s *databaseHookSuite) TestDatabaseHook_IsDisabled() {
|
||||
hook, whPersister := s.loadWebhook("279beae1-8a6d-4eaf-a791-1fa79d21d37a")
|
||||
|
||||
dbHook := NewDatabaseHook(hook, whPersister, nil)
|
||||
|
||||
require.False(t, dbHook.IsEnabled())
|
||||
s.False(dbHook.IsEnabled())
|
||||
}
|
||||
|
||||
func (s *databaseHookSuite) loadWebhook(hookId string) (models.Webhook, persistence.WebhookPersister) {
|
||||
err := s.LoadFixtures("../test/fixtures/webhooks")
|
||||
s.Require().NoError(err)
|
||||
|
||||
whPersister := s.Storage.GetWebhookPersister(nil)
|
||||
hook, err := whPersister.Get(uuid.FromStringOrNil(hookId))
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotEmpty(hook)
|
||||
|
||||
return *hook, whPersister
|
||||
}
|
||||
|
||||
@ -5,11 +5,9 @@ import "github.com/teamhanko/hanko/backend/persistence/models"
|
||||
type Event string
|
||||
|
||||
const (
|
||||
User Event = "user"
|
||||
UserCreate Event = "user.create"
|
||||
UserUpdate Event = "user.update"
|
||||
UserDelete Event = "user.delete"
|
||||
Email Event = "user.update.email"
|
||||
EmailCreate Event = "user.update.email.create"
|
||||
EmailPrimary Event = "user.update.email.primary"
|
||||
EmailDelete Event = "user.update.email.delete"
|
||||
@ -23,7 +21,7 @@ func StringIsValidEvent(value string) bool {
|
||||
func IsValidEvent(evt Event) bool {
|
||||
var isValid bool
|
||||
switch evt {
|
||||
case User, UserCreate, UserUpdate, UserDelete, Email, EmailCreate, EmailPrimary, EmailDelete:
|
||||
case "user", "user.update.email", UserCreate, UserUpdate, UserDelete, EmailCreate, EmailPrimary, EmailDelete:
|
||||
isValid = true
|
||||
default:
|
||||
isValid = false
|
||||
|
||||
@ -2,9 +2,9 @@ package webhooks
|
||||
|
||||
import (
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/teamhanko/hanko/backend/config"
|
||||
"github.com/teamhanko/hanko/backend/persistence"
|
||||
"github.com/teamhanko/hanko/backend/persistence/models"
|
||||
"github.com/teamhanko/hanko/backend/test"
|
||||
"github.com/teamhanko/hanko/backend/webhooks/events"
|
||||
@ -14,106 +14,68 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NotEmpty(t, manager)
|
||||
func TestManagerSuite(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, new(managerSuite))
|
||||
}
|
||||
|
||||
func TestManager_GenerateJWT(t *testing.T) {
|
||||
type managerSuite struct {
|
||||
test.Suite
|
||||
}
|
||||
|
||||
func (s *managerSuite) TestNewManager() {
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
manager, err := NewManager(&cfg, s.Storage.GetWebhookPersister(nil), jwkManager, nil)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(manager)
|
||||
}
|
||||
|
||||
func (s *managerSuite) TestManager_GenerateJWT() {
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
|
||||
manager, err := NewManager(&cfg, s.Storage.GetWebhookPersister(nil), jwkManager, nil)
|
||||
|
||||
testData := "lorem-ipsum"
|
||||
|
||||
dataToken, err := manager.GenerateJWT(testData, events.User)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, dataToken)
|
||||
dataToken, err := manager.GenerateJWT(testData, events.UserCreate)
|
||||
s.NoError(err)
|
||||
s.NotEmpty(dataToken)
|
||||
}
|
||||
|
||||
func TestManager_TriggerWithoutHook(t *testing.T) {
|
||||
func (s *managerSuite) TestManager_TriggerWithoutHook() {
|
||||
triggered := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Fail(t, "no hook should not trigger a http request")
|
||||
triggered = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
manager, err := NewManager(&cfg, s.Storage.GetWebhookPersister(nil), jwkManager, nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
manager.Trigger(events.User, "lorem-ipsum")
|
||||
manager.Trigger(events.UserCreate, "lorem-ipsum")
|
||||
|
||||
// give it 1 sec to trigger
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
s.False(triggered)
|
||||
}
|
||||
func TestManager_TriggerWithConfigHook(t *testing.T) {
|
||||
func (s *managerSuite) TestManager_TriggerWithConfigHook() {
|
||||
triggered := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.True(t, true)
|
||||
triggered = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hooks := config.Webhooks{config.Webhook{
|
||||
Callback: server.URL,
|
||||
Events: events.Events{
|
||||
events.User,
|
||||
events.UserCreate,
|
||||
},
|
||||
}}
|
||||
|
||||
@ -125,43 +87,28 @@ func TestManager_TriggerWithConfigHook(t *testing.T) {
|
||||
}
|
||||
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
manager, err := NewManager(&cfg, s.Storage.GetWebhookPersister(nil), jwkManager, nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
manager.Trigger(events.User, "lorem-ipsum")
|
||||
manager.Trigger(events.UserCreate, "lorem-ipsum")
|
||||
|
||||
// give it 1 sec to trigger
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
s.True(triggered)
|
||||
}
|
||||
|
||||
func TestManager_TriggerWithDisabledConfigHook(t *testing.T) {
|
||||
func (s *managerSuite) TestManager_TriggerWithDisabledConfigHook() {
|
||||
triggered := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Fail(t, "no hook should not trigger a http request")
|
||||
triggered = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hooks := config.Webhooks{config.Webhook{
|
||||
Callback: server.URL,
|
||||
Events: events.Events{
|
||||
events.User,
|
||||
events.UserCreate,
|
||||
},
|
||||
}}
|
||||
|
||||
@ -173,151 +120,87 @@ func TestManager_TriggerWithDisabledConfigHook(t *testing.T) {
|
||||
}
|
||||
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
manager, err := NewManager(&cfg, s.Storage.GetWebhookPersister(nil), jwkManager, nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
manager.Trigger(events.User, "lorem-ipsum")
|
||||
manager.Trigger(events.UserCreate, "lorem-ipsum")
|
||||
|
||||
// give it 1 sec to trigger
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
s.False(triggered)
|
||||
}
|
||||
|
||||
func TestManager_TriggerWithDbHook(t *testing.T) {
|
||||
func (s *managerSuite) TestManager_TriggerWithDbHook() {
|
||||
triggered := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.True(t, true)
|
||||
triggered = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hookUuid, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
eventUuid, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{
|
||||
models.Webhook{
|
||||
ID: hookUuid,
|
||||
Callback: server.URL,
|
||||
Enabled: true,
|
||||
Failures: 0,
|
||||
ExpiresAt: time.Now(),
|
||||
WebhookEvents: models.WebhookEvents{
|
||||
models.WebhookEvent{
|
||||
ID: eventUuid,
|
||||
Webhook: nil,
|
||||
WebhookID: hookUuid,
|
||||
Event: string(events.User),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
persister := s.Storage.GetWebhookPersister(nil)
|
||||
|
||||
manager.Trigger(events.User, "lorem-ipsum")
|
||||
s.createTestDatabaseWebhook(persister, true, server.URL)
|
||||
|
||||
manager, err := NewManager(&cfg, persister, jwkManager, nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
manager.Trigger(events.UserCreate, "lorem-ipsum")
|
||||
|
||||
// give it 1 sec to trigger
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
s.True(triggered)
|
||||
}
|
||||
|
||||
func TestManager_TriggerWithDisabledDbHook(t *testing.T) {
|
||||
func (s *managerSuite) TestManager_TriggerWithDisabledDbHook() {
|
||||
triggered := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Fail(t, "no hook should not trigger a http request")
|
||||
triggered = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hookUuid, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
eventUuid, err := uuid.NewV4()
|
||||
assert.NoError(t, err)
|
||||
|
||||
cfg := config.Config{}
|
||||
jwkManager := test.JwkManager{}
|
||||
persister := test.NewPersister(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
models.Webhooks{
|
||||
models.Webhook{
|
||||
ID: hookUuid,
|
||||
Callback: server.URL,
|
||||
Enabled: false,
|
||||
Failures: 0,
|
||||
ExpiresAt: time.Now(),
|
||||
WebhookEvents: models.WebhookEvents{
|
||||
models.WebhookEvent{
|
||||
ID: eventUuid,
|
||||
Webhook: nil,
|
||||
WebhookID: hookUuid,
|
||||
Event: string(events.User),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
persister := s.Storage.GetWebhookPersister(nil)
|
||||
|
||||
manager, err := NewManager(&cfg, persister.GetWebhookPersister(nil), jwkManager, nil)
|
||||
assert.NoError(t, err)
|
||||
s.createTestDatabaseWebhook(persister, false, server.URL)
|
||||
|
||||
manager.Trigger(events.User, "lorem-ipsum")
|
||||
manager, err := NewManager(&cfg, persister, jwkManager, nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
manager.Trigger(events.UserCreate, "lorem-ipsum")
|
||||
|
||||
// give it 1 sec to trigger
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
s.False(triggered)
|
||||
}
|
||||
|
||||
func (s *managerSuite) createTestDatabaseWebhook(persister persistence.WebhookPersister, isEnabled bool, callback string) {
|
||||
now := time.Now()
|
||||
hookId := uuid.FromStringOrNil("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da1")
|
||||
err := persister.Create(
|
||||
models.Webhook{
|
||||
ID: hookId,
|
||||
Callback: callback,
|
||||
Enabled: isEnabled,
|
||||
Failures: 0,
|
||||
ExpiresAt: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
models.WebhookEvents{
|
||||
models.WebhookEvent{
|
||||
ID: uuid.FromStringOrNil("8b00da9a-cacf-45ea-b25d-c1ce0f0d7da0"),
|
||||
WebhookID: hookId,
|
||||
Event: string(events.UserCreate),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
@ -2,7 +2,11 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/teamhanko/hanko/backend/dto/admin"
|
||||
"github.com/teamhanko/hanko/backend/persistence"
|
||||
"github.com/teamhanko/hanko/backend/webhooks"
|
||||
"github.com/teamhanko/hanko/backend/webhooks/events"
|
||||
)
|
||||
@ -19,3 +23,16 @@ func TriggerWebhooks(ctx echo.Context, evt events.Event, data interface{}) error
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func NotifyUserChange(ctx echo.Context, tx *pop.Connection, persister persistence.Persister, event events.Event, userId uuid.UUID) {
|
||||
updatedUser, err := persister.GetUserPersisterWithConnection(tx).Get(userId)
|
||||
if err != nil {
|
||||
ctx.Logger().Warn(fmt.Errorf("failed to fetch updated user: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = TriggerWebhooks(ctx, event, admin.FromUserModel(*updatedUser))
|
||||
if err != nil {
|
||||
ctx.Logger().Warn(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func TestWebhook_TriggerWithoutManager(t *testing.T) {
|
||||
|
||||
ctx := e.NewContext(req, rec)
|
||||
|
||||
err := TriggerWebhooks(ctx, events.User, "lorem")
|
||||
err := TriggerWebhooks(ctx, "user", "lorem")
|
||||
require.Error(t, err)
|
||||
|
||||
err = e.Close()
|
||||
@ -47,7 +47,7 @@ func TestWebhook_Trigger(t *testing.T) {
|
||||
ctx := e.NewContext(req, rec)
|
||||
ctx.Set("webhook_manager", tm)
|
||||
|
||||
err := TriggerWebhooks(ctx, events.User, "lorem")
|
||||
err := TriggerWebhooks(ctx, "user", "lorem")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = e.Close()
|
||||
|
||||
@ -14,17 +14,17 @@ func TestBaseWebhook_HasEvent(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: nil,
|
||||
Callback: "http://ipsum.lorem",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserUpdate},
|
||||
}
|
||||
|
||||
require.True(t, baseHook.HasEvent(events.User))
|
||||
require.True(t, baseHook.HasEvent(events.EmailCreate))
|
||||
}
|
||||
|
||||
func TestBaseWebhook_HasSubEvent(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: nil,
|
||||
Callback: "http://ipsum.lorem",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
require.True(t, baseHook.HasEvent(events.UserCreate))
|
||||
@ -37,7 +37,7 @@ func TestBaseWebhook_DoesNotHaveEvent(t *testing.T) {
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
require.False(t, baseHook.HasEvent(events.User))
|
||||
require.False(t, baseHook.HasEvent("user"))
|
||||
}
|
||||
|
||||
func TestBaseWebhook_Trigger(t *testing.T) {
|
||||
@ -49,12 +49,12 @@ func TestBaseWebhook_Trigger(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: nil,
|
||||
Callback: server.URL,
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
data := JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: "user",
|
||||
}
|
||||
|
||||
err := baseHook.Trigger(data)
|
||||
@ -65,12 +65,12 @@ func TestBaseWebhook_TriggerWithWrongUrl(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: log.New("test"),
|
||||
Callback: "http://broken!",
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
data := JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: "user",
|
||||
}
|
||||
|
||||
err := baseHook.Trigger(data)
|
||||
@ -87,12 +87,12 @@ func TestBaseWebhook_TriggerWithBadStatusCode(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: log.New("test"),
|
||||
Callback: server.URL,
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
data := JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: "user",
|
||||
}
|
||||
|
||||
err := baseHook.Trigger(data)
|
||||
@ -112,12 +112,12 @@ func TestBaseWebhook_TriggerWithBadServer(t *testing.T) {
|
||||
baseHook := BaseWebhook{
|
||||
Logger: log.New("test"),
|
||||
Callback: server.URL,
|
||||
Events: events.Events{events.User},
|
||||
Events: events.Events{events.UserCreate},
|
||||
}
|
||||
|
||||
data := JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: "user",
|
||||
}
|
||||
|
||||
err := baseHook.Trigger(data)
|
||||
|
||||
@ -66,7 +66,7 @@ func TestWorker_RunJob(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -101,7 +101,7 @@ func TestWorker_RunJobWithError(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -126,7 +126,7 @@ func TestWorker_TriggerWebhook(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -162,7 +162,7 @@ func TestWorker_TriggerWebhookWithExpireError(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -182,7 +182,7 @@ func TestWorker_TriggerWebhookIgnoreDisabledJob(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -206,7 +206,7 @@ func TestWorker_TriggerWebhookTriggerWithError(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -238,7 +238,7 @@ func TestWorker_TriggerWebhookDisableOnFailure(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
@ -269,7 +269,7 @@ func TestWorker_TriggerWebhookResetError(t *testing.T) {
|
||||
job := Job{
|
||||
Data: JobData{
|
||||
Token: "test-token",
|
||||
Event: events.User,
|
||||
Event: events.UserCreate,
|
||||
},
|
||||
|
||||
Hook: &TestHook{
|
||||
|
||||
Reference in New Issue
Block a user