diff --git a/doc/api/applications.md b/doc/api/applications.md index b2ff061a54e3103833039aec9afd435563064262..1715131a717c22ce37739bfe2d4b36cdffbce693 100644 --- a/doc/api/applications.md +++ b/doc/api/applications.md @@ -32,7 +32,7 @@ Returns `200` if the request succeeds. POST /applications ``` -Parameters: +Supported attributes: | Attribute | Type | Required | Description | |:---------------|:--------|:---------|:---------------------------------| @@ -103,7 +103,7 @@ Returns `204` if the request succeeds. DELETE /applications/:id ``` -Parameters: +Supported attributes: | Attribute | Type | Required | Description | |:----------|:--------|:---------|:----------------------------------------------------| @@ -114,3 +114,38 @@ Example request: ```shell curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/applications/:id" ``` + +## Renew an application secret + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422420) in GitLab 16.11. + +Renews an application secret. Returns `200` if the request succeeds. + +```plaintext +POST /applications/:id/renew-secret +``` + +Supported attributes: + +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:----------------------------------------------------| +| `id` | integer | yes | The ID of the application (not the `application_id`). | + +Example request: + +```shell +curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/applications/:id/renew-secret" +``` + +Example response: + +```json +{ + "id":1, + "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737", + "application_name": "MyApplication", + "secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34", + "callback_url": "http://redirect.uri", + "confidential": true +} +``` diff --git a/lib/api/applications.rb b/lib/api/applications.rb index 39f1638301ba33202bb7c8e8c4ab6fba58a403af..3cf0d9bddce0bfeb19a96dff0355d144414abd2f 100644 --- a/lib/api/applications.rb +++ b/lib/api/applications.rb @@ -61,6 +61,26 @@ class Applications < ::API::Base no_content! end + + desc 'Renew an application secret' do + detail 'Renew the secret of a specific application' + success code: 200, model: Entities::ApplicationWithSecret + end + params do + requires :id, type: Integer, desc: 'The ID of the application (not the application_id)' + end + post ':id/renew-secret' do + application = ApplicationsFinder.new(params).execute + break not_found!('Application') unless application + + application.renew_secret + + if application.save + present application, with: Entities::ApplicationWithSecret + else + render_validation_error!(application) + end + end end end end diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb index 16e24807e6788b7fc8ab6feea7aa4a86623c8af4..5de9f47c6851d4ae32bf1b00b750d0ca7dd5ccdf 100644 --- a/spec/requests/api/applications_spec.rb +++ b/spec/requests/api/applications_spec.rb @@ -198,4 +198,53 @@ end end end + + describe "POST /application/:id/renew-secret" do + let(:path) { "/applications/#{application.id}/renew-secret" } + + context 'user authorization' do + it_behaves_like 'POST request permissions for admin mode' do + let(:params) { {} } + end + end + + context 'authenticated and authorized user' do + it 'can renew a secret token' do + application = Doorkeeper::Application.last + post api(path, admin, admin_mode: true), params: {} + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['secret']).not_to be nil + expect(application.secret_matches?(json_response['secret'])).not_to eq(true) + end + + it 'return 404 when application_id not found' do + post api("/applications/#{non_existing_record_id}/renew-secret", admin, admin_mode: true) + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'return 400 when the operation is failed' do + allow_next_instance_of(ApplicationsFinder) do |finder| + allow(finder).to receive(:execute).and_return(application) + end + allow(application).to receive(:renew_secret).and_return(true) + allow(application).to receive(:valid?).and_return(false) + errors = ActiveModel::Errors.new(application) + errors.add(:name, 'Error 1') + allow(application).to receive(:errors).and_return(errors) + + post api(path, admin, admin_mode: true) + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'non-authenticated user' do + it 'cannot renew a secret token' do + post api(path) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + end end