Service => Service Auth
This commit is contained in:
parent
1222d076f2
commit
d659e435c6
@ -8,8 +8,12 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
// Token is an auth token
|
||||
Token string
|
||||
// ID is the services auth ID
|
||||
ID string
|
||||
// Secret is used to generate new tokens
|
||||
Secret string
|
||||
// Token is the services token used to authenticate itself
|
||||
Token *Token
|
||||
// Public key base64 encoded
|
||||
PublicKey string
|
||||
// Private key base64 encoded
|
||||
@ -45,10 +49,11 @@ func PrivateKey(key string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceToken sets an auth token
|
||||
func ServiceToken(t string) Option {
|
||||
// Credentials sets the auth credentials
|
||||
func Credentials(id, secret string) Option {
|
||||
return func(o *Options) {
|
||||
o.Token = t
|
||||
o.ID = id
|
||||
o.Secret = secret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,13 @@ func (s *svc) Init(opts ...auth.Option) {
|
||||
}
|
||||
|
||||
// load rules periodically from the auth service
|
||||
timer := time.NewTicker(time.Second * 30)
|
||||
ruleTimer := time.NewTicker(time.Second * 30)
|
||||
go func() {
|
||||
// load rules immediately on startup
|
||||
s.loadRules()
|
||||
|
||||
for {
|
||||
<-timer.C
|
||||
<-ruleTimer.C
|
||||
|
||||
// jitter for up to 5 seconds, this stops
|
||||
// all the services calling the auth service
|
||||
@ -70,9 +70,39 @@ func (s *svc) Init(opts ...auth.Option) {
|
||||
s.loadRules()
|
||||
}
|
||||
}()
|
||||
|
||||
// we have client credentials and must load a new token
|
||||
// periodically
|
||||
if len(s.options.ID) > 0 || len(s.options.Secret) > 0 {
|
||||
tokenTimer := time.NewTicker(time.Minute)
|
||||
|
||||
go func() {
|
||||
s.loadToken()
|
||||
|
||||
for {
|
||||
<-tokenTimer.C
|
||||
|
||||
// Do not get a new token if the current one has more than three
|
||||
// minutes remaining. We do 3 minutes to allow multiple retires in
|
||||
// the case one request fails
|
||||
t := s.Options().Token
|
||||
if t != nil && t.Expiry.Unix() > time.Now().Add(time.Minute*3).Unix() {
|
||||
continue
|
||||
}
|
||||
|
||||
// jitter for up to 5 seconds, this stops
|
||||
// all the services calling the auth service
|
||||
// at the exact same time
|
||||
time.Sleep(jitter.Do(time.Second * 5))
|
||||
s.loadToken()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *svc) Options() auth.Options {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.options
|
||||
}
|
||||
|
||||
@ -256,6 +286,24 @@ func (s *svc) loadRules() {
|
||||
s.rules = rsp.Rules
|
||||
}
|
||||
|
||||
// loadToken generates a new token for the service to use when making calls
|
||||
func (s *svc) loadToken() {
|
||||
rsp, err := s.auth.Token(context.TODO(), &pb.TokenRequest{
|
||||
Id: s.Options().ID,
|
||||
Secret: s.Options().Secret,
|
||||
TokenExpiry: int64((time.Minute * 15).Seconds()),
|
||||
})
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error generating token: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.options.Token = serializeToken(rsp.Token)
|
||||
}
|
||||
|
||||
func serializeToken(t *pb.Token) *auth.Token {
|
||||
return &auth.Token{
|
||||
Token: t.Token,
|
||||
|
@ -131,7 +131,15 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
// set the content type for the request
|
||||
header["x-content-type"] = req.ContentType()
|
||||
|
||||
// set the authorization token if one is saved locally
|
||||
// if the caller specifies using service privelages, and the client
|
||||
// has auth set, override the authorization header
|
||||
if opts.WithServicePrivileges && g.opts.Auth != nil && g.opts.Auth.Options().Token != nil {
|
||||
t := g.opts.Auth.Options().Token
|
||||
header["authorization"] = auth.BearerScheme + t.Token
|
||||
}
|
||||
|
||||
// fall back to using the authorization token set in config,
|
||||
// this enables the CLI to provide a token
|
||||
if len(header["authorization"]) == 0 {
|
||||
if token, err := config.Get("token"); err == nil && len(token) > 0 {
|
||||
header["authorization"] = auth.BearerScheme + token
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v2/auth"
|
||||
"github.com/micro/go-micro/v2/broker"
|
||||
"github.com/micro/go-micro/v2/client/selector"
|
||||
"github.com/micro/go-micro/v2/codec"
|
||||
@ -16,6 +17,7 @@ type Options struct {
|
||||
ContentType string
|
||||
|
||||
// Plugged interfaces
|
||||
Auth auth.Auth
|
||||
Broker broker.Broker
|
||||
Codecs map[string]codec.NewCodec
|
||||
Registry registry.Registry
|
||||
@ -55,6 +57,8 @@ type CallOptions struct {
|
||||
Retries int
|
||||
// Request/Response timeout
|
||||
RequestTimeout time.Duration
|
||||
// Use the services own auth token
|
||||
WithServicePrivileges bool
|
||||
|
||||
// Middleware for low level call func
|
||||
CallWrappers []CallWrapper
|
||||
@ -99,6 +103,7 @@ func NewOptions(options ...Option) Options {
|
||||
},
|
||||
PoolSize: DefaultPoolSize,
|
||||
PoolTTL: DefaultPoolTTL,
|
||||
Auth: auth.DefaultAuth,
|
||||
Broker: broker.DefaultBroker,
|
||||
Selector: selector.DefaultSelector,
|
||||
Registry: registry.DefaultRegistry,
|
||||
@ -119,6 +124,13 @@ func Broker(b broker.Broker) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Auth to be used when making a request
|
||||
func Auth(a auth.Auth) Option {
|
||||
return func(o *Options) {
|
||||
o.Auth = a
|
||||
}
|
||||
}
|
||||
|
||||
// Codec to be used to encode/decode requests for a given content type
|
||||
func Codec(contentType string, c codec.NewCodec) Option {
|
||||
return func(o *Options) {
|
||||
@ -291,6 +303,14 @@ func WithDialTimeout(d time.Duration) CallOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithServicePrivileges is a CallOption which overrides the
|
||||
// authorization header with the services own auth token
|
||||
func WithServicePrivileges() CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.WithServicePrivileges = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithMessageContentType(ct string) MessageOption {
|
||||
return func(o *MessageOptions) {
|
||||
o.ContentType = ct
|
||||
|
@ -255,9 +255,14 @@ var (
|
||||
Usage: "Auth for role based access control, e.g. service",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "auth_token",
|
||||
EnvVars: []string{"MICRO_AUTH_TOKEN"},
|
||||
Usage: "Auth token used for client authentication",
|
||||
Name: "auth_id",
|
||||
EnvVars: []string{"MICRO_AUTH_ID"},
|
||||
Usage: "Account ID used for client authentication",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "auth_secret",
|
||||
EnvVars: []string{"MICRO_AUTH_SECRET"},
|
||||
Usage: "Account secret used for client authentication",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "auth_public_key",
|
||||
@ -488,6 +493,7 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
*c.opts.Auth = a()
|
||||
clientOpts = append(clientOpts, client.Auth(*c.opts.Auth))
|
||||
}
|
||||
|
||||
// Set the profile
|
||||
@ -655,8 +661,10 @@ func (c *cmd) Before(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(ctx.String("auth_token")) > 0 {
|
||||
authOpts = append(authOpts, auth.ServiceToken(ctx.String("auth_token")))
|
||||
if len(ctx.String("auth_id")) > 0 || len(ctx.String("auth_secret")) > 0 {
|
||||
authOpts = append(authOpts, auth.Credentials(
|
||||
ctx.String("auth_id"), ctx.String("auth_secret"),
|
||||
))
|
||||
}
|
||||
|
||||
if len(ctx.String("auth_public_key")) > 0 {
|
||||
|
@ -142,6 +142,7 @@ func Tracer(t trace.Tracer) Option {
|
||||
func Auth(a auth.Auth) Option {
|
||||
return func(o *Options) {
|
||||
o.Auth = a
|
||||
o.Client.Init(client.Auth(a))
|
||||
o.Server.Init(server.Auth(a))
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"github.com/micro/go-micro/v2/plugin"
|
||||
"github.com/micro/go-micro/v2/server"
|
||||
"github.com/micro/go-micro/v2/store"
|
||||
"github.com/micro/go-micro/v2/util/config"
|
||||
"github.com/micro/go-micro/v2/util/wrapper"
|
||||
)
|
||||
|
||||
@ -117,9 +116,9 @@ func (s *service) Init(opts ...Option) {
|
||||
// Right now we're just going to load a token
|
||||
// May need to re-read value on change
|
||||
// TODO: should be scoped to micro/auth/token
|
||||
if tk, _ := config.Get("token"); len(tk) > 0 {
|
||||
s.opts.Auth.Init(auth.ServiceToken(tk))
|
||||
}
|
||||
// if tk, _ := config.Get("token"); len(tk) > 0 {
|
||||
// s.opts.Auth.Init(auth.ServiceToken(tk))
|
||||
// }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -38,15 +38,6 @@ func (c *clientWrapper) setHeaders(ctx context.Context) context.Context {
|
||||
mda, _ := metadata.FromContext(ctx)
|
||||
md := metadata.Copy(mda)
|
||||
|
||||
// get auth token
|
||||
if a := c.auth(); a != nil {
|
||||
tk := a.Options().Token
|
||||
// if the token if exists and auth header isn't set then set it
|
||||
if len(tk) > 0 && len(md["Authorization"]) == 0 {
|
||||
md["Authorization"] = auth.BearerScheme + tk
|
||||
}
|
||||
}
|
||||
|
||||
// set headers
|
||||
for k, v := range c.headers {
|
||||
if _, ok := md[k]; !ok {
|
||||
|
Loading…
Reference in New Issue
Block a user