mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
fix: use glob matcher in casbin built-in model (#3966)
This commit is contained in:
parent
52926b7cb0
commit
dfd7457c21
7 changed files with 66 additions and 45 deletions
|
|
@ -11,4 +11,4 @@ g = _, _
|
|||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && keyMatch(r.res, p.res) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj)
|
||||
m = g(r.sub, p.sub) && globMatch(r.res, p.res) && globMatch(r.act, p.act) && globMatch(r.obj, p.obj)
|
||||
|
|
|
|||
|
|
@ -47,9 +47,8 @@ import (
|
|||
"github.com/argoproj/argo-cd/util/argo"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/glob"
|
||||
settings_util "github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -265,9 +264,9 @@ func isKnownOrphanedResourceExclusion(key kube.ResourceKey, proj *appv1.AppProje
|
|||
}
|
||||
list := proj.Spec.OrphanedResources.Ignore
|
||||
for _, item := range list {
|
||||
if item.Kind == "" || match(item.Kind, key.Kind) {
|
||||
if match(item.Group, key.Group) {
|
||||
if item.Name == "" || match(item.Name, key.Name) {
|
||||
if item.Kind == "" || glob.Match(item.Kind, key.Kind) {
|
||||
if glob.Match(item.Group, key.Group) {
|
||||
if item.Name == "" || glob.Match(item.Name, key.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -276,15 +275,6 @@ func isKnownOrphanedResourceExclusion(key kube.ResourceKey, proj *appv1.AppProje
|
|||
return false
|
||||
}
|
||||
|
||||
func match(pattern, text string) bool {
|
||||
compiledGlob, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
log.Warnf("failed to compile pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
return compiledGlob.Match(text)
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managedResources []*appv1.ResourceDiff) (*appv1.ApplicationTree, error) {
|
||||
nodes := make([]appv1.ResourceNode, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import (
|
|||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/gobwas/glob"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/glob"
|
||||
)
|
||||
|
||||
type normalizerPatch struct {
|
||||
|
|
@ -62,23 +62,14 @@ func NewIgnoreNormalizer(ignore []v1alpha1.ResourceIgnoreDifferences, overrides
|
|||
return &ignoreNormalizer{patches: patches}, nil
|
||||
}
|
||||
|
||||
func match(pattern, text string) bool {
|
||||
compiledGlob, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
log.Warnf("failed to compile pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
return compiledGlob.Match(text)
|
||||
}
|
||||
|
||||
// Normalize removes fields from supplied resource using json paths from matching items of specified resources ignored differences list
|
||||
func (n *ignoreNormalizer) Normalize(un *unstructured.Unstructured) error {
|
||||
matched := make([]normalizerPatch, 0)
|
||||
for _, patch := range n.patches {
|
||||
groupKind := un.GroupVersionKind().GroupKind()
|
||||
|
||||
if match(patch.groupKind.Group, groupKind.Group) &&
|
||||
match(patch.groupKind.Kind, groupKind.Kind) &&
|
||||
if glob.Match(patch.groupKind.Group, groupKind.Group) &&
|
||||
glob.Match(patch.groupKind.Kind, groupKind.Kind) &&
|
||||
(patch.name == "" || patch.name == un.GetName()) &&
|
||||
(patch.namespace == "" || patch.namespace == un.GetNamespace()) {
|
||||
|
||||
|
|
|
|||
15
util/glob/glob.go
Normal file
15
util/glob/glob.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package glob
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Match(pattern, text string) bool {
|
||||
compiledGlob, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
log.Warnf("failed to compile pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
return compiledGlob.Match(text)
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/assets"
|
||||
"github.com/argoproj/argo-cd/util/glob"
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
|
||||
"github.com/casbin/casbin"
|
||||
|
|
@ -54,10 +55,37 @@ type Enforcer struct {
|
|||
// ClaimsEnforcerFunc is func template to enforce a JWT claims. The subject is replaced
|
||||
type ClaimsEnforcerFunc func(claims jwt.Claims, rvals ...interface{}) bool
|
||||
|
||||
func newEnforcerSafe(params ...interface{}) (e *casbin.Enforcer, err error) {
|
||||
enfs, err := casbin.NewEnforcerSafe(params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enfs.AddFunction("globMatch", func(args ...interface{}) (interface{}, error) {
|
||||
if len(args) < 2 {
|
||||
return false, nil
|
||||
}
|
||||
val, ok := args[0].(string)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
pattern, ok := args[1].(string)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return glob.Match(pattern, val), nil
|
||||
})
|
||||
return enfs, nil
|
||||
}
|
||||
|
||||
func NewEnforcer(clientset kubernetes.Interface, namespace, configmap string, claimsEnforcer ClaimsEnforcerFunc) *Enforcer {
|
||||
adapter := newAdapter("", "", "")
|
||||
builtInModel := newBuiltInModel()
|
||||
enf := casbin.NewEnforcer(builtInModel, adapter)
|
||||
enf, err := newEnforcerSafe(builtInModel, adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enf.EnableLog(false)
|
||||
return &Enforcer{
|
||||
Enforcer: enf,
|
||||
|
|
@ -134,7 +162,7 @@ func (e *Enforcer) EnforceRuntimePolicy(policy string, rvals ...interface{}) boo
|
|||
if policy == "" {
|
||||
enf = e.Enforcer
|
||||
} else {
|
||||
enf, err = casbin.NewEnforcerSafe(newBuiltInModel(), newAdapter(e.adapter.builtinPolicy, e.adapter.userDefinedPolicy, policy))
|
||||
enf, err = newEnforcerSafe(newBuiltInModel(), newAdapter(e.adapter.builtinPolicy, e.adapter.userDefinedPolicy, policy))
|
||||
if err != nil {
|
||||
log.Warnf("invalid runtime policy: %s", policy)
|
||||
enf = e.Enforcer
|
||||
|
|
@ -259,7 +287,7 @@ func (e *Enforcer) syncUpdate(cm *apiv1.ConfigMap, onUpdated func(cm *apiv1.Conf
|
|||
|
||||
// ValidatePolicy verifies a policy string is acceptable to casbin
|
||||
func ValidatePolicy(policy string) error {
|
||||
_, err := casbin.NewEnforcerSafe(newBuiltInModel(), newAdapter("", "", policy))
|
||||
_, err := newEnforcerSafe(newBuiltInModel(), newAdapter("", "", policy))
|
||||
if err != nil {
|
||||
return fmt.Errorf("policy syntax error: %s", policy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ p, mike, *, *, foo/obj, deny
|
|||
p, trudy, applications, get, foo/obj, allow
|
||||
p, trudy, applications/*, get, foo/obj, allow
|
||||
p, trudy, applications/secrets, get, foo/obj, deny
|
||||
p, danny, applications, get, */obj, allow
|
||||
p, danny, applications, get, proj1/a*p1, allow
|
||||
`
|
||||
_ = enf.SetUserPolicy(policy)
|
||||
|
||||
|
|
@ -171,6 +173,13 @@ p, trudy, applications/secrets, get, foo/obj, deny
|
|||
assert.True(t, enf.Enforce("trudy", "applications", "get", "foo/obj"))
|
||||
assert.True(t, enf.Enforce("trudy", "applications/logs", "get", "foo/obj"))
|
||||
assert.False(t, enf.Enforce("trudy", "applications/secrets", "get", "foo/obj"))
|
||||
|
||||
// Verify trailing wildcards don't grant full access
|
||||
assert.True(t, enf.Enforce("danny", "applications", "get", "foo/obj"))
|
||||
assert.True(t, enf.Enforce("danny", "applications", "get", "bar/obj"))
|
||||
assert.False(t, enf.Enforce("danny", "applications", "get", "foo/bar"))
|
||||
assert.True(t, enf.Enforce("danny", "applications", "get", "proj1/app1"))
|
||||
assert.False(t, enf.Enforce("danny", "applications", "get", "proj1/app2"))
|
||||
}
|
||||
|
||||
// TestProjectIsolationEnforcement verifies the ability to create Project specific policies
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package settings
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
import "github.com/argoproj/argo-cd/util/glob"
|
||||
|
||||
type FilteredResource struct {
|
||||
APIGroups []string `json:"apiGroups,omitempty"`
|
||||
|
|
@ -13,22 +10,13 @@ type FilteredResource struct {
|
|||
|
||||
func (r FilteredResource) matchGroup(apiGroup string) bool {
|
||||
for _, excludedApiGroup := range r.APIGroups {
|
||||
if match(excludedApiGroup, apiGroup) {
|
||||
if glob.Match(excludedApiGroup, apiGroup) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return len(r.APIGroups) == 0
|
||||
}
|
||||
|
||||
func match(pattern, text string) bool {
|
||||
compiledGlob, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
log.Warnf("failed to compile pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
return compiledGlob.Match(text)
|
||||
}
|
||||
|
||||
func (r FilteredResource) matchKind(kind string) bool {
|
||||
for _, excludedKind := range r.Kinds {
|
||||
if excludedKind == "*" || excludedKind == kind {
|
||||
|
|
@ -40,7 +28,7 @@ func (r FilteredResource) matchKind(kind string) bool {
|
|||
|
||||
func (r FilteredResource) matchCluster(cluster string) bool {
|
||||
for _, excludedCluster := range r.Clusters {
|
||||
if match(excludedCluster, cluster) {
|
||||
if glob.Match(excludedCluster, cluster) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue