完善初始化验证凭据精确回收
This commit is contained in:
parent
3283461621
commit
aa52981079
@ -150,6 +150,33 @@ func (m *DatabaseVerificationManager) Consume(token, credentialRef string) (conf
|
||||
return m.session(token, credentialRef, true)
|
||||
}
|
||||
|
||||
// Discard 按验证令牌精确回收尚未消费的数据库凭据,不影响其他初始化会话。
|
||||
func (m *DatabaseVerificationManager) Discard(token string) error {
|
||||
if m == nil {
|
||||
return errors.New("数据库验证服务尚未初始化")
|
||||
}
|
||||
token = strings.TrimSpace(token)
|
||||
if token == "" {
|
||||
return errors.New("数据库验证令牌不能为空")
|
||||
}
|
||||
now := m.now().UTC()
|
||||
m.mu.Lock()
|
||||
expired := m.clearExpiredLocked(now)
|
||||
session, ok := m.sessions[token]
|
||||
if ok {
|
||||
delete(m.sessions, token)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.deleteCredentials(expired)
|
||||
if !ok {
|
||||
return errors.New("数据库验证令牌无效或已经过期")
|
||||
}
|
||||
if err := m.deleteCredential(session.CredentialRef); err != nil {
|
||||
return errors.New("数据库临时凭据回收失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DatabaseVerificationManager) session(token, credentialRef string, consume bool) (config.DatabaseConfig, error) {
|
||||
if m == nil {
|
||||
return config.DatabaseConfig{}, errors.New("数据库验证服务尚未初始化")
|
||||
|
||||
@ -157,3 +157,44 @@ func TestSQLiteRejectsUserControlledPathAndExternalFields(t *testing.T) {
|
||||
t.Fatal("SQLite 用户自定义路径未被拒绝")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseVerificationDiscardOnlyRemovesSelectedToken(t *testing.T) {
|
||||
base := config.Default(t.TempDir())
|
||||
store := &memoryDatabaseStore{}
|
||||
manager := NewDatabaseVerificationManager(base, store)
|
||||
manager.verify = func(context.Context, config.DatabaseConfig) error { return nil }
|
||||
|
||||
first, err := manager.Verify(t.Context(), DatabaseInput{
|
||||
Driver: "mysql", Host: "db.example.test", Database: "scert",
|
||||
Username: "first_user", Password: "first-password", TLSMode: "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := manager.Verify(t.Context(), DatabaseInput{
|
||||
Driver: "postgres", Host: "db.example.test", Database: "scert",
|
||||
Username: "second_user", Password: "second-password", TLSMode: "verify-full",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := manager.Discard(first.VerificationToken); err != nil {
|
||||
t.Fatalf("精确回收数据库验证令牌失败:%v", err)
|
||||
}
|
||||
if _, err := manager.Check(first.VerificationToken, first.CredentialRef); err == nil {
|
||||
t.Fatal("已回收的数据库验证令牌仍然有效")
|
||||
}
|
||||
if _, err := manager.Check(second.VerificationToken, second.CredentialRef); err != nil {
|
||||
t.Fatalf("精确回收影响了其他数据库验证令牌:%v", err)
|
||||
}
|
||||
store.mu.Lock()
|
||||
_, firstExists := store.values[first.CredentialRef]
|
||||
_, secondExists := store.values[second.CredentialRef]
|
||||
store.mu.Unlock()
|
||||
if firstExists || !secondExists {
|
||||
t.Fatalf("数据库凭据回收范围错误:first=%v second=%v", firstExists, secondExists)
|
||||
}
|
||||
if err := manager.Discard(first.VerificationToken); err == nil {
|
||||
t.Fatal("数据库验证令牌被重复回收")
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +172,51 @@ func (m *DNSVerificationManager) Consume(token, providerID, credentialRef, testD
|
||||
return m.validateSession(token, providerID, credentialRef, testDomain, true)
|
||||
}
|
||||
|
||||
// Discard 按验证令牌精确回收尚未消费的 DNS 临时凭据,不影响其他初始化会话。
|
||||
func (m *DNSVerificationManager) Discard(token string) error {
|
||||
if m == nil {
|
||||
return errors.New("DNS 在线验证服务未初始化")
|
||||
}
|
||||
token = strings.TrimSpace(token)
|
||||
if token == "" {
|
||||
return errors.New("DNS 验证令牌不能为空")
|
||||
}
|
||||
now := m.now().UTC()
|
||||
m.mu.Lock()
|
||||
expiredRefs := m.clearExpiredLocked(now)
|
||||
session, ok := m.sessions[token]
|
||||
if ok {
|
||||
delete(m.sessions, token)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.deleteCredentials(expiredRefs)
|
||||
if !ok {
|
||||
return errors.New("DNS 验证令牌无效或已经过期")
|
||||
}
|
||||
if err := m.deleteCredential(session.CredentialRef); err != nil {
|
||||
m.logCleanupFailure()
|
||||
return errors.New("DNS 临时凭据回收失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscardAll 回收所有尚未消费的 DNS 验证令牌及其临时凭据。
|
||||
func (m *DNSVerificationManager) DiscardAll() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
references := make([]string, 0, len(m.sessions))
|
||||
for token, session := range m.sessions {
|
||||
delete(m.sessions, token)
|
||||
if strings.TrimSpace(session.CredentialRef) != "" {
|
||||
references = append(references, session.CredentialRef)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.deleteCredentials(references)
|
||||
}
|
||||
|
||||
func (m *DNSVerificationManager) validateSession(token, providerID, credentialRef, testDomain string, consume bool) error {
|
||||
if m == nil {
|
||||
return errors.New("DNS 在线验证服务未初始化")
|
||||
|
||||
@ -123,6 +123,72 @@ func TestDNSVerificationManagerRejectsMismatchedConfiguration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSVerificationDiscardOnlyRemovesSelectedToken(t *testing.T) {
|
||||
store := &memorySecretWriter{}
|
||||
manager := NewDNSVerificationManager(fakeDNSTester{}, store)
|
||||
first, err := manager.Verify(t.Context(), DNSVerificationInput{
|
||||
ProviderID: "aliyun", TestDomain: "first.example.com",
|
||||
Credentials: map[string]string{"access_key_id": "first-id", "access_key_secret": "first-secret"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := manager.Verify(t.Context(), DNSVerificationInput{
|
||||
ProviderID: "aliyun", TestDomain: "second.example.com",
|
||||
Credentials: map[string]string{"access_key_id": "second-id", "access_key_secret": "second-secret"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := manager.Discard(first.VerificationToken); err != nil {
|
||||
t.Fatalf("精确回收 DNS 验证令牌失败:%v", err)
|
||||
}
|
||||
if err := manager.Check(first.VerificationToken, "aliyun", first.CredentialRef, "first.example.com"); err == nil {
|
||||
t.Fatal("已回收的 DNS 验证令牌仍然有效")
|
||||
}
|
||||
if err := manager.Check(second.VerificationToken, "aliyun", second.CredentialRef, "second.example.com"); err != nil {
|
||||
t.Fatalf("精确回收影响了其他 DNS 验证令牌:%v", err)
|
||||
}
|
||||
store.mu.Lock()
|
||||
_, firstExists := store.values[first.CredentialRef]
|
||||
_, secondExists := store.values[second.CredentialRef]
|
||||
store.mu.Unlock()
|
||||
if firstExists || !secondExists {
|
||||
t.Fatalf("DNS 凭据回收范围错误:first=%v second=%v", firstExists, secondExists)
|
||||
}
|
||||
if err := manager.Discard(first.VerificationToken); err == nil {
|
||||
t.Fatal("DNS 验证令牌被重复回收")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSVerificationDiscardAllRemovesPendingCredentials(t *testing.T) {
|
||||
store := &memorySecretWriter{}
|
||||
manager := NewDNSVerificationManager(fakeDNSTester{}, store)
|
||||
results := make([]DNSVerificationResult, 0, 2)
|
||||
for _, testDomain := range []string{"first.example.com", "second.example.com"} {
|
||||
result, err := manager.Verify(t.Context(), DNSVerificationInput{
|
||||
ProviderID: "aliyun", TestDomain: testDomain,
|
||||
Credentials: map[string]string{"access_key_id": "test-id", "access_key_secret": "test-secret"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
manager.DiscardAll()
|
||||
for _, result := range results {
|
||||
if err := manager.Check(result.VerificationToken, "aliyun", result.CredentialRef, "first.example.com"); err == nil {
|
||||
t.Fatal("DiscardAll 后 DNS 验证令牌仍然有效")
|
||||
}
|
||||
}
|
||||
store.mu.Lock()
|
||||
remaining := len(store.values)
|
||||
store.mu.Unlock()
|
||||
if remaining != 0 {
|
||||
t.Fatalf("DiscardAll 后仍有 DNS 临时凭据:%d", remaining)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSVerificationManagerRejectsExpiredToken(t *testing.T) {
|
||||
store := &memorySecretWriter{}
|
||||
manager := NewDNSVerificationManager(fakeDNSTester{}, store)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user