diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
index 461b2dad479..76709296c89 100644
--- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
+++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
@@ -45,6 +45,7 @@ export default {
'initialActiveAccessTokens',
'noActiveTokensMessage',
'showRole',
+ 'information',
],
data() {
return {
@@ -100,6 +101,10 @@ export default {
{{ header }}
+
+ {{ information }}
+
+
{
const {
accessTokenType,
accessTokenTypePlural,
+ information,
initialActiveAccessTokens: initialActiveAccessTokensJson,
noActiveTokensMessage: noTokensMessage,
} = el.dataset;
@@ -43,6 +44,7 @@ export const initAccessTokenTableApp = () => {
provide: {
accessTokenType,
accessTokenTypePlural,
+ information,
initialActiveAccessTokens,
noActiveTokensMessage,
showRole,
diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb
index eb279298baf..9d884478e98 100644
--- a/app/controllers/admin/impersonation_tokens_controller.rb
+++ b/app/controllers/admin/impersonation_tokens_controller.rb
@@ -14,11 +14,10 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token = finder.build(impersonation_token_params)
if @impersonation_token.save
- PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token)
- redirect_to admin_user_impersonation_tokens_path, notice: _("A new impersonation token has been created.")
+ render json: { new_token: @impersonation_token.token,
+ active_access_tokens: active_impersonation_tokens }, status: :ok
else
- set_index_vars
- render :index
+ render json: { errors: @impersonation_token.errors.full_messages }, status: :unprocessable_entity
end
end
@@ -50,19 +49,19 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options))
end
+ def active_impersonation_tokens
+ tokens = finder(state: 'active', sort: 'expires_at_asc_id_desc').execute
+ ::ImpersonationAccessTokenSerializer.new.represent(tokens)
+ end
+
def impersonation_token_params
params.require(:personal_access_token).permit(:name, :expires_at, :impersonation, scopes: [])
end
- # rubocop: disable CodeReuse/ActiveRecord
def set_index_vars
@scopes = Gitlab::Auth.available_scopes_for(current_user)
@impersonation_token ||= finder.build
- @inactive_impersonation_tokens = finder(state: 'inactive').execute
- @active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at)
-
- @new_impersonation_token = PersonalAccessToken.redis_getdel(current_user.id)
+ @active_impersonation_tokens = active_impersonation_tokens
end
- # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 2c526bb38d8..8cf1d8555ce 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -8,12 +8,10 @@
.row.gl-mt-3
.col-lg-12
- - if @new_impersonation_token
- = render 'shared/access_tokens/created_container',
- type: type,
- new_token_value: @new_impersonation_token
+ #js-new-access-token-app{ data: { access_token_type: type } }
= render 'shared/access_tokens/form',
+ ajax: true,
type: type,
title: _('Add an impersonation token'),
path: admin_user_impersonation_tokens_path,
@@ -22,9 +20,4 @@
scopes: @scopes,
help_path: help_page_path('api/index', anchor: 'impersonation-tokens')
- = render 'shared/access_tokens/table',
- type: type,
- type_plural: type_plural,
- impersonation: true,
- active_tokens: @active_impersonation_tokens,
- revoke_route_helper: ->(token) { revoke_admin_user_impersonation_token_path(token.user, token) }
+ #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_impersonation_tokens.to_json, information: _("To see all the user's personal access tokens you must impersonate them first.") } }
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
index 4ef3fa77958..79057ba8142 100644
--- a/doc/development/contributing/style_guides.md
+++ b/doc/development/contributing/style_guides.md
@@ -169,6 +169,16 @@ A grace period can safely be lifted as soon as there are no warnings for 2 weeks
1. Create an issue to fix TODOs and encourage Community contributions (via ~"good for new contributors" and/or ~"Seeking community contributions"). [See some examples](https://gitlab.com/gitlab-org/gitlab/-/issues/?sort=created_date&state=opened&label_name%5B%5D=good%20for%20new%20contributors&label_name%5B%5D=static%20code%20analysis&first_page_size=20).
1. Create an issue to remove "grace period" after 2 weeks silence in `#f_rubocop` Slack channel. ([See an example](https://gitlab.com/gitlab-org/gitlab/-/issues/374903).)
+### Silenced offenses
+
+When offenses are silenced for cops in ["grace period"](#cop-grace-period),
+the `#f_rubocop` Slack channel receives a notification message every two hours.
+
+To fix this issue:
+
+1. Find cops with silenced offenses in the linked CI job.
+1. [Generate TODOs](../rake_tasks.md#generate-initial-rubocop-todo-list) for these cops.
+
#### RuboCop node pattern
When creating [node patterns](https://docs.rubocop.org/rubocop-ast/node_pattern.html) to match
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fd6310d4df0..61214fa619a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1676,9 +1676,6 @@ msgstr ""
msgid "A new email address has been added to your GitLab account: %{email}"
msgstr ""
-msgid "A new impersonation token has been created."
-msgstr ""
-
msgid "A new personal access token has been created"
msgstr ""
diff --git a/rubocop/check_graceful_task.rb b/rubocop/check_graceful_task.rb
index 7ae74e79e38..724f7fa6963 100644
--- a/rubocop/check_graceful_task.rb
+++ b/rubocop/check_graceful_task.rb
@@ -66,7 +66,13 @@ module RuboCop
end
channel = 'f_rubocop'
- message = ":warning: `#{job_name}` passed :green: but contained silenced offenses. See #{job_url}"
+ message = format(
+ ':warning: `%{job_name}` passed :green: but contained <%{job_url}|silenced offenses>. ' \
+ 'See <%{docs_link}|docs>.',
+ docs_link: 'https://docs.gitlab.com/ee/development/contributing/style_guides.html#silenced-offenses',
+ job_name: job_name,
+ job_url: job_url)
+
emoji = 'rubocop'
user_name = 'GitLab Bot'
diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
index 99e9644da66..160af8cf3f0 100644
--- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
+++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
@@ -35,6 +35,10 @@ RSpec.describe Profiles::PersonalAccessTokensController do
expect(created_token).not_to be_nil
expect(created_token.expires_at).to eq(expires_at)
end
+
+ it_behaves_like "#create access token" do
+ let(:url) { :create }
+ end
end
describe '#index' do
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index 7e57cffc791..45dccf9921f 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -9,15 +9,11 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
let!(:user) { create(:user) }
def active_impersonation_tokens
- find(".table.active-tokens")
- end
-
- def no_personal_access_tokens_message
- find(".settings-message")
+ find("[data-testid='active-tokens']")
end
def created_impersonation_token
- find("#created-personal-access-token").value
+ find_field('new-access-token').value
end
before do
@@ -80,8 +76,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
accept_gl_confirm(button_text: 'Revoke') { click_on "Revoke" }
- expect(page).to have_selector(".settings-message")
- expect(no_personal_access_tokens_message).to have_text("This user has no active impersonation tokens.")
+ expect(active_impersonation_tokens).to have_text("This user has no active impersonation tokens.")
end
it "removes expired tokens from 'active' section" do
@@ -89,8 +84,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
visit admin_user_impersonation_tokens_path(user_id: user.username)
- expect(page).to have_selector(".settings-message")
- expect(no_personal_access_tokens_message).to have_text("This user has no active impersonation tokens.")
+ expect(active_impersonation_tokens).to have_text("This user has no active impersonation tokens.")
end
end
diff --git a/spec/frontend/access_tokens/components/access_token_table_app_spec.js b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
index aed3db4aa4c..a460d18cc60 100644
--- a/spec/frontend/access_tokens/components/access_token_table_app_spec.js
+++ b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
@@ -1,6 +1,6 @@
import { GlButton, GlPagination, GlTable } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import AccessTokenTableApp from '~/access_tokens/components/access_token_table_app.vue';
import { EVENT_SUCCESS, PAGE_SIZE } from '~/access_tokens/components/constants';
import { __, s__, sprintf } from '~/locale';
@@ -11,6 +11,7 @@ describe('~/access_tokens/components/access_token_table_app', () => {
const accessTokenType = 'personal access token';
const accessTokenTypePlural = 'personal access tokens';
+ const information = undefined;
const initialActiveAccessTokens = [];
const noActiveTokensMessage = 'This user has no active personal access tokens.';
const showRole = false;
@@ -43,10 +44,11 @@ describe('~/access_tokens/components/access_token_table_app', () => {
];
const createComponent = (props = {}) => {
- wrapper = mount(AccessTokenTableApp, {
+ wrapper = mountExtended(AccessTokenTableApp, {
provide: {
accessTokenType,
accessTokenTypePlural,
+ information,
initialActiveAccessTokens,
noActiveTokensMessage,
showRole,
@@ -101,6 +103,13 @@ describe('~/access_tokens/components/access_token_table_app', () => {
);
});
+ it('should render information section', () => {
+ const info = 'This is my information';
+ createComponent({ information: info });
+
+ expect(wrapper.findByTestId('information-section').text()).toBe(info);
+ });
+
it('should render the `GlTable` component with default 6 column headers', () => {
createComponent();
diff --git a/spec/requests/admin/impersonation_tokens_controller_spec.rb b/spec/requests/admin/impersonation_tokens_controller_spec.rb
index 018f497e7e5..2017a512bce 100644
--- a/spec/requests/admin/impersonation_tokens_controller_spec.rb
+++ b/spec/requests/admin/impersonation_tokens_controller_spec.rb
@@ -35,4 +35,10 @@ RSpec.describe Admin::ImpersonationTokensController, :enable_admin_mode do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ describe "#create" do
+ it_behaves_like "#create access token" do
+ let(:url) { admin_user_impersonation_tokens_path(user_id: user.username) }
+ end
+ end
end
diff --git a/spec/rubocop/check_graceful_task_spec.rb b/spec/rubocop/check_graceful_task_spec.rb
index 0364820a602..c39a00470fd 100644
--- a/spec/rubocop/check_graceful_task_spec.rb
+++ b/spec/rubocop/check_graceful_task_spec.rb
@@ -62,25 +62,35 @@ RSpec.describe RuboCop::CheckGracefulTask do
let(:adjusted_rubocop_status) { status_success }
context 'with sufficient environment variables' do
+ let(:script) { 'scripts/slack' }
let(:channel) { 'f_rubocop' }
+ let(:emoji) { 'rubocop' }
+ let(:user_name) { 'GitLab Bot' }
+ let(:job_name) { 'some job name' }
+ let(:job_url) { 'some job url' }
+ let(:docs_link) { 'https://docs.gitlab.com/ee/development/contributing/style_guides.html#silenced-offenses' }
before do
env = {
'CI_SLACK_WEBHOOK_URL' => 'webhook_url',
- 'CI_JOB_NAME' => 'job_name',
- 'CI_JOB_URL' => 'job_url'
+ 'CI_JOB_NAME' => job_name,
+ 'CI_JOB_URL' => job_url
}
stub_const('ENV', ENV.to_hash.update(env))
end
it 'notifies slack' do
- popen_args = ['scripts/slack', channel, kind_of(String), 'rubocop', kind_of(String)]
popen_result = ['', 0]
- expect(Gitlab::Popen).to receive(:popen).with(popen_args).and_return(popen_result)
+ allow(Gitlab::Popen).to receive(:popen).with(anything).and_return(popen_result)
subject
+ message = a_kind_of(String).and include(job_name).and include(job_url).and include(docs_link)
+
+ expect(Gitlab::Popen).to have_received(:popen)
+ .with([script, channel, message, emoji, user_name])
+
expect(output.string).to include("Notifying Slack ##{channel}.")
end
diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
index 0fc45b154d8..07e64446ab3 100644
--- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb
+++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
@@ -163,3 +163,35 @@ RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_tex
end
end
end
+
+RSpec.shared_examples '#create access token' do
+ let(:url) { {} }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:token_attributes) { attributes_for(:personal_access_token) }
+
+ before do
+ sign_in(admin)
+ end
+
+ context "when POST is successful" do
+ it "renders JSON with a new token" do
+ post url, params: { personal_access_token: token_attributes }
+
+ parsed_body = Gitlab::Json.parse(response.body)
+ expect(parsed_body['new_token']).not_to be_blank
+ expect(parsed_body['errors']).to be_blank
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+
+ context "when POST is unsuccessful" do
+ it "renders JSON with an error" do
+ post url, params: { personal_access_token: token_attributes.merge(scopes: []) }
+
+ parsed_body = Gitlab::Json.parse(response.body)
+ expect(parsed_body['new_token']).to be_blank
+ expect(parsed_body['errors']).not_to be_blank
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+end