Authenticate on service start
This commit is contained in:
parent
346e034d0a
commit
54951740bf
21
registry/service/options.go
Normal file
21
registry/service/options.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/v2/client"
|
||||||
|
"github.com/micro/go-micro/v2/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientKey struct{}
|
||||||
|
|
||||||
|
// WithClient sets the RPC client
|
||||||
|
func WithClient(c client.Client) registry.Option {
|
||||||
|
return func(o *registry.Options) {
|
||||||
|
if o.Context == nil {
|
||||||
|
o.Context = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Context = context.WithValue(o.Context, clientKey{}, c)
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,8 @@ type serviceRegistry struct {
|
|||||||
name string
|
name string
|
||||||
// address
|
// address
|
||||||
address []string
|
address []string
|
||||||
// client to call registry
|
// registry is the proto client
|
||||||
client pb.RegistryService
|
registry pb.RegistryService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceRegistry) callOpts() []client.CallOption {
|
func (s *serviceRegistry) callOpts() []client.CallOption {
|
||||||
@ -46,6 +46,17 @@ func (s *serviceRegistry) Init(opts ...registry.Option) error {
|
|||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&s.opts)
|
o(&s.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract the client from the context, fallback to grpc
|
||||||
|
var cli client.Client
|
||||||
|
if c, ok := s.opts.Context.Value(clientKey{}).(client.Client); ok {
|
||||||
|
cli = c
|
||||||
|
} else {
|
||||||
|
cli = grpc.NewClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.registry = pb.NewRegistryService(DefaultService, cli)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +78,7 @@ func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.Regis
|
|||||||
pbSrv.Options.Ttl = int64(options.TTL.Seconds())
|
pbSrv.Options.Ttl = int64(options.TTL.Seconds())
|
||||||
|
|
||||||
// register the service
|
// register the service
|
||||||
_, err := s.client.Register(options.Context, pbSrv, s.callOpts()...)
|
_, err := s.registry.Register(options.Context, pbSrv, s.callOpts()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -85,7 +96,7 @@ func (s *serviceRegistry) Deregister(srv *registry.Service, opts ...registry.Der
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deregister the service
|
// deregister the service
|
||||||
_, err := s.client.Deregister(options.Context, ToProto(srv), s.callOpts()...)
|
_, err := s.registry.Deregister(options.Context, ToProto(srv), s.callOpts()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -101,7 +112,7 @@ func (s *serviceRegistry) GetService(name string, opts ...registry.GetOption) ([
|
|||||||
options.Context = context.TODO()
|
options.Context = context.TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp, err := s.client.GetService(options.Context, &pb.GetRequest{
|
rsp, err := s.registry.GetService(options.Context, &pb.GetRequest{
|
||||||
Service: name,
|
Service: name,
|
||||||
}, s.callOpts()...)
|
}, s.callOpts()...)
|
||||||
|
|
||||||
@ -125,7 +136,7 @@ func (s *serviceRegistry) ListServices(opts ...registry.ListOption) ([]*registry
|
|||||||
options.Context = context.TODO()
|
options.Context = context.TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp, err := s.client.ListServices(options.Context, &pb.ListRequest{}, s.callOpts()...)
|
rsp, err := s.registry.ListServices(options.Context, &pb.ListRequest{}, s.callOpts()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -147,7 +158,7 @@ func (s *serviceRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher,
|
|||||||
options.Context = context.TODO()
|
options.Context = context.TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
stream, err := s.client.Watch(options.Context, &pb.WatchRequest{
|
stream, err := s.registry.Watch(options.Context, &pb.WatchRequest{
|
||||||
Service: options.Service,
|
Service: options.Service,
|
||||||
}, s.callOpts()...)
|
}, s.callOpts()...)
|
||||||
|
|
||||||
@ -171,27 +182,29 @@ func NewRegistry(opts ...registry.Option) registry.Registry {
|
|||||||
|
|
||||||
// the registry address
|
// the registry address
|
||||||
addrs := options.Addrs
|
addrs := options.Addrs
|
||||||
|
|
||||||
if len(addrs) == 0 {
|
if len(addrs) == 0 {
|
||||||
addrs = []string{"127.0.0.1:8000"}
|
addrs = []string{"127.0.0.1:8000"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use mdns as a fall back in case its used
|
if options.Context == nil {
|
||||||
mReg := registry.NewRegistry()
|
options.Context = context.TODO()
|
||||||
|
}
|
||||||
|
|
||||||
// create new client with mdns
|
// extract the client from the context, fallback to grpc
|
||||||
cli := grpc.NewClient(
|
var cli client.Client
|
||||||
client.Registry(mReg),
|
if c, ok := options.Context.Value(clientKey{}).(client.Client); ok {
|
||||||
)
|
cli = c
|
||||||
|
} else {
|
||||||
|
cli = grpc.NewClient()
|
||||||
|
}
|
||||||
|
|
||||||
// service name
|
// service name. TODO: accept option
|
||||||
// TODO: accept option
|
|
||||||
name := DefaultService
|
name := DefaultService
|
||||||
|
|
||||||
return &serviceRegistry{
|
return &serviceRegistry{
|
||||||
opts: options,
|
opts: options,
|
||||||
name: name,
|
name: name,
|
||||||
address: addrs,
|
address: addrs,
|
||||||
client: pb.NewRegistryService(name, cli),
|
registry: pb.NewRegistryService(name, cli),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
service.go
44
service.go
@ -1,6 +1,7 @@
|
|||||||
package micro
|
package micro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
rtime "runtime"
|
rtime "runtime"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
"github.com/micro/go-micro/v2/debug/trace"
|
"github.com/micro/go-micro/v2/debug/trace"
|
||||||
"github.com/micro/go-micro/v2/logger"
|
"github.com/micro/go-micro/v2/logger"
|
||||||
"github.com/micro/go-micro/v2/plugin"
|
"github.com/micro/go-micro/v2/plugin"
|
||||||
|
srvRegistry "github.com/micro/go-micro/v2/registry/service"
|
||||||
"github.com/micro/go-micro/v2/runtime"
|
"github.com/micro/go-micro/v2/runtime"
|
||||||
"github.com/micro/go-micro/v2/server"
|
"github.com/micro/go-micro/v2/server"
|
||||||
"github.com/micro/go-micro/v2/store"
|
"github.com/micro/go-micro/v2/store"
|
||||||
@ -115,7 +117,8 @@ func (s *service) Init(opts ...Option) {
|
|||||||
s.opts.Store.Init(store.Table(name))
|
s.opts.Store.Init(store.Table(name))
|
||||||
|
|
||||||
// Set the client for the micro clients
|
// Set the client for the micro clients
|
||||||
// s.opts.Auth.Init(auth.WithClient(s.Client()))
|
s.opts.Auth.Init(auth.WithClient(s.Client()))
|
||||||
|
s.opts.Registry.Init(srvRegistry.WithClient(s.Client()))
|
||||||
s.opts.Runtime.Init(runtime.WithClient(s.Client()))
|
s.opts.Runtime.Init(runtime.WithClient(s.Client()))
|
||||||
s.opts.Store.Init(store.WithClient(s.Client()))
|
s.opts.Store.Init(store.WithClient(s.Client()))
|
||||||
})
|
})
|
||||||
@ -205,6 +208,11 @@ func (s *service) Run() error {
|
|||||||
logger.Infof("Starting [service] %s", s.Name())
|
logger.Infof("Starting [service] %s", s.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate an auth account
|
||||||
|
if err := s.registerAuthAccount(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.Start(); err != nil {
|
if err := s.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -223,3 +231,37 @@ func (s *service) Run() error {
|
|||||||
|
|
||||||
return s.Stop()
|
return s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) registerAuthAccount() error {
|
||||||
|
// determine the type of service from the name. we do this so we can allocate
|
||||||
|
// different roles depending on the type of services. e.g. we don't want web
|
||||||
|
// services talking directly to the runtime. TODO: find a better way to determine
|
||||||
|
// the type of service
|
||||||
|
serviceType := "service"
|
||||||
|
if strings.Contains(s.Name(), "api") {
|
||||||
|
serviceType = "api"
|
||||||
|
} else if strings.Contains(s.Name(), "web") {
|
||||||
|
serviceType = "web"
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a new auth account for the service
|
||||||
|
name := fmt.Sprintf("%v-%v", s.Name(), s.Server().Options().Id)
|
||||||
|
opts := []auth.GenerateOption{
|
||||||
|
auth.WithRoles(serviceType),
|
||||||
|
auth.WithNamespace(s.Options().Auth.Options().Namespace),
|
||||||
|
}
|
||||||
|
acc, err := s.Options().Auth.Generate(name, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a token
|
||||||
|
token, err := s.Options().Auth.Token(auth.WithCredentials(acc.ID, acc.Secret))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Options().Auth.Init(auth.ClientToken(token))
|
||||||
|
|
||||||
|
logger.Infof("Auth [%v] Authenticated as %v", s.Options().Auth, name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,7 +2,6 @@ package wrapper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -182,47 +181,14 @@ func (a *authWrapper) Call(ctx context.Context, req client.Request, rsp interfac
|
|||||||
return callWithToken(tok.AccessToken)
|
return callWithToken(tok.AccessToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have credentials we can generate a new token for the account
|
|
||||||
if len(aaOpts.ID) > 0 && len(aaOpts.Secret) > 0 {
|
|
||||||
tok, err := aa.Token(auth.WithCredentials(aaOpts.ID, aaOpts.Secret))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
aa.Init(auth.ClientToken(tok))
|
|
||||||
return callWithToken(tok.AccessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if a token was provided in config, this is normally used for
|
// check to see if a token was provided in config, this is normally used for
|
||||||
// setting the token when calling via the cli
|
// setting the token when calling via the cli
|
||||||
if token, err := config.Get("micro", "auth", "token"); err == nil && len(token) > 0 {
|
if token, err := config.Get("micro", "auth", "token"); err == nil && len(token) > 0 {
|
||||||
return callWithToken(token)
|
return callWithToken(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine the type of service from the name. we do this so we can allocate
|
// call without an auth token
|
||||||
// different roles depending on the type of services. e.g. we don't want web
|
return a.Client.Call(ctx, req, rsp, opts...)
|
||||||
// services talking directly to the runtime. TODO: find a better way to determine
|
|
||||||
// the type of service
|
|
||||||
serviceType := "service"
|
|
||||||
if strings.Contains(a.name, "api") {
|
|
||||||
serviceType = "api"
|
|
||||||
} else if strings.Contains(a.name, "web") {
|
|
||||||
serviceType = "web"
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a new auth account for the service
|
|
||||||
name := fmt.Sprintf("%v-%v", a.name, a.id)
|
|
||||||
acc, err := aa.Generate(name, auth.WithNamespace(aaOpts.Namespace), auth.WithRoles(serviceType))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
token, err := aa.Token(auth.WithCredentials(acc.ID, acc.Secret))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
aa.Init(auth.ClientToken(token))
|
|
||||||
|
|
||||||
// use the token to execute the request
|
|
||||||
return callWithToken(token.AccessToken)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthClient wraps requests with the auth header
|
// AuthClient wraps requests with the auth header
|
||||||
|
Loading…
Reference in New Issue
Block a user