horizoncd/horizon

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

Summary

Maintainability
D
2 days
Test Coverage
// Copyright © 2023 Horizoncd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
package prehandle
 
import (
"fmt"
"path"
"strconv"
 
"github.com/gin-gonic/gin"
 
"github.com/horizoncd/horizon/core/common"
herrors "github.com/horizoncd/horizon/core/errors"
"github.com/horizoncd/horizon/core/middleware"
"github.com/horizoncd/horizon/pkg/auth"
perror "github.com/horizoncd/horizon/pkg/errors"
"github.com/horizoncd/horizon/pkg/param/managerparam"
"github.com/horizoncd/horizon/pkg/server/response"
"github.com/horizoncd/horizon/pkg/server/rpcerror"
"github.com/horizoncd/horizon/pkg/util/sets"
)
 
const (
pathReleaseSchema = "schema"
)
 
var RequestInfoFty auth.RequestInfoFactory
 
func init() {
RequestInfoFty = auth.RequestInfoFactory{
APIPrefixes: sets.NewString("apis"),
}
}
 
func Middleware(r *gin.Engine, mgr *managerparam.Manager, skippers ...middleware.Skipper) gin.HandlerFunc {
return middleware.New(func(c *gin.Context) {
requestInfo, err := RequestInfoFty.NewRequestInfo(c, c.Request)
if err != nil {
response.AbortWithRequestError(c, common.RequestInfoError, err.Error())
return
}
 
// 2. construct record
authRecord := auth.AttributesRecord{
Verb: requestInfo.Verb,
APIGroup: requestInfo.APIGroup,
APIVersion: requestInfo.APIVersion,
Resource: requestInfo.Resource,
SubResource: requestInfo.Subresource,
Name: requestInfo.Name,
Scope: requestInfo.Scope,
ResourceRequest: requestInfo.IsResourceRequest,
Path: requestInfo.Path,
}
c.Set(common.ContextAuthRecord, authRecord)
 
if _, err := strconv.Atoi(authRecord.Name); err != nil &&
authRecord.Name != "" && authRecord.APIGroup == common.GroupCore {
if authRecord.Resource == common.ResourceApplication {
handleApplication(c, mgr, r, authRecord, requestInfo)
return
} else if authRecord.Resource == common.ResourceCluster {
handleCluster(c, mgr, r, authRecord, requestInfo)
return
} else if authRecord.Resource == common.ResourceTemplate {
Avoid deeply nested control flow statements.
if authRecord.SubResource == common.AliasTemplateRelease &&
len(requestInfo.Parts) == 5 && requestInfo.Parts[4] == pathReleaseSchema {
handleGetSchema(c, mgr, r, authRecord, requestInfo)
return
}
handleTemplate(c, mgr, r, authRecord, requestInfo)
return
}
}
}, skippers...)
}
 
func constructRBACParam(c *gin.Context) (*auth.AttributesRecord, error) {
requestInfo, err := RequestInfoFty.NewRequestInfo(c, c.Request)
if err != nil {
return nil, err
}
 
// 2. construct record
authRecord := auth.AttributesRecord{
Verb: requestInfo.Verb,
APIGroup: requestInfo.APIGroup,
APIVersion: requestInfo.APIVersion,
Resource: requestInfo.Resource,
SubResource: requestInfo.Subresource,
Name: requestInfo.Name,
Scope: requestInfo.Scope,
ResourceRequest: requestInfo.IsResourceRequest,
Path: requestInfo.Path,
}
return &authRecord, nil
}
 
Function `handleApplication` has 5 arguments (exceeds 4 allowed). Consider refactoring.
Similar blocks of code found in 3 locations. Consider refactoring.
func handleApplication(c *gin.Context, mgr *managerparam.Manager, r *gin.Engine,
authRecord auth.AttributesRecord, requestInfo *auth.RequestInfo) {
app, err := mgr.ApplicationMgr.GetByName(c, authRecord.Name)
if err != nil {
if e, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok && e.Source == herrors.ApplicationInDB {
response.AbortWithRPCError(c, rpcerror.NotFoundError.WithErrMsg(err.Error()))
return
}
} else {
c.Request.URL.Path = "/" + path.Join(requestInfo.APIPrefix, requestInfo.APIGroup, requestInfo.APIVersion,
requestInfo.Resource, fmt.Sprintf("%d", app.ID), requestInfo.Subresource)
for i, param := range c.Params {
if param.Key == common.ParamApplicationID {
c.Params[i].Value = fmt.Sprintf("%d", app.ID)
}
}
}
authRecordPtr, err := constructRBACParam(c)
if err != nil {
response.AbortWithRequestError(c, common.RequestInfoError, err.Error())
return
}
c.Set(common.ContextAuthRecord, *authRecordPtr)
c.Next()
}
 
