From 309f19144ccbff167b24771a5a7205df61bfa0b4 Mon Sep 17 00:00:00 2001 From: Farshid Tavakolizadeh Date: Thu, 22 Oct 2020 11:03:41 +0200 Subject: [PATCH] upgrade go-sec dependency for path substr access control --- go.mod | 2 +- go.sum | 4 +++ sample_conf/thing-directory.json | 12 ++++--- .../auth/keycloak/validator/validator.go | 2 ++ .../linksmart/go-sec/authz/authz.go | 34 +++++++++++++------ .../linksmart/go-sec/authz/claims.go | 1 + .../linksmart/go-sec/authz/config.go | 29 +++++++++++----- vendor/modules.txt | 2 +- 8 files changed, 62 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index f0ef0538..aa4f9dd8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/grandcat/zeroconf v1.0.1-0.20200528163356-cfc8183341d9 github.com/justinas/alice v0.0.0-20160512134231-052b8b6c18ed github.com/kelseyhightower/envconfig v1.4.0 - github.com/linksmart/go-sec v1.3.3 + github.com/linksmart/go-sec v1.4.0 github.com/linksmart/service-catalog/v3 v3.0.0-beta.1.0.20200302143206-92739dd2a511 github.com/miekg/dns v1.1.29 // indirect github.com/onsi/ginkgo v1.12.0 // indirect diff --git a/go.sum b/go.sum index c578a18a..1500111e 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,10 @@ github.com/linksmart/go-sec v1.3.2 h1:FSW9bvXGFZouNAqJYuv9Kh7Bfhbkezt9V/6/hbgMbA github.com/linksmart/go-sec v1.3.2/go.mod h1:W9EZRLqptioAzaxMjWEKzd5jye53aoRzMi4KO+FCFjY= github.com/linksmart/go-sec v1.3.3 h1:i+wVndlGK4jWujpFWxn1rXZpxkSvDj8GLf9KXAZkTZk= github.com/linksmart/go-sec v1.3.3/go.mod h1:W9EZRLqptioAzaxMjWEKzd5jye53aoRzMi4KO+FCFjY= +github.com/linksmart/go-sec v1.3.4-0.20201015112134-ae3c2c66dac4 h1:uAM2jQ2nxYFpe+1wbtxG2iU4GLDZzKGi6cl2TLq7hnk= +github.com/linksmart/go-sec v1.3.4-0.20201015112134-ae3c2c66dac4/go.mod h1:W9EZRLqptioAzaxMjWEKzd5jye53aoRzMi4KO+FCFjY= +github.com/linksmart/go-sec v1.4.0 h1:8MjTfzRc3KBpCECpNNXOBC2/zupVlplc1N5bHFABvHs= +github.com/linksmart/go-sec v1.4.0/go.mod h1:W9EZRLqptioAzaxMjWEKzd5jye53aoRzMi4KO+FCFjY= github.com/linksmart/service-catalog/v3 v3.0.0-beta.1.0.20200302143206-92739dd2a511 h1:JNHuaKtZUDsgbGJ5bdFBZ4vIUlJB7EBvjLdSaNOFatQ= github.com/linksmart/service-catalog/v3 v3.0.0-beta.1.0.20200302143206-92739dd2a511/go.mod h1:2C0k5NvYvMgX2y095WCfuhpfZyKrZXX/TjYxlgR9K8g= github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= diff --git a/sample_conf/thing-directory.json b/sample_conf/thing-directory.json index ee3d5ab4..3d26d96f 100644 --- a/sample_conf/thing-directory.json +++ b/sample_conf/thing-directory.json @@ -27,16 +27,20 @@ "enabled": true, "rules": [ { - "resources": ["/td"], + "paths": ["/td"], "methods": ["GET","POST", "PUT", "DELETE"], "users": ["admin"], - "groups": [] + "groups": [], + "clients": [], + "denyPathSubstrings": [] }, { - "resources": ["/td"], + "paths": ["/td"], "methods": ["GET"], "users": [], - "groups": ["anonymous"] + "groups": ["anonymous"], + "clients": [], + "denyPathSubstrings": [] } ] } diff --git a/vendor/github.com/linksmart/go-sec/auth/keycloak/validator/validator.go b/vendor/github.com/linksmart/go-sec/auth/keycloak/validator/validator.go index 6a8d1afb..03d3935c 100644 --- a/vendor/github.com/linksmart/go-sec/auth/keycloak/validator/validator.go +++ b/vendor/github.com/linksmart/go-sec/auth/keycloak/validator/validator.go @@ -43,6 +43,7 @@ func (v *KeycloakValidator) Validate(serverAddr, clientID, tokenString string) ( Type string `json:"typ"` PreferredUsername string `json:"preferred_username"` Groups []string `json:"groups"` + Roles []string `json:"roles"` ClientID string `json:"clientID"` // for tokens issued as part of client credentials grant } // Parse the jwt id_token @@ -94,6 +95,7 @@ func (v *KeycloakValidator) Validate(serverAddr, clientID, tokenString string) ( return true, &authz.Claims{ Username: claims.PreferredUsername, Groups: claims.Groups, + Roles: claims.Roles, ClientID: claims.ClientID, }, nil } diff --git a/vendor/github.com/linksmart/go-sec/authz/authz.go b/vendor/github.com/linksmart/go-sec/authz/authz.go index 253848fa..1edc2db4 100644 --- a/vendor/github.com/linksmart/go-sec/authz/authz.go +++ b/vendor/github.com/linksmart/go-sec/authz/authz.go @@ -10,28 +10,42 @@ import ( // GroupAnonymous is the group name for unauthenticated users const GroupAnonymous = "anonymous" -// Authorized checks whether a user/group is authorized to access a resource using the specific method -func (authz *Conf) Authorized(resource, method string, claims *Claims) bool { +// Authorized checks whether a user/group is authorized to access a path using the specific method +func (authz *Conf) Authorized(path, method string, claims *Claims) bool { if claims == nil { claims = &Claims{Groups: []string{GroupAnonymous}} } // Create a tree of paths // e.g. /path1/path2/path3 -> [/path1/path2/path3 /path1/path2 /path1] // e.g. / -> [/] - resourceSplit := strings.Split(resource, "/")[1:] // split and drop the first part (empty string before slash) - resourceTree := make([]string, 0, len(resourceSplit)) + pathSplit := strings.Split(path, "/")[1:] // split and drop the first part (empty string before slash) + pathTree := make([]string, 0, len(pathSplit)) // construct tree from longest to shortest (/path1) path - for i := len(resourceSplit); i >= 1; i-- { - resourceTree = append(resourceTree, "/"+strings.Join(resourceSplit[:i], "/")) + for i := len(pathSplit); i >= 1; i-- { + pathTree = append(pathTree, "/"+strings.Join(pathSplit[:i], "/")) } - //fmt.Printf("%s -> %v -> %v\n", resource, resourceSplit, resourceTree) + //fmt.Printf("%s -> %v -> %v\n", path, pathSplit, pathTree) for _, rule := range authz.Rules { - for _, res := range resourceTree { + // take Paths from deprecated Resources + if len(rule.Paths) == 0 && len(rule.Resources) != 0 { + rule.Paths = rule.Resources + } + + for _, substr := range rule.DenyPathSubstrtings { + if strings.Contains(path, substr) { + return false + } + } + + for _, p := range pathTree { // Return true if user or group matches a rule - if inSlice(res, rule.Resources) && + if inSlice(p, rule.Paths) && inSlice(method, rule.Methods) && - (inSlice(claims.Username, rule.Users) || hasIntersection(claims.Groups, rule.Groups) || inSlice(claims.ClientID, rule.Clients)) { + (inSlice(claims.Username, rule.Users) || + hasIntersection(claims.Groups, rule.Groups) || + hasIntersection(claims.Roles, rule.Roles) || + inSlice(claims.ClientID, rule.Clients)) { return true } } diff --git a/vendor/github.com/linksmart/go-sec/authz/claims.go b/vendor/github.com/linksmart/go-sec/authz/claims.go index bdb3ea9a..12dee227 100644 --- a/vendor/github.com/linksmart/go-sec/authz/claims.go +++ b/vendor/github.com/linksmart/go-sec/authz/claims.go @@ -4,6 +4,7 @@ package authz type Claims struct { Username string Groups []string + Roles []string ClientID string // for tokens issued as part of client credentials grant // Status is the message given when token is not validated Status string diff --git a/vendor/github.com/linksmart/go-sec/authz/config.go b/vendor/github.com/linksmart/go-sec/authz/config.go index fbbe7085..c37f9f87 100644 --- a/vendor/github.com/linksmart/go-sec/authz/config.go +++ b/vendor/github.com/linksmart/go-sec/authz/config.go @@ -2,7 +2,10 @@ package authz -import "errors" +import ( + "errors" + "fmt" +) // Authorization struct type Conf struct { @@ -14,11 +17,15 @@ type Conf struct { // Authorization rule type Rule struct { + Paths []string `json:"paths"` + Methods []string `json:"methods"` + Users []string `json:"users"` + Groups []string `json:"groups"` + Roles []string `json:"roles"` + Clients []string `json:"clients"` + DenyPathSubstrtings []string `json:"denyPathSubstrings"` + // Deprecated. Use Paths instead. Resources []string `json:"resources"` - Methods []string `json:"methods"` - Users []string `json:"users"` - Groups []string `json:"groups"` - Clients []string `json:"clients"` } // Validate authorization config @@ -26,14 +33,20 @@ func (authz *Conf) Validate() error { // Check each authorization rule for _, rule := range authz.Rules { - if len(rule.Resources) == 0 { + // take Paths from deprecated Resources + if len(rule.Paths) == 0 && len(rule.Resources) != 0 { + fmt.Println("go-sec/authz: rules.resources config is deprecated. Use rules.paths instead.") + rule.Paths = rule.Resources + } + + if len(rule.Paths) == 0 { return errors.New("no resources in an authorization rule") } if len(rule.Methods) == 0 { return errors.New("no methods in an authorization rule") } - if len(rule.Users)+len(rule.Groups)+len(rule.Clients) == 0 { - return errors.New("at least one user, group, or client must be set in each authorization rule") + if len(rule.Users)+len(rule.Groups)+len(rule.Roles)+len(rule.Clients) == 0 { + return errors.New("at least one user, group, role, or client must be set in each authorization rule") } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 1f53bb9a..aca8870b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -43,7 +43,7 @@ github.com/justinas/alice # github.com/kelseyhightower/envconfig v1.4.0 ## explicit github.com/kelseyhightower/envconfig -# github.com/linksmart/go-sec v1.3.3 +# github.com/linksmart/go-sec v1.4.0 ## explicit github.com/linksmart/go-sec/auth/keycloak/obtainer github.com/linksmart/go-sec/auth/keycloak/validator