Commit 977eef18 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents ce839ef5 d2430c45
# frozen_string_literal: true
module Mutations
module Ci
module JobTokenScope
class AddProject < BaseMutation
include FindsProject
graphql_name 'CiJobTokenScopeAddProject'
authorize :admin_project
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project that the CI job token scope belongs to.'
argument :target_project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project to be added to the CI job token scope.'
field :ci_job_token_scope,
Types::Ci::JobTokenScopeType,
null: true,
description: "The CI job token's scope of access."
def resolve(project_path:, target_project_path:)
project = authorized_find!(project_path)
target_project = Project.find_by_full_path(target_project_path)
result = ::Ci::JobTokenScope::AddProjectService
.new(project, current_user)
.execute(target_project)
if result.success?
{
ci_job_token_scope: ::Ci::JobToken::Scope.new(project),
errors: []
}
else
{
ci_job_token_scope: nil,
errors: [result.message]
}
end
end
end
end
end
end
......@@ -99,6 +99,7 @@ module Types
mount_mutation Mutations::Ci::CiCdSettingsUpdate
mount_mutation Mutations::Ci::Job::Play
mount_mutation Mutations::Ci::Job::Retry
mount_mutation Mutations::Ci::JobTokenScope::AddProject
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::RunnersRegistrationToken::Reset, feature_flag: :runner_graphql_query
......
# frozen_string_literal: true
module Ci
module JobTokenScope
class AddProjectService < ::BaseService
TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND = "The target_project that you are attempting to access does " \
"not exist or you don't have permission to perform this action"
def execute(target_project)
if error_response = validation_error(target_project)
return error_response
end
link = add_project!(target_project)
ServiceResponse.success(payload: { project_link: link })
rescue ActiveRecord::RecordNotUnique
ServiceResponse.error(message: "Target project is already in the job token scope")
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.message)
end
private
def validation_error(target_project)
unless project.ci_job_token_scope_enabled?
return ServiceResponse.error(message: "Job token scope is disabled for this project")
end
unless can?(current_user, :admin_project, project)
return ServiceResponse.error(message: "Insufficient permissions to modify the job token scope")
end
unless target_project
return ServiceResponse.error(message: TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
unless can?(current_user, :read_project, target_project)
return ServiceResponse.error(message: TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
nil
end
def add_project!(target_project)
::Ci::JobToken::ProjectScopeLink.create!(
source_project: project,
target_project: target_project,
added_by: current_user
)
end
end
end
end
# frozen_string_literal: true
class RemoveDeprecatedModsecurityColumns < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_clusters_applications_ingress_on_modsecurity'
def up
remove_column :clusters_applications_ingress, :modsecurity_enabled if column_exists?(:clusters_applications_ingress, :modsecurity_enabled)
remove_column :clusters_applications_ingress, :modsecurity_mode if column_exists?(:clusters_applications_ingress, :modsecurity_mode)
end
def down
add_column :clusters_applications_ingress, :modsecurity_enabled, :boolean unless column_exists?(:clusters_applications_ingress, :modsecurity_enabled)
add_column :clusters_applications_ingress, :modsecurity_mode, :smallint, null: false, default: 0 unless column_exists?(:clusters_applications_ingress, :modsecurity_mode)
add_concurrent_index :clusters_applications_ingress, [:modsecurity_enabled, :modsecurity_mode, :cluster_id], name: INDEX_NAME
end
end
28e448810fdf8bab4de44d45acac862e752f578b5b8fd77b885a385b9ef16b2d
\ No newline at end of file
......@@ -11682,9 +11682,7 @@ CREATE TABLE clusters_applications_ingress (
cluster_ip character varying,
status_reason text,
external_ip character varying,
external_hostname character varying,
modsecurity_enabled boolean,
modsecurity_mode smallint DEFAULT 0 NOT NULL
external_hostname character varying
);
CREATE SEQUENCE clusters_applications_ingress_id_seq
......@@ -23169,8 +23167,6 @@ CREATE UNIQUE INDEX index_clusters_applications_helm_on_cluster_id ON clusters_a
CREATE UNIQUE INDEX index_clusters_applications_ingress_on_cluster_id ON clusters_applications_ingress USING btree (cluster_id);
CREATE INDEX index_clusters_applications_ingress_on_modsecurity ON clusters_applications_ingress USING btree (modsecurity_enabled, modsecurity_mode, cluster_id);
CREATE UNIQUE INDEX index_clusters_applications_jupyter_on_cluster_id ON clusters_applications_jupyter USING btree (cluster_id);
CREATE INDEX index_clusters_applications_jupyter_on_oauth_application_id ON clusters_applications_jupyter USING btree (oauth_application_id);
......@@ -162,6 +162,7 @@ The following user actions are recorded:
- Failed second-factor authentication attempt ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16826) in GitLab 13.5)
- A user's personal access token was successfully created or revoked ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
- A failed attempt to create or revoke a user's personal access token ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
- Administrator added or removed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) in GitLab 14.1)
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
......
......@@ -780,6 +780,26 @@ Input type: `CiCdSettingsUpdateInput`
| <a id="mutationcicdsettingsupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcicdsettingsupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.ciJobTokenScopeAddProject`
Input type: `CiJobTokenScopeAddProjectInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcijobtokenscopeaddprojectclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcijobtokenscopeaddprojectprojectpath"></a>`projectPath` | [`ID!`](#id) | The project that the CI job token scope belongs to. |
| <a id="mutationcijobtokenscopeaddprojecttargetprojectpath"></a>`targetProjectPath` | [`ID!`](#id) | The project to be added to the CI job token scope. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcijobtokenscopeaddprojectcijobtokenscope"></a>`ciJobTokenScope` | [`CiJobTokenScopeType`](#cijobtokenscopetype) | The CI job token's scope of access. |
| <a id="mutationcijobtokenscopeaddprojectclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcijobtokenscopeaddprojecterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.clusterAgentDelete`
Input type: `ClusterAgentDeleteInput`
......
......@@ -24,6 +24,7 @@ module EE
audit_changes(:email, as: 'email address')
audit_changes(:encrypted_password, as: 'password', skip_changes: true)
audit_changes(:username, as: 'username')
audit_changes(:admin, as: 'admin status')
success
end
......
......@@ -101,6 +101,26 @@ RSpec.describe Users::UpdateService do
stub_licensed_features(admin_audit_log: true)
end
context 'updating administrator status' do
let_it_be(:admin_user) { create(:admin) }
it 'logs making a user an administrator' do
expect do
update_user_as(admin_user, user, admin: true)
end.to change { AuditEvent.count }.by(1)
expect(AuditEvent.last.present.action).to eq('Changed admin status from false to true')
end
it 'logs making an administrator a user' do
expect do
update_user_as(admin_user, create(:admin), admin: false)
end.to change { AuditEvent.count }.by(1)
expect(AuditEvent.last.present.action).to eq('Changed admin status from true to false')
end
end
context 'updating username' do
it 'logs audit event' do
previous_username = user.username
......
......@@ -37,8 +37,6 @@
"hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] },
"stack": { "type": ["string", "null"] },
"modsecurity_enabled": { "type": ["boolean", "null"] },
"modsecurity_mode": {"type": ["integer", "0"]},
"host": {"type": ["string", "null"]},
"port": {"type": ["integer", "514"]},
"protocol": {"type": ["integer", "0"]},
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Ci::JobTokenScope::AddProject do
let(:mutation) do
described_class.new(object: nil, context: { current_user: current_user }, field: nil)
end
describe '#resolve' do
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let(:target_project_path) { target_project.full_path }
subject do
mutation.resolve(project_path: project.full_path, target_project_path: target_project_path)
end
context 'when user is not logged in' do
let(:current_user) { nil }
it 'raises error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user is logged in' do
let(:current_user) { create(:user) }
context 'when user does not have permissions to admin project' do
it 'raises error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user has permissions to admin project and read target project' do
before do
project.add_maintainer(current_user)
target_project.add_guest(current_user)
end
it 'adds target project to the job token scope' do
expect do
expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty)
end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
end
context 'when the service returns an error' do
let(:service) { double(:service) }
it 'returns an error response' do
expect(::Ci::JobTokenScope::AddProjectService).to receive(:new).with(project, current_user).and_return(service)
expect(service).to receive(:execute).with(target_project).and_return(ServiceResponse.error(message: 'The error message'))
expect(subject.fetch(:ci_job_token_scope)).to be_nil
expect(subject.fetch(:errors)).to include("The error message")
end
end
end
end
end
end
......@@ -29,7 +29,7 @@ RSpec.describe Ci::JobToken::Scope do
end
end
describe 'includes?' do
describe '#includes?' do
subject { scope.includes?(target_project) }
context 'when param is the project defining the scope' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'CiJobTokenScopeAddProject' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let(:variables) do
{
project_path: project.full_path,
target_project_path: target_project.full_path
}
end
let(:mutation) do
graphql_mutation(:ci_job_token_scope_add_project, variables) do
<<~QL
errors
ciJobTokenScope {
projects {
nodes {
path
}
}
}
QL
end
end
let(:mutation_response) { graphql_mutation_response(:ci_job_token_scope_add_project) }
context 'when unauthorized' do
let(:current_user) { create(:user) }
context 'when not a maintainer' do
before do
project.add_developer(current_user)
end
it 'has graphql errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect(graphql_errors).not_to be_empty
end
end
end
context 'when authorized' do
let_it_be(:current_user) { project.owner }
before do
target_project.add_developer(current_user)
end
it 'adds the target project to the job token scope' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty
end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(false).to(true)
end
context 'when invalid target project is provided' do
before do
variables[:target_project_path] = 'unknown/project'
end
it 'has mutation errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).to contain_exactly(Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::JobTokenScope::AddProjectService do
let(:service) { described_class.new(project, current_user) }
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let_it_be(:current_user) { create(:user) }
describe '#execute' do
subject(:result) { service.execute(target_project) }
shared_examples 'returns error' do |error|
it 'returns an error response', :aggregate_failures do
expect(result).to be_error
expect(result.message).to eq(error)
end
end
context 'when job token scope is disabled for the given project' do
before do
allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
end
it_behaves_like 'returns error', 'Job token scope is disabled for this project'
end
context 'when user does not have permissions to edit the job token scope' do
it_behaves_like 'returns error', 'Insufficient permissions to modify the job token scope'
end
context 'when user has permissions to edit the job token scope' do
before do
project.add_maintainer(current_user)
end
context 'when target project is not provided' do
let(:target_project) { nil }
it_behaves_like 'returns error', Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
end
context 'when target project is provided' do
context 'when user does not have permissions to read the target project' do
it_behaves_like 'returns error', Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
end
context 'when user has permissions to read the target project' do
before do
target_project.add_guest(current_user)
end
it 'adds the project to the scope' do
expect do
expect(result).to be_success
end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
end
context 'when target project is already in scope' do
before do
create(:ci_job_token_project_scope_link,
source_project: project,
target_project: target_project)
end
it_behaves_like 'returns error', "Target project is already in the job token scope"
end
end
context 'when target project is same as the source project' do
let(:target_project) { project }
it_behaves_like 'returns error', "Validation failed: Target project can't be the same as the source project"
end
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