bouncer/internal/admin/authz.go

102 lines
1.9 KiB
Go

package admin
import (
"context"
"fmt"
"net/http"
"forge.cadoles.com/cadoles/bouncer/internal/auth"
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
var ErrCodeForbidden api.ErrorCode = "forbidden"
func assertReadAccess(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
reqUser, ok := assertRequestUser(w, r)
if !ok {
return
}
switch user := reqUser.(type) {
case *jwt.User:
role := user.Role()
if role == jwt.RoleReader || role == jwt.RoleWriter {
h.ServeHTTP(w, r)
return
}
default:
logUnexpectedUserType(r.Context(), reqUser)
}
forbidden(w, r)
}
return http.HandlerFunc(fn)
}
func assertWriteAccess(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
reqUser, ok := assertRequestUser(w, r)
if !ok {
return
}
switch user := reqUser.(type) {
case *jwt.User:
role := user.Role()
if role == jwt.RoleWriter {
h.ServeHTTP(w, r)
return
}
default:
logUnexpectedUserType(r.Context(), reqUser)
}
forbidden(w, r)
}
return http.HandlerFunc(fn)
}
func assertRequestUser(w http.ResponseWriter, r *http.Request) (auth.User, bool) {
ctx := r.Context()
user, err := auth.CtxUser(ctx)
if err != nil {
logger.Error(ctx, "could not retrieve user", logger.CapturedE(errors.WithStack(err)))
forbidden(w, r)
return nil, false
}
if user == nil {
forbidden(w, r)
return nil, false
}
return user, true
}
func forbidden(w http.ResponseWriter, r *http.Request) {
logger.Warn(r.Context(), "forbidden", logger.F("path", r.URL.Path))
api.ErrorResponse(w, http.StatusForbidden, ErrCodeForbidden, nil)
}
func logUnexpectedUserType(ctx context.Context, user auth.User) {
logger.Error(
ctx, "unexpected user type",
logger.F("subject", user.Subject()),
logger.F("type", fmt.Sprintf("%T", user)),
)
}