github.com/google/go-github/v69@v69.2.0/github/scim_test.go (about) 1 // Copyright 2021 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "context" 10 "fmt" 11 "net/http" 12 "testing" 13 "time" 14 15 "github.com/google/go-cmp/cmp" 16 ) 17 18 func TestSCIMService_ListSCIMProvisionedIdentities(t *testing.T) { 19 t.Parallel() 20 client, mux, _ := setup(t) 21 22 mux.HandleFunc("/scim/v2/organizations/o/Users", func(w http.ResponseWriter, r *http.Request) { 23 testMethod(t, r, "GET") 24 w.WriteHeader(http.StatusOK) 25 _, _ = w.Write([]byte(`{ 26 "schemas": [ 27 "urn:ietf:params:scim:api:messages:2.0:ListResponse" 28 ], 29 "totalResults": 1, 30 "itemsPerPage": 1, 31 "startIndex": 1, 32 "Resources": [ 33 { 34 "schemas": [ 35 "urn:ietf:params:scim:schemas:core:2.0:User" 36 ], 37 "id": "5fc0c238-1112-11e8-8e45-920c87bdbd75", 38 "externalId": "00u1dhhb1fkIGP7RL1d8", 39 "userName": "octocat@github.com", 40 "displayName": "Mona Octocat", 41 "name": { 42 "givenName": "Mona", 43 "familyName": "Octocat", 44 "formatted": "Mona Octocat" 45 }, 46 "emails": [ 47 { 48 "value": "octocat@github.com", 49 "primary": true 50 } 51 ], 52 "active": true, 53 "meta": { 54 "resourceType": "User", 55 "created": "2018-02-13T15:05:24.000-00:00", 56 "lastModified": "2018-02-13T15:05:24.000-00:00", 57 "location": "https://api.github.com/scim/v2/organizations/octo-org/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75" 58 } 59 } 60 ] 61 }`)) 62 }) 63 64 ctx := context.Background() 65 opts := &ListSCIMProvisionedIdentitiesOptions{} 66 identities, _, err := client.SCIM.ListSCIMProvisionedIdentities(ctx, "o", opts) 67 if err != nil { 68 t.Errorf("SCIM.ListSCIMProvisionedIdentities returned error: %v", err) 69 } 70 71 date := Timestamp{time.Date(2018, time.February, 13, 15, 5, 24, 0, time.UTC)} 72 want := SCIMProvisionedIdentities{ 73 Schemas: []string{"urn:ietf:params:scim:api:messages:2.0:ListResponse"}, 74 TotalResults: Ptr(1), 75 ItemsPerPage: Ptr(1), 76 StartIndex: Ptr(1), 77 Resources: []*SCIMUserAttributes{ 78 { 79 ID: Ptr("5fc0c238-1112-11e8-8e45-920c87bdbd75"), 80 Meta: &SCIMMeta{ 81 ResourceType: Ptr("User"), 82 Created: &date, 83 LastModified: &date, 84 Location: Ptr("https://api.github.com/scim/v2/organizations/octo-org/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75"), 85 }, 86 UserName: "octocat@github.com", 87 Name: SCIMUserName{ 88 GivenName: "Mona", 89 FamilyName: "Octocat", 90 Formatted: Ptr("Mona Octocat"), 91 }, 92 DisplayName: Ptr("Mona Octocat"), 93 Emails: []*SCIMUserEmail{ 94 { 95 Value: "octocat@github.com", 96 Primary: Ptr(true), 97 }, 98 }, 99 Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:User"}, 100 ExternalID: Ptr("00u1dhhb1fkIGP7RL1d8"), 101 Groups: nil, 102 Active: Ptr(true), 103 }, 104 }, 105 } 106 107 if !cmp.Equal(identities, &want) { 108 diff := cmp.Diff(identities, want) 109 t.Errorf("SCIM.ListSCIMProvisionedIdentities returned %+v, want %+v: diff %+v", identities, want, diff) 110 } 111 112 const methodName = "ListSCIMProvisionedIdentities" 113 testBadOptions(t, methodName, func() (err error) { 114 _, _, err = client.SCIM.ListSCIMProvisionedIdentities(ctx, "\n", opts) 115 return err 116 }) 117 118 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 119 _, r, err := client.SCIM.ListSCIMProvisionedIdentities(ctx, "o", opts) 120 return r, err 121 }) 122 } 123 124 func TestSCIMService_ListSCIMProvisionedGroups(t *testing.T) { 125 t.Parallel() 126 client, mux, _ := setup(t) 127 128 mux.HandleFunc("/scim/v2/enterprises/o/Groups", func(w http.ResponseWriter, r *http.Request) { 129 testMethod(t, r, "GET") 130 w.WriteHeader(http.StatusOK) 131 _, _ = w.Write([]byte(`{ 132 "schemas": [ 133 "urn:ietf:params:scim:api:messages:2.0:ListResponse" 134 ], 135 "totalResults": 1, 136 "itemsPerPage": 1, 137 "startIndex": 1, 138 "Resources": [ 139 { 140 "schemas": [ 141 "urn:ietf:params:scim:schemas:core:2.0:Group" 142 ], 143 "id": "123e4567-e89b-12d3-a456-426614174000", 144 "externalId": "00u1dhhb1fkIGP7RL1d8", 145 "displayName": "Mona Octocat", 146 "meta": { 147 "resourceType": "Group", 148 "created": "2018-02-13T15:05:24.000-00:00", 149 "lastModified": "2018-02-13T15:05:24.000-00:00", 150 "location": "https://api.github.com/scim/v2/enterprises/octo/Groups/123e4567-e89b-12d3-a456-426614174000" 151 }, 152 "members": [ 153 { 154 "value": "5fc0c238-1112-11e8-8e45-920c87bdbd75", 155 "$ref": "https://api.github.com/scim/v2/enterprises/octo/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75", 156 "display": "Mona Octocat" 157 } 158 ] 159 } 160 ] 161 }`)) 162 }) 163 164 ctx := context.Background() 165 opts := &ListSCIMProvisionedIdentitiesOptions{} 166 groups, _, err := client.SCIM.ListSCIMProvisionedGroupsForEnterprise(ctx, "o", opts) 167 if err != nil { 168 t.Errorf("SCIM.ListSCIMProvisionedIdentities returned error: %v", err) 169 } 170 171 date := Timestamp{time.Date(2018, time.February, 13, 15, 5, 24, 0, time.UTC)} 172 want := SCIMProvisionedGroups{ 173 Schemas: []string{"urn:ietf:params:scim:api:messages:2.0:ListResponse"}, 174 TotalResults: Ptr(1), 175 ItemsPerPage: Ptr(1), 176 StartIndex: Ptr(1), 177 Resources: []*SCIMGroupAttributes{ 178 { 179 ID: Ptr("123e4567-e89b-12d3-a456-426614174000"), 180 Meta: &SCIMMeta{ 181 ResourceType: Ptr("Group"), 182 Created: &date, 183 LastModified: &date, 184 Location: Ptr("https://api.github.com/scim/v2/enterprises/octo/Groups/123e4567-e89b-12d3-a456-426614174000"), 185 }, 186 187 DisplayName: Ptr("Mona Octocat"), 188 Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:Group"}, 189 ExternalID: Ptr("00u1dhhb1fkIGP7RL1d8"), 190 Members: []*SCIMDisplayReference{ 191 { 192 Value: "5fc0c238-1112-11e8-8e45-920c87bdbd75", 193 Ref: "https://api.github.com/scim/v2/enterprises/octo/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75", 194 Display: Ptr("Mona Octocat"), 195 }, 196 }, 197 }, 198 }, 199 } 200 201 if !cmp.Equal(groups, &want) { 202 diff := cmp.Diff(groups, want) 203 t.Errorf("SCIM.ListSCIMProvisionedGroupsForEnterprise returned %+v, want %+v: diff %+v", groups, want, diff) 204 } 205 206 const methodName = "ListSCIMProvisionedGroupsForEnterprise" 207 testBadOptions(t, methodName, func() (err error) { 208 _, _, err = client.SCIM.ListSCIMProvisionedGroupsForEnterprise(ctx, "\n", opts) 209 return err 210 }) 211 212 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 213 _, r, err := client.SCIM.ListSCIMProvisionedGroupsForEnterprise(ctx, "o", opts) 214 return r, err 215 }) 216 } 217 218 func TestSCIMService_ProvisionAndInviteSCIMUser(t *testing.T) { 219 t.Parallel() 220 client, mux, _ := setup(t) 221 222 mux.HandleFunc("/scim/v2/organizations/o/Users", func(w http.ResponseWriter, r *http.Request) { 223 testMethod(t, r, "POST") 224 w.WriteHeader(http.StatusCreated) 225 fmt.Fprint(w, `{"id":"1234567890","userName":"userName"}`) 226 }) 227 228 ctx := context.Background() 229 opts := &SCIMUserAttributes{ 230 UserName: "userName", 231 Name: SCIMUserName{ 232 GivenName: "givenName", 233 FamilyName: "familyName", 234 }, 235 Emails: []*SCIMUserEmail{ 236 { 237 Value: "octocat@github.com", 238 }, 239 }, 240 } 241 user, _, err := client.SCIM.ProvisionAndInviteSCIMUser(ctx, "o", opts) 242 if err != nil { 243 t.Errorf("SCIM.ProvisionAndInviteSCIMUser returned error: %v", err) 244 } 245 246 want := &SCIMUserAttributes{ 247 ID: Ptr("1234567890"), 248 UserName: "userName", 249 } 250 if !cmp.Equal(user, want) { 251 t.Errorf("SCIM.ProvisionAndInviteSCIMUser returned %+v, want %+v", user, want) 252 } 253 254 const methodName = "ProvisionAndInviteSCIMUser" 255 testBadOptions(t, methodName, func() (err error) { 256 _, _, err = client.SCIM.ProvisionAndInviteSCIMUser(ctx, "\n", opts) 257 return err 258 }) 259 260 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 261 got, resp, err := client.SCIM.ProvisionAndInviteSCIMUser(ctx, "o", opts) 262 if got != nil { 263 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 264 } 265 return resp, err 266 }) 267 } 268 269 func TestSCIMService_GetSCIMProvisioningInfoForUser(t *testing.T) { 270 t.Parallel() 271 client, mux, _ := setup(t) 272 273 mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { 274 testMethod(t, r, "GET") 275 w.WriteHeader(http.StatusOK) 276 _, _ = w.Write([]byte(`{ 277 "schemas": [ 278 "urn:ietf:params:scim:schemas:core:2.0:User" 279 ], 280 "id": "edefdfedf-050c-11e7-8d32", 281 "externalId": "a7d0f98382", 282 "userName": "mona.octocat@okta.example.com", 283 "displayName": "Mona Octocat", 284 "name": { 285 "givenName": "Mona", 286 "familyName": "Octocat", 287 "formatted": "Mona Octocat" 288 }, 289 "emails": [ 290 { 291 "value": "mona.octocat@okta.example.com", 292 "primary": true 293 }, 294 { 295 "value": "mona@octocat.github.com" 296 } 297 ], 298 "active": true, 299 "meta": { 300 "resourceType": "User", 301 "created": "2017-03-09T16:11:13-00:00", 302 "lastModified": "2017-03-09T16:11:13-00:00", 303 "location": "https://api.github.com/scim/v2/organizations/octo-org/Users/edefdfedf-050c-11e7-8d32" 304 } 305 }`)) 306 }) 307 308 ctx := context.Background() 309 user, _, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "o", "123") 310 if err != nil { 311 t.Errorf("SCIM.GetSCIMProvisioningInfoForUser returned error: %v", err) 312 } 313 314 date := Timestamp{time.Date(2017, time.March, 9, 16, 11, 13, 0, time.UTC)} 315 want := SCIMUserAttributes{ 316 ID: Ptr("edefdfedf-050c-11e7-8d32"), 317 Meta: &SCIMMeta{ 318 ResourceType: Ptr("User"), 319 Created: &date, 320 LastModified: &date, 321 Location: Ptr("https://api.github.com/scim/v2/organizations/octo-org/Users/edefdfedf-050c-11e7-8d32"), 322 }, 323 UserName: "mona.octocat@okta.example.com", 324 Name: SCIMUserName{ 325 GivenName: "Mona", 326 FamilyName: "Octocat", 327 Formatted: Ptr("Mona Octocat"), 328 }, 329 DisplayName: Ptr("Mona Octocat"), 330 Emails: []*SCIMUserEmail{ 331 { 332 Value: "mona.octocat@okta.example.com", 333 Primary: Ptr(true), 334 }, 335 { 336 Value: "mona@octocat.github.com", 337 }, 338 }, 339 Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:User"}, 340 ExternalID: Ptr("a7d0f98382"), 341 Groups: nil, 342 Active: Ptr(true), 343 } 344 345 if !cmp.Equal(user, &want) { 346 diff := cmp.Diff(user, want) 347 t.Errorf("SCIM.ListSCIMProvisionedIdentities returned %+v, want %+v: diff %+v", user, want, diff) 348 } 349 350 const methodName = "GetSCIMProvisioningInfoForUser" 351 testBadOptions(t, methodName, func() error { 352 _, _, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "\n", "123") 353 return err 354 }) 355 356 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 357 _, r, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "o", "123") 358 return r, err 359 }) 360 } 361 362 func TestSCIMService_UpdateProvisionedOrgMembership(t *testing.T) { 363 t.Parallel() 364 client, mux, _ := setup(t) 365 366 mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { 367 testMethod(t, r, "PUT") 368 w.WriteHeader(http.StatusOK) 369 }) 370 371 ctx := context.Background() 372 opts := &SCIMUserAttributes{ 373 UserName: "userName", 374 Name: SCIMUserName{ 375 GivenName: "givenName", 376 FamilyName: "familyName", 377 }, 378 Emails: []*SCIMUserEmail{ 379 { 380 Value: "octocat@github.com", 381 }, 382 }, 383 } 384 _, err := client.SCIM.UpdateProvisionedOrgMembership(ctx, "o", "123", opts) 385 if err != nil { 386 t.Errorf("SCIM.UpdateProvisionedOrgMembership returned error: %v", err) 387 } 388 389 const methodName = "UpdateProvisionedOrgMembership" 390 testBadOptions(t, methodName, func() error { 391 _, err := client.SCIM.UpdateProvisionedOrgMembership(ctx, "\n", "123", opts) 392 return err 393 }) 394 395 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 396 return client.SCIM.UpdateProvisionedOrgMembership(ctx, "o", "123", opts) 397 }) 398 } 399 400 func TestSCIMService_UpdateAttributeForSCIMUser(t *testing.T) { 401 t.Parallel() 402 client, mux, _ := setup(t) 403 404 mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { 405 testMethod(t, r, "PATCH") 406 w.WriteHeader(http.StatusNoContent) 407 }) 408 409 ctx := context.Background() 410 opts := &UpdateAttributeForSCIMUserOptions{} 411 _, err := client.SCIM.UpdateAttributeForSCIMUser(ctx, "o", "123", opts) 412 if err != nil { 413 t.Errorf("SCIM.UpdateAttributeForSCIMUser returned error: %v", err) 414 } 415 416 const methodName = "UpdateAttributeForSCIMUser" 417 testBadOptions(t, methodName, func() error { 418 _, err := client.SCIM.UpdateAttributeForSCIMUser(ctx, "\n", "123", opts) 419 return err 420 }) 421 422 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 423 return client.SCIM.UpdateAttributeForSCIMUser(ctx, "o", "123", opts) 424 }) 425 } 426 427 func TestSCIMService_DeleteSCIMUserFromOrg(t *testing.T) { 428 t.Parallel() 429 client, mux, _ := setup(t) 430 431 mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { 432 testMethod(t, r, "DELETE") 433 w.WriteHeader(http.StatusNoContent) 434 }) 435 436 ctx := context.Background() 437 _, err := client.SCIM.DeleteSCIMUserFromOrg(ctx, "o", "123") 438 if err != nil { 439 t.Errorf("SCIM.DeleteSCIMUserFromOrg returned error: %v", err) 440 } 441 442 const methodName = "DeleteSCIMUserFromOrg" 443 testBadOptions(t, methodName, func() error { 444 _, err := client.SCIM.DeleteSCIMUserFromOrg(ctx, "\n", "") 445 return err 446 }) 447 448 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 449 return client.SCIM.DeleteSCIMUserFromOrg(ctx, "o", "123") 450 }) 451 } 452 453 func TestSCIMUserAttributes_Marshal(t *testing.T) { 454 t.Parallel() 455 testJSONMarshal(t, &SCIMUserAttributes{}, `{ 456 "userName":"","name":{"givenName":"","familyName":""},"emails":null 457 }`) 458 459 u := &SCIMUserAttributes{ 460 UserName: "userName1", 461 Name: SCIMUserName{ 462 GivenName: "Name1", 463 FamilyName: "Fname", 464 Formatted: Ptr("formatted name"), 465 }, 466 DisplayName: Ptr("Name"), 467 Emails: []*SCIMUserEmail{ 468 { 469 Value: "value", 470 Primary: Ptr(false), 471 Type: Ptr("type"), 472 }, 473 }, 474 Schemas: []string{"schema1"}, 475 ExternalID: Ptr("id"), 476 Groups: []string{"group1"}, 477 Active: Ptr(true), 478 } 479 480 want := `{ 481 "userName": "userName1", 482 "name": { 483 "givenName": "Name1", 484 "familyName": "Fname", 485 "formatted": "formatted name" 486 }, 487 "displayName": "Name", 488 "emails": [{ 489 "value": "value", 490 "primary": false, 491 "type": "type" 492 }], 493 "schemas": ["schema1"], 494 "externalId": "id", 495 "groups": ["group1"], 496 "active": true 497 }` 498 499 testJSONMarshal(t, u, want) 500 } 501 502 func TestUpdateAttributeForSCIMUserOperations_Marshal(t *testing.T) { 503 t.Parallel() 504 testJSONMarshal(t, &UpdateAttributeForSCIMUserOperations{}, `{}`) 505 506 u := &UpdateAttributeForSCIMUserOperations{ 507 Op: "TestOp", 508 Path: Ptr("path"), 509 } 510 511 want := `{ 512 "op": "TestOp", 513 "path": "path" 514 }` 515 516 testJSONMarshal(t, u, want) 517 } 518 519 func TestUpdateAttributeForSCIMUserOptions_Marshal(t *testing.T) { 520 t.Parallel() 521 testJSONMarshal(t, &UpdateAttributeForSCIMUserOptions{}, `{}`) 522 523 u := &UpdateAttributeForSCIMUserOptions{ 524 Schemas: []string{"test", "schema"}, 525 Operations: UpdateAttributeForSCIMUserOperations{ 526 Op: "TestOp", 527 Path: Ptr("path"), 528 }, 529 } 530 531 want := `{ 532 "schemas": ["test", "schema"], 533 "operations": { 534 "op": "TestOp", 535 "path": "path" 536 } 537 }` 538 539 testJSONMarshal(t, u, want) 540 } 541 542 func TestListSCIMProvisionedIdentitiesOptions_addOptions(t *testing.T) { 543 t.Parallel() 544 testJSONMarshal(t, &ListSCIMProvisionedIdentitiesOptions{}, `{ 545 "StartIndex": null, 546 "Count": null, 547 "Filter": null 548 }`) 549 550 url := "some/path" 551 552 testAddURLOptions(t, url, &ListSCIMProvisionedIdentitiesOptions{}, url) 553 554 testAddURLOptions( 555 t, 556 url, 557 &ListSCIMProvisionedIdentitiesOptions{ 558 StartIndex: Ptr(1), 559 Count: Ptr(10), 560 }, 561 fmt.Sprintf("%s?count=10&startIndex=1", url), 562 ) 563 564 testAddURLOptions( 565 t, 566 url, 567 &ListSCIMProvisionedIdentitiesOptions{ 568 StartIndex: Ptr(1), 569 Count: Ptr(10), 570 Filter: Ptr("test"), 571 }, 572 fmt.Sprintf("%s?count=10&filter=test&startIndex=1", url), 573 ) 574 } 575 576 func TestSCIMUserName_Marshal(t *testing.T) { 577 t.Parallel() 578 testJSONMarshal(t, &SCIMUserName{}, `{ 579 "givenName":"","familyName":"" 580 }`) 581 582 u := &SCIMUserName{ 583 GivenName: "Name1", 584 FamilyName: "Fname", 585 Formatted: Ptr("formatted name"), 586 } 587 588 want := `{ 589 "givenName": "Name1", 590 "familyName": "Fname", 591 "formatted": "formatted name" 592 }` 593 testJSONMarshal(t, u, want) 594 } 595 596 func TestSCIMMeta_Marshal(t *testing.T) { 597 t.Parallel() 598 testJSONMarshal(t, &SCIMMeta{}, `{}`) 599 600 u := &SCIMMeta{ 601 ResourceType: Ptr("test"), 602 Location: Ptr("test"), 603 } 604 605 want := `{ 606 "resourceType": "test", 607 "location": "test" 608 }` 609 610 testJSONMarshal(t, u, want) 611 } 612 613 func TestSCIMProvisionedIdentities_Marshal(t *testing.T) { 614 t.Parallel() 615 testJSONMarshal(t, &SCIMProvisionedIdentities{}, `{}`) 616 617 u := &SCIMProvisionedIdentities{ 618 Schemas: []string{"test", "schema"}, 619 TotalResults: Ptr(1), 620 ItemsPerPage: Ptr(2), 621 StartIndex: Ptr(1), 622 Resources: []*SCIMUserAttributes{ 623 { 624 UserName: "SCIM", 625 Name: SCIMUserName{ 626 GivenName: "scim", 627 FamilyName: "test", 628 Formatted: Ptr("SCIM"), 629 }, 630 DisplayName: Ptr("Test SCIM"), 631 Emails: []*SCIMUserEmail{ 632 { 633 Value: "test", 634 Primary: Ptr(true), 635 Type: Ptr("test"), 636 }, 637 }, 638 Schemas: []string{"schema1"}, 639 ExternalID: Ptr("id"), 640 Groups: []string{"group1"}, 641 Active: Ptr(true), 642 }, 643 }, 644 } 645 646 want := `{ 647 "schemas": ["test", "schema"], 648 "totalResults": 1, 649 "itemsPerPage": 2, 650 "startIndex": 1, 651 "Resources": [{ 652 "userName": "SCIM", 653 "name": { 654 "givenName": "scim", 655 "familyName": "test", 656 "formatted": "SCIM" 657 }, 658 "displayName": "Test SCIM", 659 "emails": [{ 660 "value": "test", 661 "primary": true, 662 "type": "test" 663 }], 664 "schemas": ["schema1"], 665 "externalId": "id", 666 "groups": ["group1"], 667 "active": true 668 }] 669 }` 670 671 testJSONMarshal(t, u, want) 672 }