Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ APP_SLACK_CHANNEL=C01234ABCDE
# APP_SLACK_CHANNEL_PR_BYPASS=C01234ABCDE
# APP_SLACK_CHANNEL_OKTA_SYNC=C01234ABCDE
# APP_SLACK_CHANNEL_ORPHANED_USERS=C01234ABCDE
# optional: custom footer note for PR bypass notifications (supports Slack mrkdwn)
# APP_SLACK_FOOTER_NOTE_PR_BYPASS=_Please review the <https://example.com/policy|security policy>._

# api gateway base path (optional, for lambda deployments with stage prefix)
# APP_BASE_PATH=v1
Expand Down
5 changes: 4 additions & 1 deletion internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ func New(ctx context.Context, cfg *config.Config) (*App, error) {
OktaSync: cfg.SlackChannelOktaSync,
OrphanedUsers: cfg.SlackChannelOrphanedUsers,
}
app.Notifier = notifiers.NewSlackNotifierWithAPIURL(cfg.SlackToken, channels, cfg.SlackAPIURL)
messages := notifiers.SlackMessages{
PRBypassFooterNote: cfg.SlackPRBypassFooterNote,
}
app.Notifier = notifiers.NewSlackNotifierWithAPIURL(cfg.SlackToken, channels, messages, cfg.SlackAPIURL)
}

return app, nil
Expand Down
4 changes: 4 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Config struct {
SlackChannelPRBypass string
SlackChannelOktaSync string
SlackChannelOrphanedUsers string
SlackPRBypassFooterNote string
SlackAPIURL string
}

Expand Down Expand Up @@ -179,6 +180,7 @@ func NewConfigWithContext(ctx context.Context) (*Config, error) {
SlackChannelPRBypass: os.Getenv("APP_SLACK_CHANNEL_PR_BYPASS"),
SlackChannelOktaSync: os.Getenv("APP_SLACK_CHANNEL_OKTA_SYNC"),
SlackChannelOrphanedUsers: os.Getenv("APP_SLACK_CHANNEL_ORPHANED_USERS"),
SlackPRBypassFooterNote: os.Getenv("APP_SLACK_FOOTER_NOTE_PR_BYPASS"),
SlackAPIURL: os.Getenv("APP_SLACK_API_URL"),
}

Expand Down Expand Up @@ -371,6 +373,7 @@ type RedactedConfig struct {
SlackChannelPRBypass string `json:"slack_channel_pr_bypass"`
SlackChannelOktaSync string `json:"slack_channel_okta_sync"`
SlackChannelOrphanedUsers string `json:"slack_channel_orphaned_users"`
SlackPRBypassFooterNote string `json:"slack_pr_bypass_footer_note"`
SlackAPIURL string `json:"slack_api_url"`
}

Expand Down Expand Up @@ -426,6 +429,7 @@ func (c *Config) Redacted() RedactedConfig {
SlackChannelPRBypass: c.SlackChannelPRBypass,
SlackChannelOktaSync: c.SlackChannelOktaSync,
SlackChannelOrphanedUsers: c.SlackChannelOrphanedUsers,
SlackPRBypassFooterNote: c.SlackPRBypassFooterNote,
SlackAPIURL: c.SlackAPIURL,
}
}
14 changes: 11 additions & 3 deletions internal/notifiers/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,35 @@ type SlackChannels struct {
OrphanedUsers string
}

// SlackMessages holds optional custom messages for different notification
// types. empty values are excluded from the notification.
type SlackMessages struct {
PRBypassFooterNote string
}

// SlackNotifier sends formatted messages to Slack channels.
type SlackNotifier struct {
client *slack.Client
channels SlackChannels
messages SlackMessages
}

// NewSlackNotifier creates a Slack notifier with default API URL.
func NewSlackNotifier(token string, channels SlackChannels) *SlackNotifier {
return NewSlackNotifierWithAPIURL(token, channels, "")
func NewSlackNotifier(token string, channels SlackChannels, messages SlackMessages) *SlackNotifier {
return NewSlackNotifierWithAPIURL(token, channels, messages, "")
}

// NewSlackNotifierWithAPIURL creates a Slack notifier with custom API URL.
// useful for testing with mock servers.
func NewSlackNotifierWithAPIURL(token string, channels SlackChannels, apiURL string) *SlackNotifier {
func NewSlackNotifierWithAPIURL(token string, channels SlackChannels, messages SlackMessages, apiURL string) *SlackNotifier {
var opts []slack.Option
if apiURL != "" {
opts = append(opts, slack.OptionAPIURL(apiURL))
}
return &SlackNotifier{
client: slack.New(token, opts...),
channels: channels,
messages: messages,
}
}

Expand Down
7 changes: 7 additions & 0 deletions internal/notifiers/slack_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ func (s *SlackNotifier) NotifyPRBypass(ctx context.Context, result *client.PRCom
))
}

if s.messages.PRBypassFooterNote != "" {
blocks = append(blocks, slack.NewContextBlock(
"footer",
slack.NewTextBlockObject("mrkdwn", s.messages.PRBypassFooterNote, false, false),
))
}

channel := s.channelFor(s.channels.PRBypass)
_, _, err := s.client.PostMessageContext(
ctx,
Expand Down