@idurham Thanks for the detailed review and suggestions. I have made the changes. Can you please have another look?
@GitLabDuo Can you please take a look at this MR once again?
Sijin T V (cffbf796) at 18 Mar 04:07
Address GitLab Duo review comments on User Applications API
@GitLabDuo I initially applied this change by editing
openapi_v3.yaml directly, but it turns out the file is fully auto-generated via bin/rake gitlab:openapi:v3:generate — any manual edits are overwritten on regeneration. The nullable: true on required path parameters is emitted automatically by Grape's OpenAPI generator and isn't configurable per-parameter in the DSL. This is a known quirk/limitation of the Grape swagger generation layer, not something specific to this endpoint.
Sijin T V (c30b3551) at 18 Mar 03:50
Apply 20 suggestion(s) to 2 file(s)
@GitLabDuo I initially applied this change, but it was caught by GitLab's own pre-commit RuboCop cop: Cop/AvoidReturnFromBlocks, which explicitly requires break instead of return inside blocks (including Grape route handlers). So break is actually the correct pattern for this codebase.
Sijin T V (88040831) at 18 Mar 03:06
Fix permissions CI check by adding granular access token authorization
... and 597 more commits
@victorprete This is ready for review.
@GitLabDuo Session cleanup already handled correctly — can you please verify?
session[:invite_email] is cleared immediately upon successful signup via session.delete(:invite_email) in registered_with_invite_email? (registrations_controller.rb#L307):
def registered_with_invite_email?
invite_email = session.delete(:invite_email) # deletes and reads atomically
sign_up_params[:email] == invite_email
end
strong_memoize_attr :registered_with_invite_email?
session.delete both reads and removes the key in one call. This method is invoked during the create action (via build_params at line 290), so the session key is gone as soon as a user successfully signs up. The strong_memoize_attr ensures .delete only runs once per request. No reuse is possible after signup.
Sijin T V (41f09f2c) at 17 Mar 21:10
Add unit tests for Member.has_pending_invite_for_email?
... and 2 more commits
@GitLabDuo I have made the required changes. Can you review once again?
Sijin T V (3e03efe9) at 17 Mar 20:21
Add explanatory comment to UsersController#exists spec regarding se...
... and 521 more commits
Sijin T V (cd0aff0a) at 17 Mar 20:09
Add explanatory comment to UsersController#exists spec regarding se...
... and 2 more commits
@GitLabDuo What about
Member.with_case_insensitive_invite_emails([session[:invite_email]]).not_accepted_invitations.exists?
@victorprete I will finish the recommended changes requested by Gitlab Duo first. I will respond here once thats done and ready for the review.
@GitLabDuo what do you think about
return if session[:invite_email].present? && Member.with_case_insensitive_invite_emails([session[:invite_email]]).any?
Sijin T V (c33d3aa6) at 15 Mar 10:57
Fix permissions CI check by adding granular access token authorization
This Merge Request adds the User Applications REST API, fulfilling the requirements described in #23054.
It introduces a set of CRUD endpoints located at /api/v4/user/applications that allow users to manage their instance-level OAuth applications. This is designed to enable programmatic management of user-owned OAuth applications, which proves extremely useful for dynamically deploying and un-deploying Review Apps without having to employ insecure wildcard redirect URIs.
Specifically, it creates the following endpoints:
POST /api/v4/user/applications: Creates an application, exclusively returning the secret during this action.GET /api/v4/user/applications: Lists all applications owned by the authenticated user.GET /api/v4/user/applications/:id: Retrieves a specific application.PUT /api/v4/user/applications/:id: Updates an existing application (e.g. modifying the redirect_uri).DELETE /api/v4/user/applications/:id: Removes the application.The implementation follows strict security best practices regarding credential exposure and bounded context:
secret is explicitly and exclusively returned only on the POST create event. Subsequent GET or PUT requests will never expose the secret.redirect_uri targets (such as ephemeral Review Apps). This eliminates the insecure practice of users resorting to wildcard redirect URIs to handle dynamic domains.current_user. The Authn::UserApplicationsFinder inherently scopes all GET, PUT, and DELETE requests to only OAuth Applications owned by the authenticated user, completely preventing Insecure Direct Object Reference (IDOR) or cross-user data leakage.The User Applications API executes standard CRUD operations on the oauth_applications table, scoped to the current user. Here are the query execution plans:
EXPLAIN SELECT "oauth_applications".* FROM "oauth_applications" WHERE "oauth_applications"."owner_id" = 1 AND "oauth_applications"."owner_type" = 'User' AND "oauth_applications"."id" = 1 LIMIT 1;
Limit (cost=0.15..2.17 rows=1 width=237)
-> Index Scan using index_oauth_applications_on_owner_id_and_owner_type on oauth_applications (cost=0.15..2.17 rows=1 width=237)
Index Cond: ((owner_id = 1) AND ((owner_type)::text = 'User'::text))
Filter: (id = 1)
EXPLAIN INSERT INTO "oauth_applications" ("name", "uid", "secret", "redirect_uri", "scopes", "created_at", "updated_at", "owner_id", "owner_type", "confidential") VALUES ('test', 'uid', 'secret', 'url', 'api', NOW(), NOW(), 1, 'User', true) RETURNING "id";
Insert on oauth_applications (cost=0.00..0.02 rows=1 width=237)
-> Result (cost=0.00..0.02 rows=1 width=237)
EXPLAIN UPDATE "oauth_applications" SET "name" = 'test2', "updated_at" = NOW() WHERE "oauth_applications"."id" = 1;
Update on oauth_applications (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using oauth_applications_pkey on oauth_applications (cost=0.15..2.17 rows=1 width=46)
Index Cond: (id = 1)
EXPLAIN DELETE FROM "oauth_applications" WHERE "oauth_applications"."id" = 1;
Delete on oauth_applications (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using oauth_applications_pkey on oauth_applications (cost=0.15..2.17 rows=1 width=6)
Index Cond: (id = 1)
The API returns standard GitLab API JSON shapes, and correctly provisions the underlying Authn::OauthApplication record associated with the user.
| Browser Dev Tools Verification |
|---|
![]() |
Ensure your GDK is up and running.
Open your browser developer console while authenticated as any user (e.g. root) on the GitLab dashboard.
Execute the following fetch command to provision an application:
const csrf = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/v4/user/applications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf
},
body: JSON.stringify({
name: 'Browser UI Test App',
redirect_uri: 'http://localhost/callback',
scopes: 'api',
confidential: false
})
}).then(r => r.json()).then(console.log);
Verify that the response returns a success with the secret and an application_id.
You can assert the creation of the Application by checking your /-/profile/applications interface.
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Sijin T V (2d95327b) at 15 Mar 09:59
Add User Applications REST API