Merge branch 'master' into master

This commit is contained in:
potato 2020-05-28 10:19:53 +08:00 committed by GitHub
commit c08eb5f892
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1128 additions and 711 deletions

View File

@ -2,8 +2,6 @@ package resolver
import ( import (
"net/http" "net/http"
"github.com/micro/go-micro/v2/auth"
) )
// NewOptions returns new initialised options // NewOptions returns new initialised options
@ -14,7 +12,7 @@ func NewOptions(opts ...Option) Options {
} }
if options.Namespace == nil { if options.Namespace == nil {
options.Namespace = StaticNamespace(auth.DefaultNamespace) options.Namespace = StaticNamespace("go.micro")
} }
return options return options

View File

@ -7,20 +7,23 @@ import (
"time" "time"
) )
const (
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
// ScopePublic is the scope applied to a rule to allow access to the public
ScopePublic = ""
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
ScopeAccount = "*"
)
var ( var (
// ErrNotFound is returned when a resouce cannot be found // ErrInvalidToken is when the token provided is not valid
ErrNotFound = errors.New("not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided") ErrInvalidToken = errors.New("invalid token provided")
// ErrInvalidRole is returned when the role provided was invalid // ErrForbidden is when a user does not have the necessary scope to access a resource
ErrInvalidRole = errors.New("invalid role")
// 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")
) )
// Auth providers authentication and authorization // Auth provides authentication and authorization
type Auth interface { type Auth interface {
// Init the auth // Init the auth
Init(opts ...Option) Init(opts ...Option)
@ -28,65 +31,38 @@ type Auth interface {
Options() Options Options() Options
// Generate a new account // Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error) Generate(id string, opts ...GenerateOption) (*Account, error)
// Grant access to a resource // Verify an account has access to a resource using the rules
Grant(role string, res *Resource) error Verify(acc *Account, res *Resource, opts ...VerifyOption) error
// Revoke access to a resource
Revoke(role string, res *Resource) error
// Verify an account has access to a resource
Verify(acc *Account, res *Resource) error
// Inspect a token // Inspect a token
Inspect(token string) (*Account, error) Inspect(token string) (*Account, error)
// Token generated using refresh token // Token generated using refresh token or credentials
Token(opts ...TokenOption) (*Token, error) Token(opts ...TokenOption) (*Token, error)
// Grant access to a resource
Grant(rule *Rule) error
// Revoke access to a resource
Revoke(rule *Rule) error
// Rules returns all the rules used to verify requests
Rules(...RulesOption) ([]*Rule, error)
// String returns the name of the implementation // String returns the name of the implementation
String() string String() string
} }
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource
Name string `json:"name"`
// Type of resource, e.g.
Type string `json:"type"`
// Endpoint resource e.g NotesService.Create
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
type Account struct { type Account struct {
// ID of the account e.g. email // ID of the account e.g. email
ID string `json:"id"` ID string `json:"id"`
// Type of the account, e.g. service // Type of the account, e.g. service
Type string `json:"type"` Type string `json:"type"`
// Provider who issued the account // Issuer of the account
Provider string `json:"provider"` Issuer string `json:"issuer"`
// Roles associated with the Account
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 // Scopes the account has access to
Namespace string `json:"namespace"` Scopes []string `json:"scopes"`
// Secret for the account, e.g. the password // Secret for the account, e.g. the password
Secret string `json:"secret"` Secret string `json:"secret"`
} }
// HasRole returns a boolean indicating if the account has the given role
func (a *Account) HasRole(role string) bool {
if a.Roles == nil {
return false
}
for _, r := range a.Roles {
if r == role {
return true
}
}
return false
}
// Token can be short or long lived // Token can be short or long lived
type Token struct { type Token struct {
// The token to be used for accessing resources // The token to be used for accessing resources
@ -99,15 +75,47 @@ type Token struct {
Expiry time.Time `json:"expiry"` Expiry time.Time `json:"expiry"`
} }
// Expired returns a boolean indicating if the token needs to be refreshed
func (t *Token) Expired() bool {
return t.Expiry.Unix() < time.Now().Unix()
}
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource, e.g. go.micro.service.notes
Name string `json:"name"`
// Type of resource, e.g. service
Type string `json:"type"`
// Endpoint resource e.g NotesService.Create
Endpoint string `json:"endpoint"`
}
// Access defines the type of access a rule grants
type Access int
const ( const (
// DefaultNamespace used for auth // AccessGranted to a resource
DefaultNamespace = "go.micro" AccessGranted Access = iota
// TokenCookieName is the name of the cookie which stores the auth token // AccessDenied to a resource
TokenCookieName = "micro-token" AccessDenied
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
) )
// Rule is used to verify access to a resource
type Rule struct {
// ID of the rule, e.g. "public"
ID string
// Scope the rule requires, a blank scope indicates open to the public and * indicates the rule
// applies to any valid account
Scope string
// Resource the rule applies to
Resource *Resource
// Access determines if the rule grants or denies access to the resource
Access Access
// Priority the rule should take when verifying a request, the higher the value the sooner the
// rule will be applied
Priority int32
}
type accountKey struct{} type accountKey struct{}
// AccountFromContext gets the account from the context, which // AccountFromContext gets the account from the context, which

View File

@ -1,17 +0,0 @@
package auth
import "testing"
func TestHasRole(t *testing.T) {
if new(Account).HasRole("foo") {
t.Errorf("Expected the blank account to not have a role")
}
acc := Account{Roles: []string{"foo"}}
if !acc.HasRole("foo") {
t.Errorf("Expected the account to have the foo role")
}
if acc.HasRole("bar") {
t.Errorf("Expected the account to not have the bar role")
}
}

View File

@ -49,35 +49,37 @@ 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, Secret: options.Secret,
Secret: options.Secret, Metadata: options.Metadata,
Metadata: options.Metadata, Scopes: options.Scopes,
Namespace: DefaultNamespace, Issuer: n.Options().Namespace,
}, nil }, nil
} }
// Grant access to a resource // Grant access to a resource
func (n *noop) Grant(role string, res *Resource) error { func (n *noop) Grant(rule *Rule) error {
return nil return nil
} }
// Revoke access to a resource // Revoke access to a resource
func (n *noop) Revoke(role string, res *Resource) error { func (n *noop) Revoke(rule *Rule) error {
return nil return nil
} }
// Rules used to verify requests
func (n *noop) Rules(opts ...RulesOption) ([]*Rule, error) {
return []*Rule{}, nil
}
// Verify an account has access to a resource // Verify an account has access to a resource
func (n *noop) Verify(acc *Account, res *Resource) error { func (n *noop) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
return nil return nil
} }
// 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(), Issuer: n.Options().Namespace}, nil
ID: uuid.New().String(),
Namespace: DefaultNamespace,
}, nil
} }
// Token generation using an account id and secret // Token generation using an account id and secret

View File

