Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions api/appStore/InstalledAppRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
util3 "github.com/devtron-labs/devtron/pkg/appStore/util"
"github.com/devtron-labs/devtron/pkg/bean"
"net/http"
"reflect"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -363,6 +364,8 @@ func (handler *InstalledAppRestHandlerImpl) DeployBulk(w http.ResponseWriter, r
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden)
return
}
charts, authRes := handler.checkForHelmDeployAuth(request, token)
request.ChartGroupInstallChartRequest = charts
//RBAC block ends here

visited := make(map[string]bool)
Expand Down Expand Up @@ -391,10 +394,116 @@ func (handler *InstalledAppRestHandlerImpl) DeployBulk(w http.ResponseWriter, r
handler.Logger.Errorw("service err, DeployBulk", "err", err, "payload", request)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
} else {
res = authRes
}
common.WriteJsonResp(w, err, res, http.StatusOK)
}

func (handler *InstalledAppRestHandlerImpl) checkForHelmDeployAuth(request chartGroup.ChartGroupInstallRequest, token string) ([]*chartGroup.ChartGroupInstallChartRequest, *chartGroup.ChartGroupInstallAppRes) {
//the value of this map is array of integer because the GetHelmObjectByProjectIdAndEnvId method may return "//" for error cases
//so different environments may contain same object, to handle that we are using (map[string] []int)
rbacObjectToEnvIdMap1 := make(map[string][]int)
rbacObjectToEnvIdMap2 := make(map[string][]int)

rbacObjectArray1 := make([]string, 0)
rbacObjectArray2 := make([]string, 0)

envIdToChartGroupInstallChartRequest := make(map[int][]*chartGroup.ChartGroupInstallChartRequest)

for _, chartGroupInstall := range request.ChartGroupInstallChartRequest {
envIdToChartGroupInstallChartRequest[chartGroupInstall.EnvironmentId] = append(envIdToChartGroupInstallChartRequest[chartGroupInstall.EnvironmentId], chartGroupInstall)
rbacObject1, rbacObject2 := handler.enforcerUtil.GetHelmObjectByProjectIdAndEnvId(request.ProjectId, chartGroupInstall.EnvironmentId)
_, ok := rbacObjectToEnvIdMap1[rbacObject1]
if !ok {
rbacObjectToEnvIdMap1[rbacObject1] = make([]int, 0)
}
rbacObjectToEnvIdMap1[rbacObject1] = append(rbacObjectToEnvIdMap1[rbacObject1], chartGroupInstall.EnvironmentId)
rbacObjectArray1 = append(rbacObjectArray1, rbacObject1)
_, ok = rbacObjectToEnvIdMap2[rbacObject2]
if !ok {
rbacObjectToEnvIdMap2[rbacObject2] = make([]int, 0)
}
rbacObjectToEnvIdMap2[rbacObject2] = append(rbacObjectToEnvIdMap2[rbacObject2], chartGroupInstall.EnvironmentId)
rbacObjectArray2 = append(rbacObjectArray2, rbacObject2)
}
resultObjectMap1 := handler.enforcer.EnforceInBatch(token, casbin.ResourceHelmApp, casbin.ActionCreate, rbacObjectArray1)
resultObjectMap2 := handler.enforcer.EnforceInBatch(token, casbin.ResourceHelmApp, casbin.ActionCreate, rbacObjectArray2)

authorizedEnvIdSet := make(map[int]bool)

//O(n) time loop , at max we will only iterate through all the envs
for obj, ok := range resultObjectMap1 {
if ok {
envIds := rbacObjectToEnvIdMap1[obj]
for _, envId := range envIds {
authorizedEnvIdSet[envId] = true
}
}
}
for obj, ok := range resultObjectMap2 {
if ok {
envIds := rbacObjectToEnvIdMap2[obj]
for _, envId := range envIds {
authorizedEnvIdSet[envId] = true
}
}
}
authorizedChartGroupInstallRequests := make([]*chartGroup.ChartGroupInstallChartRequest, 0)
for envId, _ := range authorizedEnvIdSet {
authorizedChartGroupInstall := envIdToChartGroupInstallChartRequest[envId]
for _, authChartGroup := range authorizedChartGroupInstall {
authorizedChartGroupInstallRequests = append(authorizedChartGroupInstallRequests, authChartGroup)
}
}
unauthorizedChartGroupInstallRequests := make([]*chartGroup.ChartGroupInstallChartRequest, 0)

for _, req := range request.ChartGroupInstallChartRequest {
isAuthorized := false
for _, authReq := range authorizedChartGroupInstallRequests {
if reflect.DeepEqual(req, authReq) {
isAuthorized = true
break
}
}
if !isAuthorized {
unauthorizedChartGroupInstallRequests = append(unauthorizedChartGroupInstallRequests, req)
}
}

// Create slices for ChartGroupInstallMetadata
authorizedMetadata := make([]chartGroup.ChartGroupInstallMetadata, 0)
unauthorizedMetadata := make([]chartGroup.ChartGroupInstallMetadata, 0)

for _, req := range authorizedChartGroupInstallRequests {
metadata := handler.getChartGroupInstallMetadata(req, string(chartGroup.StatusSuccess), string(chartGroup.ReasonTriggered))
authorizedMetadata = append(authorizedMetadata, metadata)
}

for _, req := range unauthorizedChartGroupInstallRequests {
metadata := handler.getChartGroupInstallMetadata(req, string(chartGroup.StatusFailed), string(chartGroup.ReasonNotAuthorize))
unauthorizedMetadata = append(unauthorizedMetadata, metadata)
}
unauthorizeCount := len(unauthorizedChartGroupInstallRequests)
totalCount := len(request.ChartGroupInstallChartRequest)
// Combine all metadata into a single ChartGroupInstallAppRes
chartGroupInstallAppRes := &chartGroup.ChartGroupInstallAppRes{
ChartGroupInstallMetadata: append(authorizedMetadata, unauthorizedMetadata...),
Summary: fmt.Sprintf(chartGroup.FAILED_TO_TRIGGER, unauthorizeCount, totalCount),
}
return authorizedChartGroupInstallRequests, chartGroupInstallAppRes
}

