Skip to content
Open
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
102 changes: 102 additions & 0 deletions github/data_source_github_user_external_identity_by_saml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package github

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/shurcooL/githubv4"
)

func dataSourceGithubUserExternalIdentityBySaml() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceGithubUserExternalIdentityBySamlRead,

Schema: map[string]*schema.Schema{
"saml_name_id": {
Type: schema.TypeString,
Required: true,
Description: "The SAML NameID (typically an email address) to look up.",
},
"login": {
Type: schema.TypeString,
Computed: true,
Description: "The GitHub username linked to this SAML identity.",
},
"username": {
Type: schema.TypeString,
Computed: true,
Description: "The GitHub username linked to this SAML identity (same as login).",
},
"saml_identity": {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "The SAML identity attributes.",
},
"scim_identity": {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "The SCIM identity attributes.",
},
},
}
}

func dataSourceGithubUserExternalIdentityBySamlRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
samlNameId := d.Get("saml_name_id").(string)

client := meta.(*Owner).v4client
orgName := meta.(*Owner).name

var query struct {
Organization struct {
SamlIdentityProvider struct {
ExternalIdentities `graphql:"externalIdentities(first: 1, userName:$userName)"`
}
} `graphql:"organization(login: $orgName)"`
}

variables := map[string]any{
"orgName": githubv4.String(orgName),
"userName": githubv4.String(samlNameId),
}

err := client.Query(meta.(*Owner).StopContext, &query, variables)
if err != nil {
return diag.FromErr(err)
}
if len(query.Organization.SamlIdentityProvider.Edges) == 0 {
return diag.Errorf("no external identity found for SAML NameID %q in organization %q", samlNameId, orgName)
}

node := query.Organization.SamlIdentityProvider.ExternalIdentities.Edges[0].Node

samlIdentity := map[string]string{
"family_name": string(node.SamlIdentity.FamilyName),
"given_name": string(node.SamlIdentity.GivenName),
"name_id": string(node.SamlIdentity.NameId),
"username": string(node.SamlIdentity.Username),
}

scimIdentity := map[string]string{
"family_name": string(node.ScimIdentity.FamilyName),
"given_name": string(node.ScimIdentity.GivenName),
"username": string(node.ScimIdentity.Username),
}

login := string(node.User.Login)

d.SetId(fmt.Sprintf("%s/%s", orgName, samlNameId))
_ = d.Set("saml_identity", samlIdentity)
_ = d.Set("scim_identity", scimIdentity)
_ = d.Set("login", login)
_ = d.Set("username", login)
return nil
}
31 changes: 31 additions & 0 deletions github/data_source_github_user_external_identity_by_saml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package github

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccGithubUserExternalIdentityBySaml(t *testing.T) {
t.Run("queries without error", func(t *testing.T) {
config := `data "github_user_external_identity_by_saml" "test" { saml_name_id = "%s" }`

check := resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.github_user_external_identity_by_saml.test", "login"),
resource.TestCheckResourceAttrSet("data.github_user_external_identity_by_saml.test", "username"),
resource.TestCheckResourceAttrSet("data.github_user_external_identity_by_saml.test", "saml_identity.name_id"),
)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessMode(t, enterprise) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(config, testAccConf.testExternalUser),
Check: check,
},
},
})
})
}
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ func Provider() *schema.Provider {
"github_tree": dataSourceGithubTree(),
"github_user": dataSourceGithubUser(),
"github_user_external_identity": dataSourceGithubUserExternalIdentity(),
"github_user_external_identity_by_saml": dataSourceGithubUserExternalIdentityBySaml(),
"github_users": dataSourceGithubUsers(),
"github_enterprise": dataSourceGithubEnterprise(),
"github_repository_environment_deployment_policies": dataSourceGithubRepositoryEnvironmentDeploymentPolicies(),
Expand Down
47 changes: 47 additions & 0 deletions website/docs/d/user_external_identity_by_saml.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
layout: "github"
page_title: "GitHub: github_user_external_identity_by_saml"
description: |-
Look up a GitHub user by their SAML NameID.
---

# github\_user\_external\_identity\_by\_saml

Use this data source to retrieve a GitHub user's login by their SAML NameID
(typically an email address). This is a reverse lookup — given a SAML identity,
it returns the linked GitHub username.

This complements `github_user_external_identity`, which performs the opposite
lookup (GitHub username to SAML/SCIM identity).

## Example Usage

```hcl
data "github_user_external_identity_by_saml" "example" {
saml_name_id = "[email protected]"
}

resource "github_team_membership" "example" {
team_id = github_team.some_team.id
username = data.github_user_external_identity_by_saml.example.login
}
```

## Argument Reference

* `saml_name_id` - (Required) The SAML NameID to look up. This is typically
the user's email address as configured in your identity provider.

## Attribute Reference

* `login` - The GitHub username linked to the SAML identity.
* `username` - Same as `login`.
* `saml_identity` - A map of SAML identity attributes:
* `name_id` - The SAML NameID value.
* `username` - The SAML username.
* `given_name` - The user's given name.
* `family_name` - The user's family name.
* `scim_identity` - A map of SCIM identity attributes:
* `username` - The SCIM username.
* `given_name` - The user's given name.
* `family_name` - The user's family name.