mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 16:46:37 +08:00
refactor(auth): merge cap domain plugin into auth
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCapChallengeCount = 1
|
||||
defaultCapChallengeSize = 32
|
||||
defaultCapChallengeDifficulty = 4
|
||||
defaultCapChallengeTTL = 10 * time.Minute
|
||||
defaultCapTokenTTL = 20 * time.Minute
|
||||
)
|
||||
|
||||
// CapRuntimeSettings is the parsed CAPTCHA runtime configuration loaded from system_configs.
|
||||
type CapRuntimeSettings struct {
|
||||
LoginEnabled bool
|
||||
ChallengeCount int
|
||||
ChallengeSize int
|
||||
ChallengeDifficulty int
|
||||
ChallengeTTL time.Duration
|
||||
TokenTTL time.Duration
|
||||
}
|
||||
|
||||
// CAP 动态配置键常量
|
||||
const (
|
||||
ConfigKeyCapLoginEnabled = "cap_login_enabled"
|
||||
ConfigKeyCapChallengeCount = "cap_challenge_count"
|
||||
ConfigKeyCapChallengeSize = "cap_challenge_size"
|
||||
ConfigKeyCapChallengeDifficulty = "cap_challenge_difficulty"
|
||||
ConfigKeyCapChallengeTTL = "cap_challenge_ttl"
|
||||
// ConfigKeyCapTokenTTL 验证码 Token 过期时间键
|
||||
// #nosec G101
|
||||
ConfigKeyCapTokenTTL = "cap_token_ttl"
|
||||
)
|
||||
|
||||
var capRuntimeConfigKeys = []string{
|
||||
ConfigKeyCapLoginEnabled,
|
||||
ConfigKeyCapChallengeCount,
|
||||
ConfigKeyCapChallengeSize,
|
||||
ConfigKeyCapChallengeDifficulty,
|
||||
ConfigKeyCapChallengeTTL,
|
||||
ConfigKeyCapTokenTTL,
|
||||
}
|
||||
|
||||
var capRuntimeConfigKeySet = func() map[string]struct{} {
|
||||
set := make(map[string]struct{}, len(capRuntimeConfigKeys))
|
||||
for _, key := range capRuntimeConfigKeys {
|
||||
set[key] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}()
|
||||
|
||||
type capRuntimeSettingsStore struct {
|
||||
snapshot atomic.Pointer[CapRuntimeSettings]
|
||||
loadGroup singleflight.Group
|
||||
}
|
||||
|
||||
var capSettingsStore = &capRuntimeSettingsStore{}
|
||||
|
||||
// IsCapRuntimeConfigKey reports whether a system config key affects CAPTCHA runtime settings.
|
||||
func IsCapRuntimeConfigKey(key string) bool {
|
||||
_, ok := capRuntimeConfigKeySet[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// CurrentCapSettings returns the cached CAPTCHA runtime settings snapshot.
|
||||
func CurrentCapSettings(ctx context.Context) (CapRuntimeSettings, error) {
|
||||
return capSettingsStore.current(ctx)
|
||||
}
|
||||
|
||||
// CapProtectionEnabled reports whether CAPTCHA verification is required for protected routes.
|
||||
func CapProtectionEnabled(ctx context.Context) bool {
|
||||
settings, err := CurrentCapSettings(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return settings.LoginEnabled
|
||||
}
|
||||
|
||||
// InvalidateCapRuntimeSettings drops the in-process CAPTCHA settings snapshot.
|
||||
func InvalidateCapRuntimeSettings() {
|
||||
capSettingsStore.snapshot.Store(nil)
|
||||
}
|
||||
|
||||
// ResetCapRuntimeSettingsForTest clears the CAPTCHA runtime snapshot.
|
||||
func ResetCapRuntimeSettingsForTest() {
|
||||
InvalidateCapRuntimeSettings()
|
||||
}
|
||||
|
||||
// InstallCapTestRuntimeSettings installs a fixed snapshot for unit tests.
|
||||
func InstallCapTestRuntimeSettings(settings CapRuntimeSettings) func() {
|
||||
snapshot := settings
|
||||
capSettingsStore.snapshot.Store(&snapshot)
|
||||
return InvalidateCapRuntimeSettings
|
||||
}
|
||||
|
||||
func (s *capRuntimeSettingsStore) current(ctx context.Context) (CapRuntimeSettings, error) {
|
||||
if snapshot := s.snapshot.Load(); snapshot != nil {
|
||||
return *snapshot, nil
|
||||
}
|
||||
|
||||
loaded, err, _ := s.loadGroup.Do("cap-runtime-settings", func() (any, error) {
|
||||
if snapshot := s.snapshot.Load(); snapshot != nil {
|
||||
return *snapshot, nil
|
||||
}
|
||||
|
||||
settings, loadErr := loadCapRuntimeSettings(ctx)
|
||||
if loadErr != nil {
|
||||
return CapRuntimeSettings{}, loadErr
|
||||
}
|
||||
|
||||
s.snapshot.Store(&settings)
|
||||
return settings, nil
|
||||
})
|
||||
if err != nil {
|
||||
return CapRuntimeSettings{}, err
|
||||
}
|
||||
|
||||
settings, ok := loaded.(CapRuntimeSettings)
|
||||
if !ok {
|
||||
return CapRuntimeSettings{}, errors.New("cap runtime settings loader returned unexpected type")
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func loadCapRuntimeSettings(ctx context.Context) (CapRuntimeSettings, error) {
|
||||
var records []capConfigRecord
|
||||
db := getDB(ctx)
|
||||
if db == nil {
|
||||
return parseCapRuntimeSettings(nil), nil
|
||||
}
|
||||
if err := db.Table("w_system_configs").Where("key IN ?", capRuntimeConfigKeys).Find(&records).Error; err != nil {
|
||||
return CapRuntimeSettings{}, err
|
||||
}
|
||||
configs := make(map[string]string, len(records))
|
||||
for _, r := range records {
|
||||
configs[r.Key] = r.Value
|
||||
}
|
||||
return parseCapRuntimeSettings(configs), nil
|
||||
}
|
||||
|
||||
func parseCapRuntimeSettings(configs map[string]string) CapRuntimeSettings {
|
||||
settings := CapRuntimeSettings{
|
||||
ChallengeCount: defaultCapChallengeCount,
|
||||
ChallengeSize: defaultCapChallengeSize,
|
||||
ChallengeDifficulty: defaultCapChallengeDifficulty,
|
||||
ChallengeTTL: defaultCapChallengeTTL,
|
||||
TokenTTL: defaultCapTokenTTL,
|
||||
}
|
||||
|
||||
if len(configs) == 0 {
|
||||
return settings
|
||||
}
|
||||
|
||||
if val, ok := configs[ConfigKeyCapLoginEnabled]; ok {
|
||||
if enabled, err := strconv.ParseBool(val); err == nil {
|
||||
settings.LoginEnabled = enabled
|
||||
}
|
||||
}
|
||||
if val, ok := configs[ConfigKeyCapChallengeCount]; ok {
|
||||
if count, err := strconv.Atoi(val); err == nil && count > 0 {
|
||||
settings.ChallengeCount = count
|
||||
}
|
||||
}
|
||||
if val, ok := configs[ConfigKeyCapChallengeSize]; ok {
|
||||
if size, err := strconv.Atoi(val); err == nil && size > 0 {
|
||||
settings.ChallengeSize = size
|
||||
}
|
||||
}
|
||||
if val, ok := configs[ConfigKeyCapChallengeDifficulty]; ok {
|
||||
if diff, err := strconv.Atoi(val); err == nil && diff > 0 {
|
||||
settings.ChallengeDifficulty = diff
|
||||
}
|
||||
}
|
||||
if val, ok := configs[ConfigKeyCapChallengeTTL]; ok {
|
||||
if ttlSeconds, err := strconv.Atoi(val); err == nil && ttlSeconds > 0 {
|
||||
settings.ChallengeTTL = time.Duration(ttlSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
if val, ok := configs[ConfigKeyCapTokenTTL]; ok {
|
||||
if ttlSeconds, err := strconv.Atoi(val); err == nil && ttlSeconds > 0 {
|
||||
settings.TokenTTL = time.Duration(ttlSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
Reference in New Issue
Block a user