mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-14 17:28:56 +02:00
[client] Add support for disabling profiles feature via command line flag (#4235)
* Add support for disabling profiles feature via command line flag * Add profiles disabling flag to service command * Refactor profile menu initialization and enhance error notifications in event handlers
This commit is contained in:
@ -72,6 +72,7 @@ var (
|
||||
anonymizeFlag bool
|
||||
dnsRouteInterval time.Duration
|
||||
lazyConnEnabled bool
|
||||
profilesDisabled bool
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "netbird",
|
||||
|
@ -42,6 +42,7 @@ func init() {
|
||||
}
|
||||
|
||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd, svcStatusCmd, installCmd, uninstallCmd, reconfigureCmd)
|
||||
serviceCmd.PersistentFlags().BoolVar(&profilesDisabled, "disable-profiles", false, "Disables profiles feature. If enabled, the client will not be able to change or edit any profile.")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
|
||||
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
||||
|
@ -61,7 +61,7 @@ func (p *program) Start(svc service.Service) error {
|
||||
}
|
||||
}
|
||||
|
||||
serverInstance := server.New(p.ctx, util.FindFirstLogPath(logFiles))
|
||||
serverInstance := server.New(p.ctx, util.FindFirstLogPath(logFiles), profilesDisabled)
|
||||
if err := serverInstance.Start(); err != nil {
|
||||
log.Fatalf("failed to start daemon: %v", err)
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func startClientDaemon(
|
||||
s := grpc.NewServer()
|
||||
|
||||
server := client.New(ctx,
|
||||
"")
|
||||
"", false)
|
||||
if err := server.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ const (
|
||||
defaultRetryMultiplier = 1.7
|
||||
|
||||
errRestoreResidualState = "failed to restore residual state: %v"
|
||||
errProfilesDisabled = "profiles are disabled, you cannot use this feature without profiles enabled"
|
||||
)
|
||||
|
||||
// Server for service control.
|
||||
@ -69,6 +70,7 @@ type Server struct {
|
||||
isSessionActive atomic.Bool
|
||||
|
||||
profileManager profilemanager.ServiceManager
|
||||
profilesDisabled bool
|
||||
}
|
||||
|
||||
type oauthAuthFlow struct {
|
||||
@ -79,13 +81,14 @@ type oauthAuthFlow struct {
|
||||
}
|
||||
|
||||
// New server instance constructor.
|
||||
func New(ctx context.Context, logFile string) *Server {
|
||||
func New(ctx context.Context, logFile string, profilesDisabled bool) *Server {
|
||||
return &Server{
|
||||
rootCtx: ctx,
|
||||
logFile: logFile,
|
||||
persistNetworkMap: true,
|
||||
statusRecorder: peer.NewRecorder(""),
|
||||
profileManager: profilemanager.ServiceManager{},
|
||||
profilesDisabled: profilesDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,6 +323,10 @@ func (s *Server) SetConfig(callerCtx context.Context, msg *proto.SetConfigReques
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.checkProfilesDisabled() {
|
||||
return nil, gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
|
||||
}
|
||||
|
||||
profState := profilemanager.ActiveProfileState{
|
||||
Name: msg.ProfileName,
|
||||
Username: msg.Username,
|
||||
@ -737,6 +744,11 @@ func (s *Server) switchProfileIfNeeded(profileName string, userName *string, act
|
||||
}
|
||||
|
||||
if profileName != activeProf.Name || username != activeProf.Username {
|
||||
if s.checkProfilesDisabled() {
|
||||
log.Errorf("profiles are disabled, you cannot use this feature without profiles enabled")
|
||||
return gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
|
||||
}
|
||||
|
||||
log.Infof("switching to profile %s for user %s", profileName, username)
|
||||
if err := s.profileManager.SetActiveProfileState(&profilemanager.ActiveProfileState{
|
||||
Name: profileName,
|
||||
@ -1069,6 +1081,10 @@ func (s *Server) AddProfile(ctx context.Context, msg *proto.AddProfileRequest) (
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.checkProfilesDisabled() {
|
||||
return nil, gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
|
||||
}
|
||||
|
||||
if msg.ProfileName == "" || msg.Username == "" {
|
||||
return nil, gstatus.Errorf(codes.InvalidArgument, "profile name and username must be provided")
|
||||
}
|
||||
@ -1086,6 +1102,10 @@ func (s *Server) RemoveProfile(ctx context.Context, msg *proto.RemoveProfileRequ
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.checkProfilesDisabled() {
|
||||
return nil, gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
|
||||
}
|
||||
|
||||
if msg.ProfileName == "" {
|
||||
return nil, gstatus.Errorf(codes.InvalidArgument, "profile name must be provided")
|
||||
}
|
||||
@ -1142,3 +1162,13 @@ func (s *Server) GetActiveProfile(ctx context.Context, msg *proto.GetActiveProfi
|
||||
Username: activeProfile.Username,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) checkProfilesDisabled() bool {
|
||||
// Check if the environment variable is set to disable profiles
|
||||
if s.profilesDisabled {
|
||||
log.Warn("Profiles are disabled via NB_DISABLE_PROFILES environment variable")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func TestConnectWithRetryRuns(t *testing.T) {
|
||||
t.Fatalf("failed to set active profile state: %v", err)
|
||||
}
|
||||
|
||||
s := New(ctx, "debug")
|
||||
s := New(ctx, "debug", false)
|
||||
|
||||
s.config = config
|
||||
|
||||
@ -151,7 +151,7 @@ func TestServer_Up(t *testing.T) {
|
||||
t.Fatalf("failed to set active profile state: %v", err)
|
||||
}
|
||||
|
||||
s := New(ctx, "console")
|
||||
s := New(ctx, "console", false)
|
||||
|
||||
err = s.Start()
|
||||
require.NoError(t, err)
|
||||
@ -227,7 +227,7 @@ func TestServer_SubcribeEvents(t *testing.T) {
|
||||
t.Fatalf("failed to set active profile state: %v", err)
|
||||
}
|
||||
|
||||
s := New(ctx, "console")
|
||||
s := New(ctx, "console", false)
|
||||
|
||||
err = s.Start()
|
||||
require.NoError(t, err)
|
||||
|
@ -802,7 +802,21 @@ func (s *serviceClient) onTrayReady() {
|
||||
|
||||
profileMenuItem := systray.AddMenuItem("", "")
|
||||
emailMenuItem := systray.AddMenuItem("", "")
|
||||
s.mProfile = newProfileMenu(s.ctx, s.profileManager, *s.eventHandler, profileMenuItem, emailMenuItem, s.menuDownClick, s.menuUpClick, s.getSrvClient, s.loadSettings)
|
||||
|
||||
newProfileMenuArgs := &newProfileMenuArgs{
|
||||
ctx: s.ctx,
|
||||
profileManager: s.profileManager,
|
||||
eventHandler: s.eventHandler,
|
||||
profileMenuItem: profileMenuItem,
|
||||
emailMenuItem: emailMenuItem,
|
||||
downClickCallback: s.menuDownClick,
|
||||
upClickCallback: s.menuUpClick,
|
||||
getSrvClientCallback: s.getSrvClient,
|
||||
loadSettingsCallback: s.loadSettings,
|
||||
app: s.app,
|
||||
}
|
||||
|
||||
s.mProfile = newProfileMenu(*newProfileMenuArgs)
|
||||
|
||||
systray.AddSeparator()
|
||||
s.mUp = systray.AddMenuItem("Connect", "Connect")
|
||||
|
@ -86,35 +86,60 @@ func (h *eventHandler) handleDisconnectClick() {
|
||||
|
||||
func (h *eventHandler) handleAllowSSHClick() {
|
||||
h.toggleCheckbox(h.client.mAllowSSH)
|
||||
h.updateConfigWithErr()
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mAllowSSH) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update SSH settings"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleAutoConnectClick() {
|
||||
h.toggleCheckbox(h.client.mAutoConnect)
|
||||
h.updateConfigWithErr()
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mAutoConnect) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update auto-connect settings"))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleRosenpassClick() {
|
||||
h.toggleCheckbox(h.client.mEnableRosenpass)
|
||||
h.updateConfigWithErr()
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mEnableRosenpass) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update Rosenpass settings"))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleLazyConnectionClick() {
|
||||
h.toggleCheckbox(h.client.mLazyConnEnabled)
|
||||
h.updateConfigWithErr()
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mLazyConnEnabled) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update lazy connection settings"))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleBlockInboundClick() {
|
||||
h.toggleCheckbox(h.client.mBlockInbound)
|
||||
h.updateConfigWithErr()
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mBlockInbound) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update block inbound settings"))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleNotificationsClick() {
|
||||
h.toggleCheckbox(h.client.mNotifications)
|
||||
if h.client.eventManager != nil {
|
||||
if err := h.updateConfigWithErr(); err != nil {
|
||||
h.toggleCheckbox(h.client.mNotifications) // revert checkbox state on error
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to update notifications settings"))
|
||||
} else if h.client.eventManager != nil {
|
||||
h.client.eventManager.SetNotificationsEnabled(h.client.mNotifications.Checked())
|
||||
}
|
||||
h.updateConfigWithErr()
|
||||
|
||||
}
|
||||
|
||||
func (h *eventHandler) handleAdvancedSettingsClick() {
|
||||
@ -166,10 +191,12 @@ func (h *eventHandler) toggleCheckbox(item *systray.MenuItem) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *eventHandler) updateConfigWithErr() {
|
||||
func (h *eventHandler) updateConfigWithErr() error {
|
||||
if err := h.client.updateConfig(); err != nil {
|
||||
log.Errorf("failed to update config: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *eventHandler) runSelfCommand(ctx context.Context, command, arg string) {
|
||||
|
@ -334,7 +334,7 @@ type profileMenu struct {
|
||||
mu sync.Mutex
|
||||
ctx context.Context
|
||||
profileManager *profilemanager.ProfileManager
|
||||
eventHandler eventHandler
|
||||
eventHandler *eventHandler
|
||||
profileMenuItem *systray.MenuItem
|
||||
emailMenuItem *systray.MenuItem
|
||||
profileSubItems []*subItem
|
||||
@ -344,24 +344,34 @@ type profileMenu struct {
|
||||
upClickCallback func() error
|
||||
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
||||
loadSettingsCallback func()
|
||||
app fyne.App
|
||||
}
|
||||
|
||||
func newProfileMenu(ctx context.Context, profileManager *profilemanager.ProfileManager,
|
||||
type newProfileMenuArgs struct {
|
||||
ctx context.Context
|
||||
profileManager *profilemanager.ProfileManager
|
||||
eventHandler *eventHandler
|
||||
profileMenuItem *systray.MenuItem
|
||||
emailMenuItem *systray.MenuItem
|
||||
downClickCallback func() error
|
||||
upClickCallback func() error
|
||||
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
||||
loadSettingsCallback func()
|
||||
app fyne.App
|
||||
}
|
||||
|
||||
eventHandler eventHandler, profileMenuItem, emailMenuItem *systray.MenuItem,
|
||||
downClickCallback, upClickCallback func() error,
|
||||
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error),
|
||||
loadSettingsCallback func()) *profileMenu {
|
||||
func newProfileMenu(args newProfileMenuArgs) *profileMenu {
|
||||
p := profileMenu{
|
||||
ctx: ctx,
|
||||
profileManager: profileManager,
|
||||
eventHandler: eventHandler,
|
||||
profileMenuItem: profileMenuItem,
|
||||
emailMenuItem: emailMenuItem,
|
||||
downClickCallback: downClickCallback,
|
||||
upClickCallback: upClickCallback,
|
||||
getSrvClientCallback: getSrvClientCallback,
|
||||
loadSettingsCallback: loadSettingsCallback,
|
||||
ctx: args.ctx,
|
||||
profileManager: args.profileManager,
|
||||
eventHandler: args.eventHandler,
|
||||
profileMenuItem: args.profileMenuItem,
|
||||
emailMenuItem: args.emailMenuItem,
|
||||
downClickCallback: args.downClickCallback,
|
||||
upClickCallback: args.upClickCallback,
|
||||
getSrvClientCallback: args.getSrvClientCallback,
|
||||
loadSettingsCallback: args.loadSettingsCallback,
|
||||
app: args.app,
|
||||
}
|
||||
|
||||
p.emailMenuItem.Disable()
|
||||
@ -479,6 +489,8 @@ func (p *profileMenu) refresh() {
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to switch profile: %v", err)
|
||||
// show notification dialog
|
||||
p.app.SendNotification(fyne.NewNotification("Error", "Failed to switch profile"))
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user