feat: let administrators view and copy encrypted integration credentials

This commit is contained in:
2026-09-27 13:48:26 +08:00
parent 5555a7b342
commit d54bdd9847
38 changed files with 905 additions and 71 deletions

View File

@@ -10,6 +10,7 @@ import (
"time"
"unicode/utf8"
"git.yuntuo.hk/k2safe/orbit/internal/credentialvault"
"git.yuntuo.hk/k2safe/orbit/internal/fault"
core "git.yuntuo.hk/k2safe/orbit/model"
"git.yuntuo.hk/k2safe/orbit/platform/auth"
@@ -67,7 +68,7 @@ type CredentialChangeInput struct {
}
// CredentialOperation 是可安全重放的私有操作文件内容。
// 令牌只存在于 ROTATE 文件中;服务端只保存其摘要和操作回执。
// ROTATE 文件包含新令牌;服务端保存摘要、可选密文和不含秘密的操作回执。
type CredentialOperation struct {
// Version 操作文件格式版本,升级时不得静默改变语义。
Version int `json:"version"`
@@ -225,12 +226,16 @@ func ValidateCredentialOperation(op CredentialOperation) error {
// ApplyCredentialOperation applies one frozen operation or returns its original
// receipt. No callback or file I/O occurs inside the transaction. A commit error
// is intentionally returned unchanged; replaying the same operation reconciles it.
func ApplyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOperation) (CredentialChangeReceipt, error) {
return applyCredentialOperation(ctx, db, op, nil)
func ApplyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOperation, vault ...*credentialvault.Keyring) (CredentialChangeReceipt, error) {
return applyCredentialOperation(ctx, db, op, nil, vault...)
}
// applyCredentialOperation 允许控制台在同一事务先核验管理员,并使用真实人类审计主体。
func applyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOperation, authorize func(*sql.Tx) (string, error)) (CredentialChangeReceipt, error) {
func applyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOperation, authorize func(*sql.Tx) (string, error), vault ...*credentialvault.Keyring) (CredentialChangeReceipt, error) {
var ring *credentialvault.Keyring
if len(vault) > 0 {
ring = vault[0]
}
var out CredentialChangeReceipt
if err := ValidateCredentialOperation(op); err != nil {
return out, err
@@ -320,9 +325,14 @@ func applyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOper
out.PreviousValidUntil = timePtr(until)
}
}
keyID, ciphertext, err := ring.Seal(op.NamespaceID, op.CredentialID, op.Token)
if err != nil {
return err
}
// 未配置保管密钥的旧 CLI 轮换也必须清除旧密文,不能回看过时代次。
if _, err := tx.ExecContext(ctx, `UPDATE orbit_credentials
SET secret_digest=?,expires_at=?,previous_secret_digest=?,previous_valid_until=?,revision=revision+1,updated_at=?
WHERE namespace_id=? AND id=? AND revision=?`, parsed.Digest, op.ExpiresAt, previousDigest, out.PreviousValidUntil, now, op.NamespaceID, op.CredentialID, revision); err != nil {
SET secret_digest=?,expires_at=?,previous_secret_digest=?,previous_valid_until=?,secret_key_id=?,secret_ciphertext=?,revision=revision+1,updated_at=?
WHERE namespace_id=? AND id=? AND revision=?`, parsed.Digest, op.ExpiresAt, previousDigest, out.PreviousValidUntil, keyID, ciphertext, now, op.NamespaceID, op.CredentialID, revision); err != nil {
return err
}
out.ExpiresAt = op.ExpiresAt
@@ -341,7 +351,7 @@ func applyCredentialOperation(ctx context.Context, db *sql.DB, op CredentialOper
}
out.PreviousValidUntil = nil
case credentialRevokeAction:
if _, err := tx.ExecContext(ctx, `UPDATE orbit_credentials SET revoked_at=?,previous_secret_digest=NULL,previous_valid_until=NULL,revision=revision+1,updated_at=? WHERE namespace_id=? AND id=? AND revision=?`, now, now, op.NamespaceID, op.CredentialID, revision); err != nil {
if _, err := tx.ExecContext(ctx, `UPDATE orbit_credentials SET revoked_at=?,previous_secret_digest=NULL,previous_valid_until=NULL,secret_key_id=NULL,secret_ciphertext=NULL,revision=revision+1,updated_at=? WHERE namespace_id=? AND id=? AND revision=?`, now, now, op.NamespaceID, op.CredentialID, revision); err != nil {
return err
}
out.RevokedAt = timePtr(now)