Implement `POST /Groups` SCIM endpoint for self-managed
This is part of #509425 (closed).
Depends on !180288 (merged).
What does this MR do and why?
This MR implements the POST /Groups
SCIM endpoint for self-managed instances. As per the conclusions made after the investigation and PoC phase, we need to provide support for those endpoints in order to fully offer the SCIM group sync functionality.
The work here is behind a feature flag, default disabled.
We're gonna lean onto the current SAML group links functionality. When the POST /Groups
is called, it will update all existing group links with the ID of the SCIM group.
Database Changes
This MR includes an update_all
operation on the saml_group_links
table.
Query:
UPDATE saml_group_links
SET scim_group_uid = :scim_group_uid
WHERE saml_group_name = :saml_group_name;
A few observations:
- The existing index
index_saml_group_links_on_group_id_and_saml_group_name
won't be optimally used since we're only filtering onsaml_group_name
- However, given that:
- This is for self-managed instances only
- SAML group links should be typically low volume (please correct me if I'm wrong)
- The uniqueness constraint on (group_id, saml_group_name) means we're likely updating very few records
- This is an administrative SCIM provisioning operation, not a high-frequency user operation
The current query should be performant enough without needing additional indexes.
References
Please include cross links to any resources that are relevant to this MR. This will give reviewers and future readers helpful context to give an efficient review of the changes introduced.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
N/A
How to set up and validate locally
-
Make sure you have SAML enabled on your GDK.
-
Enter the Rails console:
gdk rails c
- Create test SAML group links:
group = Group.first # or create a specific test group
SamlGroupLink.create!(
group: group,
saml_group_name: "engineering",
access_level: Gitlab::Access::DEVELOPER
)
SamlGroupLink.create!(
group: group,
saml_group_name: "security",
access_level: Gitlab::Access::MAINTAINER
)
- Enable the required feature flag:
Feature.enable(:self_managed_scim_group_sync)
- Create a SCIM access token:
token = ScimOauthAccessToken.create!
puts token.token # Copy this token for the curl command
- Make the API request to associate a SCIM group ID with one of the SAML groups:
curl -XPOST --location 'http://localhost:3000/api/scim/v2/application/Groups' \
--header 'Content-Type: application/scim+json' \
--header 'Authorization: Bearer <SCIM TOKEN>' \
--data '{
"displayName": "engineering",
"externalId": "'$(uuidgen)'"
}'
- Create another SCIM group with the other SAML group:
curl -XPOST --location 'http://localhost:3000/api/scim/v2/application/Groups' \
--header 'Content-Type: application/scim+json' \
--header 'Authorization: Bearer <SCIM TOKEN>' \
--data '{
"displayName": "security",
"externalId": "'$(uuidgen)'"
}'
- Verify both updates:
SamlGroupLink.where(saml_group_name: ["engineering", "security"]).pluck(:saml_group_name, :scim_group_uid)
# Should show both groups with their respective SCIM group UIDs
Expected Results
- Both API calls should return 201 status code
- The response should include the group details in SCIM format
- The
scim_group_uid
field should be updated for both SAML group links - Attempting to update a non-existent SAML group should return an error
- Attempting to call the endpoint with a non-UUID
externalId
should return an error