Commit db64b768 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 27704ed5 c1e16db6
/* global DocumentTouch */
import sortableConfig from 'ee_else_ce/sortable/sortable_config';
import sortableConfig from '~/sortable/sortable_config';
export function sortableStart() {
document.body.classList.add('is-dragging');
......
......@@ -550,8 +550,8 @@ export default {
if (id.startsWith('#note_')) {
const noteId = id.replace('#note_', '');
const discussion = this.$store.state.notes.discussions.find((d) =>
d.notes.find((n) => n.id === noteId),
const discussion = this.$store.state.notes.discussions.find(
(d) => d.diff_file && d.notes.find((n) => n.id === noteId),
);
if (discussion) {
......
......@@ -99,7 +99,7 @@ export default {
v-gl-tooltip.hover
variant="default"
icon="file-tree"
class="gl-mr-3 js-toggle-tree-list"
class="gl-mr-3 js-toggle-tree-list btn-icon"
:title="toggleFileBrowserTitle"
:aria-label="toggleFileBrowserTitle"
:selected="showTreeList"
......@@ -109,7 +109,7 @@ export default {
{{ __('Viewing commit') }}
<gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link>
</div>
<div v-if="hasNeighborCommits" class="commit-nav-buttons ml-3">
<div v-if="hasNeighborCommits" class="commit-nav-buttons">
<gl-button-group>
<gl-button
:href="previousCommitUrl"
......@@ -160,20 +160,21 @@ export default {
/>
</template>
</gl-sprintf>
<gl-button
v-if="commit || startVersion"
:href="latestVersionPath"
variant="default"
class="js-latest-version"
:class="{ 'gl-ml-3': commit && !hasNeighborCommits }"
>
{{ __('Show latest version') }}
</gl-button>
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
:diff-files-count-text="diffFilesCountText"
:added-lines="addedLines"
:removed-lines="removedLines"
/>
<gl-button
v-if="commit || startVersion"
:href="latestVersionPath"
variant="default"
class="gl-mr-3 js-latest-version"
>
{{ __('Show latest version') }}
</gl-button>
<gl-button
v-show="whichCollapsedTypes.any"
variant="default"
......
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import Sortable from 'sortablejs';
import sortableConfig from 'ee_else_ce/sortable/sortable_config';
import sortableConfig from '~/sortable/sortable_config';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
export default {
......
......@@ -183,6 +183,8 @@
}
.commit-nav-buttons {
margin: 0 0.5rem;
a.btn,
button {
// See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/730
......
......@@ -706,7 +706,7 @@ $tabs-holder-z-index: 250;
.mr-version-dropdown,
.mr-version-compare-dropdown {
margin: 0 7px;
margin: 0 0.5rem;
}
.dropdown-title {
......@@ -715,7 +715,7 @@ $tabs-holder-z-index: 250;
// Shortening button height by 1px to make compare-versions
// header 56px and fit into our 8px design grid
button {
.btn {
height: 34px;
}
......
......@@ -19,7 +19,7 @@ module HasIntegrations
def without_integration(integration)
integrations = Integration
.select('1')
.where('services.project_id = projects.id')
.where("#{Integration.table_name}.project_id = projects.id")
.where(type: integration.type)
Project
......
......@@ -167,7 +167,7 @@ class Group < Namespace
def without_integration(integration)
integrations = Integration
.select('1')
.where('services.group_id = namespaces.id')
.where("#{Integration.table_name}.group_id = namespaces.id")
.where(type: integration.type)
where('NOT EXISTS (?)', integrations)
......
......@@ -10,9 +10,6 @@ class Integration < ApplicationRecord
include FromUnion
include EachBatch
# TODO Rename the table: https://gitlab.com/gitlab-org/gitlab/-/issues/201856
self.table_name = 'services'
INTEGRATION_NAMES = %w[
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira
......@@ -299,7 +296,7 @@ class Integration < ApplicationRecord
array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]'
where(type: type, group_id: group_ids, inherit_from_id: nil)
.order(Arel.sql("array_position(#{array}::bigint[], services.group_id)"))
.order(Arel.sql("array_position(#{array}::bigint[], #{table_name}.group_id)"))
.first
end
private_class_method :closest_group_integration
......@@ -317,7 +314,7 @@ class Integration < ApplicationRecord
with_templates ? active.where(template: true) : none,
active.where(instance: true),
active.where(group_id: group_ids, inherit_from_id: nil)
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], services.group_id), instance DESC")).group_by(&:type).each do |type, records|
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")).group_by(&:type).each do |type, records|
build_from_integration(records.first, association => scope.id).save
end
end
......
# frozen_string_literal: true
class RenameServicesToIntegrations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::SchemaHelpers
# Function and trigger names match those migrated in:
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49916
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51852
WIKI_FUNCTION_NAME = 'set_has_external_wiki'
TRACKER_FUNCTION_NAME = 'set_has_external_issue_tracker'
WIKI_TRIGGER_ON_INSERT_NAME = 'trigger_has_external_wiki_on_insert'
WIKI_TRIGGER_ON_UPDATE_NAME = 'trigger_has_external_wiki_on_update'
WIKI_TRIGGER_ON_DELETE_NAME = 'trigger_has_external_wiki_on_delete'
TRACKER_TRIGGER_ON_INSERT_NAME = 'trigger_has_external_issue_tracker_on_insert'
TRACKER_TRIGGER_ON_UPDATE_NAME = 'trigger_has_external_issue_tracker_on_update'
TRACKER_TRIGGER_ON_DELETE_NAME = 'trigger_has_external_issue_tracker_on_delete'
ALL_TRIGGERS = [
WIKI_TRIGGER_ON_INSERT_NAME,
WIKI_TRIGGER_ON_UPDATE_NAME,
WIKI_TRIGGER_ON_DELETE_NAME,
TRACKER_TRIGGER_ON_INSERT_NAME,
TRACKER_TRIGGER_ON_UPDATE_NAME,
TRACKER_TRIGGER_ON_DELETE_NAME
].freeze
def up
execute('LOCK services IN ACCESS EXCLUSIVE MODE')
drop_all_triggers(:services)
rename_table_safely(:services, :integrations)
recreate_all_triggers(:integrations)
end
def down
execute('LOCK integrations IN ACCESS EXCLUSIVE MODE')
drop_all_triggers(:integrations)
undo_rename_table_safely(:services, :integrations)
recreate_all_triggers(:services)
end
private
def drop_all_triggers(table_name)
ALL_TRIGGERS.each do |trigger_name|
drop_trigger(table_name, trigger_name)
end
end
def recreate_all_triggers(table_name)
wiki_create_insert_trigger(table_name)
wiki_create_update_trigger(table_name)
wiki_create_delete_trigger(table_name)
tracker_replace_trigger_function(table_name)
tracker_create_insert_trigger(table_name)
tracker_create_update_trigger(table_name)
tracker_create_delete_trigger(table_name)
end
def wiki_create_insert_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_INSERT_NAME}
AFTER INSERT ON #{table_name}
FOR EACH ROW
WHEN (NEW.active = TRUE AND NEW.type = 'ExternalWikiService' AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
def wiki_create_update_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_UPDATE_NAME}
AFTER UPDATE ON #{table_name}
FOR EACH ROW
WHEN (NEW.type = 'ExternalWikiService' AND OLD.active != NEW.active AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
def wiki_create_delete_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_DELETE_NAME}
AFTER DELETE ON #{table_name}
FOR EACH ROW
WHEN (OLD.type = 'ExternalWikiService' AND OLD.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
# Using `replace: true` to rewrite the existing function
def tracker_replace_trigger_function(table_name)
create_trigger_function(TRACKER_FUNCTION_NAME, replace: true) do
<<~SQL
UPDATE projects SET has_external_issue_tracker = (
EXISTS
(
SELECT 1
FROM #{table_name}
WHERE project_id = COALESCE(NEW.project_id, OLD.project_id)
AND active = TRUE
AND category = 'issue_tracker'
)
)
WHERE projects.id = COALESCE(NEW.project_id, OLD.project_id);
RETURN NULL;
SQL
end
end
def tracker_create_insert_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_INSERT_NAME}
AFTER INSERT ON #{table_name}
FOR EACH ROW
WHEN (NEW.category = 'issue_tracker' AND NEW.active = TRUE AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
def tracker_create_update_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_UPDATE_NAME}
AFTER UPDATE ON #{table_name}
FOR EACH ROW
WHEN (NEW.category = 'issue_tracker' AND OLD.active != NEW.active AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
def tracker_create_delete_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_DELETE_NAME}
AFTER DELETE ON #{table_name}
FOR EACH ROW
WHEN (OLD.category = 'issue_tracker' AND OLD.active = TRUE AND OLD.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
end
# frozen_string_literal: true
class RenameServicesIndexesToIntegrations < ActiveRecord::Migration[6.1]
INDEXES = %w(
project_and_type_where_inherit_null
project_id_and_type_unique
template
type
type_and_instance_partial
type_and_template_partial
type_id_when_active_and_project_id_not_null
unique_group_id_and_type
).freeze
def up
INDEXES.each do |index|
execute(<<~SQL)
ALTER INDEX IF EXISTS "index_services_on_#{index}" RENAME TO "index_integrations_on_#{index}"
SQL
end
end
def down
INDEXES.each do |index|
execute(<<~SQL)
ALTER INDEX IF EXISTS "index_integrations_on_#{index}" RENAME TO "index_services_on_#{index}"
SQL
end
end
end
# frozen_string_literal: true
class StealBackgroundJobsThatReferenceServices < ActiveRecord::Migration[6.1]
def up
Gitlab::BackgroundMigration.steal('BackfillJiraTrackerDeploymentType2')
Gitlab::BackgroundMigration.steal('FixProjectsWithoutPrometheusService')
Gitlab::BackgroundMigration.steal('MigrateIssueTrackersSensitiveData')
Gitlab::BackgroundMigration.steal('RemoveDuplicateServices')
end
def down
# no-op
end
end
# frozen_string_literal: true
class FinalizeRenameServicesToIntegrations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def up
finalize_table_rename(:services, :integrations)
end
def down
undo_finalize_table_rename(:services, :integrations)
end
end
cfe35a1297c4a92c4b5e62757ed74c11ffd6f207777291c11b05a4e3cee91618
\ No newline at end of file
64babbed04b9e3bf59bb723b43e3c30730527f0e0e09906073b5bd9379067ab6
\ No newline at end of file
07d0de05b6a59ba0d1f464ae488f5ead812bc643984ac3dc662c78a02a978f7f
\ No newline at end of file
eeee178019c259a6fff85219490abf62f2694227cc2facf454d93e57c373833b
\ No newline at end of file
This diff is collapsed.
......@@ -191,6 +191,10 @@ changes.
You can always change the **Security policy project** by navigating to your project's
**Security & Compliance > Policies** and modifying the selected project.
NOTE:
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
to select Security Policy Project.
## Roadmap
See the [Category Direction page](https://about.gitlab.com/direction/protect/container_network_security/)
......
......@@ -168,6 +168,8 @@ The following table lists project permissions available for each role:
| Manage Project Operations | | | | ✓ | ✓ |
| Manage Terraform state | | | | ✓ | ✓ |
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
| Manage security policy **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create or assign security policy project **(ULTIMATE)** | | | | | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Reposition comments on images (posted by any user)|✓ (*10*) | ✓ (*10*) | ✓ (*10*) | ✓ | ✓ |
| Manage Error Tracking | | | | ✓ | ✓ |
......
/* global DocumentTouch */
import sortableConfig from 'ee_else_ce/sortable/sortable_config';
import sortableConfig from '~/sortable/sortable_config';
import { NO_DRAG_CLASS } from '../constants';
export default () => {
......
export default {
animation: 200,
forceFallback: true,
fallbackClass: 'is-dragging',
fallbackOnBody: true,
ghostClass: 'is-ghost',
fallbackTolerance: 1,
};
......@@ -5,9 +5,12 @@ module Projects
class PoliciesController < Projects::ApplicationController
include SecurityAndCompliancePermissions
before_action :authorize_security_orchestration_policies!
before_action :authorize_update_security_orchestration_policy_project!, only: [:assign]
before_action do
push_frontend_feature_flag(:security_orchestration_policies_configuration, project)
check_permissions!
check_feature_flag!
end
feature_category :security_orchestration
......@@ -32,8 +35,8 @@ module Projects
private
def check_permissions!
render_404 unless Feature.enabled?(:security_orchestration_policies_configuration, project) && can?(current_user, :security_orchestration_policies, project)
def check_feature_flag!
render_404 if Feature.disabled?(:security_orchestration_policies_configuration, project)
end
def policy_project_params
......
......@@ -7,7 +7,7 @@ module Mutations
graphql_name 'SecurityPolicyProjectAssign'
authorize :security_orchestration_policies
authorize :update_security_orchestration_policy_project
argument :project_path, GraphQL::ID_TYPE,
required: true,
......
......@@ -7,7 +7,7 @@ module Mutations
graphql_name 'SecurityPolicyProjectCreate'
authorize :security_orchestration_policies
authorize :update_security_orchestration_policy_project
argument :project_path, GraphQL::ID_TYPE,
required: true,
......
......@@ -195,6 +195,10 @@ module EE
end
end
def can_update_security_orchestration_policy_project?(project)
can?(current_user, :update_security_orchestration_policy_project, project)
end
def can_create_feedback?(project, feedback_type)
feedback = Vulnerabilities::Feedback.new(project: project, feedback_type: feedback_type)
can?(current_user, :create_vulnerability_feedback, feedback)
......
......@@ -37,11 +37,12 @@ module EE
@project&.namespace || @group
end
def license_message(signed_in: signed_in?, is_admin: current_user&.admin?, license: License.current)
def license_message(signed_in: signed_in?, is_admin: current_user&.admin?, license: License.current, force_notification: false)
::Gitlab::ExpiringSubscriptionMessage.new(
subscribable: license,
signed_in: signed_in,
is_admin: is_admin
is_admin: is_admin,
force_notification: force_notification
).message
end
......
......@@ -292,8 +292,12 @@ module EE
end
def with_slack_application_disabled
joins('LEFT JOIN services ON services.project_id = projects.id AND services.type = \'GitlabSlackApplicationService\' AND services.active IS true')
.where(services: { id: nil })
joins(<<~SQL)
LEFT JOIN #{::Integration.table_name} ON #{::Integration.table_name}.project_id = projects.id
AND #{::Integration.table_name}.type = 'GitlabSlackApplicationService'
AND #{::Integration.table_name}.active IS true
SQL
.where(integrations: { id: nil })
end
override :with_web_entity_associations
......
......@@ -195,6 +195,10 @@ module EE
enable :security_orchestration_policies
end
rule { security_orchestration_policies_enabled & can?(:owner_access) }.policy do
enable :update_security_orchestration_policy_project
end
rule { security_dashboard_enabled & can?(:developer_access) }.policy do
enable :read_security_resource
enable :read_vulnerability_scanner
......
......@@ -7,8 +7,11 @@
= s_('SecurityOrchestration|Security policy project')
%p
= project_select_tag('orchestration[policy_project_id]', class: 'hidden-filter-value', toggle_class: 'js-project-search js-project-filter js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
placeholder: _('Select project'), idAttribute: 'id', data: { order_by: 'last_activity_at', idattribute: 'id', simple_filter: true, allow_clear: true, include_groups: false, include_projects_in_subgroups: true, user_id: current_user.id }, value: @assigned_policy_id)
placeholder: _('Select project'), idAttribute: 'id', disabled: !can_update_security_orchestration_policy_project?(@project), data: { order_by: 'last_activity_at', idattribute: 'id', simple_filter: true, allow_clear: true, include_groups: false, include_projects_in_subgroups: true, user_id: current_user.id }, value: @assigned_policy_id)
.text-muted
= html_escape(s_('SecurityOrchestration|A security policy project can be used enforce policies for a given project, group, or instance. It allows you to specify security policies that are important to you and enforce them with every commit.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('More information'), help_page_path('user/project/clusters/protect/container_network_security/quick_start_guide'), target: '_blank'
= field.submit _('Save changes'), class: 'btn gl-button btn-success'
- if can_update_security_orchestration_policy_project?(@project)
= field.submit _('Save changes'), class: 'btn gl-button btn-success'
- else
= field.submit _('Save changes'), class: 'btn gl-button btn-success has-tooltip', disabled: true, title: _('Only owners can update Security Policy Project')
......@@ -157,7 +157,7 @@ module EE
def check_if_license_blocks_changes!
if ::License.block_changes?
message = license_message(signed_in: true, is_admin: (user && user.admin?))
message = license_message(signed_in: true, is_admin: (user && user.admin?), force_notification: true)
raise ::Gitlab::GitAccess::ForbiddenError, strip_tags(message)
end
end
......
......@@ -7,15 +7,16 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
include ActionView::Helpers::TextHelper
attr_reader :subscribable, :signed_in, :is_admin, :namespace
attr_reader :subscribable, :signed_in, :is_admin, :namespace, :force_notification
delegate :auto_renew, to: :subscribable
def initialize(subscribable:, signed_in:, is_admin:, namespace: nil)
def initialize(subscribable:, signed_in:, is_admin:, namespace: nil, force_notification: false)
@subscribable = subscribable
@signed_in = signed_in
@is_admin = is_admin
@namespace = namespace
@force_notification = force_notification
end
def message
......@@ -39,7 +40,7 @@ module Gitlab
end
def expired_subject
if show_downgrade_messaging?
if show_downgrade_messaging? && namespace
if auto_renew
_('Something went wrong with your automatic subscription renewal.')
else
......@@ -71,7 +72,7 @@ module Gitlab
def block_changes_message
return namespace_block_changes_message if namespace
_('You didn\'t renew your subscription so it was downgraded to the GitLab Core Plan.')
_('Please delete your current license if you want to downgrade to the free plan.')
end
def namespace_block_changes_message
......@@ -127,6 +128,7 @@ module Gitlab
def require_notification?
return false if expiring_auto_renew? || ::License.future_dated.present?
return true if force_notification && subscribable.block_changes?
auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal?
end
......@@ -154,7 +156,11 @@ module Gitlab
end
def show_downgrade_messaging?
subscribable.block_changes? && (self_managed? || plan_downgraded?)
if self_managed?
subscribable.block_changes?
else
subscribable.block_changes? && plan_downgraded?
end
end
def strong
......
......@@ -48,7 +48,7 @@ RSpec.describe "Admin views license" do
travel_to(reference_date) do
visit(admin_license_path)
expect(page).to have_content "You have 7 days to renew your subscription."
expect(page).to have_content 'You have 7 days to renew your subscription.'
expect(page).to have_link 'Renew subscription', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions"
end
end
......@@ -61,7 +61,7 @@ RSpec.describe "Admin views license" do
travel_to(reference_date) do
visit(admin_license_path)
expect(page).to have_content "You didn't renew your subscription so it was downgraded to the GitLab Core Plan"
expect(page).to have_content 'Please delete your current license if you want to downgrade to the free plan'
expect(page).to have_link 'Upgrade your plan', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions"
end
end
......
......@@ -3,14 +3,17 @@
require 'spec_helper'
RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
describe '#resolve' do
let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:project) { create(:project, namespace: owner.namespace) }
let_it_be(:policy_project) { create(:project) }
let_it_be(:policy_project_id) { GitlabSchema.id_from_object(policy_project) }
let(:current_user) { owner }
subject { mutation.resolve(project_path: project.full_path, security_policy_project_id: policy_project_id) }
context 'when feature is enabled and permission is set for user' do
......@@ -19,12 +22,26 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
stub_feature_flags(security_orchestration_policies_configuration: true)
end
it 'assigns the security policy project' do
result = subject
context 'when user is an owner of the project' do
it 'assigns the security policy project' do
result = subject
expect(result[:errors]).to be_empty
expect(project.security_orchestration_policy_configuration).not_to be_nil
expect(project.security_orchestration_policy_configuration.security_policy_management_project).to eq(policy_project)
end
end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
expect(result[:errors]).to be_empty
expect(project.security_orchestration_policy_configuration).not_to be_nil
expect(project.security_orchestration_policy_configuration.security_policy_management_project).to eq(policy_project)
it 'raises exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
......@@ -47,7 +64,7 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
end
end
context 'when permission is not enabled' do
context 'when feature is not licensed' do
before do
stub_licensed_features(security_orchestration_policies: false)
end
......
......@@ -2,27 +2,44 @@
require 'spec_helper'
RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
describe '#resolve' do
let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:project) { create(:project, namespace: owner.namespace) }
let(:current_user) { owner }
subject { mutation.resolve(project_path: project.full_path) }
context 'when feature is enabled and permission is set for user' do
before do
project.add_maintainer(user)
stub_licensed_features(security_orchestration_policies: true)
stub_feature_flags(security_orchestration_policies_configuration: true)
end
it 'returns project' do
result = subject
context 'when user is an owner of the project' do
let(:current_user) { owner }
it 'returns project' do
result = subject
expect(result[:errors]).to be_empty
expect(result[:project]).to eq(Project.last)
end
end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
expect(result[:errors]).to be_empty
expect(result[:project]).to eq(Project.last)
it 'raises exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
......@@ -37,7 +54,7 @@ RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do
end
end
context 'when permission is not enabled' do
context 'when feature is not licensed' do
before do
stub_licensed_features(security_orchestration_policies: false)
end
......
......@@ -188,7 +188,8 @@ RSpec.describe EE::SubscribableBannerHelper do
expect(Gitlab::ExpiringSubscriptionMessage).to receive(:new).with(
subscribable: license,
signed_in: true,
is_admin: false
is_admin: false,
force_notification: false
).and_return(message_mock)
expect(message_mock).to receive(:message)
......
......@@ -20,6 +20,24 @@ RSpec.describe ProjectsHelper do
end
end
describe '#can_update_security_orchestration_policy_project?' do
let(:owner) { project.owner }
before do
allow(helper).to receive(:current_user) { owner }
end
it 'returns false when user cannot update security orchestration policy project' do
allow(helper).to receive(:can?).with(owner, :update_security_orchestration_policy_project, project) { false }
expect(helper.can_update_security_orchestration_policy_project?(project)).to eq false
end
it 'returns true when user can update security orchestration policy project' do
allow(helper).to receive(:can?).with(owner, :update_security_orchestration_policy_project, project) { true }
expect(helper.can_update_security_orchestration_policy_project?(project)).to eq true
end
end
describe '#can_import_members?' do
let(:owner) { project.owner }
......
......@@ -12,12 +12,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
let(:subscribable) { double(:license) }
let(:namespace) { nil }
let(:force_notification) { false }
let(:message) do
described_class.new(
subscribable: subscribable,
signed_in: true,
is_admin: true,
namespace: namespace
namespace: namespace,
force_notification: force_notification
).message
end
......@@ -100,12 +102,12 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
end
it 'has a nice subject' do
expect(subject).to include('Your subscription has been downgraded.')
expect(subject).to include('Your subscription expired!')
end
context 'no namespace' do
it 'has an expiration blocking message' do
expect(subject).to include("You didn't renew your subscription so it was downgraded to the GitLab Core Plan")
expect(subject).to include('Please delete your current license if you want to downgrade to the free plan')
end
end
......@@ -316,7 +318,7 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
let(:grace_period_effective_from) { today.to_date - 40.days }
it 'has a nice subject' do
expect(subject).to include('Your subscription has been downgraded')
expect(subject).to include('Your subscription expired!')
end
end
......@@ -326,6 +328,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
it 'stops displaying' do
expect(subject).to be nil
end
context 'and force_notification is true' do
let(:force_notification) { true }
it 'returns a message' do
expect(subject).to include('Your subscription expired!')
end
end
end
context 'and not past the cutoff date' do
......
......@@ -748,13 +748,25 @@ RSpec.describe ProjectPolicy do
stub_licensed_features(security_orchestration_policies: true)
end
context 'with developer or higher role' do
where(role: %w[owner maintainer developer])
context 'with developer or maintainer role' do
where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:security_orchestration_policies) }
it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:security_orchestration_policies) }
it { is_expected.to be_allowed(:update_security_orchestration_policy_project) }
end
end
end
......
......@@ -3,8 +3,9 @@
require 'spec_helper'
RSpec.describe Projects::Security::PoliciesController, type: :request do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: owner.namespace) }
before do
project.add_developer(user)
......@@ -45,17 +46,32 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do
stub_licensed_features(security_orchestration_policies: true)
end
it 'assigns policy project to project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
context 'when user is not an owner of the project' do
it 'returns error message' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
expect(response).to redirect_to(project_security_policy_url(project))
expect(project.security_orchestration_policy_configuration.security_policy_management_project_id).to eq(policy_project.id)
expect(response).to have_gitlab_http_status(:not_found)
expect(response).not_to render_template('new')
end
end
it 'returns error message for invalid input' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: nil } }
context 'when user is an owner of the project' do
before do
login_as(owner)
end
it 'assigns policy project to project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
expect(flash[:alert]).to eq 'Policy project doesn\'t exist'
expect(response).to redirect_to(project_security_policy_url(project))
expect(project.security_orchestration_policy_configuration.security_policy_management_project_id).to eq(policy_project.id)
end
it 'returns error message for invalid input' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: nil } }
expect(flash[:alert]).to eq 'Policy project doesn\'t exist'
end
end
end
end
......@@ -10,9 +10,7 @@ module Gitlab
# TABLES_TO_BE_RENAMED = {
# 'old_name' => 'new_name'
# }.freeze
TABLES_TO_BE_RENAMED = {
'services' => 'integrations'
}.freeze
TABLES_TO_BE_RENAMED = {}.freeze
# Minimum PostgreSQL version requirement per documentation:
# https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements
......
......@@ -15,7 +15,12 @@ module Gitlab
context_for_args = worker_class.context_for_arguments(job['args'])
wrap_in_optional_context(context_for_args, &block)
wrap_in_optional_context(context_for_args) do
# This should be inside the context for the arguments so
# that we don't override the feature category on the worker
# with the one from the caller.
Gitlab::ApplicationContext.with_context(feature_category: worker_class.get_feature_category.to_s, &block)
end
end
end
end
......
......@@ -22972,6 +22972,9 @@ msgstr ""
msgid "Only include features new to your current subscription tier."
msgstr ""
msgid "Only owners can update Security Policy Project"
msgstr ""
msgid "Only policy:"
msgstr ""
......@@ -24463,6 +24466,9 @@ msgstr ""
msgid "Please create an index before enabling indexing"
msgstr ""
msgid "Please delete your current license if you want to downgrade to the free plan."
msgstr ""
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
......@@ -37421,9 +37427,6 @@ msgstr ""
msgid "You didn't renew your subscription for %{strong}%{namespace_name}%{strong_close} so it was downgraded to the free plan."
msgstr ""
msgid "You didn't renew your subscription so it was downgraded to the GitLab Core Plan."
msgstr ""
msgid "You do not have an active license"
msgstr ""
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl do
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl, schema: 20210421163509 do
let(:services_table) { table(:services) }
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
......
......@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL SELECT' do
let(:expected_sql) do
<<~SQL.strip
SELECT "services".* FROM "services" WHERE "services"."type" = 'AsanaService'
SELECT "integrations".* FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL
end
......@@ -25,7 +25,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL CREATE' do
let(:expected_sql) do
<<~SQL.strip
INSERT INTO "services" ("type") VALUES ('AsanaService')
INSERT INTO "integrations" ("type") VALUES ('AsanaService')
SQL
end
......@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL UPDATE' do
let(:expected_sql) do
<<~SQL.strip
UPDATE "services" SET "type" = 'AsanaService'
UPDATE "integrations" SET "type" = 'AsanaService'
SQL
end
......@@ -61,7 +61,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL DELETE' do
let(:expected_sql) do
<<~SQL.strip
DELETE FROM "services" WHERE "services"."type" = 'AsanaService'
DELETE FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL
end
......
......@@ -11,6 +11,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
include ApplicationWorker
feature_category :issue_tracking
def self.job_for_args(args)
jobs.find { |job| job['args'] == args }
end
......@@ -41,5 +43,39 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
expect(job1['meta.user']).to eq(user_per_job['job1'].username)
expect(job2['meta.user']).to eq(user_per_job['job2'].username)
end
context 'when the feature category is set in the context_proc' do
it 'takes the feature category from the worker, not the caller' do
TestWithContextWorker.bulk_perform_async_with_contexts(
%w(job1 job2),
arguments_proc: -> (name) { [name, 1, 2, 3] },
context_proc: -> (_) { { feature_category: 'code_review' } }
)
job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3])
job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3])
expect(job1['meta.feature_category']).to eq('issue_tracking')
expect(job2['meta.feature_category']).to eq('issue_tracking')
end
end
context 'when the feature category is already set in the surrounding block' do
it 'takes the feature category from the worker, not the caller' do
Gitlab::ApplicationContext.with_context(feature_category: 'authentication_and_authorization') do
TestWithContextWorker.bulk_perform_async_with_contexts(
%w(job1 job2),
arguments_proc: -> (name) { [name, 1, 2, 3] },
context_proc: -> (_) { {} }
)
end
job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3])
job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3])
expect(job1['meta.feature_category']).to eq('issue_tracking')
expect(job2['meta.feature_category']).to eq('issue_tracking')
end
end
end
end
This diff is collapsed.
......@@ -139,61 +139,40 @@ RSpec.describe Integration do
end
end
describe "Test Button" do
let(:integration) { build(:service, project: project) }
describe '#can_test?' do
subject { integration.can_test? }
context 'when project-level integration' do
let(:project) { create(:project) }
it { is_expected.to be true }
end
context 'when instance-level integration' do
Integration.available_integration_types.each do |type|
let(:integration) do
described_class.send(:integration_type_to_model, type).new(instance: true)
end
it { is_expected.to be false }
end
end
describe '#can_test?' do
subject { integration.can_test? }
context 'when group-level integration' do
Integration.available_integration_types.each do |type|
let(:integration) do
described_class.send(:integration_type_to_model, type).new(group_id: group.id)
end
context 'when integration is project-level' do
let(:integration) { build(:service, project: project) }
it { is_expected.to be false }
end
end
it { is_expected.to be true }
end
describe '#test' do
let(:data) { 'test' }
context 'when integration is not project-level' do
let(:integration) { build(:service, project: nil) }
context 'when repository is not empty' do
let(:project) { build(:project, :repository) }
it { is_expected.to be false }
end
end
it 'test runs execute' do
expect(integration).to receive(:execute).with(data)
describe '#test' do
let(:integration) { build(:service, project: project) }
let(:data) { 'test' }
integration.test(data)
end
end
it 'calls #execute' do
expect(integration).to receive(:execute).with(data)
context 'when repository is empty' do
let(:project) { build(:project) }
integration.test(data)
end
it 'test runs execute' do
expect(integration).to receive(:execute).with(data)
it 'returns a result' do
result = 'foo'
allow(integration).to receive(:execute).with(data).and_return(result)
integration.test(data)
end
end
expect(integration.test(data)).to eq(
success: true,
result: result
)
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