Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bc8f92a
chore: create a makefile to setup and clean resources for development
vivekanandan-devtron Aug 14, 2026
0ce58e4
feat: add flag for frontend to show argo/flux tabs
vivekanandan-devtron Aug 17, 2026
0acc4f7
feat: changes for dropdown API in user permissions page
vivekanandan-devtron Aug 19, 2026
fb2114f
fix: missing dependencies in constructor method
vivekanandan-devtron Aug 19, 2026
eae78a5
fix: improper filtering logic
vivekanandan-devtron Aug 20, 2026
950493b
fix: prevent connecting to kubelink for failed clusters
vivekanandan-devtron Aug 21, 2026
85100ab
wip: policy enforcements
vivekanandan-devtron Aug 21, 2026
f7ac9a7
wip: policy enforcements
vivekanandan-devtron Aug 24, 2026
4d210f4
chore: remove test folder
vivekanandan-devtron Aug 31, 2026
63f95f3
Merge branch 'main' of github-devtron:devtron-labs/devtron into feat/…
vivekanandan-devtron Sep 2, 2026
9897e34
fix: code review fixes
vivekanandan-devtron Sep 2, 2026
ee628e5
fix: code review fixes
vivekanandan-devtron Sep 2, 2026
698b9df
fix: code review fixes
vivekanandan-devtron Sep 3, 2026
0a6e777
chore: rename migration scripts to maintain sync with enterprise
vivekanandan-devtron Sep 3, 2026
1e79e4b
Merge branch 'main' of github-devtron:devtron-labs/devtron into feat/…
vivekanandan-devtron Sep 7, 2026
992cbf3
chore: change migration script number
vivekanandan-devtron Sep 7, 2026
386edbd
fix: roles missing while fetching user roles
vivekanandan-devtron Sep 7, 2026
0c309cd
fix: deleted roles being listed
vivekanandan-devtron Sep 7, 2026
f7a54e3
fix: deleted roles being listed
vivekanandan-devtron Sep 7, 2026
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
33 changes: 30 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 All @@ -38,6 +39,16 @@ import (
"gopkg.in/go-playground/validator.v9"
)

const (
resultKeyArgoAppAccess = "hasArgoAppAccess"
resultKeyFluxAppAccess = "hasFluxAppAccess"
)

var gitOpsAccessResultKeyByResource = map[string]string{
casbin.ResourceArgoApp: resultKeyArgoAppAccess,
casbin.ResourceFluxApp: resultKeyFluxAppAccess,
}

type UserRestHandler interface {
CreateUser(w http.ResponseWriter, r *http.Request)
UpdateUser(w http.ResponseWriter, r *http.Request)
Expand Down Expand Up @@ -795,9 +806,25 @@ func (handler UserRestHandlerImpl) CheckUserRoles(w http.ResponseWriter, r *http
result := make(map[string]interface{})
result["roles"] = roles
result["superAdmin"] = false
result[resultKeyArgoAppAccess] = false
result[resultKeyFluxAppAccess] = false
for _, item := range roles {
if item == bean2.SUPERADMIN {
result["superAdmin"] = true
result[resultKeyArgoAppAccess] = true
result[resultKeyFluxAppAccess] = true
continue
}

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

if len(resourceActionFragment) < 2 {
continue
}

if resultKey, ok := gitOpsAccessResultKeyByResource[resourceActionFragment[0]]; ok {
result[resultKey] = 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