api_justification_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package api
  2. import (
  3. "context"
  4. "testing"
  5. "connectrpc.com/connect"
  6. "github.com/google/uuid"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  10. "github.com/OliveTin/OliveTin/internal/auth"
  11. config "github.com/OliveTin/OliveTin/internal/config"
  12. "github.com/OliveTin/OliveTin/internal/executor"
  13. )
  14. func TestStartActionRequiresJustificationForGuest(t *testing.T) {
  15. cfg := config.DefaultConfig()
  16. action := &config.Action{
  17. Title: "Send email",
  18. ID: "send_email",
  19. Justification: config.JustificationRequiredNoTemplate,
  20. Shell: "echo done",
  21. }
  22. cfg.Actions = append(cfg.Actions, action)
  23. ex := executor.DefaultExecutor(cfg)
  24. ex.RebuildActionMap()
  25. binding := ex.FindBindingWithNoEntity(action)
  26. require.NotNil(t, binding)
  27. ts, client := getNewTestServerAndClientWithExecutor(cfg, ex)
  28. defer ts.Close()
  29. _, err := client.StartAction(context.Background(), connect.NewRequest(&apiv1.StartActionRequest{
  30. BindingId: binding.ID,
  31. UniqueTrackingId: uuid.NewString(),
  32. }))
  33. require.Error(t, err)
  34. assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
  35. resp, err := client.StartAction(context.Background(), connect.NewRequest(&apiv1.StartActionRequest{
  36. BindingId: binding.ID,
  37. UniqueTrackingId: uuid.NewString(),
  38. Justification: "New user registration foo@example.com",
  39. }))
  40. require.NoError(t, err)
  41. require.NotEmpty(t, resp.Msg.ExecutionTrackingId)
  42. waitForLogJustification(t, ex, resp.Msg.ExecutionTrackingId, "New user registration foo@example.com")
  43. }
  44. func TestBuildActionExposesJustificationTemplate(t *testing.T) {
  45. cfg := config.DefaultConfig()
  46. action := &config.Action{
  47. Title: "Audited action",
  48. ID: "audited",
  49. Justification: "{{ target }}",
  50. Shell: "echo hi",
  51. }
  52. cfg.Actions = append(cfg.Actions, action)
  53. ex := executor.DefaultExecutor(cfg)
  54. ex.RebuildActionMap()
  55. binding := ex.FindBindingWithNoEntity(action)
  56. require.NotNil(t, binding)
  57. pb := buildAction(binding, &DashboardRenderRequest{
  58. cfg: cfg,
  59. ex: ex,
  60. })
  61. require.NotNil(t, pb)
  62. assert.Equal(t, "{{ target }}", pb.Justification)
  63. }
  64. func TestBuildActionExposesBlankRequiredJustification(t *testing.T) {
  65. cfg := config.DefaultConfig()
  66. action := &config.Action{
  67. Title: "Audited action",
  68. ID: "audited",
  69. Justification: config.JustificationRequiredNoTemplate,
  70. Shell: "echo hi",
  71. }
  72. cfg.Actions = append(cfg.Actions, action)
  73. ex := executor.DefaultExecutor(cfg)
  74. ex.RebuildActionMap()
  75. binding := ex.FindBindingWithNoEntity(action)
  76. require.NotNil(t, binding)
  77. pb := buildAction(binding, &DashboardRenderRequest{
  78. cfg: cfg,
  79. ex: ex,
  80. })
  81. require.NotNil(t, pb)
  82. assert.Equal(t, config.JustificationRequiredNoTemplate, pb.Justification)
  83. }
  84. func TestResolveStartJustificationUsesTemplateWhenClientValueEmpty(t *testing.T) {
  85. action := &config.Action{
  86. Justification: "{{ ansible_host }}",
  87. }
  88. binding := &executor.ActionBinding{}
  89. got := resolveStartJustification(action, binding, "", map[string]string{
  90. "ansible_host": "192.168.66.8",
  91. })
  92. assert.Equal(t, "192.168.66.8", got)
  93. }
  94. func TestResolveStartJustificationPrefersClientValue(t *testing.T) {
  95. action := &config.Action{
  96. Justification: "{{ ansible_host }}",
  97. }
  98. binding := &executor.ActionBinding{}
  99. got := resolveStartJustification(action, binding, "manual reason", map[string]string{
  100. "ansible_host": "192.168.66.8",
  101. })
  102. assert.Equal(t, "manual reason", got)
  103. }
  104. func TestStartActionResolvesJustificationTemplateForGuest(t *testing.T) {
  105. cfg := config.DefaultConfig()
  106. action := &config.Action{
  107. Title: "Run playbook",
  108. ID: "run_playbook",
  109. Justification: "{{ ansible_host }}",
  110. Shell: "echo done",
  111. Arguments: []config.ActionArgument{
  112. {Name: "ansible_host", Title: "Host"},
  113. },
  114. }
  115. cfg.Actions = append(cfg.Actions, action)
  116. ex := executor.DefaultExecutor(cfg)
  117. ex.RebuildActionMap()
  118. binding := ex.FindBindingWithNoEntity(action)
  119. require.NotNil(t, binding)
  120. ts, client := getNewTestServerAndClientWithExecutor(cfg, ex)
  121. defer ts.Close()
  122. resp, err := client.StartAction(context.Background(), connect.NewRequest(&apiv1.StartActionRequest{
  123. BindingId: binding.ID,
  124. UniqueTrackingId: uuid.NewString(),
  125. Arguments: []*apiv1.StartActionArgument{
  126. {Name: "ansible_host", Value: "stuffbox"},
  127. },
  128. }))
  129. require.NoError(t, err)
  130. require.NotEmpty(t, resp.Msg.ExecutionTrackingId)
  131. waitForLogJustification(t, ex, resp.Msg.ExecutionTrackingId, "stuffbox")
  132. }
  133. func TestValidateJustificationRequiredAllowsSystemUser(t *testing.T) {
  134. cfg := config.DefaultConfig()
  135. action := &config.Action{Title: "Cron job", Justification: config.JustificationRequiredNoTemplate}
  136. err := validateJustificationRequired(action, "", auth.UserFromSystem(cfg, "cron"))
  137. require.NoError(t, err)
  138. }