@@ -37,6 +37,21 @@ type SCIMClient struct {
3737 baseURL * url.URL
3838}
3939
40+ // scimPatchOp represents a single SCIM patch operation.
41+ // https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2
42+ type scimPatchOp struct {
43+ Op string `json:"op"`
44+ Path string `json:"path,omitempty"`
45+ Value any `json:"value,omitempty"`
46+ }
47+
48+ // scimPatchPayload is the body of a SCIM PATCH request.
49+ // https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2
50+ type scimPatchPayload struct {
51+ Schemas []string `json:"schemas"`
52+ Operations []scimPatchOp `json:"Operations"`
53+ }
54+
4055// NewSCIMClient creates a new client for the GHES SCIM API.
4156func NewSCIMClient (httpClient * http.Client , baseURL string ) (* SCIMClient , error ) {
4257 u , err := url .Parse (strings .TrimSuffix (baseURL , "/" ) + ghesSCIMURLPath )
@@ -79,6 +94,9 @@ func (c *SCIMClient) ListUsers(ctx context.Context) (map[string]*github.SCIMUser
7994// CreateUser provisions a new user.
8095func (c * SCIMClient ) CreateUser (ctx context.Context , user * github.SCIMUserAttributes ) (* github.SCIMUserAttributes , * github.Response , error ) {
8196 path := "Users"
97+ // Schema for POST: https://datatracker.ietf.org/doc/html/rfc7644#section-3.3
98+ user .Schemas = append (user .Schemas , "urn:ietf:params:scim:schemas:core:2.0:User" )
99+ user .Active = github .Bool (true )
82100 var createdUser github.SCIMUserAttributes
83101 resp , err := c .do (ctx , http .MethodPost , c .baseURL .ResolveReference (& url.URL {Path : path }).String (), user , & createdUser )
84102 if err != nil {
@@ -101,6 +119,8 @@ func (c *SCIMClient) GetUser(ctx context.Context, scimID string) (*github.SCIMUs
101119// UpdateUser updates a user's attributes.
102120func (c * SCIMClient ) UpdateUser (ctx context.Context , scimID string , user * github.SCIMUserAttributes ) (* github.SCIMUserAttributes , * github.Response , error ) {
103121 path := fmt .Sprintf ("Users/%s" , scimID )
122+ // Schema for PUT: https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.1
123+ user .Schemas = append (user .Schemas , "urn:ietf:params:scim:schemas:core:2.0:User" )
104124 var updatedUser github.SCIMUserAttributes
105125 resp , err := c .do (ctx , http .MethodPut , c .baseURL .ResolveReference (& url.URL {Path : path }).String (), user , & updatedUser )
106126 if err != nil {
@@ -113,7 +133,16 @@ func (c *SCIMClient) UpdateUser(ctx context.Context, scimID string, user *github
113133// https://docs.github.com/en/enterprise-server@3.17/admin/managing-iam/provisioning-user-accounts-with-scim/provisioning-users-and-groups-with-scim-using-the-rest-api#soft-deprovisioning-users-with-the-rest-api
114134func (c * SCIMClient ) DeactivateUser (ctx context.Context , scimID string ) (* github.SCIMUserAttributes , * github.Response , error ) {
115135 path := fmt .Sprintf ("Users/%s" , scimID )
116- payload := & github.SCIMUserAttributes {Active : github .Bool (false )}
136+ // Schema for PATCH: https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2
137+ payload := & scimPatchPayload {
138+ Schemas : []string {"urn:ietf:params:scim:api:messages:2.0:PatchOp" },
139+ Operations : []scimPatchOp {
140+ {
141+ Op : "replace" ,
142+ Value : map [string ]bool {"active" : false },
143+ },
144+ },
145+ }
117146 var deactivatedUser github.SCIMUserAttributes
118147 resp , err := c .do (ctx , http .MethodPatch , c .baseURL .ResolveReference (& url.URL {Path : path }).String (), payload , & deactivatedUser )
119148 if err != nil {
@@ -126,7 +155,15 @@ func (c *SCIMClient) DeactivateUser(ctx context.Context, scimID string) (*github
126155// https://docs.github.com/en/enterprise-server@3.17/admin/managing-iam/provisioning-user-accounts-with-scim/deprovisioning-and-reinstating-users#reinstating-a-user-account-that-was-soft-deprovisioned
127156func (c * SCIMClient ) ReactivateUser (ctx context.Context , scimID string ) (* github.SCIMUserAttributes , * github.Response , error ) {
128157 path := fmt .Sprintf ("Users/%s" , scimID )
129- payload := & github.SCIMUserAttributes {Active : github .Bool (true )}
158+ payload := & scimPatchPayload {
159+ Schemas : []string {"urn:ietf:params:scim:api:messages:2.0:PatchOp" },
160+ Operations : []scimPatchOp {
161+ {
162+ Op : "replace" ,
163+ Value : map [string ]bool {"active" : true },
164+ },
165+ },
166+ }
130167 var reactivatedUser github.SCIMUserAttributes
131168 resp , err := c .do (ctx , http .MethodPatch , c .baseURL .ResolveReference (& url.URL {Path : path }).String (), payload , & reactivatedUser )
132169 if err != nil {
0 commit comments