Enable service account creation on free tier
Summary
Enables service account creation for free tier (SaaS) and starter/unlicensed (self-managed) plans, gated behind the existing service_accounts_available_on_free_or_unlicensed feature flag.
The previous MR (!225722 (merged)) introduced the 100-account limit enforcement. This MR completes the enablement by updating the availability checks in the policy layer so free tier users can actually create and manage service accounts when the feature flag is enabled.
Note: The
service_accounts_available_on_free_or_unlicensedfeature flag must not be enabled until the follow-up MR for PAT expiry enforcement is merged. See Design document compliance below.
Changes
-
Authn::ServiceAccounts.available?: Now accepts an optionalroot_namespaceparameter and falls back to checking theservice_accounts_available_on_free_or_unlicensedfeature flag when the license does not includeservice_accounts -
Authn::ServiceAccounts.available_for_namespace?: Falls back to the same feature flag for non-trial namespaces without a paid subscription -
ee/app/policies/ee/project_policy.rb: Updatedservice_accounts_enabledcondition fromscope: :globaltoscope: :subjectand passes@subject.root_ancestortoavailable?for per-namespace feature flag targeting -
ee/lib/ee/api/entities/group_detail.rb: Replaced directlicensed_feature_available?(:service_accounts)call withAuthn::ServiceAccounts.available_for_namespace?to ensure consistency with the feature flag path -
ee/app/controllers/ee/admin/application_settings_controller.rb: Changed service account settings params to useAuthn::ServiceAccounts.available?instead ofLicense.feature_available?(:service_accounts)so free-tier admins (with FF enabled) can access SA settings
How it works
When the service_accounts_available_on_free_or_unlicensed feature flag is enabled (for a specific namespace or globally):
-
Policy layer —
service_accounts_availableconditions in group, project, and global policies returntrue, unblocking all SA permissions (create, read, delete, admin) - Service layer — The 100-account limit (from !225722 (merged)) is enforced during creation
- API/UI — Service account management endpoints and sidebar items become accessible
No changes to the license feature definition (PREMIUM_FEATURES) — the feature flag provides the free tier access path alongside the existing license check.
Design document compliance
Guards from the machine identity design document:
| Requirement | Status |
|---|---|
| 100 SA limit for free/trial |
|
| SA cannot create other SAs |
|
| SA cannot create top-level namespaces |
external: true + policy guards |
| Free tier PATs must always expire |
|
Per the design document, free tier service accounts must always have PAT expiration enforced (unlike Premium/Ultimate where it's configurable). This will be implemented in a separate follow-up MR to keep review scope focused.
How to validate
Click to expand validation steps
Prerequisites
Enable the feature flag:
# Rails console - globally
Feature.enable(:service_accounts_available_on_free_or_unlicensed)
# Or for a specific group (SaaS):
Feature.enable(:service_accounts_available_on_free_or_unlicensed, Group.find(<group_id>))
Self-Managed (Free/Unlicensed Instance)
1. Verify SA creation works
# As admin, create a service account via API
curl --request POST \
--header "PRIVATE-TOKEN: <admin_token>" \
--header "Content-Type: application/json" \
"http://localhost:3000/api/v4/service_accounts"
Expected: Service account is created successfully.
2. Verify 100 SA limit is enforced
# Rails console - check current count
User.service_accounts.count
# For testing, stub the limit:
stub_const('Authn::ServiceAccounts::LIMIT_FOR_FREE', 2)
Expected: After reaching the limit, creation fails with appropriate error.
SaaS (Free-Tier Namespace)
1. Setup a free-tier group and enable FF
# Rails console
group = Group.find_by_path('<your-group>')
# Ensure no paid subscription
group.gitlab_subscription&.destroy
Feature.enable(:service_accounts_available_on_free_or_unlicensed, group)
2. Verify SA creation works
# As group owner
curl --request POST \
--header "PRIVATE-TOKEN: <owner_token>" \
--header "Content-Type: application/json" \
"http://localhost:3000/api/v4/groups/<group_id>/service_accounts"
Expected: Service account is created successfully.
Related
- Closes #591625
- Part of &20439 (Allow SAs for Free tier)
- Depends on !225722 (merged) (merged) — limit enforcement for free tier