@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
"github.com/micro/go-micro/v2/auth/token" "github.com/micro/go-micro/v2/auth/token"
jwtToken "github.com/micro/go-micro/v2/auth/token/jwt" jwtToken "github.com/micro/go-micro/v2/auth/token/jwt"
) )
@ -16,15 +17,10 @@ func NewAuth(opts ...auth.Option) auth.Auth {
return j return j
} }
type rule struct {
role string
resource *auth.Resource
}
type jwt struct { type jwt struct {
options auth.Options options auth.Options
jwt token.Provider jwt token.Provider
rules []*rule rules []*auth.Rule
sync.Mutex sync.Mutex
} }
@ -41,10 +37,6 @@ func (j *jwt) Init(opts ...auth.Option) {
o(&j.options) o(&j.options)
} }
if len(j.options.Namespace) == 0 {
j.options.Namespace = auth.DefaultNamespace
}
j.jwt = jwtToken.NewTokenProvider( j.jwt = jwtToken.NewTokenProvider(
token.WithPrivateKey(j.options.PrivateKey), token.WithPrivateKey(j.options.PrivateKey),
token.WithPublicKey(j.options.PublicKey), token.WithPublicKey(j.options.PublicKey),
@ -60,12 +52,11 @@ func (j *jwt) Options() auth.Options {
func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
options := auth.NewGenerateOptions(opts...) options := auth.NewGenerateOptions(opts...)
account := &auth.Account{ account := &auth.Account{
ID: id, ID: id,
Type: options.Type, Type: options.Type,
Roles: options.Roles, Scopes: options.Scopes,
Provider: options.Provider, Metadata: options.Metadata,
Metadata: options.Metadata, Issuer: j.Options().Namespace,
Namespace: options.Namespace,
} }
// generate a JWT secret which can be provided to the Token() method // generate a JWT secret which can be provided to the Token() method
@ -80,84 +71,44 @@ func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, e
return account, nil return account, nil
} }
func (j *jwt) Grant(role string, res *auth.Resource) error { func (j *jwt) Grant(rule *auth.Rule) error {
j.Lock() j.Lock()
defer j.Unlock() defer j.Unlock()
j.rules = append(j.rules, &rule{role, res}) j.rules = append(j.rules, rule)
return nil return nil
} }
func (j *jwt) Revoke(role string, res *auth.Resource) error { func (j *jwt) Revoke(rule *auth.Rule) error {
j.Lock() j.Lock()
defer j.Unlock() defer j.Unlock()
rules := make([]*rule, 0, len(j.rules)) rules := []*auth.Rule{}
var ruleFound bool
for _, r := range rules { for _, r := range rules {
if r.role == role && r.resource == res { if r.ID != rule.ID {
ruleFound = true
} else {
rules = append(rules, r) rules = append(rules, r)
} }
} }
if !ruleFound {
return auth.ErrNotFound
}
j.rules = rules j.rules = rules
return nil return nil
} }
func (j *jwt) Verify(acc *auth.Account, res *auth.Resource) error { func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
j.Lock() j.Lock()
if len(res.Namespace) == 0 { defer j.Unlock()
res.Namespace = j.options.Namespace
}
rules := j.rules
j.Unlock()
for _, rule := range rules { var options auth.VerifyOptions
// validate the rule applies to the requested resource for _, o := range opts {
if rule.resource.Namespace != "*" && rule.resource.Namespace != res.Namespace { o(&options)
continue
}
if rule.resource.Type != "*" && rule.resource.Type != res.Type {
continue
}
if rule.resource.Name != "*" && rule.resource.Name != res.Name {
continue
}
if rule.resource.Endpoint != "*" && rule.resource.Endpoint != res.Endpoint {
continue
}
// a blank role indicates anyone can access the resource, even without an account
if rule.role == "" {
return nil
}
// all furter checks require an account
if acc == nil {
continue
}
// this rule allows any account access, allow the request
if rule.role == "*" {
return nil
}
// if the account has the necessary role, allow the request
for _, r := range acc.Roles {
if r == rule.role {
return nil
}
}
} }
// no rules matched, forbid the request return rules.Verify(j.rules, acc, res)
return auth.ErrForbidden }
func (j *jwt) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
j.Lock()
defer j.Unlock()
return j.rules, nil
} }
func (j *jwt) Inspect(token string) (*auth.Account, error) { func (j *jwt) Inspect(token string) (*auth.Account, error) {

View File

@ -1,6 +1,7 @@
package auth package auth
import ( import (
"context"
"time" "time"
"github.com/micro/go-micro/v2/auth/provider" "github.com/micro/go-micro/v2/auth/provider"
@ -13,9 +14,6 @@ func NewOptions(opts ...Option) Options {
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
if len(options.Namespace) == 0 {
options.Namespace = DefaultNamespace
}
if options.Client == nil { if options.Client == nil {
options.Client = client.DefaultClient options.Client = client.DefaultClient
} }
@ -124,10 +122,8 @@ func WithClient(c client.Client) Option {
type GenerateOptions struct { type GenerateOptions struct {
// Metadata associated with the account // Metadata associated with the account
Metadata map[string]string Metadata map[string]string
// Roles/scopes associated with the account // Scopes the account has access too
Roles []string Scopes []string
// Namespace the account belongs too
Namespace string
// Provider of the account, e.g. oauth // Provider of the account, e.g. oauth
Provider string Provider string
// Type of the account, e.g. user // Type of the account, e.g. user
@ -159,20 +155,6 @@ func WithMetadata(md map[string]string) GenerateOption {
} }
} }
// WithRoles for the generated account
func WithRoles(rs ...string) GenerateOption {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// WithNamespace for the generated account
func WithNamespace(n string) GenerateOption {
return func(o *GenerateOptions) {
o.Namespace = n
}
}
// WithProvider for the generated account // WithProvider for the generated account
func WithProvider(p string) GenerateOption { func WithProvider(p string) GenerateOption {
return func(o *GenerateOptions) { return func(o *GenerateOptions) {
@ -180,6 +162,13 @@ func WithProvider(p string) GenerateOption {
} }
} }
// WithScopes for the generated account
func WithScopes(s ...string) GenerateOption {
return func(o *GenerateOptions) {
o.Scopes = s
}
}
// NewGenerateOptions from a slice of options // NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions { func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions var options GenerateOptions
@ -236,3 +225,27 @@ func NewTokenOptions(opts ...TokenOption) TokenOptions {
return options return options
} }
type VerifyOptions struct {
Context context.Context
}
type VerifyOption func(o *VerifyOptions)
func VerifyContext(ctx context.Context) VerifyOption {
return func(o *VerifyOptions) {
o.Context = ctx
}
}
type RulesOptions struct {
Context context.Context
}
type RulesOption func(o *RulesOptions)
func RulesContext(ctx context.Context) RulesOption {
return func(o *RulesOptions) {
o.Context = ctx
}
}

93
auth/rules/rules.go Normal file
View File

@ -0,0 +1,93 @@
package rules
import (
"fmt"
"sort"
"strings"
"github.com/micro/go-micro/v2/auth"
)
// Verify an account has access to a resource using the rules provided. If the account does not have
// access an error will be returned. If there are no rules provided which match the resource, an error
// will be returned
func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
// the rule is only to be applied if the type matches the resource or is catch-all (*)
validTypes := []string{"*", res.Type}
// the rule is only to be applied if the name matches the resource or is catch-all (*)
validNames := []string{"*", res.Name}
// rules can have wildcard excludes on endpoints since this can also be a path for web services,
// e.g. /foo/* would include /foo/bar. We also want to check for wildcards and the exact endpoint
validEndpoints := []string{"*", res.Endpoint}
if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
for i := 1; i < len(comps)+1; i++ {
wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
validEndpoints = append(validEndpoints, wildcard)
}
}
// filter the rules to the ones which match the criteria above
filteredRules := make([]*auth.Rule, 0)
for _, rule := range rules {
if !include(validTypes, rule.Resource.Type) {
continue
}
if !include(validNames, rule.Resource.Name) {
continue
}
if !include(validEndpoints, rule.Resource.Endpoint) {
continue
}
filteredRules = append(filteredRules, rule)
}
// sort the filtered rules by priority, highest to lowest
sort.SliceStable(filteredRules, func(i, j int) bool {
return filteredRules[i].Priority > filteredRules[j].Priority
})
// loop through the rules and check for a rule which applies to this account
for _, rule := range filteredRules {
// a blank scope indicates the rule applies to everyone, even nil accounts
if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessGranted {
return nil
}
// all further checks require an account
if acc == nil {
continue
}
// this rule applies to any account
if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessGranted {
return nil
}
// if the account has the necessary scope
if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessGranted {
return nil
}
}
// if no rules matched then return forbidden
return auth.ErrForbidden
}
// include is a helper function which checks to see if the slice contains the value. includes is
// not case sensitive.
func include(slice []string, val string) bool {
for _, s := range slice {
if strings.ToLower(s) == strings.ToLower(val) {
return true
}
}
return false
}

290
auth/rules/rules_test.go Normal file
View File

@ -0,0 +1,290 @@
package rules
import (
"testing"
"github.com/micro/go-micro/v2/auth"
)
func TestVerify(t *testing.T) {
srvResource := &auth.Resource{
Type: "service",
Name: "go.micro.service.foo",
Endpoint: "Foo.Bar",
}
webResource := &auth.Resource{
Type: "service",
Name: "go.micro.web.foo",
Endpoint: "/foo/bar",
}
catchallResource := &auth.Resource{
Type: "*",
Name: "*",
Endpoint: "*",
}
tt := []struct {
Name string
Rules []*auth.Rule
Account *auth.Account
Resource *auth.Resource
Error error
}{
{
Name: "NoRules",
Rules: []*auth.Rule{},
Account: nil,
Resource: srvResource,
Error: auth.ErrForbidden,
},
{
Name: "CatchallPublicAccount",
Account: &auth.Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPublicNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateAccount",
Account: &auth.Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
},
},
Error: auth.ErrForbidden,
},
{
Name: "CatchallServiceRuleMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: srvResource.Type,
Name: srvResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "CatchallServiceRuleNoMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: srvResource.Type,
Name: "wrongname",
Endpoint: "*",
},
},
},
Error: auth.ErrForbidden,
},
{
Name: "ExactRuleValidScope",
Resource: srvResource,
Account: &auth.Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "neededscope",
Resource: srvResource,
},
},
},
{
Name: "ExactRuleInvalidScope",
Resource: srvResource,
Account: &auth.Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "invalidscope",
Resource: srvResource,
},
},
Error: auth.ErrForbidden,
},
{
Name: "CatchallDenyWithAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
},
},
Error: auth.ErrForbidden,
},
{
Name: "CatchallDenyWithNoAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
},
},
Error: auth.ErrForbidden,
},
{
Name: "RulePriorityGrantFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Priority: 1,
},
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Priority: 0,
},
},
},
{
Name: "RulePriorityDenyFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Priority: 0,
},
&auth.Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Priority: 1,
},
},
Error: auth.ErrForbidden,
},
{
Name: "WebExactEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: webResource,
},
},
},
{
Name: "WebExactEndpointInalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "invalidendpoint",
},
},
},
Error: auth.ErrForbidden,
},
{
Name: "WebWildcardEndpoint",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "WebWildcardPathEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/foo/*",
},
},
},
},
{
Name: "WebWildcardPathEndpointInvalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Scope: "*",
Resource: &auth.Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/bar/*",
},
},
},
Error: auth.ErrForbidden,
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
if err := Verify(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
t.Errorf("Expected %v but got %v", tc.Error, err)
}
})
}
}

View File

@ -188,10 +188,9 @@ func (m *Token) GetExpiry() int64 {
type Account struct { type Account 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"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Namespace string `protobuf:"bytes,5,opt,name=namespace,proto3" json:"namespace,omitempty"` Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"`
Provider string `protobuf:"bytes,6,opt,name=provider,proto3" json:"provider,omitempty"` Issuer string `protobuf:"bytes,6,opt,name=issuer,proto3" json:"issuer,omitempty"`
Secret string `protobuf:"bytes,7,opt,name=secret,proto3" json:"secret,omitempty"` Secret string `protobuf:"bytes,7,opt,name=secret,proto3" json:"secret,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -237,13 +236,6 @@ func (m *Account) GetType() string {
return "" return ""
} }
func (m *Account) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
func (m *Account) GetMetadata() map[string]string { func (m *Account) GetMetadata() map[string]string {
if m != nil { if m != nil {
return m.Metadata return m.Metadata
@ -251,16 +243,16 @@ func (m *Account) GetMetadata() map[string]string {
return nil return nil
} }
func (m *Account) GetNamespace() string { func (m *Account) GetScopes() []string {
if m != nil { if m != nil {
return m.Namespace return m.Scopes
} }
return "" return nil
} }
func (m *Account) GetProvider() string { func (m *Account) GetIssuer() string {
if m != nil { if m != nil {
return m.Provider return m.Issuer
} }
return "" return ""
} }
@ -276,7 +268,6 @@ 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:"-"`
@ -328,18 +319,10 @@ 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"`
Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` Scopes []string `protobuf:"bytes,4,rep,name=scopes,proto3" json:"scopes,omitempty"`
Secret string `protobuf:"bytes,5,opt,name=secret,proto3" json:"secret,omitempty"` Secret string `protobuf:"bytes,5,opt,name=secret,proto3" json:"secret,omitempty"`
Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"`
Provider string `protobuf:"bytes,7,opt,name=provider,proto3" json:"provider,omitempty"` Provider string `protobuf:"bytes,7,opt,name=provider,proto3" json:"provider,omitempty"`
@ -380,13 +363,6 @@ func (m *GenerateRequest) GetId() string {
return "" return ""
} }
func (m *GenerateRequest) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
func (m *GenerateRequest) GetMetadata() map[string]string { func (m *GenerateRequest) GetMetadata() map[string]string {
if m != nil { if m != nil {
return m.Metadata return m.Metadata
@ -394,11 +370,11 @@ func (m *GenerateRequest) GetMetadata() map[string]string {
return nil return nil
} }
func (m *GenerateRequest) GetNamespace() string { func (m *GenerateRequest) GetScopes() []string {
if m != nil { if m != nil {
return m.Namespace return m.Scopes
} }
return "" return nil
} }
func (m *GenerateRequest) GetSecret() string { func (m *GenerateRequest) GetSecret() string {
@ -462,7 +438,7 @@ func (m *GenerateResponse) GetAccount() *Account {
} }
type GrantRequest struct { type GrantRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"` Scope string `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -494,9 +470,9 @@ func (m *GrantRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_GrantRequest proto.InternalMessageInfo var xxx_messageInfo_GrantRequest proto.InternalMessageInfo
func (m *GrantRequest) GetRole() string { func (m *GrantRequest) GetScope() string {
if m != nil { if m != nil {
return m.Role return m.Scope
} }
return "" return ""
} }
@ -540,7 +516,7 @@ func (m *GrantResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_GrantResponse proto.InternalMessageInfo var xxx_messageInfo_GrantResponse proto.InternalMessageInfo
type RevokeRequest struct { type RevokeRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"` Scope string `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -572,9 +548,9 @@ func (m *RevokeRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_RevokeRequest proto.InternalMessageInfo var xxx_messageInfo_RevokeRequest proto.InternalMessageInfo
func (m *RevokeRequest) GetRole() string { func (m *RevokeRequest) GetScope() string {
if m != nil { if m != nil {
return m.Role return m.Scope
} }
return "" return ""
} }
@ -799,7 +775,7 @@ func (m *TokenResponse) GetToken() *Token {
type Rule struct { type Rule 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"`
Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"` Scope string `protobuf:"bytes,2,opt,name=scope,proto3" json:"scope,omitempty"`
Resource *Resource `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` Resource *Resource `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"`
Access Access `protobuf:"varint,4,opt,name=access,proto3,enum=go.micro.auth.Access" json:"access,omitempty"` Access Access `protobuf:"varint,4,opt,name=access,proto3,enum=go.micro.auth.Access" json:"access,omitempty"`
Priority int32 `protobuf:"varint,5,opt,name=priority,proto3" json:"priority,omitempty"` Priority int32 `protobuf:"varint,5,opt,name=priority,proto3" json:"priority,omitempty"`
@ -840,9 +816,9 @@ func (m *Rule) GetId() string {
return "" return ""
} }
func (m *Rule) GetRole() string { func (m *Rule) GetScope() string {
if m != nil { if m != nil {
return m.Role return m.Scope
} }
return "" return ""
} }
@ -869,13 +845,10 @@ func (m *Rule) GetPriority() int32 {
} }
type CreateRequest struct { type CreateRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"` Rule *Rule `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
Access Access `protobuf:"varint,3,opt,name=access,proto3,enum=go.micro.auth.Access" json:"access,omitempty"` XXX_unrecognized []byte `json:"-"`
Priority int32 `protobuf:"varint,4,opt,name=priority,proto3" json:"priority,omitempty"` XXX_sizecache int32 `json:"-"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *CreateRequest) Reset() { *m = CreateRequest{} } func (m *CreateRequest) Reset() { *m = CreateRequest{} }
@ -903,34 +876,13 @@ func (m *CreateRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_CreateRequest proto.InternalMessageInfo var xxx_messageInfo_CreateRequest proto.InternalMessageInfo
func (m *CreateRequest) GetRole() string { func (m *CreateRequest) GetRule() *Rule {
if m != nil { if m != nil {
return m.Role return m.Rule
}
return ""
}
func (m *CreateRequest) GetResource() *Resource {
if m != nil {
return m.Resource
} }
return nil return nil
} }
func (m *CreateRequest) GetAccess() Access {
if m != nil {
return m.Access
}
return Access_UNKNOWN
}
func (m *CreateRequest) GetPriority() int32 {
if m != nil {
return m.Priority
}
return 0
}
type CreateResponse struct { type CreateResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -963,13 +915,10 @@ func (m *CreateResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_CreateResponse proto.InternalMessageInfo var xxx_messageInfo_CreateResponse proto.InternalMessageInfo
type DeleteRequest struct { type DeleteRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
Access Access `protobuf:"varint,3,opt,name=access,proto3,enum=go.micro.auth.Access" json:"access,omitempty"` XXX_unrecognized []byte `json:"-"`
Priority int32 `protobuf:"varint,4,opt,name=priority,proto3" json:"priority,omitempty"` XXX_sizecache int32 `json:"-"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
@ -997,34 +946,13 @@ func (m *DeleteRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo
func (m *DeleteRequest) GetRole() string { func (m *DeleteRequest) GetId() string {
if m != nil { if m != nil {
return m.Role return m.Id
} }
return "" return ""
} }
func (m *DeleteRequest) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
func (m *DeleteRequest) GetAccess() Access {
if m != nil {
return m.Access
}
return Access_UNKNOWN
}
func (m *DeleteRequest) GetPriority() int32 {
if m != nil {
return m.Priority
}
return 0
}
type DeleteResponse struct { type DeleteResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -1157,64 +1085,62 @@ func init() {
func init() { proto.RegisterFile("auth/service/proto/auth.proto", fileDescriptor_21300bfacc51fc2a) } func init() { proto.RegisterFile("auth/service/proto/auth.proto", fileDescriptor_21300bfacc51fc2a) }
var fileDescriptor_21300bfacc51fc2a = []byte{ var fileDescriptor_21300bfacc51fc2a = []byte{
// 900 bytes of a gzipped FileDescriptorProto // 872 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xdd, 0x8e, 0xdb, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x51, 0x8f, 0xdb, 0x44,
0x14, 0x5e, 0xff, 0xc4, 0xc9, 0x9e, 0xfc, 0x6c, 0x34, 0xdd, 0x16, 0x2b, 0xed, 0x96, 0xad, 0x8b, 0x10, 0x3e, 0x27, 0xb1, 0x93, 0x9b, 0xc4, 0x77, 0xd1, 0xf6, 0x5a, 0xac, 0x94, 0x6b, 0xaf, 0x2e,
0xd0, 0x52, 0x41, 0x16, 0xa5, 0x37, 0x40, 0x6f, 0x58, 0x35, 0x51, 0x68, 0xa1, 0x41, 0x58, 0x45, 0x82, 0xa3, 0x82, 0x1c, 0x4a, 0x5f, 0x0a, 0x7d, 0xe1, 0xd4, 0x44, 0xa1, 0x85, 0x06, 0x61, 0x15,
0xe5, 0x06, 0x55, 0xc6, 0x39, 0xb0, 0xd6, 0x66, 0x6d, 0x33, 0x33, 0x5e, 0x91, 0x1b, 0x24, 0xde, 0x21, 0x90, 0x50, 0x65, 0x9c, 0x81, 0xb3, 0x2e, 0x67, 0x9b, 0xdd, 0xf5, 0x89, 0xbc, 0x20, 0xf1,
0x81, 0x37, 0x80, 0x2b, 0x9e, 0x89, 0x7b, 0x5e, 0x03, 0xcd, 0x9f, 0x37, 0x76, 0x9c, 0xaa, 0x40, 0xc6, 0x8f, 0xe1, 0x27, 0xf1, 0xce, 0x1f, 0xe0, 0x07, 0x20, 0xef, 0xce, 0xba, 0xb1, 0xe3, 0x54,
0x2f, 0xb8, 0x9b, 0x33, 0xe7, 0xf8, 0xcc, 0xf7, 0x7d, 0xe7, 0xcc, 0xf1, 0xc0, 0x51, 0x54, 0xf0, 0x15, 0x88, 0x37, 0xcf, 0xec, 0xb7, 0xb3, 0xf3, 0x7d, 0x3b, 0x33, 0x6b, 0x38, 0x0e, 0x73, 0x79,
0xf3, 0x53, 0x86, 0xf4, 0x2a, 0x89, 0xf1, 0x34, 0xa7, 0x19, 0xcf, 0x4e, 0xc5, 0xd6, 0x58, 0x2e, 0x71, 0x26, 0x90, 0x5f, 0xc7, 0x11, 0x9e, 0x65, 0x3c, 0x95, 0xe9, 0x59, 0xe1, 0x1a, 0xab, 0x4f,
0x49, 0xff, 0x87, 0x6c, 0x7c, 0x99, 0xc4, 0x34, 0x1b, 0x8b, 0xcd, 0xe0, 0x26, 0xdc, 0xf8, 0x22, 0xe6, 0xfe, 0x94, 0x8e, 0xaf, 0xe2, 0x88, 0xa7, 0xe3, 0xc2, 0xe9, 0xdf, 0x84, 0x1b, 0x5f, 0xc4,
0x61, 0xfc, 0x2c, 0x8e, 0xb3, 0x22, 0xe5, 0x2c, 0xc4, 0x1f, 0x0b, 0x64, 0x3c, 0x78, 0x0a, 0x87, 0x42, 0x9e, 0x47, 0x51, 0x9a, 0x27, 0x52, 0x04, 0xf8, 0x73, 0x8e, 0x42, 0xfa, 0xcf, 0xe0, 0xa8,
0xd5, 0x6d, 0x96, 0x67, 0x29, 0x43, 0x32, 0x81, 0x4e, 0xa4, 0xf7, 0x7c, 0xeb, 0xd8, 0x39, 0xe9, 0xea, 0x16, 0x59, 0x9a, 0x08, 0x64, 0x13, 0xe8, 0x85, 0xe4, 0xf3, 0xac, 0x93, 0xf6, 0x69, 0x7f,
0x4e, 0x6e, 0x8d, 0x2b, 0x09, 0xc7, 0xfa, 0x93, 0xb0, 0x8c, 0x0b, 0x7e, 0xb1, 0xa0, 0xf5, 0x3c, 0x72, 0x6b, 0x5c, 0x09, 0x38, 0xa6, 0x2d, 0x41, 0x89, 0xf3, 0x7f, 0xb3, 0xc0, 0x7e, 0x91, 0x5e,
0xbb, 0xc0, 0x94, 0xdc, 0x83, 0x5e, 0x14, 0xc7, 0xc8, 0xd8, 0x4b, 0x2e, 0x6c, 0xdf, 0x3a, 0xb6, 0x62, 0xc2, 0xee, 0xc1, 0x20, 0x8c, 0x22, 0x14, 0xe2, 0xa5, 0x2c, 0x6c, 0xcf, 0x3a, 0xb1, 0x4e,
0x4e, 0xf6, 0xc3, 0xae, 0xda, 0x53, 0x21, 0xf7, 0xa1, 0x4f, 0xf1, 0x7b, 0x8a, 0xec, 0x5c, 0xc7, 0xf7, 0x83, 0xbe, 0xf6, 0x69, 0xc8, 0x7d, 0x70, 0x39, 0xfe, 0xc8, 0x51, 0x5c, 0x10, 0xa6, 0xa5,
0xd8, 0x32, 0xa6, 0xa7, 0x37, 0x55, 0x90, 0x0f, 0xed, 0x98, 0x62, 0xc4, 0x71, 0xe9, 0x3b, 0xc7, 0x30, 0x03, 0x72, 0x6a, 0x90, 0x07, 0xdd, 0x88, 0x63, 0x28, 0x71, 0xe9, 0xb5, 0x4f, 0xac, 0xd3,
0xd6, 0x89, 0x13, 0x1a, 0x93, 0xdc, 0x02, 0x0f, 0x7f, 0xca, 0x13, 0xba, 0xf6, 0x5d, 0xe9, 0xd0, 0x76, 0x60, 0x4c, 0x76, 0x0b, 0x1c, 0xfc, 0x25, 0x8b, 0xf9, 0xda, 0xeb, 0xa8, 0x05, 0xb2, 0xfc,
0x56, 0xf0, 0xab, 0x0d, 0x6d, 0x8d, 0x8c, 0x0c, 0xc0, 0x4e, 0x96, 0xfa, 0x6c, 0x3b, 0x59, 0x12, 0xbf, 0x2d, 0xe8, 0x52, 0x66, 0xec, 0x00, 0x5a, 0xf1, 0x92, 0xce, 0x6e, 0xc5, 0x4b, 0xc6, 0xa0,
0x02, 0x2e, 0x5f, 0xe7, 0xa8, 0x4f, 0x92, 0x6b, 0x72, 0x08, 0x2d, 0x9a, 0xad, 0x90, 0xf9, 0xce, 0x23, 0xd7, 0x19, 0xd2, 0x49, 0xea, 0x9b, 0x7d, 0x0a, 0xbd, 0x2b, 0x94, 0xe1, 0x32, 0x94, 0xa1,
0xb1, 0x73, 0xb2, 0x1f, 0x2a, 0x83, 0x7c, 0x0a, 0x9d, 0x4b, 0xe4, 0xd1, 0x32, 0xe2, 0x91, 0xef, 0xd7, 0x51, 0x3c, 0xdf, 0x69, 0xe6, 0x39, 0x7e, 0x4e, 0xb0, 0x59, 0x22, 0xf9, 0x3a, 0x28, 0x77,
0x4a, 0xf6, 0xef, 0x34, 0xb3, 0x1f, 0x3f, 0xd3, 0x61, 0xb3, 0x94, 0xd3, 0x75, 0x58, 0x7e, 0x45, 0x15, 0x99, 0x88, 0x28, 0xcd, 0x50, 0x78, 0xf6, 0x49, 0xfb, 0x74, 0x3f, 0x20, 0xab, 0xf0, 0xc7,
0xee, 0xc0, 0x7e, 0x1a, 0x5d, 0x22, 0xcb, 0xa3, 0x18, 0xfd, 0x96, 0x3c, 0xf0, 0x7a, 0x83, 0x8c, 0x42, 0xe4, 0xc8, 0x3d, 0x47, 0x9d, 0x47, 0x96, 0xc2, 0x63, 0xc4, 0x51, 0x7a, 0x5d, 0xed, 0xd7,
0xa0, 0x93, 0xd3, 0xec, 0x2a, 0x59, 0x22, 0xf5, 0x3d, 0xe9, 0x2c, 0x6d, 0xc1, 0x8c, 0x61, 0x4c, 0xd6, 0xe8, 0x31, 0xb8, 0x95, 0x23, 0xd8, 0x10, 0xda, 0x97, 0xb8, 0xa6, 0xfc, 0x8b, 0x4f, 0x76,
0x91, 0xfb, 0x6d, 0xe9, 0xd1, 0xd6, 0xe8, 0x11, 0xf4, 0x2b, 0x87, 0x91, 0x21, 0x38, 0x17, 0xb8, 0x04, 0xf6, 0x75, 0xb8, 0xca, 0x0d, 0x03, 0x6d, 0x7c, 0xd2, 0x7a, 0x64, 0xf9, 0x0b, 0xe8, 0x05,
0xd6, 0xfc, 0xc4, 0x52, 0x90, 0xb9, 0x8a, 0x56, 0x85, 0x61, 0xa8, 0x8c, 0x4f, 0xec, 0x8f, 0xac, 0x28, 0xd2, 0x9c, 0x47, 0x58, 0xd0, 0x4c, 0xc2, 0x2b, 0xa4, 0x8d, 0xea, 0xbb, 0x91, 0xfa, 0x08,
0x60, 0x05, 0x9d, 0x10, 0x59, 0x56, 0xd0, 0x18, 0x85, 0x0c, 0x02, 0x89, 0xfe, 0x50, 0xae, 0x1b, 0x7a, 0x98, 0x2c, 0xb3, 0x34, 0x4e, 0xa4, 0x52, 0x77, 0x3f, 0x28, 0x6d, 0xff, 0xf7, 0x16, 0x1c,
0xa5, 0x19, 0x41, 0x07, 0xd3, 0x65, 0x9e, 0x25, 0x29, 0x97, 0xea, 0xef, 0x87, 0xa5, 0x5d, 0xa5, 0xce, 0x31, 0x41, 0x1e, 0x4a, 0xa4, 0x52, 0xd9, 0x92, 0xf3, 0xb3, 0x0d, 0xe9, 0xda, 0x4a, 0xba,
0xe7, 0xd6, 0xe8, 0x05, 0xbf, 0xdb, 0x70, 0x30, 0xc7, 0x14, 0x69, 0xc4, 0x51, 0x37, 0xda, 0x56, 0x0f, 0x6a, 0xd2, 0xd5, 0x22, 0xbc, 0x81, 0x84, 0x9d, 0xba, 0x84, 0x24, 0x95, 0xbd, 0x29, 0x55,
0x31, 0x4a, 0xe1, 0xed, 0x4d, 0xe1, 0x3f, 0xdb, 0x10, 0xde, 0x91, 0xc2, 0xbf, 0x5f, 0x13, 0xbe, 0xc9, 0xc6, 0xa9, 0xb2, 0xc9, 0x78, 0x7a, 0x1d, 0x2f, 0x91, 0x93, 0xb0, 0xa5, 0xfd, 0xdf, 0xa4,
0x96, 0xf7, 0xf5, 0x0a, 0x50, 0x47, 0xb8, 0x21, 0x72, 0x6b, 0x53, 0xe4, 0x52, 0x07, 0xaf, 0xaa, 0x9d, 0xc2, 0xf0, 0x15, 0x0f, 0xea, 0x8e, 0x8f, 0xa0, 0x4b, 0x55, 0xaf, 0x62, 0xec, 0x6e, 0x0e,
0x43, 0x59, 0xac, 0x76, 0xb5, 0x58, 0xff, 0xad, 0x28, 0x53, 0x18, 0x5e, 0xb3, 0xd1, 0xf7, 0xee, 0x03, 0xf3, 0xbf, 0x85, 0xc1, 0x9c, 0x87, 0x89, 0x34, 0x62, 0x1e, 0x81, 0xad, 0x48, 0x52, 0x0e,
0x43, 0x68, 0xeb, 0xfb, 0x24, 0x73, 0xec, 0xbe, 0x76, 0x26, 0x2c, 0x78, 0x01, 0xbd, 0x39, 0x8d, 0xda, 0x60, 0x0f, 0xa1, 0xc7, 0xe9, 0x1a, 0x55, 0x22, 0xfd, 0xc9, 0x5b, 0xb5, 0xc0, 0xe6, 0x96,
0x52, 0x6e, 0x84, 0x26, 0xe0, 0x0a, 0x2d, 0x4d, 0x79, 0xc5, 0x9a, 0x3c, 0x84, 0x0e, 0xd5, 0xe5, 0x83, 0x12, 0xe8, 0x1f, 0x82, 0x4b, 0xa1, 0x75, 0x76, 0xfe, 0x77, 0xe0, 0x06, 0x78, 0x9d, 0x5e,
0x97, 0x30, 0xba, 0x93, 0xb7, 0x6a, 0x69, 0x4d, 0x77, 0x84, 0x65, 0x60, 0x70, 0x00, 0x7d, 0x9d, 0xe2, 0xff, 0x70, 0xd8, 0x10, 0x0e, 0x4c, 0x6c, 0x3a, 0xed, 0x5d, 0x38, 0x78, 0x9a, 0x88, 0x0c,
0x58, 0x61, 0x0b, 0xbe, 0x81, 0x7e, 0x88, 0x57, 0xd9, 0x05, 0xbe, 0xf1, 0xa3, 0x86, 0x30, 0x30, 0xa3, 0x4d, 0x6e, 0x9b, 0x6d, 0xaf, 0x0d, 0xff, 0x09, 0x1c, 0x96, 0xb8, 0x7f, 0x2d, 0xe3, 0xaf,
0x99, 0xf5, 0x59, 0xef, 0xc2, 0xe0, 0x49, 0xca, 0x72, 0x8c, 0x4b, 0x5e, 0x87, 0xd0, 0xda, 0x1c, 0x30, 0x50, 0x93, 0x61, 0x57, 0x4d, 0xbe, 0xaa, 0x98, 0x56, 0xa5, 0x62, 0xb6, 0xa6, 0x4d, 0xbb,
0x26, 0xca, 0x08, 0x1e, 0xc3, 0x41, 0x19, 0xf7, 0xaf, 0x25, 0xfc, 0x19, 0x7a, 0x72, 0xde, 0xec, 0x61, 0xda, 0xdc, 0x83, 0x81, 0x5a, 0x7c, 0x59, 0x99, 0x2c, 0x7d, 0xe5, 0x9b, 0xe9, 0xf1, 0xf2,
0xea, 0xd5, 0xeb, 0x6e, 0xb1, 0x2b, 0xdd, 0xb2, 0x35, 0xc3, 0x9c, 0x86, 0x19, 0x76, 0x0f, 0x7a, 0x18, 0x5c, 0x3a, 0x9f, 0x28, 0x3c, 0xd8, 0xe4, 0xda, 0x9f, 0x1c, 0xd5, 0x08, 0x68, 0x30, 0x29,
0xd2, 0xf9, 0xb2, 0x32, 0xaf, 0xba, 0x72, 0x6f, 0xa6, 0x86, 0xd6, 0x23, 0xe8, 0xeb, 0xf3, 0x35, 0xf0, 0x87, 0x05, 0x9d, 0x20, 0x5f, 0xe1, 0x56, 0xd6, 0xe5, 0xfd, 0xb4, 0x76, 0xdd, 0x4f, 0xfb,
0x85, 0x07, 0x9b, 0x5c, 0xbb, 0x93, 0xc3, 0x1a, 0x01, 0x15, 0xac, 0x15, 0xf8, 0xc3, 0x02, 0x37, 0x0d, 0xef, 0x87, 0x7d, 0x08, 0x8e, 0x9e, 0xb2, 0x2a, 0xfb, 0x83, 0xc9, 0xcd, 0x6d, 0x45, 0x51,
0x2c, 0x56, 0xd8, 0x34, 0xee, 0x64, 0x75, 0xec, 0x1d, 0xd5, 0x71, 0x5e, 0xb3, 0x3a, 0xe4, 0x03, 0x88, 0x80, 0x40, 0xba, 0x6b, 0xe2, 0x94, 0xc7, 0x72, 0xad, 0x7a, 0xcc, 0x0e, 0x4a, 0xdb, 0x7f,
0xf0, 0xd4, 0xe4, 0x96, 0xd8, 0x07, 0x93, 0x9b, 0xdb, 0x7a, 0x22, 0x63, 0xa1, 0x0e, 0x52, 0xf7, 0x04, 0xee, 0x13, 0x35, 0x6d, 0x8d, 0xd8, 0xef, 0x41, 0x87, 0xe7, 0x2b, 0x24, 0xaa, 0x37, 0xea,
0x25, 0xc9, 0x68, 0xc2, 0xd7, 0xf2, 0x76, 0xb5, 0xc2, 0xd2, 0x0e, 0x7e, 0xb3, 0xa0, 0xff, 0x58, 0xc9, 0xe4, 0x2b, 0x0c, 0x14, 0xa0, 0x28, 0x12, 0xb3, 0x93, 0x8a, 0xe4, 0x2e, 0xb8, 0x53, 0x5c,
0x8e, 0xf0, 0x37, 0xdd, 0x43, 0x1b, 0x28, 0x9d, 0x7f, 0x8a, 0xd2, 0xad, 0xa1, 0x1c, 0xc2, 0xc0, 0xe1, 0xce, 0x61, 0x52, 0x6c, 0x31, 0x00, 0xda, 0xe2, 0x42, 0xbf, 0x78, 0x99, 0xcc, 0x43, 0xf5,
0x80, 0xd4, 0xed, 0x28, 0x70, 0x4f, 0x71, 0x85, 0xff, 0x7b, 0xdc, 0x06, 0xa4, 0xc6, 0xdd, 0x87, 0x31, 0x0c, 0xb4, 0x49, 0xc2, 0xbf, 0x0f, 0x76, 0x71, 0x96, 0x79, 0x9d, 0x1a, 0xb3, 0xd1, 0x88,
0xae, 0xf8, 0xbd, 0x9b, 0xbf, 0xfd, 0xc7, 0xd0, 0x53, 0xa6, 0xee, 0xb3, 0xf7, 0xa0, 0x45, 0x0b, 0x07, 0x63, 0x70, 0x34, 0x6d, 0xd6, 0x87, 0xee, 0xd7, 0x8b, 0xcf, 0x17, 0x5f, 0x7e, 0xb3, 0x18,
0x31, 0x84, 0xd5, 0x2f, 0xfe, 0x46, 0x1d, 0x6d, 0xb1, 0xc2, 0x50, 0x45, 0x3c, 0x18, 0x83, 0xa7, 0xee, 0x15, 0xc6, 0x3c, 0x38, 0x5f, 0xbc, 0x98, 0x4d, 0x87, 0x16, 0x03, 0x70, 0xa6, 0xb3, 0xc5,
0x90, 0x90, 0x2e, 0xb4, 0xbf, 0x5e, 0x7c, 0xbe, 0xf8, 0xf2, 0xc5, 0x62, 0xb8, 0x27, 0x8c, 0x79, 0xd3, 0xd9, 0x74, 0xd8, 0x9a, 0xfc, 0x65, 0x41, 0xe7, 0x3c, 0x97, 0x17, 0xec, 0x39, 0xf4, 0x4c,
0x78, 0xb6, 0x78, 0x3e, 0x9b, 0x0e, 0x2d, 0x02, 0xe0, 0x4d, 0x67, 0x8b, 0x27, 0xb3, 0xe9, 0xd0, 0xeb, 0xb3, 0x3b, 0xaf, 0x9f, 0x6d, 0xa3, 0xbb, 0x3b, 0xd7, 0x89, 0xcf, 0x1e, 0x7b, 0x06, 0x5d,
0x9e, 0xfc, 0x65, 0x81, 0x7b, 0x56, 0xf0, 0x73, 0xf2, 0x0c, 0x3a, 0x66, 0xca, 0x91, 0xbb, 0xaf, 0xea, 0x00, 0x76, 0x5c, 0x43, 0x57, 0x3b, 0x68, 0x74, 0x67, 0xd7, 0x72, 0x19, 0x6b, 0x6a, 0x9e,
0x1e, 0xe6, 0xa3, 0xb7, 0x77, 0xfa, 0x35, 0x9f, 0x3d, 0xf2, 0x14, 0xda, 0xfa, 0xc2, 0x93, 0xa3, 0xda, 0xdb, 0x8d, 0x15, 0x47, 0x71, 0xde, 0x6e, 0x5e, 0x34, 0x51, 0x26, 0xdf, 0x43, 0xcf, 0xbc,
0x5a, 0x74, 0x75, 0x60, 0x8c, 0xee, 0xee, 0x72, 0x97, 0xb9, 0xa6, 0xe6, 0xbd, 0x72, 0xbb, 0xf1, 0xfc, 0xec, 0x2b, 0xe8, 0x14, 0x02, 0x33, 0xbf, 0xb6, 0xa7, 0xe1, 0xaf, 0x61, 0x74, 0xff, 0xb5,
0x82, 0xe9, 0x3c, 0x77, 0x9a, 0x9d, 0x26, 0xcb, 0xe4, 0x5b, 0xe8, 0x98, 0xe7, 0x13, 0xf9, 0x0a, 0x98, 0x32, 0xfc, 0x9f, 0x16, 0xd8, 0xc5, 0x45, 0x08, 0x36, 0x07, 0x47, 0x57, 0x04, 0xab, 0xa7,
0x5c, 0x21, 0x30, 0x09, 0x6a, 0xdf, 0x34, 0x3c, 0xbd, 0x46, 0xf7, 0x5f, 0x19, 0x53, 0xa6, 0xff, 0x54, 0x29, 0xb1, 0xd1, 0xf1, 0x8e, 0xd5, 0x92, 0xf7, 0x1c, 0x1c, 0x5d, 0x27, 0x5b, 0x81, 0x2a,
0xd3, 0x82, 0x96, 0x28, 0x04, 0x23, 0x73, 0xf0, 0x54, 0x5b, 0x92, 0x3a, 0xa4, 0xca, 0x95, 0x1a, 0xf5, 0xb5, 0x15, 0xa8, 0x56, 0x5c, 0x7b, 0xec, 0x9c, 0xe8, 0x8e, 0x1a, 0xa8, 0x98, 0x20, 0xb7,
0x1d, 0xed, 0xf0, 0x96, 0xbc, 0xe7, 0xe0, 0xa9, 0x3e, 0xd9, 0x4a, 0x54, 0xe9, 0xf1, 0xad, 0x44, 0x1b, 0xd7, 0x4c, 0x88, 0x1f, 0x1c, 0xf5, 0xa3, 0xf5, 0xf0, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff,
0xb5, 0xe6, 0xda, 0x23, 0x67, 0x9a, 0xee, 0xa8, 0x81, 0x8a, 0x49, 0x72, 0xbb, 0xd1, 0x67, 0x52, 0x52, 0x12, 0xc2, 0xdb, 0x89, 0x09, 0x00, 0x00,
0x7c, 0xe7, 0xc9, 0xd7, 0xea, 0xc3, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x67, 0x3c, 0x6e,
0xce, 0x0a, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

View File

@ -35,10 +35,9 @@ message Token {
message Account { message Account {
string id = 1; string id = 1;
string type = 2; string type = 2;
repeated string roles = 3;
map<string, string> metadata = 4; map<string, string> metadata = 4;
string namespace = 5; repeated string scopes = 5;
string provider = 6; string issuer = 6;
string secret = 7; string secret = 7;
} }
@ -46,14 +45,12 @@ 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 {
string id = 1; string id = 1;
repeated string roles = 2;
map<string, string> metadata = 3; map<string, string> metadata = 3;
string namespace = 4; repeated string scopes = 4;
string secret = 5; string secret = 5;
string type = 6; string type = 6;
string provider = 7; string provider = 7;
@ -64,14 +61,14 @@ message GenerateResponse {
} }
message GrantRequest { message GrantRequest {
string role = 1; string scope = 1;
Resource resource = 2; Resource resource = 2;
} }
message GrantResponse {} message GrantResponse {}
message RevokeRequest { message RevokeRequest {
string role = 1; string scope = 1;
Resource resource = 2; Resource resource = 2;
} }
@ -104,26 +101,20 @@ enum Access {
message Rule { message Rule {
string id = 1; string id = 1;
string role = 2; string scope = 2;
Resource resource = 3; Resource resource = 3;
Access access = 4; Access access = 4;
int32 priority = 5; int32 priority = 5;
} }
message CreateRequest { message CreateRequest {
string role = 1; Rule rule = 1;
Resource resource = 2;
Access access = 3;
int32 priority = 4;
} }
message CreateResponse {} message CreateResponse {}
message DeleteRequest { message DeleteRequest {
string role = 1; string id = 1;
Resource resource = 2;
Access access = 3;
int32 priority = 4;
} }
message DeleteResponse {} message DeleteResponse {}

View File

@ -2,25 +2,22 @@ package service
import ( import (
"context" "context"
"fmt"
"sort"
"strings" "strings"
"time" "time"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
pb "github.com/micro/go-micro/v2/auth/service/proto" pb "github.com/micro/go-micro/v2/auth/service/proto"
"github.com/micro/go-micro/v2/auth/token" "github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/jwt" "github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v2/client" "github.com/micro/go-micro/v2/client"
log "github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/util/jitter"
) )
// svc is the service implementation of the Auth interface // svc is the service implementation of the Auth interface
type svc struct { type svc struct {
options auth.Options options auth.Options
auth pb.AuthService auth pb.AuthService
rule pb.RulesService rules pb.RulesService
jwt token.Provider jwt token.Provider
} }
@ -38,7 +35,7 @@ func (s *svc) Init(opts ...auth.Option) {
} }
s.auth = pb.NewAuthService("go.micro.auth", s.options.Client) s.auth = pb.NewAuthService("go.micro.auth", s.options.Client)
s.rule = pb.NewRulesService("go.micro.auth", s.options.Client) s.rules = pb.NewRulesService("go.micro.auth", s.options.Client)
// if we have a JWT public key passed as an option, // if we have a JWT public key passed as an option,
// we can decode tokens with the type "JWT" locally // we can decode tokens with the type "JWT" locally
@ -57,13 +54,12 @@ func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, e
options := auth.NewGenerateOptions(opts...) options := auth.NewGenerateOptions(opts...)
rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{ rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{
Id: id, Id: id,
Type: options.Type, Type: options.Type,
Secret: options.Secret, Secret: options.Secret,
Roles: options.Roles, Scopes: options.Scopes,
Metadata: options.Metadata, Metadata: options.Metadata,
Provider: options.Provider, Provider: options.Provider,
Namespace: options.Namespace,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -73,87 +69,75 @@ func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, e
} }
// Grant access to a resource // Grant access to a resource
func (s *svc) Grant(role string, res *auth.Resource) error { func (s *svc) Grant(rule *auth.Rule) error {
_, err := s.rule.Create(context.TODO(), &pb.CreateRequest{ access := pb.Access_UNKNOWN
Role: role, if rule.Access == auth.AccessGranted {
Access: pb.Access_GRANTED, access = pb.Access_GRANTED
Resource: &pb.Resource{ } else if rule.Access == auth.AccessDenied {
Namespace: res.Namespace, access = pb.Access_DENIED
Type: res.Type, }
Name: res.Name,
Endpoint: res.Endpoint, _, err := s.rules.Create(context.TODO(), &pb.CreateRequest{
Rule: &pb.Rule{
Id: rule.ID,
Scope: rule.Scope,
Priority: rule.Priority,
Access: access,
Resource: &pb.Resource{
Type: rule.Resource.Type,
Name: rule.Resource.Name,
Endpoint: rule.Resource.Endpoint,
},
}, },
}) })
return err return err
} }
// Revoke access to a resource // Revoke access to a resource
func (s *svc) Revoke(role string, res *auth.Resource) error { func (s *svc) Revoke(rule *auth.Rule) error {
_, err := s.rule.Delete(context.TODO(), &pb.DeleteRequest{ _, err := s.rules.Delete(context.TODO(), &pb.DeleteRequest{
Role: role, Id: rule.ID,
Access: pb.Access_GRANTED,
Resource: &pb.Resource{
Namespace: res.Namespace,
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
}) })
return err return err
} }
func (s *svc) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
var options auth.RulesOptions
for _, o := range opts {
o(&options)
}
if options.Context == nil {
options.Context = context.TODO()
}
rsp, err := s.rules.List(options.Context, &pb.ListRequest{}, client.WithCache(time.Second*30))
if err != nil {
return nil, err
}
rules := make([]*auth.Rule, len(rsp.Rules))
for i, r := range rsp.Rules {
rules[i] = serializeRule(r)
}
return rules, nil
}
// 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, opts ...auth.VerifyOption) error {
// set the namespace on the resource var options auth.VerifyOptions
if len(res.Namespace) == 0 { for _, o := range opts {
res.Namespace = s.Options().Namespace o(&options)
} }
queries := [][]string{ rs, err := s.Rules(auth.RulesContext(options.Context))
{res.Namespace, res.Type, res.Name, res.Endpoint}, // check for specific role, e.g. service.foo.ListFoo:admin (role is checked in accessForRule) if err != nil {
{res.Namespace, res.Type, res.Name, "*"}, // check for wildcard endpoint, e.g. service.foo* return err
{res.Namespace, res.Type, "*"}, // check for wildcard name, e.g. service.*
{res.Namespace, "*"}, // check for wildcard type, e.g. *
{"*"}, // check for wildcard namespace
} }
// endpoint is a url which can have wildcard excludes, e.g. return rules.Verify(rs, acc, res)
// "/foo/*" will allow "/foo/bar"
if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
for i := 1; i < len(comps); i++ {
wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
queries = append(queries, []string{res.Type, res.Name, wildcard})
}
}
// set a default account id / namespace to log
logID := acc.ID
if len(logID) == 0 {
logID = "[no account]"
}
logNamespace := acc.Namespace
if len(logNamespace) == 0 {
logNamespace = "[no namespace]"
}
for _, q := range queries {
for _, rule := range s.listRules(q...) {
switch accessForRule(rule, acc, res) {
case pb.Access_UNKNOWN:
continue // rule did not specify access, check the next rule
case pb.Access_GRANTED:
log.Tracef("%v:%v granted access to %v:%v:%v:%v by rule %v", logNamespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, rule.Id)
return nil // rule grants the account access to the resource
case pb.Access_DENIED:
log.Tracef("%v:%v denied access to %v:%v:%v:%v by rule %v", logNamespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, rule.Id)
return auth.ErrForbidden // rule denies access to the resource
}
}
}
// no rules were found for the resource, default to denying access
log.Tracef("%v:%v denied access to %v:%v:%v:%v by lack of rule (%v rules found for namespace)", logNamespace, logID, res.Namespace, res.Type, res.Name, res.Endpoint, len(s.listRules(res.Namespace)))
return auth.ErrForbidden
} }
// Inspect a token // Inspect a token
@ -189,82 +173,6 @@ func (s *svc) Token(opts ...auth.TokenOption) (*auth.Token, error) {
return serializeToken(rsp.Token), nil return serializeToken(rsp.Token), nil
} }
var ruleJoinKey = ":"
// accessForRule returns a rule status, indicating if a rule permits access to a
// resource for a given account
func accessForRule(rule *pb.Rule, acc *auth.Account, res *auth.Resource) pb.Access {
// a blank role permits access to the public
if rule.Role == "" {
return rule.Access
}
// a * role permits access to any user
if rule.Role == "*" && acc != nil {
return rule.Access
}
for _, role := range acc.Roles {
if rule.Role == role {
return rule.Access
}
// allow user.anything if role is user.*
if strings.HasSuffix(rule.Role, ".*") && strings.HasPrefix(rule.Role, role+".") {
return rule.Access
}
}
return pb.Access_UNKNOWN
}
// listRules gets all the rules from the store which match the filters.
// filters are namespace, type, name and then endpoint.
func (s *svc) listRules(filters ...string) []*pb.Rule {
// load rules using the client cache
allRules, err := s.loadRules()
if err != nil {
return []*pb.Rule{}
}
var rules []*pb.Rule
for _, r := range allRules {
if len(filters) > 0 && r.Resource.Namespace != filters[0] {
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)
}
// sort rules by priority
sort.Slice(rules, func(i, j int) bool {
return rules[i].Priority < rules[j].Priority
})
return rules
}
// loadRules retrieves the rules from the auth service
func (s *svc) loadRules() ([]*pb.Rule, error) {
rsp, err := s.rule.List(context.TODO(), &pb.ListRequest{}, client.WithCache(time.Minute))
if err != nil {
log.Debugf("Error listing rules: %v", err)
return nil, err
}
return rsp.Rules, nil
}
func serializeToken(t *pb.Token) *auth.Token { func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{ return &auth.Token{
AccessToken: t.AccessToken, AccessToken: t.AccessToken,
@ -276,12 +184,32 @@ func serializeToken(t *pb.Token) *auth.Token {
func serializeAccount(a *pb.Account) *auth.Account { func serializeAccount(a *pb.Account) *auth.Account {
return &auth.Account{ return &auth.Account{
ID: a.Id, ID: a.Id,
Roles: a.Roles, Secret: a.Secret,
Secret: a.Secret, Issuer: a.Issuer,
Metadata: a.Metadata, Metadata: a.Metadata,
Provider: a.Provider, Scopes: a.Scopes,
Namespace: a.Namespace, }
}
func serializeRule(r *pb.Rule) *auth.Rule {
var access auth.Access
if r.Access == pb.Access_GRANTED {
access = auth.AccessGranted
} else {
access = auth.AccessDenied
}
return &auth.Rule{
ID: r.Id,
Scope: r.Scope,
Access: access,
Priority: r.Priority,
Resource: &auth.Resource{
Type: r.Resource.Type,
Name: r.Resource.Name,
Endpoint: r.Resource.Endpoint,
},
} }
} }
@ -292,22 +220,9 @@ func NewAuth(opts ...auth.Option) auth.Auth {
options.Client = client.DefaultClient options.Client = client.DefaultClient
} }
service := &svc{ return &svc{
auth: pb.NewAuthService("go.micro.auth", options.Client), auth: pb.NewAuthService("go.micro.auth", options.Client),
rule: pb.NewRulesService("go.micro.auth", options.Client), rules: pb.NewRulesService("go.micro.auth", options.Client),
options: options, options: options,
} }
// load rules periodically from the auth service
go func() {
ruleTimer := time.NewTicker(time.Second * 30)
for {
<-ruleTimer.C
time.Sleep(jitter.Do(time.Second * 5))
service.loadRules()
}
}()
return service
} }

View File

@ -32,10 +32,10 @@ func TestInspect(t *testing.T) {
t.Run("Valid token", func(t *testing.T) { t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"} md := map[string]string{"foo": "bar"}
roles := []string{"admin"} scopes := []string{"admin"}
subject := "test" subject := "test"
tok, err := b.Generate(&auth.Account{ID: subject, Roles: roles, Metadata: md}) tok, err := b.Generate(&auth.Account{ID: subject, Scopes: scopes, Metadata: md})
if err != nil { if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err) t.Fatalf("Generate returned %v error, expected nil", err)
} }
@ -47,8 +47,8 @@ func TestInspect(t *testing.T) {
if tok2.ID != subject { if tok2.ID != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.ID, subject) t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.ID, subject)
} }
if len(tok2.Roles) != len(roles) { if len(tok2.Scopes) != len(scopes) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles)) t.Errorf("Inspect returned %v scopes, expected %v", len(tok2.Scopes), len(scopes))
} }
if len(tok2.Metadata) != len(md) { if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md) t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)

View File

@ -11,11 +11,9 @@ import (
// authClaims to be encoded in the JWT // authClaims to be encoded in the JWT
type authClaims struct { type authClaims struct {
Type string `json:"type"` Type string `json:"type"`
Roles []string `json:"roles"` Scopes []string `json:"scopes"`
Provider string `json:"provider"` Metadata map[string]string `json:"metadata"`
Metadata map[string]string `json:"metadata"`
Namespace string `json:"namespace"`
jwt.StandardClaims jwt.StandardClaims
} }
@ -52,8 +50,9 @@ func (j *JWT) Generate(acc *auth.Account, opts ...token.GenerateOption) (*token.
// generate the JWT // generate the JWT
expiry := time.Now().Add(options.Expiry) expiry := time.Now().Add(options.Expiry)
t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{ t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{
acc.Type, acc.Roles, acc.Provider, acc.Metadata, acc.Namespace, jwt.StandardClaims{ acc.Type, acc.Scopes, acc.Metadata, jwt.StandardClaims{
Subject: acc.ID, Subject: acc.ID,
Issuer: acc.Issuer,
ExpiresAt: expiry.Unix(), ExpiresAt: expiry.Unix(),
}, },
}) })
@ -97,12 +96,11 @@ func (j *JWT) Inspect(t string) (*auth.Account, error) {
// return the token // return the token
return &auth.Account{ return &auth.Account{
ID: claims.Subject, ID: claims.Subject,
Type: claims.Type, Issuer: claims.Issuer,
Roles: claims.Roles, Type: claims.Type,
Provider: claims.Provider, Scopes: claims.Scopes,
Metadata: claims.Metadata, Metadata: claims.Metadata,
Namespace: claims.Namespace,
}, nil }, nil
} }

View File

@ -42,10 +42,10 @@ func TestInspect(t *testing.T) {
t.Run("Valid token", func(t *testing.T) { t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"} md := map[string]string{"foo": "bar"}
roles := []string{"admin"} scopes := []string{"admin"}
subject := "test" subject := "test"
acc := &auth.Account{ID: subject, Roles: roles, Metadata: md} acc := &auth.Account{ID: subject, Scopes: scopes, Metadata: md}
tok, err := j.Generate(acc) tok, err := j.Generate(acc)
if err != nil { if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err) t.Fatalf("Generate returned %v error, expected nil", err)
@ -58,8 +58,8 @@ func TestInspect(t *testing.T) {
if acc.ID != subject { if acc.ID != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", acc.ID, subject) t.Errorf("Inspect returned %v as the token subject, expected %v", acc.ID, subject)
} }
if len(tok2.Roles) != len(roles) { if len(tok2.Scopes) != len(scopes) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles)) t.Errorf("Inspect returned %v scopes, expected %v", len(tok2.Scopes), len(scopes))
} }
if len(tok2.Metadata) != len(md) { if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md) t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)

View File

@ -48,10 +48,10 @@ func (c *Cache) List() map[string]string {
// key returns a hash for the context and request // key returns a hash for the context and request
func key(ctx context.Context, req *Request) string { func key(ctx context.Context, req *Request) string {
md, _ := metadata.FromContext(ctx) ns, _ := metadata.Get(ctx, "Micro-Namespace")
bytes, _ := json.Marshal(map[string]interface{}{ bytes, _ := json.Marshal(map[string]interface{}{
"metadata": md, "namespace": ns,
"request": map[string]interface{}{ "request": map[string]interface{}{
"service": (*req).Service(), "service": (*req).Service(),
"endpoint": (*req).Endpoint(), "endpoint": (*req).Endpoint(),

View File

@ -65,7 +65,7 @@ func TestCacheKey(t *testing.T) {
}) })
t.Run("DifferentMetadata", func(t *testing.T) { t.Run("DifferentMetadata", func(t *testing.T) {
mdCtx := metadata.Set(context.TODO(), "foo", "bar") mdCtx := metadata.Set(context.TODO(), "Micro-Namespace", "bar")
key1 := key(mdCtx, &req1) key1 := key(mdCtx, &req1)
key2 := key(ctx, &req1) key2 := key(ctx, &req1)

View File

@ -278,6 +278,7 @@ var (
Name: "auth_namespace", Name: "auth_namespace",
EnvVars: []string{"MICRO_AUTH_NAMESPACE"}, EnvVars: []string{"MICRO_AUTH_NAMESPACE"},
Usage: "Namespace for the services auth account", Usage: "Namespace for the services auth account",
Value: "go.micro",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "auth_public_key", Name: "auth_public_key",

View File

@ -2,10 +2,12 @@ package service
import ( import (
"context" "context"
"net/http"
"github.com/micro/go-micro/v2/client" "github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/config/source" "github.com/micro/go-micro/v2/config/source"
proto "github.com/micro/go-micro/v2/config/source/service/proto" proto "github.com/micro/go-micro/v2/config/source/service/proto"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/logger" "github.com/micro/go-micro/v2/logger"
) )
@ -29,7 +31,9 @@ func (m *service) Read() (set *source.ChangeSet, err error) {
Namespace: m.namespace, Namespace: m.namespace,
Path: m.path, Path: m.path,
}) })
if err != nil { if verr, ok := err.(*errors.Error); ok && verr.Code == http.StatusNotFound {
return nil, nil
} else if err != nil {
return nil, err return nil, err
} }

View File

@ -353,10 +353,10 @@ func (m *mdnsRegistry) GetService(service string, opts ...GetOption) ([]*Service
} }
addr := "" addr := ""
// prefer ipv4 addrs // prefer ipv4 addrs
if e.AddrV4 != nil { if len(e.AddrV4) > 0 {
addr = e.AddrV4.String() addr = e.AddrV4.String()
// else use ipv6 // else use ipv6
} else if e.AddrV6 != nil { } else if len(e.AddrV6) > 0 {
addr = "[" + e.AddrV6.String() + "]" addr = "[" + e.AddrV6.String() + "]"
} else { } else {
if logger.V(logger.InfoLevel, logger.DefaultLogger) { if logger.V(logger.InfoLevel, logger.DefaultLogger) {

View File

@ -166,9 +166,7 @@ type CreateOptions struct {
// create type of service // create type of service
Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"`
// image to use // image to use
Image string `protobuf:"bytes,6,opt,name=image,proto3" json:"image,omitempty"` Image string `protobuf:"bytes,6,opt,name=image,proto3" json:"image,omitempty"`
// namespace to use
Namespace string `protobuf:"bytes,7,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:"-"`
@ -241,13 +239,6 @@ func (m *CreateOptions) GetImage() string {
return "" return ""
} }
func (m *CreateOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type CreateRequest struct { type CreateRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Options *CreateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` Options *CreateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
@ -332,9 +323,7 @@ type ReadOptions struct {
// version of the service // version of the service
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// type of service // type of service
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
// namespace of service
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:"-"`
@ -386,13 +375,6 @@ func (m *ReadOptions) GetType() string {
return "" return ""
} }
func (m *ReadOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type ReadRequest struct { type ReadRequest struct {
Options *ReadOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"` Options *ReadOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -472,8 +454,6 @@ func (m *ReadResponse) GetServices() []*Service {
} }
type DeleteOptions struct { type DeleteOptions struct {
// namespace of the service
Namespace string `protobuf:"bytes,1,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:"-"`
@ -504,13 +484,6 @@ func (m *DeleteOptions) XXX_DiscardUnknown() {
var xxx_messageInfo_DeleteOptions proto.InternalMessageInfo var xxx_messageInfo_DeleteOptions proto.InternalMessageInfo
func (m *DeleteOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type DeleteRequest struct { type DeleteRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Options *DeleteOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` Options *DeleteOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
@ -590,8 +563,6 @@ func (m *DeleteResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
type UpdateOptions struct { type UpdateOptions struct {
// namespace of the service
Namespace string `protobuf:"bytes,1,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:"-"`
@ -622,13 +593,6 @@ func (m *UpdateOptions) XXX_DiscardUnknown() {
var xxx_messageInfo_UpdateOptions proto.InternalMessageInfo var xxx_messageInfo_UpdateOptions proto.InternalMessageInfo
func (m *UpdateOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type UpdateRequest struct { type UpdateRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Options *UpdateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` Options *UpdateOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
@ -708,8 +672,6 @@ func (m *UpdateResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_UpdateResponse proto.InternalMessageInfo var xxx_messageInfo_UpdateResponse proto.InternalMessageInfo
type ListOptions struct { type ListOptions struct {
// namespace to list from
Namespace string `protobuf:"bytes,1,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:"-"`
@ -740,13 +702,6 @@ func (m *ListOptions) XXX_DiscardUnknown() {
var xxx_messageInfo_ListOptions proto.InternalMessageInfo var xxx_messageInfo_ListOptions proto.InternalMessageInfo
func (m *ListOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type ListRequest struct { type ListRequest struct {
Options *ListOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"` Options *ListOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -826,8 +781,6 @@ func (m *ListResponse) GetServices() []*Service {
} }
type LogsOptions struct { type LogsOptions struct {
// namespace of the service
Namespace string `protobuf:"bytes,1,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:"-"`
@ -858,13 +811,6 @@ func (m *LogsOptions) XXX_DiscardUnknown() {
var xxx_messageInfo_LogsOptions proto.InternalMessageInfo var xxx_messageInfo_LogsOptions proto.InternalMessageInfo
func (m *LogsOptions) GetNamespace() string {
if m != nil {
return m.Namespace
}
return ""
}
type LogsRequest struct { type LogsRequest struct {
// service to request logs for // service to request logs for
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
@ -1031,52 +977,50 @@ func init() {
} }
var fileDescriptor_2434d8152598889b = []byte{ var fileDescriptor_2434d8152598889b = []byte{
// 711 bytes of a gzipped FileDescriptorProto // 683 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x6f, 0xd3, 0x4c, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0xd3, 0x40,
0x10, 0xae, 0x63, 0x27, 0x69, 0x27, 0x6f, 0x5e, 0x55, 0xab, 0x0a, 0x99, 0xf2, 0x15, 0x99, 0x03, 0x14, 0xad, 0xe3, 0x3c, 0xda, 0x1b, 0x42, 0xab, 0x51, 0x85, 0x4c, 0x79, 0x45, 0x66, 0x41, 0xd9,
0x45, 0xa8, 0x2e, 0x4a, 0x85, 0xf8, 0x3a, 0x96, 0x94, 0x4b, 0x2b, 0x24, 0x23, 0x7e, 0xc0, 0xe2, 0x38, 0x28, 0x15, 0xe2, 0xb5, 0x2c, 0x09, 0x9b, 0x46, 0x48, 0x46, 0xfd, 0x80, 0xc1, 0xb9, 0x8a,
0x8c, 0x22, 0x8b, 0xda, 0x6b, 0xbc, 0xeb, 0x48, 0x3d, 0x71, 0xe4, 0x8f, 0x70, 0xe7, 0x67, 0x70, 0x2c, 0x6a, 0x8f, 0xf1, 0x8c, 0x23, 0x65, 0xc5, 0x92, 0x35, 0xff, 0xc0, 0xbf, 0xb0, 0xe6, 0x8f,
0xe6, 0x1f, 0xa1, 0xfd, 0x8a, 0x3f, 0x1a, 0x47, 0xad, 0xaa, 0xde, 0x76, 0xc6, 0xb3, 0xb3, 0xcf, 0xd0, 0xbc, 0xfc, 0x48, 0xeb, 0x48, 0xa8, 0xea, 0x6e, 0xce, 0xf5, 0xcc, 0x9d, 0x73, 0xce, 0x9d,
0xf3, 0xcc, 0xec, 0xac, 0xe1, 0x69, 0x51, 0x66, 0x22, 0x49, 0xf1, 0x88, 0x63, 0xb1, 0x4c, 0x62, 0x7b, 0x65, 0x78, 0x9e, 0x17, 0xa9, 0x88, 0x13, 0x9c, 0x70, 0xcc, 0xd7, 0x71, 0x84, 0x93, 0x2c,
0x3c, 0xca, 0x0b, 0x26, 0xd8, 0x91, 0xf1, 0x86, 0xca, 0x22, 0xbb, 0x0b, 0x16, 0xa6, 0x49, 0x5c, 0x67, 0x82, 0x4d, 0x4c, 0x34, 0x50, 0x88, 0x1c, 0xad, 0x58, 0x90, 0xc4, 0x51, 0xce, 0x02, 0x13,
0xb0, 0xd0, 0xf8, 0x83, 0xbf, 0x0e, 0x0c, 0x3f, 0xeb, 0x1d, 0x84, 0x80, 0x97, 0xd1, 0x14, 0x7d, 0xf7, 0xff, 0x3a, 0x30, 0xf8, 0xa2, 0x4f, 0x10, 0x02, 0xdd, 0x94, 0x26, 0xe8, 0x39, 0x63, 0xe7,
0x67, 0xe2, 0x1c, 0xec, 0x44, 0x6a, 0x4d, 0x7c, 0x18, 0x2e, 0xb1, 0xe0, 0x09, 0xcb, 0xfc, 0x9e, 0xf4, 0x20, 0x54, 0x6b, 0xe2, 0xc1, 0x60, 0x8d, 0x39, 0x8f, 0x59, 0xea, 0x75, 0x54, 0xd8, 0x42,
0x72, 0x5b, 0x93, 0xdc, 0x83, 0x01, 0x67, 0x65, 0x11, 0xa3, 0xef, 0xaa, 0x0f, 0xc6, 0x22, 0x27, 0xf2, 0x00, 0xfa, 0x9c, 0x15, 0x79, 0x84, 0x9e, 0xab, 0x3e, 0x18, 0x44, 0xce, 0x61, 0x3f, 0x41,
0xb0, 0x9d, 0xa2, 0xa0, 0x73, 0x2a, 0xa8, 0xef, 0x4d, 0xdc, 0x83, 0xd1, 0xf4, 0x59, 0xd8, 0x3e, 0x41, 0x97, 0x54, 0x50, 0xaf, 0x3b, 0x76, 0x4f, 0x87, 0xd3, 0x17, 0xc1, 0xf6, 0xb5, 0x81, 0xb9,
0x36, 0x34, 0x47, 0x86, 0xe7, 0x26, 0x72, 0x96, 0x89, 0xe2, 0x32, 0x5a, 0x6d, 0xdc, 0x7f, 0x0f, 0x32, 0x58, 0x98, 0x9d, 0xb3, 0x54, 0xe4, 0x9b, 0xb0, 0x3c, 0x78, 0xf2, 0x01, 0x46, 0x8d, 0x4f,
0xe3, 0xc6, 0x27, 0xb2, 0x0b, 0xee, 0x37, 0xbc, 0x34, 0xd0, 0xe4, 0x92, 0xec, 0x41, 0x7f, 0x49, 0xe4, 0x08, 0xdc, 0x6f, 0xb8, 0x31, 0xd4, 0xe4, 0x92, 0x1c, 0x43, 0x6f, 0x4d, 0xaf, 0x0a, 0x34,
0x2f, 0x4a, 0x34, 0xb8, 0xb4, 0xf1, 0xae, 0xf7, 0xc6, 0x09, 0x52, 0xe8, 0xcf, 0x96, 0x98, 0x09, 0xbc, 0x34, 0x78, 0xdf, 0x79, 0xeb, 0xf8, 0x09, 0xf4, 0x66, 0x6b, 0x4c, 0x85, 0x14, 0x24, 0x36,
0x49, 0x48, 0x5c, 0xe6, 0x2b, 0x42, 0x72, 0x4d, 0x1e, 0xc2, 0x8e, 0x44, 0xc0, 0x05, 0x4d, 0x73, 0x59, 0x29, 0x48, 0xae, 0xc9, 0x63, 0x38, 0x90, 0x0c, 0xb8, 0xa0, 0x49, 0xa6, 0x8e, 0xba, 0x61,
0xb5, 0xd5, 0x8d, 0x2a, 0x87, 0xa4, 0x6b, 0xf4, 0x33, 0xac, 0xac, 0x59, 0x17, 0xc2, 0x6b, 0x08, 0x15, 0x90, 0x72, 0x8d, 0x7f, 0x46, 0x95, 0x85, 0x75, 0x23, 0xba, 0x0d, 0x23, 0xfc, 0x5f, 0x0e,
0x11, 0xfc, 0x76, 0x60, 0x7c, 0x52, 0x20, 0x15, 0xf8, 0x29, 0x17, 0x09, 0xcb, 0xb8, 0x8c, 0x8d, 0x8c, 0xce, 0x73, 0xa4, 0x02, 0x3f, 0x67, 0x22, 0x66, 0x29, 0x97, 0x7b, 0x23, 0x96, 0x24, 0x34,
0x59, 0x9a, 0xd2, 0x6c, 0xee, 0x3b, 0x13, 0x57, 0xc6, 0x1a, 0x53, 0x22, 0xa2, 0xc5, 0x82, 0xfb, 0x5d, 0x7a, 0xce, 0xd8, 0x95, 0x7b, 0x0d, 0x94, 0x8c, 0x68, 0xbe, 0xe2, 0x5e, 0x47, 0x85, 0xd5,
0x3d, 0xe5, 0x56, 0x6b, 0x49, 0x0d, 0xb3, 0xa5, 0xef, 0x2a, 0x97, 0x5c, 0x4a, 0x69, 0x59, 0x29, 0x5a, 0x4a, 0xc3, 0x74, 0xed, 0xb9, 0x2a, 0x24, 0x97, 0xd2, 0x5a, 0x56, 0x88, 0xac, 0x10, 0xe6,
0xf2, 0x52, 0x98, 0xa3, 0x8c, 0xb5, 0xe2, 0xd3, 0xaf, 0xf1, 0xd9, 0x83, 0x7e, 0x92, 0xd2, 0x05, 0x2a, 0x83, 0x4a, 0x3d, 0xbd, 0x9a, 0x9e, 0x63, 0xe8, 0xc5, 0x09, 0x5d, 0xa1, 0xd7, 0xd7, 0x36,
0xfa, 0x03, 0x2d, 0x83, 0x32, 0x24, 0x4b, 0x59, 0x3e, 0x9e, 0xd3, 0x18, 0xfd, 0xa1, 0xfa, 0x52, 0x28, 0xe0, 0xff, 0xb0, 0x94, 0x42, 0xfc, 0x5e, 0x20, 0x17, 0xe4, 0xac, 0x12, 0x26, 0xdd, 0x18,
0x39, 0x82, 0x1f, 0x16, 0x70, 0x84, 0xdf, 0x4b, 0xe4, 0x82, 0x1c, 0x57, 0xb4, 0xa5, 0x56, 0xa3, 0x4e, 0x1f, 0xb6, 0x16, 0xa5, 0xd2, 0xfc, 0x0e, 0x06, 0x4c, 0x4b, 0x52, 0x4e, 0x0d, 0xa7, 0xcf,
0xe9, 0xfd, 0xce, 0x92, 0x55, 0x8a, 0xbc, 0x85, 0x21, 0xd3, 0x84, 0x95, 0x8e, 0xa3, 0xe9, 0x93, 0xae, 0x1f, 0x6a, 0x28, 0x0f, 0xed, 0x7e, 0xff, 0x08, 0xee, 0x5b, 0x02, 0x3c, 0x63, 0x29, 0x47,
0xab, 0x9b, 0x1a, 0xba, 0x44, 0x36, 0x3e, 0xd8, 0x85, 0xff, 0x2d, 0x00, 0x9e, 0xb3, 0x8c, 0x63, 0xff, 0x12, 0x86, 0x21, 0xd2, 0x65, 0xcd, 0xa3, 0x3a, 0xa1, 0x9b, 0x9d, 0xde, 0x7a, 0x72, 0x56,
0xc0, 0x61, 0x14, 0x21, 0x9d, 0xd7, 0x14, 0xac, 0x03, 0x5a, 0x5f, 0x87, 0x56, 0x43, 0x5a, 0x75, 0xbf, 0x5b, 0xe9, 0xf7, 0xe7, 0x3a, 0xad, 0xd5, 0xf9, 0xa6, 0xa2, 0xac, 0x75, 0x3e, 0xb9, 0x4e,
0xdc, 0x66, 0xb5, 0x2b, 0x1d, 0xbc, 0xb6, 0x0e, 0xa7, 0xfa, 0x50, 0xab, 0xc2, 0xeb, 0x8a, 0x90, 0xb9, 0x46, 0xa3, 0x22, 0x3c, 0x83, 0x7b, 0x3a, 0x8f, 0xa6, 0x4b, 0x5e, 0xc3, 0xbe, 0x21, 0xc4,
0x56, 0xe1, 0xd1, 0x55, 0x42, 0x35, 0x90, 0x15, 0x9d, 0x19, 0xfc, 0xa7, 0xf3, 0x68, 0x32, 0xe4, 0x55, 0x11, 0x77, 0x3a, 0x56, 0x6e, 0xf5, 0x0f, 0x61, 0xf4, 0x11, 0xaf, 0xb0, 0x74, 0x44, 0x56,
0x15, 0x6c, 0x1b, 0xb8, 0x5c, 0x35, 0xc0, 0x46, 0x3d, 0x57, 0xa1, 0xc1, 0x21, 0x8c, 0x3f, 0xe0, 0x42, 0x07, 0xee, 0xbc, 0x12, 0x8d, 0x7b, 0x1b, 0x95, 0xb0, 0x04, 0x4c, 0x25, 0x0e, 0x61, 0x74,
0x05, 0x56, 0x7d, 0xd4, 0x40, 0xef, 0xac, 0xa9, 0xa2, 0x0e, 0xbf, 0xf3, 0x2a, 0x36, 0x50, 0x35, 0x99, 0x2d, 0x69, 0x83, 0xa3, 0x0e, 0xdc, 0x39, 0xc7, 0xc6, 0xbd, 0x0d, 0x8e, 0x96, 0x80, 0xe1,
0xaa, 0x68, 0x01, 0x98, 0x2a, 0x1e, 0xc2, 0xf8, 0x4b, 0x3e, 0xa7, 0x37, 0x60, 0xa0, 0xc3, 0xef, 0x38, 0x82, 0xe1, 0x45, 0xcc, 0x85, 0x65, 0x38, 0xd7, 0xf0, 0x7f, 0xaa, 0x5c, 0x3b, 0xde, 0xa8,
0x9c, 0x41, 0x03, 0x55, 0x83, 0x81, 0x05, 0x60, 0x18, 0xbc, 0x80, 0xd1, 0x59, 0xc2, 0xc5, 0xf5, 0xb2, 0xce, 0x73, 0xbb, 0x2a, 0x4b, 0x76, 0x6c, 0xc5, 0x2d, 0xbb, 0xdf, 0x8e, 0xc6, 0x96, 0x5e,
0xf0, 0x9f, 0xea, 0xe0, 0x9b, 0xf4, 0x4f, 0x2d, 0x79, 0xa3, 0x7f, 0x74, 0x9e, 0xdb, 0xf5, 0x8f, 0xfb, 0xdb, 0x96, 0x43, 0x53, 0xe4, 0x48, 0x13, 0x65, 0xd1, 0x7e, 0x68, 0x90, 0xec, 0xe2, 0x88,
0xc4, 0xce, 0x16, 0xfc, 0x7a, 0xd8, 0x7f, 0x39, 0x3a, 0xda, 0x82, 0xef, 0xbe, 0x71, 0x72, 0xd0, 0x15, 0xa9, 0x50, 0x4f, 0xdb, 0x0d, 0x35, 0x90, 0x51, 0x1e, 0xa7, 0x11, 0xaa, 0x31, 0xe0, 0x86,
0x8b, 0x02, 0x69, 0xaa, 0xe4, 0xdd, 0x8e, 0x8c, 0x25, 0x27, 0x4f, 0xcc, 0xca, 0x4c, 0xa8, 0x0b, 0x1a, 0xd4, 0xc5, 0xf7, 0x5a, 0xc5, 0x57, 0xec, 0x2a, 0xf1, 0x7f, 0x1c, 0x38, 0xb8, 0x60, 0xab,
0xe7, 0x46, 0xda, 0x90, 0x5e, 0x9e, 0x64, 0xe6, 0xb6, 0xb9, 0x91, 0x36, 0xea, 0xd2, 0xf4, 0x3b, 0x10, 0x23, 0x96, 0x2f, 0x9b, 0x83, 0xd0, 0xd9, 0x1e, 0x84, 0xb3, 0xda, 0x14, 0xef, 0x28, 0x63,
0xa5, 0xa9, 0xb0, 0x57, 0xd2, 0xfc, 0x71, 0x60, 0xe7, 0x8c, 0x2d, 0x22, 0x8c, 0x59, 0x31, 0x6f, 0x5e, 0xde, 0x78, 0x8b, 0x4e, 0xd6, 0x36, 0xc7, 0xa5, 0x13, 0x09, 0x72, 0x2e, 0xe7, 0x93, 0x99,
0x0e, 0x6f, 0xa7, 0x3d, 0xbc, 0x67, 0xb5, 0x97, 0xa7, 0xa7, 0x64, 0x7b, 0xbe, 0xf6, 0x14, 0x9d, 0xa7, 0x06, 0xde, 0x6a, 0xc2, 0x4f, 0x7f, 0xba, 0x30, 0x08, 0x35, 0x09, 0xb2, 0x80, 0xbe, 0x9e,
0xac, 0xeb, 0xed, 0x91, 0x4a, 0xa4, 0xc8, 0xb9, 0x9c, 0xa9, 0xe6, 0x0d, 0x30, 0xe6, 0xad, 0x5e, 0x34, 0xa4, 0x75, 0x3a, 0x99, 0xba, 0x9c, 0x8c, 0xdb, 0x37, 0x98, 0x67, 0xb7, 0x47, 0x3e, 0x41,
0xa5, 0xe9, 0x4f, 0x17, 0x86, 0x91, 0x06, 0x41, 0xce, 0x61, 0xa0, 0xe7, 0x1f, 0xe9, 0x9c, 0x99, 0x57, 0xce, 0x01, 0xd2, 0x32, 0x37, 0x6c, 0xaa, 0xa7, 0x6d, 0x9f, 0xcb, 0x44, 0x0b, 0xe8, 0xeb,
0xa6, 0x2e, 0xfb, 0x93, 0xee, 0x00, 0xd3, 0xb2, 0x5b, 0xe4, 0x23, 0x78, 0x72, 0xfe, 0x90, 0x8e, 0xbe, 0x23, 0xad, 0xbd, 0xba, 0x83, 0xd7, 0x56, 0xcb, 0xaa, 0x74, 0xba, 0x45, 0x48, 0x6b, 0x5b,
0x79, 0x65, 0x53, 0x3d, 0xee, 0xfa, 0xbc, 0x4a, 0x74, 0x0e, 0x03, 0x7d, 0xa3, 0x49, 0xe7, 0x14, 0xed, 0x48, 0xb7, 0xd5, 0x5d, 0x7b, 0x64, 0x0e, 0x5d, 0xf9, 0x46, 0x48, 0xcb, 0xdb, 0xb1, 0xa9,
0xd8, 0x80, 0xab, 0x35, 0x0c, 0x54, 0x3a, 0x7d, 0xbd, 0x48, 0xe7, 0x95, 0xdc, 0x90, 0xae, 0x75, 0x1e, 0xed, 0x28, 0xba, 0xbf, 0xf7, 0xca, 0xf9, 0xda, 0x57, 0x3f, 0x16, 0x67, 0xff, 0x02, 0x00,
0x33, 0xb7, 0xc8, 0x29, 0x78, 0xb2, 0x47, 0x48, 0x47, 0xef, 0xd8, 0x54, 0x0f, 0x36, 0x14, 0x3d, 0x00, 0xff, 0xff, 0xe1, 0x5b, 0x52, 0x93, 0x7f, 0x08, 0x00, 0x00,
0xd8, 0x7a, 0xe9, 0x7c, 0x1d, 0xa8, 0x9f, 0xa1, 0xe3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8e,
0x6c, 0x03, 0x59, 0x33, 0x09, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

View File

@ -41,8 +41,6 @@ message CreateOptions {
string type = 5; string type = 5;
// image to use // image to use
string image = 6; string image = 6;
// namespace to use
string namespace = 7;
} }
message CreateRequest { message CreateRequest {
@ -59,8 +57,6 @@ message ReadOptions {
string version = 2; string version = 2;
// type of service // type of service
string type = 3; string type = 3;
// namespace of service
string namespace = 4;
} }
message ReadRequest { message ReadRequest {
@ -72,8 +68,6 @@ message ReadResponse {
} }
message DeleteOptions { message DeleteOptions {
// namespace of the service
string namespace = 1;
} }
message DeleteRequest { message DeleteRequest {
@ -84,8 +78,6 @@ message DeleteRequest {
message DeleteResponse {} message DeleteResponse {}
message UpdateOptions { message UpdateOptions {
// namespace of the service
string namespace = 1;
} }
message UpdateRequest { message UpdateRequest {
@ -96,8 +88,6 @@ message UpdateRequest {
message UpdateResponse {} message UpdateResponse {}
message ListOptions { message ListOptions {
// namespace to list from
string namespace = 1;
} }
message ListRequest { message ListRequest {
@ -109,8 +99,6 @@ message ListResponse {
} }
message LogsOptions { message LogsOptions {
// namespace of the service
string namespace = 1;
} }
message LogsRequest{ message LogsRequest{

View File

@ -54,12 +54,11 @@ func (s *svc) Create(svc *runtime.Service, opts ...runtime.CreateOption) error {
Metadata: svc.Metadata, Metadata: svc.Metadata,
}, },
Options: &pb.CreateOptions{ Options: &pb.CreateOptions{
Command: options.Command, Command: options.Command,
Args: options.Args, Args: options.Args,
Env: options.Env, Env: options.Env,
Type: options.Type, Type: options.Type,
Image: options.Image, Image: options.Image,
Namespace: options.Namespace,
}, },
} }
@ -84,9 +83,6 @@ func (s *svc) Logs(service *runtime.Service, opts ...runtime.LogsOption) (runtim
Service: service.Name, Service: service.Name,
Stream: options.Stream, Stream: options.Stream,
Count: options.Count, Count: options.Count,
Options: &pb.LogsOptions{
Namespace: options.Namespace,
},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -176,10 +172,9 @@ func (s *svc) Read(opts ...runtime.ReadOption) ([]*runtime.Service, error) {
// runtime service create request // runtime service create request
req := &pb.ReadRequest{ req := &pb.ReadRequest{
Options: &pb.ReadOptions{ Options: &pb.ReadOptions{
Service: options.Service, Service: options.Service,
Version: options.Version, Version: options.Version,
Type: options.Type, Type: options.Type,
Namespace: options.Namespace,
}, },
} }
@ -220,9 +215,6 @@ func (s *svc) Update(svc *runtime.Service, opts ...runtime.UpdateOption) error {
Source: svc.Source, Source: svc.Source,
Metadata: svc.Metadata, Metadata: svc.Metadata,
}, },
Options: &pb.UpdateOptions{
Namespace: options.Namespace,
},
} }
if _, err := s.runtime.Update(options.Context, req); err != nil { if _, err := s.runtime.Update(options.Context, req); err != nil {
@ -250,9 +242,6 @@ func (s *svc) Delete(svc *runtime.Service, opts ...runtime.DeleteOption) error {
Source: svc.Source, Source: svc.Source,
Metadata: svc.Metadata, Metadata: svc.Metadata,
}, },
Options: &pb.DeleteOptions{
Namespace: options.Namespace,
},
} }
if _, err := s.runtime.Delete(options.Context, req); err != nil { if _, err := s.runtime.Delete(options.Context, req); err != nil {

View File

@ -18,17 +18,17 @@ func Generate(id string, name string, a auth.Auth) error {
// if no credentials were provided, generate an account // if no credentials were provided, generate an account
if len(accID) == 0 || len(accSecret) == 0 { if len(accID) == 0 || len(accSecret) == 0 {
name := fmt.Sprintf("%v-%v", name, id) name := fmt.Sprintf("%v-%v", name, id)
opts := []auth.GenerateOption{ opts := []auth.GenerateOption{
auth.WithType("service"), auth.WithType("service"),
auth.WithRoles("service"), auth.WithScopes("service"),
auth.WithNamespace(a.Options().Namespace),
} }
acc, err := a.Generate(name, opts...) acc, err := a.Generate(name, opts...)
if err != nil { if err != nil {
return err return err
} }
logger.Infof("Auth [%v] Authenticated as %v in the %v namespace", a, name, acc.Namespace) logger.Infof("Auth [%v] Authenticated as %v issued by %v", a, name, acc.Issuer)
accID = acc.ID accID = acc.ID
accSecret = acc.Secret accSecret = acc.Secret

View File

@ -34,7 +34,7 @@ type ServiceEntry struct {
// complete is used to check if we have all the info we need // complete is used to check if we have all the info we need
func (s *ServiceEntry) complete() bool { func (s *ServiceEntry) complete() bool {
return (s.AddrV4 != nil || s.AddrV6 != nil || s.Addr != nil) && s.Port != 0 && s.hasTXT return (len(s.AddrV4) > 0 || len(s.AddrV6) > 0 || len(s.Addr) > 0) && s.Port != 0 && s.hasTXT
} }
// QueryParam is used to customize how a Lookup is performed // QueryParam is used to customize how a Lookup is performed

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"reflect" "reflect"
"strings" "strings"
"time"
"github.com/micro/go-micro/v2/auth" "github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/client" "github.com/micro/go-micro/v2/client"
@ -157,9 +156,14 @@ func (a *authWrapper) Call(ctx context.Context, req client.Request, rsp interfac
return a.Client.Call(ctx, req, rsp, opts...) return a.Client.Call(ctx, req, rsp, opts...)
} }
// set the namespace header if it has not been set (e.g. on a service to service request)
if _, ok := metadata.Get(ctx, "Micro-Namespace"); !ok {
ctx = metadata.Set(ctx, "Micro-Namespace", aa.Options().Namespace)
}
// check to see if we have a valid access token // check to see if we have a valid access token
aaOpts := aa.Options() aaOpts := aa.Options()
if aaOpts.Token != nil && aaOpts.Token.Expiry.Unix() > time.Now().Unix() { if aaOpts.Token != nil && !aaOpts.Token.Expired() {
ctx = metadata.Set(ctx, "Authorization", auth.BearerScheme+aaOpts.Token.AccessToken) ctx = metadata.Set(ctx, "Authorization", auth.BearerScheme+aaOpts.Token.AccessToken)
return a.Client.Call(ctx, req, rsp, opts...) return a.Client.Call(ctx, req, rsp, opts...)
} }
@ -187,20 +191,28 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
// Extract the token if present. Note: if noop is being used // Extract the token if present. Note: if noop is being used
// then the token can be blank without erroring // then the token can be blank without erroring
var token string var account *auth.Account
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(req.Service(), "invalid authorization header. expected Bearer schema") return errors.Unauthorized(req.Service(), "invalid authorization header. expected Bearer schema")
} }
token = header[len(auth.BearerScheme):] // Strip the prefix and inspect the resulting token
account, _ = a.Inspect(strings.TrimPrefix(header, auth.BearerScheme))
} }
// Inspect the token and get the account // Extract the namespace header
account, err := a.Inspect(token) ns, ok := metadata.Get(ctx, "Micro-Namespace")
if err != nil { if !ok {
account = &auth.Account{Namespace: a.Options().Namespace} ns = a.Options().Namespace
ctx = metadata.Set(ctx, "Micro-Namespace", ns)
}
// Check the issuer matches the services namespace. TODO: Stop allowing go.micro to access
// any namespace and instead check for the server issuer.
if account != nil && account.Issuer != ns && account.Issuer != "go.micro" {
return errors.Forbidden(req.Service(), "Account was not issued by %v", ns)
} }
// construct the resource // construct the resource
@ -211,15 +223,15 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
} }
// Verify the caller has access to the resource // Verify the caller has access to the resource
err = a.Verify(account, res) err := a.Verify(account, res, auth.VerifyContext(ctx))
if err != nil && len(account.ID) > 0 { if err != nil && account != nil {
return errors.Forbidden(req.Service(), "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(req.Service(), "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
if len(account.ID) > 0 { if account != nil {
ctx = auth.ContextWithAccount(ctx, account) ctx = auth.ContextWithAccount(ctx, account)
} }

View File

@ -2,12 +2,16 @@ package wrapper
import ( import (
"context" "context"
"net/http"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/client" "github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/metadata" "github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/server"
) )
func TestWrapper(t *testing.T) { func TestWrapper(t *testing.T) {
@ -54,6 +58,313 @@ func TestWrapper(t *testing.T) {
} }
} }
type testAuth struct {
verifyCount int
inspectCount int
namespace string
inspectAccount *auth.Account
verifyError error
auth.Auth
}
func (a *testAuth) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
a.verifyCount = a.verifyCount + 1
return a.verifyError
}
func (a *testAuth) Inspect(token string) (*auth.Account, error) {
a.inspectCount = a.inspectCount + 1
return a.inspectAccount, nil
}
func (a *testAuth) Options() auth.Options {
return auth.Options{Namespace: a.namespace}
}
type testRequest struct {
service string
endpoint string
server.Request
}
func (r testRequest) Service() string {
return r.service
}
func (r testRequest) Endpoint() string {
return r.endpoint
}
func TestAuthHandler(t *testing.T) {
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
return nil
}
debugReq := testRequest{service: "go.micro.service.foo", endpoint: "Debug.Foo"}
serviceReq := testRequest{service: "go.micro.service.foo", endpoint: "Foo.Bar"}
// Debug endpoints should be excluded from auth so auth.Verify should never get called
t.Run("DebugEndpoint", func(t *testing.T) {
a := testAuth{}
handler := AuthHandler(func() auth.Auth {
return &a
})
err := handler(h)(context.TODO(), debugReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if a.verifyCount != 0 {
t.Errorf("Did not expect verify to be called")
}
})
// If the Authorization header is blank, no error should be returned and verify not called
t.Run("BlankAuthorizationHeader", func(t *testing.T) {
a := testAuth{}
handler := AuthHandler(func() auth.Auth {
return &a
})
err := handler(h)(context.TODO(), serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if a.inspectCount != 0 {
t.Errorf("Did not expect inspect to be called")
}
})
// If the Authorization header is invalid, an error should be returned and verify not called
t.Run("InvalidAuthorizationHeader", func(t *testing.T) {
a := testAuth{}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", "Invalid")
err := handler(h)(ctx, serviceReq, nil)
if verr, ok := err.(*errors.Error); !ok || verr.Code != http.StatusUnauthorized {
t.Errorf("Expected unauthorized error but got %v", err)
}
if a.inspectCount != 0 {
t.Errorf("Did not expect inspect to be called")
}
})
// If the Authorization header is valid, no error should be returned and verify should called
t.Run("ValidAuthorizationHeader", func(t *testing.T) {
a := testAuth{}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if a.inspectCount != 1 {
t.Errorf("Expected inspect to be called")
}
})
// If the namespace header was not set on the request, the wrapper should set it to the auths
// own namespace
t.Run("BlankNamespaceHeader", func(t *testing.T) {
a := testAuth{namespace: "mynamespace"}
handler := AuthHandler(func() auth.Auth {
return &a
})
inCtx := context.TODO()
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
inCtx = ctx
return nil
}
err := handler(h)(inCtx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if ns, _ := metadata.Get(inCtx, "Micro-Namespace"); ns != a.namespace {
t.Errorf("Expected namespace to be set to %v but was %v", a.namespace, ns)
}
})
t.Run("ValidNamespaceHeader", func(t *testing.T) {
a := testAuth{namespace: "mynamespace"}
handler := AuthHandler(func() auth.Auth {
return &a
})
inNs := "reqnamespace"
inCtx := metadata.Set(context.TODO(), "Micro-Namespace", inNs)
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
inCtx = ctx
return nil
}
err := handler(h)(inCtx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if ns, _ := metadata.Get(inCtx, "Micro-Namespace"); ns != inNs {
t.Errorf("Expected namespace to remain as %v but was set to %v", inNs, ns)
}
})
// If the callers account was set but the issuer didn't match that of the request, the request
// should be forbidden
t.Run("InvalidAccountIssuer", func(t *testing.T) {
a := testAuth{
namespace: "validnamespace",
inspectAccount: &auth.Account{Issuer: "invalidnamespace"},
}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if verr, ok := err.(*errors.Error); !ok || verr.Code != http.StatusForbidden {
t.Errorf("Expected forbidden error but got %v", err)
}
})
t.Run("ValidAccountIssuer", func(t *testing.T) {
a := testAuth{
namespace: "validnamespace",
inspectAccount: &auth.Account{Issuer: "validnamespace"},
}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
})
// If the caller had a nil account and verify returns an error, the request should be unauthorised
t.Run("NilAccountUnauthorized", func(t *testing.T) {
a := testAuth{verifyError: auth.ErrForbidden}
handler := AuthHandler(func() auth.Auth {
return &a
})
err := handler(h)(context.TODO(), serviceReq, nil)
if verr, ok := err.(*errors.Error); !ok || verr.Code != http.StatusUnauthorized {
t.Errorf("Expected unauthorizard error but got %v", err)
}
})
t.Run("AccountForbidden", func(t *testing.T) {
a := testAuth{verifyError: auth.ErrForbidden, inspectAccount: &auth.Account{}}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if verr, ok := err.(*errors.Error); !ok || verr.Code != http.StatusForbidden {
t.Errorf("Expected forbidden error but got %v", err)
}
})
t.Run("AccountValid", func(t *testing.T) {
a := testAuth{inspectAccount: &auth.Account{}}
handler := AuthHandler(func() auth.Auth {
return &a
})
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
})
// If an account is returned from inspecting the token, it should be set in the context
t.Run("ContextWithAccount", func(t *testing.T) {
accID := "myaccountid"
a := testAuth{inspectAccount: &auth.Account{ID: accID}}
handler := AuthHandler(func() auth.Auth {
return &a
})
inCtx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
inCtx = ctx
return nil
}
err := handler(h)(inCtx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if acc, ok := auth.AccountFromContext(inCtx); !ok {
t.Errorf("Expected an account to be set in the context")
} else if acc.ID != accID {
t.Errorf("Expected the account in the context to have the ID %v but it actually had %v", accID, acc.ID)
}
})
// If verify returns an error the handler should not be called
t.Run("HandlerNotCalled", func(t *testing.T) {
a := testAuth{verifyError: auth.ErrForbidden}
handler := AuthHandler(func() auth.Auth {
return &a
})
var handlerCalled bool
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
handlerCalled = true
return nil
}
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if verr, ok := err.(*errors.Error); !ok || verr.Code != http.StatusUnauthorized {
t.Errorf("Expected unauthorizard error but got %v", err)
}
if handlerCalled {
t.Errorf("Expected the handler to not be called")
}
})
// If verify does not return an error the handler should be called
t.Run("HandlerNotCalled", func(t *testing.T) {
a := testAuth{}
handler := AuthHandler(func() auth.Auth {
return &a
})
var handlerCalled bool
h := func(ctx context.Context, req server.Request, rsp interface{}) error {
handlerCalled = true
return nil
}
ctx := metadata.Set(context.TODO(), "Authorization", auth.BearerScheme+"Token")
err := handler(h)(ctx, serviceReq, nil)
if err != nil {
t.Errorf("Expected nil error but got %v", err)
}
if !handlerCalled {
t.Errorf("Expected the handler be called")
}
})
}
type testClient struct { type testClient struct {
callCount int callCount int
callRsp interface{} callRsp interface{}

View File

@ -444,7 +444,7 @@ func (s *service) Init(opts ...Option) error {
func (s *service) Run() error { func (s *service) Run() error {
// generate an auth account // generate an auth account
srvID := s.opts.Service.Server().Options().Id srvID := s.opts.Service.Server().Options().Id
srvName := s.opts.Service.Name() srvName := s.Options().Name
if err := authutil.Generate(srvID, srvName, s.opts.Service.Options().Auth); err != nil { if err := authutil.Generate(srvID, srvName, s.opts.Service.Options().Auth); err != nil {
return err return err
} }