From d6c24706eabc218dd18da2e38fa586a47ca5628b Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Wed, 10 Dec 2025 20:17:54 +0900 Subject: [PATCH 1/7] Add revoke endpoint for runner controller token API Changelog: added --- .../revoke_token_service.rb | 21 ++++++ ...d_status_to_ci_runner_controller_tokens.rb | 19 +++++ db/schema_migrations/20251205053417 | 1 + db/structure.sql | 3 + ee/app/models/ci/runner_controller_token.rb | 13 +++- ee/lib/api/ci/runner_controller_tokens.rb | 34 ++++++++- .../models/ci/runner_controller_token_spec.rb | 25 +++++++ .../api/ci/runner_controller_tokens_spec.rb | 72 ++++++++++++++++++- spec/factories/ci/runner_controller_tokens.rb | 4 ++ .../revoke_token_service_spec.rb | 32 +++++++++ 10 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 app/services/ci/runner_controllers/revoke_token_service.rb create mode 100644 db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb create mode 100644 db/schema_migrations/20251205053417 create mode 100644 spec/services/ci/runner_controllers/revoke_token_service_spec.rb diff --git a/app/services/ci/runner_controllers/revoke_token_service.rb b/app/services/ci/runner_controllers/revoke_token_service.rb new file mode 100644 index 00000000000000..99d76d2fc81e4c --- /dev/null +++ b/app/services/ci/runner_controllers/revoke_token_service.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Ci + module RunnerControllers + class RevokeTokenService + attr_reader :token + + def initialize(token:) + @token = token + end + + def execute + if token.revoke + ServiceResponse.success + else + ServiceResponse.error(message: token.errors.full_messages) + end + end + end + end +end diff --git a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb new file mode 100644 index 00000000000000..ed6b5584033a30 --- /dev/null +++ b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddStatusToCiRunnerControllerTokens < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + + milestone '18.7' + + INDEX_NAME = 'index_ci_runner_controller_tokens_on_status' + + def up + add_column :ci_runner_controller_tokens, :status, :integer, limit: 2, default: 0, null: false + add_concurrent_index :ci_runner_controller_tokens, :status, name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :ci_runner_controller_tokens, INDEX_NAME + remove_column :ci_runner_controller_tokens, :status + end +end diff --git a/db/schema_migrations/20251205053417 b/db/schema_migrations/20251205053417 new file mode 100644 index 00000000000000..b9a099349c05ba --- /dev/null +++ b/db/schema_migrations/20251205053417 @@ -0,0 +1 @@ +8c479d11dbfad9f35cb9c2972c38f30c9fafccc870fa0533ccec64c7c67dfd2a \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 99384eb26ec0eb..8cce2e0e5809b3 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -15084,6 +15084,7 @@ CREATE TABLE ci_runner_controller_tokens ( runner_controller_id bigint NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, + status smallint DEFAULT 0 NOT NULL, CONSTRAINT check_84d7d76c86 CHECK ((char_length(description) <= 1024)), CONSTRAINT check_ec7c3fc764 CHECK ((char_length(token_digest) <= 255)) ); @@ -41246,6 +41247,8 @@ CREATE INDEX index_ci_resources_on_project_id ON ci_resources USING btree (proje CREATE UNIQUE INDEX index_ci_resources_on_resource_group_id_and_build_id ON ci_resources USING btree (resource_group_id, build_id); +CREATE INDEX index_ci_runner_controller_tokens_on_status ON ci_runner_controller_tokens USING btree (status); + CREATE UNIQUE INDEX index_ci_runner_controller_tokens_on_token_digest ON ci_runner_controller_tokens USING btree (token_digest); CREATE INDEX index_ci_runner_machines_on_executor_type ON ONLY ci_runner_machines USING btree (executor_type); diff --git a/ee/app/models/ci/runner_controller_token.rb b/ee/app/models/ci/runner_controller_token.rb index f19351201cf854..9d627527b4f6a5 100644 --- a/ee/app/models/ci/runner_controller_token.rb +++ b/ee/app/models/ci/runner_controller_token.rb @@ -18,12 +18,23 @@ class RunnerControllerToken < Ci::ApplicationRecord before_create :ensure_token - private + enum :status, { + active: 0, + revoked: 1 + } + + scope :active, -> { where(status: :active) } def self.token_prefix ::Authn::TokenField::PrefixHelper.prepend_instance_prefix(TOKEN_PREFIX) end + def revoke + update(status: :revoked) + end + + private + def token_prefix self.class.token_prefix end diff --git a/ee/lib/api/ci/runner_controller_tokens.rb b/ee/lib/api/ci/runner_controller_tokens.rb index 523edd7d2d858e..46cfac0e5ef622 100644 --- a/ee/lib/api/ci/runner_controller_tokens.rb +++ b/ee/lib/api/ci/runner_controller_tokens.rb @@ -32,7 +32,7 @@ class RunnerControllerTokens < ::API::Base controller = ::Ci::RunnerController.find_by_id(params[:runner_controller_id]) not_found! unless controller - tokens = controller.tokens + tokens = controller.tokens.active present paginate(tokens), with: Entities::Ci::RunnerControllerToken end @@ -53,7 +53,7 @@ class RunnerControllerTokens < ::API::Base controller = ::Ci::RunnerController.find_by_id(params[:runner_controller_id]) not_found! unless controller - token = controller.tokens.find_by_id(params[:id]) + token = controller.tokens.active.find_by_id(params[:id]) if token present token, with: Entities::Ci::RunnerControllerToken else @@ -88,6 +88,36 @@ class RunnerControllerTokens < ::API::Base bad_request!(token.errors.full_messages.to_sentence) end end + + desc 'Revoke a runner controller token' do + detail 'Revoke a token for a specific runner controller.' + success code: 204 + failure [ + { code: 403, message: 'Forbidden' }, + { code: 400, message: 'Bad Request' }, + { code: 404, message: 'Not found' } + ] + tags %w[runner_controller_tokens] + end + params do + requires :runner_controller_id, type: Integer, desc: 'ID of the runner controller' + requires :id, type: Integer, desc: 'ID of the runner controller token' + end + delete ':runner_controller_id/tokens/:id' do + controller = ::Ci::RunnerController.find_by_id(params[:runner_controller_id]) + not_found! unless controller + + token = controller.tokens.active.find_by_id(params[:id]) + not_found! unless token + + result = ::Ci::RunnerControllers::RevokeTokenService.new(token: token).execute + + if result.success? + no_content! + else + bad_request!(result.message.to_sentence) + end + end end end end diff --git a/ee/spec/models/ci/runner_controller_token_spec.rb b/ee/spec/models/ci/runner_controller_token_spec.rb index 54341a755874d0..21b3bd9db84749 100644 --- a/ee/spec/models/ci/runner_controller_token_spec.rb +++ b/ee/spec/models/ci/runner_controller_token_spec.rb @@ -35,4 +35,29 @@ token.save! end end + + describe 'enums' do + it { is_expected.to define_enum_for(:status).with_values(active: 0, revoked: 1) } + end + + describe 'scopes' do + describe '.active' do + it 'returns only active tokens' do + active_token = create(:ci_runner_controller_token, status: :active) + revoked_token = create(:ci_runner_controller_token, status: :revoked) + + expect(described_class.active).to include(active_token) + expect(described_class.active).not_to include(revoked_token) + end + end + end + + describe '#revoke' do + it 'sets the status to revoked' do + token = create(:ci_runner_controller_token, status: :active) + token.revoke + + expect(token.status).to eq('revoked') + end + end end diff --git a/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb b/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb index db55d787af75ca..60048d5df5ff6a 100644 --- a/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb +++ b/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb @@ -31,13 +31,17 @@ describe 'GET /runner_controllers/:runner_controller_id/tokens' do context 'when user is admin' do - it 'returns a list of runner controller tokens' do + it 'returns a list of active runner controller tokens' do create_list(:ci_runner_controller_token, 2, runner_controller: runner_controller) + revoked_token = create(:ci_runner_controller_token, :revoked, runner_controller: runner_controller) get api(path, admin, admin_mode: true) expect(response).to have_gitlab_http_status(:ok) expect(json_response.size).to eq(3) + json_response.each do |token_response| + expect(token_response['id']).not_to eq(revoked_token.id) + end end end @@ -81,6 +85,14 @@ it_behaves_like 'returns status 404 (not found)' end + + context 'when the token is revoked' do + let(:revoked_token) { create(:ci_runner_controller_token, :revoked, runner_controller: runner_controller) } + + subject(:call_endpoint) { get api("#{path}/#{revoked_token.id}", admin, admin_mode: true) } + + it_behaves_like 'returns status 404 (not found)' + end end context 'when user is not admin' do @@ -167,4 +179,62 @@ it_behaves_like 'returns status 404 (not found)' end end + + describe 'DELETE /runner_controllers/:runner_controller_id/tokens/:id' do + context 'when user is admin' do + it 'revokes the runner controller token' do + delete api("#{path}/#{token.id}", admin, admin_mode: true) + + expect(response).to have_gitlab_http_status(:no_content) + expect(token.reload.revoked?).to be true + end + + context 'when token revoke fails' do + before do + allow(Ci::RunnerControllerToken).to receive(:find_by_id).and_return(token) + allow(token).to receive(:revoke).and_return(false) + allow(token).to receive_message_chain(:errors, :full_messages).and_return(['Revoke failed']) + end + + it 'returns 400 with error message' do + delete api("#{path}/#{token.id}", admin, admin_mode: true) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq('400 Bad request - Revoke failed') + end + end + + context 'when runner controller does not exist' do + subject(:call_endpoint) do + delete api("/runner_controllers/#{non_existing_record_id}/tokens/#{token.id}", admin, admin_mode: true) + end + + it_behaves_like 'returns status 404 (not found)' + end + + context 'when runner controller token does not exist' do + subject(:call_endpoint) do + delete api("#{path}/#{non_existing_record_id}", admin, admin_mode: true) + end + + it_behaves_like 'returns status 404 (not found)' + end + + context 'when the token is already revoked' do + let(:revoked_token) { create(:ci_runner_controller_token, :revoked, runner_controller: runner_controller) } + + subject(:call_endpoint) { delete api("#{path}/#{revoked_token.id}", admin, admin_mode: true) } + + it_behaves_like 'returns status 404 (not found)' + end + end + + context 'when user is not admin' do + it 'returns status 403 (forbidden)' do + delete api("#{path}/#{token.id}", non_admin_user) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end end diff --git a/spec/factories/ci/runner_controller_tokens.rb b/spec/factories/ci/runner_controller_tokens.rb index 6d33db13556215..2684591fdbb9f9 100644 --- a/spec/factories/ci/runner_controller_tokens.rb +++ b/spec/factories/ci/runner_controller_tokens.rb @@ -5,5 +5,9 @@ association :runner_controller, factory: :ci_runner_controller description { "Token for runner controller" } + + trait :revoked do + status { :revoked } + end end end diff --git a/spec/services/ci/runner_controllers/revoke_token_service_spec.rb b/spec/services/ci/runner_controllers/revoke_token_service_spec.rb new file mode 100644 index 00000000000000..7c28bd6ff97607 --- /dev/null +++ b/spec/services/ci/runner_controllers/revoke_token_service_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::RunnerControllers::RevokeTokenService, feature_category: :continuous_integration do + let(:runner_controller) { create(:ci_runner_controller) } + let(:runner_controller_token) { create(:ci_runner_controller_token, runner_controller: runner_controller) } + + describe '#execute' do + subject(:execute) { described_class.new(token: runner_controller_token).execute } + + it 'successfully revokes the token' do + execute + + expect(runner_controller_token.reload.revoked?).to be true + end + + context 'when it fails' do + before do + allow(runner_controller_token).to receive(:revoke).and_return(false) + allow(runner_controller_token).to receive_message_chain(:errors, :full_messages).and_return(['Some error']) + end + + it 'returns an error response' do + response = execute + + expect(response).to be_error + expect(response.message).to eq(['Some error']) + end + end + end +end -- GitLab From f8f391cc62858bc11665a3a61a39f9808a3604a5 Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Fri, 12 Dec 2025 16:39:02 +0900 Subject: [PATCH 2/7] Use composite index on runner controller tokens status --- ...053417_add_status_to_ci_runner_controller_tokens.rb | 10 +++++++--- db/structure.sql | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb index ed6b5584033a30..108d45ea93fa4e 100644 --- a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb +++ b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb @@ -5,15 +5,19 @@ class AddStatusToCiRunnerControllerTokens < Gitlab::Database::Migration[2.3] milestone '18.7' - INDEX_NAME = 'index_ci_runner_controller_tokens_on_status' + INDEX_NAME = 'index_ci_runner_controller_tokens_on_rc_id_and_status' + OLD_INDEX_NAME = 'index_ci_rac_tokens_on_rac_id' def up add_column :ci_runner_controller_tokens, :status, :integer, limit: 2, default: 0, null: false - add_concurrent_index :ci_runner_controller_tokens, :status, name: INDEX_NAME + + remove_concurrent_index_by_name :ci_runner_controller_tokens, OLD_INDEX_NAME + add_concurrent_index :ci_runner_controller_tokens, [:runner_controller_id, :status], name: INDEX_NAME end def down - remove_concurrent_index_by_name :ci_runner_controller_tokens, INDEX_NAME remove_column :ci_runner_controller_tokens, :status + + add_concurrent_index :ci_runner_controller_tokens, :runner_controller_id, name: OLD_INDEX_NAME end end diff --git a/db/structure.sql b/db/structure.sql index 8cce2e0e5809b3..e5e65b7e5dbfee 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -41235,8 +41235,6 @@ CREATE UNIQUE INDEX index_ci_project_mirrors_on_project_id ON ci_project_mirrors CREATE UNIQUE INDEX index_ci_project_monthly_usages_on_project_id_and_date ON ci_project_monthly_usages USING btree (project_id, date); -CREATE INDEX index_ci_rac_tokens_on_rac_id ON ci_runner_controller_tokens USING btree (runner_controller_id); - CREATE UNIQUE INDEX index_ci_refs_on_project_id_and_ref_path ON ci_refs USING btree (project_id, ref_path); CREATE UNIQUE INDEX index_ci_resource_groups_on_project_id_and_key ON ci_resource_groups USING btree (project_id, key); @@ -41247,7 +41245,7 @@ CREATE INDEX index_ci_resources_on_project_id ON ci_resources USING btree (proje CREATE UNIQUE INDEX index_ci_resources_on_resource_group_id_and_build_id ON ci_resources USING btree (resource_group_id, build_id); -CREATE INDEX index_ci_runner_controller_tokens_on_status ON ci_runner_controller_tokens USING btree (status); +CREATE INDEX index_ci_runner_controller_tokens_on_rc_id_and_status ON ci_runner_controller_tokens USING btree (runner_controller_id, status); CREATE UNIQUE INDEX index_ci_runner_controller_tokens_on_token_digest ON ci_runner_controller_tokens USING btree (token_digest); -- GitLab From 73347237300b1c8c35d0b813503b69517cc7a4c2 Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Mon, 15 Dec 2025 12:01:56 +0900 Subject: [PATCH 3/7] Reorder migration queries --- ...51205053417_add_status_to_ci_runner_controller_tokens.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb index 108d45ea93fa4e..3f0234a469818a 100644 --- a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb +++ b/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb @@ -11,13 +11,13 @@ class AddStatusToCiRunnerControllerTokens < Gitlab::Database::Migration[2.3] def up add_column :ci_runner_controller_tokens, :status, :integer, limit: 2, default: 0, null: false - remove_concurrent_index_by_name :ci_runner_controller_tokens, OLD_INDEX_NAME add_concurrent_index :ci_runner_controller_tokens, [:runner_controller_id, :status], name: INDEX_NAME + remove_concurrent_index_by_name :ci_runner_controller_tokens, OLD_INDEX_NAME end def down - remove_column :ci_runner_controller_tokens, :status - add_concurrent_index :ci_runner_controller_tokens, :runner_controller_id, name: OLD_INDEX_NAME + + remove_column :ci_runner_controller_tokens, :status end end -- GitLab From a01824fef14b704dda0bd05866fbe904b1d44fd1 Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Tue, 16 Dec 2025 13:33:43 +0900 Subject: [PATCH 4/7] Move runner controllers under /ee directory --- .../app}/services/ci/runner_controllers/revoke_token_service.rb | 0 {spec => ee/spec}/factories/ci/runner_controller_tokens.rb | 0 .../services/ci/runner_controllers/revoke_token_service_spec.rb | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {app => ee/app}/services/ci/runner_controllers/revoke_token_service.rb (100%) rename {spec => ee/spec}/factories/ci/runner_controller_tokens.rb (100%) rename {spec => ee/spec}/services/ci/runner_controllers/revoke_token_service_spec.rb (100%) diff --git a/app/services/ci/runner_controllers/revoke_token_service.rb b/ee/app/services/ci/runner_controllers/revoke_token_service.rb similarity index 100% rename from app/services/ci/runner_controllers/revoke_token_service.rb rename to ee/app/services/ci/runner_controllers/revoke_token_service.rb diff --git a/spec/factories/ci/runner_controller_tokens.rb b/ee/spec/factories/ci/runner_controller_tokens.rb similarity index 100% rename from spec/factories/ci/runner_controller_tokens.rb rename to ee/spec/factories/ci/runner_controller_tokens.rb diff --git a/spec/services/ci/runner_controllers/revoke_token_service_spec.rb b/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb similarity index 100% rename from spec/services/ci/runner_controllers/revoke_token_service_spec.rb rename to ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb -- GitLab From c939165a5cb1c5746b012a19948857ab9c58527a Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Wed, 17 Dec 2025 14:36:28 +0900 Subject: [PATCH 5/7] Update migration timestamp --- ...20251217053122_add_status_to_ci_runner_controller_tokens.rb} | 2 +- db/schema_migrations/20251205053417 | 1 - db/schema_migrations/20251217053122 | 1 + ee/app/models/ci/runner_controller_token.rb | 2 -- 4 files changed, 2 insertions(+), 4 deletions(-) rename db/migrate/{20251205053417_add_status_to_ci_runner_controller_tokens.rb => 20251217053122_add_status_to_ci_runner_controller_tokens.rb} (97%) delete mode 100644 db/schema_migrations/20251205053417 create mode 100644 db/schema_migrations/20251217053122 diff --git a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb b/db/migrate/20251217053122_add_status_to_ci_runner_controller_tokens.rb similarity index 97% rename from db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb rename to db/migrate/20251217053122_add_status_to_ci_runner_controller_tokens.rb index 3f0234a469818a..f63eb34a4b2d60 100644 --- a/db/migrate/20251205053417_add_status_to_ci_runner_controller_tokens.rb +++ b/db/migrate/20251217053122_add_status_to_ci_runner_controller_tokens.rb @@ -3,7 +3,7 @@ class AddStatusToCiRunnerControllerTokens < Gitlab::Database::Migration[2.3] disable_ddl_transaction! - milestone '18.7' + milestone '18.8' INDEX_NAME = 'index_ci_runner_controller_tokens_on_rc_id_and_status' OLD_INDEX_NAME = 'index_ci_rac_tokens_on_rac_id' diff --git a/db/schema_migrations/20251205053417 b/db/schema_migrations/20251205053417 deleted file mode 100644 index b9a099349c05ba..00000000000000 --- a/db/schema_migrations/20251205053417 +++ /dev/null @@ -1 +0,0 @@ -8c479d11dbfad9f35cb9c2972c38f30c9fafccc870fa0533ccec64c7c67dfd2a \ No newline at end of file diff --git a/db/schema_migrations/20251217053122 b/db/schema_migrations/20251217053122 new file mode 100644 index 00000000000000..a1d385731336b5 --- /dev/null +++ b/db/schema_migrations/20251217053122 @@ -0,0 +1 @@ +15797b51433fc53b79cf313292896e3c308b77ca2881d925103bd4eb305ae07d \ No newline at end of file diff --git a/ee/app/models/ci/runner_controller_token.rb b/ee/app/models/ci/runner_controller_token.rb index 9d627527b4f6a5..ee62354929c1fc 100644 --- a/ee/app/models/ci/runner_controller_token.rb +++ b/ee/app/models/ci/runner_controller_token.rb @@ -23,8 +23,6 @@ class RunnerControllerToken < Ci::ApplicationRecord revoked: 1 } - scope :active, -> { where(status: :active) } - def self.token_prefix ::Authn::TokenField::PrefixHelper.prepend_instance_prefix(TOKEN_PREFIX) end -- GitLab From 9eb980fe24cf85a07d25c97685335692e8fdcdd2 Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Wed, 17 Dec 2025 15:43:33 +0900 Subject: [PATCH 6/7] Add permission check before revoking the runner controller token --- .../revoke_token_service.rb | 13 ++++- ee/lib/api/ci/runner_controller_tokens.rb | 2 +- .../revoke_token_service_spec.rb | 57 +++++++++++++++---- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/ee/app/services/ci/runner_controllers/revoke_token_service.rb b/ee/app/services/ci/runner_controllers/revoke_token_service.rb index 99d76d2fc81e4c..504d0493a17d77 100644 --- a/ee/app/services/ci/runner_controllers/revoke_token_service.rb +++ b/ee/app/services/ci/runner_controllers/revoke_token_service.rb @@ -3,19 +3,28 @@ module Ci module RunnerControllers class RevokeTokenService - attr_reader :token + attr_reader :token, :current_user - def initialize(token:) + def initialize(token:, current_user:) @token = token + @current_user = current_user end def execute + return error_no_permissions unless current_user.can_admin_all_resources? + if token.revoke ServiceResponse.success else ServiceResponse.error(message: token.errors.full_messages) end end + + private + + def error_no_permissions + ServiceResponse.error(message: 'Administrator permission is required to revoke this token') + end end end end diff --git a/ee/lib/api/ci/runner_controller_tokens.rb b/ee/lib/api/ci/runner_controller_tokens.rb index 46cfac0e5ef622..d98037625fa74d 100644 --- a/ee/lib/api/ci/runner_controller_tokens.rb +++ b/ee/lib/api/ci/runner_controller_tokens.rb @@ -110,7 +110,7 @@ class RunnerControllerTokens < ::API::Base token = controller.tokens.active.find_by_id(params[:id]) not_found! unless token - result = ::Ci::RunnerControllers::RevokeTokenService.new(token: token).execute + result = ::Ci::RunnerControllers::RevokeTokenService.new(token:, current_user:).execute if result.success? no_content! diff --git a/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb b/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb index 7c28bd6ff97607..b73c34438b1878 100644 --- a/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb +++ b/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb @@ -3,29 +3,64 @@ require 'spec_helper' RSpec.describe Ci::RunnerControllers::RevokeTokenService, feature_category: :continuous_integration do + include AdminModeHelper + let(:runner_controller) { create(:ci_runner_controller) } let(:runner_controller_token) { create(:ci_runner_controller_token, runner_controller: runner_controller) } + let(:admin_user) { create(:admin) } + let(:non_admin_user) { create(:user) } describe '#execute' do - subject(:execute) { described_class.new(token: runner_controller_token).execute } - - it 'successfully revokes the token' do - execute + subject(:execute) { described_class.new(token: runner_controller_token, current_user: current_user).execute } - expect(runner_controller_token.reload.revoked?).to be true - end + context 'when the user is an admin' do + let(:current_user) { admin_user } - context 'when it fails' do before do - allow(runner_controller_token).to receive(:revoke).and_return(false) - allow(runner_controller_token).to receive_message_chain(:errors, :full_messages).and_return(['Some error']) + enable_admin_mode!(current_user) + end + + it 'successfully revokes the token' do + execute + + expect(runner_controller_token.reload.revoked?).to be true end - it 'returns an error response' do + it 'returns a success response' do + response = execute + + expect(response).to be_success + end + + context 'when it fails' do + before do + allow(runner_controller_token).to receive(:revoke).and_return(false) + allow(runner_controller_token).to receive_message_chain(:errors, :full_messages).and_return(['Some error']) + end + + it 'returns an error response' do + response = execute + + expect(response).to be_error + expect(response.message).to eq(['Some error']) + end + end + end + + context 'when the user is not an admin' do + let(:current_user) { non_admin_user } + + it 'returns an error response indicating insufficient permissions' do response = execute expect(response).to be_error - expect(response.message).to eq(['Some error']) + expect(response.message).to eq('Administrator permission is required to revoke this token') + end + + it 'does not revoke the token' do + execute + + expect(runner_controller_token.reload.active?).to be true end end end -- GitLab From efdfa20c3ae6bfee5cd28b577934b2bdcbd4ceae Mon Sep 17 00:00:00 2001 From: Taka Nishida Date: Wed, 17 Dec 2025 19:48:22 +0900 Subject: [PATCH 7/7] Rename revoke method to revoke! --- ee/app/models/ci/runner_controller_token.rb | 2 +- ee/app/services/ci/runner_controllers/revoke_token_service.rb | 2 +- ee/spec/models/ci/runner_controller_token_spec.rb | 2 +- ee/spec/requests/api/ci/runner_controller_tokens_spec.rb | 2 +- .../services/ci/runner_controllers/revoke_token_service_spec.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ee/app/models/ci/runner_controller_token.rb b/ee/app/models/ci/runner_controller_token.rb index ee62354929c1fc..43145f81421627 100644 --- a/ee/app/models/ci/runner_controller_token.rb +++ b/ee/app/models/ci/runner_controller_token.rb @@ -27,7 +27,7 @@ def self.token_prefix ::Authn::TokenField::PrefixHelper.prepend_instance_prefix(TOKEN_PREFIX) end - def revoke + def revoke! update(status: :revoked) end diff --git a/ee/app/services/ci/runner_controllers/revoke_token_service.rb b/ee/app/services/ci/runner_controllers/revoke_token_service.rb index 504d0493a17d77..8b0de0fbc0dbea 100644 --- a/ee/app/services/ci/runner_controllers/revoke_token_service.rb +++ b/ee/app/services/ci/runner_controllers/revoke_token_service.rb @@ -13,7 +13,7 @@ def initialize(token:, current_user:) def execute return error_no_permissions unless current_user.can_admin_all_resources? - if token.revoke + if token.revoke! ServiceResponse.success else ServiceResponse.error(message: token.errors.full_messages) diff --git a/ee/spec/models/ci/runner_controller_token_spec.rb b/ee/spec/models/ci/runner_controller_token_spec.rb index 21b3bd9db84749..0a136ac67e4ba4 100644 --- a/ee/spec/models/ci/runner_controller_token_spec.rb +++ b/ee/spec/models/ci/runner_controller_token_spec.rb @@ -55,7 +55,7 @@ describe '#revoke' do it 'sets the status to revoked' do token = create(:ci_runner_controller_token, status: :active) - token.revoke + token.revoke! expect(token.status).to eq('revoked') end diff --git a/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb b/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb index 60048d5df5ff6a..81507fe0ef5172 100644 --- a/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb +++ b/ee/spec/requests/api/ci/runner_controller_tokens_spec.rb @@ -192,7 +192,7 @@ context 'when token revoke fails' do before do allow(Ci::RunnerControllerToken).to receive(:find_by_id).and_return(token) - allow(token).to receive(:revoke).and_return(false) + allow(token).to receive(:revoke!).and_return(false) allow(token).to receive_message_chain(:errors, :full_messages).and_return(['Revoke failed']) end diff --git a/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb b/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb index b73c34438b1878..99a75cb0f6b620 100644 --- a/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb +++ b/ee/spec/services/ci/runner_controllers/revoke_token_service_spec.rb @@ -34,7 +34,7 @@ context 'when it fails' do before do - allow(runner_controller_token).to receive(:revoke).and_return(false) + allow(runner_controller_token).to receive(:revoke!).and_return(false) allow(runner_controller_token).to receive_message_chain(:errors, :full_messages).and_return(['Some error']) end -- GitLab