diff --git a/internal/proxy/middleware/attribute.go b/internal/proxy/attribute/attribute.go similarity index 57% rename from internal/proxy/middleware/attribute.go rename to internal/proxy/attribute/attribute.go index 080efedfe..b540f385d 100644 --- a/internal/proxy/middleware/attribute.go +++ b/internal/proxy/attribute/attribute.go @@ -1,12 +1,21 @@ -package middleware +package attribute + +import ( + "strings" + + "github.com/valyala/fasttemplate" +) const ( - AttributeTypeQuery AttributeType = "query" - AttributeTypeHeader AttributeType = "header" AttributeTypeJSONPayload AttributeType = "json_payload" AttributeTypeGRPCPayload AttributeType = "grpc_payload" - AttributeTypePathParam AttributeType = "path_param" + AttributeTypeQuery AttributeType = "query" + AttributeTypeHeader AttributeType = "header" AttributeTypeConstant AttributeType = "constant" + AttributeTypeComposite AttributeType = "composite" + + SourceRequest AttributeType = "request" + SourceResponse AttributeType = "response" ) type AttributeType string @@ -18,4 +27,13 @@ type Attribute struct { Path string `yaml:"path" mapstructure:"path"` Params []string `yaml:"params" mapstructure:"params"` Value string `yaml:"value" mapstructure:"value"` + Source string `yaml:"source" mapstructure:"source"` +} + +func ComposeAttribute(attribute string, attrs map[string]interface{}) string { + if strings.Contains(attribute, "{") { + template := fasttemplate.New(attribute, "{", "}") + return template.ExecuteString(attrs) + } + return attribute } diff --git a/internal/proxy/helper.go b/internal/proxy/helper.go deleted file mode 100644 index 507a00189..000000000 --- a/internal/proxy/helper.go +++ /dev/null @@ -1,15 +0,0 @@ -package proxy - -import ( - "strings" - - "github.com/valyala/fasttemplate" -) - -func ComposeAttribute(attribute string, attrs map[string]interface{}) string { - if strings.Contains(attribute, "{") { - template := fasttemplate.New(attribute, "{", "}") - return template.ExecuteString(attrs) - } - return attribute -} diff --git a/internal/proxy/hook/authz/authz.go b/internal/proxy/hook/authz/authz.go index d1433c9c6..5408b98d8 100644 --- a/internal/proxy/hook/authz/authz.go +++ b/internal/proxy/hook/authz/authz.go @@ -18,7 +18,7 @@ import ( "github.com/goto/shield/core/relation" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/user" - "github.com/goto/shield/internal/proxy" + proxyattr "github.com/goto/shield/internal/proxy/attribute" "github.com/goto/shield/internal/proxy/hook" "github.com/goto/shield/internal/proxy/middleware" "github.com/goto/shield/pkg/body_extractor" @@ -93,9 +93,9 @@ type Relation struct { } type Config struct { - Action string `yaml:"action" mapstructure:"action"` - Attributes map[string]hook.Attribute `yaml:"attributes" mapstructure:"attributes"` - Relations []Relation `yaml:"relations" mapstructure:"relations"` + Action string `yaml:"action" mapstructure:"action"` + Attributes map[string]proxyattr.Attribute `yaml:"attributes" mapstructure:"attributes"` + Relations []Relation `yaml:"relations" mapstructure:"relations"` } func (a Authz) Info() hook.Info { @@ -152,17 +152,17 @@ func (a Authz) ServeHook(res *http.Response, err error) (*http.Response, error) for id, attr := range config.Attributes { bdy, _ := middleware.ExtractRequestBody(res.Request) bodySource := &res.Body - if attr.Source == string(hook.SourceRequest) { + if attr.Source == string(proxyattr.SourceRequest) { bodySource = &bdy } headerSource := &res.Header - if attr.Source == string(hook.SourceRequest) { + if attr.Source == string(proxyattr.SourceRequest) { headerSource = &res.Request.Header } switch attr.Type { - case hook.AttributeTypeGRPCPayload: + case proxyattr.AttributeTypeGRPCPayload: if !strings.HasPrefix(res.Header.Get("Content-Type"), "application/grpc") { a.log.Error("middleware: not a grpc request", "attr", attr) return a.escape.ServeHook(res, fmt.Errorf("invalid header for http request: %s", res.Header.Get("Content-Type"))) @@ -176,7 +176,7 @@ func (a Authz) ServeHook(res *http.Response, err error) (*http.Response, error) attributes[id] = payloadField a.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case hook.AttributeTypeJSONPayload: + case proxyattr.AttributeTypeJSONPayload: if attr.Key == "" { a.log.Error("middleware: payload key field empty") return a.escape.ServeHook(res, fmt.Errorf("payload key field empty")) @@ -190,7 +190,7 @@ func (a Authz) ServeHook(res *http.Response, err error) (*http.Response, error) attributes[id] = payloadField a.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case hook.AttributeTypeHeader: + case proxyattr.AttributeTypeHeader: if attr.Key == "" { a.log.Error("middleware: header key field empty") return a.escape.ServeHook(res, fmt.Errorf("failed to parse json payload")) @@ -204,7 +204,7 @@ func (a Authz) ServeHook(res *http.Response, err error) (*http.Response, error) attributes[id] = headerAttr a.log.Info("middleware: extracted", "field", headerAttr, "attr", attr) - case hook.AttributeTypeQuery: + case proxyattr.AttributeTypeQuery: if attr.Key == "" { a.log.Error("middleware: query key field empty") return a.escape.ServeHook(res, fmt.Errorf("failed to parse json payload")) @@ -218,7 +218,7 @@ func (a Authz) ServeHook(res *http.Response, err error) (*http.Response, error) attributes[id] = queryAttr a.log.Info("middleware: extracted", "field", queryAttr, "attr", attr) - case hook.AttributeTypeConstant, hook.AttributeTypeComposite: + case proxyattr.AttributeTypeConstant, proxyattr.AttributeTypeComposite: if attr.Value == "" { a.log.Error("middleware:", string(attr.Type), "value empty") return a.escape.ServeHook(res, fmt.Errorf("failed to parse json payload")) @@ -396,7 +396,7 @@ func getAttributesValues(attributes interface{}) ([]string, error) { func composeResourcesName(resourceList []string, permissionAttributes map[string]interface{}) []string { var resourcesName []string for _, res := range resourceList { - resourcesName = append(resourcesName, proxy.ComposeAttribute(res, permissionAttributes)) + resourcesName = append(resourcesName, proxyattr.ComposeAttribute(res, permissionAttributes)) } return resourcesName } diff --git a/internal/proxy/hook/authz/authz_test.go b/internal/proxy/hook/authz/authz_test.go index 1691b66e7..de3b12f6d 100644 --- a/internal/proxy/hook/authz/authz_test.go +++ b/internal/proxy/hook/authz/authz_test.go @@ -16,6 +16,7 @@ import ( "github.com/goto/shield/core/relation" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/rule" + "github.com/goto/shield/internal/proxy/attribute" "github.com/goto/shield/internal/proxy/hook" "github.com/goto/shield/internal/proxy/hook/authz/mocks" shieldlogger "github.com/goto/shield/pkg/logger" @@ -275,7 +276,7 @@ func TestServeHook(t *testing.T) { rule.HookSpec{ Name: "authz", Config: map[string]interface{}{ - "attributes": map[string]hook.Attribute{ + "attributes": map[string]attribute.Attribute{ "project": { Type: "constant", Value: testPermissionAttributesMap["project"].(string), @@ -346,7 +347,7 @@ func TestServeHook(t *testing.T) { rule.HookSpec{ Name: "authz", Config: map[string]interface{}{ - "attributes": map[string]hook.Attribute{ + "attributes": map[string]attribute.Attribute{ "project": { Type: "constant", Value: testPermissionAttributesMap["project"].(string), @@ -442,7 +443,7 @@ func TestServeHook(t *testing.T) { rule.HookSpec{ Name: "authz", Config: map[string]interface{}{ - "attributes": map[string]hook.Attribute{ + "attributes": map[string]attribute.Attribute{ "project": { Type: "constant", Value: testPermissionAttributesMap["project"].(string), @@ -541,7 +542,7 @@ func TestServeHook(t *testing.T) { rule.HookSpec{ Name: "authz", Config: map[string]interface{}{ - "attributes": map[string]hook.Attribute{ + "attributes": map[string]attribute.Attribute{ "project": { Type: "constant", Value: testPermissionAttributesMap["project"].(string), @@ -641,7 +642,7 @@ func TestServeHook(t *testing.T) { rule.HookSpec{ Name: "authz", Config: map[string]interface{}{ - "attributes": map[string]hook.Attribute{ + "attributes": map[string]attribute.Attribute{ "project": { Type: "constant", Value: testPermissionAttributesMap["project"].(string), diff --git a/internal/proxy/hook/hook.go b/internal/proxy/hook/hook.go index fa405c95f..bc30cfdda 100644 --- a/internal/proxy/hook/hook.go +++ b/internal/proxy/hook/hook.go @@ -17,28 +17,6 @@ type Info struct { Description string } -const ( - AttributeTypeJSONPayload AttributeType = "json_payload" - AttributeTypeGRPCPayload AttributeType = "grpc_payload" - AttributeTypeQuery AttributeType = "query" - AttributeTypeHeader AttributeType = "header" - AttributeTypeConstant AttributeType = "constant" - AttributeTypeComposite AttributeType = "composite" - - SourceRequest AttributeType = "request" - SourceResponse AttributeType = "response" -) - -type AttributeType string - -type Attribute struct { - Key string `yaml:"key" mapstructure:"key"` - Type AttributeType `yaml:"type" mapstructure:"type"` - Index string `yaml:"index" mapstructure:"index"` // proto index - Source string `yaml:"source" mapstructure:"source"` - Value string `yaml:"value" mapstructure:"value"` -} - func ExtractHook(r *http.Request, name string) (rule.HookSpec, bool) { rl, ok := ExtractRule(r) if !ok { diff --git a/internal/proxy/middleware/attributes/attributes.go b/internal/proxy/middleware/attributes/attributes.go index 67f2bc21a..2fdc12111 100644 --- a/internal/proxy/middleware/attributes/attributes.go +++ b/internal/proxy/middleware/attributes/attributes.go @@ -12,6 +12,7 @@ import ( "github.com/goto/salt/log" "github.com/goto/shield/core/project" + "github.com/goto/shield/internal/proxy/attribute" "github.com/goto/shield/internal/proxy/middleware" "github.com/goto/shield/pkg/body_extractor" ) @@ -24,7 +25,7 @@ type Attributes struct { } type Config struct { - Attributes map[string]middleware.Attribute `yaml:"attributes" mapstructure:"attributes"` + Attributes map[string]attribute.Attribute `yaml:"attributes" mapstructure:"attributes"` } type ProjectService interface { @@ -86,7 +87,7 @@ func (a *Attributes) ServeHTTP(rw http.ResponseWriter, req *http.Request) { _ = res switch attr.Type { - case middleware.AttributeTypeGRPCPayload: + case attribute.AttributeTypeGRPCPayload: // check if grpc request if !strings.HasPrefix(req.Header.Get("Content-Type"), "application/grpc") { a.log.Error("middleware: not a grpc request", "attr", attr) @@ -105,7 +106,7 @@ func (a *Attributes) ServeHTTP(rw http.ResponseWriter, req *http.Request) { requestAttributes[res] = payloadField a.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case middleware.AttributeTypeJSONPayload: + case attribute.AttributeTypeJSONPayload: if attr.Key == "" { a.log.Error("middleware: payload key field empty") a.notAllowed(rw) @@ -121,7 +122,7 @@ func (a *Attributes) ServeHTTP(rw http.ResponseWriter, req *http.Request) { requestAttributes[res] = payloadField a.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case middleware.AttributeTypeHeader: + case attribute.AttributeTypeHeader: if attr.Key == "" { a.log.Error("middleware: header key field empty") a.notAllowed(rw) @@ -137,7 +138,7 @@ func (a *Attributes) ServeHTTP(rw http.ResponseWriter, req *http.Request) { requestAttributes[res] = headerAttr a.log.Info("middleware: extracted", "field", headerAttr, "attr", attr) - case middleware.AttributeTypeQuery: + case attribute.AttributeTypeQuery: if attr.Key == "" { a.log.Error("middleware: query key field empty") a.notAllowed(rw) @@ -153,7 +154,7 @@ func (a *Attributes) ServeHTTP(rw http.ResponseWriter, req *http.Request) { requestAttributes[res] = queryAttr a.log.Info("middleware: extracted", "field", queryAttr, "attr", attr) - case middleware.AttributeTypeConstant: + case attribute.AttributeTypeConstant: if attr.Value == "" { a.log.Error("middleware: constant value empty") a.notAllowed(rw) diff --git a/internal/proxy/middleware/authz/authz.go b/internal/proxy/middleware/authz/authz.go index 587f19c1c..fa6b0b620 100644 --- a/internal/proxy/middleware/authz/authz.go +++ b/internal/proxy/middleware/authz/authz.go @@ -14,7 +14,7 @@ import ( "github.com/goto/shield/core/group" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/user" - "github.com/goto/shield/internal/proxy" + "github.com/goto/shield/internal/proxy/attribute" "github.com/goto/shield/internal/proxy/middleware" "github.com/goto/shield/internal/schema" "github.com/goto/shield/pkg/body_extractor" @@ -44,9 +44,9 @@ type Authz struct { } type Config struct { - Actions []string `yaml:"actions" mapstructure:"actions"` - Permissions []Permission `yaml:"permissions" mapstructure:"permissions"` - Attributes map[string]middleware.Attribute `yaml:"attributes" mapstructure:"attributes"` + Actions []string `yaml:"actions" mapstructure:"actions"` + Permissions []Permission `yaml:"permissions" mapstructure:"permissions"` + Attributes map[string]attribute.Attribute `yaml:"attributes" mapstructure:"attributes"` } type Permission struct { @@ -133,7 +133,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { _ = res switch attr.Type { - case middleware.AttributeTypeGRPCPayload: + case attribute.AttributeTypeGRPCPayload: // check if grpc request if !strings.HasPrefix(req.Header.Get("Content-Type"), "application/grpc") { c.log.Error("middleware: not a grpc request", "attr", attr) @@ -151,7 +151,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { permissionAttributes[res] = payloadField c.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case middleware.AttributeTypeJSONPayload: + case attribute.AttributeTypeJSONPayload: if attr.Key == "" { c.log.Error("middleware: payload key field empty") c.notAllowed(rw, nil) @@ -167,7 +167,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { permissionAttributes[res] = payloadField c.log.Info("middleware: extracted", "field", payloadField, "attr", attr) - case middleware.AttributeTypeHeader: + case attribute.AttributeTypeHeader: if attr.Key == "" { c.log.Error("middleware: header key field empty") c.notAllowed(rw, nil) @@ -183,7 +183,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { permissionAttributes[res] = headerAttr c.log.Info("middleware: extracted", "field", headerAttr, "attr", attr) - case middleware.AttributeTypeQuery: + case attribute.AttributeTypeQuery: if attr.Key == "" { c.log.Error("middleware: query key field empty") c.notAllowed(rw, nil) @@ -199,7 +199,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { permissionAttributes[res] = queryAttr c.log.Info("middleware: extracted", "field", queryAttr, "attr", attr) - case middleware.AttributeTypeConstant: + case attribute.AttributeTypeConstant: if attr.Value == "" { c.log.Error("middleware: constant value empty") c.notAllowed(rw, nil) @@ -279,7 +279,7 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (c Authz) preparePermissionResource(ctx context.Context, perm Permission, attrs map[string]interface{}) (resource.Resource, error) { resourceName, ok := attrs[perm.Attribute].(string) if !ok { - resourceName = proxy.ComposeAttribute(perm.Attribute, attrs) + resourceName = attribute.ComposeAttribute(perm.Attribute, attrs) } res := resource.Resource{ diff --git a/internal/proxy/middleware/basic_auth/auth.go b/internal/proxy/middleware/basic_auth/auth.go index 585abd8f8..fe179abcb 100644 --- a/internal/proxy/middleware/basic_auth/auth.go +++ b/internal/proxy/middleware/basic_auth/auth.go @@ -7,6 +7,7 @@ import ( "strings" "text/template" + "github.com/goto/shield/internal/proxy/attribute" "github.com/goto/shield/internal/proxy/middleware" "github.com/goto/shield/pkg/body_extractor" "github.com/goto/shield/pkg/httputil" @@ -53,8 +54,8 @@ type Credentials struct { } type Scope struct { - Action string `yaml:"action" mapstructure:"action"` - Attributes map[string]middleware.Attribute `yaml:"attributes" mapstructure:"attributes"` // auth field -> Attribute + Action string `yaml:"action" mapstructure:"action"` + Attributes map[string]attribute.Attribute `yaml:"attributes" mapstructure:"attributes"` // auth field -> Attribute } func New(logger log.Logger, next http.Handler) *BasicAuth { @@ -139,7 +140,7 @@ func (w BasicAuth) authorizeRequest(conf Config, user string, req *http.Request) for res, attr := range conf.Scope.Attributes { templateMap[res] = "" switch attr.Type { - case middleware.AttributeTypeGRPCPayload: + case attribute.AttributeTypeGRPCPayload: // check if grpc request if !strings.HasPrefix(req.Header.Get("Content-Type"), "application/grpc") { w.log.Error("middleware: not a valid grpc request") @@ -155,7 +156,7 @@ func (w BasicAuth) authorizeRequest(conf Config, user string, req *http.Request) templateMap[res] = payloadField w.log.Debug("middleware: extracted", "field", payloadField, "attr", attr) - case middleware.AttributeTypeJSONPayload: + case attribute.AttributeTypeJSONPayload: if attr.Key == "" { w.log.Error("middleware: payload key field empty") return false diff --git a/test/e2e_test/testbench/testdata/configs/rules/rule.yaml b/test/e2e_test/testbench/testdata/configs/rules/rule.yaml index e7db87f6c..92f2ad785 100644 --- a/test/e2e_test/testbench/testdata/configs/rules/rule.yaml +++ b/test/e2e_test/testbench/testdata/configs/rules/rule.yaml @@ -1,7 +1,7 @@ rules: - backends: - name: entropy - target: "http://localhost:51612" + target: "http://localhost:53123" frontends: - name: ping path: "/api/ping"