Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MM-44185] Add conditions for subscription validation #858

Merged
merged 22 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dffc492
ensure user has access to selected sec level. disallow "exclude" clau…
mickmister May 17, 2022
1b4bc83
if a subscription has no configured sec level, assume the issue shoul…
mickmister May 17, 2022
329300f
add test data file
mickmister May 17, 2022
6865983
fix ci withL npm install --verbose
mickmister May 18, 2022
029b314
Merge branch 'master' into validate-subscription
mattermod Jun 17, 2022
fffbadb
enforce empty during webhook call if exclude is being used
mickmister Jul 25, 2022
a71d576
add plugin setting to toggle security level functionality
mickmister Aug 18, 2022
ce03bff
fix tests
mickmister Sep 15, 2022
90220b1
Merge branch 'master' into validate-subscription
mattermod Sep 29, 2022
c64f1b5
add form validation for securitylevel field
mickmister Dec 6, 2022
6568e35
change plugin setting to bool
mickmister Dec 6, 2022
a1de436
change plugin setting description
mickmister Dec 6, 2022
4af547d
WIP
mickmister Feb 2, 2023
938b32a
Merge branch 'master' into validate-subscription
mickmister Feb 2, 2023
1d5d8d0
show message about security level under JQL query
mickmister Feb 2, 2023
7811c97
extract if statement into function
mickmister Feb 23, 2023
d362a7f
fix issues from ci
mickmister Mar 17, 2023
da01066
Merge branch 'master' into validate-subscription
mickmister Mar 17, 2023
cd84020
fix merge issues and lint
mickmister Mar 17, 2023
af7e1af
Merge branch 'master' into validate-subscription
mickmister Mar 30, 2023
9239901
Merge branch 'master' into validate-subscription
mickmister Apr 28, 2023
a84d6af
Merge branch 'master' into validate-subscription
mattermost-build May 1, 2023
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: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ endif
## Ensures NPM dependencies are installed without having to run this all the time.
webapp/.npminstall:
ifneq ($(HAS_WEBAPP),)
cd webapp && $(NPM) install
cd webapp && $(NPM) install --verbose
touch $@
endif

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/rbriski/atlassian-jwt v0.0.0-20180307182949-7bb4ae273058
github.com/stretchr/testify v1.7.0
github.com/trivago/tgo v1.0.1
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
)

Expand Down
13 changes: 7 additions & 6 deletions server/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import (
)

const (
labelsField = "labels"
statusField = "status"
reporterField = "reporter"
priorityField = "priority"
descriptionField = "description"
resolutionField = "resolution"
labelsField = "labels"
statusField = "status"
reporterField = "reporter"
priorityField = "priority"
descriptionField = "description"
resolutionField = "resolution"
securityLevelField = "security"
)

func makePost(userID, channelID, message string) *model.Post {
Expand Down
23 changes: 23 additions & 0 deletions server/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/trivago/tgo/tcontainer"

"github.com/mattermost/mattermost-plugin-jira/server/utils/kvstore"
)
Expand Down Expand Up @@ -84,6 +85,28 @@ func (client testClient) AddComment(issueKey string, comment *jira.Comment) (*ji
return nil, nil
}

func (client testClient) GetCreateMeta(options *jira.GetQueryOptions) (*jira.CreateMetaInfo, error) {
return &jira.CreateMetaInfo{
Projects: []*jira.MetaProject{
{
IssueTypes: []*jira.MetaIssueType{
{
Fields: tcontainer.MarshalMap{
"security": tcontainer.MarshalMap{
"allowedValues": []interface{}{
tcontainer.MarshalMap{
"id": "10001",
},
},
},
},
},
},
},
},
}, nil
}

