Skip to content
Draft
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
50 changes: 38 additions & 12 deletions api/argoApplication/ArgoApplicationRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"errors"
"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/pkg/argoApplication"
"github.com/devtron-labs/devtron/pkg/argoApplication/bean"
"github.com/devtron-labs/devtron/pkg/argoApplication/read"
"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
"github.com/devtron-labs/devtron/util/rbac"
"go.uber.org/zap"
"net/http"
"strconv"
Expand All @@ -39,26 +41,24 @@ type ArgoApplicationRestHandlerImpl struct {
readService read.ArgoApplicationReadService
logger *zap.SugaredLogger
enforcer casbin.Enforcer
enforcerUtilGitOps rbac.EnforcerUtilGitOps
}

func NewArgoApplicationRestHandlerImpl(argoApplicationService argoApplication.ArgoApplicationService,
readService read.ArgoApplicationReadService, logger *zap.SugaredLogger, enforcer casbin.Enforcer) *ArgoApplicationRestHandlerImpl {
readService read.ArgoApplicationReadService, logger *zap.SugaredLogger, enforcer casbin.Enforcer,
enforcerUtilGitOps rbac.EnforcerUtilGitOps) *ArgoApplicationRestHandlerImpl {
return &ArgoApplicationRestHandlerImpl{
argoApplicationService: argoApplicationService,
readService: readService,
logger: logger,
enforcer: enforcer,
enforcerUtilGitOps: enforcerUtilGitOps,
}

}

func (handler *ArgoApplicationRestHandlerImpl) ListApplications(w http.ResponseWriter, r *http.Request) {
// handle super-admin RBAC
token := r.Header.Get("token")
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
v := r.URL.Query()
clusterIdString := v.Get("clusterIds")
var clusterIds []int
Expand All @@ -80,16 +80,34 @@ func (handler *ArgoApplicationRestHandlerImpl) ListApplications(w http.ResponseW
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
common.WriteJsonResp(w, nil, resp, http.StatusOK)
// RBAC enforcer applying: filter the listing to the applications the caller may see.
// Batched rather than a per-app Enforce loop; an app whose object cannot be built is dropped.
objects := make([]string, 0, len(resp))
objectByApp := make(map[*bean.ArgoApplicationListDto]string, len(resp))
for _, app := range resp {
object := handler.enforcerUtilGitOps.GetExternalGitOpsAppObjectByClusterName(app.ClusterName, app.Namespace, app.Name)
if len(object) == 0 {
continue
}
objectByApp[app] = object
objects = append(objects, object)
}
authorisedObjects := make(map[string]bool)
if len(objects) > 0 {
authorisedObjects = handler.enforcer.EnforceInBatch(token, casbin.ResourceArgoApp, casbin.ActionGet, objects)
}
authorisedApps := make([]*bean.ArgoApplicationListDto, 0, len(resp))
for _, app := range resp {
if object, ok := objectByApp[app]; ok && authorisedObjects[strings.ToLower(object)] {
authorisedApps = append(authorisedApps, app)
}
}
//RBAC enforcer Ends
common.WriteJsonResp(w, nil, authorisedApps, http.StatusOK)
}

func (handler *ArgoApplicationRestHandlerImpl) GetApplicationDetail(w http.ResponseWriter, r *http.Request) {
// handle super-admin RBAC
token := r.Header.Get("token")
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
ctx := r.Context()
ctx = context.WithValue(ctx, "token", token)

Expand All @@ -108,6 +126,14 @@ func (handler *ArgoApplicationRestHandlerImpl) GetApplicationDetail(w http.Respo
return
}
}
// RBAC enforcer applying
object := handler.enforcerUtilGitOps.GetExternalGitOpsAppObject(clusterId, namespace, resourceName)
if len(object) == 0 || !handler.enforcer.Enforce(token, casbin.ResourceArgoApp, casbin.ActionGet, object) {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
//RBAC enforcer Ends

resp, err := handler.readService.GetAppDetailEA(ctx, resourceName, namespace, clusterId)
if err != nil {
handler.logger.Errorw("error in getting argo application app detail", "err", err, "resourceName", resourceName, "clusterId", clusterId)
Expand Down
28 changes: 25 additions & 3 deletions api/auth/user/UserRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ package user
import (
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"

util2 "github.com/devtron-labs/devtron/api/auth/user/util"
"github.com/devtron-labs/devtron/pkg/auth/user/helper"
"github.com/devtron-labs/devtron/util/commonEnforcementFunctionsUtil"
"github.com/gorilla/schema"
"net/http"
"strconv"
"strings"

"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/internal/util"
Expand Down Expand Up @@ -795,10 +796,31 @@ func (handler UserRestHandlerImpl) CheckUserRoles(w http.ResponseWriter, r *http
result := make(map[string]interface{})
result["roles"] = roles
result["superAdmin"] = false
result["hasArgoAppAccess"] = false
result["hasFluxAppAccess"] = false
for _, item := range roles {
if item == bean2.SUPERADMIN {
result["superAdmin"] = true
result["hasArgoAppAccess"] = true
result["hasFluxAppAccess"] = true
continue
}

roleFragments := strings.Split(item, "_")
resourceActionFragment := strings.Split(roleFragments[0], ":")

if len(resourceActionFragment) < 2 {
continue
}

if resourceActionFragment[0] == "argo-app" && (resourceActionFragment[1] == "admin" || resourceActionFragment[1] == "view") {
result["hasArgoAppAccess"] = true
}

if resourceActionFragment[0] == "flux-app" && (resourceActionFragment[1] == "admin" || resourceActionFragment[1] == "view") {
result["hasFluxAppAccess"] = true
}

}
common.WriteJsonResp(w, err, result, http.StatusOK)
}
Expand Down
29 changes: 20 additions & 9 deletions api/fluxApplication/FluxApplicationRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
clientErrors "github.com/devtron-labs/devtron/pkg/errors"
"github.com/devtron-labs/devtron/util/rbac"
"github.com/devtron-labs/devtron/pkg/fluxApplication"
"github.com/gorilla/mux"
"go.uber.org/zap"
Expand All @@ -20,26 +21,34 @@
fluxApplicationService fluxApplication.FluxApplicationService
logger *zap.SugaredLogger
enforcer casbin.Enforcer
enforcerUtilGitOps rbac.EnforcerUtilGitOps
}

func NewFluxApplicationRestHandlerImpl(fluxApplicationService fluxApplication.FluxApplicationService,
logger *zap.SugaredLogger, enforcer casbin.Enforcer) *FluxApplicationRestHandlerImpl {
logger *zap.SugaredLogger, enforcer casbin.Enforcer,
enforcerUtilGitOps rbac.EnforcerUtilGitOps) *FluxApplicationRestHandlerImpl {
return &FluxApplicationRestHandlerImpl{
fluxApplicationService: fluxApplicationService,
logger: logger,
enforcer: enforcer,
enforcerUtilGitOps: enforcerUtilGitOps,
}

}

// checkFluxAppAuth builds the RBAC object from the app identity and enforces on it. Passed into
// the service because the app list is streamed and cannot be filtered after the fact.
func (handler *FluxApplicationRestHandlerImpl) checkFluxAppAuth(token string, clusterName string, namespace string, appName string) bool {

Check warning on line 41 in api/fluxApplication/FluxApplicationRestHandler.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Group together these consecutive parameters of the same type.

See more on https://sonarcloud.io/project/issues?id=devtron-labs_devtron&issues=AaA52piWYM5hpR5f3SK3&open=AaA52piWYM5hpR5f3SK3&pullRequest=7014
object := handler.enforcerUtilGitOps.GetExternalGitOpsAppObjectByClusterName(clusterName, namespace, appName)
if len(object) == 0 {
return false
}
return handler.enforcer.Enforce(token, casbin.ResourceFluxApp, casbin.ActionGet, object)
}

func (handler *FluxApplicationRestHandlerImpl) ListFluxApplications(w http.ResponseWriter, r *http.Request) {

//handle super-admin RBAC
token := r.Header.Get("token")
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
v := r.URL.Query()
clusterIdString := v.Get("clusterIds")
var clusterIds []int
Expand All @@ -59,7 +68,7 @@
return
}
handler.logger.Debugw("extracted ClusterIds successfully ", "clusterIds", clusterIds)
handler.fluxApplicationService.ListFluxApplications(r.Context(), clusterIds, noStream, w)
handler.fluxApplicationService.ListFluxApplications(r.Context(), clusterIds, noStream, w, token, handler.checkFluxAppAuth)
}

func (handler *FluxApplicationRestHandlerImpl) GetApplicationDetail(w http.ResponseWriter, r *http.Request) {
Expand All @@ -76,12 +85,14 @@
return
}

// handle super-admin RBAC
// RBAC enforcer applying
token := r.Header.Get("token")
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
object := handler.enforcerUtilGitOps.GetExternalGitOpsAppObject(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.Name)
if len(object) == 0 || !handler.enforcer.Enforce(token, casbin.ResourceFluxApp, casbin.ActionGet, object) {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
//RBAC enforcer Ends

res, err := handler.fluxApplicationService.GetFluxAppDetail(r.Context(), appIdentifier)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions api/helm-app/wire_helmApp.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ var HelmAppWireSet = wire.NewSet(
gRPC.GetConfig,
rbac.NewEnforcerUtilHelmImpl,
wire.Bind(new(rbac.EnforcerUtilHelm), new(*rbac.EnforcerUtilHelmImpl)),

rbac.NewEnforcerUtilGitOpsImpl,
wire.Bind(new(rbac.EnforcerUtilGitOps), new(*rbac.EnforcerUtilGitOpsImpl)),
)
Loading
Loading