feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
+52
-20
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
@@ -22,6 +23,7 @@ var (
|
||||
type PasskeyCredential struct {
|
||||
ID int `json:"id" gorm:"primaryKey"`
|
||||
UserID int `json:"user_id" gorm:"uniqueIndex;not null"`
|
||||
RPID *string `json:"rp_id,omitempty" gorm:"column:rp_id;type:varchar(253)"`
|
||||
CredentialID string `json:"credential_id" gorm:"type:varchar(512);uniqueIndex;not null"` // base64 encoded
|
||||
PublicKey string `json:"public_key" gorm:"type:text;not null"` // base64 encoded
|
||||
AttestationType string `json:"attestation_type" gorm:"type:varchar(255)"`
|
||||
@@ -161,29 +163,49 @@ func GetPasskeyByCredentialID(credentialID []byte) (*PasskeyCredential, error) {
|
||||
// UpdatePasskeyAssertionState persists only fields produced by a successful
|
||||
// assertion. Registration identity (credential ID, public key, AAGUID,
|
||||
// transports and attestation metadata) is immutable on this path.
|
||||
func UpdatePasskeyAssertionState(userID int, credential *webauthn.Credential, lastUsedAt time.Time) error {
|
||||
if userID <= 0 || credential == nil || len(credential.ID) == 0 || lastUsedAt.IsZero() {
|
||||
func UpdatePasskeyAssertionState(userID int, credential *webauthn.Credential, lastUsedAt time.Time, rpID string) error {
|
||||
if userID <= 0 || credential == nil || len(credential.ID) == 0 || lastUsedAt.IsZero() || rpID == "" {
|
||||
return fmt.Errorf("Passkey 保存失败,请重试")
|
||||
}
|
||||
credentialID := base64.StdEncoding.EncodeToString(credential.ID)
|
||||
result := DB.Model(&PasskeyCredential{}).
|
||||
Where("user_id = ? AND credential_id = ?", userID, credentialID).
|
||||
Updates(map[string]any{
|
||||
"sign_count": credential.Authenticator.SignCount,
|
||||
"clone_warning": credential.Authenticator.CloneWarning,
|
||||
"user_present": credential.Flags.UserPresent,
|
||||
"user_verified": credential.Flags.UserVerified,
|
||||
"backup_eligible": credential.Flags.BackupEligible,
|
||||
"backup_state": credential.Flags.BackupState,
|
||||
"last_used_at": lastUsedAt,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected != 1 {
|
||||
return ErrPasskeyNotFound
|
||||
}
|
||||
return nil
|
||||
passkeyOptionMutex.Lock()
|
||||
defer passkeyOptionMutex.Unlock()
|
||||
return DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := validatePasskeyRPIDWithTx(tx, rpID); err != nil {
|
||||
return err
|
||||
}
|
||||
var stored PasskeyCredential
|
||||
if err := lockForUpdate(tx).Where("user_id = ? AND credential_id = ?", userID, credentialID).First(&stored).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrPasskeyNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Compare in Go: the database collation may fold historical case.
|
||||
if stored.RPID != nil && *stored.RPID != "" && *stored.RPID != rpID {
|
||||
return system_setting.ErrPasskeyRPIDUnavailable
|
||||
}
|
||||
result := tx.Model(&PasskeyCredential{}).
|
||||
Where("user_id = ? AND credential_id = ?", userID, credentialID).
|
||||
Where("rp_id IS NULL OR rp_id = ? OR rp_id = ?", "", rpID).
|
||||
Updates(map[string]any{
|
||||
"rp_id": rpID,
|
||||
"sign_count": credential.Authenticator.SignCount,
|
||||
"clone_warning": credential.Authenticator.CloneWarning,
|
||||
"user_present": credential.Flags.UserPresent,
|
||||
"user_verified": credential.Flags.UserVerified,
|
||||
"backup_eligible": credential.Flags.BackupEligible,
|
||||
"backup_state": credential.Flags.BackupState,
|
||||
"last_used_at": lastUsedAt,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected != 1 {
|
||||
return ErrPasskeyNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func upsertPasskeyCredentialWithTx(tx *gorm.DB, credential *PasskeyCredential) error {
|
||||
@@ -212,7 +234,17 @@ func upsertPasskeyCredentialWithAuthVersion(credential *PasskeyCredential, ident
|
||||
if credential == nil || credential.UserID <= 0 {
|
||||
return fmt.Errorf("Passkey 保存失败,请重试")
|
||||
}
|
||||
passkeyOptionMutex.Lock()
|
||||
defer passkeyOptionMutex.Unlock()
|
||||
if err := DB.Transaction(func(tx *gorm.DB) error {
|
||||
if identity != nil && (credential.RPID == nil || *credential.RPID == "") {
|
||||
return system_setting.ErrPasskeyRPIDUnavailable
|
||||
}
|
||||
if credential.RPID != nil && *credential.RPID != "" {
|
||||
if err := validatePasskeyRPIDWithTx(tx, *credential.RPID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if identity != nil {
|
||||
if identity.UserID != credential.UserID {
|
||||
return ErrUserSessionInactive
|
||||
|
||||
Reference in new issue
Block a user