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
19 changes: 19 additions & 0 deletions api/client/accesslist/accesslist.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ func (c *Client) GetAccessList(ctx context.Context, name string) (*accesslist.Ac
return accessList, trace.Wrap(err)
}

// GetAccessListsToReview returns access lists that the user needs to review.
func (c *Client) GetAccessListsToReview(ctx context.Context) ([]*accesslist.AccessList, error) {
resp, err := c.grpcClient.GetAccessListsToReview(ctx, &accesslistv1.GetAccessListsToReviewRequest{})
if err != nil {
return nil, trace.Wrap(err)
}

accessLists := make([]*accesslist.AccessList, len(resp.AccessLists))
for i, accessList := range resp.AccessLists {
var err error
accessLists[i], err = conv.FromProto(accessList)
if err != nil {
return nil, trace.Wrap(err)
}
}

return accessLists, nil
}

// UpsertAccessList creates or updates an access list resource.
func (c *Client) UpsertAccessList(ctx context.Context, accessList *accesslist.AccessList) (*accesslist.AccessList, error) {
resp, err := c.grpcClient.UpsertAccessList(ctx, &accesslistv1.UpsertAccessListRequest{
Expand Down
883 changes: 508 additions & 375 deletions api/gen/proto/go/teleport/accesslist/v1/accesslist_service.pb.go

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions api/proto/teleport/accesslist/v1/accesslist_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ service AccessListService {
rpc DeleteAccessList(DeleteAccessListRequest) returns (google.protobuf.Empty);
// DeleteAllAccessLists hard deletes all access lists.
rpc DeleteAllAccessLists(DeleteAllAccessListsRequest) returns (google.protobuf.Empty);
// GetAccessListsToReview will return access lists that need to be reviewed by the current user.
rpc GetAccessListsToReview(GetAccessListsToReviewRequest) returns (GetAccessListsToReviewResponse);

// ListAccessListMembers returns a paginated list of all access list members.
rpc ListAccessListMembers(ListAccessListMembersRequest) returns (ListAccessListMembersResponse);
Expand Down Expand Up @@ -113,6 +115,14 @@ message DeleteAccessListRequest {
// DeleteAllAccessListsRequest is the request for deleting all access lists.
message DeleteAllAccessListsRequest {}

// GetAccessListsToReviewRequest is the request for getting access lists that the current user needs to review.
message GetAccessListsToReviewRequest {}

// GetAccessListsToReviewResponse is the response for getting access lists that the current user needs to review.
message GetAccessListsToReviewResponse {
repeated AccessList access_lists = 1;
}

// ListAccessListMembersRequest is the request for getting paginated access list members.
message ListAccessListMembersRequest {
// page_size is the size of the page to request.
Expand Down
2 changes: 2 additions & 0 deletions lib/services/access_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type AccessListsGetter interface {
ListAccessLists(context.Context, int, string) ([]*accesslist.AccessList, string, error)
// GetAccessList returns the specified access list resource.
GetAccessList(context.Context, string) (*accesslist.AccessList, error)
// GetAccessListsToReview returns access lists that the user needs to review.
GetAccessListsToReview(context.Context) ([]*accesslist.AccessList, error)
}

// AccessLists defines an interface for managing AccessLists.
Expand Down
5 changes: 5 additions & 0 deletions lib/services/local/access_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ func (a *AccessListService) GetAccessList(ctx context.Context, name string) (*ac
return accessList, trace.Wrap(err)
}

// GetAccessListsToReview returns access lists that the user needs to review. This is not implemented in the local service.
func (a *AccessListService) GetAccessListsToReview(ctx context.Context) ([]*accesslist.AccessList, error) {
return nil, trace.NotImplemented("GetAccessListsToReview should not be called")
}

// UpsertAccessList creates or updates an access list resource.
func (a *AccessListService) UpsertAccessList(ctx context.Context, accessList *accesslist.AccessList) (*accesslist.AccessList, error) {
err := a.service.RunWhileLocked(ctx, lockName(accessList.GetName()), accessListLockTTL, func(ctx context.Context, _ backend.Backend) error {
Expand Down
57 changes: 45 additions & 12 deletions tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/accesslist"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/api/utils/keys"
Expand Down Expand Up @@ -1732,7 +1733,7 @@ func onLogin(cf *CLIConf) error {
return trace.Wrap(err)
}

return trace.Wrap(printProfiles(cf, profile, profiles))
return trace.Wrap(printLoginInformation(cf, profile, profiles, cf.getAccessListsToReview(tc)))

// if the proxy names match but nothing else is specified; show motd and update active profile and kube configs
case host(cf.Proxy) == host(profile.ProxyURL.Host) &&
Expand Down Expand Up @@ -1954,7 +1955,7 @@ func onLogin(cf *CLIConf) error {
}

// Print status to show information of the logged in user.
if err := printProfiles(cf, profile, profiles); err != nil {
if err := printLoginInformation(cf, profile, profiles, cf.getAccessListsToReview(tc)); err != nil {
return trace.Wrap(err)
}

Expand Down Expand Up @@ -4036,9 +4037,8 @@ func printStatus(debug bool, p *profileInfo, env map[string]string, isActive boo
fmt.Printf("\n")
}

// printProfiles displays the provided profile information
// to the user.
func printProfiles(cf *CLIConf, profile *client.ProfileStatus, profiles []*client.ProfileStatus) error {
// printLoginInformation displays the provided profile information to the user.
func printLoginInformation(cf *CLIConf, profile *client.ProfileStatus, profiles []*client.ProfileStatus, accessListsToReview []*accesslist.AccessList) error {
env := getTshEnv()
active, others := makeAllProfileInfo(profile, profiles, env)

Expand Down Expand Up @@ -4076,6 +4076,16 @@ func printProfiles(cf *CLIConf, profile *client.ProfileStatus, profiles []*clien
}
}

if len(accessListsToReview) > 0 {
fmt.Printf("Access lists that need to be reviewed:\n")
now := time.Now()

for _, accessList := range accessListsToReview {
fmt.Printf("\t%s (%s left to review)\n", accessList.GetName(), accessList.Spec.Audit.NextAuditDate.Sub(now).Round(time.Second).String())
}
fmt.Println()
}

return nil
}

Expand All @@ -4094,7 +4104,14 @@ func onStatus(cf *CLIConf) error {
return trace.Wrap(err)
}

if err := printProfiles(cf, profile, profiles); err != nil {
// make the teleport client and retrieve the certificate from the proxy:
tc, err := makeClient(cf)
if err != nil {
log.WithError(err).Warn("Failed to make client for retrieving cluster alerts.")
return trace.Wrap(err)
}

if err := printLoginInformation(cf, profile, profiles, cf.getAccessListsToReview(tc)); err != nil {
return trace.Wrap(err)
}

Expand All @@ -4107,12 +4124,6 @@ func onStatus(cf *CLIConf) error {
return trace.NotFound("Active profile expired.")
}

tc, err := makeClient(cf)
if err != nil {
log.WithError(err).Warn("Failed to make client for retrieving cluster alerts.")
return nil
}

if tc.PrivateKeyPolicy == keys.PrivateKeyPolicyHardwareKeyTouch {
log.Debug("Skipping cluster alerts due to Hardware Key Touch requirement.")
} else {
Expand Down Expand Up @@ -4909,6 +4920,28 @@ func onHeadlessApprove(cf *CLIConf) error {
return trace.Wrap(err)
}

// getAccessListsToReview will return access lists that the logged in user needs to review. On error,
// this will return an empty list.
func (cf *CLIConf) getAccessListsToReview(tc *client.TeleportClient) []*accesslist.AccessList {
clusterClient, err := tc.ConnectToCluster(cf.Context)
if err != nil {
log.WithError(err).Debug("Error connecting to the cluster")
return nil
}
defer func() {
clusterClient.Close()
}()

// Get the access lists to review. If the call returns NotImplemented, ignore it, as we may be communicating with an OSS
// server, which does not support access lists.
accessListsToReview, err := clusterClient.AuthClient.AccessListClient().GetAccessListsToReview(cf.Context)
if err != nil && !trace.IsNotImplemented(err) {
log.WithError(err).Debug("Error getting access lists to review")
}

return accessListsToReview
}

var mlockModes = []string{mlockModeNo, mlockModeAuto, mlockModeBestEffort, mlockModeStrict}

const (
Expand Down