mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-30 22:26:42 +00:00
remove logic from handler towards setup service
This commit is contained in:
@@ -3,20 +3,40 @@ package instance
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/shared/auth"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
)
|
||||
|
||||
const setupPATTokenName = "setup-token"
|
||||
const (
|
||||
setupPATTokenName = "setup-token"
|
||||
|
||||
// SetupPATEnabledEnvKey enables setup-time Personal Access Token creation.
|
||||
SetupPATEnabledEnvKey = "NB_SETUP_PAT_ENABLED"
|
||||
|
||||
setupPATDefaultExpireDays = 1
|
||||
|
||||
// patMinExpireDays and patMaxExpireDays mirror the bounds enforced by
|
||||
// DefaultAccountManager.CreatePAT in management/server/user.go. They are
|
||||
// duplicated here so /api/setup can reject invalid input before it creates
|
||||
// the embedded-IdP user.
|
||||
patMinExpireDays = 1
|
||||
patMaxExpireDays = 365
|
||||
)
|
||||
|
||||
// SetupOptions controls optional work performed during initial instance setup.
|
||||
type SetupOptions struct {
|
||||
CreatePAT bool
|
||||
PATExpireInDays int
|
||||
// CreatePAT requests creation of a setup Personal Access Token. It is honored
|
||||
// only when SetupPATEnabledEnvKey is set to "true".
|
||||
CreatePAT bool
|
||||
// PATExpireInDays defaults to 1 day when CreatePAT is requested and setup PAT
|
||||
// creation is enabled.
|
||||
PATExpireInDays *int
|
||||
}
|
||||
|
||||
// SetupResult contains resources created during initial instance setup.
|
||||
@@ -31,6 +51,7 @@ type SetupResult struct {
|
||||
type SetupService struct {
|
||||
instanceManager Manager
|
||||
accountManager account.Manager
|
||||
setupPATEnabled bool
|
||||
}
|
||||
|
||||
// NewSetupService creates a setup use-case service.
|
||||
@@ -38,13 +59,43 @@ func NewSetupService(instanceManager Manager, accountManager account.Manager) *S
|
||||
return &SetupService{
|
||||
instanceManager: instanceManager,
|
||||
accountManager: accountManager,
|
||||
setupPATEnabled: os.Getenv(SetupPATEnabledEnvKey) == "true",
|
||||
}
|
||||
}
|
||||
|
||||
// SetupOwner creates the initial owner user and, optionally, provisions the
|
||||
// account and a setup Personal Access Token. If account or PAT provisioning
|
||||
// fails, created resources are rolled back so setup can be retried.
|
||||
func normalizeSetupOptions(opts SetupOptions, setupPATEnabled bool) (SetupOptions, error) {
|
||||
if !opts.CreatePAT {
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
if !setupPATEnabled {
|
||||
opts.CreatePAT = false
|
||||
opts.PATExpireInDays = nil
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
if opts.PATExpireInDays == nil {
|
||||
defaultExpireInDays := setupPATDefaultExpireDays
|
||||
opts.PATExpireInDays = &defaultExpireInDays
|
||||
}
|
||||
|
||||
if *opts.PATExpireInDays < patMinExpireDays || *opts.PATExpireInDays > patMaxExpireDays {
|
||||
return opts, status.Errorf(status.InvalidArgument, "pat_expire_in must be between %d and %d", patMinExpireDays, patMaxExpireDays)
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// SetupOwner creates the initial owner user and, when requested and enabled by
|
||||
// SetupPATEnabledEnvKey, provisions the account and a setup Personal Access
|
||||
// Token. If account or PAT provisioning fails, created resources are rolled
|
||||
// back so setup can be retried.
|
||||
func (m *SetupService) SetupOwner(ctx context.Context, email, password, name string, opts SetupOptions) (*SetupResult, error) {
|
||||
opts, err := normalizeSetupOptions(opts, m.setupPATEnabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userData, err := m.instanceManager.CreateOwnerUser(ctx, email, password, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -74,7 +125,7 @@ func (m *SetupService) SetupOwner(ctx context.Context, email, password, name str
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pat, err := m.accountManager.CreatePAT(ctx, accountID, userData.ID, userData.ID, setupPATTokenName, opts.PATExpireInDays)
|
||||
pat, err := m.accountManager.CreatePAT(ctx, accountID, userData.ID, userData.ID, setupPATTokenName, *opts.PATExpireInDays)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("create setup PAT: %w", err)
|
||||
m.rollbackSetup(ctx, userData.ID, "setup PAT provisioning failed", err, accountID)
|
||||
|
||||
@@ -46,7 +46,75 @@ func (m *setupInstanceManagerMock) GetVersionInfo(context.Context) (*VersionInfo
|
||||
|
||||
var _ Manager = (*setupInstanceManagerMock)(nil)
|
||||
|
||||
func intPtr(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
func TestSetupOwner_PATFeatureDisabled_IgnoresCreatePAT(t *testing.T) {
|
||||
t.Setenv(SetupPATEnabledEnvKey, "false")
|
||||
|
||||
createCalls := 0
|
||||
setupManager := NewSetupService(
|
||||
&setupInstanceManagerMock{
|
||||
createOwnerUserFn: func(_ context.Context, email, _, name string) (*idp.UserData, error) {
|
||||
createCalls++
|
||||
return &idp.UserData{ID: "owner-id", Email: email, Name: name}, nil
|
||||
},
|
||||
},
|
||||
&mock_server.MockAccountManager{},
|
||||
)
|
||||
|
||||
result, err := setupManager.SetupOwner(context.Background(), "admin@example.com", "securepassword123", "Admin", SetupOptions{
|
||||
CreatePAT: true,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
assert.Equal(t, "owner-id", result.User.ID)
|
||||
assert.Empty(t, result.PATPlainToken)
|
||||
assert.Equal(t, 1, createCalls)
|
||||
}
|
||||
|
||||
func TestSetupOwner_PATFeatureEnabled_MissingExpireDefaultsToOneDay(t *testing.T) {
|
||||
t.Setenv(SetupPATEnabledEnvKey, "true")
|
||||
|
||||
createCalled := false
|
||||
setupManager := NewSetupService(
|
||||
&setupInstanceManagerMock{
|
||||
createOwnerUserFn: func(_ context.Context, email, _, name string) (*idp.UserData, error) {
|
||||
createCalled = true
|
||||
return &idp.UserData{ID: "owner-id", Email: email, Name: name}, nil
|
||||
},
|
||||
},
|
||||
&mock_server.MockAccountManager{
|
||||
GetAccountIDByUserIdFunc: func(_ context.Context, userAuth auth.UserAuth) (string, error) {
|
||||
assert.Equal(t, "owner-id", userAuth.UserId)
|
||||
return "acc-1", nil
|
||||
},
|
||||
CreatePATFunc: func(_ context.Context, accountID, initiatorUserID, targetUserID, tokenName string, expiresIn int) (*types.PersonalAccessTokenGenerated, error) {
|
||||
assert.Equal(t, "acc-1", accountID)
|
||||
assert.Equal(t, "owner-id", initiatorUserID)
|
||||
assert.Equal(t, "owner-id", targetUserID)
|
||||
assert.Equal(t, setupPATTokenName, tokenName)
|
||||
assert.Equal(t, setupPATDefaultExpireDays, expiresIn)
|
||||
return &types.PersonalAccessTokenGenerated{PlainToken: "nbp_plain"}, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
result, err := setupManager.SetupOwner(context.Background(), "admin@example.com", "securepassword123", "Admin", SetupOptions{
|
||||
CreatePAT: true,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
assert.True(t, createCalled)
|
||||
assert.Equal(t, "nbp_plain", result.PATPlainToken)
|
||||
}
|
||||
|
||||
func TestSetupOwner_CreatePATFails_RollsBackSetupAccountAndUser(t *testing.T) {
|
||||
t.Setenv(SetupPATEnabledEnvKey, "true")
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
accountStore := nbstore.NewMockStore(ctrl)
|
||||
account := &types.Account{Id: "acc-1"}
|
||||
@@ -83,7 +151,7 @@ func TestSetupOwner_CreatePATFails_RollsBackSetupAccountAndUser(t *testing.T) {
|
||||
|
||||
result, err := setupManager.SetupOwner(context.Background(), "admin@example.com", "securepassword123", "Admin", SetupOptions{
|
||||
CreatePAT: true,
|
||||
PATExpireInDays: 30,
|
||||
PATExpireInDays: intPtr(30),
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
@@ -93,6 +161,8 @@ func TestSetupOwner_CreatePATFails_RollsBackSetupAccountAndUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetupOwner_CreatePATFails_AccountAlreadyGoneStillRollsBackUser(t *testing.T) {
|
||||
t.Setenv(SetupPATEnabledEnvKey, "true")
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
accountStore := nbstore.NewMockStore(ctrl)
|
||||
accountStore.EXPECT().GetAccount(gomock.Any(), "acc-1").Return(nil, status.NewAccountNotFoundError("acc-1"))
|
||||
@@ -120,7 +190,7 @@ func TestSetupOwner_CreatePATFails_AccountAlreadyGoneStillRollsBackUser(t *testi
|
||||
|
||||
result, err := setupManager.SetupOwner(context.Background(), "admin@example.com", "securepassword123", "Admin", SetupOptions{
|
||||
CreatePAT: true,
|
||||
PATExpireInDays: 30,
|
||||
PATExpireInDays: intPtr(30),
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
@@ -130,6 +200,8 @@ func TestSetupOwner_CreatePATFails_AccountAlreadyGoneStillRollsBackUser(t *testi
|
||||
}
|
||||
|
||||
func TestSetupOwner_CreatePATFails_AccountRollbackFailureStillRollsBackUser(t *testing.T) {
|
||||
t.Setenv(SetupPATEnabledEnvKey, "true")
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
accountStore := nbstore.NewMockStore(ctrl)
|
||||
account := &types.Account{Id: "acc-1"}
|
||||
@@ -159,7 +231,7 @@ func TestSetupOwner_CreatePATFails_AccountRollbackFailureStillRollsBackUser(t *t
|
||||
|
||||
result, err := setupManager.SetupOwner(context.Background(), "admin@example.com", "securepassword123", "Admin", SetupOptions{
|
||||
CreatePAT: true,
|
||||
PATExpireInDays: 30,
|
||||
PATExpireInDays: intPtr(30),
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
|
||||
Reference in New Issue
Block a user