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
2 changes: 2 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3899,6 +3899,8 @@ func convertEnrichedResource(resource *proto.PaginatedResource) (*types.Enriched
return &types.EnrichedResource{ResourceWithLabels: r, Logins: resource.Logins, RequiresRequest: resource.RequiresRequest}, nil
} else if r := resource.GetSAMLIdPServiceProvider(); r != nil {
return &types.EnrichedResource{ResourceWithLabels: r, RequiresRequest: resource.RequiresRequest}, nil
} else if r := resource.GetGitServer(); r != nil {
return &types.EnrichedResource{ResourceWithLabels: r, RequiresRequest: resource.RequiresRequest}, nil
} else {
return nil, trace.BadParameter("received unsupported resource %T", resource.Resource)
}
Expand Down
2,018 changes: 1,050 additions & 968 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,8 @@ message PaginatedResource {
types.AppServerOrSAMLIdPServiceProviderV1 AppServerOrSAMLIdPServiceProvider = 11 [deprecated = true];
// SAMLIdPServiceProvider represents a SAML IdP service provider resource.
types.SAMLIdPServiceProviderV1 SAMLIdPServiceProvider = 12 [(gogoproto.jsontag) = "saml_idp_service_provider,omitempty"];
// GitServer represents a Git server resource.
types.ServerV2 git_server = 15;
}

// Logins allowed for the included resource. Only to be populated for SSH and Desktops.
Expand Down
14 changes: 14 additions & 0 deletions api/types/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ type EnrichedResource struct {
RequiresRequest bool
}

// EnrichedResources is a wrapper of []*EnrichedResource.
// A EnrichedResource is a [ResourceWithLabels] wrapped with additional
// user-specific information.
type EnrichedResources []*EnrichedResource

// ToResourcesWithLabels converts to ResourcesWithLabels.
func (r EnrichedResources) ToResourcesWithLabels() ResourcesWithLabels {
ret := make(ResourcesWithLabels, 0, len(r))
for _, resource := range r {
ret = append(ret, resource.ResourceWithLabels)
}
return ret
}

// ResourcesWithLabels is a list of labeled resources.
type ResourcesWithLabels []ResourceWithLabels

Expand Down
9 changes: 9 additions & 0 deletions api/types/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -855,3 +855,12 @@ func GetGitHubOrgFromNodeAddr(addr string) (string, bool) {
}
return "", false
}

