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
52 changes: 52 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3519,6 +3519,58 @@ func (c *Client) GetDatabases(ctx context.Context) ([]types.Database, error) {
return databases, nil
}

// ListDatabases returns a page of database resources.
//
// Note that database resources here refers to "dynamically-added" databases
// such as databases created by `tctl create`, the discovery service, or the
// CreateDatabase API. Databases discovered by the database agent (legacy
// discovery flow using `database_service.aws/database_service.azure`) and
// static databases defined in the `database_service.databases` section of the
// service YAML configuration are not collected in this API.
func (c *Client) ListDatabases(ctx context.Context, limit int, start string) ([]types.Database, string, error) {
resp, err := c.grpc.ListDatabases(ctx, &proto.ListDatabasesRequest{
PageSize: int32(limit),
PageToken: start,
})
if err != nil {
return nil, "", trace.Wrap(err)
}
databases := make([]types.Database, len(resp.Databases))
for i := range resp.Databases {
databases[i] = resp.Databases[i]
}
return databases, resp.NextPageToken, nil
}

// RangeDatabases returns database resources within the range [start, end).
func (c *Client) RangeDatabases(ctx context.Context, start, end string) iter.Seq2[types.Database, error] {
return func(yield func(types.Database, error) bool) {
for {
databases, next, err := c.ListDatabases(ctx, 0, start)
if err != nil {
yield(nil, err)
return
}

for _, db := range databases {
if end != "" && db.GetName() >= end {
return
}

if !yield(db, nil) {
return
}
}

if next == "" {
return
}

start = next
}
}
}

