diff --git a/ee/server/integrationtest/scim/scim_test.go b/ee/server/integrationtest/scim/scim_test.go index 4c72defca1..1c00549862 100644 --- a/ee/server/integrationtest/scim/scim_test.go +++ b/ee/server/integrationtest/scim/scim_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/elimity-com/scim/errors" "github.com/fleetdm/fleet/v4/server/authz" "github.com/fleetdm/fleet/v4/server/datastore/mysql" "github.com/fleetdm/fleet/v4/server/service" @@ -710,16 +711,10 @@ func testCreateUser(t *testing.T, s *Suite) { "active": true, } - var createResp1 map[string]interface{} - s.DoJSON(t, "POST", scimPath("/Users"), userWithoutGivenName, http.StatusCreated, &createResp1) - assert.Equal(t, "no-given-name@example.com", createResp1["userName"]) - userID1 := createResp1["id"].(string) - - // Verify name only has familyName - name1, ok := createResp1["name"].(map[string]interface{}) - assert.True(t, ok, "Name should be an object") - assert.Equal(t, "NoGivenName", name1["familyName"]) - assert.Nil(t, name1["givenName"], "givenName should be nil") + var errorResp map[string]interface{} + s.DoJSON(t, "POST", scimPath("/Users"), userWithoutGivenName, http.StatusBadRequest, &errorResp) + assert.EqualValues(t, errorResp["schemas"], []interface{}{"urn:ietf:params:scim:api:messages:2.0:Error"}) + assert.Equal(t, errors.ScimErrorInvalidValue.Detail, errorResp["detail"]) // Test creating a user without familyName userWithoutFamilyName := map[string]interface{}{ @@ -738,16 +733,10 @@ func testCreateUser(t *testing.T, s *Suite) { "active": true, } - var createResp2 map[string]interface{} - s.DoJSON(t, "POST", scimPath("/Users"), userWithoutFamilyName, http.StatusCreated, &createResp2) - assert.Equal(t, "no-family-name@example.com", createResp2["userName"]) - userID2 := createResp2["id"].(string) - - // Verify name only has givenName - name2, ok := createResp2["name"].(map[string]interface{}) - assert.True(t, ok, "Name should be an object") - assert.Equal(t, "NoFamilyName", name2["givenName"]) - assert.Nil(t, name2["familyName"], "familyName should be nil") + errorResp = nil + s.DoJSON(t, "POST", scimPath("/Users"), userWithoutFamilyName, http.StatusBadRequest, &errorResp) + assert.EqualValues(t, errorResp["schemas"], []interface{}{"urn:ietf:params:scim:api:messages:2.0:Error"}) + assert.Equal(t, errors.ScimErrorInvalidValue.Detail, errorResp["detail"]) // Test creating a user without emails userWithoutEmails := map[string]interface{}{ @@ -937,8 +926,6 @@ func testCreateUser(t *testing.T, s *Suite) { assert.Equal(t, "external-system-123456", createResp6["externalId"]) // Make sure these users can be deleted. - s.Do(t, "DELETE", scimPath("/Users/"+userID1), nil, http.StatusNoContent) - s.Do(t, "DELETE", scimPath("/Users/"+userID2), nil, http.StatusNoContent) s.Do(t, "DELETE", scimPath("/Users/"+userID3), nil, http.StatusNoContent) s.Do(t, "DELETE", scimPath("/Users/"+userID4), nil, http.StatusNoContent) s.Do(t, "DELETE", scimPath("/Users/"+userID5), nil, http.StatusNoContent) @@ -1050,7 +1037,8 @@ func testPatchUserFailure(t *testing.T, s *Suite) { "value": map[string]interface{}{ "active": false, "name": map[string]interface{}{ - "givenName": "Updated", + "givenName": "Updated", + "familyName": "Updated", }, }, }, diff --git a/ee/server/scim/scim.go b/ee/server/scim/scim.go index 2a47757599..e0a6bbe1f1 100644 --- a/ee/server/scim/scim.go +++ b/ee/server/scim/scim.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/elimity-com/scim" "github.com/elimity-com/scim/errors" @@ -54,10 +55,12 @@ func RegisterSCIM( schema.SimpleStringParams(schema.StringParams{ Description: optional.NewString("The family name of the User, or last name in most Western languages (e.g., 'Jensen' given the full name 'Ms. Barbara J Jensen, III')."), Name: "familyName", + Required: true, }), schema.SimpleStringParams(schema.StringParams{ Description: optional.NewString("The given name of the User, or first name in most Western languages (e.g., 'Barbara' given the full name 'Ms. Barbara J Jensen, III')."), Name: "givenName", + Required: true, }), }, }), @@ -242,6 +245,11 @@ func LastRequestMiddleware(ds fleet.Datastore, logger kitlog.Logger, next http.H } else { details = multi.body.String() } + if multi.statusCode == errors.ScimErrorInvalidValue.Status && details == errors.ScimErrorInvalidValue.Detail && + strings.Contains(r.URL.Path, "/Users") { + // We customize the error message here since we can't do it inside the 3rd party SCIM library. + details = `Missing required attributes. "userName", "givenName", and "familyName" are required. Please configure your identity provider to send required attributes to Fleet.` + } default: status = "error" details = fmt.Sprintf("Unhandled status code: %d", multi.statusCode)