feat: introduce hanko profile element and related api changes (#495)

* feat: introduce hanko profile element and related api changes
This commit is contained in:
bjoern-m
2023-01-25 10:55:23 +01:00
committed by GitHub
parent 93ad9c4056
commit ca62cf421f
254 changed files with 13765 additions and 3955 deletions

View File

@ -81,6 +81,52 @@ func (h *PasscodeHandler) Init(c echo.Context) error {
return dto.NewHTTPError(http.StatusBadRequest).SetInternal(errors.New("user not found"))
}
var emailId uuid.UUID
if body.EmailId != nil {
emailId, err = uuid.FromString(*body.EmailId)
if err != nil {
return dto.NewHTTPError(http.StatusBadRequest, "failed to parse emailId as uuid").SetInternal(err)
}
}
// Determine where to send the passcode
var email *models.Email
if !emailId.IsNil() {
// Send the passcode to the specified email address
email, err = h.persister.GetEmailPersister().Get(emailId)
if email == nil {
return dto.NewHTTPError(http.StatusBadRequest, "the specified emailId is not available")
}
} else if e := user.Emails.GetPrimary(); e == nil {
// Workaround to support hanko element versions before v0.1.0-alpha:
// If user has no primary email, check if a cookie with an email id is present
emailIdCookie, err := c.Cookie("hanko_email_id")
if err != nil {
return fmt.Errorf("failed to get email id cookie: %w", err)
}
if emailIdCookie != nil && emailIdCookie.Value != "" {
emailId, err = uuid.FromString(emailIdCookie.Value)
if err != nil {
return dto.NewHTTPError(http.StatusBadRequest, "failed to parse emailId as uuid").SetInternal(err)
}
email, err = h.persister.GetEmailPersister().Get(emailId)
if email == nil {
return dto.NewHTTPError(http.StatusBadRequest, "the specified emailId is not available")
}
} else {
// Can't determine email address to which the passcode should be sent to
return dto.NewHTTPError(http.StatusBadRequest, "an emailId needs to be specified")
}
} else {
// Send the passcode to the primary email address
email = e
}
if email.User != nil && email.User.ID.String() != user.ID.String() {
return dto.NewHTTPError(http.StatusForbidden).SetInternal(errors.New("email address is assigned to another user"))
}
passcode, err := h.passcodeGenerator.Generate()
if err != nil {
return fmt.Errorf("failed to generate passcode: %w", err)
@ -98,6 +144,7 @@ func (h *PasscodeHandler) Init(c echo.Context) error {
passcodeModel := models.Passcode{
ID: passcodeId,
UserId: userId,
EmailID: email.ID,
Ttl: h.TTL,
Code: string(hashedPasscode),
CreatedAt: now,
@ -123,7 +170,7 @@ func (h *PasscodeHandler) Init(c echo.Context) error {
}
message := gomail.NewMessage()
message.SetAddressHeader("To", user.Email, "")
message.SetAddressHeader("To", email.Address, "")
message.SetAddressHeader("From", h.emailConfig.FromAddress, h.emailConfig.FromName)
message.SetHeader("Subject", h.renderer.Translate(lang, "email_subject_login", data))
@ -168,6 +215,8 @@ func (h *PasscodeHandler) Finish(c echo.Context) error {
transactionError := h.persister.Transaction(func(tx *pop.Connection) error {
passcodePersister := h.persister.GetPasscodePersisterWithConnection(tx)
userPersister := h.persister.GetUserPersisterWithConnection(tx)
emailPersister := h.persister.GetEmailPersisterWithConnection(tx)
primaryEmailPersister := h.persister.GetPrimaryEmailPersisterWithConnection(tx)
passcode, err := passcodePersister.Get(passcodeId)
if err != nil {
return fmt.Errorf("failed to get passcode: %w", err)
@ -231,11 +280,38 @@ func (h *PasscodeHandler) Finish(c echo.Context) error {
return fmt.Errorf("failed to delete passcode: %w", err)
}
if !user.Verified {
user.Verified = true
err = userPersister.Update(*user)
if passcode.Email.User != nil && passcode.Email.User.ID.String() != user.ID.String() {
return dto.NewHTTPError(http.StatusForbidden, "email address has been claimed by another user")
}
if !passcode.Email.Verified {
// Update email verified status and assign the email address to the user.
passcode.Email.Verified = true
passcode.Email.UserID = &user.ID
err = emailPersister.Update(passcode.Email)
if err != nil {
return fmt.Errorf("failed to update user: %w", err)
return fmt.Errorf("failed to update the email verified status: %w", err)
}
if user.Emails.GetPrimary() == nil {
primaryEmail := models.NewPrimaryEmail(passcode.Email.ID, user.ID)
err = primaryEmailPersister.Create(*primaryEmail)
if err != nil {
return fmt.Errorf("failed to create primary email: %w", err)
}
user.Emails = models.Emails{passcode.Email}
user.Emails.SetPrimary(primaryEmail)
err = h.auditLogger.Create(c, models.AuditLogPrimaryEmailChanged, user, nil)
if err != nil {
return fmt.Errorf("failed to create audit log: %w", err)
}
}
err = h.auditLogger.Create(c, models.AuditLogEmailVerified, user, nil)
if err != nil {
return fmt.Errorf("failed to create audit log: %w", err)
}
}