Files
opencloud/services/groupware/pkg/middleware/auth.go
Pascal Bleser 531e50fb73 API documentation changes for groupware-apidocs
* add example generator infrastructure, with some examples for pkg/jmap
   and pkg/groupware, with more needing to be done

 * alter the apidoc Makefile to stop using go-swagger but, instead, use
   the openapi.yml file that must be dropped into that directory using
   groupware-apidocs (will improve the integration there later)

 * add Makefile target to generate examples

 * bump redocly from 2.4.0 to 2.14.5

 * introduce Request.PathParam() and .PathParamDoc() to improve API
   documentation, as well as future-proofing

 * improve X-Request-ID and Trace-Id header handling in the middleware
   by logging it safely when an error occurs in the middleware
2026-02-04 09:40:23 +01:00

97 lines
3.1 KiB
Go

package middleware
import (
"net/http"
gmmetadata "go-micro.dev/v4/metadata"
"google.golang.org/grpc/metadata"
"github.com/opencloud-eu/opencloud/pkg/account"
"github.com/opencloud-eu/opencloud/pkg/log"
opkgm "github.com/opencloud-eu/opencloud/pkg/middleware"
"github.com/opencloud-eu/reva/v2/pkg/auth/scope"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/token/manager/jwt"
)
// authOptions initializes the available default options.
func authOptions(opts ...account.Option) account.Options {
opt := account.Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
func Auth(opts ...account.Option) func(http.Handler) http.Handler {
opt := authOptions(opts...)
tokenManager, err := jwt.New(map[string]any{
"secret": opt.JWTSecret,
"expires": int64(24 * 60 * 60), // token expiration in seconds
})
if err != nil {
opt.Logger.Fatal().Err(err).Msgf("Could not initialize token-manager")
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
t := r.Header.Get(revactx.TokenHeader)
if t == "" {
requestID := r.Header.Get("X-Request-ID")
traceID := GetTraceID(ctx)
l := opt.Logger.Error().Str(log.RequestIDString, log.SafeString(requestID))
if traceID != "" {
l = l.Str(LogTraceID, log.SafeString(traceID))
}
l.Msgf("missing access token in header %v", revactx.TokenHeader)
w.WriteHeader(http.StatusUnauthorized) // missing access token
return
}
u, tokenScope, err := tokenManager.DismantleToken(r.Context(), t)
if err != nil {
requestID := r.Header.Get("X-Request-ID")
traceID := GetTraceID(ctx)
l := opt.Logger.Error().Str(log.RequestIDString, log.SafeString(requestID))
if traceID != "" {
l = l.Str(LogTraceID, log.SafeString(traceID))
}
l.Err(err).Msgf("invalid access token in header %v", revactx.TokenHeader)
w.WriteHeader(http.StatusUnauthorized) // invalid token
return
}
if ok, err := scope.VerifyScope(ctx, tokenScope, r); err != nil || !ok {
requestID := r.Header.Get("X-Request-ID")
traceID := GetTraceID(ctx)
l := opt.Logger.Error().Str(log.RequestIDString, log.SafeString(requestID))
if traceID != "" {
l = l.Str(LogTraceID, log.SafeString(traceID))
}
l.Err(err).Msg("verifying scope failed")
w.WriteHeader(http.StatusUnauthorized) // invalid scope
return
}
ctx = revactx.ContextSetToken(ctx, t)
ctx = revactx.ContextSetUser(ctx, u)
ctx = gmmetadata.Set(ctx, opkgm.AccountID, u.GetId().GetOpaqueId())
if m := u.GetOpaque().GetMap(); m != nil {
if roles, ok := m["roles"]; ok {
ctx = gmmetadata.Set(ctx, opkgm.RoleIDs, string(roles.GetValue()))
}
}
ctx = metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, t)
initiatorID := r.Header.Get(revactx.InitiatorHeader)
if initiatorID != "" {
ctx = revactx.ContextSetInitiator(ctx, initiatorID)
ctx = metadata.AppendToOutgoingContext(ctx, revactx.InitiatorHeader, initiatorID)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}