api_justification.go 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package api
  2. import (
  3. "fmt"
  4. "strings"
  5. "connectrpc.com/connect"
  6. apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  7. authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
  8. "github.com/OliveTin/OliveTin/internal/config"
  9. "github.com/OliveTin/OliveTin/internal/entities"
  10. "github.com/OliveTin/OliveTin/internal/executor"
  11. "github.com/OliveTin/OliveTin/internal/tpl"
  12. log "github.com/sirupsen/logrus"
  13. )
  14. func validateJustificationRequired(action *config.Action, justification string, user *authpublic.AuthenticatedUser) error {
  15. if !actionRequiresJustificationConfig(action) || justificationProvided(justification, user) {
  16. return nil
  17. }
  18. return fmt.Errorf("justification is required for this action")
  19. }
  20. func actionRequiresJustificationConfig(action *config.Action) bool {
  21. return action != nil && action.RequiresJustification()
  22. }
  23. func justificationProvided(justification string, user *authpublic.AuthenticatedUser) bool {
  24. return strings.TrimSpace(justification) != "" || executor.IsSystemExecution(user)
  25. }
  26. func connectInvalidJustification(err error) error {
  27. return connect.NewError(connect.CodeInvalidArgument, err)
  28. }
  29. func startActionArgumentsFromProto(args []*apiv1.StartActionArgument) map[string]string {
  30. result := make(map[string]string, len(args))
  31. for _, arg := range args {
  32. result[arg.Name] = arg.Value
  33. }
  34. return result
  35. }
  36. func resolveStartJustification(action *config.Action, binding *executor.ActionBinding, clientJustification string, args map[string]string) string {
  37. if strings.TrimSpace(clientJustification) != "" {
  38. return clientJustification
  39. }
  40. return resolveJustificationFromTemplate(action, binding, clientJustification, args)
  41. }
  42. func resolveJustificationFromTemplate(action *config.Action, binding *executor.ActionBinding, fallback string, args map[string]string) string {
  43. templateText := action.JustificationTemplateText()
  44. if templateText == "" {
  45. return fallback
  46. }
  47. resolved, err := tpl.ParseTemplateWithActionContext(templateText, bindingEntity(binding), args)
  48. if err != nil {
  49. log.WithFields(justificationTemplateErrorFields(templateText, binding, err)).Warn("Failed to resolve justification template")
  50. return fallback
  51. }
  52. return resolved
  53. }
  54. func justificationTemplateErrorFields(templateText string, binding *executor.ActionBinding, err error) log.Fields {
  55. fields := log.Fields{
  56. "template": templateText,
  57. "error": err,
  58. }
  59. entity := bindingEntity(binding)
  60. if entity == nil {
  61. return fields
  62. }
  63. fields["entityKey"] = entity.UniqueKey
  64. if binding.Action != nil && binding.Action.Entity != "" {
  65. fields["entityType"] = binding.Action.Entity
  66. }
  67. return fields
  68. }
  69. func bindingEntity(binding *executor.ActionBinding) *entities.Entity {
  70. if binding == nil {
  71. return nil
  72. }
  73. return binding.Entity
  74. }
  75. func restartRequiresJustificationError() error {
  76. return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("justification is required for this action; use StartAction with a justification instead"))
  77. }