Merge branch 'master' into auth-resolver

This commit is contained in:
ben-toogood 2020-04-03 14:19:03 +01:00 committed by GitHub
commit 350dd41732
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 211 additions and 85 deletions

View File

@ -2,6 +2,7 @@ package auth
import ( import (
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -9,6 +10,7 @@ import (
"github.com/micro/go-micro/v2/api/resolver" "github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/path" "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"
) )
// CombinedAuthHandler wraps a server and authenticates requests // CombinedAuthHandler wraps a server and authenticates requests
@ -36,6 +38,16 @@ type authHandler struct {
} }
func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Determine the namespace
namespace, err := namespaceFromRequest(req)
if err != nil {
logger.Error(err)
namespace = auth.DefaultNamespace
}
// Set the namespace in the header
req.Header.Set(auth.NamespaceKey, namespace)
// Extract the token from the request // Extract the token from the request
var token string var token string
if header := req.Header.Get("Authorization"); len(header) > 0 { if header := req.Header.Get("Authorization"); len(header) > 0 {
@ -56,7 +68,14 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// account doesn't necesserially mean a forbidden request // account doesn't necesserially mean a forbidden request
acc, err := h.auth.Inspect(token) acc, err := h.auth.Inspect(token)
if err != nil { if err != nil {
acc = &auth.Account{} acc = &auth.Account{Namespace: namespace}
}
// Check the accounts namespace matches the namespace we're operating
// within. If not forbid the request and log the occurance.
if acc.Namespace != namespace {
logger.Warnf("Cross namespace request forbidden: account %v (%v) requested access to %v in the %v namespace", acc.ID, acc.Namespace, req.URL.Path, namespace)
w.WriteHeader(http.StatusForbidden)
} }
// Determine the name of the service being requested // Determine the name of the service being requested
@ -85,15 +104,15 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 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
res := &auth.Resource{Type: "service", Name: resName, Endpoint: resEndpoint} res := &auth.Resource{Type: "service", Name: resName, Endpoint: resEndpoint, Namespace: namespace}
if err := h.auth.Verify(acc, res); err == nil { if err := h.auth.Verify(acc, res); err == nil {
// The account has the necessary permissions to access the resource // The account has the necessary permissions to access the resource
h.handler.ServeHTTP(w, req) h.handler.ServeHTTP(w, req)
return return
} }
// The account is set, but they don't have enough permissions, // The account is set, but they don't have enough permissions, hence
// hence we 403. // we return a forbidden error.
if len(acc.ID) > 0 { if len(acc.ID) > 0 {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
return return
@ -111,3 +130,42 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
loginWithRedirect := fmt.Sprintf("%v?%v", loginURL, params.Encode()) loginWithRedirect := fmt.Sprintf("%v?%v", loginURL, params.Encode())
http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect) http.Redirect(w, req, loginWithRedirect, http.StatusTemporaryRedirect)
} }
func namespaceFromRequest(req *http.Request) (string, error) {
// determine the host, e.g. dev.micro.mu:8080
host := req.URL.Host
if len(host) == 0 {
host = req.Host
}
// check for an ip address
if net.ParseIP(host) != nil {
return auth.DefaultNamespace, nil
}
// split the host to remove the port
host, _, err := net.SplitHostPort(req.Host)
if err != nil {
return "", err
}
// check for dev enviroment
if host == "localhost" || host == "127.0.0.1" {
return auth.DefaultNamespace, nil
}
// if host is not a subdomain, deturn default namespace
comps := strings.Split(host, ".")
if len(comps) != 3 {
return auth.DefaultNamespace, nil
}
// check for the micro.mu domain
domain := fmt.Sprintf("%v.%v", comps[1], comps[2])
if domain == "micro.mu" {
return auth.DefaultNamespace, nil
}
// return the subdomain as the host
return comps[0], nil
}

View File

@ -21,8 +21,6 @@ var (
ErrInvalidRole = errors.New("invalid role") ErrInvalidRole = errors.New("invalid role")
// ErrForbidden is returned when a user does not have the necessary roles to access a resource // ErrForbidden is returned when a user does not have the necessary roles to access a resource
ErrForbidden = errors.New("resource forbidden") ErrForbidden = errors.New("resource forbidden")
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
) )
// Auth providers authentication and authorization // Auth providers authentication and authorization
@ -50,11 +48,13 @@ type Auth interface {
// Resource is an entity such as a user or // Resource is an entity such as a user or
type Resource struct { type Resource struct {
// Name of the resource // Name of the resource
Name string Name string `json:"name"`
// Type of resource, e.g. // Type of resource, e.g.
Type string Type string `json:"type"`
// Endpoint resource e.g NotesService.Create // Endpoint resource e.g NotesService.Create
Endpoint string Endpoint string `json:"endpoint"`
// Namespace the resource belongs to
Namespace string `json:"namespace"`
} }
// Account provided by an auth provider // Account provided by an auth provider
@ -69,7 +69,7 @@ type Account struct {
Roles []string `json:"roles"` Roles []string `json:"roles"`
// Any other associated metadata // Any other associated metadata
Metadata map[string]string `json:"metadata"` Metadata map[string]string `json:"metadata"`
// Namespace the account belongs to, default blank // Namespace the account belongs to
Namespace string `json:"namespace"` Namespace string `json:"namespace"`
// Secret for the account, e.g. the password // Secret for the account, e.g. the password
Secret string `json:"secret"` Secret string `json:"secret"`
@ -88,12 +88,18 @@ type Token struct {
} }
const ( const (
// DefaultNamespace used for auth
DefaultNamespace = "micro"
// NamespaceKey is the key used when storing the namespace in metadata
NamespaceKey = "Micro-Namespace"
// MetadataKey is the key used when storing the account in metadata // MetadataKey is the key used when storing the account in metadata
MetadataKey = "auth-account" MetadataKey = "auth-account"
// TokenCookieName is the name of the cookie which stores the auth token // TokenCookieName is the name of the cookie which stores the auth token
TokenCookieName = "micro-token" TokenCookieName = "micro-token"
// SecretCookieName is the name of the cookie which stores the auth secret // SecretCookieName is the name of the cookie which stores the auth secret
SecretCookieName = "micro-secret" SecretCookieName = "micro-secret"
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
) )
// AccountFromContext gets the account from the context, which // AccountFromContext gets the account from the context, which

View File

@ -38,10 +38,11 @@ func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
options := NewGenerateOptions(opts...) options := NewGenerateOptions(opts...)
return &Account{ return &Account{
ID: id, ID: id,
Roles: options.Roles, Roles: options.Roles,
Secret: options.Secret, Secret: options.Secret,
Metadata: options.Metadata, Metadata: options.Metadata,
Namespace: DefaultNamespace,
}, nil }, nil
} }
@ -63,7 +64,8 @@ func (n *noop) Verify(acc *Account, res *Resource) error {
// Inspect a token // Inspect a token
func (n *noop) Inspect(token string) (*Account, error) { func (n *noop) Inspect(token string) (*Account, error) {
return &Account{ return &Account{
ID: uuid.New().String(), ID: uuid.New().String(),
Namespace: DefaultNamespace,
}, nil }, nil
} }

View File

@ -272,6 +272,7 @@ type Resource struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -323,6 +324,13 @@ func (m *Resource) GetEndpoint() string {
return "" return ""
} }
func (m *Resource) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type GenerateRequest struct { type GenerateRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"` Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
@ -1123,12 +1131,12 @@ func init() {
} }
var fileDescriptor_11312eec02fd5712 = []byte{ var fileDescriptor_11312eec02fd5712 = []byte{
// 896 bytes of a gzipped FileDescriptorProto // 901 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0x4b, 0x6f, 0xdb, 0x46, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0x4b, 0x6f, 0xdb, 0x46,
0x10, 0x36, 0x49, 0x89, 0x92, 0x47, 0x0f, 0x0b, 0x1b, 0x27, 0x25, 0x98, 0x47, 0x1d, 0xa6, 0x28, 0x10, 0x36, 0x49, 0x89, 0x92, 0x47, 0x0f, 0x0b, 0x1b, 0x27, 0x25, 0x98, 0x47, 0x1d, 0xa6, 0x28,
0xdc, 0xa0, 0xa1, 0x0b, 0xe5, 0xd0, 0x47, 0x2e, 0x35, 0x22, 0x41, 0x4d, 0xda, 0xa8, 0x28, 0x91, 0xdc, 0xa0, 0xa1, 0x0b, 0xe5, 0xd0, 0x47, 0x2e, 0x35, 0x22, 0x41, 0x4d, 0xda, 0xa8, 0x28, 0x91,
0x22, 0xbd, 0x14, 0x01, 0x43, 0x4d, 0x6d, 0xc2, 0x32, 0xc9, 0xee, 0x2e, 0x8d, 0xfa, 0x52, 0xa0, 0x22, 0xbd, 0x14, 0x01, 0x43, 0x4d, 0x6d, 0xc2, 0x32, 0xc9, 0xee, 0x2e, 0x8d, 0xea, 0x52, 0xa0,
0xa7, 0xde, 0x7a, 0xea, 0x4f, 0xe8, 0xcf, 0xea, 0xbd, 0x7f, 0xa3, 0xe0, 0x3e, 0x28, 0x91, 0xa2, 0xa7, 0xde, 0x7a, 0xea, 0x4f, 0xe8, 0xcf, 0xea, 0xbd, 0x7f, 0xa3, 0xe0, 0x3e, 0x68, 0x91, 0xa2,
0x02, 0xa3, 0xf5, 0x21, 0xb7, 0x9d, 0x07, 0x67, 0xe6, 0xfb, 0x66, 0x76, 0xb8, 0xf0, 0xe9, 0x49, 0x02, 0xa3, 0xf5, 0x21, 0xb7, 0x9d, 0x07, 0x67, 0xe6, 0xfb, 0x66, 0x76, 0xb8, 0xf0, 0xe9, 0x49,
0xcc, 0x4f, 0xf3, 0x37, 0x7e, 0x94, 0x9e, 0x1f, 0x9d, 0xc7, 0x11, 0x4d, 0x8f, 0x4e, 0xd2, 0x47, 0xcc, 0x4f, 0xf3, 0x37, 0x7e, 0x94, 0x9e, 0x1f, 0x9d, 0xc7, 0x11, 0x4d, 0x8f, 0x4e, 0xd2, 0x47,
0xf2, 0x10, 0xe6, 0xfc, 0xf4, 0x88, 0x21, 0xbd, 0x88, 0x23, 0x3c, 0xca, 0x68, 0xca, 0xa5, 0xca, 0xf2, 0x10, 0xe6, 0xfc, 0xf4, 0x88, 0x21, 0xbd, 0x88, 0x23, 0x3c, 0xca, 0x68, 0xca, 0xa5, 0xca,
@ -1139,45 +1147,46 @@ var fileDescriptor_11312eec02fd5712 = []byte{
0x4c, 0xc8, 0x7d, 0xe8, 0x87, 0x51, 0x84, 0x8c, 0xbd, 0xe6, 0x85, 0xec, 0x18, 0x07, 0xc6, 0xe1, 0x4c, 0xc8, 0x7d, 0xe8, 0x87, 0x51, 0x84, 0x8c, 0xbd, 0xe6, 0x85, 0xec, 0x18, 0x07, 0xc6, 0xe1,
0x6e, 0xd0, 0x93, 0x3a, 0xe9, 0xf2, 0x00, 0x06, 0x14, 0x7f, 0xa2, 0xc8, 0x4e, 0x95, 0x8f, 0x29, 0x6e, 0xd0, 0x93, 0x3a, 0xe9, 0xf2, 0x00, 0x06, 0x14, 0x7f, 0xa2, 0xc8, 0x4e, 0x95, 0x8f, 0x29,
0x7c, 0xfa, 0x4a, 0x29, 0x9d, 0x1c, 0xe8, 0x44, 0x14, 0x43, 0x8e, 0x0b, 0xc7, 0x3a, 0x30, 0x0e, 0x7c, 0xfa, 0x4a, 0x29, 0x9d, 0x1c, 0xe8, 0x44, 0x14, 0x43, 0x8e, 0x0b, 0xc7, 0x3a, 0x30, 0x0e,
0xad, 0x40, 0x8b, 0xe4, 0x16, 0xd8, 0xf8, 0x4b, 0x16, 0xd3, 0x4b, 0xa7, 0x25, 0x0c, 0x4a, 0xf2, 0xad, 0x40, 0x8b, 0xe4, 0x16, 0xd8, 0xf8, 0x4b, 0x16, 0xd3, 0x95, 0xd3, 0x12, 0x06, 0x25, 0x79,
0xfe, 0x34, 0xa1, 0xa3, 0x2a, 0x23, 0x43, 0x30, 0xe3, 0x85, 0xca, 0x6d, 0xc6, 0x0b, 0x42, 0xa0, 0x7f, 0x9a, 0xd0, 0x51, 0x95, 0x91, 0x21, 0x98, 0xf1, 0x42, 0xe5, 0x36, 0xe3, 0x05, 0x21, 0xd0,
0xc5, 0x2f, 0x33, 0x54, 0x99, 0xc4, 0x99, 0xec, 0x43, 0x9b, 0xa6, 0x4b, 0x64, 0x8e, 0x75, 0x60, 0xe2, 0xab, 0x0c, 0x55, 0x26, 0x71, 0x26, 0xfb, 0xd0, 0xa6, 0xe9, 0x12, 0x99, 0x63, 0x1d, 0x58,
0x1d, 0xee, 0x06, 0x52, 0x20, 0x5f, 0x42, 0xf7, 0x1c, 0x79, 0xb8, 0x08, 0x79, 0xe8, 0xb4, 0x04, 0x87, 0xbb, 0x81, 0x14, 0xc8, 0x97, 0xd0, 0x3d, 0x47, 0x1e, 0x2e, 0x42, 0x1e, 0x3a, 0x2d, 0x81,
0xfa, 0x0f, 0x9a, 0xd1, 0xfb, 0x2f, 0x94, 0xdb, 0x34, 0xe1, 0xf4, 0x32, 0x28, 0xbf, 0x22, 0x77, 0xfe, 0x83, 0x66, 0xf4, 0xfe, 0x0b, 0xe5, 0x36, 0x4d, 0x38, 0x5d, 0x05, 0xe5, 0x57, 0xe4, 0x0e,
0x60, 0x37, 0x09, 0xcf, 0x91, 0x65, 0x61, 0x84, 0x4e, 0x5b, 0x24, 0x5c, 0x29, 0x88, 0x0b, 0xdd, 0xec, 0x26, 0xe1, 0x39, 0xb2, 0x2c, 0x8c, 0xd0, 0x69, 0x8b, 0x84, 0x97, 0x0a, 0xe2, 0x42, 0x37,
0x8c, 0xa6, 0x17, 0xf1, 0x02, 0xa9, 0x63, 0x0b, 0x63, 0x29, 0x17, 0xc8, 0x18, 0x46, 0x14, 0xb9, 0xa3, 0xe9, 0x45, 0xbc, 0x40, 0xea, 0xd8, 0xc2, 0x58, 0xca, 0x05, 0x32, 0x86, 0x11, 0x45, 0xee,
0xd3, 0x11, 0x16, 0x25, 0xb9, 0x4f, 0x60, 0x50, 0x49, 0x46, 0x46, 0x60, 0x9d, 0xe1, 0xa5, 0xc2, 0x74, 0x84, 0x45, 0x49, 0xee, 0x13, 0x18, 0x54, 0x92, 0x91, 0x11, 0x58, 0x67, 0xb8, 0x52, 0xf8,
0x57, 0x1c, 0x0b, 0x30, 0x17, 0xe1, 0x32, 0xd7, 0x08, 0xa5, 0xf0, 0x85, 0xf9, 0x99, 0xe1, 0xcd, 0x8a, 0x63, 0x01, 0xe6, 0x22, 0x5c, 0xe6, 0x1a, 0xa1, 0x14, 0xbe, 0x30, 0x3f, 0x33, 0xbc, 0x25,
0xa1, 0x1b, 0x20, 0x4b, 0x73, 0x1a, 0x61, 0x41, 0x43, 0x51, 0x89, 0xfa, 0x50, 0x9c, 0x1b, 0xa9, 0x74, 0x03, 0x64, 0x69, 0x4e, 0x23, 0x2c, 0x68, 0x28, 0x2a, 0x51, 0x1f, 0x8a, 0x73, 0x23, 0x35,
0x71, 0xa1, 0x8b, 0xc9, 0x22, 0x4b, 0xe3, 0x84, 0x0b, 0xf6, 0x77, 0x83, 0x52, 0xf6, 0xfe, 0x32, 0x2e, 0x74, 0x31, 0x59, 0x64, 0x69, 0x9c, 0x70, 0xc1, 0xfe, 0x6e, 0x50, 0xca, 0x55, 0x78, 0xad,
0x61, 0x6f, 0x86, 0x09, 0xd2, 0x90, 0xa3, 0x1a, 0xa5, 0x0d, 0xba, 0x4b, 0x6a, 0xcd, 0x75, 0x6a, 0x1a, 0x3c, 0xef, 0x2f, 0x13, 0xf6, 0x66, 0x98, 0x20, 0x0d, 0x39, 0xaa, 0x41, 0xdb, 0x68, 0x46,
0xbf, 0x5a, 0xa3, 0xd6, 0x12, 0xd4, 0x7e, 0x5c, 0xa3, 0xb6, 0x16, 0xf7, 0x6a, 0x14, 0xb7, 0xea, 0x49, 0xbc, 0xb9, 0x4e, 0xfc, 0x57, 0x6b, 0xc4, 0x5b, 0x82, 0xf8, 0x8f, 0x6b, 0xc4, 0xd7, 0xe2,
0x14, 0xaf, 0x68, 0x6c, 0xaf, 0xd3, 0x58, 0x22, 0xb5, 0xab, 0x48, 0xcb, 0x76, 0x74, 0xaa, 0xed, 0x5e, 0xad, 0x01, 0xf5, 0x0a, 0xd7, 0x48, 0x6e, 0xaf, 0x93, 0x5c, 0xf2, 0x60, 0x57, 0x79, 0x28,
0xf8, 0x7f, 0xb4, 0x4f, 0x60, 0xb4, 0x42, 0xa3, 0x6e, 0xd6, 0x27, 0xd0, 0x51, 0x37, 0x46, 0xc4, 0x9b, 0xd5, 0xa9, 0x36, 0xeb, 0xff, 0x35, 0x65, 0x02, 0xa3, 0x4b, 0x34, 0xea, 0xde, 0x7d, 0x02,
0xd8, 0x7e, 0xb1, 0xb4, 0x9b, 0xf7, 0x0a, 0xfa, 0x33, 0x1a, 0x26, 0x5c, 0x13, 0x4d, 0xa0, 0x55, 0x1d, 0x75, 0x9f, 0x44, 0x8c, 0xed, 0xd7, 0x4e, 0xbb, 0x79, 0xaf, 0xa0, 0x3f, 0xa3, 0x61, 0xc2,
0x70, 0xa9, 0x1b, 0x58, 0x9c, 0xc9, 0x63, 0xe8, 0x52, 0xd5, 0x60, 0x51, 0x46, 0x6f, 0xfc, 0x5e, 0x35, 0xd1, 0x04, 0x5a, 0x05, 0x97, 0xba, 0xbd, 0xc5, 0x99, 0x3c, 0x86, 0x2e, 0x55, 0xed, 0x17,
0x2d, 0xac, 0xee, 0x7f, 0x50, 0x3a, 0x7a, 0x7b, 0x30, 0x50, 0x81, 0x65, 0x6d, 0xde, 0x0f, 0x30, 0x65, 0xf4, 0xc6, 0xef, 0xd5, 0xc2, 0xea, 0xe9, 0x08, 0x4a, 0x47, 0x6f, 0x0f, 0x06, 0x2a, 0xb0,
0x08, 0xf0, 0x22, 0x3d, 0xc3, 0x6b, 0x4f, 0x35, 0x82, 0xa1, 0x8e, 0xac, 0x72, 0x7d, 0x08, 0xc3, 0xac, 0xcd, 0xfb, 0x01, 0x06, 0x01, 0x5e, 0xa4, 0x67, 0x78, 0xed, 0xa9, 0x46, 0x30, 0xd4, 0x91,
0x67, 0x09, 0xcb, 0x30, 0x2a, 0x71, 0xed, 0x43, 0x7b, 0x7d, 0x5d, 0x48, 0xc1, 0x7b, 0x0a, 0x7b, 0x55, 0xae, 0x0f, 0x61, 0xf8, 0x2c, 0x61, 0x19, 0x46, 0x25, 0xae, 0x7d, 0x68, 0xaf, 0x2f, 0x13,
0xa5, 0xdf, 0x7f, 0xa6, 0xf0, 0x57, 0xe8, 0x8b, 0x8d, 0xb2, 0x6d, 0x56, 0x57, 0xd3, 0x62, 0x56, 0x29, 0x78, 0x4f, 0x61, 0xaf, 0xf4, 0xfb, 0xcf, 0x14, 0xfe, 0x0a, 0x7d, 0xb1, 0x6f, 0xb6, 0xcd,
0xa6, 0x65, 0x63, 0x4b, 0x59, 0x0d, 0x5b, 0xea, 0x3e, 0xf4, 0x85, 0xf1, 0x75, 0x65, 0x23, 0xf5, 0xea, 0xe5, 0xb4, 0x98, 0x95, 0x69, 0xd9, 0xd8, 0x61, 0x56, 0xc3, 0x0e, 0xbb, 0x0f, 0x7d, 0x61,
0x84, 0x6e, 0x2a, 0xd7, 0xd2, 0x13, 0x18, 0xa8, 0xfc, 0x0a, 0xc2, 0xc3, 0x75, 0xac, 0xbd, 0xf1, 0x7c, 0x5d, 0xd9, 0x57, 0x3d, 0xa1, 0x9b, 0xca, 0xa5, 0xf5, 0x04, 0x06, 0x2a, 0xbf, 0x82, 0xf0,
0x7e, 0x0d, 0x80, 0x74, 0x56, 0x0c, 0xfc, 0x61, 0x40, 0x2b, 0xc8, 0x97, 0xd8, 0xb4, 0xd0, 0x44, 0x70, 0x1d, 0x6b, 0x6f, 0xbc, 0x5f, 0x03, 0x20, 0x9d, 0x15, 0x03, 0x7f, 0x18, 0xd0, 0x0a, 0xf2,
0x77, 0xcc, 0x2d, 0xdd, 0xb1, 0xae, 0xd8, 0x1d, 0xf2, 0x08, 0x6c, 0xb9, 0x9b, 0x45, 0xed, 0xc3, 0x25, 0x36, 0xad, 0x3b, 0xd1, 0x1d, 0x73, 0x4b, 0x77, 0xac, 0x2b, 0x76, 0x87, 0x3c, 0x02, 0x5b,
0xf1, 0xcd, 0x4d, 0x3e, 0x91, 0xb1, 0x40, 0x39, 0x79, 0xbf, 0x1b, 0x30, 0x78, 0x2a, 0x16, 0xf1, 0x6e, 0x6e, 0x51, 0xfb, 0x70, 0x7c, 0x73, 0x93, 0x4f, 0x64, 0x2c, 0x50, 0x4e, 0xde, 0xef, 0x06,
0x75, 0xcf, 0xc9, 0x5a, 0x25, 0xd6, 0x55, 0x2a, 0x19, 0xc1, 0x50, 0x17, 0xa2, 0xc6, 0xaa, 0xa8, 0x0c, 0x9e, 0x8a, 0x35, 0x7d, 0xdd, 0x73, 0xb2, 0x56, 0x89, 0x75, 0x95, 0x4a, 0x46, 0x30, 0xd4,
0x6d, 0x82, 0x4b, 0x7c, 0x27, 0x6a, 0xd3, 0x85, 0xa8, 0xda, 0x06, 0xd0, 0x2b, 0x7e, 0xb6, 0xfa, 0x85, 0xa8, 0xb1, 0x2a, 0x6a, 0x9b, 0xe0, 0x12, 0xdf, 0x89, 0xda, 0x74, 0x21, 0xaa, 0xb6, 0x01,
0xdf, 0xfb, 0x39, 0xf4, 0xa5, 0xa8, 0x66, 0xe2, 0x23, 0x68, 0xd3, 0xbc, 0x58, 0x98, 0xf2, 0x87, 0xf4, 0x8a, 0x5f, 0xb1, 0xfe, 0x33, 0x7f, 0x0e, 0x7d, 0x29, 0xaa, 0x99, 0xf8, 0x08, 0xda, 0x34,
0x7b, 0xa3, 0x5e, 0x51, 0xbe, 0xc4, 0x40, 0x7a, 0x3c, 0xf4, 0xc1, 0x96, 0xd9, 0x48, 0x0f, 0x3a, 0x2f, 0x16, 0xa6, 0xfc, 0x1d, 0xdf, 0xa8, 0x57, 0x94, 0x2f, 0x31, 0x90, 0x1e, 0x0f, 0x7d, 0xb0,
0xdf, 0xcf, 0xbf, 0x9e, 0x7f, 0xfb, 0x6a, 0x3e, 0xda, 0x29, 0x84, 0x59, 0x70, 0x3c, 0x7f, 0x39, 0x65, 0x36, 0xd2, 0x83, 0xce, 0xf7, 0xf3, 0xaf, 0xe7, 0xdf, 0xbe, 0x9a, 0x8f, 0x76, 0x0a, 0x61,
0x9d, 0x8c, 0x0c, 0x02, 0x60, 0x4f, 0xa6, 0xf3, 0x67, 0xd3, 0xc9, 0xc8, 0x1c, 0xff, 0x63, 0x40, 0x16, 0x1c, 0xcf, 0x5f, 0x4e, 0x27, 0x23, 0x83, 0x00, 0xd8, 0x93, 0xe9, 0xfc, 0xd9, 0x74, 0x32,
0xeb, 0x38, 0xe7, 0xa7, 0xe4, 0x05, 0x74, 0xf5, 0x46, 0x22, 0xf7, 0xde, 0xbe, 0x78, 0xdd, 0xf7, 0x32, 0xc7, 0xff, 0x18, 0xd0, 0x3a, 0xce, 0xf9, 0x29, 0x79, 0x01, 0x5d, 0xbd, 0x91, 0xc8, 0xbd,
0xb7, 0xda, 0x15, 0x9e, 0x1d, 0xf2, 0x1c, 0x3a, 0xea, 0x72, 0x92, 0xbb, 0x35, 0xef, 0xea, 0xe5, 0xb7, 0x2f, 0x5e, 0xf7, 0xfd, 0xad, 0x76, 0x85, 0x67, 0x87, 0x3c, 0x87, 0x8e, 0xba, 0x9c, 0xe4,
0x76, 0xef, 0x6d, 0x33, 0x97, 0xb1, 0x26, 0xfa, 0xf5, 0x70, 0xbb, 0xf1, 0x32, 0xa8, 0x38, 0x77, 0x6e, 0xcd, 0xbb, 0x7a, 0xb9, 0xdd, 0x7b, 0xdb, 0xcc, 0x65, 0xac, 0x89, 0x7e, 0x5b, 0xdc, 0x6e,
0x9a, 0x8d, 0x3a, 0xca, 0xf8, 0x47, 0xe8, 0xea, 0xc7, 0x0c, 0xf9, 0x0e, 0x5a, 0x05, 0xc1, 0xc4, 0xbc, 0x0c, 0x2a, 0xce, 0x9d, 0x66, 0xa3, 0x8e, 0x32, 0xfe, 0x11, 0xba, 0xfa, 0xa9, 0x43, 0xbe,
0xab, 0x7d, 0xd3, 0xf0, 0x10, 0x72, 0x1f, 0xbc, 0xd5, 0xa7, 0x0c, 0xff, 0xb7, 0x01, 0xed, 0xa2, 0x83, 0x56, 0x41, 0x30, 0xf1, 0x6a, 0xdf, 0x34, 0x3c, 0x93, 0xdc, 0x07, 0x6f, 0xf5, 0x29, 0xc3,
0x11, 0x8c, 0xcc, 0xc0, 0x96, 0xa3, 0x47, 0xea, 0x25, 0x55, 0xae, 0x86, 0x7b, 0x77, 0x8b, 0xb5, 0xff, 0x6d, 0x40, 0xbb, 0x68, 0x04, 0x23, 0x33, 0xb0, 0xe5, 0xe8, 0x91, 0x7a, 0x49, 0x95, 0xab,
0xc4, 0x3d, 0x03, 0x5b, 0xce, 0xc9, 0x46, 0xa0, 0xca, 0x1c, 0x6f, 0x04, 0xaa, 0x0d, 0xd7, 0x0e, 0xe1, 0xde, 0xdd, 0x62, 0x2d, 0x71, 0xcf, 0xc0, 0x96, 0x73, 0xb2, 0x11, 0xa8, 0x32, 0xc7, 0x1b,
0x39, 0x56, 0x70, 0xdd, 0x06, 0x28, 0x3a, 0xc8, 0xed, 0x46, 0x9b, 0x0e, 0xf1, 0xc6, 0x16, 0x6f, 0x81, 0x6a, 0xc3, 0xb5, 0x43, 0x8e, 0x15, 0x5c, 0xb7, 0x01, 0x8a, 0x0e, 0x72, 0xbb, 0xd1, 0xa6,
0xc7, 0xc7, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x8f, 0xf4, 0x22, 0x76, 0x0a, 0x00, 0x00, 0x43, 0xbc, 0xb1, 0xc5, 0xcb, 0xf2, 0xf1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfa, 0x51, 0x05,
0x6e, 0x94, 0x0a, 0x00, 0x00,
} }

View File

@ -46,6 +46,7 @@ message Resource{
string name = 1; string name = 1;
string type = 2; string type = 2;
string endpoint = 3; string endpoint = 3;
string namespace = 4;
} }
message GenerateRequest { message GenerateRequest {

View File

@ -132,9 +132,10 @@ func (s *svc) Grant(role string, res *auth.Resource) error {
Role: role, Role: role,
Access: pb.Access_GRANTED, Access: pb.Access_GRANTED,
Resource: &pb.Resource{ Resource: &pb.Resource{
Type: res.Type, Namespace: res.Namespace,
Name: res.Name, Type: res.Type,
Endpoint: res.Endpoint, Name: res.Name,
Endpoint: res.Endpoint,
}, },
}) })
return err return err
@ -146,9 +147,10 @@ func (s *svc) Revoke(role string, res *auth.Resource) error {
Role: role, Role: role,
Access: pb.Access_GRANTED, Access: pb.Access_GRANTED,
Resource: &pb.Resource{ Resource: &pb.Resource{
Type: res.Type, Namespace: res.Namespace,
Name: res.Name, Type: res.Type,
Endpoint: res.Endpoint, Name: res.Name,
Endpoint: res.Endpoint,
}, },
}) })
return err return err
@ -157,10 +159,11 @@ func (s *svc) Revoke(role string, res *auth.Resource) error {
// Verify an account has access to a resource // Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error { func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error {
queries := [][]string{ queries := [][]string{
{res.Type, res.Name, res.Endpoint}, // check for specific role, e.g. service.foo.ListFoo:admin (role is checked in accessForRule) {res.Namespace, res.Type, res.Name, res.Endpoint}, // check for specific role, e.g. service.foo.ListFoo:admin (role is checked in accessForRule)
{res.Type, res.Name, "*"}, // check for wildcard endpoint, e.g. service.foo* {res.Namespace, res.Type, res.Name, "*"}, // check for wildcard endpoint, e.g. service.foo*
{res.Type, "*"}, // check for wildcard name, e.g. service.* {res.Namespace, res.Type, "*"}, // check for wildcard name, e.g. service.*
{"*"}, // check for wildcard type, e.g. * {res.Namespace, "*"}, // check for wildcard type, e.g. *
{"*"}, // check for wildcard namespace
} }
// endpoint is a url which can have wildcard excludes, e.g. // endpoint is a url which can have wildcard excludes, e.g.
@ -172,23 +175,29 @@ func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error {
} }
} }
// set a default account id to log
logID := acc.ID
if len(logID) == 0 {
logID = "[no account]"
}
for _, q := range queries { for _, q := range queries {
for _, rule := range s.listRules(q...) { for _, rule := range s.listRules(q...) {
switch accessForRule(rule, acc, res) { switch accessForRule(rule, acc, res) {
case pb.Access_UNKNOWN: case pb.Access_UNKNOWN:
continue // rule did not specify access, check the next rule continue // rule did not specify access, check the next rule
case pb.Access_GRANTED: case pb.Access_GRANTED:
log.Infof("%v granted access to %v:%v:%v by rule %v", acc.ID, res.Type, res.Name, res.Endpoint, rule.Id) log.Infof("%v:%v granted access to %v:%v:%v:%v by rule %v", acc.Namespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, rule.Id)
return nil // rule grants the account access to the resource return nil // rule grants the account access to the resource
case pb.Access_DENIED: case pb.Access_DENIED:
log.Infof("%v denied access to %v:%v:%v by rule %v", acc.ID, res.Type, res.Name, res.Endpoint, rule.Id) log.Infof("%v:%v denied access to %v:%v:%v:%v by rule %v", acc.Namespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, rule.Id)
return auth.ErrForbidden // rule denies access to the resource return auth.ErrForbidden // rule denies access to the resource
} }
} }
} }
// no rules were found for the resource, default to denying access // no rules were found for the resource, default to denying access
log.Infof("%v denied access to %v:%v:%v by lack of rule (%v rules found)", acc.ID, res.Type, res.Name, res.Endpoint, len(s.rules)) log.Infof("%v:%v denied access to %v:%v:%v:%v by lack of rule (%v rules found for namespace)", acc.Namespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, len(s.listRules(res.Namespace)))
return auth.ErrForbidden return auth.ErrForbidden
} }
@ -249,19 +258,28 @@ func accessForRule(rule *pb.Rule, acc *auth.Account, res *auth.Resource) pb.Acce
return pb.Access_UNKNOWN return pb.Access_UNKNOWN
} }
// listRules gets all the rules from the store which have an id // listRules gets all the rules from the store which match the filters.
// prefix matching the filters // filters are namespace, type, name and then endpoint.
func (s *svc) listRules(filters ...string) []*pb.Rule { func (s *svc) listRules(filters ...string) []*pb.Rule {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
prefix := strings.Join(filters, ruleJoinKey)
var rules []*pb.Rule var rules []*pb.Rule
for _, r := range s.rules { for _, r := range s.rules {
if strings.HasPrefix(r.Id, prefix) { if len(filters) > 0 && r.Resource.Namespace != filters[0] {
rules = append(rules, r) continue
} }
if len(filters) > 1 && r.Resource.Type != filters[1] {
continue
}
if len(filters) > 2 && r.Resource.Name != filters[2] {
continue
}
if len(filters) > 3 && r.Resource.Endpoint != filters[3] {
continue
}
rules = append(rules, r)
} }
return rules return rules

View File

@ -494,6 +494,7 @@ func (c *cmd) Before(ctx *cli.Context) error {
*c.opts.Auth = a() *c.opts.Auth = a()
clientOpts = append(clientOpts, client.Auth(*c.opts.Auth)) clientOpts = append(clientOpts, client.Auth(*c.opts.Auth))
serverOpts = append(serverOpts, server.Auth(*c.opts.Auth))
} }
// Set the profile // Set the profile

View File

@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/broker" "github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/codec" "github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/registry" "github.com/micro/go-micro/v2/registry"
@ -66,6 +67,7 @@ func MaxMsgSize(s int) server.Option {
func newOptions(opt ...server.Option) server.Options { func newOptions(opt ...server.Option) server.Options {
opts := server.Options{ opts := server.Options{
Auth: auth.DefaultAuth,
Codecs: make(map[string]codec.NewCodec), Codecs: make(map[string]codec.NewCodec),
Metadata: map[string]string{}, Metadata: map[string]string{},
Broker: broker.DefaultBroker, Broker: broker.DefaultBroker,

View File

@ -60,6 +60,10 @@ func newOptions(opt ...Option) Options {
o(&opts) o(&opts)
} }
if opts.Auth == nil {
opts.Auth = auth.DefaultAuth
}
if opts.Broker == nil { if opts.Broker == nil {
opts.Broker = broker.DefaultBroker opts.Broker = broker.DefaultBroker
} }

View File

@ -34,8 +34,9 @@ func newService(opts ...Option) Service {
// service name // service name
serviceName := options.Server.Options().Name serviceName := options.Server.Options().Name
// TODO: better accessors // authFn returns the auth, we pass as a function since auth
authFn := func() auth.Auth { return service.opts.Auth } // has not yet been set at this point.
authFn := func() auth.Auth { return options.Server.Options().Auth }
// wrap client to inject From-Service header on any calls // wrap client to inject From-Service header on any calls
options.Client = wrapper.FromService(serviceName, options.Client, authFn) options.Client = wrapper.FromService(serviceName, options.Client, authFn)

View File

@ -9,6 +9,7 @@ import (
"github.com/micro/go-micro/v2/debug/stats" "github.com/micro/go-micro/v2/debug/stats"
"github.com/micro/go-micro/v2/debug/trace" "github.com/micro/go-micro/v2/debug/trace"
"github.com/micro/go-micro/v2/errors" "github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata" "github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/server" "github.com/micro/go-micro/v2/server"
) )
@ -165,24 +166,47 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
if header, ok := metadata.Get(ctx, "Authorization"); ok { if header, ok := metadata.Get(ctx, "Authorization"); ok {
// Ensure the correct scheme is being used // Ensure the correct scheme is being used
if !strings.HasPrefix(header, auth.BearerScheme) { if !strings.HasPrefix(header, auth.BearerScheme) {
return errors.Unauthorized("go.micro.auth", "invalid authorization header. expected Bearer schema") return errors.Unauthorized(req.Service(), "invalid authorization header. expected Bearer schema")
} }
token = header[len(auth.BearerScheme):] token = header[len(auth.BearerScheme):]
} }
// Get the namespace for the request
namespace, ok := metadata.Get(ctx, auth.NamespaceKey)
if !ok {
logger.Errorf("Missing request namespace")
namespace = auth.DefaultNamespace
}
// Inspect the token and get the account // Inspect the token and get the account
account, err := a.Inspect(token) account, err := a.Inspect(token)
if err != nil { if err != nil {
account = &auth.Account{} account = &auth.Account{Namespace: namespace}
}
// Check the accounts namespace matches the namespace we're operating
// within. If not forbid the request and log the occurance.
if account.Namespace != namespace {
logger.Warnf("Cross namespace request forbidden: account %v (%v) requested access to %v %v in the %v namespace",
account.ID, account.Namespace, req.Service(), req.Endpoint(), namespace)
return errors.Forbidden(req.Service(), "cross namespace request")
}
// construct the resource
res := &auth.Resource{
Type: "service",
Name: req.Service(),
Endpoint: req.Endpoint(),
Namespace: namespace,
} }
// Verify the caller has access to the resource // Verify the caller has access to the resource
err = a.Verify(account, &auth.Resource{Type: "service", Name: req.Service(), Endpoint: req.Endpoint()}) err = a.Verify(account, res)
if err != nil && len(account.ID) > 0 { if err != nil && len(account.ID) > 0 {
return errors.Forbidden("go.micro.auth", "Forbidden call made to %v:%v by %v", req.Service(), req.Endpoint(), account.ID) return errors.Forbidden(req.Service(), "Forbidden call made to %v:%v by %v", req.Service(), req.Endpoint(), account.ID)
} else if err != nil { } else if err != nil {
return errors.Unauthorized("go.micro.auth", "Unauthorised call made to %v:%v", req.Service(), req.Endpoint()) return errors.Unauthorized(req.Service(), "Unauthorised call made to %v:%v", req.Service(), req.Endpoint())
} }
// There is an account, set it in the context // There is an account, set it in the context