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
99 changes: 99 additions & 0 deletions github/data_source_github_enterprise_scim_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package github

import (
"context"
"fmt"

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

func dataSourceGithubEnterpriseSCIMGroup() *schema.Resource {
return &schema.Resource{
Description: "Lookup SCIM provisioning information for a single GitHub enterprise group.",
ReadContext: dataSourceGithubEnterpriseSCIMGroupRead,

Schema: map[string]*schema.Schema{
"enterprise": {
Description: "The enterprise slug.",
Type: schema.TypeString,
Required: true,
},
"scim_group_id": {
Description: "The SCIM group ID.",
Type: schema.TypeString,
Required: true,
},
"excluded_attributes": {
Description: "Optional SCIM excludedAttributes query parameter.",
Type: schema.TypeString,
Optional: true,
},

"schemas": {
Type: schema.TypeList,
Computed: true,
Description: "SCIM schemas for this group.",
Elem: &schema.Schema{Type: schema.TypeString},
},
"id": {
Type: schema.TypeString,
Computed: true,
Description: "The SCIM group ID.",
},
"external_id": {
Type: schema.TypeString,
Computed: true,
Description: "The external ID for the group.",
},
"display_name": {
Type: schema.TypeString,
Computed: true,
Description: "The SCIM group displayName.",
},
"members": {
Type: schema.TypeList,
Computed: true,
Description: "Group members.",
Elem: &schema.Resource{Schema: enterpriseSCIMGroupMemberSchema()},
},
"meta": {
Type: schema.TypeList,
Computed: true,
Description: "Resource metadata.",
Elem: &schema.Resource{Schema: enterpriseSCIMMetaSchema()},
},
},
}
}

func dataSourceGithubEnterpriseSCIMGroupRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client

enterprise := d.Get("enterprise").(string)
scimGroupID := d.Get("scim_group_id").(string)
excluded := d.Get("excluded_attributes").(string)

path := fmt.Sprintf("scim/v2/enterprises/%s/Groups/%s", enterprise, scimGroupID)
urlStr, err := enterpriseSCIMListURL(path, enterpriseSCIMListOptions{ExcludedAttributes: excluded})
if err != nil {
return diag.FromErr(err)
}

group := enterpriseSCIMGroup{}
_, err = enterpriseSCIMGet(ctx, client, urlStr, &group)
if err != nil {
return diag.FromErr(err)
}

d.SetId(fmt.Sprintf("%s/%s", enterprise, scimGroupID))

_ = d.Set("schemas", group.Schemas)
_ = d.Set("id", group.ID)
_ = d.Set("external_id", group.ExternalID)
_ = d.Set("display_name", group.DisplayName)
_ = d.Set("members", flattenEnterpriseSCIMGroupMembers(group.Members))
_ = d.Set("meta", flattenEnterpriseSCIMMeta(group.Meta))

return nil
}
63 changes: 63 additions & 0 deletions github/data_source_github_enterprise_scim_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package github

import (
"context"
"net/http"
"net/url"
"testing"

gh "github.com/google/go-github/v67/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func TestDataSourceGithubEnterpriseSCIMGroupRead(t *testing.T) {
ts := githubApiMock([]*mockResponse{
{
ExpectedUri: "/scim/v2/enterprises/ent/Groups/g1",
ExpectedHeaders: map[string]string{
"Accept": enterpriseSCIMAcceptHeader,
},
StatusCode: 200,
ResponseBody: `{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "g1",
"externalId": "eg1",
"displayName": "Group One",
"members": [{"value": "u1", "$ref": "https://example.test/u1", "display": "user1"}],
"meta": {"resourceType": "Group", "created": "2020-01-01T00:00:00Z"}
}`,
},
})
defer ts.Close()

httpClient := &http.Client{Transport: http.DefaultTransport}
client := gh.NewClient(httpClient)
baseURL, _ := url.Parse(ts.URL + "/")
client.BaseURL = baseURL

owner := &Owner{v3client: client}

r := dataSourceGithubEnterpriseSCIMGroup()
d := schema.TestResourceDataRaw(t, r.Schema, map[string]any{
"enterprise": "ent",
"scim_group_id": "g1",
"excluded_attributes": "",
})

diags := dataSourceGithubEnterpriseSCIMGroupRead(context.Background(), d, owner)
if len(diags) > 0 {
t.Fatalf("unexpected diagnostics: %#v", diags)
}

if got := d.Get("id").(string); got != "g1" {
t.Fatalf("expected id g1, got %q", got)
}
members := d.Get("members").([]any)
if len(members) != 1 {
t.Fatalf("expected 1 member, got %d", len(members))
}
m0 := members[0].(map[string]any)
if m0["ref"].(string) != "https://example.test/u1" {
t.Fatalf("expected ref to be set, got %v", m0["ref"])
}
}
222 changes: 222 additions & 0 deletions github/data_source_github_enterprise_scim_groups.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package github

