diff --git a/lib/web/ui/integration.go b/lib/web/ui/integration.go index 3c0aab3c817d8..106af1872a70c 100644 --- a/lib/web/ui/integration.go +++ b/lib/web/ui/integration.go @@ -49,6 +49,11 @@ type IntegrationAWSOIDCSpec struct { Audience string `json:"audience,omitempty"` } +// IntegrationGitHub contains the specific fields for the `github` subkind integration. +type IntegrationGitHub struct { + Organization string `json:"organization"` +} + // CheckAndSetDefaults for the aws oidc integration spec. func (r *IntegrationAWSOIDCSpec) CheckAndSetDefaults() error { if r.RoleARN == "" { @@ -104,6 +109,8 @@ type Integration struct { SubKind string `json:"subKind,omitempty"` // AWSOIDC contains the fields for `aws-oidc` subkind integration. AWSOIDC *IntegrationAWSOIDCSpec `json:"awsoidc,omitempty"` + // GitHub contains the fields for `github` subkind integration. + GitHub *IntegrationGitHub `json:"github,omitempty"` } // CheckAndSetDefaults for the create request. @@ -123,6 +130,16 @@ func (r *Integration) CheckAndSetDefaults() error { } } + switch r.SubKind { + case types.IntegrationSubKindGitHub: + if r.GitHub == nil { + return trace.BadParameter("missing spec for GitHub integrations") + } + if err := types.ValidateGitHubOrganizationName(r.GitHub.Organization); err != nil { + return trace.Wrap(err) + } + } + return nil } @@ -195,6 +212,14 @@ func MakeIntegration(ig types.Integration) (*Integration, error) { IssuerS3Prefix: s3Prefix, Audience: ig.GetAWSOIDCIntegrationSpec().Audience, } + case types.IntegrationSubKindGitHub: + spec := ig.GetGitHubIntegrationSpec() + if spec == nil { + return nil, trace.BadParameter("missing spec for GitHub integrations") + } + ret.GitHub = &IntegrationGitHub{ + Organization: spec.Organization, + } } return ret, nil diff --git a/lib/web/ui/integration_test.go b/lib/web/ui/integration_test.go new file mode 100644 index 0000000000000..f10594a028489 --- /dev/null +++ b/lib/web/ui/integration_test.go @@ -0,0 +1,84 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package ui + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" +) + +func TestMakeIntegration(t *testing.T) { + oidcIntegration, err := types.NewIntegrationAWSOIDC( + types.Metadata{ + Name: "aws-oidc", + }, + &types.AWSOIDCIntegrationSpecV1{ + RoleARN: "arn:aws:iam::123456789012:role/OidcRole", + }, + ) + require.NoError(t, err) + + githubIntegration, err := types.NewIntegrationGitHub( + types.Metadata{ + Name: "github-my-org", + }, + &types.GitHubIntegrationSpecV1{ + Organization: "my-org", + }, + ) + require.NoError(t, err) + + testCases := []struct { + integration types.Integration + want Integration + }{ + { + integration: oidcIntegration, + want: Integration{ + Name: "aws-oidc", + SubKind: types.IntegrationSubKindAWSOIDC, + AWSOIDC: &IntegrationAWSOIDCSpec{ + RoleARN: "arn:aws:iam::123456789012:role/OidcRole", + }, + }, + }, + { + integration: githubIntegration, + want: Integration{ + Name: "github-my-org", + SubKind: types.IntegrationSubKindGitHub, + GitHub: &IntegrationGitHub{ + Organization: "my-org", + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.integration.GetName(), func(t *testing.T) { + actual, err := MakeIntegration(tc.integration) + require.NoError(t, err) + require.NotNil(t, actual) + require.Equal(t, tc.want, *actual) + }) + } +} diff --git a/web/packages/teleport/src/Integrations/IntegrationList.tsx b/web/packages/teleport/src/Integrations/IntegrationList.tsx index dfd8a89159efa..a226cea01523f 100644 --- a/web/packages/teleport/src/Integrations/IntegrationList.tsx +++ b/web/packages/teleport/src/Integrations/IntegrationList.tsx @@ -358,6 +358,10 @@ const IconCell = ({ item }: { item: IntegrationLike }) => { formattedText = 'Azure OIDC'; icon = ; break; + case IntegrationKind.GitHub: + formattedText = item.name; + icon = ; + break; } } diff --git a/web/packages/teleport/src/services/integrations/integrations.test.ts b/web/packages/teleport/src/services/integrations/integrations.test.ts index 4e5bbcc089d4e..fb917c96a6b97 100644 --- a/web/packages/teleport/src/services/integrations/integrations.test.ts +++ b/web/packages/teleport/src/services/integrations/integrations.test.ts @@ -63,6 +63,7 @@ test('fetch integration list: fetchIntegrations()', async () => { items: [ awsOidcIntegration, awsOidcIntegrationWithAudience, + githubIntegration, nonAwsOidcIntegration, ], nextKey: 'some-key', @@ -93,6 +94,17 @@ test('fetch integration list: fetchIntegrations()', async () => { }, statusCode: IntegrationStatusCode.Running, }, + { + kind: 'github', + name: 'github-my-org', + resourceType: 'integration', + spec: { + roleArn: undefined, + audience: undefined, + }, + details: 'GitHub Organization "my-org"', + statusCode: IntegrationStatusCode.Running, + }, { kind: 'abc', name: 'non-aws-oidc-integration', @@ -232,6 +244,13 @@ const awsOidcIntegrationWithAudience = { audience: IntegrationAudience.AwsIdentityCenter, }, }; +const githubIntegration = { + name: 'github-my-org', + subKind: 'github', + github: { + organization: 'my-org', + }, +}; const mockAwsDbs = [ { diff --git a/web/packages/teleport/src/services/integrations/integrations.ts b/web/packages/teleport/src/services/integrations/integrations.ts index 139b684bd0796..dfdb2e0ad347b 100644 --- a/web/packages/teleport/src/services/integrations/integrations.ts +++ b/web/packages/teleport/src/services/integrations/integrations.ts @@ -422,7 +422,7 @@ export function makeIntegrations(json: any): Integration[] { function makeIntegration(json: any): Integration { json = json || {}; - const { name, subKind, awsoidc } = json; + const { name, subKind, awsoidc, github } = json; return { resourceType: 'integration', name, @@ -433,6 +433,9 @@ function makeIntegration(json: any): Integration { issuerS3Prefix: awsoidc?.issuerS3Prefix, audience: awsoidc?.audience, }, + details: github + ? `GitHub Organization "${github.organization}"` + : undefined, // The integration resource does not have a "status" field, but is // a required field for the table that lists both plugin and // integration resources together. As discussed, the only diff --git a/web/packages/teleport/src/services/integrations/types.ts b/web/packages/teleport/src/services/integrations/types.ts index 0852a1612e779..ccc795fe887cd 100644 --- a/web/packages/teleport/src/services/integrations/types.ts +++ b/web/packages/teleport/src/services/integrations/types.ts @@ -62,6 +62,7 @@ export enum IntegrationKind { AwsOidc = 'aws-oidc', AzureOidc = 'azure-oidc', ExternalAuditStorage = 'external-audit-storage', + GitHub = 'github', } /**