Commit f9486b48 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch 'pedropombeiro/330526/reset-runner-registration-token' into 'master'

Add RunnersRegistrationToken::Reset mutation

See merge request gitlab-org/gitlab!63638
parents 4440e175 61b97fe3
# frozen_string_literal: true
module Mutations
module Ci
module RunnersRegistrationToken
class Reset < BaseMutation
graphql_name 'RunnersRegistrationTokenReset'
authorize :update_runners_registration_token
ScopeID = ::GraphQL::ID_TYPE
argument :type, ::Types::Ci::RunnerTypeEnum,
required: true,
description: 'Scope of the object to reset the token for.'
argument :id, ScopeID,
required: false,
description: 'ID of the project or group to reset the token for. Omit if resetting instance runner token.'
field :token,
GraphQL::STRING_TYPE,
null: true,
description: 'The runner token after mutation.'
def resolve(**args)
{
token: reset_token(**args),
errors: []
}
end
private
def find_object(type:, **args)
id = args[:id]
case type
when 'group_type'
GitlabSchema.object_from_id(id, expected_type: ::Group)
when 'project_type'
GitlabSchema.object_from_id(id, expected_type: ::Project)
end
end
def reset_token(type:, **args)
id = args[:id]
case type
when 'instance_type'
raise Gitlab::Graphql::Errors::ArgumentError, "id must not be specified for '#{type}' scope" if id.present?
authorize!(:global)
ApplicationSetting.current.reset_runners_registration_token!
ApplicationSetting.current_without_cache.runners_registration_token
when 'group_type', 'project_type'
project_or_group = authorized_find!(type: type, id: id)
project_or_group.reset_runners_token!
project_or_group.runners_token
end
end
end
end
end
end
...@@ -101,6 +101,7 @@ module Types ...@@ -101,6 +101,7 @@ module Types
mount_mutation Mutations::Ci::Job::Retry mount_mutation Mutations::Ci::Job::Retry
mount_mutation Mutations::Ci::Runner::Update, feature_flag: :runner_graphql_query mount_mutation Mutations::Ci::Runner::Update, feature_flag: :runner_graphql_query
mount_mutation Mutations::Ci::Runner::Delete, feature_flag: :runner_graphql_query mount_mutation Mutations::Ci::Runner::Delete, feature_flag: :runner_graphql_query
mount_mutation Mutations::Ci::RunnersRegistrationToken::Reset, feature_flag: :runner_graphql_query
mount_mutation Mutations::Namespace::PackageSettings::Update mount_mutation Mutations::Namespace::PackageSettings::Update
mount_mutation Mutations::UserCallouts::Create mount_mutation Mutations::UserCallouts::Create
end end
......
...@@ -115,6 +115,7 @@ class GlobalPolicy < BasePolicy ...@@ -115,6 +115,7 @@ class GlobalPolicy < BasePolicy
enable :approve_user enable :approve_user
enable :reject_user enable :reject_user
enable :read_usage_trends_measurement enable :read_usage_trends_measurement
enable :update_runners_registration_token
end end
# We can't use `read_statistics` because the user may have different permissions for different projects # We can't use `read_statistics` because the user may have different permissions for different projects
......
...@@ -144,6 +144,7 @@ class GroupPolicy < BasePolicy ...@@ -144,6 +144,7 @@ class GroupPolicy < BasePolicy
enable :admin_cluster enable :admin_cluster
enable :read_deploy_token enable :read_deploy_token
enable :create_jira_connect_subscription enable :create_jira_connect_subscription
enable :update_runners_registration_token
end end
rule { owner }.policy do rule { owner }.policy do
......
...@@ -419,6 +419,7 @@ class ProjectPolicy < BasePolicy ...@@ -419,6 +419,7 @@ class ProjectPolicy < BasePolicy
enable :update_freeze_period enable :update_freeze_period
enable :destroy_freeze_period enable :destroy_freeze_period
enable :admin_feature_flags_client enable :admin_feature_flags_client
enable :update_runners_registration_token
end end
rule { public_project & metrics_dashboard_allowed }.policy do rule { public_project & metrics_dashboard_allowed }.policy do
......
...@@ -3528,6 +3528,28 @@ Input type: `RunnerUpdateInput` ...@@ -3528,6 +3528,28 @@ Input type: `RunnerUpdateInput`
| <a id="mutationrunnerupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationrunnerupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationrunnerupdaterunner"></a>`runner` | [`CiRunner`](#cirunner) | The runner after mutation. | | <a id="mutationrunnerupdaterunner"></a>`runner` | [`CiRunner`](#cirunner) | The runner after mutation. |
### `Mutation.runnersRegistrationTokenReset`
Available only when feature flag `runner_graphql_query` is enabled.
Input type: `RunnersRegistrationTokenResetInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnersregistrationtokenresetclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnersregistrationtokenresetid"></a>`id` | [`ID`](#id) | ID of the project or group to reset the token for. Omit if resetting instance runner token. |
| <a id="mutationrunnersregistrationtokenresettype"></a>`type` | [`CiRunnerType!`](#cirunnertype) | Scope of the object to reset the token for. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnersregistrationtokenresetclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnersregistrationtokenreseterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationrunnersregistrationtokenresettoken"></a>`token` | [`String`](#string) | The runner token after mutation. |
### `Mutation.terraformStateDelete` ### `Mutation.terraformStateDelete`
Input type: `TerraformStateDeleteInput` Input type: `TerraformStateDeleteInput`
......
...@@ -565,4 +565,34 @@ RSpec.describe GlobalPolicy do ...@@ -565,4 +565,34 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:log_in) } it { is_expected.not_to be_allowed(:log_in) }
end end
end end
describe 'update_runners_registration_token' do
context 'when anonymous' do
let(:current_user) { nil }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'regular user' do
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'when external' do
let(:current_user) { build(:user, :external) }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'admin' do
let(:current_user) { create(:admin) }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
end
end end
...@@ -923,4 +923,54 @@ RSpec.describe GroupPolicy do ...@@ -923,4 +923,54 @@ RSpec.describe GroupPolicy do
it { expect(described_class.new(current_user, subgroup)).to be_allowed(:read_label) } it { expect(described_class.new(current_user, subgroup)).to be_allowed(:read_label) }
end end
end end
describe 'update_runners_registration_token' do
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
context 'with reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
end end
...@@ -1595,4 +1595,40 @@ RSpec.describe ProjectPolicy do ...@@ -1595,4 +1595,40 @@ RSpec.describe ProjectPolicy do
end end
end end
end end
describe 'update_runners_registration_token' do
context 'when anonymous' do
let(:current_user) { anonymous }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'admin' do
let(:current_user) { create(:admin) }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
%w(guest reporter developer).each do |role|
context role do
let(:current_user) { send(role) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
%w(maintainer owner).each do |role|
context role do
let(:current_user) { send(role) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'RunnersRegistrationTokenReset' do
include GraphqlHelpers
let(:mutation) { graphql_mutation(:runners_registration_token_reset, input) }
let(:mutation_response) { graphql_mutation_response(:runners_registration_token_reset) }
subject { post_graphql_mutation(mutation, current_user: user) }
shared_examples 'unauthorized' do
it 'returns an error' do
subject
expect(graphql_errors).not_to be_empty
expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does not exist or you don't have permission to perform this action"))
expect(mutation_response).to be_nil
end
end
shared_context 'when unauthorized' do |scope|
context 'when unauthorized' do
let_it_be(:user) { create(:user) }
context "when not a #{scope} member" do
it_behaves_like 'unauthorized'
end
context "with a non-admin #{scope} member" do
before do
target.add_developer(user)
end
it_behaves_like 'unauthorized'
end
end
end
shared_context 'when authorized' do |scope|
it 'resets runner registration token' do
expect { subject }.to change { get_token }
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response).not_to be_nil
expect(mutation_response['errors']).to be_empty
expect(mutation_response['token']).not_to be_empty
expect(mutation_response['token']).to eq(get_token)
end
context 'when malformed id is provided' do
let(:input) { { type: "#{scope.upcase}_TYPE", id: 'some string' } }
it 'returns errors' do
expect { subject }.not_to change { get_token }
expect(graphql_errors).not_to be_empty
expect(mutation_response).to be_nil
end
end
end
context 'applied to project' do
let_it_be(:project) { create_default(:project) }
let(:input) { { type: 'PROJECT_TYPE', id: project.to_global_id.to_s } }
include_context 'when unauthorized', 'project' do
let(:target) { project }
end
include_context 'when authorized', 'project' do
let_it_be(:user) { project.owner }
def get_token
project.reload.runners_token
end
end
end
context 'applied to group' do
let_it_be(:group) { create_default(:group) }
let(:input) { { type: 'GROUP_TYPE', id: group.to_global_id.to_s } }
include_context 'when unauthorized', 'group' do
let(:target) { group }
end
include_context 'when authorized', 'group' do
let_it_be(:user) { create_default(:group_member, :maintainer, user: create(:user), group: group ).user }
def get_token
group.reload.runners_token
end
end
end
context 'applied to instance' do
before do
ApplicationSetting.create_from_defaults
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
let(:input) { { type: 'INSTANCE_TYPE' } }
context 'when unauthorized' do
let(:user) { create(:user) }
it_behaves_like 'unauthorized'
end
include_context 'when authorized', 'instance' do
let_it_be(:user) { create(:user, :admin) }
def get_token
ApplicationSetting.current_without_cache.runners_registration_token
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment