Skip to content

Commit

Permalink
[v14] Add initial servicenow plugin (#32131)
Browse files Browse the repository at this point in the history
* Add initial servicenow plugin (#31024)

* Use createAccessrequestV2 for servicenow tests

---------

Co-authored-by: Roman Tkachenko <roman@goteleport.com>
  • Loading branch information
EdwardDowling and r0mant committed Sep 21, 2023
1 parent 584d898 commit df6dc24
Show file tree
Hide file tree
Showing 17 changed files with 3,821 additions and 1,631 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 @@ -5336,6 +5336,8 @@ message PluginSpecV1 {
PluginJiraSettings jira = 8;
// Settings for the Discord plugin
PluginDiscordSettings discord = 9;
// Settings for the ServiceNow plugin
PluginServiceNowSettings serviceNow = 10;
}
}

Expand All @@ -5361,6 +5363,20 @@ message PluginOpsgenieAccessSettings {
string api_endpoint = 5;
}

// PluginServiceNowSettings are the settings for the serviceNow plugin
message PluginServiceNowSettings {
option (gogoproto.equal) = true;

// ApiEndpoint is the ServiceNow API endpoint.
string api_endpoint = 1 [(gogoproto.jsontag) = "api_endpoint,omitempty"];
// Username is the ServiceNow API username.
string username = 2 [(gogoproto.jsontag) = "username,omitempty"];
// Password is the ServiceNow API password.
string password = 3 [(gogoproto.jsontag) = "password,omitempty"];
// CloseCode is the close code that ServiceNow incidents will use.
string close_code = 4 [(gogoproto.jsontag) = "close_code,omitempty"];
}

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

Expand Down
25 changes: 25 additions & 0 deletions api/types/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type PluginType string
const (
// PluginTypeUnknown is returned when no plugin type matches.
PluginTypeUnknown PluginType = ""
// PluginTypeServiceNow is the Servicenow access request plugin
PluginTypeServiceNow = "servicenow"
// PluginTypeSlack is the Slack access request plugin
PluginTypeSlack = "slack"
// PluginTypeOpenAI is the OpenAI plugin
Expand Down Expand Up @@ -249,6 +251,18 @@ func (p *PluginV1) CheckAndSetDefaults() error {
if staticCreds == nil {
return trace.BadParameter("Discord plugin must be used with the static credentials ref type")
}
case *PluginSpecV1_ServiceNow:
if settings.ServiceNow == nil {
return trace.BadParameter("missing ServiceNow settings")
}
if err := settings.ServiceNow.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}

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

default:
return trace.BadParameter("settings are not set or have an unknown type")
Expand Down Expand Up @@ -406,6 +420,8 @@ func (p *PluginV1) GetType() PluginType {
return PluginTypeMattermost
case *PluginSpecV1_Discord:
return PluginTypeDiscord
case *PluginSpecV1_ServiceNow:
return PluginTypeServiceNow
default:
return PluginTypeUnknown
}
Expand Down Expand Up @@ -516,6 +532,15 @@ func (c *PluginDiscordSettings) CheckAndSetDefaults() error {
return nil
}

// CheckAndSetDefaults checks that the required fields for the servicenow plugin are set.
func (c *PluginServiceNowSettings) CheckAndSetDefaults() error {
if c.ApiEndpoint == "" {
return trace.BadParameter("API endpoint must be set")
}

return nil
}

// CheckAndSetDefaults validates and set the default values
func (c *PluginOAuth2AccessTokenCredentials) CheckAndSetDefaults() error {
if c.AccessToken == "" {
Expand Down
3,652 changes: 2,054 additions & 1,598 deletions api/types/types.pb.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion integrations/access/common/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func (a *BaseApp) getMessageRecipients(ctx context.Context, req types.AccessRequ
recipientSet := NewRecipientSet()

switch a.Conf.GetPluginType() {
case types.PluginTypeOpsgenie:
case types.PluginTypeOpsgenie, types.PluginTypeServiceNow:
if recipients, ok := req.GetSystemAnnotations()[types.TeleportNamespace+types.ReqAnnotationSchedulesLabel]; ok {
for _, recipient := range recipients {
rec, err := a.bot.FetchRecipient(ctx, recipient)
Expand Down
13 changes: 11 additions & 2 deletions integrations/access/common/plugindata.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"fmt"
"strings"

"github.com/gravitational/trace"

"github.com/gravitational/teleport/integrations/lib/plugindata"
)

Expand All @@ -44,7 +46,11 @@ type SentMessages []MessageData
func DecodePluginData(dataMap map[string]string) (GenericPluginData, error) {
data := GenericPluginData{}

data.AccessRequestData = plugindata.DecodeAccessRequestData(dataMap)
var err error
data.AccessRequestData, err = plugindata.DecodeAccessRequestData(dataMap)
if err != nil {
return GenericPluginData{}, trace.Wrap(err)
}

if channelID, timestamp := dataMap["channel_id"], dataMap["timestamp"]; channelID != "" && timestamp != "" {
data.SentMessages = append(data.SentMessages, MessageData{ChannelID: channelID, MessageID: timestamp})
Expand All @@ -62,7 +68,10 @@ func DecodePluginData(dataMap map[string]string) (GenericPluginData, error) {

// EncodePluginData serializes a GenericPluginData struct into a string map.
func EncodePluginData(data GenericPluginData) (map[string]string, error) {
result := plugindata.EncodeAccessRequestData(data.AccessRequestData)
result, err := plugindata.EncodeAccessRequestData(data.AccessRequestData)
if err != nil {
return nil, trace.Wrap(err)
}

var encodedMessages []string
for _, msg := range data.SentMessages {
Expand Down
2 changes: 2 additions & 0 deletions integrations/access/common/recipient.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
const (
// RecipientKindSchedule shows a recipient is a schedule.
RecipientKindSchedule = "schedule"
// RecipientKindRotation shows a recipient is a rotation.
RecipientKindRotation = "rota"
)

// RawRecipientsMap is a mapping of roles to recipient(s).
Expand Down

0 comments on commit df6dc24

Please sign in to comment.