Files
warmbly/internal/api/handler/email_onboarding.go
T

186 lines
4.7 KiB
Go

package handler
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/api/middleware"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/models"
)
// OnboardingOAuthStartRequest starts an OAuth round trip for a Gmail or Outlook account.
type OnboardingOAuthStartRequest struct {
Provider string `json:"provider"`
}
// OnboardingOAuthFinishRequest carries the authorization code + state back from the provider.
type OnboardingOAuthFinishRequest struct {
Code string `json:"code"`
State string `json:"state"`
}
// OnboardingSMTPIMAPRequest connects an SMTP/IMAP mailbox in a single call.
type OnboardingSMTPIMAPRequest struct {
Email string `json:"email"`
Name string `json:"name"`
SMTP *models.Service `json:"smtp"`
IMAP *models.Service `json:"imap"`
}
func (h *Handler) StartEmailOAuth(c *gin.Context) {
userID := middleware.GetUserID(c)
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.Handle(c, errx.ErrNoOrganization)
return
}
var req OnboardingOAuthStartRequest
if err := c.ShouldBindJSON(&req); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
resp, xerr := h.EmailService.OAuthStart(c.Request.Context(), userID, orgID, models.InboxProvider(req.Provider))
if xerr != nil {
errx.Handle(c, xerr)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) FinishEmailOAuth(c *gin.Context) {
userIDStr := middleware.GetUserID(c)
var req OnboardingOAuthFinishRequest
if err := c.ShouldBindJSON(&req); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
acc, reauthed, xerr := h.EmailService.OAuthFinish(c.Request.Context(), userIDStr, req.Code, req.State)
if xerr != nil {
errx.Handle(c, xerr)
return
}
// A reauth round trip updated an existing mailbox rather than creating one.
action, status := models.AuditActionConnect, http.StatusCreated
if reauthed {
action, status = models.AuditActionUpdate, http.StatusOK
}
h.auditOrg(c, action, models.AuditEntityEmailAccount, &acc.ID, nil, map[string]string{
"provider": acc.Provider,
"email": acc.Email,
})
c.JSON(status, acc)
}
// ReauthEmailOAuth starts an OAuth round trip that renews the tokens of an
// existing mailbox after the provider invalidated them (issue #274). The
// finish leg is the ordinary FinishEmailOAuth.
func (h *Handler) ReauthEmailOAuth(c *gin.Context) {
userID := middleware.GetUserID(c)
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.Handle(c, errx.ErrNoOrganization)
return
}
id, err := uuid.Parse(c.Param("id"))
if err != nil {
errx.Handle(c, errx.ErrUuid)
return
}
resp, xerr := h.EmailService.OAuthReauth(c.Request.Context(), userID, orgID, id)
if xerr != nil {
errx.Handle(c, xerr)
return
}
c.JSON(http.StatusOK, resp)
}
// OnboardingSMTPIMAPCredentials carries replacement credentials for an
// existing SMTP/IMAP mailbox; the account fields never change on a reauth.
type OnboardingSMTPIMAPCredentials struct {
SMTP *models.Service `json:"smtp"`
IMAP *models.Service `json:"imap"`
}
// UpdateEmailSMTPIMAP replaces an SMTP/IMAP mailbox's credentials after a
// password change, validating them live before storing, and reactivates it.
func (h *Handler) UpdateEmailSMTPIMAP(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.Handle(c, errx.ErrNoOrganization)
return
}
id, err := uuid.Parse(c.Param("id"))
if err != nil {
errx.Handle(c, errx.ErrUuid)
return
}
var req OnboardingSMTPIMAPCredentials
if err := c.ShouldBindJSON(&req); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
acc, xerr := h.EmailService.UpdateSMTPIMAPCredentials(c.Request.Context(), orgID, id, &models.SmtpImap{
SMTP: req.SMTP,
IMAP: req.IMAP,
})
if xerr != nil {
errx.Handle(c, xerr)
return
}
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntityEmailAccount, &acc.ID, nil, map[string]string{
"provider": "smtp_imap",
"email": acc.Email,
})
c.JSON(http.StatusOK, acc)
}
func (h *Handler) ConnectEmailSMTPIMAP(c *gin.Context) {
userIDStr := middleware.GetUserID(c)
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.Handle(c, errx.ErrNoOrganization)
return
}
var req OnboardingSMTPIMAPRequest
if err := c.ShouldBindJSON(&req); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
acc, xerr := h.EmailService.OnboardSMTPIMAP(c.Request.Context(), userIDStr, orgID, &models.NewSMTPIMAPAccount{
Email: req.Email,
Name: req.Name,
SMTP: req.SMTP,
IMAP: req.IMAP,
})
if xerr != nil {
errx.Handle(c, xerr)
return
}
h.auditOrg(c, models.AuditActionConnect, models.AuditEntityEmailAccount, &acc.ID, nil, map[string]string{
"provider": "smtp_imap",
"email": acc.Email,
})
c.JSON(http.StatusCreated, acc)
}