Skip to content

Commit

Permalink
Address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
atburke committed Apr 3, 2024
1 parent 76246a4 commit 594a1b9
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 68 deletions.
8 changes: 3 additions & 5 deletions lib/services/access_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package services

import (
"context"
"fmt"
"slices"
"sort"
"strings"
Expand Down Expand Up @@ -204,7 +203,7 @@ type DynamicAccessOracle interface {
GetAccessCapabilities(ctx context.Context, req types.AccessCapabilitiesRequest) (*types.AccessCapabilities, error)
}

func canFilterRequestableRolesByResource(a RequestValidatorGetter, req types.AccessCapabilitiesRequest) (bool, error) {
func shouldFilterRequestableRolesByResource(a RequestValidatorGetter, req types.AccessCapabilitiesRequest) (bool, error) {
if !req.FilterRequestableRolesByResource {
return false, nil
}
Expand All @@ -225,12 +224,11 @@ func canFilterRequestableRolesByResource(a RequestValidatorGetter, req types.Acc
// CalculateAccessCapabilities aggregates the requested capabilities using the supplied getter
// to load relevant resources.
func CalculateAccessCapabilities(ctx context.Context, clock clockwork.Clock, clt RequestValidatorGetter, identity tlsca.Identity, req types.AccessCapabilitiesRequest) (*types.AccessCapabilities, error) {
fmt.Printf("CalculateAccessCapabilities %+v\n", req)
canFilter, err := canFilterRequestableRolesByResource(clt, req)
shouldFilter, err := shouldFilterRequestableRolesByResource(clt, req)
if err != nil {
return nil, trace.Wrap(err)
}
if !canFilter && req.FilterRequestableRolesByResource {
if !shouldFilter && req.FilterRequestableRolesByResource {
req.ResourceIDs = nil
}

Expand Down
29 changes: 9 additions & 20 deletions tool/tsh/common/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,11 @@ import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keypaths"
"github.com/gravitational/teleport/lib/asciitable"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/client"
kubeclient "github.com/gravitational/teleport/lib/client/kube"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/kube/kubeconfig"
kubeutils "github.com/gravitational/teleport/lib/kube/utils"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/tool/common"
Expand Down Expand Up @@ -1595,25 +1593,16 @@ func (c *kubeLoginCommand) accessRequestForKubeCluster(ctx context.Context, cf *
}
}

// Roles to request will be automatically determined on the backend.
req, err := services.NewAccessRequestWithResources(tc.Username, nil /* roles */, requestResourceIDs)
if err != nil {
return nil, trace.Wrap(err)
}

// Set the DryRun flag and send the request to auth for full validation. If
// the user has no search_as_roles or is not allowed to connect to the Kube cluster
// we will get an error here.
req.SetDryRun(true)
req.SetRequestReason("Dry run, this request will not be created. If you see this, there is a bug.")
if err := tc.WithRootClusterClient(ctx, func(clt auth.ClientI) error {
req, err = clt.CreateAccessRequestV2(ctx, req)
return trace.Wrap(err)
}); err != nil {
return nil, trace.Wrap(err)
var req types.AccessRequest
switch cf.RequestMode {
case accessRequestModeResource:
req, err = getAutoResourceRequest(ctx, tc, requestResourceIDs)
if err != nil {
return nil, trace.Wrap(err)
}
default:
return nil, trace.BadParameter("unexpected request mode %q", cf.RequestMode)
}
req.SetDryRun(false)
req.SetRequestReason("")

return req, nil
}
Expand Down
108 changes: 65 additions & 43 deletions tool/tsh/common/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -3256,50 +3256,25 @@ func accessRequestForSSH(ctx context.Context, cf *CLIConf, tc *client.TeleportCl
}}
switch cf.RequestMode {
case accessRequestModeRole:
rootClient, err := clt.ConnectToRootCluster(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
resp, err := rootClient.GetAccessCapabilities(ctx, types.AccessCapabilitiesRequest{
RequestableRoles: true,
ResourceIDs: requestResourceIDs,
Login: tc.HostLogin,
FilterRequestableRolesByResource: true,
})
if err != nil {
return nil, trace.Wrap(err)
}

var selectedRole string
switch len(resp.RequestableRoles) {
case 0:
return nil, trace.AccessDenied("no roles to request that would grant access")
case 1:
selectedRole = resp.RequestableRoles[0]
default:
selectedRole, err = prompt.PickOne(
ctx, os.Stdout, prompt.NewContextReader(os.Stdin),
"Choose role to request",
resp.RequestableRoles)
if err != nil {
return nil, trace.Wrap(err)
}
}

req, err = services.NewAccessRequestWithResources(tc.Username, []string{selectedRole}, nil /* requestResources */)
if err != nil {
return nil, trace.Wrap(err)
}
req, err = getAutoRoleRequest(ctx, clt, requestResourceIDs, tc)
case accessRequestModeResource:
// Roles to request will be automatically determined on the backend.
req, err = services.NewAccessRequestWithResources(tc.Username, nil /* roles */, requestResourceIDs)
if err != nil {
return nil, trace.Wrap(err)
}
req, err = getAutoResourceRequest(ctx, tc, requestResourceIDs)
default:
return nil, trace.BadParameter("unexpected request mode %q", cf.RequestMode)
}
if err != nil {
return nil, trace.Wrap(err)
}

return req, nil
}

func getAutoResourceRequest(ctx context.Context, tc *client.TeleportClient, requestResourceIDs []types.ResourceID) (types.AccessRequest, error) {
// Roles to request will be automatically determined on the backend.
req, err := services.NewAccessRequestWithResources(tc.Username, nil, requestResourceIDs)
if err != nil {
return nil, trace.Wrap(err)
}
req.SetLoginHint(tc.HostLogin)

// Set the DryRun flag and send the request to auth for full validation. If
Expand All @@ -3315,7 +3290,28 @@ func accessRequestForSSH(ctx context.Context, cf *CLIConf, tc *client.TeleportCl
}
req.SetDryRun(false)
req.SetRequestReason("")
return req, nil
}

func getAutoRoleRequest(ctx context.Context, clt *client.ClusterClient, requestResourceIDs []types.ResourceID, tc *client.TeleportClient) (types.AccessRequest, error) {
rootClient, err := clt.ConnectToRootCluster(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
resp, err := rootClient.GetAccessCapabilities(ctx, types.AccessCapabilitiesRequest{
RequestableRoles: true,
ResourceIDs: requestResourceIDs,
Login: tc.HostLogin,
FilterRequestableRolesByResource: true,
})
if err != nil {
return nil, trace.Wrap(err)
}

req, err := services.NewAccessRequestWithResources(tc.Username, resp.RequestableRoles, nil)
if err != nil {
return nil, trace.Wrap(err)
}
return req, nil
}

Expand All @@ -3334,10 +3330,6 @@ func retryWithAccessRequest(
return trace.Wrap(origErr)
}

// Print and log the original AccessDenied error.
fmt.Fprintln(os.Stderr, utils.UserMessageFromError(origErr))
fmt.Fprintf(os.Stdout, "You do not currently have access to %q, attempting to request access.\n\n", resource)

// Try to construct an access request for this resource.
req, err := onAccessRequestCreator(cf.Context, cf, tc)
if err != nil {
Expand All @@ -3349,6 +3341,13 @@ func retryWithAccessRequest(
return trace.Wrap(origErr)
}

// Print and log the original AccessDenied error.
fmt.Fprintln(os.Stderr, utils.UserMessageFromError(origErr))
fmt.Fprintf(os.Stdout, "You do not currently have access to %q, attempting to request access.\n\n", resource)
if err := promptUserForAccessRequestDetails(cf, req); err != nil {
return trace.Wrap(err)
}

if err := setAccessRequestReason(cf, req); err != nil {
return trace.Wrap(err)
}
Expand All @@ -3362,6 +3361,29 @@ func retryWithAccessRequest(
return trace.Wrap(fn())
}

func promptUserForAccessRequestDetails(cf *CLIConf, req types.AccessRequest) error {
if cf.RequestMode != accessRequestModeRole {
return nil
}
// If this is a role access request, ensure that it only has one role.
switch len(req.GetRoles()) {
case 0:
return trace.AccessDenied("no roles to request that would grant access")
case 1:
return nil
default:
selectedRole, err := prompt.PickOne(
cf.Context, os.Stdout, prompt.NewContextReader(os.Stdin),
"Choose role to request",
req.GetRoles())
if err != nil {
return trace.Wrap(err)
}
req.SetRoles([]string{selectedRole})
}
return nil
}

func setAccessRequestReason(cf *CLIConf, req types.AccessRequest) (err error) {
requestReason := cf.RequestReason
if requestReason == "" {
Expand Down

0 comments on commit 594a1b9

Please sign in to comment.