mirror of
https://github.com/hanzoui/registry-backend.git
synced 2026-10-30 20:30:45 +00:00
238 lines
8.9 KiB
Go
238 lines
8.9 KiB
Go
package drip_authorization
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"registry-backend/drip"
|
|
"registry-backend/ent"
|
|
"registry-backend/ent/schema"
|
|
drip_authentication "registry-backend/server/middleware/authentication"
|
|
drip_services "registry-backend/services/registry"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/newrelic/go-agent/v3/newrelic"
|
|
strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Assertor interface {
|
|
AssertPublisherBanned(ctx context.Context, client *ent.Client, publisherID string) error
|
|
AssertPublisherPermissions(ctx context.Context,
|
|
client *ent.Client,
|
|
publisherID string,
|
|
userID string,
|
|
permissions []schema.PublisherPermissionType) (err error)
|
|
IsPersonalAccessTokenValidForPublisher(ctx context.Context,
|
|
client *ent.Client,
|
|
publisherID string,
|
|
accessToken string,
|
|
) (bool, error)
|
|
AssertNodeBelongsToPublisher(ctx context.Context, client *ent.Client, publisherID string, nodeID string) error
|
|
AssertAccessTokenBelongsToPublisher(ctx context.Context, client *ent.Client, publisherID string, tokenId uuid.UUID) error
|
|
AssertNodeBanned(ctx context.Context, client *ent.Client, nodeID string) error
|
|
}
|
|
|
|
// AuthorizationManager manages authorization-related tasks
|
|
type AuthorizationManager struct {
|
|
EntClient *ent.Client
|
|
Assertor Assertor
|
|
NewRelicApp *newrelic.Application
|
|
}
|
|
|
|
// NewAuthorizationManager creates a new instance of AuthorizationManager
|
|
func NewAuthorizationManager(
|
|
entClient *ent.Client, assertor Assertor, newRelicApp *newrelic.Application) *AuthorizationManager {
|
|
return &AuthorizationManager{
|
|
EntClient: entClient,
|
|
Assertor: assertor,
|
|
NewRelicApp: newRelicApp,
|
|
}
|
|
}
|
|
|
|
// assertUserBanned checks if the user is banned
|
|
func (m *AuthorizationManager) assertUserBanned() drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertUserBanned")
|
|
defer segment.End()
|
|
}
|
|
v := ctx.Value(drip_authentication.UserContextKey)
|
|
userDetails, ok := v.(*drip_authentication.UserDetails)
|
|
if !ok {
|
|
return nil, echo.NewHTTPError(http.StatusUnauthorized, "user not found")
|
|
}
|
|
|
|
u, err := m.EntClient.User.Get(ctx, userDetails.ID)
|
|
if err != nil {
|
|
return nil, echo.NewHTTPError(http.StatusUnauthorized, "user not found")
|
|
}
|
|
|
|
if u.Status == schema.UserStatusTypeBanned {
|
|
return nil, echo.NewHTTPError(http.StatusForbidden, "user/publisher is banned")
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assertPublisherPermission checks if the user has the required permissions for the publisher
|
|
func (m *AuthorizationManager) assertPublisherPermission(
|
|
permissions []schema.PublisherPermissionType, extractor func(req interface{}) string) drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertPublisherPermission")
|
|
defer segment.End()
|
|
}
|
|
v := ctx.Value(drip_authentication.UserContextKey)
|
|
userDetails, ok := v.(*drip_authentication.UserDetails)
|
|
if !ok {
|
|
return nil, echo.NewHTTPError(http.StatusUnauthorized, "user not found")
|
|
}
|
|
publisherID := extractor(request)
|
|
|
|
log.Ctx(ctx).Info().Msgf("Checking if user ID %s has permission "+
|
|
"to update publisher ID %s", userDetails.ID, publisherID)
|
|
err = m.Assertor.AssertPublisherPermissions(ctx, m.EntClient, publisherID, userDetails.ID, permissions)
|
|
switch {
|
|
case ent.IsNotFound(err):
|
|
log.Ctx(ctx).Info().Msgf("Publisher with ID %s not found", publisherID)
|
|
return nil, echo.NewHTTPError(http.StatusNotFound, "Publisher Not Found")
|
|
|
|
case drip_services.IsPermissionError(err):
|
|
log.Ctx(ctx).Error().Msgf("Permission denied for user ID %s on "+
|
|
"publisher ID %s w/ err: %v", userDetails.ID, publisherID, err)
|
|
return nil, echo.NewHTTPError(http.StatusForbidden, "Permission denied")
|
|
|
|
case err != nil:
|
|
log.Ctx(ctx).Error().Msgf("Failed to assert publisher "+
|
|
"permission %s w/ err: %v", publisherID, err)
|
|
return nil, err
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assertNodeBanned checks if the node is banned
|
|
func (m *AuthorizationManager) assertNodeBanned(extractor func(req interface{}) string) drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertNodeBanned")
|
|
defer segment.End()
|
|
}
|
|
nodeID := extractor(request)
|
|
err = m.Assertor.AssertNodeBanned(ctx, m.EntClient, nodeID)
|
|
switch {
|
|
case drip_services.IsPermissionError(err):
|
|
log.Ctx(ctx).Error().Msgf("Node %s banned", nodeID)
|
|
return nil, echo.NewHTTPError(http.StatusForbidden, "Node Banned")
|
|
|
|
case err != nil:
|
|
log.Ctx(ctx).Error().Msgf("Failed to assert node ban status %s w/ err: %v", nodeID, err)
|
|
return nil, err
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assertPublisherBanned checks if the publisher is banned
|
|
func (m *AuthorizationManager) assertPublisherBanned(extractor func(req interface{}) string) drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertPublisherBanned")
|
|
defer segment.End()
|
|
}
|
|
publisherID := extractor(request)
|
|
|
|
switch err = m.Assertor.AssertPublisherBanned(ctx, m.EntClient, publisherID); {
|
|
case drip_services.IsPermissionError(err):
|
|
log.Ctx(ctx).Error().Msgf("Publisher %s banned", publisherID)
|
|
return nil, echo.NewHTTPError(http.StatusForbidden, "Node Banned")
|
|
|
|
case err != nil:
|
|
log.Ctx(ctx).Error().Msgf("Failed to assert publisher ban status %s w/ err: %v", publisherID, err)
|
|
return nil, err
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assertPersonalAccessTokenValid check if personal access token is valid for a publisher
|
|
func (m *AuthorizationManager) assertPersonalAccessTokenValid(
|
|
extractorPublsherID func(req interface{}) (nodeid string),
|
|
extractorPAT func(req interface{}) (pat string),
|
|
) drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertPersonalAccessTokenValid")
|
|
defer segment.End()
|
|
}
|
|
pubID := extractorPublsherID(request)
|
|
pat := extractorPAT(request)
|
|
tokenValid, err := m.Assertor.IsPersonalAccessTokenValidForPublisher(
|
|
ctx, m.EntClient, pubID, pat)
|
|
if err != nil {
|
|
log.Ctx(ctx).Error().Msgf("Token validation failed w/ err: %v", err)
|
|
return nil, echo.NewHTTPError(http.StatusBadRequest, "Failed to validate token")
|
|
}
|
|
if !tokenValid {
|
|
log.Ctx(ctx).Error().Msg("Invalid personal access token")
|
|
return nil, echo.NewHTTPError(http.StatusBadRequest, "Invalid personal access token")
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|
|
|
|
// assertNodeBelongsToPublisher check if a node belongs to a publisher
|
|
func (m *AuthorizationManager) assertNodeBelongsToPublisher(
|
|
extractorPublsherID func(req interface{}) (nodeid string),
|
|
extractorNodeID func(req interface{}) (nodeid string),
|
|
) drip.StrictMiddlewareFunc {
|
|
return func(f strictecho.StrictEchoHandlerFunc, operationID string) strictecho.StrictEchoHandlerFunc {
|
|
return func(c echo.Context, request interface{}) (response interface{}, err error) {
|
|
ctx := c.Request().Context()
|
|
if txn := newrelic.FromContext(ctx); txn != nil {
|
|
segment := txn.StartSegment("AuthorizationManager.assertNodeBelongsToPublisher")
|
|
defer segment.End()
|
|
}
|
|
pubID := extractorPublsherID(request)
|
|
nodeID := extractorNodeID(request)
|
|
|
|
err = m.Assertor.AssertNodeBelongsToPublisher(ctx, m.EntClient, pubID, nodeID)
|
|
switch {
|
|
case ent.IsNotFound(err):
|
|
return f(c, request)
|
|
|
|
case drip_services.IsPermissionError(err):
|
|
log.Ctx(ctx).Error().Msgf(
|
|
"Permission denied for publisher ID %s on node ID %s w/ err: %v", pubID, nodeID, err)
|
|
return nil, echo.NewHTTPError(http.StatusForbidden, "Permission denied")
|
|
|
|
case err != nil:
|
|
return nil, err
|
|
}
|
|
|
|
return f(c, request)
|
|
}
|
|
}
|
|
}
|