diff --git a/integrations/access/accessrequest/plugindata_test.go b/integrations/access/accessrequest/plugindata_test.go index 3234763dd541a..68ed073d90e9d 100644 --- a/integrations/access/accessrequest/plugindata_test.go +++ b/integrations/access/accessrequest/plugindata_test.go @@ -20,32 +20,39 @@ package accessrequest import ( "testing" + "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/gravitational/teleport/integrations/lib/plugindata" ) -var samplePluginData = PluginData{ - AccessRequestData: plugindata.AccessRequestData{ - User: "user-foo", - Roles: []string{"role-foo", "role-bar"}, - Resources: []string{"cluster-a/node/foo", "cluster-a/node/bar"}, - RequestReason: "foo reason", - ReviewsCount: 3, - ResolutionTag: plugindata.ResolvedApproved, - ResolutionReason: "foo ok", - }, - SentMessages: SentMessages{ - {ChannelID: "CHANNEL1", MessageID: "0000001"}, - {ChannelID: "CHANNEL2", MessageID: "0000002"}, - }, +func getSamplePluginData(t *testing.T) PluginData { + maxDuration, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + require.NoError(t, err) + return PluginData{ + AccessRequestData: plugindata.AccessRequestData{ + User: "user-foo", + Roles: []string{"role-foo", "role-bar"}, + Resources: []string{"cluster-a/node/foo", "cluster-a/node/bar"}, + RequestReason: "foo reason", + ReviewsCount: 3, + ResolutionTag: plugindata.ResolvedApproved, + ResolutionReason: "foo ok", + MaxDuration: &maxDuration, + }, + SentMessages: SentMessages{ + {ChannelID: "CHANNEL1", MessageID: "0000001"}, + {ChannelID: "CHANNEL2", MessageID: "0000002"}, + }, + } } func TestEncodePluginData(t *testing.T) { - dataMap, err := EncodePluginData(samplePluginData) + dataMap, err := EncodePluginData(getSamplePluginData(t)) assert.NoError(t, err) - assert.Len(t, dataMap, 8) + assert.Len(t, dataMap, 9) assert.Equal(t, "user-foo", dataMap["user"]) assert.Equal(t, "role-foo,role-bar", dataMap["roles"]) assert.Equal(t, `["cluster-a/node/foo","cluster-a/node/bar"]`, dataMap["resources"]) @@ -54,6 +61,7 @@ func TestEncodePluginData(t *testing.T) { assert.Equal(t, "APPROVED", dataMap["resolution"]) assert.Equal(t, "foo ok", dataMap["resolve_reason"]) assert.Equal(t, "CHANNEL1/0000001,CHANNEL2/0000002", dataMap["messages"]) + assert.Equal(t, "2006-01-02T15:04:05Z", dataMap["max_duration"]) } func TestDecodePluginData(t *testing.T) { @@ -66,9 +74,10 @@ func TestDecodePluginData(t *testing.T) { "resolution": "APPROVED", "resolve_reason": "foo ok", "messages": "CHANNEL1/0000001,CHANNEL2/0000002", + "max_duration": "2006-01-02T15:04:05Z", }) assert.NoError(t, err) - assert.Equal(t, samplePluginData, pluginData) + assert.Equal(t, getSamplePluginData(t), pluginData) } func TestEncodeEmptyPluginData(t *testing.T) { diff --git a/integrations/access/msteams/app.go b/integrations/access/msteams/app.go index 4e9c577b34c95..2499f5c1e07e8 100644 --- a/integrations/access/msteams/app.go +++ b/integrations/access/msteams/app.go @@ -298,10 +298,12 @@ func (a *App) onWatcherEvent(ctx context.Context, event types.Event) error { // onPendingRequest is called when there's a new request or a review func (a *App) onPendingRequest(ctx context.Context, req types.AccessRequest) error { id := req.GetName() + maxDuration := req.GetMaxDuration() data := pd.AccessRequestData{ User: req.GetUser(), Roles: req.GetRoles(), RequestReason: req.GetRequestReason(), + MaxDuration: &maxDuration, } log := a.log.With("request_id", id) diff --git a/integrations/access/msteams/card.go b/integrations/access/msteams/card.go index 93945d35cca29..dce9b8ebc0c96 100644 --- a/integrations/access/msteams/card.go +++ b/integrations/access/msteams/card.go @@ -52,6 +52,7 @@ func BuildCard(id string, webProxyURL *url.URL, clusterName string, data plugind {Title: "Cluster", Value: clusterName}, {Title: "User", Value: data.User}, {Title: "Role(s)", Value: strings.Join(data.Roles, ", ")}, + {Title: "Max Duration", Value: data.MaxDuration.String()}, } if data.RequestReason != "" { diff --git a/integrations/access/msteams/testlib/message_parser.go b/integrations/access/msteams/testlib/message_parser.go index d903ea68e3259..1c27649d75387 100644 --- a/integrations/access/msteams/testlib/message_parser.go +++ b/integrations/access/msteams/testlib/message_parser.go @@ -52,7 +52,7 @@ func (msg testTeamsMessage) checkStatusApproved(t *testing.T, reason string) { require.GreaterOrEqual(t, len(body), 3) require.Equal(t, "✅", body[1].Columns[0].Items[0].Text) require.Equal(t, "APPROVED", body[1].Columns[1].Items[0].Text) - require.Equal(t, reason, body[2].Facts[4].Value) + require.Equal(t, reason, body[2].Facts[5].Value) } func (msg testTeamsMessage) checkStatusDenied(t *testing.T, reason string) { @@ -61,7 +61,7 @@ func (msg testTeamsMessage) checkStatusDenied(t *testing.T, reason string) { require.GreaterOrEqual(t, len(body), 3) require.Equal(t, "❌", body[1].Columns[0].Items[0].Text) require.Equal(t, "DENIED", body[1].Columns[1].Items[0].Text) - require.Equal(t, reason, body[2].Facts[4].Value) + require.Equal(t, reason, body[2].Facts[5].Value) } func (msg testTeamsMessage) checkStatusExpired(t *testing.T) { diff --git a/integrations/lib/plugindata/access_request.go b/integrations/lib/plugindata/access_request.go index fd70d44927144..2217357d356f2 100644 --- a/integrations/lib/plugindata/access_request.go +++ b/integrations/lib/plugindata/access_request.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/gravitational/trace" ) @@ -49,6 +50,7 @@ type AccessRequestData struct { Resources []string SuggestedReviewers []string LoginsByRole map[string][]string + MaxDuration *time.Time } // DecodeAccessRequestData deserializes a string map to PluginData struct. @@ -63,6 +65,15 @@ func DecodeAccessRequestData(dataMap map[string]string) (data AccessRequestData, } data.ResolutionTag = ResolutionTag(dataMap["resolution"]) data.ResolutionReason = dataMap["resolve_reason"] + if str := dataMap["max_duration"]; str != "" { + var maxDuration time.Time + maxDuration, err = time.Parse(time.RFC3339, str) + if err != nil { + err = trace.Wrap(err) + return + } + data.MaxDuration = &maxDuration + } if str, ok := dataMap["resources"]; ok { err = json.Unmarshal([]byte(str), &data.Resources) @@ -131,6 +142,9 @@ func EncodeAccessRequestData(data AccessRequestData) (map[string]string, error) result["reviews_count"] = reviewsCountStr result["resolution"] = string(data.ResolutionTag) result["resolve_reason"] = data.ResolutionReason + if data.MaxDuration != nil { + result["max_duration"] = data.MaxDuration.Format(time.RFC3339) + } if len(data.SystemAnnotations) != 0 { annotaions, err := json.Marshal(data.SystemAnnotations)