jenkins
This commit is contained in:
parent
dce95412dd
commit
d83720c8fd
|
|
@ -37,6 +37,29 @@ type SecurityConfig struct {
|
||||||
ForceNewUserChangePassword bool
|
ForceNewUserChangePassword bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDatabaseNotInitialized is returned by storage-backed settings that are
|
||||||
|
// intentionally unavailable during the first-install bootstrap phase.
|
||||||
|
var ErrDatabaseNotInitialized = errors.New("database not initialized")
|
||||||
|
|
||||||
|
// DefaultSecurityConfig matches the seed values used for a fresh installation.
|
||||||
|
// It is owned by biz so bootstrap callers do not need to depend on data.
|
||||||
|
func DefaultSecurityConfig() *SecurityConfig {
|
||||||
|
return &SecurityConfig{
|
||||||
|
ID: 1,
|
||||||
|
CaptchaTimeout: 3600,
|
||||||
|
KeyLong: 6,
|
||||||
|
ImgWidth: 240,
|
||||||
|
ImgHeight: 80,
|
||||||
|
PwdMinLength: 8,
|
||||||
|
LimitWindow: 60,
|
||||||
|
LimitCount: 30,
|
||||||
|
LockThreshold: 5,
|
||||||
|
LockDuration: 30,
|
||||||
|
PwdExpireDays: 90,
|
||||||
|
ForceNewUserChangePassword: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type SecurityRepo interface {
|
type SecurityRepo interface {
|
||||||
SecurityConfig(context.Context) (*SecurityConfig, error)
|
SecurityConfig(context.Context) (*SecurityConfig, error)
|
||||||
SaveSecurityConfig(context.Context, *SecurityConfig) error
|
SaveSecurityConfig(context.Context, *SecurityConfig) error
|
||||||
|
|
@ -99,8 +122,17 @@ func (uc *SecurityUsecase) Current(ctx context.Context) (*SecurityConfig, error)
|
||||||
uc.mu.RUnlock()
|
uc.mu.RUnlock()
|
||||||
value, err := uc.repo.SecurityConfig(ctx)
|
value, err := uc.repo.SecurityConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// The login page and captcha are also the entry point for first install.
|
||||||
|
// Do not cache this fallback: after initdb succeeds the next request must
|
||||||
|
// load the persisted row instead of serving bootstrap defaults forever.
|
||||||
|
if errors.Is(err, ErrDatabaseNotInitialized) {
|
||||||
|
return DefaultSecurityConfig(), nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if value == nil {
|
||||||
|
return nil, errors.New("security config is nil")
|
||||||
|
}
|
||||||
uc.mu.Lock()
|
uc.mu.Lock()
|
||||||
copy := *value
|
copy := *value
|
||||||
uc.cachedConfig = ©
|
uc.cachedConfig = ©
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,27 @@ type securityUpdateRepo struct {
|
||||||
persistedCopy *SecurityConfig
|
persistedCopy *SecurityConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bootstrapSecurityRepo struct {
|
||||||
|
initialized bool
|
||||||
|
calls int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *bootstrapSecurityRepo) SecurityConfig(context.Context) (*SecurityConfig, error) {
|
||||||
|
r.calls++
|
||||||
|
if !r.initialized {
|
||||||
|
return nil, ErrDatabaseNotInitialized
|
||||||
|
}
|
||||||
|
return &SecurityConfig{ID: 1, LimitCount: 42}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*bootstrapSecurityRepo) SaveSecurityConfig(context.Context, *SecurityConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*bootstrapSecurityRepo) BackfillPasswordUpdatedAt(context.Context, time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *securityUpdateRepo) SecurityConfig(context.Context) (*SecurityConfig, error) {
|
func (r *securityUpdateRepo) SecurityConfig(context.Context) (*SecurityConfig, error) {
|
||||||
r.callOrder = append(r.callOrder, "get")
|
r.callOrder = append(r.callOrder, "get")
|
||||||
copy := *r.current
|
copy := *r.current
|
||||||
|
|
@ -110,3 +131,31 @@ func TestSecurityReadsPersistedValueWhileCurrentUsesCache(t *testing.T) {
|
||||||
t.Fatalf("Security() returned stale value: %d", fresh.LimitCount)
|
t.Fatalf("Security() returned stale value: %d", fresh.LimitCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCurrentUsesBootstrapDefaultsWithoutCachingThem(t *testing.T) {
|
||||||
|
repo := &bootstrapSecurityRepo{}
|
||||||
|
uc := NewSecurityUsecase(repo, nil, nil, nil)
|
||||||
|
|
||||||
|
bootstrap, err := uc.Current(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Current() during bootstrap error = %v", err)
|
||||||
|
}
|
||||||
|
if bootstrap.ID != 1 || bootstrap.KeyLong != 6 || bootstrap.ImgWidth != 240 || bootstrap.ImgHeight != 80 || bootstrap.LimitEnable {
|
||||||
|
t.Fatalf("bootstrap config = %+v", bootstrap)
|
||||||
|
}
|
||||||
|
if repo.calls != 1 {
|
||||||
|
t.Fatalf("bootstrap repository calls = %d, want 1", repo.calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.initialized = true
|
||||||
|
active, err := uc.Current(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Current() after initialization error = %v", err)
|
||||||
|
}
|
||||||
|
if active.LimitCount != 42 {
|
||||||
|
t.Fatalf("active config = %+v, want persisted config", active)
|
||||||
|
}
|
||||||
|
if repo.calls != 2 {
|
||||||
|
t.Fatalf("repository calls = %d, want fallback to be uncached", repo.calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ type SecurityConfigPO struct {
|
||||||
func (SecurityConfigPO) TableName() string { return "sys_security_config" }
|
func (SecurityConfigPO) TableName() string { return "sys_security_config" }
|
||||||
|
|
||||||
func DefaultSecurityConfig() SecurityConfigPO {
|
func DefaultSecurityConfig() SecurityConfigPO {
|
||||||
return SecurityConfigPO{ID: 1, CaptchaTimeout: 3600, KeyLong: 6, ImgWidth: 240, ImgHeight: 80, PwdMinLength: 8, LimitWindow: 60, LimitCount: 30, LockThreshold: 5, LockDuration: 30, PwdExpireDays: 90}
|
return securityToPO(system.DefaultSecurityConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
type securityRepo struct{ data DatabaseProvider }
|
type securityRepo struct{ data DatabaseProvider }
|
||||||
|
|
@ -60,7 +60,7 @@ func securityToPO(v *system.SecurityConfig) SecurityConfigPO {
|
||||||
|
|
||||||
func (r *securityRepo) SecurityConfig(ctx context.Context) (*system.SecurityConfig, error) {
|
func (r *securityRepo) SecurityConfig(ctx context.Context) (*system.SecurityConfig, error) {
|
||||||
if !r.data.DatabaseReady() {
|
if !r.data.DatabaseReady() {
|
||||||
return nil, errors.New("数据库未初始化")
|
return nil, system.ErrDatabaseNotInitialized
|
||||||
}
|
}
|
||||||
db := r.data.DB().WithContext(ctx)
|
db := r.data.DB().WithContext(ctx)
|
||||||
var po SecurityConfigPO
|
var po SecurityConfigPO
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue