summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/stylesheets/pages/issuable.scss2
-rw-r--r--app/helpers/emails_helper.rb26
-rw-r--r--app/models/active_session.rb5
-rw-r--r--app/models/clusters/cluster.rb15
-rw-r--r--app/services/clusters/applications/ingress_modsecurity_usage_service.rb69
-rw-r--r--app/views/layouts/notify.html.haml2
-rw-r--r--app/views/layouts/notify.text.erb2
-rw-r--r--changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml5
-rw-r--r--changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml5
-rw-r--r--changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml5
-rw-r--r--config/routes/group.rb2
-rw-r--r--db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb17
-rw-r--r--db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb17
-rw-r--r--db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb23
-rw-r--r--db/schema.rb6
-rw-r--r--lib/gitlab/usage_data.rb7
-rw-r--r--locale/gitlab.pot12
-rw-r--r--rubocop/cop/put_group_routes_under_scope.rb43
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--spec/helpers/emails_helper_spec.rb22
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb18
-rw-r--r--spec/models/active_session_spec.rb29
-rw-r--r--spec/models/clusters/cluster_spec.rb34
-rw-r--r--spec/rubocop/cop/put_group_routes_under_scope_spec.rb48
-rw-r--r--spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb196
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/C++.gitignore0
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/Java.gitignore0
27 files changed, 586 insertions, 25 deletions
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 5617ab0af41..09b335f9ba2 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -883,7 +883,7 @@
.time-tracking-help-state {
background: $white-light;
- margin: 16px -20px 0;
+ margin: 16px -20px -20px;
padding: 16px 20px;
border-top: 1px solid $border-gray-light;
border-bottom: 1px solid $border-gray-light;
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index c244eba9e08..ba2330dfc9a 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -112,20 +112,20 @@ module EmailsHelper
end
end
- # "You are receiving this email because #{reason}"
+ # "You are receiving this email because #{reason} on #{gitlab_host}."
def notification_reason_text(reason)
- string = case reason
- when NotificationReason::OWN_ACTIVITY
- 'of your activity'
- when NotificationReason::ASSIGNED
- 'you have been assigned an item'
- when NotificationReason::MENTIONED
- 'you have been mentioned'
- else
- 'of your account'
- end
-
- "#{string} on #{Gitlab.config.gitlab.host}"
+ gitlab_host = Gitlab.config.gitlab.host
+
+ case reason
+ when NotificationReason::OWN_ACTIVITY
+ _("You're receiving this email because of your activity on %{host}.") % { host: gitlab_host }
+ when NotificationReason::ASSIGNED
+ _("You're receiving this email because you have been assigned an item on %{host}.") % { host: gitlab_host }
+ when NotificationReason::MENTIONED
+ _("You're receiving this email because you have been mentioned on %{host}.") % { host: gitlab_host }
+ else
+ _("You're receiving this email because of your account on %{host}.") % { host: gitlab_host }
+ end
end
def create_list_id_string(project, list_id_max_length = 255)
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index a6d5fc1137d..3ecc3137157 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -146,8 +146,9 @@ class ActiveSession
# remove sessions if there are more than ALLOWED_NUMBER_OF_ACTIVE_SESSIONS.
sessions = active_session_entries(session_ids, user.id, redis)
sessions.sort_by! {|session| session.updated_at }.reverse!
- sessions = sessions[ALLOWED_NUMBER_OF_ACTIVE_SESSIONS..-1].map { |session| session.session_id }
- destroy_sessions(redis, user, sessions)
+ sessions = sessions.drop(ALLOWED_NUMBER_OF_ACTIVE_SESSIONS)
+ sessions = sessions.map { |session| session.session_id }
+ destroy_sessions(redis, user, sessions) if sessions.any?
end
def self.cleaned_up_lookup_entries(redis, user)
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 62b2217a9af..315cdcffb42 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -271,6 +271,21 @@ module Clusters
kubernetes_namespaces.delete_all(:delete_all)
end
+ def clusterable
+ return unless cluster_type
+
+ case cluster_type
+ when 'project_type'
+ project
+ when 'group_type'
+ group
+ when 'instance_type'
+ instance
+ else
+ raise NotImplementedError
+ end
+ end
+
private
def unique_management_project_environment_scope
diff --git a/app/services/clusters/applications/ingress_modsecurity_usage_service.rb b/app/services/clusters/applications/ingress_modsecurity_usage_service.rb
new file mode 100644
index 00000000000..4aac8bb3cbd
--- /dev/null
+++ b/app/services/clusters/applications/ingress_modsecurity_usage_service.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+# rubocop: disable CodeReuse/ActiveRecord
+module Clusters
+ module Applications
+ ##
+ # This service measures usage of the Modsecurity Web Application Firewall across the entire
+ # instance's deployed environments.
+ #
+ # The default configuration is`AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE=DetectionOnly` so we
+ # measure non-default values via definition of either ci_variables or ci_pipeline_variables.
+ # Since both these values are encrypted, we must decrypt and count them in memory.
+ #
+ # NOTE: this service is an approximation as it does not yet take into account `environment_scope` or `ci_group_variables`.
+ ##
+ class IngressModsecurityUsageService
+ ADO_MODSEC_KEY = "AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE"
+
+ def initialize(blocking_count: 0, disabled_count: 0)
+ @blocking_count = blocking_count
+ @disabled_count = disabled_count
+ end
+
+ def execute
+ conditions = -> { merge(::Environment.available).merge(::Deployment.success).where(key: ADO_MODSEC_KEY) }
+
+ ci_pipeline_var_enabled =
+ ::Ci::PipelineVariable
+ .joins(pipeline: { environments: :last_visible_deployment })
+ .merge(conditions)
+ .order('deployments.environment_id, deployments.id DESC')
+
+ ci_var_enabled =
+ ::Ci::Variable
+ .joins(project: { environments: :last_visible_deployment })
+ .merge(conditions)
+ .merge(
+ # Give priority to pipeline variables by excluding from dataset
+ ::Ci::Variable.joins(project: :environments).where.not(
+ environments: { id: ci_pipeline_var_enabled.select('DISTINCT ON (deployments.environment_id) deployments.environment_id') }
+ )
+ ).select('DISTINCT ON (deployments.environment_id) ci_variables.*')
+
+ sum_modsec_config_counts(
+ ci_pipeline_var_enabled.select('DISTINCT ON (deployments.environment_id) ci_pipeline_variables.*')
+ )
+ sum_modsec_config_counts(ci_var_enabled)
+
+ {
+ ingress_modsecurity_blocking: @blocking_count,
+ ingress_modsecurity_disabled: @disabled_count
+ }
+ end
+
+ private
+
+ # These are encrypted so we must decrypt and count in memory
+ def sum_modsec_config_counts(dataset)
+ dataset.each do |var|
+ case var.value
+ when "On" then @blocking_count += 1
+ when "Off" then @disabled_count += 1
+ # `else` could be default or any unsupported user input
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index de487a94d40..e922b505be8 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -20,7 +20,7 @@
#{link_to _("View it on GitLab"), @target_url}.
%br
-# Don't link the host in the line below, one link in the email is easier to quickly click than two.
- = _("You're receiving this email because %{reason}.") % { reason: notification_reason_text(@reason) }
+ = notification_reason_text(@reason)
If you'd like to receive fewer emails, you can
- if @labels_url
adjust your #{link_to 'label subscriptions', @labels_url}.
diff --git a/app/views/layouts/notify.text.erb b/app/views/layouts/notify.text.erb
index 0ee30c2a6cf..49ad0b5abc5 100644
--- a/app/views/layouts/notify.text.erb
+++ b/app/views/layouts/notify.text.erb
@@ -11,7 +11,7 @@
<% end -%>
<% end -%>
-<%= "You're receiving this email because #{notification_reason_text(@reason)}." %>
+<%= notification_reason_text(@reason) %>
<%= render_if_exists 'layouts/mailer/additional_text' %>
<%= text_footer_message -%>
diff --git a/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml b/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml
new file mode 100644
index 00000000000..3c2b2ad7e1d
--- /dev/null
+++ b/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml
@@ -0,0 +1,5 @@
+---
+title: Add modsecurity deployment counts to usage ping
+merge_request: 20196
+author:
+type: added
diff --git a/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml b/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml
new file mode 100644
index 00000000000..11d2cfa4d4b
--- /dev/null
+++ b/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml
@@ -0,0 +1,5 @@
+---
+title: Validate unique environment scope for instance clusters
+merge_request: 20886
+author:
+type: fixed
diff --git a/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml b/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml
new file mode 100644
index 00000000000..0abf69d9d4c
--- /dev/null
+++ b/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml
@@ -0,0 +1,5 @@
+---
+title: Remove extra spacing below sidebar time tracking info
+merge_request: 20657
+author: Lee Tickett
+type: other
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 437c80b8c92..30671d4e0a1 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
+# rubocop: disable Cop/PutGroupRoutesUnderScope
resources :groups, only: [:index, :new, :create] do
post :preview_markdown
end
+# rubocop: enable Cop/PutGroupRoutesUnderScope
constraints(::Constraints::GroupUrlConstrainer.new) do
scope(path: 'groups/*id',
diff --git a/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb b/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb
new file mode 100644
index 00000000000..169ecf5ea41
--- /dev/null
+++ b/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToModSecCiVariables < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_variables, :project_id, where: "key = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'"
+ end
+
+ def down
+ remove_concurrent_index :ci_variables, :project_id
+ end
+end
diff --git a/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb b/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb
new file mode 100644
index 00000000000..6b13f565a11
--- /dev/null
+++ b/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToModSecCiPipelineVariables < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_pipeline_variables, :pipeline_id, where: "key = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'"
+ end
+
+ def down
+ remove_concurrent_index :ci_pipeline_variables, :pipeline_id
+ end
+end
diff --git a/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb b/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb
new file mode 100644
index 00000000000..bda461af7bc
--- /dev/null
+++ b/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class DropOperationsFeatureFlagsClientsToken < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ # Ignored in 12.5 - https://gitlab.com/gitlab-org/gitlab/merge_requests/18923
+ remove_column :operations_feature_flags_clients, :token
+ end
+
+ def down
+ unless column_exists?(:operations_feature_flags_clients, :token)
+ add_column :operations_feature_flags_clients, :token, :string # rubocop:disable Migration/AddLimitToStringColumns
+ end
+
+ add_concurrent_index :operations_feature_flags_clients, [:project_id, :token], unique: true,
+ name: 'index_operations_feature_flags_clients_on_project_id_and_token'
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 253d82e790b..3bdf41e609f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_11_25_140458) do
+ActiveRecord::Schema.define(version: 2019_12_02_031812) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -821,6 +821,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.integer "pipeline_id", null: false
t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true
+ t.index ["pipeline_id"], name: "index_ci_pipeline_variables_on_pipeline_id", where: "((key)::text = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'::text)"
end
create_table "ci_pipelines", id: :serial, force: :cascade do |t|
@@ -979,6 +980,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.boolean "masked", default: false, null: false
t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true
+ t.index ["project_id"], name: "index_ci_variables_on_project_id", where: "((key)::text = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'::text)"
end
create_table "cluster_groups", id: :serial, force: :cascade do |t|
@@ -2774,9 +2776,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
create_table "operations_feature_flags_clients", force: :cascade do |t|
t.integer "project_id", null: false
- t.string "token"
t.string "token_encrypted"
- t.index ["project_id", "token"], name: "index_operations_feature_flags_clients_on_project_id_and_token", unique: true
t.index ["project_id", "token_encrypted"], name: "index_feature_flags_clients_on_project_id_and_token_encrypted", unique: true
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index a0ff34c76e2..7268e11ff23 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -108,7 +108,8 @@ module Gitlab
services_usage,
approximate_counts,
usage_counters,
- user_preferences_usage
+ user_preferences_usage,
+ ingress_modsecurity_usage
)
}
end
@@ -170,6 +171,10 @@ module Gitlab
}
end
+ def ingress_modsecurity_usage
+ ::Clusters::Applications::IngressModsecurityUsageService.new.execute
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def services_usage
types = {
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9926fd8994e..8d28cf86dd6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20306,15 +20306,21 @@ msgstr ""
msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
msgstr ""
-msgid "You're receiving this email because %{reason}."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "You're receiving this email because of your activity on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because you have been assigned an item on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because you have been mentioned on %{host}."
+msgstr ""
+
msgid "You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication."
msgstr ""
diff --git a/rubocop/cop/put_group_routes_under_scope.rb b/rubocop/cop/put_group_routes_under_scope.rb
new file mode 100644
index 00000000000..bcdde01fdb5
--- /dev/null
+++ b/rubocop/cop/put_group_routes_under_scope.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ # Checks for a group routes outside '/-/' scope.
+ # For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572
+ class PutGroupRoutesUnderScope < RuboCop::Cop::Cop
+ MSG = 'Put new group routes under /-/ scope'
+
+ def_node_matcher :dash_scope?, <<~PATTERN
+ (:send nil? :scope (hash <(pair (sym :path)(str "groups/*group_id/-")) ...>))
+ PATTERN
+
+ def on_send(node)
+ return unless in_group_routes?(node)
+ return unless resource?(node)
+ return unless outside_scope?(node)
+
+ add_offense(node)
+ end
+
+ def outside_scope?(node)
+ node.each_ancestor(:block).none? do |parent|
+ dash_scope?(parent.to_a.first)
+ end
+ end
+
+ def in_group_routes?(node)
+ path = node.location.expression.source_buffer.name
+ dirname = File.dirname(path)
+ filename = File.basename(path)
+
+ dirname.end_with?('config/routes') &&
+ filename.end_with?('group.rb')
+ end
+
+ def resource?(node)
+ node.method_name == :resource ||
+ node.method_name == :resources
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index 9c9948e2a61..1465c73d570 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -15,6 +15,7 @@ require_relative 'cop/avoid_route_redirect_leading_slash'
require_relative 'cop/line_break_around_conditional_block'
require_relative 'cop/prefer_class_methods_over_module'
require_relative 'cop/put_project_routes_under_scope'
+require_relative 'cop/put_group_routes_under_scope'
require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index 931b7008173..0ff9080ef94 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -74,6 +74,28 @@ describe EmailsHelper do
end
end
+ describe 'notification_reason_text' do
+ subject { helper.notification_reason_text(reason_code) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:reason_code, :reason_text) do
+ NotificationReason::OWN_ACTIVITY | ' of your activity '
+ NotificationReason::ASSIGNED | ' you have been assigned an item '
+ NotificationReason::MENTIONED | ' you have been mentioned '
+ "" | ' of your account '
+ nil | ' of your account '
+ end
+
+ with_them do
+ it { is_expected.to start_with "You're receiving this email because" }
+
+ it { is_expected.to include reason_text }
+
+ it { is_expected.to end_with "on #{Gitlab.config.gitlab.host}." }
+ end
+ end
+
describe 'sanitize_name' do
context 'when name contains a valid URL string' do
it 'returns name with `.` replaced with `_` to prevent mail clients from auto-linking URLs' do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 713ddca6c2b..7a5d6f5ad5a 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -297,6 +297,24 @@ describe Gitlab::UsageData do
end
end
+ describe '#ingress_modsecurity_usage' do
+ subject { described_class.ingress_modsecurity_usage }
+
+ it 'gathers variable data' do
+ allow_any_instance_of(
+ ::Clusters::Applications::IngressModsecurityUsageService
+ ).to receive(:execute).and_return(
+ {
+ ingress_modsecurity_blocking: 1,
+ ingress_modsecurity_disabled: 2
+ }
+ )
+
+ expect(subject[:ingress_modsecurity_blocking]).to eq(1)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(2)
+ end
+ end
+
describe '#license_usage_data' do
subject { described_class.license_usage_data }
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index c26675e75bf..072d0fa86e5 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -329,6 +329,35 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
)
end
end
+
+ context 'when the number of active sessions is lower than the limit' do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ ((max_number_of_sessions_plus_two - 4)..max_number_of_sessions_plus_two).each do |number|
+ redis.del("session:user:gitlab:#{user.id}:#{number}")
+ end
+ end
+ end
+
+ it 'does not remove active session entries, but removes lookup entries' do
+ lookup_entries_before_cleanup = Gitlab::Redis::SharedState.with do |redis|
+ redis.smembers("session:lookup:user:gitlab:#{user.id}")
+ end
+
+ sessions_before_cleanup = Gitlab::Redis::SharedState.with do |redis|
+ redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
+ end
+
+ ActiveSession.cleanup(user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}")
+ sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
+ expect(sessions.count).to eq(sessions_before_cleanup.count)
+ expect(lookup_entries.count).to be < lookup_entries_before_cleanup.count
+ end
+ end
+ end
end
end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 7c419a195cd..807214dcc14 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -976,4 +976,38 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
expect(cluster.kubernetes_namespaces).to be_empty
end
end
+
+ describe '#clusterable' do
+ subject { cluster.clusterable }
+
+ context 'project type' do
+ let(:cluster) { create(:cluster, :project) }
+
+ it { is_expected.to eq(cluster.project) }
+ end
+
+ context 'group type' do
+ let(:cluster) { create(:cluster, :group) }
+
+ it { is_expected.to eq(cluster.group) }
+ end
+
+ context 'instance type' do
+ let(:cluster) { create(:cluster, :instance) }
+
+ it { is_expected.to be_a(Clusters::Instance) }
+ end
+
+ context 'unknown type' do
+ let(:cluster) { create(:cluster, :project) }
+
+ before do
+ allow(cluster).to receive(:cluster_type).and_return('unknown_type')
+ end
+
+ it 'raises NotImplementedError' do
+ expect { subject }.to raise_error(NotImplementedError)
+ end
+ end
+ end
end
diff --git a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
new file mode 100644
index 00000000000..fc4d0015dde
--- /dev/null
+++ b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require_relative '../../../rubocop/cop/put_group_routes_under_scope'
+
+describe RuboCop::Cop::PutGroupRoutesUnderScope do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ before do
+ allow(cop).to receive(:in_group_routes?).and_return(true)
+ end
+
+ it 'registers an offense when route is outside scope' do
+ expect_offense(<<~PATTERN.strip_indent)
+ scope(path: 'groups/*group_id/-', module: :groups) do
+ resource :issues
+ end
+
+ resource :notes
+ ^^^^^^^^^^^^^^^ Put new group routes under /-/ scope
+ PATTERN
+ end
+
+ it 'does not register an offense when resource inside the scope' do
+ expect_no_offenses(<<~PATTERN.strip_indent)
+ scope(path: 'groups/*group_id/-', module: :groups) do
+ resource :issues
+ resource :notes
+ end
+ PATTERN
+ end
+
+ it 'does not register an offense when resource is deep inside the scope' do
+ expect_no_offenses(<<~PATTERN.strip_indent)
+ scope(path: 'groups/*group_id/-', module: :groups) do
+ resource :issues
+ resource :projects do
+ resource :issues do
+ resource :notes
+ end
+ end
+ end
+ PATTERN
+ end
+end
diff --git a/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb b/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb
new file mode 100644
index 00000000000..d456284f76a
--- /dev/null
+++ b/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb
@@ -0,0 +1,196 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::IngressModsecurityUsageService do
+ describe '#execute' do
+ ADO_MODSEC_KEY = Clusters::Applications::IngressModsecurityUsageService::ADO_MODSEC_KEY
+
+ let(:project_with_ci_var) { create(:environment).project }
+ let(:project_with_pipeline_var) { create(:environment).project }
+
+ subject { described_class.new.execute }
+
+ context 'with multiple projects' do
+ let(:pipeline1) { create(:ci_pipeline, :with_job, project: project_with_pipeline_var) }
+ let(:pipeline2) { create(:ci_pipeline, :with_job, project: project_with_ci_var) }
+
+ let!(:deployment_with_pipeline_var) do
+ create(
+ :deployment,
+ :success,
+ environment: project_with_pipeline_var.environments.first,
+ project: project_with_pipeline_var,
+ deployable: pipeline1.builds.last
+ )
+ end
+ let!(:deployment_with_project_var) do
+ create(
+ :deployment,
+ :success,
+ environment: project_with_ci_var.environments.first,
+ project: project_with_ci_var,
+ deployable: pipeline2.builds.last
+ )
+ end
+
+ context 'mixed data' do
+ let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, key: ADO_MODSEC_KEY, value: "On") }
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, key: ADO_MODSEC_KEY, value: "Off") }
+
+ it 'gathers variable data' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(1)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(1)
+ end
+ end
+
+ context 'blocking' do
+ let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "On" } }
+
+ let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) }
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, **modsec_values) }
+
+ it 'gathers variable data' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(2)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(0)
+ end
+ end
+
+ context 'disabled' do
+ let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "Off" } }
+
+ let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) }
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, **modsec_values) }
+
+ it 'gathers variable data' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(0)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(2)
+ end
+ end
+ end
+
+ context 'when set as both ci and pipeline variables' do
+ let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "Off" } }
+
+ let(:pipeline) { create(:ci_pipeline, :with_job, project: project_with_ci_var) }
+ let!(:deployment) do
+ create(
+ :deployment,
+ :success,
+ environment: project_with_ci_var.environments.first,
+ project: project_with_ci_var,
+ deployable: pipeline.builds.last
+ )
+ end
+
+ let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) }
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline, **modsec_values) }
+
+ it 'wont double-count projects' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(0)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(1)
+ end
+
+ it 'gives precedence to pipeline variable' do
+ pipeline_variable.update(value: "On")
+
+ expect(subject[:ingress_modsecurity_blocking]).to eq(1)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(0)
+ end
+ end
+
+ context 'when a project has multiple environments' do
+ let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "On" } }
+
+ let!(:env1) { project_with_pipeline_var.environments.first }
+ let!(:env2) { create(:environment, project: project_with_pipeline_var) }
+
+ let!(:pipeline_with_2_deployments) do
+ create(:ci_pipeline, :with_job, project: project_with_ci_var).tap do |pip|
+ pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var)
+ end
+ end
+
+ let!(:deployment1) do
+ create(
+ :deployment,
+ :success,
+ environment: env1,
+ project: project_with_pipeline_var,
+ deployable: pipeline_with_2_deployments.builds.last
+ )
+ end
+ let!(:deployment2) do
+ create(
+ :deployment,
+ :success,
+ environment: env2,
+ project: project_with_pipeline_var,
+ deployable: pipeline_with_2_deployments.builds.last
+ )
+ end
+
+ context 'when set as ci variable' do
+ let!(:ci_variable) { create(:ci_variable, project: project_with_pipeline_var, **modsec_values) }
+
+ it 'gathers variable data' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(2)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(0)
+ end
+ end
+
+ context 'when set as pipeline variable' do
+ let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_with_2_deployments, **modsec_values) }
+
+ it 'gathers variable data' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(2)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(0)
+ end
+ end
+ end
+
+ context 'when an environment has multiple deployments' do
+ let!(:env) { project_with_pipeline_var.environments.first }
+
+ let!(:pipeline_first) do
+ create(:ci_pipeline, :with_job, project: project_with_pipeline_var).tap do |pip|
+ pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var)
+ end
+ end
+ let!(:pipeline_last) do
+ create(:ci_pipeline, :with_job, project: project_with_pipeline_var).tap do |pip|
+ pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var)
+ end
+ end
+
+ let!(:deployment_first) do
+ create(
+ :deployment,
+ :success,
+ environment: env,
+ project: project_with_pipeline_var,
+ deployable: pipeline_first.builds.last
+ )
+ end
+ let!(:deployment_last) do
+ create(
+ :deployment,
+ :success,
+ environment: env,
+ project: project_with_pipeline_var,
+ deployable: pipeline_last.builds.last
+ )
+ end
+
+ context 'when set as pipeline variable' do
+ let!(:first_pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_first, key: ADO_MODSEC_KEY, value: "On") }
+ let!(:last_pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_last, key: ADO_MODSEC_KEY, value: "Off") }
+
+ it 'gives precedence to latest deployment' do
+ expect(subject[:ingress_modsecurity_blocking]).to eq(0)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(1)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
index 259148fa18f..259148fa18f 100644..100755
--- a/vendor/gitignore/C++.gitignore
+++ b/vendor/gitignore/C++.gitignore
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index a1c2a238a96..a1c2a238a96 100644..100755
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore