Files
orbit/internal/storage/mysql/console_credential_tokens.go

86 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mysql
import (
"context"
"database/sql"
"git.yuntuo.hk/k2safe/orbit/internal/consoleauth"
"git.yuntuo.hk/k2safe/orbit/internal/fault"
core "git.yuntuo.hk/k2safe/orbit/model"
"git.yuntuo.hk/k2safe/orbit/platform/auth"
)
var _ consoleauth.CredentialTokens = (*ConsoleApplicationStore)(nil)
// AccessCredentialToken 认证、版本核对、密文读取/补存和审计在同一事务完成。
// 导入相同当前秘密可重复调用,不变更认证 revision、到期时间或旧代过渡截止。
func (s *ConsoleApplicationStore) AccessCredentialToken(ctx context.Context, session auth.Token, namespace, credential core.ID, in consoleauth.CredentialTokenInput, importCurrent bool) (consoleauth.CredentialToken, error) {
var out consoleauth.CredentialToken
if namespace.Validate() != nil || credential.Validate() != nil || in.ExpectedRevision == 0 || (!importCurrent && in.Token != "") {
return out, fault.Invalid("invalid credential token request")
}
err := WithTx(ctx, s.db, func(tx *sql.Tx) error {
actor, err := s.admin(ctx, tx, session)
if err != nil {
return err
}
if s.vault == nil {
return fault.New("UNAVAILABLE", "Token 保管密钥尚未配置,请联系部署管理员")
}
var revision uint64
var revoked sql.NullTime
var digest core.Digest
var keyID sql.NullString
var ciphertext []byte
err = tx.QueryRowContext(ctx, `SELECT revision,revoked_at,secret_digest,secret_key_id,secret_ciphertext FROM orbit_credentials WHERE namespace_id=? AND id=? FOR UPDATE`, namespace, credential).Scan(&revision, &revoked, &digest, &keyID, &ciphertext)
if err == sql.ErrNoRows {
return fault.NotFound
}
if err != nil {
return err
}
if revision != in.ExpectedRevision || revoked.Valid {
return fault.New("STATE_CONFLICT", "凭据已变更或撤销,请刷新后重试")
}
raw := in.Token
action := "console.credential.token.view"
if !importCurrent {
if !keyID.Valid {
return fault.New("STATE_CONFLICT", "此历史凭据尚未保管 Token,请先导入当前 Token")
}
raw, err = s.vault.Open(namespace, credential, keyID.String, ciphertext)
if err != nil {
return fault.New("UNAVAILABLE", "Token 解密失败,请检查保管密钥文件")
}
}
parsed, err := auth.Parse(raw)
if err != nil || parsed.NamespaceID != namespace || parsed.CredentialID != credential || subtleCompareDigest(parsed.Digest, digest) != 1 {
return fault.Invalid("Token 与当前凭据不匹配;不能导入过渡期旧 Token")
}
if importCurrent {
action = "console.credential.token.import"
id, encrypted, err := s.vault.Seal(namespace, credential, raw)
if err != nil {
return err
}
if _, err := tx.ExecContext(ctx, `UPDATE orbit_credentials SET secret_key_id=?,secret_ciphertext=?,updated_at=UTC_TIMESTAMP(6) WHERE namespace_id=? AND id=?`, id, encrypted, namespace, credential); err != nil {
return err
}
}
operation, err := core.NewID()
if err != nil {
return err
}
if err := appendAuditActor(ctx, tx, namespace, actor, operation, action, credential, "administrator access to current integration configuration", nil, map[string]any{"revision": revision}); err != nil {
return err
}
out = consoleauth.CredentialToken{NamespaceID: namespace, CredentialID: credential, Revision: revision, Token: raw}
return nil
})
// 包括提交结果未知:只有确认审计提交成功,才允许向调用方交付秘密。
if err != nil {
return consoleauth.CredentialToken{}, err
}
return out, nil
}