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
44 changes: 44 additions & 0 deletions tool/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,50 @@ func FormatResourceName(r types.ResourceWithLabels, verbose bool) string {
return r.GetName()
}

// FormatResourceAccessID returns the provided ResourceAccessID in its string form,
// appending constraints when present.
func FormatResourceAccessID(rid types.ResourceAccessID) string {
resourceIDString := types.ResourceIDToString(rid.GetResourceID())
constraintsString := ""

if c := rid.GetConstraints(); c != nil && c.GetDetails() != nil {
switch d := c.GetDetails().(type) {
case *types.ResourceConstraints_AwsConsole:
if d.AwsConsole == nil {
break
}
constraintsString = fmt.Sprintf("role_arns=%s", strings.Join(d.AwsConsole.RoleArns, ","))
}
}

if constraintsString != "" {
return fmt.Sprintf("%s (%s)", resourceIDString, constraintsString)
}

return resourceIDString
}

// FormatResourceAccessIDs returns the provided ResourceAccessIDs in string form,
// appending constraints to each when present. Uses JSON.Marshal to
// ensure proper handling for any IDs containing commas/quotes.
func FormatResourceAccessIDs(rids []types.ResourceAccessID) (string, error) {
out := ""

if len(rids) > 0 {
resourceIDStrings := make([]string, 0, len(rids))
for _, rid := range rids {
resourceIDStrings = append(resourceIDStrings, FormatResourceAccessID(rid))
}
bytes, err := json.Marshal(resourceIDStrings)
if err != nil {
return "", trace.Wrap(err, "failed to marshal ResourceAccessIDs")
}
out = string(bytes)
}

return out, nil
}

// GetDiscoveredResourceName returns the resource original name discovered in
// the cloud by the Teleport Discovery Service.
func GetDiscoveredResourceName(r types.ResourceWithLabels) (discoveredName string, ok bool) {
Expand Down
110 changes: 110 additions & 0 deletions tool/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package common
import (
"bytes"
"context"
"fmt"
"maps"
"testing"

Expand Down Expand Up @@ -145,3 +146,112 @@ func TestFormatLabels(t *testing.T) {
})
}
}

func TestFormatResourceAccessIDs(t *testing.T) {
Comment thread
kiosion marked this conversation as resolved.
t.Parallel()

const (
ARN1 = "arn:aws:iam::123456789012:role/Role1"
ARN2 = "arn:aws:iam::123456789012:role/Role2"
)

rids := []types.ResourceAccessID{
{
Id: types.ResourceID{
Kind: types.KindApp,
Name: "aws_console",
ClusterName: "cluster",
},
Constraints: &types.ResourceConstraints{
Version: types.V1,
Details: &types.ResourceConstraints_AwsConsole{
AwsConsole: &types.AWSConsoleResourceConstraints{
RoleArns: []string{ARN1, ARN2},
},
},
},
},
{
Id: types.ResourceID{
Kind: types.KindNode,
Name: "ssh_server",
ClusterName: "cluster",
},
Constraints: nil,
},
}

t.Run("with aws_console constraints", func(t *testing.T) {
t.Parallel()

out, err := FormatResourceAccessIDs(rids)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("[\"/cluster/app/aws_console (role_arns=%s,%s)\",\"/cluster/node/ssh_server\"]", ARN1, ARN2), out)
})

t.Run("with empty aws_console constraints", func(t *testing.T) {
t.Parallel()

rid := types.ResourceAccessID{
Id: rids[0].Id,
Constraints: &types.ResourceConstraints{
Version: types.V1,
Details: &types.ResourceConstraints_AwsConsole{
AwsConsole: &types.AWSConsoleResourceConstraints{
RoleArns: []string{},
},
},
},
}

out, err := FormatResourceAccessIDs([]types.ResourceAccessID{rid})
require.NoError(t, err)
require.Equal(t, "[\"/cluster/app/aws_console (role_arns=)\"]", out)
})

t.Run("with empty or nil list", func(t *testing.T) {
t.Parallel()

out, err := FormatResourceAccessIDs(nil)
require.NoError(t, err)
require.Empty(t, out)

out, err = FormatResourceAccessIDs([]types.ResourceAccessID{})
require.NoError(t, err)
require.Empty(t, out)
})

