Commit f1ef965c authored by Philip Cunningham's avatar Philip Cunningham Committed by charlie ablett

Add DastScan model, dast_scans table and tests

Adds new model that groups DastSiteProfile and
DastScannerProfile.
parent d0db106b
---
title: Add dast_profiles database table
merge_request: 51296
author:
type: added
# frozen_string_literal: true
class CreateDastProfiles < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
table_comment = { owner: 'group::dynamic analysis', description: 'Profile used to run a DAST on-demand scan' }
create_table_with_constraints :dast_profiles, comment: table_comment.to_json do |t| # rubocop:disable Migration/AddLimitToTextColumns
t.references :project, null: false, foreign_key: false, index: false
t.references :dast_site_profile, null: false, foreign_key: { on_delete: :cascade }
t.references :dast_scanner_profile, null: false, foreign_key: { on_delete: :cascade }
t.timestamps_with_timezone
# rubocop:disable Migration/AddLimitToTextColumns
t.text :name, null: false
t.text :description, null: false
# rubocop:enable Migration/AddLimitToTextColumns
t.index [:project_id, :name], unique: true
t.text_limit :name, 255
t.text_limit :description, 255
end
end
def down
with_lock_retries do
drop_table :dast_profiles
end
end
end
# frozen_string_literal: true
class AddProjectFkForDastProfile < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key :dast_profiles, :projects, column: :project_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :dast_profiles, column: :project_id
end
end
end
6075e469081fcca124c0c4b485071a086545b502c398314cca05052765072caf
\ No newline at end of file
a98ca25378df3fc798b6ae361b3a47b697f6b853796975221329db023cb98466
\ No newline at end of file
......@@ -11589,6 +11589,30 @@ CREATE SEQUENCE custom_emoji_id_seq
ALTER SEQUENCE custom_emoji_id_seq OWNED BY custom_emoji.id;
CREATE TABLE dast_profiles (
id bigint NOT NULL,
project_id bigint NOT NULL,
dast_site_profile_id bigint NOT NULL,
dast_scanner_profile_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
name text NOT NULL,
description text NOT NULL,
CONSTRAINT check_5fcf73bf61 CHECK ((char_length(name) <= 255)),
CONSTRAINT check_c34e505c24 CHECK ((char_length(description) <= 255))
);
COMMENT ON TABLE dast_profiles IS '{"owner":"group::dynamic analysis","description":"Profile used to run a DAST on-demand scan"}';
CREATE SEQUENCE dast_profiles_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dast_profiles_id_seq OWNED BY dast_profiles.id;
CREATE TABLE dast_scanner_profiles (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
......@@ -18574,6 +18598,8 @@ ALTER TABLE ONLY csv_issue_imports ALTER COLUMN id SET DEFAULT nextval('csv_issu
ALTER TABLE ONLY custom_emoji ALTER COLUMN id SET DEFAULT nextval('custom_emoji_id_seq'::regclass);
ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profiles ALTER COLUMN id SET DEFAULT nextval('dast_site_profiles_id_seq'::regclass);
......@@ -19723,6 +19749,9 @@ ALTER TABLE ONLY csv_issue_imports
ALTER TABLE ONLY custom_emoji
ADD CONSTRAINT custom_emoji_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_profiles
ADD CONSTRAINT dast_profiles_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_scanner_profiles
ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id);
......@@ -21546,6 +21575,12 @@ CREATE UNIQUE INDEX index_custom_emoji_on_namespace_id_and_name ON custom_emoji
CREATE UNIQUE INDEX index_daily_build_group_report_results_unique_columns ON ci_daily_build_group_report_results USING btree (project_id, ref_path, date, group_name);
CREATE INDEX index_dast_profiles_on_dast_scanner_profile_id ON dast_profiles USING btree (dast_scanner_profile_id);
CREATE INDEX index_dast_profiles_on_dast_site_profile_id ON dast_profiles USING btree (dast_site_profile_id);
CREATE UNIQUE INDEX index_dast_profiles_on_project_id_and_name ON dast_profiles USING btree (project_id, name);
CREATE UNIQUE INDEX index_dast_scanner_profiles_on_project_id_and_name ON dast_scanner_profiles USING btree (project_id, name);
CREATE INDEX index_dast_site_profiles_on_dast_site_id ON dast_site_profiles USING btree (dast_site_id);
......@@ -24095,6 +24130,9 @@ ALTER TABLE ONLY merge_requests
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_aa5798e761 FOREIGN KEY (closed_by_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY dast_profiles
ADD CONSTRAINT fk_aa76ef30e9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY alert_management_alerts
ADD CONSTRAINT fk_aad61aedca FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE SET NULL;
......@@ -24569,6 +24607,9 @@ ALTER TABLE ONLY service_desk_settings
ALTER TABLE ONLY saml_group_links
ADD CONSTRAINT fk_rails_22e312c530 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_profiles
ADD CONSTRAINT fk_rails_23cae5abe1 FOREIGN KEY (dast_scanner_profile_id) REFERENCES dast_scanner_profiles(id) ON DELETE CASCADE;
ALTER TABLE ONLY group_custom_attributes
ADD CONSTRAINT fk_rails_246e0db83a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
......@@ -25643,6 +25684,9 @@ ALTER TABLE ONLY alert_management_alert_user_mentions
ALTER TABLE ONLY snippet_statistics
ADD CONSTRAINT fk_rails_ebc283ccf1 FOREIGN KEY (snippet_id) REFERENCES snippets(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_profiles
ADD CONSTRAINT fk_rails_ed1e66fbbf FOREIGN KEY (dast_site_profile_id) REFERENCES dast_site_profiles(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_security_settings
ADD CONSTRAINT fk_rails_ed4abe1338 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
......
# frozen_string_literal: true
module Dast
class Profile < ApplicationRecord
self.table_name = 'dast_profiles'
belongs_to :project
belongs_to :dast_site_profile
belongs_to :dast_scanner_profile
validates :description, length: { maximum: 255 }
validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }
validates :project_id, :dast_site_profile_id, :dast_scanner_profile_id, presence: true
validate :project_ids_match
private
def project_ids_match
association_project_id_matches(dast_site_profile)
association_project_id_matches(dast_scanner_profile)
end
def association_project_id_matches(association)
return if association.nil?
unless project_id == association.project_id
errors.add(:project_id, "must match #{association.class.underscore}.project_id")
end
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :dast_profile, class: 'Dast::Profile' do
project
dast_site_profile { association :dast_site_profile, project: project }
dast_scanner_profile { association :dast_scanner_profile, project: project }
sequence :name do |i|
"#{FFaker::Product.product_name.truncate(200)} - #{i}"
end
description { FFaker::Product.product_name }
trait :with_dast_site_validation do
dast_site { association :dast_site, :with_dast_site_validation, project: project }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Dast::Profile, type: :model do
subject { create(:dast_profile) }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:dast_site_profile) }
it { is_expected.to belong_to(:dast_scanner_profile) }
end
describe 'validations' do
it { is_expected.to be_valid }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_length_of(:description).is_at_most(255) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
it { is_expected.to validate_presence_of(:project_id) }
it { is_expected.to validate_presence_of(:dast_site_profile_id) }
it { is_expected.to validate_presence_of(:dast_scanner_profile_id) }
context 'when the project_id and dast_site_profile.project_id do not match' do
let(:project) { create(:project) }
let(:dast_site_profile) { create(:dast_site_profile) }
subject { build(:dast_profile, project: project, dast_site_profile: dast_site_profile) }
it 'is not valid' do
aggregate_failures do
expect(subject.valid?).to be_falsey
expect(subject.errors.full_messages).to include('Project must match dast_site_profile.project_id')
end
end
end
context 'when the project_id and dast_scanner_profile.project_id do not match' do
let(:project) { create(:project) }
let(:dast_scanner_profile) { create(:dast_scanner_profile) }
subject { build(:dast_profile, project: project, dast_scanner_profile: dast_scanner_profile) }
it 'is not valid' do
aggregate_failures do
expect(subject.valid?).to be_falsey
expect(subject.errors.full_messages).to include('Project must match dast_scanner_profile.project_id')
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