39 lines
1.3 KiB
Go
39 lines
1.3 KiB
Go
|
|
package authmw
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
"code.nochebuena.dev/einherjar/contracts/logging"
|
||
|
|
"code.nochebuena.dev/einherjar/contracts/security"
|
||
|
|
"code.nochebuena.dev/einherjar/core/xerrors"
|
||
|
|
"code.nochebuena.dev/einherjar/web/httputil"
|
||
|
|
)
|
||
|
|
|
||
|
|
// AuthzMiddleware gates the request against a single required permission on a named resource.
|
||
|
|
// Returns 401 if no identity is in context; 403 if the permission check fails or the
|
||
|
|
// provider returns an error (fail-closed: provider failure denies access).
|
||
|
|
func AuthzMiddleware(logger logging.Logger, provider security.PermissionProvider, resource string, required security.Permission) func(http.Handler) http.Handler {
|
||
|
|
return func(next http.Handler) http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
identity, ok := security.FromContext(r.Context())
|
||
|
|
if !ok {
|
||
|
|
httputil.Error(logger, w, r, xerrors.Unauthorized("missing identity"))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
mask, err := provider.ResolveMask(r.Context(), identity.UID, resource)
|
||
|
|
if err != nil {
|
||
|
|
httputil.Error(logger, w, r, xerrors.PermissionDenied("permission check failed").WithError(err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !mask.Has(required) {
|
||
|
|
httputil.Error(logger, w, r, xerrors.PermissionDenied("insufficient permissions"))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
next.ServeHTTP(w, r)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|