Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
65b5cfe
Initial Enterprise SCIM schema
elminster-aom Nov 7, 2025
c1c2f12
Add missing type structs
elminster-aom Nov 9, 2025
f7d36fc
Deprecate ListSCIMProvisionedGroupsForEnterprise for ListProvisionedS…
elminster-aom Nov 9, 2025
0838cc2
Refactor - Focus on ListProvisionedSCIMGroupsForEnterprise only
elminster-aom Nov 10, 2025
a08d988
Breaking change - Refactor SCIMService.ListSCIMProvisionedGroupsForEn…
elminster-aom Nov 10, 2025
0700f94
Bump version of go-github to v78.0.0 (#3815)
gmlewis Nov 8, 2025
b7731e5
Bump go-github from v77 to v78 in /scrape (#3816)
gmlewis Nov 8, 2025
ab42c15
chore: Enable 'modernize'; bump golangci-lint to v2.6.1 (#3817)
alexandear Nov 8, 2025
2b21b37
feat: Add `required_reviewers` support to `PullRequestRuleParameters`…
smazmi Nov 9, 2025
2568481
Add support for new custom properties for orgs APIs (#3804)
Not-Dhananjay-Mishra Nov 10, 2025
a44d6ae
build(deps): Bump golang.org/x/sync from 0.17.0 to 0.18.0 in /tools (…
dependabot[bot] Nov 10, 2025
c2d8c9e
chore: Enable nolintlint linter (#3821)
alexandear Nov 10, 2025
ee7887d
Fix struct type issues and adjust standards
elminster-aom Nov 11, 2025
6bdaa40
Merge branch 'master' into enterpriseSCIM
elminster-aom Nov 11, 2025
5e283e1
Merge branch 'master' into enterpriseSCIM
elminster-aom Nov 12, 2025
b33cd46
Rename ListProvisionedSCIMGroupsForEnterprise to ListProvisionedSCIME…
elminster-aom Nov 13, 2025
7bc8c30
Improve comments
elminster-aom Nov 13, 2025
af91ab8
Improve nomenclature
elminster-aom Nov 13, 2025
862685b
Fix lint issue with jsonfieldname
elminster-aom Nov 13, 2025
e506052
Merge branch 'master' into enterpriseSCIM
elminster-aom Nov 13, 2025
4ed75ac
Simplify to ListProvisionedSCIMGroups()
elminster-aom Nov 13, 2025
e8fffba
Fix ListProvisionedSCIMGroupsEnterpriseOptions type
elminster-aom Nov 14, 2025
919fbd9
Update github/enterprise_scim.go
elminster-aom Nov 14, 2025
9de4f3d
Merge branch 'master' into enterpriseSCIM
elminster-aom Nov 14, 2025
04ec0d3
Fix lint error in SCIMEnterpriseGroupAttributes comments
elminster-aom Nov 14, 2025
7368118
Merge branch 'master' into enterpriseSCIM
elminster-aom Nov 14, 2025
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/enterprise_scim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2025 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
"context"
"fmt"
)

// SCIMSchemasURINamespacesGroups is the SCIM schema URI namespace for group resources.
// This constant represents the standard SCIM core schema for group objects as defined by RFC 7643.
const SCIMSchemasURINamespacesGroups = "urn:ietf:params:scim:schemas:core:2.0:Group"

// SCIMSchemasURINamespacesListResponse is the SCIM schema URI namespace for list response resources.
// This constant represents the standard SCIM namespace for list responses used in paginated queries, as defined by RFC 7644.
const SCIMSchemasURINamespacesListResponse = "urn:ietf:params:scim:api:messages:2.0:ListResponse"

// SCIMEnterpriseGroupAttributes represents supported SCIM Enterprise group attributes.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#supported-scim-group-attributes
type SCIMEnterpriseGroupAttributes struct {
DisplayName *string `json:"displayName,omitempty"` // Human-readable name for a group.
Members []*SCIMEnterpriseDisplayReference `json:"members,omitempty"` // List of members who are assigned to the group in SCIM provider
ExternalID *string `json:"externalId,omitempty"` // This identifier is generated by a SCIM provider. Must be unique per user.
// Bellow: Only populated as a result of calling SetSCIMInformationForProvisionedGroup:
Schemas []string `json:"schemas,omitempty"` // The URIs that are used to indicate the namespaces of the SCIM schemas.
ID *string `json:"id,omitempty"` // The internally generated id for the group object.
Meta *SCIMEnterpriseMeta `json:"meta,omitempty"` // The metadata associated with the creation/updates to the group.
}

// SCIMEnterpriseDisplayReference represents a JSON SCIM (System for Cross-domain Identity Management) resource reference.
type SCIMEnterpriseDisplayReference struct {
Value string `json:"value"` // The local unique identifier for the member (e.g., user ID or group ID).
Ref string `json:"$ref"` // The URI reference to the member resource (e.g., https://api.github.com/scim/v2/Users/{id}).
Display *string `json:"display,omitempty"` // The display name associated with the member (e.g., user name or group name).
}

// SCIMEnterpriseMeta represents metadata about the SCIM resource.
type SCIMEnterpriseMeta struct {
ResourceType string `json:"resourceType"` // A type of a resource (`User` or `Group`).
Created *Timestamp `json:"created,omitempty"` // A date and time when the user was created.
LastModified *Timestamp `json:"lastModified,omitempty"` // A date and time when the user was last modified.
Location *string `json:"location,omitempty"` // A URL location of an object
}

// SCIMEnterpriseGroups represents the result of calling ListProvisionedSCIMGroups.
type SCIMEnterpriseGroups struct {
Schemas []string `json:"schemas,omitempty"`
TotalResults *int `json:"totalResults,omitempty"`
Resources []*SCIMEnterpriseGroupAttributes `json:"Resources,omitempty"`
StartIndex *int `json:"startIndex,omitempty"`
ItemsPerPage *int `json:"itemsPerPage,omitempty"`
}

// ListProvisionedSCIMGroupsEnterpriseOptions represents query parameters for ListProvisionedSCIMGroups.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise--parameters
type ListProvisionedSCIMGroupsEnterpriseOptions struct {
// If specified, only results that match the specified filter will be returned.
// Possible filters are `externalId`, `id`, and `displayName`. For example, `externalId eq "a123"`.
Filter string `url:"filter,omitempty"`
// Excludes the specified attribute from being returned in the results.
ExcludedAttributes string `url:"excludedAttributes,omitempty"`
// Used for pagination: the starting index of the first result to return when paginating through values.
// Default: 1.
StartIndex int `url:"startIndex,omitempty"`
// Used for pagination: the number of results to return per page.
// Default: 30.
Count int `url:"count,omitempty"`
}

// ListProvisionedSCIMGroups lists provisioned SCIM groups in an enterprise.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise
//
//meta:operation GET /scim/v2/enterprises/{enterprise}/Groups
func (s *EnterpriseService) ListProvisionedSCIMGroups(ctx context.Context, enterprise string, opts *ListProvisionedSCIMGroupsEnterpriseOptions) (*SCIMEnterpriseGroups, *Response, error) {
u := fmt.Sprintf("scim/v2/enterprises/%v/Groups", enterprise)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

groups := new(SCIMEnterpriseGroups)
resp, err := s.client.Do(ctx, req, groups)
if err != nil {
return nil, resp, err
}

return groups, resp, nil
}
201 changes: 201 additions & 0 deletions github/enterprise_scim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright 2025 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
"net/http"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestSCIMEnterpriseGroups_Marshal(t *testing.T) {
t.Parallel()
testJSONMarshal(t, &SCIMEnterpriseGroups{}, "{}")

u := &SCIMEnterpriseGroups{
Schemas: []string{SCIMSchemasURINamespacesListResponse},
TotalResults: Ptr(1),
ItemsPerPage: Ptr(1),
StartIndex: Ptr(1),
Resources: []*SCIMEnterpriseGroupAttributes{{
DisplayName: Ptr("gn1"),
Members: []*SCIMEnterpriseDisplayReference{{
Value: "idm1",
Ref: "https://api.github.com/scim/v2/enterprises/ee/Users/idm1",
Display: Ptr("m1"),
}},
Schemas: []string{SCIMSchemasURINamespacesGroups},
ExternalID: Ptr("eidgn1"),
ID: Ptr("idgn1"),
Meta: &SCIMEnterpriseMeta{
ResourceType: "Group",
Created: &Timestamp{referenceTime},
LastModified: &Timestamp{referenceTime},
Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Groups/idgn1"),
},
}},
}

want := `{
"schemas": ["` + SCIMSchemasURINamespacesListResponse + `"],
"totalResults": 1,
"itemsPerPage": 1,
"startIndex": 1,
"Resources": [{
"schemas": ["` + SCIMSchemasURINamespacesGroups + `"],
"id": "idgn1",
"externalId": "eidgn1",
"displayName": "gn1",
"meta": {
"resourceType": "Group",
"created": ` + referenceTimeStr + `,
"lastModified": ` + referenceTimeStr + `,
"location": "https://api.github.com/scim/v2/enterprises/ee/Groups/idgn1"
},
"members": [{
"value": "idm1",
"$ref": "https://api.github.com/scim/v2/enterprises/ee/Users/idm1",
"display": "m1"
}]
}]
}`

testJSONMarshal(t, u, want)
}

func TestSCIMEnterpriseGroupAttributes_Marshal(t *testing.T) {
t.Parallel()
testJSONMarshal(t, &SCIMEnterpriseGroupAttributes{}, "{}")

u := &SCIMEnterpriseGroupAttributes{
DisplayName: Ptr("dn"),
Members: []*SCIMEnterpriseDisplayReference{{
Value: "v",
Ref: "r",
Display: Ptr("d"),
}},
ExternalID: Ptr("eid"),
ID: Ptr("id"),
Schemas: []string{"s1"},
Meta: &SCIMEnterpriseMeta{
ResourceType: "rt",
Created: &Timestamp{referenceTime},
LastModified: &Timestamp{referenceTime},
Location: Ptr("l"),
},
}

want := `{
"schemas": ["s1"],
"externalId": "eid",
"displayName": "dn",
"members" : [{
"value": "v",
"$ref": "r",
"display": "d"
}],
"id": "id",
"meta": {
"resourceType": "rt",
"created": ` + referenceTimeStr + `,
"lastModified": ` + referenceTimeStr + `,
"location": "l"
}
}`

testJSONMarshal(t, u, want)
}

func TestEnterpriseService_ListProvisionedSCIMEnterpriseGroups(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/scim/v2/enterprises/ee/Groups", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{
"startIndex": "1",
"excludedAttributes": "members,meta",
"count": "3",
"filter": `externalId eq "914a"`,
})
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{
"schemas": ["` + SCIMSchemasURINamespacesListResponse + `"],
"totalResults": 1,
"itemsPerPage": 1,
"startIndex": 1,
"Resources": [{
"schemas": ["` + SCIMSchemasURINamespacesGroups + `"],
"id": "914a",
"externalId": "de88",
"displayName": "gn1",
"meta": {
"resourceType": "Group",
"created": ` + referenceTimeStr + `,
"lastModified": ` + referenceTimeStr + `,
"location": "https://api.github.com/scim/v2/enterprises/ee/Groups/914a"
},
"members": [{
"value": "e7f9",
"$ref": "https://api.github.com/scim/v2/enterprises/ee/Users/e7f9",
"display": "d1"
}]
}]
}`))
})

ctx := t.Context()
opts := &ListProvisionedSCIMGroupsEnterpriseOptions{
StartIndex: 1,
ExcludedAttributes: "members,meta",
Count: 3,
Filter: `externalId eq "914a"`,
}
groups, _, err := client.Enterprise.ListProvisionedSCIMGroups(ctx, "ee", opts)
if err != nil {
t.Errorf("Enterprise.ListProvisionedSCIMGroups returned error: %v", err)
}

want := SCIMEnterpriseGroups{
Schemas: []string{SCIMSchemasURINamespacesListResponse},
TotalResults: Ptr(1),
ItemsPerPage: Ptr(1),
StartIndex: Ptr(1),
Resources: []*SCIMEnterpriseGroupAttributes{{
ID: Ptr("914a"),
Meta: &SCIMEnterpriseMeta{
ResourceType: "Group",
Created: &Timestamp{referenceTime},
LastModified: &Timestamp{referenceTime},
Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Groups/914a"),
},
DisplayName: Ptr("gn1"),
Schemas: []string{SCIMSchemasURINamespacesGroups},
ExternalID: Ptr("de88"),
Members: []*SCIMEnterpriseDisplayReference{{
Value: "e7f9",
Ref: "https://api.github.com/scim/v2/enterprises/ee/Users/e7f9",
Display: Ptr("d1"),
}},
}},
}

if diff := cmp.Diff(want, *groups); diff != "" {
t.Errorf("Enterprise.ListProvisionedSCIMGroups diff mismatch (-want +got):\n%v", diff)
}

const methodName = "ListProvisionedSCIMGroups"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Enterprise.ListProvisionedSCIMGroups(ctx, "\n", opts)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
_, r, err := client.Enterprise.ListProvisionedSCIMGroups(ctx, "o", opts)
return r, err
})
}
Loading
Loading