diff --git a/app/services/users/reset_incoming_email_token_service.rb b/app/services/users/reset_incoming_email_token_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..12399b0fd05f894009c7a9899cd1b9741d736bfa --- /dev/null +++ b/app/services/users/reset_incoming_email_token_service.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Users + class ResetIncomingEmailTokenService < BaseService + def initialize(current_user:, user:) + @current_user = current_user + @user = user + end + + def execute! + return ServiceResponse.error(message: s_('Not permitted to reset user feed token')) unless reset_permitted? + + Users::UpdateService.new(current_user, user: user).execute!(&:reset_incoming_email_token!) + + ServiceResponse.success(message: 'Incoming mail token was successfully reset') + end + + private + + attr_reader :user + + def reset_permitted? + Ability.allowed?(current_user, :update_user, user) + end + end +end diff --git a/doc/api/admin/token.md b/doc/api/admin/token.md index 7a22620a2c2d644957921d08d18f4fcce53f4630..0b11b202aa3745a7b8ee83390c12028a76e7353a 100644 --- a/doc/api/admin/token.md +++ b/doc/api/admin/token.md @@ -111,6 +111,7 @@ Example response: > - [Cluster agent tokens added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178211) in GitLab 17.9. > - [Runner authentication tokens added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179066) in GitLab 17.9. > - [OAuth application secrets added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179035) in GitLab 17.9. +> - [Incoming email tokens added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180763) in GitLab 17.9. FLAG: The availability of this feature is controlled by a feature flag. @@ -130,6 +131,7 @@ Revokes or resets a given token based on the token type. This endpoint supports | [Feed tokens](../../security/tokens/_index.md#feed-token) | Reset | | [Runner authentication tokens](../../security/tokens/_index.md#runner-authentication-tokens) | Reset | | [OAuth application secrets](../../integration/oauth_provider.md) | Reset | +| [Incoming email tokens](../../security/tokens/_index.md#incoming-email-token) | Reset | ```plaintext DELETE /api/v4/admin/token diff --git a/lib/authn/tokens/incoming_email_token.rb b/lib/authn/tokens/incoming_email_token.rb index e4d2fd47686d5bb95e1c1eada7f251dc7d1a2fa4..d4eba4d35ce2fd9c81acaa55144df9162d6af808 100644 --- a/lib/authn/tokens/incoming_email_token.rb +++ b/lib/authn/tokens/incoming_email_token.rb @@ -18,10 +18,10 @@ def present_with ::API::Entities::User end - def revoke!(_current_user) + def revoke!(current_user) raise ::Authn::AgnosticTokenIdentifier::NotFoundError, 'Not Found' if revocable.blank? - raise ::Authn::AgnosticTokenIdentifier::UnsupportedTokenError, 'Unsupported token type' + Users::ResetIncomingEmailTokenService.new(current_user: current_user, user: revocable).execute! end end end diff --git a/spec/lib/authn/tokens/incoming_email_token_spec.rb b/spec/lib/authn/tokens/incoming_email_token_spec.rb index bf44a3f4650eb85b6079bf73e7364b1eac2dc8a5..67eefce88ebc80b58c6577d656cf92daf311d64c 100644 --- a/spec/lib/authn/tokens/incoming_email_token_spec.rb +++ b/spec/lib/authn/tokens/incoming_email_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Authn::Tokens::IncomingEmailToken, feature_category: :system_access do +RSpec.describe Authn::Tokens::IncomingEmailToken, :aggregate_failures, feature_category: :system_access do let_it_be(:user) { create(:user) } subject(:token) { described_class.new(plaintext, :api_admin_token) } @@ -14,10 +14,11 @@ it_behaves_like 'finding the valid revocable' describe '#revoke!' do - it 'does not support revocation yet' do - expect do - token.revoke!(user) - end.to raise_error(::Authn::AgnosticTokenIdentifier::UnsupportedTokenError, 'Unsupported token type') + subject(:revoke) { token.revoke!(user) } + + it 'successfully resets the token' do + expect { revoke }.to change { user.reload.incoming_email_token } + expect(revoke.success?).to be_truthy end end end diff --git a/spec/requests/api/admin/token_spec.rb b/spec/requests/api/admin/token_spec.rb index 36d315835026e96bfe5dfb2c833e68ce964075b7..a6a9a5ed16ee33975904718131513860569e9730 100644 --- a/spec/requests/api/admin/token_spec.rb +++ b/spec/requests/api/admin/token_spec.rb @@ -198,6 +198,16 @@ end end + context 'when the token is an incoming email token' do + let(:plaintext) { user.incoming_email_token } + + it 'resets the token' do + expect { delete_token }.to change { user.reload.incoming_email_token } + + expect(response).to have_gitlab_http_status(:no_content) + end + end + context 'when the revocation feature is disabled' do before do stub_feature_flags(api_admin_token_revoke: false) diff --git a/spec/services/users/reset_incoming_email_token_service_spec.rb b/spec/services/users/reset_incoming_email_token_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..556ce5686b5d7f4243c689cda2f1f4bd95821af6 --- /dev/null +++ b/spec/services/users/reset_incoming_email_token_service_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::ResetIncomingEmailTokenService, feature_category: :system_access do + shared_examples_for 'a successfully reset token' do + it { expect(execute.success?).to be true } + it { expect { execute }.to change { user.incoming_email_token } } + end + + shared_examples_for 'an unsuccessfully reset token' do + it { expect(execute.success?).to be false } + it { expect { execute }.not_to change { user.incoming_email_token } } + end + + describe '#execute!' do + let(:service) { described_class.new(current_user: current_user, user: user) } + + let_it_be(:existing_user) { create(:user) } + + subject(:execute) { service.execute! } + + context 'when current_user is an admin', :enable_admin_mode do + let(:current_user) { create(:admin) } + let(:user) { existing_user } + + it_behaves_like 'a successfully reset token' + end + + context 'when current_user is not an administrator' do + let(:current_user) { existing_user } + + context 'when user is a different user' do + let(:user) { create(:user) } + + it_behaves_like 'an unsuccessfully reset token' + end + + context 'when user is current_user' do + let(:user) { current_user } + + it_behaves_like 'a successfully reset token' + end + end + end +end