Skip to content

Commit c0f82e9

Browse files
Merge pull request #6103 from devtron-labs/image-scan
chore: implemented Rbac enforcer in batch
2 parents 6cfab58 + 53c4152 commit c0f82e9

File tree

3 files changed

+156
-17
lines changed

3 files changed

+156
-17
lines changed

api/restHandler/ImageScanRestHandler.go

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ import (
3434
"go.uber.org/zap"
3535
)
3636

37+
const (
38+
ObjectTypeApp = "app"
39+
ObjectTypeChart = "chart"
40+
ObjectTypePod = "pod"
41+
)
42+
3743
type ImageScanRestHandler interface {
3844
ScanExecutionList(w http.ResponseWriter, r *http.Request)
3945
FetchExecutionDetail(w http.ResponseWriter, r *http.Request)
@@ -90,37 +96,72 @@ func (impl ImageScanRestHandlerImpl) ScanExecutionList(w http.ResponseWriter, r
9096
}
9197
return
9298
}
99+
93100
token := r.Header.Get("token")
94101
var ids []int
102+
var appRBACObjects []string
103+
var envRBACObjects []string
104+
var podRBACObjects []string
105+
podRBACMap := make(map[string]int)
106+
107+
IdToAppEnvPairs := make(map[int][2]int)
95108
for _, item := range deployInfoList {
96-
if item.ScanObjectMetaId > 0 && (item.ObjectType == "app" || item.ObjectType == "chart") {
97-
object := impl.enforcerUtil.GetAppRBACNameByAppId(item.ScanObjectMetaId)
98-
if ok := impl.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionGet, object); !ok {
99-
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
100-
return
109+
if item.ScanObjectMetaId > 0 && (item.ObjectType == ObjectTypeApp || item.ObjectType == ObjectTypeChart) {
110+
IdToAppEnvPairs[item.Id] = [2]int{item.ScanObjectMetaId, item.EnvId}
111+
}
112+
}
113+
114+
appObjects, envObjects, appIdtoApp, envIdToEnv, err := impl.enforcerUtil.GetAppAndEnvRBACNamesByAppAndEnvIds(IdToAppEnvPairs)
115+
if err != nil {
116+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
117+
return
118+
}
119+
120+
for _, item := range deployInfoList {
121+
if item.ScanObjectMetaId > 0 && (item.ObjectType == ObjectTypeApp || item.ObjectType == ObjectTypeChart) {
122+
appObject := appObjects[item.Id]
123+
envObject := envObjects[item.Id]
124+
if appObject != "" {
125+
appRBACObjects = append(appRBACObjects, appObject)
101126
}
102-
object = impl.enforcerUtil.GetEnvRBACNameByAppId(item.ScanObjectMetaId, item.EnvId)
103-
if ok := impl.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionGet, object); ok {
104-
ids = append(ids, item.Id)
127+
if envObject != "" {
128+
envRBACObjects = append(envRBACObjects, envObject)
105129
}
106-
} else if item.ScanObjectMetaId > 0 && (item.ObjectType == "pod") {
130+
} else if item.ScanObjectMetaId > 0 && (item.ObjectType == ObjectTypePod) {
107131
environments, err := impl.environmentService.GetByClusterId(item.ClusterId)
108132
if err != nil {
109133
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
110134
return
111135
}
112-
pass := false
113136
for _, environment := range environments {
114-
if ok := impl.enforcer.Enforce(token, casbin.ResourceGlobalEnvironment, casbin.ActionGet, environment.EnvironmentIdentifier); ok {
115-
pass = true
116-
continue
117-
}
137+
podObject := environment.EnvironmentIdentifier
138+
podRBACObjects = append(podRBACObjects, podObject)
139+
podRBACMap[podObject] = item.Id
118140
}
119-
if pass {
120-
ids = append(ids, item.Id)
141+
}
142+
}
143+
144+
appResults := impl.enforcer.EnforceInBatch(token, casbin.ResourceApplications, casbin.ActionGet, appRBACObjects)
145+
envResults := impl.enforcer.EnforceInBatch(token, casbin.ResourceEnvironment, casbin.ActionGet, envRBACObjects)
146+
podResults := impl.enforcer.EnforceInBatch(token, casbin.ResourceGlobalEnvironment, casbin.ActionGet, podRBACObjects)
147+
148+
for _, item := range deployInfoList {
149+
if impl.enforcerUtil.IsAuthorizedForAppInAppResults(item.ScanObjectMetaId, appResults, appIdtoApp) && impl.enforcerUtil.IsAuthorizedForEnvInEnvResults(item.ScanObjectMetaId, item.EnvId, envResults, appIdtoApp, envIdToEnv) {
150+
ids = append(ids, item.Id)
151+
}
152+
}
153+
for podObject, authorized := range podResults {
154+
if authorized {
155+
if itemId, exists := podRBACMap[podObject]; exists {
156+
ids = append(ids, itemId)
121157
}
122158
}
123-
// skip for pod
159+
}
160+
161+
if ids == nil || len(ids) == 0 {
162+
responseList := make([]*securityBean.ImageScanHistoryResponse, 0)
163+
common.WriteJsonResp(w, nil, &securityBean.ImageScanHistoryListingResponse{ImageScanHistoryResponse: responseList}, http.StatusOK)
164+
return
124165
}
125166

126167
results, err := impl.imageScanService.FetchScanExecutionListing(request, ids)

internal/sql/repository/app/AppRepository.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type AppRepository interface {
7474
FindByIds(ids []*int) ([]*App, error)
7575
FetchAppsByFilterV2(appNameIncludes string, appNameExcludes string, environmentId int) ([]*App, error)
7676
FindAppAndProjectByAppId(appId int) (*App, error)
77+
FindAppAndProjectByAppIds(appIds []*int) ([]*App, error)
7778
FindAppAndProjectByAppName(appName string) (*App, error)
7879
GetConnection() *pg.DB
7980
FindAllMatchesByAppName(appName string, appType helper.AppType) ([]*App, error)
@@ -181,6 +182,19 @@ func (repo AppRepositoryImpl) FindJobByDisplayName(appName string) (*App, error)
181182
return pipelineGroup, err
182183
}
183184

185+
func (repo AppRepositoryImpl) FindAppAndProjectByAppIds(appIds []*int) ([]*App, error) {
186+
var apps []*App
187+
if len(appIds) == 0 {
188+
return apps, nil
189+
}
190+
191+
err := repo.dbConnection.Model(&apps).Column("Team").
192+
Where("app.id in (?)", pg.In(appIds)).
193+
Where("app.active=?", true).
194+
Select()
195+
return apps, err
196+
}
197+
184198
func (repo AppRepositoryImpl) FindActiveListByName(appName string) ([]*App, error) {
185199
var apps []*App
186200
err := repo.dbConnection.

util/rbac/EnforcerUtil.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ import (
3737
)
3838

3939
type EnforcerUtil interface {
40+
GetAppAndEnvRBACNamesByAppAndEnvIds(IdToAppEnvPairs map[int][2]int) (map[int]string, map[int]string, map[int]*app.App, map[int]*repository.Environment, error)
41+
IsAuthorizedForAppInAppResults(appId int, rbacResults map[string]bool, appIdtoApp map[int]*app.App) bool
42+
IsAuthorizedForEnvInEnvResults(appId int, envId int, appResults map[string]bool, appIdtoApp map[int]*app.App, envIdToEnv map[int]*repository.Environment) bool
4043
GetAppRBACName(appName string) string
4144
GetRbacObjectsForAllApps(appType helper.AppType) map[int]string
4245
GetRbacObjectsForAllAppsWithTeamID(teamID int, appType helper.AppType) map[int]string
@@ -111,6 +114,87 @@ func NewEnforcerUtilImpl(logger *zap.SugaredLogger, teamRepository team.TeamRepo
111114
}
112115
}
113116

117+
func (impl EnforcerUtilImpl) IsAuthorizedForAppInAppResults(appId int, rbacResults map[string]bool, appIdtoApp map[int]*app.App) bool {
118+
app, appExists := appIdtoApp[appId]
119+
if !appExists {
120+
return false
121+
}
122+
123+
appObject := fmt.Sprintf("%s/%s", app.Team.Name, app.AppName)
124+
if authorized, exists := rbacResults[appObject]; exists && authorized {
125+
return true
126+
}
127+
return false
128+
}
129+
130+
func (impl EnforcerUtilImpl) IsAuthorizedForEnvInEnvResults(appId int, envId int, rbacResults map[string]bool, appIdtoApp map[int]*app.App, envIdToEnv map[int]*repository.Environment) bool {
131+
app, appExists := appIdtoApp[appId]
132+
if !appExists {
133+
return false
134+
}
135+
env, envExists := envIdToEnv[envId]
136+
if !envExists {
137+
return false
138+
}
139+
140+
envObject := fmt.Sprintf("%s/%s", env.EnvironmentIdentifier, app.AppName)
141+
if authorized, exists := rbacResults[envObject]; exists && authorized {
142+
return true
143+
}
144+
return false
145+
}
146+
147+
func (impl EnforcerUtilImpl) GetAppAndEnvRBACNamesByAppAndEnvIds(appEnvPairs map[int][2]int) (map[int]string, map[int]string, map[int]*app.App, map[int]*repository.Environment, error) {
148+
appObjects := make(map[int]string)
149+
envObjects := make(map[int]string)
150+
151+
appIds := make([]*int, 0, len(appEnvPairs))
152+
envIds := make([]*int, 0, len(appEnvPairs))
153+
for _, pair := range appEnvPairs {
154+
appId := pair[0]
155+
envId := pair[1]
156+
appIds = append(appIds, &appId)
157+
envIds = append(envIds, &envId)
158+
}
159+
appIdToAppMap := make(map[int]*app.App)
160+
envIdToEnvMap := make(map[int]*repository.Environment)
161+
applications, err := impl.appRepo.FindAppAndProjectByAppIds(appIds)
162+
163+
if err != nil {
164+
return nil, nil, appIdToAppMap, envIdToEnvMap, err
165+
}
166+
167+
for _, app := range applications {
168+
appIdToAppMap[app.Id] = app
169+
}
170+
171+
environments, err := impl.environmentRepository.FindByIds(envIds)
172+
if err != nil {
173+
return nil, nil, appIdToAppMap, envIdToEnvMap, err
174+
}
175+
176+
for _, env := range environments {
177+
envIdToEnvMap[env.Id] = env
178+
}
179+
180+
for id, pair := range appEnvPairs {
181+
appId := pair[0]
182+
envId := pair[1]
183+
// check if app and env exists
184+
// handling for deleted app and env
185+
if _, ok := appIdToAppMap[appId]; !ok {
186+
continue
187+
}
188+
if _, ok := envIdToEnvMap[envId]; !ok {
189+
continue
190+
}
191+
appObjects[id] = fmt.Sprintf("%s/%s", appIdToAppMap[appId].Team.Name, appIdToAppMap[appId].AppName)
192+
envObjects[id] = fmt.Sprintf("%s/%s", envIdToEnvMap[envId].EnvironmentIdentifier, appIdToAppMap[appId].AppName)
193+
}
194+
195+
return appObjects, envObjects, appIdToAppMap, envIdToEnvMap, nil
196+
}
197+
114198
func (impl EnforcerUtilImpl) GetRbacObjectsByEnvIdsAndAppId(envIds []int, appId int) (map[int]string, map[string]string) {
115199

116200
objects := make(map[int]string)

0 commit comments

Comments
 (0)