func (handler *InstalledAppRestHandlerImpl) getChartGroupInstallMetadata(req *chartGroup.ChartGroupInstallChartRequest, triggerStatus string, reason string) chartGroup.ChartGroupInstallMetadata {
metadata := chartGroup.ChartGroupInstallMetadata{
AppName: req.AppName,
EnvironmentId: req.EnvironmentId,
TriggerStatus: triggerStatus,
Reason: reason,
}
return metadata
}

func (handler *InstalledAppRestHandlerImpl) CheckAppExists(w http.ResponseWriter, r *http.Request) {
userId, err := handler.userAuthService.GetLoggedInUser(r)
if userId == 0 || err != nil {
Expand Down
19 changes: 18 additions & 1 deletion pkg/appStore/chartGroup/bean.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@ type ChartGroupInstallChartRequest struct {
ReferenceValueKind string `json:"referenceValueKind, omitempty" validate:"oneof=DEFAULT TEMPLATE DEPLOYED"`
ChartGroupEntryId int `json:"chartGroupEntryId"` //optional
}

type ChartGroupInstallMetadata struct {
AppName string `json:"appName"`
EnvironmentId int `json:"environmentId"`
TriggerStatus string `json:"triggerStatus"`
Reason string `json:"reason"`
}
type ChartGroupInstallAppRes struct {
ChartGroupInstallMetadata []ChartGroupInstallMetadata `json:"chartGroupInstallMetadata"`
Summary string `json:"summary"`
}
type TriggerStatus string
type Reason string

const FAILED_TO_TRIGGER = "%d/%d failed to trigger"
const (
StatusFailed TriggerStatus = "failed"
StatusSuccess TriggerStatus = "success"
ReasonNotAuthorize Reason = "not authorized"
ReasonTriggered Reason = "triggered"
)
2 changes: 1 addition & 1 deletion wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.