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',
}
/**