Skip to content

Commit

Permalink
GitHub proxy part 4: tsh git ls with unified resource (#49596)
Browse files Browse the repository at this point in the history
* GitHub proxy part 4: tsh git ls

* fix ut

* update username note

* fix
  • Loading branch information
greedy52 authored Dec 5, 2024
1 parent 5548311 commit 8f50cf1
Show file tree
Hide file tree
Showing 12 changed files with 1,531 additions and 1,042 deletions.
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

0 comments on commit 8f50cf1

Please sign in to comment.