Skip to content

Commit

Permalink
fix: use glob matcher in casbin built-in model (argoproj#3966)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Matyushentsev authored and rachelwang20 committed Aug 5, 2020
1 parent 4c8f242 commit ab0a042
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 45 deletions.
2 changes: 1 addition & 1 deletion assets/model.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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)
18 changes: 4 additions & 14 deletions controller/appcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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)

Expand Down
15 changes: 3 additions & 12 deletions util/argo/normalizers/diff_normalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()) {

Expand Down
15 changes: 15 additions & 0 deletions util/glob/glob.go
Original file line number Diff line number Diff line change
@@ -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)
}
34 changes: 31 additions & 3 deletions util/rbac/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
9 changes: 9 additions & 0 deletions util/rbac/rbac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
18 changes: 3 additions & 15 deletions util/settings/filtered_resource.go
Original file line number Diff line number Diff line change
@@ -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"`
Expand All @@ -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 {
Expand All @@ -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
}
}
Expand Down

0 comments on commit ab0a042

Please sign in to comment.