t.Run("with namespace resource with SubResourceName", func(t *testing.T) {
t.Parallel()

rid := types.ResourceAccessID{
Id: types.ResourceID{
ClusterName: "cluster",
Kind: types.KindKubeNamespace,
Name: "my-kube-cluster",
SubResourceName: "production",
},
}

out, err := FormatResourceAccessIDs([]types.ResourceAccessID{rid})
require.NoError(t, err)
require.Equal(t, "[\"/cluster/namespace/my-kube-cluster/production\"]", out)
})

t.Run("with kube pod resource with SubResourceName", func(t *testing.T) {
t.Parallel()

rid := types.ResourceAccessID{
Id: types.ResourceID{
ClusterName: "cluster",
Kind: types.AccessRequestPrefixKindKubeNamespaced + "pods",
Name: "my-kube-cluster",
SubResourceName: "default/nginx",
},
}

out, err := FormatResourceAccessIDs([]types.ResourceAccessID{rid})
require.NoError(t, err)
require.Equal(t, "[\"/cluster/kube:ns:pods/my-kube-cluster/default/nginx\"]", out)
})
}
7 changes: 5 additions & 2 deletions tool/tctl/common/access_request_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/tool/common"
commonclient "github.com/gravitational/teleport/tool/tctl/common/client"
tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config"
)
Expand Down Expand Up @@ -487,7 +488,9 @@ func printRequestsOverview(reqs []types.AccessRequest, format string) error {
"Full reason was truncated, use the `tctl requests get` subcommand to view the full reason.",
)
for _, req := range reqs {
resourceIDsString, err := types.ResourceIDsToString(req.GetRequestedResourceIDs())
// This table isn't a comprehensive overview of each request; omit constraints on resources for brevity
// and only print their stringified ResourceIDs.
resourceIDsString, err := types.ResourceIDsToString(types.RiskyExtractResourceIDs(req.GetAllRequestedResourceIDs()))
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -518,7 +521,7 @@ func printRequestsDetailed(reqs []types.AccessRequest, format string) error {
switch format {
case teleport.Text:
for _, req := range reqs {
resourceIDsString, err := types.ResourceIDsToString(req.GetRequestedResourceIDs())
resourceIDsString, err := common.FormatResourceAccessIDs(req.GetAllRequestedResourceIDs())
if err != nil {
return trace.Wrap(err)
}
Expand Down
15 changes: 7 additions & 8 deletions tool/tsh/common/access_request.go
Comment thread
kiosion marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,9 @@ func printRequest(cf *CLIConf, req types.AccessRequest) error {
reviewers = strings.Join(r, ", ")
}

resourcesStr := ""
if resources := req.GetRequestedResourceIDs(); len(resources) > 0 {
var err error
if resourcesStr, err = types.ResourceIDsToString(resources); err != nil {
return trace.Wrap(err)
}
resourcesStr, err := common.FormatResourceAccessIDs(req.GetAllRequestedResourceIDs())
if err != nil {
return trace.Wrap(err)
}

table := asciitable.MakeHeadlessTable(2)
Expand All @@ -210,7 +207,7 @@ func printRequest(cf *CLIConf, req types.AccessRequest) error {
}
table.AddRow([]string{"Status:", req.GetState().String()})

_, err := table.AsBuffer().WriteTo(cf.Stdout())
_, err = table.AsBuffer().WriteTo(cf.Stdout())
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -363,7 +360,9 @@ func showRequestTable(cf *CLIConf, reqs []types.AccessRequest) error {
if now.After(req.GetAccessExpiry()) {
continue
}
resourceIDsString, err := types.ResourceIDsToString(req.GetRequestedResourceIDs())
// This table isn't a comprehensive overview of each request; omit constraints on resources for brevity
// and only print their stringified ResourceIDs.
resourceIDsString, err := types.ResourceIDsToString(types.RiskyExtractResourceIDs(req.GetAllRequestedResourceIDs()))
if err != nil {
return trace.Wrap(err)
}
Expand Down
Loading
Loading