Skip to content

Commit

Permalink
[v13] Changes to the Jira plugin required to run as a hosted integrat…
Browse files Browse the repository at this point in the history
…ion (#30040)

Backports #29729

Changelog: none
  • Loading branch information
tcsc committed Aug 5, 2023
1 parent f3f3fb6 commit af1d59a
Show file tree
Hide file tree
Showing 10 changed files with 2,280 additions and 1,608 deletions.
16 changes: 16 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5320,6 +5320,8 @@ message PluginSpecV1 {
PluginPagerDutySettings pager_duty = 6;
// Settings for the Mattermost plugin
PluginMattermostSettings mattermost = 7;
// Settings for the JIRA plugin
PluginJiraSettings jira = 8;
}
}

Expand Down Expand Up @@ -5358,6 +5360,20 @@ message PluginPagerDutySettings {
string api_endpoint = 2;
}

message PluginJiraSettings {
option (gogoproto.equal) = true;

// ServerURL is the address of the target JIRA Server instance.
string server_url = 1;

// ProjectKey is the key of the Jira project that will receive
// notifications and issues from the plugin.
string project_key = 2;

// IssueType is the type of Jira Issue that the plugin will create
string issue_type = 3;
}

// Defines settings for the OpenAI plugin. Currently there are no settings.
message PluginOpenAISettings {
option (gogoproto.equal) = true;
Expand Down
43 changes: 43 additions & 0 deletions api/types/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
PluginTypeOkta = "okta"
// PluginTypeJamf is the Jamf MDM plugin
PluginTypeJamf = "jamf"
// PluginTypeJira is the Jira access plugin
PluginTypeJira = "jira"
// PluginTypeOpsgenie is the Opsgenie access request plugin
PluginTypeOpsgenie = "opsgenie"
// PluginTypePagerDuty is the PagerDuty access plugin
Expand Down Expand Up @@ -183,6 +185,29 @@ func (p *PluginV1) CheckAndSetDefaults() error {
if len(staticCreds.Labels) == 0 {
return trace.BadParameter("labels must be specified")
}

case *PluginSpecV1_Jira:
if settings.Jira == nil {
return trace.BadParameter("missing Jira settings")
}

if err := settings.Jira.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}

if p.Credentials == nil {
return trace.BadParameter("credentials must be set")
}

staticCreds := p.Credentials.GetStaticCredentialsRef()
if staticCreds == nil {
return trace.BadParameter("jira plugin must be used with the static credentials ref type")
}

if len(staticCreds.Labels) == 0 {
return trace.BadParameter("labels must be specified")
}

case *PluginSpecV1_Okta:
// Check settings.
if settings.Okta == nil {
Expand Down Expand Up @@ -345,6 +370,8 @@ func (p *PluginV1) GetType() PluginType {
return PluginTypeOkta
case *PluginSpecV1_Jamf:
return PluginTypeJamf
case *PluginSpecV1_Jira:
return PluginTypeJira
case *PluginSpecV1_Opsgenie:
return PluginTypeOpsgenie
case *PluginSpecV1_PagerDuty:
Expand Down Expand Up @@ -383,6 +410,22 @@ func (s *PluginJamfSettings) CheckAndSetDefaults() error {
return nil
}

func (s *PluginJiraSettings) CheckAndSetDefaults() error {
if s.ServerUrl == "" {
return trace.BadParameter("Jira server URL must be set")
}

if s.ProjectKey == "" {
return trace.BadParameter("Jira project key must be set")
}

if s.IssueType == "" {
return trace.BadParameter("Jira issue type must be set")
}

return nil
}

// CheckAndSetDefaults validates and set the default values
func (s *PluginOpsgenieAccessSettings) CheckAndSetDefaults() error {
if s.ApiEndpoint == "" {
Expand Down
108 changes: 108 additions & 0 deletions api/types/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,111 @@ func TestPluginMattermostValidation(t *testing.T) {
})
}
}

func requireBadParameterError(t require.TestingT, err error, args ...any) {
if tt, ok := t.(*testing.T); ok {
tt.Helper()
}
require.Error(t, err)
require.True(t, trace.IsBadParameter(err), args...)
}

func reqireNamedBadParameterError(name string) require.ErrorAssertionFunc {
return func(t require.TestingT, err error, args ...any) {
if tt, ok := t.(*testing.T); ok {
tt.Helper()
}
require.ErrorContains(t, err, name)
require.True(t, trace.IsBadParameter(err))
}
}

func TestPluginJiraValidation(t *testing.T) {
validSettings := func() *PluginSpecV1_Jira {
return &PluginSpecV1_Jira{
&PluginJiraSettings{
ServerUrl: "https://example.com",
ProjectKey: "PRJ",
IssueType: "Task",
},
}
}
validCreds := func() *PluginCredentialsV1 {
return &PluginCredentialsV1{
Credentials: &PluginCredentialsV1_StaticCredentialsRef{
&PluginStaticCredentialsRef{
Labels: map[string]string{
"jira/address": "https://jira.example.com",
"jira/project": "PRJ",
"jira/issueType": "Task",
},
},
},
}
}

testCases := []struct {
name string
mutateSettings func(*PluginSpecV1_Jira)
mutateCreds func(*PluginCredentialsV1)
assertErr require.ErrorAssertionFunc
}{
{
name: "Valid",
assertErr: require.NoError,
}, {
name: "Missing Settings",
mutateSettings: func(s *PluginSpecV1_Jira) { s.Jira = nil },
assertErr: requireBadParameterError,
}, {
name: "Missing Server URL",
mutateSettings: func(s *PluginSpecV1_Jira) { s.Jira.ServerUrl = "" },
assertErr: reqireNamedBadParameterError("server URL"),
}, {
name: "Missing Project Key",
mutateSettings: func(s *PluginSpecV1_Jira) { s.Jira.ProjectKey = "" },
assertErr: reqireNamedBadParameterError("project key"),
}, {
name: "Missing Issue Type",
mutateSettings: func(s *PluginSpecV1_Jira) { s.Jira.IssueType = "" },
assertErr: reqireNamedBadParameterError("issue type"),
}, {
name: "Missing Credentials",
mutateCreds: func(c *PluginCredentialsV1) { c.Credentials = nil },
assertErr: requireBadParameterError,
}, {
name: "Missing Credential Labels",
mutateCreds: func(c *PluginCredentialsV1) {
c.Credentials.(*PluginCredentialsV1_StaticCredentialsRef).
StaticCredentialsRef.
Labels = map[string]string{}
},
assertErr: reqireNamedBadParameterError("labels"),
}, {
name: "Invalid Credential Type",
mutateCreds: func(c *PluginCredentialsV1) {
c.Credentials = &PluginCredentialsV1_Oauth2AccessToken{}
},
assertErr: reqireNamedBadParameterError("static credentials"),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
settings := validSettings()
if tc.mutateSettings != nil {
tc.mutateSettings(settings)
}

creds := validCreds()
if tc.mutateCreds != nil {
tc.mutateCreds(creds)
}

plugin := NewPluginV1(Metadata{Name: "uut"}, PluginSpecV1{
Settings: settings,
}, creds)
tc.assertErr(t, plugin.CheckAndSetDefaults())
})
}
}

0 comments on commit af1d59a

Please sign in to comment.