Commit c2389dcf authored by Markus Koller's avatar Markus Koller

Merge branch '321632-prevent-used-dast-profiles-from-being-delete' into 'master'

Prevent DAST profiles from being deleted when referenced by policies

See merge request gitlab-org/gitlab!54699
parents ae30671c 0ae53f72
......@@ -1302,6 +1302,7 @@ Represents a DAST scanner profile.
| `globalId` **{warning-solid}** | DastScannerProfileID! | **Deprecated:** Use `id`. Deprecated in 13.6. |
| `id` | DastScannerProfileID! | ID of the DAST scanner profile. |
| `profileName` | String | Name of the DAST scanner profile. |
| `referencedInSecurityPolicies` | String! => Array | List of security policy names that are referencing given project. |
| `scanType` | DastScanTypeEnum | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
| `showDebugMessages` | Boolean! | Indicates if debug messages should be included in DAST console output. True to include the debug messages. |
| `spiderTimeout` | Int | The maximum number of minutes allowed for the spider to traverse the site. |
......@@ -1348,6 +1349,7 @@ Represents a DAST Site Profile.
| `id` | DastSiteProfileID! | ID of the site profile. |
| `normalizedTargetUrl` | String | Normalized URL of the target to be scanned. |
| `profileName` | String | The name of the site profile. |
| `referencedInSecurityPolicies` | String! => Array | List of security policy names that are referencing given project. |
| `targetUrl` | String | The URL of the target to be scanned. |
| `userPermissions` | DastSiteProfilePermissions! | Permissions for the current user on the resource |
| `validationStatus` | DastSiteProfileValidationStatusEnum | The current validation status of the site profile. |
......
......@@ -21,13 +21,12 @@ module Mutations
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ::Types::GlobalIDType[::DastSiteProfile].coerce_isolated_input(id)
dast_site_profile = find_dast_site_profile(project: project, global_id: id)
service = ::DastSiteProfiles::DestroyService.new(project, current_user)
result = service.execute(id: id.model_id)
return { errors: dast_site_profile.errors.full_messages } unless dast_site_profile.destroy
return { errors: result.errors } unless result.success?
{ errors: [] }
rescue ActiveRecord::RecordNotFound
raise_resource_not_available_error!
end
private
......@@ -35,12 +34,6 @@ module Mutations
def find_object(full_path)
Project.find_by_full_path(full_path)
end
def find_dast_site_profile(project:, global_id:)
project.dast_site_profiles.find(global_id.model_id)
rescue ActiveRecord::RecordNotFound
raise_resource_not_available_error!
end
end
end
end
......@@ -40,6 +40,11 @@ module Types
field :edit_path, GraphQL::STRING_TYPE, null: true,
description: 'Relative web path to the edit page of a scanner profile.'
field :referenced_in_security_policies, [GraphQL::STRING_TYPE], null: true,
complexity: 10,
calls_gitaly: true,
description: 'List of security policy names that are referencing given project.'
def edit_path
Rails.application.routes.url_helpers.edit_project_security_configuration_dast_profiles_dast_scanner_profile_path(object.project, object)
end
......
......@@ -29,6 +29,11 @@ module Types
field :normalized_target_url, GraphQL::STRING_TYPE, null: true,
description: 'Normalized URL of the target to be scanned.'
field :referenced_in_security_policies, [GraphQL::STRING_TYPE], null: true,
complexity: 10,
calls_gitaly: true,
description: 'List of security policy names that are referencing given project.'
def target_url
object.dast_site.url
end
......
......@@ -17,4 +17,10 @@ class DastScannerProfile < ApplicationRecord
def full_scan_enabled?
scan_type == 'active'
end
def referenced_in_security_policies
return [] unless project.security_orchestration_policy_configuration.present?
project.security_orchestration_policy_configuration.active_policy_names_with_dast_scanner_profile(name)
end
end
......@@ -21,6 +21,12 @@ class DastSiteProfile < ApplicationRecord
dast_site_validation.state
end
def referenced_in_security_policies
return [] unless project.security_orchestration_policy_configuration.present?
project.security_orchestration_policy_configuration.active_policy_names_with_dast_site_profile(name)
end
private
def cleanup_dast_site
......
......@@ -2,6 +2,8 @@
module Security
class OrchestrationPolicyConfiguration < ApplicationRecord
include Gitlab::Utils::StrongMemoize
self.table_name = 'security_orchestration_policy_configurations'
POLICIES_BASE_PATH = '.gitlab/security-policies/'
......@@ -14,7 +16,13 @@ module Security
validates :project, presence: true, uniqueness: true
validates :security_policy_management_project, presence: true, uniqueness: true
def enabled?
::Feature.enabled?(:security_orchestration_policies_configuration, project)
end
def active_policies
return [] unless enabled?
security_policy_management_project
.repository
.ls_files(security_policy_management_project.default_branch_or_master)
......@@ -30,8 +38,33 @@ module Security
.select { |action| action[:scan].in?(ON_DEMAND_SCANS) }
end
def active_policy_names_with_dast_site_profile(profile_name)
active_policy_names_with_dast_profiles.dig(:site_profiles, profile_name)
end
def active_policy_names_with_dast_scanner_profile(profile_name)
active_policy_names_with_dast_profiles.dig(:scanner_profiles, profile_name)
end
private
def active_policy_names_with_dast_profiles
strong_memoize(:active_policy_names_with_dast_profiles) do
profiles = { site_profiles: Hash.new { Set.new }, scanner_profiles: Hash.new { Set.new } }
active_policies.each do |policy|
policy[:actions].each do |action|
next unless action[:scan].in?(ON_DEMAND_SCANS)
profiles[:site_profiles][action[:site_profile]] += [policy[:name]]
profiles[:scanner_profiles][action[:scanner_profile]] += [policy[:name]] if action[:scanner_profile].present?
end
end
profiles
end
end
def policy_at(path)
security_policy_management_project
.repository
......
......@@ -9,6 +9,7 @@ module DastScannerProfiles
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: "Scanner profile not found for given parameters") unless dast_scanner_profile
return ServiceResponse.error(message: "Cannot delete #{dast_scanner_profile.name} referenced in security policy") if referenced_in_security_policy?(dast_scanner_profile)
if dast_scanner_profile.destroy
ServiceResponse.success(payload: dast_scanner_profile)
......@@ -23,6 +24,10 @@ module DastScannerProfiles
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
......
......@@ -9,6 +9,7 @@ module DastScannerProfiles
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: "Scanner profile not found for given parameters") unless dast_scanner_profile
return ServiceResponse.error(message: "Cannot modify #{dast_scanner_profile.name} referenced in security policy") if referenced_in_security_policy?(dast_scanner_profile)
update_args = {
name: profile_name,
......@@ -32,6 +33,10 @@ module DastScannerProfiles
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_update_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
......
# frozen_string_literal: true
module DastSiteProfiles
class DestroyService < BaseService
include Gitlab::Allowable
def execute(id:)
return unauthorized unless can_delete_site_profile?
dast_site_profile = find_dast_site_profile(id)
return ServiceResponse.error(message: "Site profile not found for given parameters") unless dast_site_profile
return ServiceResponse.error(message: "Cannot delete #{dast_site_profile.name} referenced in security policy") if referenced_in_security_policy?(dast_site_profile)
if dast_site_profile.destroy
ServiceResponse.success(payload: dast_site_profile)
else
ServiceResponse.error(message: 'Site profile failed to delete')
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to delete this site profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_site_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_site_profile(id)
project.dast_site_profiles.id_in(id).first
end
end
end
......@@ -5,9 +5,11 @@ module DastSiteProfiles
def execute(id:, profile_name:, target_url:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
ActiveRecord::Base.transaction do
dast_site_profile = find_dast_site_profile!(id)
dast_site_profile = find_dast_site_profile!(id)
return ServiceResponse.error(message: "Cannot modify #{dast_site_profile.name} referenced in security policy") if referenced_in_security_policy?(dast_site_profile)
ActiveRecord::Base.transaction do
service = DastSites::FindOrCreateService.new(project, current_user)
dast_site = service.execute!(url: target_url)
......@@ -28,6 +30,10 @@ module DastSiteProfiles
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
# rubocop: disable CodeReuse/ActiveRecord
def find_dast_site_profile!(id)
DastSiteProfilesFinder.new(project_id: project.id, id: id).execute.first!
......
......@@ -45,9 +45,9 @@ RSpec.describe Mutations::DastSiteProfiles::Delete do
context 'when there is an issue deleting the dast_site_profile' do
it 'returns an error' do
allow(mutation).to receive(:find_dast_site_profile).and_return(dast_site_profile)
allow(dast_site_profile).to receive(:destroy).and_return(false)
dast_site_profile.errors.add(:name, 'is weird')
allow_next_instance_of(::DastSiteProfiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird']))
end
expect(subject[:errors]).to include('Name is weird')
end
......
......@@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['DastScannerProfile'] do
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile) }
let_it_be(:project) { dast_scanner_profile.project }
let_it_be(:user) { create(:user) }
let_it_be(:fields) { %i[id globalId profileName spiderTimeout targetTimeout editPath scanType useAjaxSpider showDebugMessages] }
let_it_be(:fields) { %i[id globalId profileName spiderTimeout targetTimeout editPath scanType useAjaxSpider showDebugMessages referencedInSecurityPolicies] }
let(:response) do
GitlabSchema.execute(
......@@ -50,6 +50,7 @@ RSpec.describe GitlabSchema.types['DastScannerProfile'] do
scanType
useAjaxSpider
showDebugMessages
referencedInSecurityPolicies
}
}
}
......
......@@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do
let_it_be(:dast_site_profile) { create(:dast_site_profile) }
let_it_be(:project) { dast_site_profile.project }
let_it_be(:user) { create(:user) }
let_it_be(:fields) { %i[id profileName targetUrl editPath validationStatus userPermissions normalizedTargetUrl] }
let_it_be(:fields) { %i[id profileName targetUrl editPath validationStatus userPermissions normalizedTargetUrl referencedInSecurityPolicies] }
subject do
GitlabSchema.execute(
......@@ -47,6 +47,7 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do
editPath
validationStatus
normalizedTargetUrl
referencedInSecurityPolicies
}
}
}
......
......@@ -46,4 +46,30 @@ RSpec.describe DastScannerProfile, type: :model do
it { is_expected.to eq(false) }
end
end
describe '#referenced_in_security_policies' do
context 'there is no security_orchestration_policy_configuration assigned to project' do
it 'returns the referenced policy name' do
expect(subject.referenced_in_security_policies).to eq([])
end
end
context 'there is security_orchestration_policy_configuration assigned to project' do
let(:security_orchestration_policy_configuration) { instance_double(Security::OrchestrationPolicyConfiguration, present?: true, active_policy_names_with_dast_scanner_profile: ['Policy Name']) }
before do
allow(subject.project).to receive(:security_orchestration_policy_configuration).and_return(security_orchestration_policy_configuration)
end
it 'calls security_orchestration_policy_configuration.active_policy_names_with_dast_scanner_profile with profile name' do
expect(security_orchestration_policy_configuration).to receive(:active_policy_names_with_dast_scanner_profile).with(subject.name)
subject.referenced_in_security_policies
end
it 'returns empty array' do
expect(subject.referenced_in_security_policies).to eq(['Policy Name'])
end
end
end
end
......@@ -98,4 +98,30 @@ RSpec.describe DastSiteProfile, type: :model do
end
end
end
describe '#referenced_in_security_policies' do
context 'there is no security_orchestration_policy_configuration assigned to project' do
it 'returns empty array' do
expect(subject.referenced_in_security_policies).to eq([])
end
end
context 'there is security_orchestration_policy_configuration assigned to project' do
let(:security_orchestration_policy_configuration) { instance_double(Security::OrchestrationPolicyConfiguration, present?: true, active_policy_names_with_dast_site_profile: ['Policy Name']) }
before do
allow(subject.project).to receive(:security_orchestration_policy_configuration).and_return(security_orchestration_policy_configuration)
end
it 'calls security_orchestration_policy_configuration.active_policy_names_with_dast_site_profile with profile name' do
expect(security_orchestration_policy_configuration).to receive(:active_policy_names_with_dast_site_profile).with(subject.name)
subject.referenced_in_security_policies
end
it 'returns the referenced policy name' do
expect(subject.referenced_in_security_policies).to eq(['Policy Name'])
end
end
end
end
......@@ -24,6 +24,22 @@ RSpec.describe Security::OrchestrationPolicyConfiguration do
it { is_expected.to validate_uniqueness_of(:security_policy_management_project) }
end
describe '#enabled?' do
subject { security_orchestration_policy_configuration.enabled? }
context 'when feature is enabled' do
it { is_expected.to eq(true) }
end
context 'when feature is disabled' do
before do
stub_feature_flags(security_orchestration_policies_configuration: false)
end
it { is_expected.to eq(false) }
end
end
describe '#active_policies' do
let(:enforce_dast_yaml) do
<<-EOS
......@@ -86,6 +102,16 @@ RSpec.describe Security::OrchestrationPolicyConfiguration do
it 'returns only enabled policies' do
expect(active_policies).to eq(expected_active_policies)
end
context 'when feature is disabled' do
before do
stub_feature_flags(security_orchestration_policies_configuration: false)
end
it 'returns empty array' do
expect(active_policies).to eq([])
end
end
end
describe '#on_demand_scan_actions' do
......@@ -173,4 +199,68 @@ RSpec.describe Security::OrchestrationPolicyConfiguration do
expect(on_demand_scan_actions).to eq(expected_actions)
end
end
describe '#active_policy_names_with_dast_site_profile' do
let(:enforce_dast_yaml) do
<<-EOS
type: scan_execution_policy
name: Run DAST in every pipeline
description: This policy enforces to run DAST for every pipeline within the project
enabled: true
rules:
- type: pipeline
branches:
- "production"
actions:
- scan: dast
site_profile: Site Profile
scanner_profile: Scanner Profile
- scan: dast
site_profile: Site Profile
scanner_profile: Scanner Profile 2
EOS
end
before do
allow(security_policy_management_project).to receive(:repository).and_return(repository)
allow(repository).to receive(:ls_files).and_return(['.gitlab/security-policies/enforce-dast.yml'])
allow(repository).to receive(:blob_data_at).with(default_branch, '.gitlab/security-policies/enforce-dast.yml').and_return(enforce_dast_yaml)
end
it 'returns list of policy names where site profile is referenced' do
expect(security_orchestration_policy_configuration.active_policy_names_with_dast_site_profile('Site Profile')).to contain_exactly('Run DAST in every pipeline')
end
end
describe '#active_policy_names_with_dast_scanner_profile' do
let(:enforce_dast_yaml) do
<<-EOS
type: scan_execution_policy
name: Run DAST in every pipeline
description: This policy enforces to run DAST for every pipeline within the project
enabled: true
rules:
- type: pipeline
branches:
- "production"
actions:
- scan: dast
site_profile: Site Profile
scanner_profile: Scanner Profile
- scan: dast
site_profile: Site Profile 2
scanner_profile: Scanner Profile
EOS
end
before do
allow(security_policy_management_project).to receive(:repository).and_return(repository)
allow(repository).to receive(:ls_files).and_return(['.gitlab/security-policies/enforce-dast.yml'])
allow(repository).to receive(:blob_data_at).with(default_branch, '.gitlab/security-policies/enforce-dast.yml').and_return(enforce_dast_yaml)
end
it 'returns list of policy names where site profile is referenced' do
expect(security_orchestration_policy_configuration.active_policy_names_with_dast_scanner_profile('Scanner Profile')).to contain_exactly('Run DAST in every pipeline')
end
end
end
......@@ -8,11 +8,12 @@ RSpec.describe 'Creating a DAST Site Profile' do
let!(:dast_site_profile) { create(:dast_site_profile, project: project) }
let(:mutation_name) { :dast_site_profile_delete }
let(:dast_site_profile_id) { dast_site_profile.to_global_id.to_s }
let(:mutation) do
graphql_mutation(
mutation_name,
full_path: full_path,
id: dast_site_profile.to_global_id.to_s
id: dast_site_profile_id
)
end
......@@ -24,22 +25,18 @@ RSpec.describe 'Creating a DAST Site Profile' do
context 'when there is an issue deleting the dast_site_profile' do
before do
mutation_klass = Mutations::DastSiteProfiles::Delete
allow_any_instance_of(mutation_klass).to receive(:find_dast_site_profile).and_return(dast_site_profile)
allow(dast_site_profile).to receive(:destroy).and_return(false)
dast_site_profile.errors.add(:name, 'is weird')
allow_next_instance_of(::DastSiteProfiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird']))
end
end
it_behaves_like 'a mutation that returns errors in the response', errors: ['Name is weird']
end
context 'when the dast_site_profile does not exist' do
before do
dast_site_profile.destroy!
end
let(:dast_site_profile_id) { Gitlab::GlobalId.build(nil, model_name: 'DastSiteProfile', id: 'does_not_exist') }
it_behaves_like 'a mutation that returns top-level errors',
errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
it_behaves_like 'a mutation that returns errors in the response', errors: ['Site profile not found for given parameters']
end
context 'when wrong type of global id is passed' do
......
......@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe DastScannerProfiles::DestroyService do
let_it_be(:user) { create(:user) }
let_it_be(:dast_scanner_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let(:project) { dast_scanner_profile.project }
let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let(:project) { dast_profile.project }
before do
stub_licensed_features(security_on_demand_scans: true)
......@@ -18,7 +18,7 @@ RSpec.describe DastScannerProfiles::DestroyService do
)
end
let(:dast_scanner_profile_id) { dast_scanner_profile.id }
let(:dast_scanner_profile_id) { dast_profile.id }
let(:status) { subject.status }
let(:message) { subject.message }
let(:payload) { subject.payload }
......@@ -77,6 +77,8 @@ RSpec.describe DastScannerProfiles::DestroyService do
expect(message).to eq('You are not authorized to update this scanner profile')
end
end
include_examples 'restricts modification if referenced by policy', :delete
end
end
end
......@@ -4,17 +4,17 @@ require 'spec_helper'
RSpec.describe DastScannerProfiles::UpdateService do
let_it_be(:user) { create(:user) }
let_it_be(:dast_scanner_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let_it_be(:dast_scanner_profile_2, reload: true) { create(:dast_scanner_profile) }
let(:project) { dast_scanner_profile.project }
let(:project_2) { dast_scanner_profile_2.project }
let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let_it_be(:dast_profile_2, reload: true) { create(:dast_scanner_profile) }
let(:project) { dast_profile.project }
let(:project_2) { dast_profile_2.project }
let_it_be(:new_profile_name) { SecureRandom.hex }
let_it_be(:new_target_timeout) { dast_scanner_profile.target_timeout + 1 }
let_it_be(:new_spider_timeout) { dast_scanner_profile.spider_timeout + 1 }
let_it_be(:new_target_timeout) { dast_profile.target_timeout + 1 }
let_it_be(:new_spider_timeout) { dast_profile.spider_timeout + 1 }
let_it_be(:new_scan_type) { (DastScannerProfile.scan_types.keys - [DastScannerProfile.last.scan_type]).first }
let(:new_use_ajax_spider) { !dast_scanner_profile.use_ajax_spider }
let(:new_show_debug_messages) { !dast_scanner_profile.show_debug_messages }
let(:new_use_ajax_spider) { !dast_profile.use_ajax_spider }
let(:new_show_debug_messages) { !dast_profile.show_debug_messages }
before do
stub_licensed_features(security_on_demand_scans: true)
......@@ -33,7 +33,7 @@ RSpec.describe DastScannerProfiles::UpdateService do
)
end
let(:dast_scanner_profile_id) { dast_scanner_profile.id }
let(:dast_scanner_profile_id) { dast_profile.id }
let(:status) { subject.status }
let(:message) { subject.message }
let(:payload) { subject.payload }
......@@ -56,7 +56,7 @@ RSpec.describe DastScannerProfiles::UpdateService do
subject do
described_class.new(project_2, user).execute(
id: dast_scanner_profile.id,
id: dast_profile.id,
profile_name: new_profile_name,
target_timeout: new_target_timeout,
spider_timeout: new_spider_timeout,
......@@ -94,9 +94,9 @@ RSpec.describe DastScannerProfiles::UpdateService do
updated_dast_scanner_profile = payload.reload
aggregate_failures do
expect(updated_dast_scanner_profile.scan_type).to eq(dast_scanner_profile.scan_type)
expect(updated_dast_scanner_profile.use_ajax_spider).to eq(dast_scanner_profile.use_ajax_spider)
expect(updated_dast_scanner_profile.show_debug_messages).to eq(dast_scanner_profile.show_debug_messages)
expect(updated_dast_scanner_profile.scan_type).to eq(dast_profile.scan_type)
expect(updated_dast_scanner_profile.use_ajax_spider).to eq(dast_profile.use_ajax_spider)
expect(updated_dast_scanner_profile.show_debug_messages).to eq(dast_profile.show_debug_messages)
end
end
end
......@@ -164,6 +164,8 @@ RSpec.describe DastScannerProfiles::UpdateService do
expect(message).to eq('You are not authorized to update this scanner profile')
end
end
include_examples 'restricts modification if referenced by policy', :modify
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DastSiteProfiles::DestroyService do
let_it_be(:user) { create(:user) }
let_it_be(:dast_profile, reload: true) { create(:dast_site_profile) }
let(:project) { dast_profile.project }
before do
stub_licensed_features(security_on_demand_scans: true)
end
describe '#execute' do
subject do
described_class.new(project, user).execute(
id: dast_site_profile_id
)
end
let(:dast_site_profile_id) { dast_profile.id }
let(:status) { subject.status }
let(:message) { subject.message }
let(:payload) { subject.payload }
context 'when a user does not have access to the project' do
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('You are not authorized to delete this site profile')
end
end
context 'when the user can run a DAST scan' do
before do
project.add_developer(user)
end
it 'returns a success status' do
expect(status).to eq(:success)
end
it 'deletes the dast_site_profile' do
expect { subject }.to change { DastSiteProfile.count }.by(-1)
end
it 'returns a dast_site_profile payload' do
expect(payload).to be_a(DastSiteProfile)
end
context 'when the dast_site_profile does not exist' do
let(:dast_site_profile_id) do
Gitlab::GlobalId.build(nil, model_name: 'DastSiteProfile', id: 'does_not_exist')
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Site profile not found for given parameters')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('You are not authorized to delete this site profile')
end
end
include_examples 'restricts modification if referenced by policy', :delete
end
end
end
......@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe DastSiteProfiles::UpdateService do
let(:project) { dast_site_profile.project }
let(:project) { dast_profile.project }
let(:user) { create(:user) }
let(:dast_site_profile) { create(:dast_site_profile) }
let(:dast_profile) { create(:dast_site_profile) }
let(:new_profile_name) { SecureRandom.hex }
let(:new_target_url) { generate(:url) }
......@@ -17,7 +17,7 @@ RSpec.describe DastSiteProfiles::UpdateService do
describe '#execute' do
subject do
described_class.new(project, user).execute(
id: dast_site_profile.id,
id: dast_profile.id,
profile_name: new_profile_name,
target_url: new_target_url
)
......@@ -74,7 +74,7 @@ RSpec.describe DastSiteProfiles::UpdateService do
context 'when the dast_site_profile doesn\'t exist' do
before do
dast_site_profile.destroy!
dast_profile.destroy!
end
it 'returns an error status' do
......@@ -99,6 +99,8 @@ RSpec.describe DastSiteProfiles::UpdateService do
expect(message).to eq('Insufficient permissions')
end
end
include_examples 'restricts modification if referenced by policy', :modify
end
end
end
# frozen_string_literal: true
RSpec.shared_examples 'restricts modification if referenced by policy' do |modification_type|
context 'when project has security policies enabled' do
before do
allow_next_found_instance_of(dast_profile.class) do |profile|
allow(profile).to receive(:referenced_in_security_policies).and_return(policy_names)
end
end
context 'when there is no policy that is referencing the profile' do
let(:policy_names) { [] }
it 'returns a success status' do
expect(status).to eq(:success)
end
end
context 'when there is a policy that is referencing the profile' do
let(:policy_names) { [dast_profile.name] }
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to include("Cannot #{modification_type}")
end
end
end
end
......@@ -33888,6 +33888,9 @@ msgstr ""
msgid "You are not allowed to unlink your primary login account"
msgstr ""
msgid "You are not authorized to delete this site profile"
msgstr ""
msgid "You are not authorized to perform this action"
msgstr ""
......
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