From 8b35c264eb9980a29152e0bc31b57e5eb61cec03 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Thu, 2 Apr 2020 17:44:48 +0100 Subject: [PATCH 1/6] Pass resolver to api auth handler --- api/server/auth/auth.go | 30 +++++++++++++++++++++++------- api/server/http/http.go | 14 +++++++++----- api/server/options.go | 15 +++++++++++++++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/api/server/auth/auth.go b/api/server/auth/auth.go index 1bd60508..7e86f651 100644 --- a/api/server/auth/auth.go +++ b/api/server/auth/auth.go @@ -6,20 +6,25 @@ import ( "net/url" "strings" + "github.com/micro/go-micro/v2/api/resolver" "github.com/micro/go-micro/v2/auth" ) // CombinedAuthHandler wraps a server and authenticates requests -func CombinedAuthHandler(h http.Handler) http.Handler { +func CombinedAuthHandler(namespace string, r resolver.Resolver, h http.Handler) http.Handler { return authHandler{ - handler: h, - auth: auth.DefaultAuth, + handler: h, + resolver: r, + auth: auth.DefaultAuth, + namespace: namespace, } } type authHandler struct { - handler http.Handler - auth auth.Auth + handler http.Handler + auth auth.Auth + resolver resolver.Resolver + namespace string } func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -45,10 +50,21 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if err != nil { acc = &auth.Account{} } + + // Determine the name of the service being requested + endpoint, err := h.resolver.Resolve(req) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + resName := h.namespace + "." + endpoint.Name + + // Perform the verification check to see if the account has access to + // the resource they're requesting err = h.auth.Verify(acc, &auth.Resource{ Type: "service", - Name: "go.micro.web", - Endpoint: req.URL.Path, + Name: resName, + Endpoint: endpoint.Path, }) // The account has the necessary permissions to access the diff --git a/api/server/http/http.go b/api/server/http/http.go index 0af91256..2599d2db 100644 --- a/api/server/http/http.go +++ b/api/server/http/http.go @@ -8,10 +8,9 @@ import ( "os" "sync" - "github.com/micro/go-micro/v2/api/server/auth" - "github.com/gorilla/handlers" "github.com/micro/go-micro/v2/api/server" + "github.com/micro/go-micro/v2/api/server/auth" "github.com/micro/go-micro/v2/api/server/cors" "github.com/micro/go-micro/v2/logger" ) @@ -25,9 +24,14 @@ type httpServer struct { exit chan chan error } -func NewServer(address string) server.Server { +func NewServer(address string, opts ...server.Option) server.Server { + var options server.Options + for _, o := range opts { + o(&options) + } + return &httpServer{ - opts: server.Options{}, + opts: options, mux: http.NewServeMux(), address: address, exit: make(chan chan error), @@ -49,7 +53,7 @@ func (s *httpServer) Init(opts ...server.Option) error { func (s *httpServer) Handle(path string, handler http.Handler) { h := handlers.CombinedLoggingHandler(os.Stdout, handler) - h = auth.CombinedAuthHandler(handler) + h = auth.CombinedAuthHandler(s.opts.Namespace, s.opts.Resolver, handler) if s.opts.EnableCORS { h = cors.CombinedCORSHandler(h) diff --git a/api/server/options.go b/api/server/options.go index 99be1a03..5d167ced 100644 --- a/api/server/options.go +++ b/api/server/options.go @@ -3,6 +3,7 @@ package server import ( "crypto/tls" + "github.com/micro/go-micro/v2/api/resolver" "github.com/micro/go-micro/v2/api/server/acme" ) @@ -15,6 +16,8 @@ type Options struct { EnableTLS bool ACMEHosts []string TLSConfig *tls.Config + Namespace string + Resolver resolver.Resolver } func EnableCORS(b bool) Option { @@ -52,3 +55,15 @@ func TLSConfig(t *tls.Config) Option { o.TLSConfig = t } } + +func Namespace(n string) Option { + return func(o *Options) { + o.Namespace = n + } +} + +func Resolver(r resolver.Resolver) Option { + return func(o *Options) { + o.Resolver = r + } +} From fdcb013f2413bfe9e08c013241767955bf09a47e Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Fri, 3 Apr 2020 09:18:30 +0100 Subject: [PATCH 2/6] Fix web registry compatability bugs --- api/resolver/resolver.go | 6 ++++++ api/router/registry/registry.go | 2 +- api/server/auth/auth.go | 12 ++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/api/resolver/resolver.go b/api/resolver/resolver.go index 2e23d62e..12854b19 100644 --- a/api/resolver/resolver.go +++ b/api/resolver/resolver.go @@ -2,9 +2,15 @@ package resolver import ( + "errors" "net/http" ) +var ( + ErrNotFound = errors.New("not found") + ErrInvalidPath = errors.New("invalid path") +) + // Resolver resolves requests to endpoints type Resolver interface { Resolve(r *http.Request) (*Endpoint, error) diff --git a/api/router/registry/registry.go b/api/router/registry/registry.go index 375aa7d0..c024424f 100644 --- a/api/router/registry/registry.go +++ b/api/router/registry/registry.go @@ -325,7 +325,7 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) { } // no match - return nil, errors.New("not found") + return nil, registry.ErrNotFound } func (r *registryRouter) Route(req *http.Request) (*api.Service, error) { diff --git a/api/server/auth/auth.go b/api/server/auth/auth.go index 7e86f651..68e7f7a3 100644 --- a/api/server/auth/auth.go +++ b/api/server/auth/auth.go @@ -53,11 +53,19 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Determine the name of the service being requested endpoint, err := h.resolver.Resolve(req) - if err != nil { + if err == resolver.ErrInvalidPath || err == resolver.ErrNotFound { + // a file not served by the resolver has been requested (e.g. favicon.ico) + endpoint = &resolver.Endpoint{Path: req.URL.Path} + } else if err != nil { w.WriteHeader(http.StatusInternalServerError) return } - resName := h.namespace + "." + endpoint.Name + + // construct the resource name, e.g. home => go.micro.web.home + resName := h.namespace + if len(endpoint.Name) > 0 { + resName = resName + "." + endpoint.Name + } // Perform the verification check to see if the account has access to // the resource they're requesting From 760233b858dc3350b427b90da4d08c4c02e63687 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Fri, 3 Apr 2020 09:34:52 +0100 Subject: [PATCH 3/6] Reverse Change --- api/router/registry/registry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/router/registry/registry.go b/api/router/registry/registry.go index c024424f..375aa7d0 100644 --- a/api/router/registry/registry.go +++ b/api/router/registry/registry.go @@ -325,7 +325,7 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) { } // no match - return nil, registry.ErrNotFound + return nil, errors.New("not found") } func (r *registryRouter) Route(req *http.Request) (*api.Service, error) { From 183c8bfb818893f79c1caaf68531f43b7a9df19b Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Fri, 3 Apr 2020 09:45:39 +0100 Subject: [PATCH 4/6] Apply fix for apis --- api/server/auth/auth.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/api/server/auth/auth.go b/api/server/auth/auth.go index 68e7f7a3..0c486963 100644 --- a/api/server/auth/auth.go +++ b/api/server/auth/auth.go @@ -67,17 +67,19 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { resName = resName + "." + endpoint.Name } + // determine the resource path. there is an inconsistency in how resolvers + // use method, some use it as Users.ReadUser (the rpc method), and others + // use it as the HTTP method, e.g GET. TODO: Refactor this to make it consistent. + resEndpoint := endpoint.Path + if len(endpoint.Path) == 0 { + resEndpoint = endpoint.Method + } + // Perform the verification check to see if the account has access to // the resource they're requesting - err = h.auth.Verify(acc, &auth.Resource{ - Type: "service", - Name: resName, - Endpoint: endpoint.Path, - }) - - // The account has the necessary permissions to access the - // resource - if err == nil { + res := &auth.Resource{Type: "service", Name: resName, Endpoint: resEndpoint} + if err := h.auth.Verify(acc, res); err == nil { + // The account has the necessary permissions to access the resource h.handler.ServeHTTP(w, req) return } From 91b9c3f92eef36c86d088a688d00d34810075a04 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Fri, 3 Apr 2020 10:08:39 +0100 Subject: [PATCH 5/6] Add defaults --- api/server/auth/auth.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/server/auth/auth.go b/api/server/auth/auth.go index 0c486963..b17ef8ce 100644 --- a/api/server/auth/auth.go +++ b/api/server/auth/auth.go @@ -7,11 +7,19 @@ import ( "strings" "github.com/micro/go-micro/v2/api/resolver" + "github.com/micro/go-micro/v2/api/resolver/path" "github.com/micro/go-micro/v2/auth" ) // CombinedAuthHandler wraps a server and authenticates requests func CombinedAuthHandler(namespace string, r resolver.Resolver, h http.Handler) http.Handler { + if r == nil { + r = path.NewResolver() + } + if len(namespace) == 0 { + namespace = "go.micro" + } + return authHandler{ handler: h, resolver: r, From 1096c8fb39eba9b2ec625cd7af2b2420cc3f62aa Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Fri, 3 Apr 2020 10:16:19 +0100 Subject: [PATCH 6/6] Fix failing test --- api/resolver/path/path.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/resolver/path/path.go b/api/resolver/path/path.go index fec80f62..c4d41fc6 100644 --- a/api/resolver/path/path.go +++ b/api/resolver/path/path.go @@ -2,7 +2,6 @@ package path import ( - "errors" "net/http" "strings" @@ -13,7 +12,7 @@ type Resolver struct{} func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { if req.URL.Path == "/" { - return nil, errors.New("unknown name") + return nil, resolver.ErrNotFound } parts := strings.Split(req.URL.Path[1:], "/") return &resolver.Endpoint{