efritz/chevron

View on GitHub
middleware/auth.go

Summary

Maintainability
A
0 mins
Test Coverage
package middleware

import (
    "context"
    "net/http"

    "github.com/efritz/nacelle"
    "github.com/efritz/response"

    "github.com/efritz/chevron"
)

type (
    AuthMiddleware struct {
        authorizer                  Authorizer
        errorFactory                ErrorFactory
        forbiddenResponseFactory    ResponseFactory
        unauthorizedResponseFactory ErrorFactory
    }

    Authorizer interface {
        Authorize(context.Context, *http.Request) (AuthResult, interface{}, error)
    }

    AuthResult     int
    AuthorizerFunc func(context.Context, *http.Request) (AuthResult, interface{}, error)

    tokenAuthPayload string
)

var TokenAuthPayload = tokenAuthPayload("chevron.middleware.auth")

const (
    AuthResultInvalid AuthResult = iota
    AuthResultOK
    AuthResultForbidden
    AuthResultUnauthorized
)

func (f AuthorizerFunc) Authorize(ctx context.Context, req *http.Request) (AuthResult, interface{}, error) {
    return f(ctx, req)
}

func NewAuthMiddleware(authorizer Authorizer, configs ...AuthMiddlewareConfigFunc) chevron.Middleware {
    m := &AuthMiddleware{
        authorizer:                  authorizer,
        errorFactory:                defaultErrorFactory,
        forbiddenResponseFactory:    defaultForbiddenResponseFactory,
        unauthorizedResponseFactory: defaultUnauthorizedResponseFactory,
    }

    for _, f := range configs {
        f(m)
    }

    return m
}

func (m *AuthMiddleware) Convert(f chevron.Handler) (chevron.Handler, error) {
    handler := func(ctx context.Context, req *http.Request, logger nacelle.Logger) response.Response {
        result, payload, err := m.authorizer.Authorize(ctx, req)

        switch result {
        case AuthResultForbidden:
            return m.forbiddenResponseFactory()
        case AuthResultUnauthorized:
            return m.unauthorizedResponseFactory(err)
        default:
        }

        if err != nil {
            logger.Error("failed to invoke authorizer (%s)", err.Error())
            return m.errorFactory(err)
        }

        return f(context.WithValue(ctx, TokenAuthPayload, payload), req, logger)
    }

    return handler, nil
}

func defaultForbiddenResponseFactory() response.Response {
    return response.Empty(http.StatusForbidden)
}

func defaultUnauthorizedResponseFactory(err error) response.Response {
    return response.Empty(http.StatusUnauthorized)
}