func TestTransitionJiraIssue(t *testing.T) {
api := &plugintest.API{}
api.On("SendEphemeralPost", mock.Anything, mock.Anything).Return(nil)
Expand Down
99 changes: 88 additions & 11 deletions server/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

jira "github.com/andygrunwald/go-jira"
"github.com/pkg/errors"
"github.com/trivago/tgo/tcontainer"
mickmister marked this conversation as resolved.
Show resolved Hide resolved

"github.com/mattermost/mattermost-server/v6/model"

Expand Down Expand Up @@ -141,37 +142,47 @@ func (p *Plugin) matchesSubsciptionFilters(wh *webhook, filters SubscriptionFilt
return false
}

if filters.IssueTypes.Len() != 0 && !filters.IssueTypes.ContainsAny(wh.JiraWebhook.Issue.Fields.Type.ID) {
issue := &wh.JiraWebhook.Issue

if filters.IssueTypes.Len() != 0 && !filters.IssueTypes.ContainsAny(issue.Fields.Type.ID) {
return false
}

if filters.Projects.Len() != 0 && !filters.Projects.ContainsAny(wh.JiraWebhook.Issue.Fields.Project.Key) {
if filters.Projects.Len() != 0 && !filters.Projects.ContainsAny(issue.Fields.Project.Key) {
return false
}

validFilter := true

containsSecurityLevelFilter := false
for _, field := range filters.Fields {
// Broken filter, values must be provided
if field.Inclusion == "" || (field.Values.Len() == 0 && field.Inclusion != FilterEmpty) {
validFilter = false
break
return false
}

if field.Key == securityLevelField {
containsSecurityLevelFilter = true
}

value := getIssueFieldValue(&wh.JiraWebhook.Issue, field.Key)
value := getIssueFieldValue(issue, field.Key)
containsAny := value.ContainsAny(field.Values.Elems()...)
containsAll := value.ContainsAll(field.Values.Elems()...)

if (field.Inclusion == FilterIncludeAny && !containsAny) ||
(field.Inclusion == FilterIncludeAll && !containsAll) ||
(field.Inclusion == FilterExcludeAny && containsAny) ||
(field.Inclusion == FilterEmpty && value.Len() > 0) {
validFilter = false
break
return false
}
}

return validFilter
if !containsSecurityLevelFilter {
securityLevel := getIssueFieldValue(issue, securityLevelField)
if securityLevel.Len() > 0 {
return false
}
}

return true
}

func (p *Plugin) getChannelsSubscribed(wh *webhook, instanceID types.ID) ([]ChannelSubscription, error) {
Expand Down Expand Up @@ -298,6 +309,36 @@ func (p *Plugin) validateSubscription(instanceID types.ID, subscription *Channel
return errors.New("please provide a project identifier")
}

projectKey := subscription.Filters.Projects.Elems()[0]

var securityLevels StringSet
for _, field := range subscription.Filters.Fields {
if field.Key != securityLevelField {
mickmister marked this conversation as resolved.
Show resolved Hide resolved
continue
}

if field.Inclusion == FilterEmpty {
continue
}

if field.Inclusion == FilterExcludeAny {
return errors.New("security level does not allow for an \"Exclude\" clause")
}

if securityLevels == nil {
securityLevelsArray, err := getSecurityLevelsForProject(client, projectKey)
if err != nil {
return errors.Wrap(err, "failed to get security levels for project")
}

securityLevels = NewStringSet(securityLevelsArray...)
}

if !securityLevels.ContainsAll(field.Values.Elems()...) {
return errors.New("invalid access to security level")
}
}

channelID := subscription.ChannelID
subs, err := p.getSubscriptionsForChannel(instanceID, channelID)
if err != nil {
Expand All @@ -310,7 +351,6 @@ func (p *Plugin) validateSubscription(instanceID types.ID, subscription *Channel
}
}

projectKey := subscription.Filters.Projects.Elems()[0]
_, err = client.GetProject(projectKey)
if err != nil {
return errors.WithMessagef(err, "failed to get project %q", projectKey)
Expand All @@ -319,6 +359,43 @@ func (p *Plugin) validateSubscription(instanceID types.ID, subscription *Channel
return nil
}

func getSecurityLevelsForProject(client Client, projectKey string) ([]string, error) {
createMeta, err := client.GetCreateMeta(&jira.GetQueryOptions{
Expand: "projects.issuetypes.fields",
ProjectKeys: projectKey,
})
if err != nil {
return nil, errors.Wrap(err, "error fetching user security levels")
}

securityLevels1, err := createMeta.Projects[0].IssueTypes[0].Fields.MarshalMap(securityLevelField)
if err != nil {
return nil, errors.Wrap(err, "error parsing user security levels")
}

allowed, ok := securityLevels1["allowedValues"].([]interface{})
if !ok {
return nil, errors.New("error parsing user security levels: failed to type assertion on array")
}

out := []string{}
for _, level := range allowed {
value, ok := level.(tcontainer.MarshalMap)
if !ok {
return nil, errors.New("error parsing user security levels: failed to type assertion on map")
}

id, ok := value["id"].(string)
if !ok {
return nil, errors.New("error parsing user security levels: failed to type assertion on string")
}

out = append(out, id)
}

return out, nil
}

func (p *Plugin) editChannelSubscription(instanceID types.ID, modifiedSubscription *ChannelSubscription, client Client) error {
subKey := keyWithInstanceID(instanceID, JiraSubscriptionsKey)
return p.atomicModify(subKey, func(initialBytes []byte) ([]byte, error) {
Expand Down
Loading