Commit 38caa7f0 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!36792
parents 42f861cc 1e7845ed
...@@ -30,12 +30,12 @@ module Mutations ...@@ -30,12 +30,12 @@ module Mutations
raise_resource_not_available_error! unless Feature.enabled?(:security_on_demand_scans_feature_flag, project) raise_resource_not_available_error! unless Feature.enabled?(:security_on_demand_scans_feature_flag, project)
service = ::DastSiteProfiles::CreateService.new(project, current_user) 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? if result.success?
raise 'Not implemented' { id: result.payload.to_global_id, errors: [] }
else else
{ errors: dast_site_profile.errors } { errors: result.errors }
end end
end end
......
...@@ -2,12 +2,26 @@ ...@@ -2,12 +2,26 @@
module DastSiteProfiles module DastSiteProfiles
class CreateService < BaseService class CreateService < BaseService
def execute(name: nil, target_url: nil) def execute(name:, target_url:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed? 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)
rescue => err
Gitlab::ErrorTracking.track_exception(err)
ServiceResponse.error(message: 'Internal server error')
end end
private
def allowed? def allowed?
Ability.allowed?(current_user, :run_ondemand_dast_scan, project) Ability.allowed?(current_user, :run_ondemand_dast_scan, project)
end 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
...@@ -47,26 +47,52 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -47,26 +47,52 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
end end
context 'when the user is an owner' do 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) group.add_owner(user)
expect(subject[:errors]).to eq(['Not implemented']) expect(subject[:id].to_s).to include('gid://gitlab/DastSiteProfile/1')
end end
end end
context 'when the user is a maintainer' do 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) project.add_maintainer(user)
expect(subject[:errors]).to eq(['Not implemented']) expect(subject[:id].to_s).to include('gid://gitlab/DastSiteProfile/2')
end end
end end
context 'when the user is a developer' do context 'when the user is a developer' do
it 'stubs out the response' do before do
project.add_developer(user) project.add_developer(user)
end
it 'returns the dast_site_profile id' do
expect(subject[:id].to_s).to include('gid://gitlab/DastSiteProfile/3')
end
it 'calls the dast_site_profile creation service' do
service = double('service')
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 end
end end
......
...@@ -46,7 +46,11 @@ RSpec.describe 'Creating a DAST Site Profile' do ...@@ -46,7 +46,11 @@ RSpec.describe 'Creating a DAST Site Profile' do
project.add_developer(current_user) project.add_developer(current_user)
end end
it_behaves_like 'a mutation that returns errors in the response', errors: ['Not implemented'] it 'returns a the dast_site_profile id' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['id']).to eq('gid://gitlab/DastSiteProfile/1')
end
end end
end end
end end
...@@ -13,6 +13,8 @@ RSpec.describe DastSiteProfiles::CreateService do ...@@ -13,6 +13,8 @@ RSpec.describe DastSiteProfiles::CreateService do
let(:status) { subject.status } let(:status) { subject.status }
let(:message) { subject.message } 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 context 'when the user does not have permission to run a dast scan' do
it 'returns an error status' do it 'returns an error status' do
...@@ -29,12 +31,54 @@ RSpec.describe DastSiteProfiles::CreateService do ...@@ -29,12 +31,54 @@ RSpec.describe DastSiteProfiles::CreateService do
project.add_developer(user) project.add_developer(user)
end end
it 'returns an error status' do it 'returns a success status' do
expect(status).to eq(:error) expect(status).to eq(:success)
end end
it 'populates message' do it 'creates a dast_site_profile' do
expect(message).to eq('Not implemented') 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
context 'when an unknown error occurs' do
it 'populates errors with a generic message' do
allow(DastSiteProfile).to receive(:create!).and_raise(StandardError)
expect(errors).to include('Internal server error')
end
end end
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