86 lines
3.5 KiB
Go
86 lines
3.5 KiB
Go
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
|
||
}
|