From eff19a636dec7c321a629e8b2f0ebd48301480b2 Mon Sep 17 00:00:00 2001 From: Muhammad Abduh Date: Wed, 4 Oct 2023 17:56:37 +0700 Subject: [PATCH] feat: support middleware group slug --- cmd/serve.go | 2 +- cmd/serve_proxy.go | 7 ++- internal/proxy/middleware/authz/authz.go | 43 ++++++++++++++++--- test/e2e_test/smoke/proxy_test.go | 2 +- test/e2e_test/testbench/spicedb.go | 2 +- .../testdata/configs/rules/rule.yaml | 13 +++++- .../testdata/configs/rules/rule.yamltpl | 11 +++++ 7 files changed, 69 insertions(+), 11 deletions(-) diff --git a/cmd/serve.go b/cmd/serve.go index a6d5e9a2b..b64994145 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -123,7 +123,7 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error { } // serving proxies - cbs, cps, err := serveProxies(ctx, logger, cfg.App.IdentityProxyHeader, cfg.App.UserIDHeader, cfg.Proxy, deps.ResourceService, deps.RelationService, deps.UserService, deps.ProjectService, deps.RelationAdapter) + cbs, cps, err := serveProxies(ctx, logger, cfg.App.IdentityProxyHeader, cfg.App.UserIDHeader, cfg.Proxy, deps.ResourceService, deps.RelationService, deps.UserService, deps.GroupService, deps.ProjectService, deps.RelationAdapter) if err != nil { return err } diff --git a/cmd/serve_proxy.go b/cmd/serve_proxy.go index 1cf6702a6..f7c42c313 100644 --- a/cmd/serve_proxy.go +++ b/cmd/serve_proxy.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/goto/salt/log" + "github.com/goto/shield/core/group" "github.com/goto/shield/core/project" "github.com/goto/shield/core/relation" "github.com/goto/shield/core/resource" @@ -34,6 +35,7 @@ func serveProxies( resourceService *resource.Service, relationService *relation.Service, userService *user.Service, + groupService *group.Service, projectService *project.Service, relationAdapter *adapter.Relation, ) ([]func() error, []func(ctx context.Context) error, error) { @@ -66,7 +68,7 @@ func serveProxies( ruleService := rule.NewService(ruleBlobRepository) - middlewarePipeline := buildMiddlewarePipeline(logger, h2cProxy, identityProxyHeaderKey, userIDHeaderKey, resourceService, userService, ruleService, projectService) + middlewarePipeline := buildMiddlewarePipeline(logger, h2cProxy, identityProxyHeaderKey, userIDHeaderKey, resourceService, userService, groupService, ruleService, projectService) cps := proxy.Serve(ctx, logger, svcConfig, middlewarePipeline) cleanUpProxies = append(cleanUpProxies, cps) @@ -93,12 +95,13 @@ func buildMiddlewarePipeline( identityProxyHeaderKey, userIDHeaderKey string, resourceService *resource.Service, userService *user.Service, + groupService *group.Service, ruleService *rule.Service, projectService *project.Service, ) http.Handler { // Note: execution order is bottom up prefixWare := prefix.New(logger, proxy) - casbinAuthz := authz.New(logger, prefixWare, userIDHeaderKey, resourceService, userService) + casbinAuthz := authz.New(logger, prefixWare, userIDHeaderKey, resourceService, userService, groupService) basicAuthn := basic_auth.New(logger, casbinAuthz) attributeExtractor := attributes.New(logger, basicAuthn, identityProxyHeaderKey, projectService) matchWare := rulematch.New(logger, attributeExtractor, rulematch.NewRouteMatcher(ruleService)) diff --git a/internal/proxy/middleware/authz/authz.go b/internal/proxy/middleware/authz/authz.go index 89ce10288..6b991135d 100644 --- a/internal/proxy/middleware/authz/authz.go +++ b/internal/proxy/middleware/authz/authz.go @@ -11,10 +11,13 @@ import ( "github.com/mitchellh/mapstructure" "github.com/goto/shield/core/action" + "github.com/goto/shield/core/group" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/user" "github.com/goto/shield/internal/proxy/middleware" + "github.com/goto/shield/internal/schema" "github.com/goto/shield/pkg/body_extractor" + "github.com/goto/shield/pkg/uuid" ) type ResourceService interface { @@ -25,12 +28,17 @@ type UserService interface { FetchCurrentUser(ctx context.Context) (user.User, error) } +type GroupService interface { + GetBySlug(ctx context.Context, slug string) (group.Group, error) +} + type Authz struct { log log.Logger userIDHeaderKey string next http.Handler resourceService ResourceService userService UserService + groupService GroupService } type Config struct { @@ -50,13 +58,15 @@ func New( next http.Handler, userIDHeaderKey string, resourceService ResourceService, - userService UserService) *Authz { + userService UserService, + groupService GroupService) *Authz { return &Authz{ log: log, userIDHeaderKey: userIDHeaderKey, next: next, resourceService: resourceService, userService: userService, + groupService: groupService, } } @@ -209,10 +219,13 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { isAuthorized := false for _, permission := range config.Permissions { - isAuthorized, err = c.resourceService.CheckAuthz(req.Context(), resource.Resource{ - Name: permissionAttributes[permission.Attribute].(string), - NamespaceID: permission.Namespace, - }, action.Action{ + res, err := c.preparePermissionResource(req.Context(), permission, permissionAttributes) + if err != nil { + c.log.Error("error while preparing permission resource", "err", err) + c.notAllowed(rw, err) + return + } + isAuthorized, err = c.resourceService.CheckAuthz(req.Context(), res, action.Action{ ID: permission.Name, }) if err != nil { @@ -235,6 +248,26 @@ func (c *Authz) ServeHTTP(rw http.ResponseWriter, req *http.Request) { c.next.ServeHTTP(rw, req) } +func (c Authz) preparePermissionResource(ctx context.Context, perm Permission, attrs map[string]interface{}) (resource.Resource, error) { + resourceName := attrs[perm.Attribute].(string) + res := resource.Resource{ + Name: resourceName, + NamespaceID: perm.Namespace, + } + + if perm.Namespace == schema.GroupNamespace { + // resolve group id from slug + if !uuid.IsValid(resourceName) { + grp, err := c.groupService.GetBySlug(ctx, resourceName) + if err != nil { + return resource.Resource{}, err + } + res.Name = grp.ID + } + } + return res, nil +} + func (w Authz) notAllowed(rw http.ResponseWriter, err error) { if err != nil { switch { diff --git a/test/e2e_test/smoke/proxy_test.go b/test/e2e_test/smoke/proxy_test.go index 5655470b3..5b9821bba 100644 --- a/test/e2e_test/smoke/proxy_test.go +++ b/test/e2e_test/smoke/proxy_test.go @@ -146,7 +146,7 @@ func (s *EndToEndProxySmokeTestSuite) TestProxyToEchoServer() { req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(reqBodyBytes)) s.Require().NoError(err) - req.Header.Set(testbench.IdentityHeader, "member2-group1@gotocompany.com") + req.Header.Set(testbench.IdentityHeader, "admin1-group1-org1@gotocompany.com") req.Header.Set("X-Shield-Org", s.orgID) res, err := http.DefaultClient.Do(req) diff --git a/test/e2e_test/testbench/spicedb.go b/test/e2e_test/testbench/spicedb.go index 628061d81..6f086f8ea 100644 --- a/test/e2e_test/testbench/spicedb.go +++ b/test/e2e_test/testbench/spicedb.go @@ -27,7 +27,7 @@ func migrateSpiceDB(logger log.Logger, network *docker.Network, pool *dockertest return err } - if err := resource.Expire(120); err != nil { + if err := resource.Expire(360); err != nil { return err } diff --git a/test/e2e_test/testbench/testdata/configs/rules/rule.yaml b/test/e2e_test/testbench/testdata/configs/rules/rule.yaml index 4e10f07fa..9c28052e7 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:51266" + target: "http://localhost:60127" frontends: - name: ping path: "/api/ping" @@ -40,6 +40,17 @@ rules: - name: create_resource_group_slug path: "/api/resource_slug" method: "POST" + middlewares: + - name: authz + config: + attributes: + owner_group: + value: org1-group1 + type: constant + permissions: + - name: view + namespace: shield/group + attribute: owner_group hooks: - name: authz config: diff --git a/test/e2e_test/testbench/testdata/configs/rules/rule.yamltpl b/test/e2e_test/testbench/testdata/configs/rules/rule.yamltpl index 9835c6367..b51a7490b 100644 --- a/test/e2e_test/testbench/testdata/configs/rules/rule.yamltpl +++ b/test/e2e_test/testbench/testdata/configs/rules/rule.yamltpl @@ -40,6 +40,17 @@ rules: - name: create_resource_group_slug path: "/api/resource_slug" method: "POST" + middlewares: + - name: authz + config: + attributes: + owner_group: + value: org1-group1 + type: constant + permissions: + - name: view + namespace: shield/group + attribute: owner_group hooks: - name: authz config: