Files
warmbly/internal/api/middleware/auth.go
T

123 lines
3.2 KiB
Go

package middleware
import (
"strings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/models"
)
const (
UserIDKey = "user_id"
AccessTokenKey = "access_token"
SessionKey = "session"
OrganizationIDKey = "organization_id"
// SessionMemberKey holds the caller's membership in the session's workspace.
SessionMemberKey = "session_member"
)
func (h *Handler) AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
errx.Handle(c, errx.ErrAuth)
c.Abort()
return
}
token := strings.TrimPrefix(authHeader, "Bearer ")
session, err := h.TokenService.ValidateAccessToken(c.Request.Context(), token)
if err != nil {
errx.Handle(c, err)
c.Abort()
return
}
c.Set(UserIDKey, session.UserID.String())
c.Set(SessionKey, session)
c.Set(AccessTokenKey, token)
if xerr := h.setSessionOrganization(c, session); xerr != nil {
errx.JSON(c, xerr)
c.Abort()
return
}
c.Next()
}
}
// setSessionOrganization puts the session's workspace in the request only
// while the session's user is a member of it; otherwise the request has none.
func (h *Handler) setSessionOrganization(c *gin.Context, session *models.Session) *errx.Error {
if session.CurrentOrganizationID == nil {
return nil
}
orgID := *session.CurrentOrganizationID
if h.OrganizationService == nil {
c.Set(OrganizationIDKey, orgID)
return nil
}
member, xerr := h.OrganizationService.GetMembership(c.Request.Context(), orgID, session.UserID)
if xerr != nil {
return xerr
}
if member == nil {
// Clear a selection that outlived its membership, and stop the request's copy of the session naming it.
if h.TokenService != nil {
_ = h.TokenService.LeaveOrganization(c.Request.Context(), session.UserID, orgID)
}
detached := *session
detached.CurrentOrganizationID = nil
c.Set(SessionKey, &detached)
return nil
}
c.Set(OrganizationIDKey, orgID)
c.Set(SessionMemberKey, member)
return nil
}
// memberHasPermission answers from the membership resolved at authentication
// when it is for orgID, and from the database otherwise.
func (h *Handler) memberHasPermission(c *gin.Context, orgID, userID uuid.UUID, perm models.OrganizationPermission) (bool, *errx.Error) {
if v, ok := c.Get(SessionMemberKey); ok {
if m, ok := v.(*models.OrganizationMember); ok && m.OrganizationID == orgID && m.UserID == userID {
return m.HasPermission(perm), nil
}
}
return h.OrganizationService.HasPermission(c.Request.Context(), orgID, userID, perm)
}
func GetUserID(c *gin.Context) string {
return c.GetString(UserIDKey)
}
func GetUserUUID(c *gin.Context) (uuid.UUID, error) {
return uuid.Parse(c.GetString(UserIDKey))
}
func GetAccessToken(c *gin.Context) string {
return c.GetString(AccessTokenKey)
}
func GetSession(c *gin.Context) *models.Session {
if session, exists := c.Get(SessionKey); exists {
if s, ok := session.(*models.Session); ok {
return s
}
}
return nil
}
func GetOrganizationID(c *gin.Context) *uuid.UUID {
if orgID, exists := c.Get(OrganizationIDKey); exists {
if id, ok := orgID.(uuid.UUID); ok {
return &id
}
}
return nil
}