Function `handleCluster` has 5 arguments (exceeds 4 allowed). Consider refactoring.
Similar blocks of code found in 3 locations. Consider refactoring.
func handleCluster(c *gin.Context, mgr *managerparam.Manager, r *gin.Engine,
authRecord auth.AttributesRecord, requestInfo *auth.RequestInfo) {
cluster, err := mgr.ClusterMgr.GetByName(c, authRecord.Name)
if err != nil {
if e, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok && e.Source == herrors.ClusterInDB {
response.AbortWithRPCError(c, rpcerror.NotFoundError.WithErrMsg(err.Error()))
return
}
} else {
c.Request.URL.Path = "/" + path.Join(requestInfo.APIPrefix, requestInfo.APIGroup, requestInfo.APIVersion,
requestInfo.Resource, fmt.Sprintf("%d", cluster.ID), requestInfo.Subresource)
for i, param := range c.Params {
if param.Key == common.ParamClusterID {
c.Params[i].Value = fmt.Sprintf("%d", cluster.ID)
}
}
}
authRecordPtr, err := constructRBACParam(c)
if err != nil {
response.AbortWithRequestError(c, common.RequestInfoError, err.Error())
return
}
c.Set(common.ContextAuthRecord, *authRecordPtr)
c.Next()
}
 
Function `handleGetSchema` has 5 arguments (exceeds 4 allowed). Consider refactoring.
Function `handleGetSchema` has 5 return statements (exceeds 4 allowed).
func handleGetSchema(c *gin.Context, mgr *managerparam.Manager, r *gin.Engine,
authRecord auth.AttributesRecord, requestInfo *auth.RequestInfo) {
template, err := mgr.TemplateMgr.GetByName(c, authRecord.Name)
if err != nil {
if e, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok && e.Source == herrors.TemplateInDB {
response.AbortWithRPCError(c, rpcerror.NotFoundError.WithErrMsg(err.Error()))
return
}
c.Next()
return
}
 
release, err := mgr.TemplateReleaseMgr.
GetByTemplateNameAndRelease(c, authRecord.Name, requestInfo.Parts[3])
if err != nil {
if e, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok && e.Source == herrors.TemplateReleaseInDB {
response.AbortWithRPCError(c, rpcerror.NotFoundError.WithErrMsg(err.Error()))
return
}
c.Next()
return
}
 
c.Request.URL.Path = "/" + path.Join(requestInfo.APIPrefix, requestInfo.APIGroup, requestInfo.APIVersion,
requestInfo.Resource, fmt.Sprintf("%d", template.ID),
requestInfo.Subresource, fmt.Sprintf("%d", release.ID), pathReleaseSchema)
for i, param := range c.Params {
if param.Key == common.ParamTemplateID {
c.Params[i].Value = fmt.Sprintf("%d", template.ID)
}
if param.Key == common.ParamReleaseID {
c.Params[i].Value = fmt.Sprintf("%d", release.ID)
}
}
authRecordPtr, err := constructRBACParam(c)
if err != nil {
response.AbortWithRequestError(c, common.RequestInfoError, err.Error())
return
}
c.Set(common.ContextAuthRecord, *authRecordPtr)
c.Next()
}
 
Function `handleTemplate` has 5 arguments (exceeds 4 allowed). Consider refactoring.
Similar blocks of code found in 3 locations. Consider refactoring.
func handleTemplate(c *gin.Context, mgr *managerparam.Manager, r *gin.Engine,
authRecord auth.AttributesRecord, requestInfo *auth.RequestInfo) {
template, err := mgr.TemplateMgr.GetByName(c, authRecord.Name)
if err != nil {
if e, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok && e.Source == herrors.TemplateInDB {
response.AbortWithRPCError(c, rpcerror.NotFoundError.WithErrMsg(err.Error()))
return
}
} else {
c.Request.URL.Path = "/" + path.Join(requestInfo.APIPrefix, requestInfo.APIGroup, requestInfo.APIVersion,
requestInfo.Resource, fmt.Sprintf("%d", template.ID), requestInfo.Subresource)
for i, param := range c.Params {
if param.Key == common.ParamTemplateID {
c.Params[i].Value = fmt.Sprintf("%d", template.ID)
}
}
}
authRecordPtr, err := constructRBACParam(c)
if err != nil {
response.AbortWithRequestError(c, common.RequestInfoError, err.Error())
return
}
c.Set(common.ContextAuthRecord, *authRecordPtr)
c.Next()
}