horizoncd/horizon

View on GitHub
core/middleware/admission/admission.go

Summary

Maintainability
B
4 hrs
Test Coverage
package admission
 
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
 
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
 
"github.com/horizoncd/horizon/core/common"
"github.com/horizoncd/horizon/core/middleware"
admissionwebhook "github.com/horizoncd/horizon/pkg/admission"
admissionmodels "github.com/horizoncd/horizon/pkg/admission/models"
"github.com/horizoncd/horizon/pkg/auth"
"github.com/horizoncd/horizon/pkg/server/response"
"github.com/horizoncd/horizon/pkg/server/rpcerror"
"github.com/horizoncd/horizon/pkg/util/log"
)
 
// Middleware to validate and mutate admission request
Function `Middleware` has a Cognitive Complexity of 33 (exceeds 20 allowed). Consider refactoring.
Function `Middleware` has 74 lines of code (exceeds 50 allowed). Consider refactoring.
func Middleware(skippers ...middleware.Skipper) gin.HandlerFunc {
return middleware.New(func(c *gin.Context) {
// get auth record
record, ok := c.Get(common.ContextAuthRecord)
if !ok {
response.AbortWithRPCError(c,
rpcerror.BadRequestError.WithErrMsg("request with no auth record"))
return
}
attr := record.(auth.AttributesRecord)
// non resource request or read only request should be ignored
if !attr.IsResourceRequest() || attr.IsReadOnly() {
c.Next()
return
}
var object interface{}
// read request body and avoid side-effects on c.Request.Body
bodyBytes, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("request body is invalid, err: %v", err)))
return
}
if len(bodyBytes) > 0 {
contentType := c.ContentType()
if contentType == binding.MIMEJSON || contentType == "" {
if err := json.Unmarshal(bodyBytes, &object); err != nil {
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("unmarshal request body failed, err: %v", err)))
return
}
} else {
log.Errorf(c, "unsupported content type: %s", contentType)
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("unsupported content type: %s", contentType)))
return
}
}
// fill in the request url query into admission request options
queries := c.Request.URL.Query()
options := make(map[string]interface{}, len(queries))
for k, v := range queries {
if len(v) == 1 {
options[k] = v[0]
} else {
options[k] = v
}
}
admissionRequest := &admissionwebhook.Request{
Operation: admissionmodels.Operation(attr.GetVerb()),
Resource: attr.GetResource(),
Name: attr.GetName(),
SubResource: attr.GetSubResource(),
Version: attr.GetAPIVersion(),
Object: object,
Options: options,
}
admissionRequest, err = admissionwebhook.Mutating(c, admissionRequest)
if err != nil {
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("admission mutating failed: %v", err)))
return
}
if err := admissionwebhook.Validating(c, admissionRequest); err != nil {
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("admission validating failed: %v", err)))
return
}
if admissionRequest.Object != nil {
bodyBytes, err = json.Marshal(admissionRequest.Object)
if err != nil {
response.AbortWithRPCError(c,
rpcerror.ParamError.WithErrMsg(fmt.Sprintf("marshal request body failed, err: %v", err)))
return
}
}
// restore the request body
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
c.Next()
}, skippers...)
}