Add Namespace to CombinedAuthHandler

This commit is contained in:
Ben Toogood 2020-04-07 09:40:40 +01:00
parent 3a378eb7d6
commit 7206d5f964
4 changed files with 91 additions and 48 deletions

View File

@ -15,18 +15,16 @@ import (
) )
// CombinedAuthHandler wraps a server and authenticates requests // CombinedAuthHandler wraps a server and authenticates requests
func CombinedAuthHandler(namespace string, r resolver.Resolver, h http.Handler) http.Handler { func CombinedAuthHandler(prefix, namespace string, r resolver.Resolver, h http.Handler) http.Handler {
if r == nil { if r == nil {
r = path.NewResolver() r = path.NewResolver()
} }
if len(namespace) == 0 {
namespace = "go.micro"
}
return authHandler{ return authHandler{
handler: h, handler: h,
resolver: r, resolver: r,
auth: auth.DefaultAuth, auth: auth.DefaultAuth,
servicePrefix: prefix,
namespace: namespace, namespace: namespace,
} }
} }
@ -36,17 +34,13 @@ type authHandler struct {
auth auth.Auth auth auth.Auth
resolver resolver.Resolver resolver resolver.Resolver
namespace string namespace string
servicePrefix string
} }
func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Determine the namespace // Determine the namespace and set it in the header
namespace, err := namespaceFromRequest(req) namespace := h.namespaceFromRequest(req)
if err != nil { fmt.Printf("Namespace is %v\n", namespace)
logger.Error(err)
namespace = auth.DefaultNamespace
}
// Set the namespace in the header
req.Header.Set(auth.NamespaceKey, namespace) req.Header.Set(auth.NamespaceKey, namespace)
// Extract the token from the request // Extract the token from the request
@ -96,7 +90,7 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
// construct the resource name, e.g. home => go.micro.web.home // construct the resource name, e.g. home => go.micro.web.home
resName := h.namespace resName := h.servicePrefix
if len(endpoint.Name) > 0 { if len(endpoint.Name) > 0 {
resName = resName + "." + endpoint.Name resName = resName + "." + endpoint.Name
} }
@ -138,39 +132,47 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect) http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect)
} }
func namespaceFromRequest(req *http.Request) (string, error) { func (h authHandler) namespaceFromRequest(req *http.Request) string {
// needed to tmp debug host in prod. will be removed. // check to see what the provided namespace is, we only do
logger.Infof("Host is '%v'; URL Host is '%v'; URL Hostname is '%v'", req.Host, req.URL.Host, req.URL.Hostname()) // domain mapping if the namespace is set to 'domain'
if h.namespace != "domain" {
return h.namespace
}
// determine the host, e.g. dev.micro.mu:8080 // determine the host, e.g. dev.micro.mu:8080
host := req.URL.Hostname() var host string
if len(host) == 0 { if h, _, err := net.SplitHostPort(req.Host); err == nil {
// fallback to req.Host host = h // host does contain a port
host, _, _ = net.SplitHostPort(req.Host) } else {
host = req.Host // host does not contain a port
}
// check for the micro.mu domain
if strings.HasSuffix(host, "micro.mu") {
return auth.DefaultNamespace
} }
// check for an ip address // check for an ip address
if net.ParseIP(host) != nil { if net.ParseIP(host) != nil {
return auth.DefaultNamespace, nil return auth.DefaultNamespace
} }
// check for dev enviroment // check for dev enviroment
if host == "localhost" || host == "127.0.0.1" { if host == "localhost" || host == "127.0.0.1" {
return auth.DefaultNamespace, nil return auth.DefaultNamespace
} }
// if host is not a subdomain, deturn default namespace // if host is not a subdomain, deturn default namespace
comps := strings.Split(host, ".") comps := strings.Split(host, ".")
if len(comps) != 3 { if len(comps) < 3 {
return auth.DefaultNamespace, nil return auth.DefaultNamespace
} }
// check for the micro.mu domain // return the reversed subdomain as the namespace
domain := fmt.Sprintf("%v.%v", comps[1], comps[2]) nComps := comps[0 : len(comps)-2]
if domain == "micro.mu" { for i := len(nComps)/2 - 1; i >= 0; i-- {
return auth.DefaultNamespace, nil opp := len(nComps) - 1 - i
nComps[i], nComps[opp] = nComps[opp], nComps[i]
} }
return strings.Join(nComps, ".")
// return the subdomain as the host
return comps[0], nil
} }

View File

@ -0,0 +1,34 @@
package auth
import (
"net/http"
"testing"
"github.com/micro/go-micro/v2/auth"
)
func TestNamespaceFromRequest(t *testing.T) {
tt := []struct {
Host string
Namespace string
}{
{Host: "micro.mu", Namespace: auth.DefaultNamespace},
{Host: "web.micro.mu", Namespace: auth.DefaultNamespace},
{Host: "api.micro.mu", Namespace: auth.DefaultNamespace},
{Host: "myapp.com", Namespace: auth.DefaultNamespace},
{Host: "staging.myapp.com", Namespace: "staging"},
{Host: "staging.myapp.m3o.app", Namespace: "myapp.staging"},
{Host: "127.0.0.1", Namespace: auth.DefaultNamespace},
{Host: "localhost", Namespace: auth.DefaultNamespace},
{Host: "81.151.101.146", Namespace: auth.DefaultNamespace},
}
for _, tc := range tt {
t.Run(tc.Host, func(t *testing.T) {
ns := namespaceFromRequest(&http.Request{Host: tc.Host})
if ns != tc.Namespace {
t.Errorf("Expected namespace %v for host %v, actually got %v", tc.Namespace, tc.Host, ns)
}
})
}
}

View File

@ -53,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(s.opts.Namespace, s.opts.Resolver, handler) h = auth.CombinedAuthHandler(s.opts.ServiceNamespace, 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

@ -16,8 +16,9 @@ type Options struct {
EnableTLS bool EnableTLS bool
ACMEHosts []string ACMEHosts []string
TLSConfig *tls.Config TLSConfig *tls.Config
Namespace string
Resolver resolver.Resolver Resolver resolver.Resolver
Namespace string
ServiceNamespace string
} }
func EnableCORS(b bool) Option { func EnableCORS(b bool) Option {
@ -56,6 +57,12 @@ func TLSConfig(t *tls.Config) Option {
} }
} }
func ServiceNamespace(n string) Option {
return func(o *Options) {
o.ServiceNamespace = n
}
}
func Namespace(n string) Option { func Namespace(n string) Option {
return func(o *Options) { return func(o *Options) {
o.Namespace = n o.Namespace = n