Error handling is structured in such a way that typically, a ServiceCodeError is passed through the echo HTTP error, in reference to internally defined errors. We want to be able to obtain and return specific external errors, for example during validation from openapi3. Add a 'details' field to the serviceError struct, to contain extra / externally defined information. Modify HTTPErrorHandler to anticipate either a string or a ServiceErrorCode from echo, and respond accordingly. Edit the affected tests to expect the appropriate response.
87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
package v2
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/getkin/kin-openapi/openapi3filter"
|
|
"github.com/google/uuid"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/osbuild/osbuild-composer/internal/auth"
|
|
)
|
|
|
|
// getTenantChannel returns the tenant channel for the provided request context
|
|
func (s *Server) getTenantChannel(ctx echo.Context) (string, error) {
|
|
// channel is empty if JWT is not enabled
|
|
var channel string
|
|
if s.config.JWTEnabled {
|
|
tenant, err := auth.GetFromClaims(ctx.Request().Context(), s.config.TenantProviderFields)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// prefix the tenant to prevent collisions if support for specifying channels in a request is ever added
|
|
channel = "org-" + tenant
|
|
}
|
|
return channel, nil
|
|
}
|
|
|
|
type ComposeHandlerFunc func(ctx echo.Context, id string) error
|
|
|
|
// Ensures that the job's channel matches the JWT cannel set in the echo.Context
|
|
func (s *Server) EnsureJobChannel(next ComposeHandlerFunc) ComposeHandlerFunc {
|
|
return func(c echo.Context, id string) error {
|
|
jobId, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return HTTPError(ErrorInvalidComposeId)
|
|
}
|
|
|
|
ctxChannel, err := s.getTenantChannel(c)
|
|
if err != nil {
|
|
return HTTPErrorWithInternal(ErrorTenantNotFound, err)
|
|
}
|
|
|
|
jobChannel, err := s.workers.JobChannel(jobId)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return HTTPErrorWithInternal(ErrorComposeNotFound, err)
|
|
}
|
|
|
|
if jobChannel != ctxChannel {
|
|
return HTTPError(ErrorComposeNotFound)
|
|
}
|
|
|
|
return next(c, id)
|
|
}
|
|
}
|
|
|
|
func (s *Server) ValidateRequest(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
request := c.Request()
|
|
|
|
// extract route and parameters from request
|
|
route, params, err := s.router.FindRoute(request)
|
|
if err != nil {
|
|
return HTTPErrorWithInternal(ErrorResourceNotFound, err)
|
|
}
|
|
|
|
input := &openapi3filter.RequestValidationInput{
|
|
Request: request,
|
|
PathParams: params,
|
|
Route: route,
|
|
Options: &openapi3filter.Options{
|
|
AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
|
|
},
|
|
}
|
|
|
|
ctx := request.Context()
|
|
if err := openapi3filter.ValidateRequest(ctx, input); err != nil {
|
|
details := ""
|
|
re, ok := err.(*openapi3filter.RequestError)
|
|
if ok {
|
|
details = re.Error()
|
|
}
|
|
return HTTPErrorWithDetails(ErrorValidationFailed, err, details)
|
|
}
|
|
|
|
return next(c)
|
|
}
|
|
}
|