netbird/client/server/server.go
Maycon Santos fec3132585
Adding peer registration support to JWT (#305)
The management will validate the JWT as it does in the API
 and will register the Peer to the user's account.

New fields were added to grpc messages in management
 and client daemon and its clients were updated

Peer has one new field, UserID, 
that will hold the id of the user that registered it

JWT middleware CheckJWT got a splitter 
and renamed to support validation for non HTTP requests

Added test for adding new Peer with UserID

Lots of tests update because of a new field
2022-05-05 20:02:15 +02:00

224 lines
5.1 KiB
Go

package server
import (
"context"
"fmt"
"sync"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/proto"
)
// Server for service control.
type Server struct {
rootCtx context.Context
actCancel context.CancelFunc
managementURL string
adminURL string
configPath string
logFile string
mutex sync.Mutex
config *internal.Config
proto.UnimplementedDaemonServiceServer
}
// New server instance constructor.
func New(ctx context.Context, managementURL, adminURL, configPath, logFile string) *Server {
return &Server{
rootCtx: ctx,
managementURL: managementURL,
adminURL: adminURL,
configPath: configPath,
logFile: logFile,
}
}
func (s *Server) Start() error {
state := internal.CtxGetState(s.rootCtx)
// if current state contains any error, return it
// in all other cases we can continue execution only if status is idle and up command was
// not in the progress or already successfully estabilished connection.
status, err := state.Status()
if err != nil {
return err
}
if status != internal.StatusIdle {
return nil
}
ctx, cancel := context.WithCancel(s.rootCtx)
s.actCancel = cancel
// if configuration exists, we just start connections.
config, err := internal.ReadConfig(s.managementURL, s.adminURL, s.configPath, nil)
if err != nil {
log.Warnf("no config file, skip connection stage: %v", err)
return nil
}
s.config = config
go func() {
if err := internal.RunClient(ctx, config); err != nil {
log.Errorf("init connections: %v", err)
}
}()
return nil
}
// Login uses setup key to prepare configuration for the daemon.
func (s *Server) Login(_ context.Context, msg *proto.LoginRequest) (*proto.LoginResponse, error) {
s.mutex.Lock()
if s.actCancel != nil {
s.actCancel()
}
ctx, cancel := context.WithCancel(s.rootCtx)
s.actCancel = cancel
s.mutex.Unlock()
state := internal.CtxGetState(ctx)
defer state.Set(internal.StatusIdle)
state.Set(internal.StatusConnecting)
s.mutex.Lock()
managementURL := s.managementURL
if msg.ManagementUrl != "" {
managementURL = msg.ManagementUrl
}
adminURL := s.adminURL
if msg.AdminURL != "" {
adminURL = msg.AdminURL
}
s.mutex.Unlock()
config, err := internal.GetConfig(managementURL, adminURL, s.configPath, msg.PreSharedKey)
if err != nil {
return nil, err
}
s.mutex.Lock()
s.config = config
s.mutex.Unlock()
// login operation uses backoff scheme to connect to management API
// we don't wait for result and return response immediately.
if err := internal.Login(ctx, s.config, msg.SetupKey, msg.JwtToken); err != nil {
log.Errorf("failed login: %v", err)
return nil, err
}
return &proto.LoginResponse{}, nil
}
// Up starts engine work in the daemon.
func (s *Server) Up(_ context.Context, msg *proto.UpRequest) (*proto.UpResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
state := internal.CtxGetState(s.rootCtx)
// if current state contains any error, return it
// in all other cases we can continue execution only if status is idle and up command was
// not in the progress or already successfully established connection.
status, err := state.Status()
if err != nil {
return nil, err
}
if status != internal.StatusIdle {
return nil, fmt.Errorf("up already in progress: current status %s", status)
}
// it should be nill here, but .
if s.actCancel != nil {
s.actCancel()
}
ctx, cancel := context.WithCancel(s.rootCtx)
s.actCancel = cancel
if s.config == nil {
return nil, fmt.Errorf("config is not defined, please call login command first")
}
go func() {
if err := internal.RunClient(ctx, s.config); err != nil {
log.Errorf("run client connection: %v", state.Wrap(err))
return
}
}()
return &proto.UpResponse{}, nil
}
// Down dengine work in the daemon.
func (s *Server) Down(ctx context.Context, msg *proto.DownRequest) (*proto.DownResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.actCancel == nil {
return nil, fmt.Errorf("service is not up")
}
s.actCancel()
return &proto.DownResponse{}, nil
}
// Status starts engine work in the daemon.
func (s *Server) Status(
ctx context.Context,
msg *proto.StatusRequest,
) (*proto.StatusResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
status, err := internal.CtxGetState(s.rootCtx).Status()
if err != nil {
return nil, err
}
return &proto.StatusResponse{Status: string(status)}, nil
}
// GetConfig of the daemon.
func (s *Server) GetConfig(
ctx context.Context,
msg *proto.GetConfigRequest,
) (*proto.GetConfigResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
managementURL := s.managementURL
adminURL := s.adminURL
preSharedKey := ""
if s.config != nil {
if managementURL == "" && s.config.ManagementURL != nil {
managementURL = s.config.ManagementURL.String()
}
if s.config.AdminURL != nil {
adminURL = s.config.AdminURL.String()
}
preSharedKey = s.config.PreSharedKey
if preSharedKey != "" {
preSharedKey = "**********"
}
}
return &proto.GetConfigResponse{
ManagementUrl: managementURL,
AdminURL: adminURL,
ConfigFile: s.configPath,
LogFile: s.logFile,
PreSharedKey: preSharedKey,
}, nil
}