// DeleteDatabase deletes specified database resource.
func (c *Client) DeleteDatabase(ctx context.Context, name string) error {
_, err := c.grpc.DeleteDatabase(ctx, &types.ResourceRequest{Name: name})
Expand Down
2,544 changes: 1,506 additions & 1,038 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions api/client/proto/authservice_grpc.pb.go

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

18 changes: 18 additions & 0 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,22 @@ message ListAppsResponse {
string next_key = 2;
}

message ListDatabasesRequest {
// The maximum number of items to return.
// The server may impose a different page size at its discretion.
int32 page_size = 1;
// The next_page_token value returned from a previous List request, if any.
string page_token = 2;
}

message ListDatabasesResponse {
// a list of databases.
repeated types.DatabaseV3 databases = 1;
// Token to retrieve the next page of results, or empty if there are no
// more results in the list.
string next_page_token = 2;
}

// GetWindowsDesktopServicesResponse contains all registered Windows desktop services.
message GetWindowsDesktopServicesResponse {
// Services is a list of Windows desktop services.
Expand Down Expand Up @@ -3339,6 +3355,8 @@ service AuthService {

// GetDatabases returns all registered databases.
rpc GetDatabases(google.protobuf.Empty) returns (types.DatabaseV3List);
// ListDatabases returns a page of registered databases.
rpc ListDatabases(ListDatabasesRequest) returns (ListDatabasesResponse);
// GetDatabase returns a database by name.
rpc GetDatabase(types.ResourceRequest) returns (types.DatabaseV3);
// CreateDatabase creates a new database resource.
Expand Down
3 changes: 2 additions & 1 deletion build.assets/tooling/cmd/buf-plugin-linters/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ func newDefaultConfig() *Config {
"proto.AuthService.GetAlertAcks": {},
"proto.AuthService.GetApps": {},
"proto.AuthService.GetClusterAlerts": {},
"proto.AuthService.GetDatabases": {},
"proto.AuthService.GetEvents": {},
"proto.AuthService.GetGithubConnectors": {},
"proto.AuthService.GetInstallers": {},
Expand Down Expand Up @@ -190,6 +189,8 @@ func newDefaultConfig() *Config {
"teleport.lib.teleterm.v1.TerminalService.ListDatabaseUsers": {},
"proto.AuthService.GetInventoryStatus": {},

// RPCs deprecated from v19 onwards:
"proto.AuthService.GetDatabases": {},
// repeated field `schemas` in `Resource` does not require pagination.
"teleport.scim.v1.SCIMService.GetSCIMResource": {},
// `Device` message contains repeated field `DeviceCollectedData` but is not paginated.
Expand Down
37 changes: 37 additions & 0 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import (
dtauthz "github.com/gravitational/teleport/lib/devicetrust/authz"
dtconfig "github.com/gravitational/teleport/lib/devicetrust/config"
"github.com/gravitational/teleport/lib/events"
iterstream "github.com/gravitational/teleport/lib/itertools/stream"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/services/local"
Expand Down Expand Up @@ -6545,6 +6546,42 @@ func (a *ServerWithRoles) GetDatabases(ctx context.Context) (result []types.Data
return result, nil
}

// ListDatabases returns a page of database resources.
func (a *ServerWithRoles) ListDatabases(ctx context.Context, limit int, startKey string) ([]types.Database, string, error) {
if err := a.authorizeAction(types.KindDatabase, types.VerbList, types.VerbRead); err != nil {
return nil, "", trace.Wrap(err)
}

if limit <= 0 || limit > apidefaults.DefaultChunkSize {
limit = apidefaults.DefaultChunkSize
}

var next string
var seen int
out, err := iterstream.Collect(
iterstream.TakeWhile(
iterstream.FilterMap(
a.authServer.RangeDatabases(ctx, startKey, ""),
func(db types.Database) (types.Database, bool) {
if a.checkAccessToDatabase(db) == nil {
return db, true
}
return nil, false
},
),
func(db types.Database) bool {
if seen < limit {
seen++
return true
}
next = db.GetName()
return false
},
),
)
return out, next, trace.Wrap(err)
}

// DeleteDatabase removes the specified database resource.
func (a *ServerWithRoles) DeleteDatabase(ctx context.Context, name string) error {
if err := a.authorizeAction(types.KindDatabase, types.VerbDelete); err != nil {
Expand Down
31 changes: 31 additions & 0 deletions lib/auth/auth_with_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import (
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/events/eventstest"
"github.com/gravitational/teleport/lib/integrations/awsra/createsession"
"github.com/gravitational/teleport/lib/itertools/stream"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/modules/modulestest"
"github.com/gravitational/teleport/lib/okta/oktatest"
Expand Down Expand Up @@ -2868,13 +2869,35 @@ func TestDatabasesCRUDRBAC(t *testing.T) {
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
))

dbs, next, err := devClt.ListDatabases(ctx, 0, "")
require.NoError(t, err)
require.Empty(t, next)
require.Empty(t, cmp.Diff([]types.Database{devDatabase}, dbs,
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
))

// Admin should see both.
dbs, err = adminClt.GetDatabases(ctx)
require.NoError(t, err)
require.Empty(t, cmp.Diff([]types.Database{adminDatabase, devDatabase}, dbs,
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
))

dbs, next, err = adminClt.ListDatabases(ctx, 0, "")
require.NoError(t, err)
require.Empty(t, next)
require.Empty(t, cmp.Diff([]types.Database{adminDatabase, devDatabase}, dbs,
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
))

// With limit, next should be dev
dbs, next, err = adminClt.ListDatabases(ctx, 1, "")
require.NoError(t, err)
require.Equal(t, devDatabase.GetName(), next)
require.Empty(t, cmp.Diff([]types.Database{adminDatabase}, dbs,
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
))

// Dev shouldn't be able to delete dev database...
err = devClt.DeleteDatabase(ctx, adminDatabase.GetName())
require.True(t, trace.IsAccessDenied(err))
Expand Down Expand Up @@ -2969,6 +2992,14 @@ func mustGetDatabases(t *testing.T, client *authclient.Client, wantDatabases []t
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
cmpopts.EquateEmpty(),
))

actualDatabases, err = stream.Collect(client.RangeDatabases(t.Context(), "", ""))
require.NoError(t, err)

require.Empty(t, cmp.Diff(wantDatabases, actualDatabases,
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
cmpopts.EquateEmpty(),
))
}

func TestKubernetesClusterCRUD_DiscoveryService(t *testing.T) {
Expand Down
25 changes: 25 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ type ReadProxyAccessPoint interface {
// GetDatabases returns all database resources.
GetDatabases(ctx context.Context) ([]types.Database, error)

// ListDatabases returns a page of database resources.
ListDatabases(ctx context.Context, limit int, startKey string) ([]types.Database, string, error)

// RangeDatabases returns database resources within the range [start, end).
RangeDatabases(ctx context.Context, start, end string) iter.Seq2[types.Database, error]

// GetDatabase returns the specified database resource.
GetDatabase(ctx context.Context, name string) (types.Database, error)

Expand Down Expand Up @@ -613,6 +619,12 @@ type ReadDatabaseAccessPoint interface {
// GetDatabases returns all database resources.
GetDatabases(ctx context.Context) ([]types.Database, error)

// ListDatabases returns a page of database resources.
ListDatabases(ctx context.Context, limit int, startKey string) ([]types.Database, string, error)

// RangeDatabases returns database resources within the range [start, end).
RangeDatabases(ctx context.Context, start, end string) iter.Seq2[types.Database, error]

// GetDatabase returns the specified database resource.
GetDatabase(ctx context.Context, name string) (types.Database, error)
}
Expand Down Expand Up @@ -725,6 +737,13 @@ type ReadDiscoveryAccessPoint interface {

// GetDatabases returns all database resources.
GetDatabases(ctx context.Context) ([]types.Database, error)

// ListDatabases returns a page of database resources.
ListDatabases(ctx context.Context, limit int, startKey string) ([]types.Database, string, error)

// RangeDatabases returns database resources within the range [start, end).
RangeDatabases(ctx context.Context, start, end string) iter.Seq2[types.Database, error]

// GetDatabase returns a database resource with the given name if it exists.
GetDatabase(ctx context.Context, name string) (types.Database, error)

Expand Down Expand Up @@ -1075,6 +1094,12 @@ type Cache interface {
// GetDatabases returns all database resources.
GetDatabases(ctx context.Context) ([]types.Database, error)

// ListDatabases returns a page of database resources.
ListDatabases(ctx context.Context, limit int, startKey string) ([]types.Database, string, error)

// RangeDatabases returns database resources within the range [start, end).
RangeDatabases(ctx context.Context, start, end string) iter.Seq2[types.Database, error]

// GetDatabase returns the specified database resource.
GetDatabase(ctx context.Context, name string) (types.Database, error)

Expand Down
28 changes: 28 additions & 0 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4025,6 +4025,34 @@ func (g *GRPCServer) GetDatabases(ctx context.Context, _ *emptypb.Empty) (*types
}, nil
}

// ListDatabases returns a page of database resources.
func (g *GRPCServer) ListDatabases(ctx context.Context, req *authpb.ListDatabasesRequest) (*authpb.ListDatabasesResponse, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}

databases, next, err := auth.ListDatabases(ctx, int(req.PageSize), req.PageToken)
if err != nil {
return nil, trace.Wrap(err)
}

resp := &authpb.ListDatabasesResponse{
Databases: make([]*types.DatabaseV3, 0, len(databases)),
NextPageToken: next,
}

for _, database := range databases {
databaseV3, ok := database.(*types.DatabaseV3)
if !ok {
return nil, trace.BadParameter("unsupported database type %T", database)
}
resp.Databases = append(resp.Databases, databaseV3)
}

return resp, nil
}

// DeleteDatabase removes the specified database.
func (g *GRPCServer) DeleteDatabase(ctx context.Context, req *types.ResourceRequest) (*emptypb.Empty, error) {
auth, err := g.authenticate(ctx)
Expand Down
Loading
Loading