Commit 8214da07 authored by Ash McKenzie's avatar Ash McKenzie

Merge branch 'create-dast-site-profile-from-mutation-225404' into 'master'

Create DastSiteProfile from GraphQL mutation

See merge request gitlab-org/gitlab!37276
parents bdc878ba bb54da0d
......@@ -30,12 +30,12 @@ module Mutations
raise_resource_not_available_error! unless Feature.enabled?(:security_on_demand_scans_feature_flag, project)
service = ::DastSiteProfiles::CreateService.new(project, current_user)
dast_site_profile = service.execute(name: profile_name, target_url: target_url)
result = service.execute(name: profile_name, target_url: target_url)
if dast_site_profile.success?
raise 'Not implemented'
if result.success?
{ id: result.payload.to_global_id, errors: [] }
else
{ errors: dast_site_profile.errors }
{ errors: result.errors }
end
end
......
......@@ -2,12 +2,23 @@
module DastSiteProfiles
class CreateService < BaseService
def execute(name: nil, target_url: nil)
def execute(name:, target_url:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
ServiceResponse.error(message: 'Not implemented')
ActiveRecord::Base.transaction do
service = DastSites::FindOrCreateService.new(project, current_user)
dast_site = service.execute!(url: target_url)
dast_site_profile = DastSiteProfile.create!(project: project, dast_site: dast_site, name: name)
ServiceResponse.success(payload: dast_site_profile)
end
rescue ActiveRecord::RecordInvalid => err
ServiceResponse.error(message: err.record.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :run_ondemand_dast_scan, project)
end
......
# frozen_string_literal: true
module DastSites
class FindOrCreateService < BaseService
PermissionsError = Class.new(StandardError)
def execute!(url:)
raise PermissionsError.new('Insufficient permissions') unless allowed?
find_or_create_by!(url)
end
private
def allowed?
Ability.allowed?(current_user, :run_ondemand_dast_scan, project)
end
def find_or_create_by!(url)
DastSite.safe_find_or_create_by!(project: project, url: url)
end
end
end
......@@ -9,6 +9,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
let(:full_path) { project.full_path }
let(:profile_name) { SecureRandom.hex }
let(:target_url) { FFaker::Internet.uri(:https) }
let(:dast_site_profile) { DastSiteProfile.find_by(project: project, name: profile_name) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
......@@ -47,26 +48,52 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
end
context 'when the user is an owner' do
it 'stubs out the response' do
it 'returns the dast_site_profile id' do
group.add_owner(user)
expect(subject[:errors]).to eq(['Not implemented'])
expect(subject[:id]).to eq(dast_site_profile.to_global_id)
end
end
context 'when the user is a maintainer' do
it 'stubs out the response' do
it 'returns the dast_site_profile id' do
project.add_maintainer(user)
expect(subject[:errors]).to eq(['Not implemented'])
expect(subject[:id]).to eq(dast_site_profile.to_global_id)
end
end
context 'when the user is a developer' do
it 'stubs out the response' do
before do
project.add_developer(user)
end
it 'returns the dast_site_profile id' do
expect(subject[:id]).to eq(dast_site_profile.to_global_id)
end
it 'calls the dast_site_profile creation service' do
service = double(described_class)
result = double('result', success?: false, errors: [])
expect(DastSiteProfiles::CreateService).to receive(:new).and_return(service)
expect(service).to receive(:execute).with(name: profile_name, target_url: target_url).and_return(result)
subject
end
context 'when the project name already exists' do
it 'returns an error' do
subject
response = mutation.resolve(
full_path: full_path,
profile_name: profile_name,
target_url: target_url
)
expect(subject[:errors]).to eq(['Not implemented'])
expect(response[:errors]).to include('Name has already been taken')
end
end
end
end
......
......@@ -10,6 +10,7 @@ RSpec.describe 'Creating a DAST Site Profile' do
let(:full_path) { project.full_path }
let(:profile_name) { FFaker::Company.catch_phrase }
let(:target_url) { FFaker::Internet.uri(:https) }
let(:dast_site_profile) { DastSiteProfile.find_by(project: project, name: profile_name) }
let(:mutation) do
graphql_mutation(
......@@ -46,7 +47,19 @@ RSpec.describe 'Creating a DAST Site Profile' do
project.add_developer(current_user)
end
it_behaves_like 'a mutation that returns errors in the response', errors: ['Not implemented']
it 'returns the dast_site_profile id' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response["id"]).to eq(dast_site_profile.to_global_id.to_s)
end
context 'when an unknown error occurs' do
before do
allow(DastSiteProfile).to receive(:create!).and_raise(StandardError)
end
it_behaves_like 'a mutation that returns top-level errors', errors: ['Internal server error']
end
end
end
end
......@@ -13,6 +13,8 @@ RSpec.describe DastSiteProfiles::CreateService do
let(:status) { subject.status }
let(:message) { subject.message }
let(:errors) { subject.errors }
let(:payload) { subject.payload }
context 'when the user does not have permission to run a dast scan' do
it 'returns an error status' do
......@@ -29,12 +31,46 @@ RSpec.describe DastSiteProfiles::CreateService do
project.add_developer(user)
end
it 'returns an error status' do
expect(status).to eq(:error)
it 'returns a success status' do
expect(status).to eq(:success)
end
it 'populates message' do
expect(message).to eq('Not implemented')
it 'creates a dast_site_profile' do
expect { subject }.to change(DastSiteProfile, :count).by(1)
end
it 'creates a dast_site' do
expect { subject }.to change(DastSite, :count).by(1)
end
it 'returns a dast_site_profile payload' do
expect(payload).to be_a(DastSiteProfile)
end
context 'when the dast_site already exists' do
before do
create(:dast_site, project: project, url: target_url)
end
it 'returns a success status' do
expect(status).to eq(:success)
end
it 'does not create a new dast_site' do
expect { subject }.not_to change(DastSite, :count)
end
end
context 'when the target url is localhost' do
let(:target_url) { 'http://localhost:3000/hello-world' }
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates errors' do
expect(errors).to include('Url is blocked: Requests to localhost are not allowed')
end
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DastSites::FindOrCreateService do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user) }
let(:url) { FFaker::Internet.uri(:http) }
describe '#execute!' do
subject { described_class.new(project, user).execute!(url: url) }
context 'when the user does not have permission to run a dast scan' do
it 'raises an exception' do
expect { subject }.to raise_error(DastSites::FindOrCreateService::PermissionsError) do |err|
expect(err.message).to include('Insufficient permissions')
end
end
end
context 'when the user can run a dast scan' do
before do
project.add_developer(user)
end
it 'returns a dast_site' do
expect(subject).to be_a(DastSite)
end
it 'creates a dast_site' do
expect { subject }.to change(DastSite, :count).by(1)
end
context 'when the dast_site already exists' do
before do
create(:dast_site, project: project, url: url)
end
it 'returns the existing dast_site' do
expect(subject).to be_a(DastSite)
end
it 'does not create a new dast_site' do
expect { subject }.not_to change(DastSite, :count)
end
end
context 'when the target url is localhost' do
let(:url) { 'http://localhost:3000/hello-world' }
it 'raises an exception' do
expect { subject }.to raise_error(ActiveRecord::RecordInvalid) do |err|
expect(err.record.errors.full_messages).to include('Url is blocked: Requests to localhost are not allowed')
end
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