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
10 changes: 10 additions & 0 deletions server/plugin/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,16 @@ func getAutocompleteData(config *Configuration) *model.AutocompleteData {
HelpText: "Include posts from members of the configured organization",
},
})
subscriptionsAdd.AddNamedStaticListArgument("include-only-org-members", "Events triggered only by organization members will be delivered (the organization config should be set, otherwise this flag has not effect)", false, []model.AutocompleteListItem{
{
Item: "true",
HelpText: "Include posts only from members of the configured organization",
},
{
Item: "false",
HelpText: "Include posts from members and collaborators of the configured organization",
},
})
}

subscriptionsAdd.AddNamedStaticListArgument("render-style", "Determine the rendering style of various notifications.", false, []model.AutocompleteListItem{
Expand Down
37 changes: 29 additions & 8 deletions server/plugin/subscriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ import (
)

const (
SubscriptionsKey = "subscriptions"
flagExcludeOrgMember = "exclude-org-member"
flagRenderStyle = "render-style"
flagFeatures = "features"
flagExcludeRepository = "exclude"
SubscriptionsKey = "subscriptions"
flagExcludeOrgMember = "exclude-org-member"
flagIncludeOnlyOrgMembers = "include-only-org-members"
flagRenderStyle = "render-style"
flagFeatures = "features"
flagExcludeRepository = "exclude"
)

type SubscriptionFlags struct {
ExcludeOrgMembers bool
RenderStyle string
ExcludeRepository []string
ExcludeOrgMembers bool
IncludeOnlyOrgMembers bool
RenderStyle string
ExcludeRepository []string
}

func (s *SubscriptionFlags) AddFlag(flag string, value string) error {
Expand All @@ -38,6 +40,12 @@ func (s *SubscriptionFlags) AddFlag(flag string, value string) error {
return err
}
s.ExcludeOrgMembers = parsed
case flagIncludeOnlyOrgMembers:
parsed, err := strconv.ParseBool(value)
if err != nil {
return err
}
s.IncludeOnlyOrgMembers = parsed
case flagRenderStyle:
s.RenderStyle = value
case flagExcludeRepository:
Expand All @@ -59,6 +67,11 @@ func (s SubscriptionFlags) String() string {
flags = append(flags, flag)
}

if s.IncludeOnlyOrgMembers {
flag := "--" + flagIncludeOnlyOrgMembers + " true"
flags = append(flags, flag)
}

if s.RenderStyle != "" {
flag := "--" + flagRenderStyle + " " + s.RenderStyle
flags = append(flags, flag)
Expand Down Expand Up @@ -161,6 +174,10 @@ func (s *Subscription) ExcludeOrgMembers() bool {
return s.Flags.ExcludeOrgMembers
}

func (s *Subscription) IncludeOnlyOrgMembers() bool {
return s.Flags.IncludeOnlyOrgMembers
}

func (s *Subscription) RenderStyle() string {
return s.Flags.RenderStyle
}
Expand Down Expand Up @@ -190,6 +207,10 @@ func (p *Plugin) Subscribe(ctx context.Context, githubClient *github.Client, use
return errors.New("Unable to set --exclude-org-member flag. The GitHub plugin is not locked to a single organization.")
}

if flags.IncludeOnlyOrgMembers && !p.isOrganizationLocked() {
return errors.New("Unable to set --include-only-org-members flag. The GitHub plugin is not locked to a single organization.")
}

var err, cErr error

if repo == "" {
Expand Down
102 changes: 102 additions & 0 deletions server/plugin/subscriptions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
package plugin

import (
"context"
"testing"

"github.com/google/go-github/v54/github"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -121,3 +124,102 @@ func TestPlugin_GetSubscriptionsByChannel(t *testing.T) {
})
}
}

func TestAddFlag(t *testing.T) {
tests := []struct {
name string
flags SubscriptionFlags
flag string
value string
want bool
wantErr bool
}{
{
name: "IncludeOnlyOrgMembers flag is parsed",
flags: SubscriptionFlags{},
flag: "include-only-org-members",
value: "true",
want: true,
wantErr: false,
},
{
name: "IncludeOnlyOrgMembers flag cannot be parsed",
flags: SubscriptionFlags{},
flag: "include-only-org-members",
value: "test",
want: false,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Helper()

err := tt.flags.AddFlag(tt.flag, tt.value)
CheckError(t, tt.wantErr, err)
assert.Equal(t, tt.flags.IncludeOnlyOrgMembers, tt.want)
})
}
}

func TestString(t *testing.T) {
tests := []struct {
name string
flags SubscriptionFlags
flag string
value string
want string
}{
{
name: "Return --include-only-org-members string",
flags: SubscriptionFlags{},
flag: "include-only-org-members",
value: "true",
want: "--include-only-org-members true",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
_ = tt.flags.AddFlag(tt.flag, tt.value)
got := tt.flags.String()
assert.Equal(t, tt.want, got)
})
}
}

func TestSubscribe(t *testing.T) {
tests := []struct {
name string
flags SubscriptionFlags
plugin *Plugin
errMsg string
}{
{
name: "Return error if GitHub organization is not set when --include-only-org-members flag is true",
flags: SubscriptionFlags{IncludeOnlyOrgMembers: true},
plugin: NewPlugin(),
errMsg: "Unable to set --include-only-org-members flag. The GitHub plugin is not locked to a single organization.",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
err := tt.plugin.Subscribe(
context.Background(),
github.NewClient(nil),
model.NewId(),
"test-owner",
"test-repo",
model.NewId(),
"test-features",
tt.flags,
)
require.Error(t, err)
assert.Contains(t, err.Error(), tt.errMsg)
})
}
}
1 change: 1 addition & 0 deletions server/plugin/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ Reviewers: {{range $i, $el := .RequestedReviewers -}} {{- if $i}}, {{end}}{{temp
" * `discussion_comments` - includes new discussion comments\n" +
" * Defaults to `pulls,issues,creates,deletes`\n\n" +
" * `--exclude-org-member` - events triggered by organization members will not be delivered (the GitHub organization config should be set, otherwise this flag has not effect)\n" +
" * `--include-only-org-members` - events triggered only by organization members will be delivered (the GitHub organization config should be set, otherwise this flag has not effect)\n" +
" * `--render-style` - notifications will be delivered in the specified style (for example, the body of a pull request will not be displayed). Supported values are `collapsed`, `skip-body` or `default` (same as omitting the flag).\n" +
"* `/github subscriptions delete owner[/repo]` - Unsubscribe the current channel from a repository\n" +
"* `/github me` - Display the connected GitHub account\n" +
Expand Down
3 changes: 3 additions & 0 deletions server/plugin/test_utils.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package plugin

import (
Expand Down
57 changes: 57 additions & 0 deletions server/plugin/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func (wb *WebhookBroker) Close() {
}

func (p *Plugin) handleWebhook(w http.ResponseWriter, r *http.Request) {
p.client.Log.Info("Webhook event received")
config := p.getConfiguration()
body, err := io.ReadAll(r.Body)
if err != nil {
Expand Down Expand Up @@ -382,6 +383,26 @@ func (p *Plugin) excludeConfigOrgMember(user *github.User, subscription *Subscri
return p.isUserOrganizationMember(githubClient, user, info, organization)
}

func (p *Plugin) shouldDenyEventDueToNotOrgMember(user *github.User, subscription *Subscription) bool {
if !subscription.IncludeOnlyOrgMembers() {
return false
}

githubClient, err := p.GetGitHubClient(context.Background(), subscription.CreatorID)
if err != nil {
p.client.Log.Warn("Failed to get user info", "error", err.Error())
return false
}

info, nErr := p.getGitHubUserInfo(subscription.CreatorID)
if nErr != nil {
p.client.Log.Warn("Failed to exclude org member", "error", nErr.Message)
return false
}

return !p.isUserOrganizationMember(githubClient, user, info, p.getConfiguration().GitHubOrg)
}

func (p *Plugin) postPullRequestEvent(event *github.PullRequestEvent) {
repo := event.GetRepo()

Expand Down Expand Up @@ -438,6 +459,10 @@ func (p *Plugin) postPullRequestEvent(event *github.PullRequestEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

label := sub.Label()

contained := false
Expand Down Expand Up @@ -633,6 +658,10 @@ func (p *Plugin) postIssueEvent(event *github.IssuesEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

renderedMessage, err := renderTemplate(issueTemplate, GetEventWithRenderConfig(event, sub))
if err != nil {
p.client.Log.Warn("Failed to render template", "error", err.Error())
Expand Down Expand Up @@ -705,6 +734,10 @@ func (p *Plugin) postPushEvent(event *github.PushEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

post := p.makeBotPost(pushedCommitsMessage, "custom_git_push")

post.ChannelId = sub.ChannelID
Expand Down Expand Up @@ -742,6 +775,10 @@ func (p *Plugin) postCreateEvent(event *github.CreateEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

post := p.makeBotPost(newCreateMessage, "custom_git_create")

post.ChannelId = sub.ChannelID
Expand Down Expand Up @@ -781,6 +818,10 @@ func (p *Plugin) postDeleteEvent(event *github.DeleteEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

post := p.makeBotPost(newDeleteMessage, "custom_git_delete")
post.ChannelId = sub.ChannelID
if err = p.client.Post.CreatePost(post); err != nil {
Expand Down Expand Up @@ -822,6 +863,10 @@ func (p *Plugin) postIssueCommentEvent(event *github.IssueCommentEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

label := sub.Label()

contained := false
Expand Down Expand Up @@ -910,6 +955,10 @@ func (p *Plugin) postPullRequestReviewEvent(event *github.PullRequestReviewEvent
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

label := sub.Label()

contained := false
Expand Down Expand Up @@ -960,6 +1009,10 @@ func (p *Plugin) postPullRequestReviewCommentEvent(event *github.PullRequestRevi
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

label := sub.Label()

contained := false
Expand Down Expand Up @@ -1368,6 +1421,10 @@ func (p *Plugin) postStarEvent(event *github.StarEvent) {
continue
}

if p.shouldDenyEventDueToNotOrgMember(event.GetSender(), sub) {
continue
}

post := p.makeBotPost(newStarMessage, "custom_git_star")

post.ChannelId = sub.ChannelID
Expand Down
Loading