mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 15:26:36 +08:00
refactor(auth): modularize auth plugin with physical subpackages and decoupled services
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package do provides domain data objects for the auth plugin.
|
||||
package do
|
||||
|
||||
// CachedToken represents the minimal cached representation of an access token.
|
||||
type CachedToken struct {
|
||||
ID uint64 `json:"id"`
|
||||
UserID uint64 `json:"user_id"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package do provides domain data objects for the auth plugin.
|
||||
package do
|
||||
|
||||
import (
|
||||
"Wavelet/plugins/domain/auth/consts"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CapConfigRecord maps the columns selected from the system config table.
|
||||
type CapConfigRecord struct {
|
||||
Key string `gorm:"column:key"`
|
||||
Value string `gorm:"column:value"`
|
||||
}
|
||||
|
||||
// ParseCapRuntimeSettings parses system config key-value map into CapRuntimeSettings with fallback defaults.
|
||||
func ParseCapRuntimeSettings(configs map[string]string) CapRuntimeSettings {
|
||||
settings := CapRuntimeSettings{
|
||||
ChallengeCount: consts.DefaultCapChallengeCount,
|
||||
ChallengeSize: consts.DefaultCapChallengeSize,
|
||||
ChallengeDifficulty: consts.DefaultCapChallengeDifficulty,
|
||||
ChallengeTTL: consts.DefaultCapChallengeTTL,
|
||||
TokenTTL: consts.DefaultCapTokenTTL,
|
||||
}
|
||||
|
||||
if len(configs) == 0 {
|
||||
return settings
|
||||
}
|
||||
|
||||
if val, ok := configs[consts.ConfigKeyCapLoginEnabled]; ok {
|
||||
if enabled, err := strconv.ParseBool(val); err == nil {
|
||||
settings.LoginEnabled = enabled
|
||||
}
|
||||
}
|
||||
if val, ok := configs[consts.ConfigKeyCapChallengeCount]; ok {
|
||||
if count, err := strconv.Atoi(val); err == nil && count > 0 {
|
||||
settings.ChallengeCount = count
|
||||
}
|
||||
}
|
||||
if val, ok := configs[consts.ConfigKeyCapChallengeSize]; ok {
|
||||
if size, err := strconv.Atoi(val); err == nil && size > 0 {
|
||||
settings.ChallengeSize = size
|
||||
}
|
||||
}
|
||||
if val, ok := configs[consts.ConfigKeyCapChallengeDifficulty]; ok {
|
||||
if diff, err := strconv.Atoi(val); err == nil && diff > 0 {
|
||||
settings.ChallengeDifficulty = diff
|
||||
}
|
||||
}
|
||||
if val, ok := configs[consts.ConfigKeyCapChallengeTTL]; ok {
|
||||
if ttlSeconds, err := strconv.Atoi(val); err == nil && ttlSeconds > 0 {
|
||||
settings.ChallengeTTL = time.Duration(ttlSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
if val, ok := configs[consts.ConfigKeyCapTokenTTL]; ok {
|
||||
if ttlSeconds, err := strconv.Atoi(val); err == nil && ttlSeconds > 0 {
|
||||
settings.TokenTTL = time.Duration(ttlSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package do_test
|
||||
|
||||
import (
|
||||
"Wavelet/plugins/domain/auth/consts"
|
||||
"Wavelet/plugins/domain/auth/model/do"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseCapRuntimeSettings(t *testing.T) {
|
||||
t.Run("Default fallback on empty config", func(t *testing.T) {
|
||||
settings := do.ParseCapRuntimeSettings(nil)
|
||||
assert.False(t, settings.LoginEnabled)
|
||||
assert.Equal(t, consts.DefaultCapChallengeCount, settings.ChallengeCount)
|
||||
assert.Equal(t, consts.DefaultCapChallengeSize, settings.ChallengeSize)
|
||||
assert.Equal(t, consts.DefaultCapChallengeDifficulty, settings.ChallengeDifficulty)
|
||||
assert.Equal(t, consts.DefaultCapChallengeTTL, settings.ChallengeTTL)
|
||||
assert.Equal(t, consts.DefaultCapTokenTTL, settings.TokenTTL)
|
||||
})
|
||||
|
||||
t.Run("Parsed custom configs", func(t *testing.T) {
|
||||
configs := map[string]string{
|
||||
consts.ConfigKeyCapLoginEnabled: "true",
|
||||
consts.ConfigKeyCapChallengeCount: "3",
|
||||
consts.ConfigKeyCapChallengeSize: "64",
|
||||
consts.ConfigKeyCapChallengeDifficulty: "5",
|
||||
consts.ConfigKeyCapChallengeTTL: "300",
|
||||
consts.ConfigKeyCapTokenTTL: "600",
|
||||
}
|
||||
settings := do.ParseCapRuntimeSettings(configs)
|
||||
assert.True(t, settings.LoginEnabled)
|
||||
assert.Equal(t, 3, settings.ChallengeCount)
|
||||
assert.Equal(t, 64, settings.ChallengeSize)
|
||||
assert.Equal(t, 5, settings.ChallengeDifficulty)
|
||||
assert.Equal(t, 300*time.Second, settings.ChallengeTTL)
|
||||
assert.Equal(t, 600*time.Second, settings.TokenTTL)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package do provides domain data objects for the auth plugin.
|
||||
package do
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// OAuthStatePayload represents the cached state verification payload for OAuth flow.
|
||||
type OAuthStatePayload struct {
|
||||
SourceName string `json:"source_name"`
|
||||
Purpose string `json:"purpose"`
|
||||
UserID uint64 `json:"user_id,omitempty"`
|
||||
SessionHash string `json:"session_hash"`
|
||||
}
|
||||
|
||||
// Encode converts OAuthStatePayload to a JSON string.
|
||||
func (p OAuthStatePayload) Encode() (string, error) {
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// DecodeOAuthStatePayload parses a JSON string into OAuthStatePayload.
|
||||
func DecodeOAuthStatePayload(value string) (OAuthStatePayload, error) {
|
||||
var payload OAuthStatePayload
|
||||
if err := json.Unmarshal([]byte(value), &payload); err != nil {
|
||||
return OAuthStatePayload{}, err
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package do_test
|
||||
|
||||
import (
|
||||
"Wavelet/plugins/domain/auth/model/do"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOAuthStatePayload(t *testing.T) {
|
||||
payload := do.OAuthStatePayload{
|
||||
SourceName: "github",
|
||||
Purpose: "login",
|
||||
UserID: 12345,
|
||||
SessionHash: "hash-abc-123",
|
||||
}
|
||||
|
||||
encoded, err := payload.Encode()
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, encoded)
|
||||
|
||||
decoded, err := do.DecodeOAuthStatePayload(encoded)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, payload, decoded)
|
||||
|
||||
_, err = do.DecodeOAuthStatePayload("invalid-json")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user