Files
new-api/model/subscription_auth_test.go
Calcium-Ion 31d70fca39 refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
2026-07-20 16:48:43 +08:00

151 lines
4.8 KiB
Go

package model
import (
"context"
"errors"
"fmt"
"net"
"strings"
"testing"
"time"
"github.com/QuantumNous/new-api/common"
"github.com/glebarez/sqlite"
"github.com/go-redis/redis/v8"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func TestSubscriptionGroupTransitionsPreserveAuthVersionAndSessions(t *testing.T) {
truncateTables(t)
useUserCacheMiniRedis(t)
now := time.Now().Unix()
user := User{
Username: "subscription-auth-user",
Password: "unused-password-hash",
Role: common.RoleCommonUser,
Status: common.UserStatusEnabled,
Group: "default",
AuthVersion: 1,
}
require.NoError(t, DB.Create(&user).Error)
require.NoError(t, CreateUserSession(&UserSession{
SID: "subscription-auth-session",
UserID: user.Id,
Version: 1,
UserAuthVersion: 1,
Status: UserSessionStatusActive,
RefreshHash: "refresh-hash",
LoginMethod: "password",
LastActiveAt: now,
ExpiresAt: now + 3600,
}))
require.NoError(t, populateUserCache(user))
plan := &SubscriptionPlan{
Title: "Upgraded",
DurationUnit: SubscriptionDurationMonth,
DurationValue: 1,
TotalAmount: 100,
UpgradeGroup: "pro",
Enabled: true,
}
require.NoError(t, DB.Create(plan).Error)
subscription, err := CreateUserSubscriptionFromPlanTx(DB, user.Id, plan, "test")
require.NoError(t, err)
require.Equal(t, "default", subscription.PrevUserGroup)
require.NoError(t, RefreshUserGroupCache(user.Id))
var updated User
require.NoError(t, DB.First(&updated, user.Id).Error)
assert.Equal(t, "pro", updated.Group)
assert.EqualValues(t, 1, updated.AuthVersion)
var session UserSession
require.NoError(t, DB.First(&session, "sid = ?", "subscription-auth-session").Error)
assert.Equal(t, UserSessionStatusActive, session.Status)
cached, err := GetUserCache(user.Id)
require.NoError(t, err)
assert.Equal(t, "pro", cached.Group)
assert.EqualValues(t, 1, cached.AuthVersion)
require.NoError(t, DB.Transaction(func(tx *gorm.DB) error {
target, err := downgradeUserGroupForSubscriptionTx(tx, subscription, now+1)
assert.Equal(t, "default", target)
return err
}))
require.NoError(t, RefreshUserGroupCache(user.Id))
require.NoError(t, DB.First(&updated, user.Id).Error)
assert.Equal(t, "default", updated.Group)
assert.EqualValues(t, 1, updated.AuthVersion)
require.NoError(t, DB.First(&session, "sid = ?", "subscription-auth-session").Error)
assert.Equal(t, UserSessionStatusActive, session.Status)
cached, err = GetUserCache(user.Id)
require.NoError(t, err)
assert.Equal(t, "default", cached.Group)
}
func TestSubscriptionGroupCacheRefreshFailureDoesNotChangeCommittedResult(t *testing.T) {
previousDB, previousLogDB := DB, LOG_DB
previousMainDatabaseType, previousLogDatabaseType := common.MainDatabaseType(), common.LogDatabaseType()
common.SetDatabaseTypes(common.DatabaseTypeSQLite, common.DatabaseTypeSQLite)
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", strings.ReplaceAll(t.Name(), "/", "_"))
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
require.NoError(t, err)
DB, LOG_DB = db, db
require.NoError(t, db.AutoMigrate(&User{}, &SubscriptionPlan{}, &UserSubscription{}))
sqlDB, err := db.DB()
require.NoError(t, err)
sqlDB.SetMaxOpenConns(4)
t.Cleanup(func() {
DB, LOG_DB = previousDB, previousLogDB
common.SetDatabaseTypes(previousMainDatabaseType, previousLogDatabaseType)
_ = sqlDB.Close()
})
user := User{
Username: "subscription-cache-failure",
Password: "unused-password-hash",
Role: common.RoleCommonUser,
Status: common.UserStatusEnabled,
Group: "default",
AuthVersion: 1,
}
require.NoError(t, DB.Create(&user).Error)
plan := &SubscriptionPlan{
Title: "Cache failure plan",
DurationUnit: SubscriptionDurationMonth,
DurationValue: 1,
TotalAmount: 100,
UpgradeGroup: "pro",
Enabled: true,
}
require.NoError(t, DB.Create(plan).Error)
InvalidateSubscriptionPlanCache(plan.Id)
oldRedisEnabled, oldRDB := common.RedisEnabled, common.RDB
common.RedisEnabled = true
common.RDB = redis.NewClient(&redis.Options{
Dialer: func(context.Context, string, string) (net.Conn, error) {
return nil, errors.New("forced redis failure")
},
MaxRetries: -1,
})
t.Cleanup(func() {
_ = common.RDB.Close()
common.RedisEnabled, common.RDB = oldRedisEnabled, oldRDB
})
message, err := AdminBindSubscription(user.Id, plan.Id, "test")
require.NoError(t, err)
assert.Contains(t, message, "pro")
var updated User
require.NoError(t, DB.First(&updated, user.Id).Error)
assert.Equal(t, "pro", updated.Group)
assert.EqualValues(t, 1, updated.AuthVersion)
var subscription UserSubscription
require.NoError(t, DB.Where("user_id = ?", user.Id).First(&subscription).Error)
assert.Equal(t, "active", subscription.Status)
}