feat: manage existing credential actions with audited replay
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"git.yuntuo.hk/k2safe/orbit/internal/consoleauth"
|
||||
@@ -194,14 +195,17 @@ func applicationPermissions(in consoleauth.ApplicationInput) (auth.Permissions,
|
||||
return p, p.Validate()
|
||||
}
|
||||
func validateApplicationInput(in consoleauth.ApplicationInput) error {
|
||||
if in.Kind != "SET_PERMISSIONS" && in.Actions != nil {
|
||||
return fault.Invalid("actions is only valid for SET_PERMISSIONS")
|
||||
}
|
||||
if in.ValidDays < 0 || in.ValidDays > 365 {
|
||||
return fault.Invalid("valid_days must be 0 (permanent) or 1-365")
|
||||
}
|
||||
if in.GraceDays < 0 || in.GraceDays > 30 || (in.Kind != "ROTATE" && in.GraceDays != 0) {
|
||||
return fault.Invalid("grace_days must be 0-30 for rotation only")
|
||||
}
|
||||
if in.Kind == "FINALIZE" && in.ValidDays != 0 {
|
||||
return fault.Invalid("finalize cannot set validity")
|
||||
if (in.Kind == "FINALIZE" || in.Kind == "SET_PERMISSIONS") && in.ValidDays != 0 {
|
||||
return fault.Invalid("this change cannot set validity")
|
||||
}
|
||||
if err := validCredentialReason(in.Reason); err != nil {
|
||||
return err
|
||||
@@ -213,12 +217,15 @@ func validateApplicationInput(in consoleauth.ApplicationInput) error {
|
||||
}
|
||||
_, err := applicationPermissions(in)
|
||||
return err
|
||||
case "ROTATE", "SET_EXPIRY", "FINALIZE":
|
||||
case "ROTATE", "SET_EXPIRY", "FINALIZE", "SET_PERMISSIONS":
|
||||
if in.NamespaceID.Validate() != nil || in.CredentialID.Validate() != nil || in.ExpectedRevision == 0 || in.Application != "" || in.Namespace != "" || in.Environment != "" || in.Queue != "" || len(in.Capabilities) != 0 {
|
||||
return fault.Invalid("invalid rotation fields")
|
||||
return fault.Invalid("invalid credential change fields")
|
||||
}
|
||||
if in.Kind == "SET_PERMISSIONS" {
|
||||
return validateCredentialChangeInput(CredentialChangeInput{NamespaceID: in.NamespaceID, CredentialID: in.CredentialID, ExpectedRevision: in.ExpectedRevision, Action: in.Kind, Actions: in.Actions, Reason: in.Reason})
|
||||
}
|
||||
default:
|
||||
return fault.Invalid("kind must be CREATE, ROTATE, SET_EXPIRY or FINALIZE")
|
||||
return fault.Invalid("kind must be CREATE, ROTATE, SET_EXPIRY, FINALIZE or SET_PERMISSIONS")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -227,6 +234,12 @@ func (s *ConsoleApplicationStore) PrepareApplication(ctx context.Context, token
|
||||
if err := validateApplicationInput(in); err != nil {
|
||||
return out, err
|
||||
}
|
||||
if in.Kind == "SET_PERMISSIONS" {
|
||||
out.Version = 3
|
||||
// 固定排序副本,避免准备返回后由调用方改变切片或重放时重算意图。
|
||||
out.Input.Actions = append([]string(nil), in.Actions...)
|
||||
sort.Strings(out.Input.Actions)
|
||||
}
|
||||
err := WithTx(ctx, s.db, func(tx *sql.Tx) error {
|
||||
if _, err := s.admin(ctx, tx, token); err != nil {
|
||||
return err
|
||||
@@ -279,7 +292,8 @@ func (s *ConsoleApplicationStore) ApplyApplication(ctx context.Context, token au
|
||||
if err := validateApplicationInput(op.Input); err != nil {
|
||||
return out, err
|
||||
}
|
||||
if (op.Version != 0 && op.Version != 2) || op.OperationID.Validate() != nil || op.ApplicationID.Validate() != nil || op.NamespaceID.Validate() != nil || op.CredentialID.Validate() != nil {
|
||||
validVersion := (op.Input.Kind == "SET_PERMISSIONS" && op.Version == 3) || (op.Input.Kind != "SET_PERMISSIONS" && (op.Version == 0 || op.Version == 2))
|
||||
if !validVersion || op.OperationID.Validate() != nil || op.ApplicationID.Validate() != nil || op.NamespaceID.Validate() != nil || op.CredentialID.Validate() != nil {
|
||||
return out, fault.Invalid("invalid fixed operation identity")
|
||||
}
|
||||
if (op.Input.ValidDays == 0) != (op.ExpiresAt == nil) || (op.Input.GraceDays == 0) != (op.PreviousValidUntil == nil) {
|
||||
@@ -293,7 +307,7 @@ func (s *ConsoleApplicationStore) ApplyApplication(ctx context.Context, token au
|
||||
if op.QueueID != "" || op.NamespaceID != op.Input.NamespaceID || op.CredentialID != op.Input.CredentialID {
|
||||
return out, fault.Invalid("credential identity mismatch")
|
||||
}
|
||||
change := CredentialOperation{Version: version, OperationID: op.OperationID, NamespaceID: op.NamespaceID, CredentialID: op.CredentialID, ExpectedRevision: op.Input.ExpectedRevision, Action: op.Input.Kind, Reason: op.Input.Reason, Token: op.Token, ExpiresAt: op.ExpiresAt, PreviousValidUntil: op.PreviousValidUntil}
|
||||
change := CredentialOperation{Version: version, OperationID: op.OperationID, NamespaceID: op.NamespaceID, CredentialID: op.CredentialID, ExpectedRevision: op.Input.ExpectedRevision, Action: op.Input.Kind, Actions: op.Input.Actions, Reason: op.Input.Reason, Token: op.Token, ExpiresAt: op.ExpiresAt, PreviousValidUntil: op.PreviousValidUntil}
|
||||
receipt, err := applyCredentialOperation(ctx, s.db, change, func(tx *sql.Tx) (string, error) {
|
||||
actor, err := s.admin(ctx, tx, token)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user