Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v9] Fix tsh db ls for remote clusters. (#12281) #12853

Merged
merged 5 commits into from
May 24, 2022
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
10 changes: 10 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,16 @@ func (c *Client) GetUser(name string, withSecrets bool) (types.User, error) {
return user, nil
}

// GetCurrentUser returns current user as seen by the server.
// Useful especially in the context of remote clusters which perform role and trait mapping.
func (c *Client) GetCurrentUser(ctx context.Context) (types.User, error) {
currentUser, err := c.grpc.GetCurrentUser(ctx, &empty.Empty{})
if err != nil {
return nil, trail.FromGRPC(err)
}
return currentUser, nil
}

// GetUsers returns a list of users.
// withSecrets controls whether authentication details are returned.
func (c *Client) GetUsers(withSecrets bool) ([]types.User, error) {
Expand Down
1,185 changes: 613 additions & 572 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions api/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,9 @@ service AuthService {

// GetUser gets a user resource by name.
rpc GetUser(GetUserRequest) returns (types.UserV2);
// GetCurrentUser returns current user as seen by the server.
// Useful especially in the context of remote clusters which perform role and trait mapping.
rpc GetCurrentUser(google.protobuf.Empty) returns (types.UserV2);
// GetUsers gets all current user resources.
rpc GetUsers(GetUsersRequest) returns (stream types.UserV2);
// CreateUser inserts a new user entry to a backend.
Expand Down
19 changes: 19 additions & 0 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"time"

"github.com/coreos/go-semver/semver"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
Expand Down Expand Up @@ -1932,6 +1933,24 @@ func (a *ServerWithRoles) GetUser(name string, withSecrets bool) (types.User, er
return a.authServer.Identity.GetUser(name, withSecrets)
}

// GetCurrentUser returns current user as seen by the server.
// Useful especially in the context of remote clusters which perform role and trait mapping.
func (a *ServerWithRoles) GetCurrentUser(ctx context.Context) (types.User, error) {
// check access to roles
for _, role := range a.context.User.GetRoles() {
_, err := a.GetRole(ctx, role)
if err != nil {
return nil, trace.Wrap(err)
}
}

usrRes := a.context.User.WithoutSecrets()
if usr, ok := usrRes.(types.User); ok {
return usr, nil
}
return nil, trace.BadParameter("expected types.User when fetching current user information, got %T", usrRes)
}

// DeleteUser deletes an existng user in a backend by username.
func (a *ServerWithRoles) DeleteUser(ctx context.Context, user string) error {
if err := a.action(apidefaults.Namespace, types.KindUser, types.VerbDelete); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions lib/auth/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,10 @@ type IdentityService interface {
// GetUser returns user by name
GetUser(name string, withSecrets bool) (types.User, error)

// GetCurrentUser returns current user as seen by the server.
// Useful especially in the context of remote clusters which perform role and trait mapping.
GetCurrentUser(ctx context.Context) (types.User, error)

// CreateUser inserts a new entry in a backend.
CreateUser(ctx context.Context, user types.User) error

Expand Down
17 changes: 17 additions & 0 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,23 @@ func (g *GRPCServer) GetUser(ctx context.Context, req *proto.GetUserRequest) (*t
return v2, nil
}

func (g *GRPCServer) GetCurrentUser(ctx context.Context, req *empty.Empty) (*types.UserV2, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
user, err := auth.ServerWithRoles.GetCurrentUser(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
v2, ok := user.(*types.UserV2)
if !ok {
log.Warnf("expected type services.UserV2, got %T for user %q", user, user.GetName())
return nil, trace.Errorf("encountered unexpected user type")
}
return v2, nil
}

func (g *GRPCServer) GetUsers(req *proto.GetUsersRequest, stream proto.AuthService_GetUsersServer) error {
auth, err := g.authenticate(stream.Context())
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions lib/auth/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,36 @@ func (s *TLSSuite) TestReadOwnRole(c *check.C) {
fixtures.ExpectAccessDenied(c, err)
}

func TestGetCurrentUser(t *testing.T) {
ctx := context.Background()
srv := newTestTLSServer(t)

user1, _, err := CreateUserAndRole(srv.Auth(), "user1", []string{"user1"})
require.NoError(t, err)

client1, err := srv.NewClient(TestIdentity{I: LocalUser{Username: user1.GetName()}})
require.NoError(t, err)

currentUser, err := client1.GetCurrentUser(ctx)
require.NoError(t, err)
require.Equal(t, &types.UserV2{
Kind: "user",
SubKind: "",
Version: "v2",
Metadata: types.Metadata{
Name: "user1",
Namespace: "default",
Description: "",
Labels: nil,
Expires: nil,
ID: currentUser.GetMetadata().ID, // ignore changes here
},
Spec: types.UserSpecV2{
Roles: []string{"user:user1"},
},
}, currentUser)
}

func (s *TLSSuite) TestAuthPreference(c *check.C) {
clt, err := s.server.NewClient(TestAdmin())
c.Assert(err, check.IsNil)
Expand Down
22 changes: 21 additions & 1 deletion tool/tsh/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,27 @@ func onListDatabases(cf *CLIConf) error {
return trace.Wrap(err)
}

roleSet, err := services.FetchRoles(profile.Roles, cluster, profile.Traits)
// get roles and traits. default to the set from profile, try to get up-to-date version from server point of view.
roles := profile.Roles
traits := profile.Traits

// GetCurrentUser() may not be implemented, fail gracefully.
user, err := cluster.GetCurrentUser(cf.Context)
if err == nil {
roles = user.GetRoles()
traits = user.GetTraits()
} else {
log.Debugf("Failed to fetch current user information: %v.", err)
}

// get the role definition for all roles of user.
// this may only fail if the role which we are looking for does not exist, or we don't have access to it.
// example scenario when this may happen:
// 1. we have set of roles [foo bar] from profile.
// 2. the cluster is remote and maps the [foo, bar] roles to single role [guest]
// 3. the remote cluster doesn't implement GetCurrentUser(), so we have no way to learn of [guest].
// 4. services.FetchRoles([foo bar], ..., ...) fails as [foo bar] does not exist on remote cluster.
roleSet, err := services.FetchRoles(roles, cluster, traits)
if err != nil {
log.Debugf("Failed to fetch user roles: %v.", err)
}
Expand Down