Merge pull request #1475 from micro/auth-resolver

Auth integrate resolver to support micro web & api
This commit is contained in:
ben-toogood 2020-04-06 14:57:41 +01:00 committed by GitHub
commit 0f570d98e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 21 deletions

View File

@ -2,7 +2,6 @@
package path package path
import ( import (
"errors"
"net/http" "net/http"
"strings" "strings"
@ -13,7 +12,7 @@ type Resolver struct{}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" { if req.URL.Path == "/" {
return nil, errors.New("unknown name") return nil, resolver.ErrNotFound
} }
parts := strings.Split(req.URL.Path[1:], "/") parts := strings.Split(req.URL.Path[1:], "/")
return &resolver.Endpoint{ return &resolver.Endpoint{

View File

@ -2,9 +2,15 @@
package resolver package resolver
import ( import (
"errors"
"net/http" "net/http"
) )
var (
ErrNotFound = errors.New("not found")
ErrInvalidPath = errors.New("invalid path")
)
// Resolver resolves requests to endpoints // Resolver resolves requests to endpoints
type Resolver interface { type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error) Resolve(r *http.Request) (*Endpoint, error)

View File

@ -7,21 +7,34 @@ import (
"net/url" "net/url"
"strings" "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" "github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/logger" "github.com/micro/go-micro/v2/logger"
) )
// CombinedAuthHandler wraps a server and authenticates requests // 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 {
if r == nil {
r = path.NewResolver()
}
if len(namespace) == 0 {
namespace = "go.micro"
}
return authHandler{ return authHandler{
handler: h, handler: h,
auth: auth.DefaultAuth, resolver: r,
auth: auth.DefaultAuth,
namespace: namespace,
} }
} }
type authHandler struct { type authHandler struct {
handler http.Handler handler http.Handler
auth auth.Auth auth auth.Auth
resolver resolver.Resolver
namespace string
} }
func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
@ -65,17 +78,35 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
} }
// Determine the name of the service being requested
endpoint, err := h.resolver.Resolve(req)
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
}
// construct the resource name, e.g. home => go.micro.web.home
resName := h.namespace
if len(endpoint.Name) > 0 {
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 // Perform the verification check to see if the account has access to
// the resource they're requesting // the resource they're requesting
err = h.auth.Verify(acc, &auth.Resource{ res := &auth.Resource{Type: "service", Name: resName, Endpoint: resEndpoint, Namespace: namespace}
Type: "service", if err := h.auth.Verify(acc, res); err == nil {
Name: "go.micro.web", // The account has the necessary permissions to access the resource
Endpoint: req.URL.Path,
Namespace: namespace,
})
// The account has the necessary permissions to access the resource
if err == nil {
h.handler.ServeHTTP(w, req) h.handler.ServeHTTP(w, req)
return return
} }

View File

@ -8,10 +8,9 @@ import (
"os" "os"
"sync" "sync"
"github.com/micro/go-micro/v2/api/server/auth"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/micro/go-micro/v2/api/server" "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/api/server/cors"
"github.com/micro/go-micro/v2/logger" "github.com/micro/go-micro/v2/logger"
) )
@ -25,9 +24,14 @@ type httpServer struct {
exit chan chan error 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{ return &httpServer{
opts: server.Options{}, opts: options,
mux: http.NewServeMux(), mux: http.NewServeMux(),
address: address, address: address,
exit: make(chan chan error), 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) { func (s *httpServer) Handle(path string, handler http.Handler) {
h := handlers.CombinedLoggingHandler(os.Stdout, 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 { if s.opts.EnableCORS {
h = cors.CombinedCORSHandler(h) h = cors.CombinedCORSHandler(h)

View File

@ -3,6 +3,7 @@ package server
import ( import (
"crypto/tls" "crypto/tls"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/server/acme" "github.com/micro/go-micro/v2/api/server/acme"
) )
@ -15,6 +16,8 @@ type Options struct {
EnableTLS bool EnableTLS bool
ACMEHosts []string ACMEHosts []string
TLSConfig *tls.Config TLSConfig *tls.Config
Namespace string
Resolver resolver.Resolver
} }
func EnableCORS(b bool) Option { func EnableCORS(b bool) Option {
@ -52,3 +55,15 @@ func TLSConfig(t *tls.Config) Option {
o.TLSConfig = t 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
}
}