// GetOrganizationURL returns the URL to the GitHub organization.
func (m *GitHubServerMetadata) GetOrganizationURL() string {
if m == nil {
return ""
}
// Public github.com for now.
return fmt.Sprintf("%s/%s", GithubURL, m.Organization)
}
3 changes: 2 additions & 1 deletion lib/services/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ func MatchResourceByFilters(resource types.ResourceWithLabels, filter MatchResou
types.KindKubernetesCluster,
types.KindWindowsDesktop, types.KindWindowsDesktopService,
types.KindUserGroup,
types.KindIdentityCenterAccount:
types.KindIdentityCenterAccount,
types.KindGitServer:
specResource = resource
case types.KindKubeServer:
if seenMap != nil {
Expand Down
45 changes: 43 additions & 2 deletions lib/services/unified_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var UnifiedResourceKinds []string = []string{
types.KindWindowsDesktop,
types.KindSAMLIdPServiceProvider,
types.KindIdentityCenterAccount,
types.KindGitServer,
}

// UnifiedResourceCacheConfig is used to configure a UnifiedResourceCache
Expand Down Expand Up @@ -356,6 +357,7 @@ type ResourceGetter interface {
KubernetesServerGetter
SAMLIdpServiceProviderGetter
IdentityCenterAccountGetter
GitServerGetter
}

// newWatcher starts and returns a new resource watcher for unified resources.
Expand Down Expand Up @@ -387,8 +389,11 @@ func makeResourceSortKey(resource types.Resource) resourceSortKey {
// the container type.
switch r := resource.(type) {
case types.Server:
name = r.GetHostname() + "/" + r.GetName()
kind = types.KindNode
switch r.GetKind() {
case types.KindNode, types.KindGitServer:
name = r.GetHostname() + "/" + r.GetName()
kind = r.GetKind()
}
case types.AppServer:
app := r.GetApp()
if app != nil {
Expand Down Expand Up @@ -464,6 +469,11 @@ func (c *UnifiedResourceCache) getResourcesAndUpdateCurrent(ctx context.Context)
return trace.Wrap(err)
}

newGitServers, err := c.getGitServers(ctx)
if err != nil {
return trace.Wrap(err)
}

c.rw.Lock()
defer c.rw.Unlock()
// empty the trees
Expand All @@ -480,6 +490,7 @@ func (c *UnifiedResourceCache) getResourcesAndUpdateCurrent(ctx context.Context)
putResources[types.SAMLIdPServiceProvider](c, newSAMLApps)
putResources[types.WindowsDesktop](c, newDesktops)
putResources[resource](c, newICAccounts)
putResources[types.Server](c, newGitServers)
c.stale = false
c.defineCollectorAsInitialized()
return nil
Expand Down Expand Up @@ -611,6 +622,23 @@ func (c *UnifiedResourceCache) getIdentityCenterAccounts(ctx context.Context) ([
return accounts, nil
}

func (c *UnifiedResourceCache) getGitServers(ctx context.Context) (all []types.Server, err error) {
var page []types.Server
nextToken := ""
for {
page, nextToken, err = c.ListGitServers(ctx, apidefaults.DefaultChunkSize, nextToken)
if err != nil {
return nil, trace.Wrap(err, "getting Git servers for unified resource watcher")
}

all = append(all, page...)
if nextToken == "" {
break
}
}
return all, nil
}

// read applies the supplied closure to either the primary tree or the ttl-based fallback tree depending on
// wether or not the cache is currently healthy. locking is handled internally and the passed-in tree should
// not be accessed after the closure completes.
Expand Down Expand Up @@ -939,6 +967,19 @@ func MakePaginatedResource(ctx context.Context, requestType string, r types.Reso
return nil, trace.Wrap(err)
}

case types.KindGitServer:
server, ok := resource.(*types.ServerV2)
if !ok {
return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource)
}

protoResource = &proto.PaginatedResource{
Resource: &proto.PaginatedResource_GitServer{
GitServer: server,
},
RequiresRequest: requiresRequest,
}

default:
return nil, trace.NotImplemented("resource type %s doesn't support pagination", resource.GetKind())
}
Expand Down
127 changes: 56 additions & 71 deletions lib/services/unified_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,40 +46,49 @@ import (
"github.com/gravitational/teleport/lib/services/local"
)

func TestUnifiedResourceWatcher(t *testing.T) {
t.Parallel()
type client struct {
services.Presence
services.WindowsDesktops
services.SAMLIdPServiceProviders
services.GitServers
services.IdentityCenterAccounts
types.Events
}

ctx := context.Background()
func newClient(t *testing.T) *client {
t.Helper()

bk, err := memory.New(memory.Config{})
require.NoError(t, err)

type client struct {
services.Presence
services.WindowsDesktops
services.SAMLIdPServiceProviders
services.IdentityCenterAccounts
types.Events
}

samlService, err := local.NewSAMLIdPServiceProviderService(bk)
require.NoError(t, err)

gitService, err := local.NewGitServerService(bk)
require.NoError(t, err)
icService, err := local.NewIdentityCenterService(local.IdentityCenterServiceConfig{
Backend: bk,
})
require.NoError(t, err)

clt := &client{
return &client{
Presence: local.NewPresenceService(bk),
WindowsDesktops: local.NewWindowsDesktopService(bk),
SAMLIdPServiceProviders: samlService,
Events: local.NewEventsService(bk),
GitServers: gitService,
IdentityCenterAccounts: icService,
}
}

func TestUnifiedResourceWatcher(t *testing.T) {
t.Parallel()

ctx := context.Background()
clt := newClient(t)

// Add node to the backend.
node := newNodeServer(t, "node1", "hostname1", "127.0.0.1:22", false /*tunnel*/)
_, err = clt.UpsertNode(ctx, node)
_, err := clt.UpsertNode(ctx, node)
require.NoError(t, err)

db, err := types.NewDatabaseV3(types.Metadata{
Expand All @@ -99,6 +108,10 @@ func TestUnifiedResourceWatcher(t *testing.T) {
require.NoError(t, err)
_, err = clt.UpsertDatabaseServer(ctx, dbServer)
require.NoError(t, err)
gitServer := newGitServer(t, "my-org")
require.NoError(t, err)
_, err = clt.CreateGitServer(ctx, gitServer)
require.NoError(t, err)

w, err := services.NewUnifiedResourceCache(ctx, services.UnifiedResourceCacheConfig{
ResourceWatcherConfig: services.ResourceWatcherConfig{
Expand All @@ -108,10 +121,10 @@ func TestUnifiedResourceWatcher(t *testing.T) {
ResourceGetter: clt,
})
require.NoError(t, err)
// node and db expected initially
// node, db, and git_server expected initially
res, err := w.GetUnifiedResources(ctx)
require.NoError(t, err)
require.Len(t, res, 2)
require.Len(t, res, 3)

assert.Eventually(t, func() bool {
return w.IsInitialized()
Expand Down Expand Up @@ -152,10 +165,16 @@ func TestUnifiedResourceWatcher(t *testing.T) {
err = clt.UpsertWindowsDesktop(ctx, win)
require.NoError(t, err)

// add another git server
gitServer2 := newGitServer(t, "my-org-2")
_, err = clt.UpsertGitServer(ctx, gitServer2)
require.NoError(t, err)

icAcct := newIdentityCenterAccount(t, ctx, clt)

// we expect each of the resources above to exist
expectedRes := []types.ResourceWithLabels{node, app, samlapp, dbServer, win,
gitServer, gitServer2,
types.Resource153ToUnifiedResource(icAcct)}
assert.Eventually(t, func() bool {
res, err = w.GetUnifiedResources(ctx)
Expand Down Expand Up @@ -193,6 +212,7 @@ func TestUnifiedResourceWatcher(t *testing.T) {

// this should include the updated node, and shouldn't have any apps included
expectedRes = []types.ResourceWithLabels{nodeUpdated, samlapp, dbServer, win,
gitServer, gitServer2,
types.Resource153ToUnifiedResource(icAcct)}

assert.Eventually(t, func() bool {
Expand Down Expand Up @@ -233,33 +253,7 @@ func TestUnifiedResourceWatcher_PreventDuplicates(t *testing.T) {
t.Parallel()

ctx := context.Background()

bk, err := memory.New(memory.Config{})
require.NoError(t, err)

type client struct {
services.Presence
services.WindowsDesktops
services.SAMLIdPServiceProviders
services.IdentityCenterAccountGetter
types.Events
}

samlService, err := local.NewSAMLIdPServiceProviderService(bk)
require.NoError(t, err)

icService, err := local.NewIdentityCenterService(local.IdentityCenterServiceConfig{
Backend: bk,
})
require.NoError(t, err)

clt := &client{
Presence: local.NewPresenceService(bk),
WindowsDesktops: local.NewWindowsDesktopService(bk),
SAMLIdPServiceProviders: samlService,
Events: local.NewEventsService(bk),
IdentityCenterAccountGetter: icService,
}
clt := newClient(t)
w, err := services.NewUnifiedResourceCache(ctx, services.UnifiedResourceCacheConfig{
ResourceWatcherConfig: services.ResourceWatcherConfig{
Component: teleport.ComponentUnifiedResource,
Expand Down Expand Up @@ -296,33 +290,7 @@ func TestUnifiedResourceWatcher_DeleteEvent(t *testing.T) {
t.Parallel()

ctx := context.Background()

bk, err := memory.New(memory.Config{})
require.NoError(t, err)

type client struct {
services.Presence
services.WindowsDesktops
services.SAMLIdPServiceProviders
services.IdentityCenterAccounts
types.Events
}

samlService, err := local.NewSAMLIdPServiceProviderService(bk)
require.NoError(t, err)

icService, err := local.NewIdentityCenterService(local.IdentityCenterServiceConfig{
Backend: bk,
})
require.NoError(t, err)

clt := &client{
Presence: local.NewPresenceService(bk),
WindowsDesktops: local.NewWindowsDesktopService(bk),
SAMLIdPServiceProviders: samlService,
Events: local.NewEventsService(bk),
IdentityCenterAccounts: icService,
}
clt := newClient(t)
w, err := services.NewUnifiedResourceCache(ctx, services.UnifiedResourceCacheConfig{
ResourceWatcherConfig: services.ResourceWatcherConfig{
Component: teleport.ComponentUnifiedResource,
Expand Down Expand Up @@ -419,9 +387,14 @@ func TestUnifiedResourceWatcher_DeleteEvent(t *testing.T) {

icAcct := newIdentityCenterAccount(t, ctx, clt)

// add git server
gitServer := newGitServer(t, "my-org")
_, err = clt.CreateGitServer(ctx, gitServer)
require.NoError(t, err)

assert.Eventually(t, func() bool {
res, _ := w.GetUnifiedResources(ctx)
return len(res) == 7
return len(res) == 8
}, 5*time.Second, 10*time.Millisecond, "Timed out waiting for unified resources to be added")

// delete everything
Expand All @@ -439,6 +412,8 @@ func TestUnifiedResourceWatcher_DeleteEvent(t *testing.T) {
require.NoError(t, err)
err = clt.DeleteIdentityCenterAccount(ctx, services.IdentityCenterAccountID(icAcct.GetMetadata().GetName()))
require.NoError(t, err)
err = clt.DeleteGitServer(ctx, gitServer.GetName())
require.NoError(t, err)

assert.Eventually(t, func() bool {
res, _ := w.GetUnifiedResources(ctx)
Expand Down Expand Up @@ -491,6 +466,16 @@ func newTestEntityDescriptor(entityID string) string {
return fmt.Sprintf(testEntityDescriptor, entityID)
}

func newGitServer(t *testing.T, githubOrg string) types.Server {
t.Helper()
gitServer, err := types.NewGitHubServer(types.GitHubServerMetadata{
Organization: githubOrg,
Integration: githubOrg,
})
require.NoError(t, err)
return gitServer
}

// A test entity descriptor from https://sptest.iamshowcase.com/testsp_metadata.xml.
const testEntityDescriptor = `<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="%s" validUntil="2025-12-09T09:13:31.006Z">
Expand Down
Loading