import (
"context"
"fmt"
"net/url"

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

func dataSourceGithubEnterpriseSCIMGroups() *schema.Resource {
return &schema.Resource{
Description: "Lookup SCIM groups provisioned for a GitHub enterprise.",
ReadContext: dataSourceGithubEnterpriseSCIMGroupsRead,

Schema: map[string]*schema.Schema{
"enterprise": {
Description: "The enterprise slug.",
Type: schema.TypeString,
Required: true,
},
"filter": {
Description: "Optional SCIM filter. See GitHub SCIM enterprise docs for supported filters.",
Type: schema.TypeString,
Optional: true,
},
"excluded_attributes": {
Description: "Optional SCIM excludedAttributes query parameter.",
Type: schema.TypeString,
Optional: true,
},
"results_per_page": {
Description: "Number of results per request (mapped to SCIM 'count'). Used while auto-fetching all pages.",
Type: schema.TypeInt,
Optional: true,
Default: 100,
ValidateFunc: validation.IntBetween(1, 100),
},

"schemas": {
Description: "SCIM response schemas.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"total_results": {
Description: "The total number of results returned by the SCIM endpoint.",
Type: schema.TypeInt,
Computed: true,
},
"start_index": {
Description: "The startIndex from the first SCIM page.",
Type: schema.TypeInt,
Computed: true,
},
"items_per_page": {
Description: "The itemsPerPage from the first SCIM page.",
Type: schema.TypeInt,
Computed: true,
},
"resources": {
Description: "All SCIM groups.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: enterpriseSCIMGroupSchema(),
},
},
},
}
}

func dataSourceGithubEnterpriseSCIMGroupsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client

enterprise := d.Get("enterprise").(string)
filter := d.Get("filter").(string)
excluded := d.Get("excluded_attributes").(string)
count := d.Get("results_per_page").(int)

groups, first, err := enterpriseSCIMListAllGroups(ctx, client, enterprise, filter, excluded, count)
if err != nil {
return diag.FromErr(err)
}

flat := make([]any, 0, len(groups))
for _, g := range groups {
flat = append(flat, flattenEnterpriseSCIMGroup(g))
}

id := fmt.Sprintf("%s/scim-groups", enterprise)
if filter != "" {
id = fmt.Sprintf("%s?filter=%s", id, url.QueryEscape(filter))
}
if excluded != "" {
if filter == "" {
id = fmt.Sprintf("%s?excluded_attributes=%s", id, url.QueryEscape(excluded))
} else {
id = fmt.Sprintf("%s&excluded_attributes=%s", id, url.QueryEscape(excluded))
}
}

d.SetId(id)

_ = d.Set("schemas", first.Schemas)
_ = d.Set("total_results", first.TotalResults)
if first.StartIndex > 0 {
_ = d.Set("start_index", first.StartIndex)
} else {
_ = d.Set("start_index", 1)
}
if first.ItemsPerPage > 0 {
_ = d.Set("items_per_page", first.ItemsPerPage)
} else {
_ = d.Set("items_per_page", count)
}
if err := d.Set("resources", flat); err != nil {
return diag.FromErr(fmt.Errorf("error setting resources: %w", err))
}

return nil
}

func enterpriseSCIMMetaSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"resource_type": {
Type: schema.TypeString,
Computed: true,
Description: "The SCIM resource type.",
},
"created": {
Type: schema.TypeString,
Computed: true,
Description: "The creation timestamp.",
},
"last_modified": {
Type: schema.TypeString,
Computed: true,
Description: "The lastModified timestamp.",
},
"location": {
Type: schema.TypeString,
Computed: true,
Description: "The resource location.",
},
"version": {
Type: schema.TypeString,
Computed: true,
Description: "The resource version.",
},
"etag": {
Type: schema.TypeString,
Computed: true,
Description: "The resource eTag.",
},
"password_changed_at": {
Type: schema.TypeString,
Computed: true,
Description: "The password changed at timestamp (if present).",
},
}
}

func enterpriseSCIMGroupSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"schemas": {
Type: schema.TypeList,
Computed: true,
Description: "SCIM schemas for this group.",
Elem: &schema.Schema{Type: schema.TypeString},
},
"id": {
Type: schema.TypeString,
Computed: true,
Description: "The SCIM group ID.",
},
"external_id": {
Type: schema.TypeString,
Computed: true,
Description: "The external ID for the group.",
},
"display_name": {
Type: schema.TypeString,
Computed: true,
Description: "The SCIM group displayName.",
},
"members": {
Type: schema.TypeList,
Computed: true,
Description: "Group members.",
Elem: &schema.Resource{Schema: enterpriseSCIMGroupMemberSchema()},
},
"meta": {
Type: schema.TypeList,
Computed: true,
Description: "Resource metadata.",
Elem: &schema.Resource{Schema: enterpriseSCIMMetaSchema()},
},
}
}

func enterpriseSCIMGroupMemberSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"value": {
Type: schema.TypeString,
Computed: true,
Description: "Member identifier.",
},
"ref": {
Type: schema.TypeString,
Computed: true,
Description: "Member reference URL.",
},
"display_name": {
Type: schema.TypeString,
Computed: true,
Description: "Member display name.",
},
}
}
Loading