diff options
156 files changed, 1633 insertions, 686 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 41714eefa97..3898206e3b5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -262,25 +262,6 @@ Naming/HeredocDelimiterNaming: Naming/RescuedExceptionsVariableName: Enabled: false -# Offense count: 6 -# Cop supports --auto-correct. -Performance/InefficientHashSearch: - Exclude: - - 'app/controllers/concerns/sessionless_authentication.rb' - - 'app/models/note.rb' - - 'app/models/user_preference.rb' - - 'ee/app/models/ee/project.rb' - - 'lib/gitlab/import_export/members_mapper.rb' - - 'qa/spec/spec_helper.rb' - -# Offense count: 3 -# Cop supports --auto-correct. -Performance/ReverseEach: - Exclude: - - 'app/models/commit.rb' - - 'db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb' - - 'lib/gitlab/profiler.rb' - # Offense count: 7081 # Configuration parameters: Prefixes. # Prefixes: when, with, without diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 54e2589c707..7dd75d03ab9 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import { pluralize } from './lib/utils/text_utility'; +import { n__ } from '~/locale'; import { localTimeAgo } from './lib/utils/datetime_utility'; import Pager from './pager'; import axios from './lib/utils/axios_utils'; @@ -90,9 +90,10 @@ export default class CommitsList { .first() .find('li.commit').length, ); + $commitsHeadersLast .find('span.commits-count') - .text(`${commitsCount} ${pluralize('commit', commitsCount)}`); + .text(n__('%d commit', '%d commits', commitsCount)); } localTimeAgo($processedData.find('.js-timeago')); diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js index a0426301a0a..babbfe93082 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js @@ -8,22 +8,26 @@ export default class CycleAnalyticsService { } fetchCycleAnalyticsData(options = { startDate: 30 }) { + const { startDate, projectIds } = options; + return this.axios .get('', { params: { - 'cycle_analytics[start_date]': options.startDate, + 'cycle_analytics[start_date]': startDate, + 'cycle_analytics[project_ids]': projectIds, }, }) .then(x => x.data); } fetchStageData(options) { - const { stage, startDate } = options; + const { stage, startDate, projectIds } = options; return this.axios .get(`events/${stage.name}.json`, { params: { 'cycle_analytics[start_date]': startDate, + 'cycle_analytics[project_ids]': projectIds, }, }) .then(x => x.data); diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue index af5550aec3b..7ede7a4f430 100644 --- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue +++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue @@ -1,6 +1,7 @@ <script> +import { n__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; -import { pluralize, truncate } from '~/lib/utils/text_utility'; +import { truncate } from '~/lib/utils/text_utility'; import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; import { GlTooltipDirective } from '@gitlab/ui'; import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants'; @@ -42,7 +43,7 @@ export default { return ''; } - return pluralize(`${this.moreCount} more comment`, this.moreCount); + return n__('%d more comment', '%d more comments', this.moreCount); }, }, methods: { diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 062d21ed247..a4715789337 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -2,8 +2,7 @@ import $ from 'jquery'; import _ from 'underscore'; import timeago from 'timeago.js'; import dateFormat from 'dateformat'; -import { pluralize } from './text_utility'; -import { languageCode, s__, __ } from '../../locale'; +import { languageCode, s__, __, n__ } from '../../locale'; window.timeago = timeago; @@ -231,14 +230,10 @@ export const timeIntervalInWords = intervalInSeconds => { const secondsInteger = parseInt(intervalInSeconds, 10); const minutes = Math.floor(secondsInteger / 60); const seconds = secondsInteger - minutes * 60; - let text = ''; - - if (minutes >= 1) { - text = `${minutes} ${pluralize('minute', minutes)} ${seconds} ${pluralize('second', seconds)}`; - } else { - text = `${seconds} ${pluralize('second', seconds)}`; - } - return text; + const secondsText = n__('%d second', '%d seconds', seconds); + return minutes >= 1 + ? [n__('%d minute', '%d minutes', minutes), secondsText].join(' ') + : secondsText; }; export const dateInWords = (date, abbreviated = false, hideYear = false) => { diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index d38f59b5861..d13fbeb5fc7 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -29,14 +29,6 @@ export const humanize = string => string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); /** - * Adds an 's' to the end of the string when count is bigger than 0 - * @param {String} str - * @param {Number} count - * @returns {String} - */ -export const pluralize = (str, count) => str + (count > 1 || count === 0 ? 's' : ''); - -/** * Replaces underscores with dashes * @param {*} str * @returns {String} diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue index 015c1527500..f05db8376a4 100644 --- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue +++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue @@ -42,6 +42,12 @@ export default { keys: ['feature', 'request'], }, { + metric: 'rugged', + header: 'Rugged calls', + details: 'details', + keys: ['feature', 'args'], + }, + { metric: 'redis', header: 'Redis calls', details: 'details', diff --git a/app/assets/stylesheets/framework/tooltips.scss b/app/assets/stylesheets/framework/tooltips.scss index 98f28987a82..edc2fb532c8 100644 --- a/app/assets/stylesheets/framework/tooltips.scss +++ b/app/assets/stylesheets/framework/tooltips.scss @@ -1,7 +1,6 @@ .tooltip-inner { - font-size: $tooltip-font-size; + font-size: $gl-font-size-small; border-radius: $border-radius-default; - line-height: 16px; + line-height: $gl-line-height; font-weight: $gl-font-weight-normal; - padding: 8px; } diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss index ea96381a098..604b48e11ab 100644 --- a/app/assets/stylesheets/framework/variables_overrides.scss +++ b/app/assets/stylesheets/framework/variables_overrides.scss @@ -48,3 +48,7 @@ $spacers: ( 9: ($spacer * 8) ); $pagination-color: $gl-text-color; +$tooltip-padding-y: 0.5rem; +$tooltip-padding-x: 0.75rem; +$tooltip-arrow-height: 0.5rem; +$tooltip-arrow-width: 1rem; diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index e9ec8876688..99411641874 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -106,6 +106,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :lets_encrypt_notification_email, :lets_encrypt_terms_of_service_accepted, :domain_blacklist_file, + :raw_blob_request_limit, disabled_oauth_sign_in_sources: [], import_sources: [], repository_storages: [], diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index 590eefc6dab..4304b8565ce 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -13,7 +13,7 @@ module SessionlessAuthentication end def sessionless_user? - current_user && !session.keys.include?('warden.user.user.key') + current_user && !session.key?('warden.user.user.key') end def sessionless_sign_in(user) diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index aa4aa0fbdac..ebb50fc8b10 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -10,7 +10,7 @@ class Import::GithubController < Import::BaseController rescue_from Octokit::Unauthorized, with: :provider_unauthorized def new - if github_import_configured? && logged_in_with_provider? + if !ci_cd_only? && github_import_configured? && logged_in_with_provider? go_to_provider_for_permissions elsif session[access_token_key] redirect_to status_import_url @@ -169,11 +169,15 @@ class Import::GithubController < Import::BaseController # rubocop: enable CodeReuse/ActiveRecord def provider_auth - if session[access_token_key].blank? + if !ci_cd_only? && session[access_token_key].blank? go_to_provider_for_permissions end end + def ci_cd_only? + %w[1 true].include?(params[:ci_cd_only]) + end + def client_options {} end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 42ae5b0ef3c..3254229d9cb 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -8,10 +8,30 @@ class Projects::RawController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_download_code! + before_action :show_rate_limit, only: [:show] def show @blob = @repository.blob_at(@commit.id, @path) send_blob(@repository, @blob, inline: (params[:inline] != 'false')) end + + private + + def show_rate_limit + limiter = ::Gitlab::ActionRateLimiter.new(action: :show_raw_controller) + + return unless limiter.throttled?([@project, @commit, @path], raw_blob_request_limit) + + limiter.log_request(request, :raw_blob_request_limit, current_user) + + flash[:alert] = _('You cannot access the raw file. Please wait a minute.') + redirect_to project_blob_path(@project, File.join(@ref, @path)) + end + + def raw_blob_request_limit + Gitlab::CurrentSettings + .current_application_settings + .raw_blob_request_limit + end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 4bf9b708401..3847a35fbab 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -177,6 +177,7 @@ module ApplicationSettingsHelper :domain_blacklist_enabled, :domain_blacklist_raw, :domain_whitelist_raw, + :outbound_local_requests_whitelist_raw, :dsa_key_restriction, :ecdsa_key_restriction, :ed25519_key_restriction, diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 5906ddabee4..81ff359556d 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -52,7 +52,7 @@ module AvatarsHelper user: commit_or_event.author, user_name: commit_or_event.author_name, user_email: commit_or_event.author_email, - css_class: 'd-none d-sm-inline' + css_class: 'd-none d-sm-inline-block' })) end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index db4f29cd996..2ed016beea4 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -4,11 +4,10 @@ module LabelsHelper extend self include ActionView::Helpers::TagHelper - def show_label_issuables_link?(label, issuables_type, current_user: nil, project: nil) + def show_label_issuables_link?(label, issuables_type, current_user: nil) return true unless label.project_label? - return true unless project - project.feature_available?(issuables_type, current_user) + label.project.feature_available?(issuables_type, current_user) end # Link to a Label diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 8e558487c1c..a769a8f07fd 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -41,6 +41,11 @@ class ApplicationSetting < ApplicationRecord validates :uuid, presence: true + validates :outbound_local_requests_whitelist, + length: { maximum: 1_000, message: N_('is too long (maximum is 1000 entries)') } + + validates :outbound_local_requests_whitelist, qualified_domain_array: true, allow_blank: true + validates :session_expire_delay, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index df4caed175d..1e612bd0e78 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -2,6 +2,7 @@ module ApplicationSettingImplementation extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace | # or @@ -96,7 +97,9 @@ module ApplicationSettingImplementation diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, commit_email_hostname: default_commit_email_hostname, protected_ci_variables: false, - local_markdown_version: 0 + local_markdown_version: 0, + outbound_local_requests_whitelist: [], + raw_blob_request_limit: 300 } end @@ -131,31 +134,52 @@ module ApplicationSettingImplementation end def domain_whitelist_raw - self.domain_whitelist&.join("\n") + array_to_string(self.domain_whitelist) end def domain_blacklist_raw - self.domain_blacklist&.join("\n") + array_to_string(self.domain_blacklist) end def domain_whitelist_raw=(values) - self.domain_whitelist = [] - self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR) - self.domain_whitelist.reject! { |d| d.empty? } - self.domain_whitelist + self.domain_whitelist = domain_strings_to_array(values) end def domain_blacklist_raw=(values) - self.domain_blacklist = [] - self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) - self.domain_blacklist.reject! { |d| d.empty? } - self.domain_blacklist + self.domain_blacklist = domain_strings_to_array(values) end def domain_blacklist_file=(file) self.domain_blacklist_raw = file.read end + def outbound_local_requests_whitelist_raw + array_to_string(self.outbound_local_requests_whitelist) + end + + def outbound_local_requests_whitelist_raw=(values) + self.outbound_local_requests_whitelist = domain_strings_to_array(values) + end + + def outbound_local_requests_whitelist_arrays + strong_memoize(:outbound_local_requests_whitelist_arrays) do + ip_whitelist = [] + domain_whitelist = [] + + self.outbound_local_requests_whitelist.each do |str| + ip_obj = Gitlab::Utils.string_to_ip_object(str) + + if ip_obj + ip_whitelist << ip_obj + else + domain_whitelist << str + end + end + + [ip_whitelist, domain_whitelist] + end + end + def repository_storages Array(read_attribute(:repository_storages)) end @@ -255,6 +279,17 @@ module ApplicationSettingImplementation private + def array_to_string(arr) + arr&.join("\n") + end + + def domain_strings_to_array(values) + values + .split(DOMAIN_LIST_SEPARATOR) + .reject(&:empty?) + .uniq + end + def ensure_uuid! return if uuid? diff --git a/app/models/commit.rb b/app/models/commit.rb index be37fa2e76f..0889ce7e287 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -346,7 +346,7 @@ class Commit if commits_in_merge_request.present? message_body << "" - commits_in_merge_request.reverse.each do |commit_in_merge| + commits_in_merge_request.reverse_each do |commit_in_merge| message_body << "#{commit_in_merge.short_id} #{commit_in_merge.title}" end end diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index 39e12ac2b06..facd81dde80 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -86,4 +86,9 @@ class ContainerRepository < ApplicationRecord def self.build_root_repository(project) self.new(project: project, name: '') end + + def self.find_by_path!(path) + self.find_by!(project: path.repository_project, + name: path.repository_name) + end end diff --git a/app/models/label.rb b/app/models/label.rb index b83e0862bab..dd403562bfa 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -33,7 +33,7 @@ class Label < ApplicationRecord default_scope { order(title: :asc) } - scope :templates, -> { where(template: true) } + scope :templates, -> { where(template: true, type: [Label.name, nil]) } scope :with_title, ->(title) { where(title: title) } scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) } scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) } diff --git a/app/models/note.rb b/app/models/note.rb index 5c31cff9816..3f182c1f099 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -292,7 +292,7 @@ class Note < ApplicationRecord end def special_role=(role) - raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.values.include?(role) + raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.value?(role) @special_role = role end diff --git a/app/models/project.rb b/app/models/project.rb index 8030c645e2e..0020e423628 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1862,16 +1862,24 @@ class Project < ApplicationRecord end def append_or_update_attribute(name, value) - old_values = public_send(name.to_s) # rubocop:disable GitlabSecurity/PublicSend + if Project.reflect_on_association(name).try(:macro) == :has_many + # if this is 1-to-N relation, update the parent object + value.each do |item| + item.update!( + Project.reflect_on_association(name).foreign_key => id) + end + + # force to drop relation cache + public_send(name).reset # rubocop:disable GitlabSecurity/PublicSend - if Project.reflect_on_association(name).try(:macro) == :has_many && old_values.any? - update_attribute(name, old_values + value) + # succeeded + true else + # if this is another relation or attribute, update just object update_attribute(name, value) end - - rescue ActiveRecord::RecordNotSaved => e - handle_update_attribute_error(e, value) + rescue ActiveRecord::RecordInvalid => e + raise e, "Failed to set #{name}: #{e.message}" end # Tries to set repository as read_only, checking for existing Git transfers in progress beforehand @@ -2260,18 +2268,6 @@ class Project < ApplicationRecord ContainerRepository.build_root_repository(self).has_tags? end - def handle_update_attribute_error(ex, value) - if ex.message.start_with?('Failed to replace') - if value.respond_to?(:each) - invalid = value.detect(&:invalid?) - - raise ex, ([ex.message] + invalid.errors.full_messages).join(' ') if invalid - end - end - - raise ex - end - def fetch_branch_allows_collaboration(user, branch_name = nil) return false unless user diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index f39f54f0434..e11d0c48b4b 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true class ProjectAutoDevops < ApplicationRecord - include IgnorableColumn - - ignore_column :domain - belongs_to :project, inverse_of: :auto_devops enum deploy_strategy: { diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index f1326f4c8cb..b236250c24e 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -26,7 +26,7 @@ class UserPreference < ApplicationRecord def set_notes_filter(filter_id, issuable) # No need to update the column if the value is already set. - if filter_id && NOTES_FILTERS.values.include?(filter_id) + if filter_id && NOTES_FILTERS.value?(filter_id) field = notes_filter_field_for(issuable) self[field] = filter_id diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml index d16304ed338..e58bb526c11 100644 --- a/app/views/admin/application_settings/_outbound.html.haml +++ b/app/views/admin/application_settings/_outbound.html.haml @@ -9,6 +9,13 @@ Allow requests to the local network from hooks and services .form-group + = f.label :outbound_local_requests_whitelist_raw, class: 'label-bold' do + = _('Whitelist to allow requests to the local network from hooks and services') + = f.text_area :outbound_local_requests_whitelist_raw, placeholder: "example.com, 192.168.1.1", class: 'form-control', rows: 8 + %span.form-text.text-muted + = _('Requests to these domain(s)/address(es) on the local network will be allowed when local requests from hooks and services are disabled. IP ranges such as 1:0:0:0:0:0:0:0/124 or 127.0.0.0/28 are supported. Domain wildcards are not supported currently. Use comma, semicolon, or newline to separate multiple entries. The whitelist can hold a maximum of 4000 entries. Domains should use IDNA encoding. Ex: domain.com, 192.168.1.1, 127.0.0.0/28.') + + .form-group .form-check = f.check_box :dns_rebinding_protection_enabled, class: 'form-check-input' = f.label :dns_rebinding_protection_enabled, class: 'form-check-label' do diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml index 7821a83530f..b52171afc69 100644 --- a/app/views/admin/application_settings/_performance.html.haml +++ b/app/views/admin/application_settings/_performance.html.haml @@ -15,4 +15,10 @@ AuthorizedKeysCommand. Click on the help icon for more details. = link_to icon('question-circle'), help_page_path('administration/operations/fast_ssh_key_lookup') + .form-group + = f.label :raw_blob_request_limit, _('Raw blob request rate limit per minute'), class: 'label-bold' + = f.number_field :raw_blob_request_limit, class: 'form-control' + .form-text.text-muted + = _('Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0.') + = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 2ddea0b9f16..8fecdc6e8a6 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -5,7 +5,7 @@ .branch = merge_path_description(@merge_request, 'to') .author - Author #{@merge_request.author_name} + Author: #{@merge_request.author_name} .assignee = assignees_label(@merge_request) .approvers diff --git a/app/views/peek/views/_gc.html.haml b/app/views/peek/views/_gc.html.haml deleted file mode 100644 index 2a586261ce1..00000000000 --- a/app/views/peek/views/_gc.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- local_assigns.fetch(:view) - -%span.bold - %span{ title: _('Invoke Time'), data: { defer_to: "#{view.defer_key}-gc_time" } }... - \/ - %span{ title: _('Invoke Count'), data: { defer_to: "#{view.defer_key}-invokes" } }... -gc diff --git a/app/views/peek/views/_redis.html.haml b/app/views/peek/views/_redis.html.haml deleted file mode 100644 index f7fba6c95fc..00000000000 --- a/app/views/peek/views/_redis.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- local_assigns.fetch(:view) - -%span.bold - %span{ data: { defer_to: "#{view.defer_key}-duration" } }... - \/ - %span{ data: { defer_to: "#{view.defer_key}-calls" } }... -redis diff --git a/app/views/peek/views/_sidekiq.html.haml b/app/views/peek/views/_sidekiq.html.haml deleted file mode 100644 index 7efbc05890d..00000000000 --- a/app/views/peek/views/_sidekiq.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- local_assigns.fetch(:view) - -%span.bold - %span{ data: { defer_to: "#{view.defer_key}-duration" } }... - \/ - %span{ data: { defer_to: "#{view.defer_key}-calls" } }... -sidekiq diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 02f6ef02843..78cce58938e 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -4,7 +4,7 @@ - page_title @tag.name, s_('TagsPage|Tags') %div{ class: container_class } - .top-area.multi-line + .top-area.multi-line.flex-wrap .nav-text .title %span.item-title.ref-name diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index af11ce94ec5..b05d903fabe 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -1,7 +1,7 @@ - force_priority = local_assigns.fetch(:force_priority, false) - subject_or_group_defined = defined?(@project) || defined?(@group) -- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project) -- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests, project: @project) +- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues) +- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests) .label-name = render_label(label, tooltip: false) diff --git a/changelogs/unreleased/48717-rate-limit-raw-controller-show.yml b/changelogs/unreleased/48717-rate-limit-raw-controller-show.yml new file mode 100644 index 00000000000..38ee95a7553 --- /dev/null +++ b/changelogs/unreleased/48717-rate-limit-raw-controller-show.yml @@ -0,0 +1,5 @@ +--- +title: Add Rate Request Limiter to RawController#show endpoint +merge_request: 30635 +author: +type: added diff --git a/changelogs/unreleased/63730-fix-500-status-labels-pd.yml b/changelogs/unreleased/63730-fix-500-status-labels-pd.yml new file mode 100644 index 00000000000..a1e2ae0e5df --- /dev/null +++ b/changelogs/unreleased/63730-fix-500-status-labels-pd.yml @@ -0,0 +1,5 @@ +--- +title: Fix admin labels page when there are invalid records +merge_request: 30885 +author: +type: fixed diff --git a/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml b/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml new file mode 100644 index 00000000000..fb0f4cedc62 --- /dev/null +++ b/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml @@ -0,0 +1,5 @@ +--- +title: Fixed distorted avatars when resource not reachable +merge_request: 30904 +author: Marc Schwede +type: other diff --git a/changelogs/unreleased/64763-fix-tags-page-layout.yml b/changelogs/unreleased/64763-fix-tags-page-layout.yml new file mode 100644 index 00000000000..db6b1f31506 --- /dev/null +++ b/changelogs/unreleased/64763-fix-tags-page-layout.yml @@ -0,0 +1,5 @@ +--- +title: Fix tag page layout +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/ab-count-strategies.yml b/changelogs/unreleased/ab-count-strategies.yml new file mode 100644 index 00000000000..bd95ff45d6f --- /dev/null +++ b/changelogs/unreleased/ab-count-strategies.yml @@ -0,0 +1,5 @@ +--- +title: Use tablesample approximate counting by default. +merge_request: 31048 +author: +type: performance diff --git a/changelogs/unreleased/add-outbound-requests-whitelist-for-local-networks.yml b/changelogs/unreleased/add-outbound-requests-whitelist-for-local-networks.yml new file mode 100644 index 00000000000..9b50175f536 --- /dev/null +++ b/changelogs/unreleased/add-outbound-requests-whitelist-for-local-networks.yml @@ -0,0 +1,5 @@ +--- +title: Add Outbound requests whitelist for local networks +merge_request: 30350 +author: Istvan Szalai +type: added diff --git a/changelogs/unreleased/optimise-import-performance.yml b/changelogs/unreleased/optimise-import-performance.yml new file mode 100644 index 00000000000..c63f44d5109 --- /dev/null +++ b/changelogs/unreleased/optimise-import-performance.yml @@ -0,0 +1,5 @@ +--- +title: Optimise import performance +merge_request: 31045 +author: +type: performance diff --git a/changelogs/unreleased/sh-add-rugged-to-peek.yml b/changelogs/unreleased/sh-add-rugged-to-peek.yml new file mode 100644 index 00000000000..8a030f3daf2 --- /dev/null +++ b/changelogs/unreleased/sh-add-rugged-to-peek.yml @@ -0,0 +1,5 @@ +--- +title: Add Rugged calls to performance bar +merge_request: 30983 +author: +type: other diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 0e78980350f..dd53127ac2c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -400,6 +400,15 @@ production: &base # path: shared/registry # issuer: gitlab-issuer + # Add notification settings if you plan to use Geo Replication for the registry + # notifications: + # - name: geo_event + # url: https://example.com/api/v4/container_registry_event/events + # timeout: 2s + # threshold: 5 + # backoff: 1s + # headers: + # Authorization: secret_phrase ## Error Reporting and Logging with Sentry sentry: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 494c4dd1f93..32fec7c3d22 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -259,6 +259,7 @@ Settings.registry['key'] ||= nil Settings.registry['issuer'] ||= nil Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':') Settings.registry['path'] = Settings.absolute(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry')) +Settings.registry['notifications'] ||= [] # # Error Reporting and Logging with Sentry diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 1ffd133239d..8416ae430c7 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -26,6 +26,7 @@ Peek.into PEEK_DB_VIEW Peek.into Peek::Views::Gitaly Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::RedisDetailed +Peek.into Peek::Views::Rugged Peek.into Peek::Views::GC Peek.into Peek::Views::Tracing if Labkit::Tracing.tracing_url_enabled? diff --git a/db/migrate/20180824202952_add_outbound_requests_whitelist_to_application_settings.rb b/db/migrate/20180824202952_add_outbound_requests_whitelist_to_application_settings.rb new file mode 100644 index 00000000000..4ee654ce873 --- /dev/null +++ b/db/migrate/20180824202952_add_outbound_requests_whitelist_to_application_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddOutboundRequestsWhitelistToApplicationSettings < ActiveRecord::Migration[5.1] + DOWNTIME = false + + def change + add_column :application_settings, :outbound_local_requests_whitelist, :string, array: true, limit: 255 + end +end diff --git a/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb b/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb index 0048268ca6f..bf7f7b44dec 100644 --- a/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb +++ b/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb @@ -35,7 +35,7 @@ class AddIndexesForMergeRequestDiffsQuery < ActiveRecord::Migration[5.0] end def down - INDEX_SPECS.reverse.each do |spec| + INDEX_SPECS.reverse_each do |spec| remove_concurrent_index(*spec) end end diff --git a/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb b/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb new file mode 100644 index 00000000000..8963e837e08 --- /dev/null +++ b/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddGeoContainerRepositoryUpdatedEventsTable < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :geo_container_repository_updated_events, force: :cascade do |t| + t.integer :container_repository_id, null: false + + t.index :container_repository_id, name: :idx_geo_con_rep_updated_events_on_container_repository_id, using: :btree + end + + add_column :geo_event_log, :container_repository_updated_event_id, :bigint + end +end diff --git a/db/migrate/20190611100202_add_index_to_geo_event_log.rb b/db/migrate/20190611100202_add_index_to_geo_event_log.rb new file mode 100644 index 00000000000..c5c855fed61 --- /dev/null +++ b/db/migrate/20190611100202_add_index_to_geo_event_log.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToGeoEventLog < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :geo_event_log, :container_repository_updated_event_id + end + + def down + remove_concurrent_index(:geo_event_log, :container_repository_updated_event_id) + end +end diff --git a/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb b/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb new file mode 100644 index 00000000000..cf7fd03e9af --- /dev/null +++ b/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class AddForeignKeysForContainerRepository < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key(:geo_container_repository_updated_events, :container_repositories, column: :container_repository_id, on_delete: :cascade) + + add_concurrent_foreign_key(:geo_event_log, :geo_container_repository_updated_events, column: :container_repository_updated_event_id, on_delete: :cascade) + end + + def down + if foreign_key_exists?(:geo_container_repository_updated_events, :container_repositories) + remove_foreign_key(:geo_container_repository_updated_events, :container_repositories) + end + + if foreign_key_exists?(:geo_event_log, :geo_container_repository_updated_events) + remove_foreign_key(:geo_event_log, :geo_container_repository_updated_events) + end + end +end diff --git a/db/migrate/20190715142138_add_raw_blob_request_limit_to_application_settings.rb b/db/migrate/20190715142138_add_raw_blob_request_limit_to_application_settings.rb new file mode 100644 index 00000000000..e8198e11ea7 --- /dev/null +++ b/db/migrate/20190715142138_add_raw_blob_request_limit_to_application_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddRawBlobRequestLimitToApplicationSettings < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :application_settings, :raw_blob_request_limit, :integer, default: 300, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index f42a4c600ea..2ec8fcce8f8 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_07_15_114644) do +ActiveRecord::Schema.define(version: 2019_07_15_142138) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -228,6 +228,8 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do t.boolean "lock_memberships_to_ldap", default: false, null: false t.boolean "time_tracking_limit_to_hours", default: false, null: false t.string "grafana_url", default: "/-/grafana", null: false + t.string "outbound_local_requests_whitelist", limit: 255, array: true + t.integer "raw_blob_request_limit", default: 300, null: false t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id" @@ -1275,6 +1277,11 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do t.string "key", null: false end + create_table "geo_container_repository_updated_events", force: :cascade do |t| + t.integer "container_repository_id", null: false + t.index ["container_repository_id"], name: "idx_geo_con_rep_updated_events_on_container_repository_id" + end + create_table "geo_event_log", force: :cascade do |t| t.datetime "created_at", null: false t.bigint "repository_updated_event_id" @@ -1289,7 +1296,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do t.bigint "job_artifact_deleted_event_id" t.bigint "reset_checksum_event_id" t.bigint "cache_invalidation_event_id" + t.bigint "container_repository_updated_event_id" t.index ["cache_invalidation_event_id"], name: "index_geo_event_log_on_cache_invalidation_event_id", where: "(cache_invalidation_event_id IS NOT NULL)" + t.index ["container_repository_updated_event_id"], name: "index_geo_event_log_on_container_repository_updated_event_id" t.index ["hashed_storage_attachments_event_id"], name: "index_geo_event_log_on_hashed_storage_attachments_event_id", where: "(hashed_storage_attachments_event_id IS NOT NULL)" t.index ["hashed_storage_migrated_event_id"], name: "index_geo_event_log_on_hashed_storage_migrated_event_id", where: "(hashed_storage_migrated_event_id IS NOT NULL)" t.index ["job_artifact_deleted_event_id"], name: "index_geo_event_log_on_job_artifact_deleted_event_id", where: "(job_artifact_deleted_event_id IS NOT NULL)" @@ -3700,7 +3709,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do add_foreign_key "fork_network_members", "projects", on_delete: :cascade add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade + add_foreign_key "geo_container_repository_updated_events", "container_repositories", name: "fk_212c89c706", on_delete: :cascade add_foreign_key "geo_event_log", "geo_cache_invalidation_events", column: "cache_invalidation_event_id", name: "fk_42c3b54bed", on_delete: :cascade + add_foreign_key "geo_event_log", "geo_container_repository_updated_events", column: "container_repository_updated_event_id", name: "fk_6ada82d42a", on_delete: :cascade add_foreign_key "geo_event_log", "geo_hashed_storage_migrated_events", column: "hashed_storage_migrated_event_id", name: "fk_27548c6db3", on_delete: :cascade add_foreign_key "geo_event_log", "geo_job_artifact_deleted_events", column: "job_artifact_deleted_event_id", name: "fk_176d3fbb5d", on_delete: :cascade add_foreign_key "geo_event_log", "geo_lfs_object_deleted_events", column: "lfs_object_deleted_event_id", name: "fk_d5af95fcd9", on_delete: :cascade diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md index 0e11dffa0d6..fd076bb79d8 100644 --- a/doc/administration/geo/replication/configuration.md +++ b/doc/administration/geo/replication/configuration.md @@ -17,7 +17,7 @@ You are encouraged to first read through all the steps before executing them in your testing/production environment. NOTE: **Note:** -**Do not** set up any custom authentication for the **secondary** nodes. This will be handled by the **primary** node. +**Do not** set up any custom authentication for the **secondary** nodes. This will be handled by the **primary** node. Any change that requires access to the **Admin Area** needs to be done in the **primary** node because the **secondary** node is a read-only replica. @@ -242,7 +242,7 @@ node's Geo Nodes dashboard in your browser. ![Geo dashboard](img/geo_node_dashboard.png) If your installation isn't working properly, check the -[troubleshooting document]. +[troubleshooting document](troubleshooting.md). The two most obvious issues that can become apparent in the dashboard are: diff --git a/doc/administration/geo/replication/updating_the_geo_nodes.md b/doc/administration/geo/replication/updating_the_geo_nodes.md index 166ee94eca4..550b3b07a95 100644 --- a/doc/administration/geo/replication/updating_the_geo_nodes.md +++ b/doc/administration/geo/replication/updating_the_geo_nodes.md @@ -251,7 +251,7 @@ Omnibus is the following: 1. Check the steps about defining `postgresql['sql_user_password']`, `gitlab_rails['db_password']`. 1. Make sure `postgresql['max_replication_slots']` matches the number of **secondary** Geo nodes locations. 1. Install GitLab on the **secondary** server. -1. Re-run the [database replication process][database-replication]. +1. Re-run the [database replication process](database.md#step-3-initiate-the-replication-process). ## Special update notes for 9.0.x diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png Binary files differindex 2bf686f9017..8a6f8b3b273 100644 --- a/doc/administration/monitoring/performance/img/performance_bar.png +++ b/doc/administration/monitoring/performance/img/performance_bar.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png Binary files differindex 7af6d401d1d..265178729c4 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png +++ b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_redis_calls.png b/doc/administration/monitoring/performance/img/performance_bar_redis_calls.png Binary files differnew file mode 100644 index 00000000000..78dd7594adf --- /dev/null +++ b/doc/administration/monitoring/performance/img/performance_bar_redis_calls.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_rugged_calls.png b/doc/administration/monitoring/performance/img/performance_bar_rugged_calls.png Binary files differnew file mode 100644 index 00000000000..f4068268137 --- /dev/null +++ b/doc/administration/monitoring/performance/img/performance_bar_rugged_calls.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png Binary files differindex b3219b4fa94..dab323eb066 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png +++ b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md index 4ee156fdc11..15eab7dcab0 100644 --- a/doc/administration/monitoring/performance/performance_bar.md +++ b/doc/administration/monitoring/performance/performance_bar.md @@ -8,15 +8,16 @@ activated, it looks as follows: It allows you to see (from left to right): - the current host serving the page -- the timing of the page (backend, frontend) - time taken and number of DB queries, click through for details of these queries ![SQL profiling using the Performance Bar](img/performance_bar_sql_queries.png) - time taken and number of [Gitaly] calls, click through for details of these calls ![Gitaly profiling using the Performance Bar](img/performance_bar_gitaly_calls.png) +- time taken and number of [Rugged] calls, click through for details of these calls + ![Rugged profiling using the Performance Bar](img/performance_bar_rugged_calls.png) - profile of the code used to generate the page, line by line. In the profile view, the numbers in the left panel represent wall time, cpu time, and number of calls (based on [rblineprof](https://github.com/tmm1/rblineprof)). ![Line profiling using the Performance Bar](img/performance_bar_line_profiling.png) -- time taken and number of calls to Redis -- time taken and number of background jobs created by Sidekiq +- time taken and number of Redis calls, click through for details of these calls + ![Redis profiling using the Performance Bar](img/performance_bar_redis_calls.png) - time taken and number of Ruby GC calls On the far right is a request selector that allows you to view the same metrics @@ -43,3 +44,4 @@ You can toggle the Bar using the same shortcut. ![GitLab Performance Bar Admin Settings](img/performance_bar_configuration_settings.png) [Gitaly]: ../../gitaly/index.md +[Rugged]: ../../high_availability/nfs.md#improving-nfs-performance-with-gitlab diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md index 098d946a9fa..604dff5983d 100644 --- a/doc/administration/troubleshooting/debug.md +++ b/doc/administration/troubleshooting/debug.md @@ -209,7 +209,7 @@ ps auwx | grep unicorn | awk '{ print " -p " $2}' | xargs strace -tt -T -f -s 10 The output in `/tmp/unicorn.txt` may help diagnose the root cause. -# More information +## More information - [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/) - [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt) diff --git a/doc/api/README.md b/doc/api/README.md index f9cc1a09870..91bc8ca8aab 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -29,7 +29,7 @@ The following API resources are available in the project context: | [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` | | [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` | | [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) | -| [Dependencies](dependencies.md) **[ULTIMATE]** | `/projects/:id/dependencies` +| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies` | [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) | | [Deployments](deployments.md) | `/projects/:id/deployments` | | [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) | diff --git a/doc/api/epics.md b/doc/api/epics.md index d05eb0a8804..3036b3c2364 100644 --- a/doc/api/epics.md +++ b/doc/api/epics.md @@ -10,7 +10,7 @@ If epics feature is not available a `403` status code will be returned. The [epic issues API](epic_issues.md) allows you to interact with issues associated with an epic. -# Milestone dates integration +## Milestone dates integration > [Introduced][ee-6448] in GitLab 11.3. diff --git a/doc/api/settings.md b/doc/api/settings.md index ff48cac1f47..68da88af698 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -39,6 +39,7 @@ Example response: "session_expire_delay" : 10080, "home_page_url" : null, "default_snippet_visibility" : "private", + "outbound_local_requests_whitelist": [], "domain_whitelist" : [], "domain_blacklist_enabled" : false, "domain_blacklist" : [], @@ -113,6 +114,7 @@ Example response: "default_project_visibility": "internal", "default_snippet_visibility": "private", "default_group_visibility": "private", + "outbound_local_requests_whitelist": [], "domain_whitelist": [], "domain_blacklist_enabled" : false, "domain_blacklist" : [], @@ -193,6 +195,7 @@ are listed in the descriptions of the relevant settings. | `domain_blacklist` | array of strings | required by: `domain_blacklist_enabled` | Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. | | `domain_blacklist_enabled` | boolean | no | (**If enabled, requires:** `domain_blacklist`) Allows blocking sign-ups from emails from specific domains. | | `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. | +| `outbound_local_requests_whitelist` | array of strings | no | Define a list of trusted domains or ip addresses to which local requests are allowed when local requests for hooks and services are disabled. | `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. | | `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. | | `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. | diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index efdcaf5a6f5..9f1ce1fc230 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -6,7 +6,6 @@ type: concepts, howto GitLab CI/CD allows you to use Docker Engine to build and test docker-based projects. - One of the new trends in Continuous Integration/Deployment is to: 1. Create an application image. @@ -29,7 +28,16 @@ during jobs. ## Runner Configuration -There are three methods to enable the use of `docker build` and `docker run` during jobs; each with their own tradeoffs. +There are three methods to enable the use of `docker build` and `docker run` +during jobs; each with their own tradeoffs. + +An alternative to using `docker build` is to [use kaniko](using_kaniko.md). +This avoids having to execute Runner in privileged mode. + +TIP: **Tip:** +To see how Docker and Runner are configured for shared Runners on +GitLab.com, see [GitLab.com Shared +Runners](../../user/gitlab_com/index.md#shared-runners). ### Use shell executor @@ -88,9 +96,9 @@ For more information please read [On Docker security: `docker` group considered The second approach is to use the special docker-in-docker (dind) [Docker image](https://hub.docker.com/_/docker/) with all tools installed (`docker`) and run the job script in context of that -image in privileged mode. +image in privileged mode. -NOTE: **Note:** `docker-compose` is not part of docker-in-docker (dind). In case you'd like to use `docker-compose` in your CI builds, please follow the [installation instructions for docker-compose](https://docs.docker.com/compose/install/) provided by docker. +NOTE: **Note:** `docker-compose` is not part of docker-in-docker (dind). In case you'd like to use `docker-compose` in your CI builds, please follow the [installation instructions for docker-compose](https://docs.docker.com/compose/install/) provided by docker. In order to do that, follow the steps: @@ -115,6 +123,13 @@ In order to do that, follow the steps: want to use [docker-in-docker] mode, you always have to use `privileged = true` in your Docker containers. + DANGER: **Danger:** + By enabling `--docker-privileged`, you are effectively disabling all of + the security mechanisms of containers and exposing your host to privilege + escalation which can lead to container breakout. For more information, check + out the official Docker documentation on + [Runtime privilege and Linux capabilities][docker-cap]. + The above command will create a `config.toml` entry similar to this: ```toml @@ -173,11 +188,6 @@ In order to do that, follow the steps: Docker-in-Docker works well, and is the recommended configuration, but it is not without its own challenges: -- By enabling `--docker-privileged`, you are effectively disabling all of - the security mechanisms of containers and exposing your host to privilege - escalation which can lead to container breakout. For more information, check - out the official Docker documentation on - [Runtime privilege and Linux capabilities][docker-cap]. - When using docker-in-docker, each job is in a clean environment without the past history. Concurrent jobs work fine because every build gets it's own instance of Docker engine so they won't conflict with each other. But this diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 06a81c3d0c7..98f30350968 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -345,7 +345,7 @@ GitLab provides API endpoints to: - [Triggering pipelines through the API](triggers/README.md). - [Pipeline triggers API](../api/pipeline_triggers.md). -### Start multiple manual actions in a stage +### Start multiple manual actions in a stage > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27188) in GitLab 11.11. diff --git a/doc/development/chaos_endpoints.md b/doc/development/chaos_endpoints.md index eb6dde2d24e..961520db7d8 100644 --- a/doc/development/chaos_endpoints.md +++ b/doc/development/chaos_endpoints.md @@ -69,7 +69,7 @@ curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10&token=se This endpoint attempts to fully utilise a single core, at 100%, for the given period. -Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds). +Depending on your rack server setup, your request may timeout after a predetermined period (normally 60 seconds). If you're using Unicorn, this is done by killing the worker process. ``` @@ -80,7 +80,7 @@ GET /-/chaos/cpu_spin?duration_s=50&async=true | Attribute | Type | Required | Description | | ------------ | ------- | -------- | --------------------------------------------------------------------- | -| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s | +| `duration_s` | integer | no | Duration, in seconds, that the core will be utilized. Defaults to 30s | | `async` | boolean | no | Set to true to consume CPU in a Sidekiq background worker process | ```bash @@ -93,7 +93,7 @@ curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60&token=secret This endpoint attempts to fully utilise a single core, and interleave it with DB request, for the given period. This endpoint can be used to model yielding execution to another threads when running concurrently. -Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds). +Depending on your rack server setup, your request may timeout after a predetermined period (normally 60 seconds). If you're using Unicorn, this is done by killing the worker process. ``` @@ -105,7 +105,7 @@ GET /-/chaos/db_spin?duration_s=50&async=true | Attribute | Type | Required | Description | | ------------ | ------- | -------- | --------------------------------------------------------------------------- | | `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s | -| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s | +| `duration_s` | integer | no | Duration, in seconds, that the core will be utilized. Defaults to 30s | | `async` | boolean | no | Set to true to perform the operation in a Sidekiq background worker process | ```bash diff --git a/doc/development/contributing/community_roles.md b/doc/development/contributing/community_roles.md index 3296cb173d7..7d2d1b77a0e 100644 --- a/doc/development/contributing/community_roles.md +++ b/doc/development/contributing/community_roles.md @@ -1,4 +1,4 @@ -### Community members & roles +# Community members & roles GitLab community members and their privileges/responsibilities. diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md index 0d9397c3bd5..09b4a3c3d96 100644 --- a/doc/development/fe_guide/axios.md +++ b/doc/development/fe_guide/axios.md @@ -1,15 +1,18 @@ # Axios + We use [axios][axios] to communicate with the server in Vue applications and most new code. In order to guarantee all defaults are set you *should not use `axios` directly*, you should import `axios` from `axios_utils`. ## CSRF token + All our request require a CSRF token. To guarantee this token is set, we are importing [axios][axios], setting the token, and exporting `axios` . This exported module should be used instead of directly using `axios` to ensure the token is set. ## Usage + ```javascript import axios from './lib/utils/axios_utils'; diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md index f895cfd36ac..95c4a094c04 100644 --- a/doc/development/fe_guide/style_guide_scss.md +++ b/doc/development/fe_guide/style_guide_scss.md @@ -212,6 +212,7 @@ selectors are intended for use only with JavaScript to allow for removal or renaming without breaking styling. ### IDs + Don't use ID selectors in CSS. ```scss diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md index f827d240bf6..9f0ac8cc753 100644 --- a/doc/development/go_guide/index.md +++ b/doc/development/go_guide/index.md @@ -173,6 +173,45 @@ func Test(t *testing.T) { } ``` +### Table-Driven Tests + +Using [Table-Driven Tests](https://github.com/golang/go/wiki/TableDrivenTests) +is generally good practice when you have multiple entries of +inputs/outputs for the same function. Below are some guidelines one can +follow when writing table-driven test. These guidelines are mostly +extracted from Go standard library source code. Keep in mind it's OK not +to follow these guidelines when it makes sense. + +#### Defining test cases + +Each table entry is a complete test case with inputs and expected +results, and sometimes with additional information such as a test name +to make the test output easily readable. + +- [Define a slice of anonymous struct](https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/encoding/csv/reader_test.go#L16) + inside of the test. +- [Define a slice of anonymous struct](https://github.com/golang/go/blob/55d31e16c12c38d36811bdee65ac1f7772148250/src/cmd/go/internal/module/module_test.go#L9-L66) + outside of the test. +- [Named structs](https://github.com/golang/go/blob/2e0cd2aef5924e48e1ceb74e3d52e76c56dd34cc/src/cmd/go/internal/modfetch/coderepo_test.go#L54-L69) + for code reuse. +- [Using `map[string]struct{}`](https://github.com/golang/go/blob/6d5caf38e37bf9aeba3291f1f0b0081f934b1187/src/cmd/trace/annotations_test.go#L180-L235). + +#### Contents of the test case + +- Ideally, each test case should have a field with a unique identifier + to use for naming subtests. In the Go standard library, this is commonly the + `name string` field. +- Use `want`/`expect`/`actual` when you are specifcing something in the + test case that will be used for assertion. + +#### Variable names + +- Each table-driven test map/slice of struct can be named `tests`. +- When looping through `tests` the anonymous struct can be referred + to as `tt` or `tc`. +- The description of the test can be referred to as + `name`/`testName`/`tn`. + ### Benchmarks Programs handling a lot of IO or complex operations should always include diff --git a/doc/development/module_with_instance_variables.md b/doc/development/module_with_instance_variables.md index 7bdfa04fc57..443eee0b62c 100644 --- a/doc/development/module_with_instance_variables.md +++ b/doc/development/module_with_instance_variables.md @@ -1,6 +1,6 @@ -## Modules with instance variables could be considered harmful +# Modules with instance variables could be considered harmful -### Background +## Background Rails somehow encourages people using modules and instance variables everywhere. For example, using instance variables in the controllers, @@ -9,7 +9,7 @@ helpers, and views. They're also encouraging the use of saving everything in a giant, single object, and people could access everything in that one giant object. -### The problems +## The problems Of course this is convenient to develop, because we just have everything within reach. However this has a number of downsides when that chosen object @@ -24,7 +24,7 @@ manipulated from 3 different modules. It's hard to track when those variables start giving us troubles. We don't know which module would suddenly change one of the variables. Everything could touch anything. -### Similar concerns +## Similar concerns People are saying multiple inheritance is bad. Mixing multiple modules with multiple instance variables scattering everywhere suffer from the same issue. @@ -40,7 +40,7 @@ Note that `included` doesn't solve the whole issue. They define the dependencies, but they still allow each modules to talk implicitly via the instance variables in the final giant object, and that's where the problem is. -### Solutions +## Solutions We should split the giant object into multiple objects, and they communicate with each other with the API, i.e. public methods. In short, composition over @@ -53,7 +53,7 @@ With clearly defined API, this would make things less coupled and much easier to debug and track, and much more extensible for other objects to use, because they communicate in a clear way, rather than implicit dependencies. -### Acceptable use +## Acceptable use However, it's not always bad to use instance variables in a module, as long as it's contained in the same module; that is, no other modules or @@ -74,7 +74,7 @@ Unfortunately it's not easy to code more complex rules into the cop, so we rely on people's best judgement. If we could find another good pattern we could easily add to the cop, we should do it. -### How to rewrite and avoid disabling this cop +## How to rewrite and avoid disabling this cop Even if we could just disable the cop, we should avoid doing so. Some code could be easily rewritten in simple form. Consider this acceptable method: @@ -181,7 +181,7 @@ rather than whatever includes the module, and those modules which were also included, making it much easier to track down any issues, and reducing the chance of having name conflicts. -### How to disable this cop +## How to disable this cop Put the disabling comment right after your code in the same line: @@ -210,14 +210,14 @@ end Note that you need to enable it at some point, otherwise everything below won't be checked. -### Things we might need to ignore right now +## Things we might need to ignore right now Because of the way Rails helpers and mailers work, we might not be able to avoid the use of instance variables there. For those cases, we could ignore them at the moment. At least we're not going to share those modules with other random objects, so they're still somewhat isolated. -### Instance variables in views +## Instance variables in views They're bad because we can't easily tell who's using the instance variables (from controller's point of view) and where we set them up (from partials' diff --git a/doc/development/new_fe_guide/dependencies.md b/doc/development/new_fe_guide/dependencies.md index 12a4f089d41..8a6930acd37 100644 --- a/doc/development/new_fe_guide/dependencies.md +++ b/doc/development/new_fe_guide/dependencies.md @@ -15,6 +15,18 @@ Exceptions are made for some tools that we require in the `gitlab:assets:compile` CI job such as `webpack-bundle-analyzer` to analyze our production assets post-compile. +To add or upgrade a dependency, run: + +```sh +yarn add <your dependency here> +``` + +This may introduce duplicate dependencies. To de-duplicate `yarn.lock`, run: + +```sh +node_modules/.bin/yarn-deduplicate --list --strategy fewer yarn.lock && yarn install +``` + --- > TODO: Add Dependencies diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md index 81a29170129..ae5c4c6a6cc 100644 --- a/doc/development/new_fe_guide/development/accessibility.md +++ b/doc/development/new_fe_guide/development/accessibility.md @@ -1,17 +1,21 @@ # Accessiblity + Using semantic HTML plays a key role when it comes to accessibility. ## Accessible Rich Internet Applications - ARIA + WAI-ARIA, the Accessible Rich Internet Applications specification, defines a way to make Web content and Web applications more accessible to people with disabilities. > Note: It is [recommended][using-aria] to use semantic elements as the primary method to achieve accessibility rather than adding aria attributes. Adding aria attributes should be seen as a secondary method for creating accessible elements. ### Role + The `role` attribute describes the role the element plays in the context of the document. Check the list of WAI-ARIA roles [here][roles] ## Icons + When using icons or images that aren't absolutely needed to understand the context, we should use `aria-hidden="true"`. On the other hand, if an icon is crucial to understand the context we should do one of the following: @@ -20,6 +24,7 @@ On the other hand, if an icon is crucial to understand the context we should do 1. Use `aria-labelledby` to point to an element that contains the explanation for that icon ## Form inputs + In forms we should use the `for` attribute in the label statement: ``` diff --git a/doc/development/new_fe_guide/style/index.md b/doc/development/new_fe_guide/style/index.md index 335d9e66240..f073dc56f1f 100644 --- a/doc/development/new_fe_guide/style/index.md +++ b/doc/development/new_fe_guide/style/index.md @@ -8,7 +8,7 @@ ## [Vue style guide](vue.md) -# Tooling +## Tooling ## [Prettier](prettier.md) diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md index 59da02ed6fd..fce144f8dc2 100644 --- a/doc/development/reusing_abstractions.md +++ b/doc/development/reusing_abstractions.md @@ -14,13 +14,13 @@ on maintainability, the ability to easily debug problems, or even performance. An example would be to use `ProjectsFinder` in `IssuesFinder` to limit issues to those belonging to a set of projects. While initially this may seem like a good idea, both classes provide a very high level interface with very little control. -This means that `IssuesFinder` may not be able to produce a better optimised +This means that `IssuesFinder` may not be able to produce a better optimized database query, as a large portion of the query is controlled by the internals of `ProjectsFinder`. To work around this problem, you would use the same code used by `ProjectsFinder`, instead of using `ProjectsFinder` itself directly. This allows -you to compose your behaviour better, giving you more control over the behaviour +you to compose your behavior better, giving you more control over the behavior of the code. To illustrate, consider the following code from `IssuableFinder#projects`: @@ -52,7 +52,7 @@ functionality is added to this (high level) interface. Instead of _only_ affecting the cases where this is necessary, it may also start affecting `IssuableFinder` in a negative way. For example, the query produced by `GroupProjectsFinder` may include unnecessary conditions. Since we're using a -finder here, we can't easily opt-out of that behaviour. We could add options to +finder here, we can't easily opt-out of that behavior. We could add options to do so, but then we'd need as many options as we have features. Every option adds two code paths, which means that for four features we have to cover 8 different code paths. @@ -213,6 +213,5 @@ The API provided by Active Record itself, such as the `where` method, `save`, Everything in `app/workers`. -The scheduling of Sidekiq jobs using `SomeWorker.perform_async`, `perform_in`, -etc. Directly invoking a worker using `SomeWorker.new.perform` should be avoided -at all times in application code, though this is fine to use in tests. +Use `SomeWorker.perform_async` or `SomeWorker.perform_in` to schedule Sidekiq +jobs. Never directly invoke a worker using `SomeWorker.new.perform`. diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md index 661ab9cef1a..ccec6f7d719 100644 --- a/doc/development/verifying_database_capabilities.md +++ b/doc/development/verifying_database_capabilities.md @@ -25,7 +25,7 @@ else end ``` -# Read-only database +## Read-only database The database can be used in read-only mode. In this case we have to make sure all GET requests don't attempt any write operations to the diff --git a/doc/integration/slack.md b/doc/integration/slack.md index f84ab769218..9fcf2c2d99a 100644 --- a/doc/integration/slack.md +++ b/doc/integration/slack.md @@ -1,5 +1,5 @@ --- -redirect_to: '../project_services/slack.md' +redirect_to: '../user/project/integrations/slack.md' --- -This document was moved to [project_services/slack.md](../project_services/slack.md). +This document was moved to [project_services/slack.md](../user/project/integrations/slack.md). diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 30b9e88ade8..c416dbac492 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -1,6 +1,7 @@ # Auto DevOps -> [Introduced][ce-37115] in GitLab 10.0. Generally available on GitLab 11.0. +> - [Introduced][ce-37115] in GitLab 10.0. +> - Generally available on GitLab 11.0. Auto DevOps provides pre-defined CI/CD configuration which allows you to automatically detect, build, test, deploy, and monitor your applications. Leveraging CI/CD best practices and tools, Auto DevOps aims @@ -43,16 +44,16 @@ platform or a Platform as a Service (PaaS). It takes inspiration from the innovative work done by [Heroku](https://www.heroku.com/) and goes beyond it in multiple ways: -1. Auto DevOps works with any Kubernetes cluster; you're not limited to running - on GitLab's infrastructure. (Note that many features also work without Kubernetes.) -1. There is no additional cost (no markup on the infrastructure costs), and you - can use a self-hosted Kubernetes cluster or Containers as a Service on any - public cloud (for example, [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)). -1. Auto DevOps has more features including security testing, performance testing, - and code quality testing. -1. Auto DevOps offers an incremental graduation path. If you need advanced customizations, - you can start modifying the templates without having to start over on a - completely different platform. Review the [customizing](#customizing) section for more information. +- Auto DevOps works with any Kubernetes cluster; you're not limited to running + on GitLab's infrastructure. (Note that many features also work without Kubernetes). +- There is no additional cost (no markup on the infrastructure costs), and you + can use a self-hosted Kubernetes cluster or Containers as a Service on any + public cloud (for example, [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)). +- Auto DevOps has more features including security testing, performance testing, + and code quality testing. +- Auto DevOps offers an incremental graduation path. If you need advanced customizations, + you can start modifying the templates without having to start over on a + completely different platform. Review the [customizing](#customizing) section for more information. ## Features @@ -90,38 +91,38 @@ For an overview on the creation of Auto DevOps, read the blog post [From 2/3 of To make full use of Auto DevOps, you will need: -1. **GitLab Runner** (needed for all stages) - Your Runner needs to be - configured to be able to run Docker. Generally this means using the - [Docker](https://docs.gitlab.com/runner/executors/docker.html) or [Kubernetes - executor](https://docs.gitlab.com/runner/executors/kubernetes.html), with - [privileged mode enabled](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode). - The Runners do not need to be installed in the Kubernetes cluster, but the - Kubernetes executor is easy to use and is automatically autoscaling. - Docker-based Runners can be configured to autoscale as well, using [Docker - Machine](https://docs.gitlab.com/runner/install/autoscaling.html). Runners - should be registered as [shared Runners](../../ci/runners/README.md#registering-a-shared-runner) - for the entire GitLab instance, or [specific Runners](../../ci/runners/README.md#registering-a-specific-runner) - that are assigned to specific projects. -1. **Base domain** (needed for Auto Review Apps and Auto Deploy) - You will need - a domain configured with wildcard DNS which is going to be used by all of your - Auto DevOps applications. [Read the specifics](#auto-devops-base-domain). -1. **Kubernetes** (needed for Auto Review Apps, Auto Deploy, and Auto Monitoring) - - To enable deployments, you will need Kubernetes 1.5+. You need a [Kubernetes cluster][kubernetes-clusters] - for the project, or a Kubernetes [default service template](../../user/project/integrations/services_templates.md) - for the entire GitLab installation. - 1. **A load balancer** - You can use NGINX ingress by deploying it to your - Kubernetes cluster using the - [`nginx-ingress`](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress) - Helm chart. -1. **Prometheus** (needed for Auto Monitoring) - To enable Auto Monitoring, you - will need Prometheus installed somewhere (inside or outside your cluster) and - configured to scrape your Kubernetes cluster. To get response metrics - (in addition to system metrics), you need to - [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-nginx-ingress-monitoring). - The [Prometheus service](../../user/project/integrations/prometheus.md) - integration needs to be enabled for the project, or enabled as a - [default service template](../../user/project/integrations/services_templates.md) - for the entire GitLab installation. +- **GitLab Runner** (needed for all stages) - Your Runner needs to be + configured to be able to run Docker. Generally this means using the + [Docker](https://docs.gitlab.com/runner/executors/docker.html) or [Kubernetes + executor](https://docs.gitlab.com/runner/executors/kubernetes.html), with + [privileged mode enabled](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode). + The Runners do not need to be installed in the Kubernetes cluster, but the + Kubernetes executor is easy to use and is automatically autoscaling. + Docker-based Runners can be configured to autoscale as well, using [Docker + Machine](https://docs.gitlab.com/runner/install/autoscaling.html). Runners + should be registered as [shared Runners](../../ci/runners/README.md#registering-a-shared-runner) + for the entire GitLab instance, or [specific Runners](../../ci/runners/README.md#registering-a-specific-runner) + that are assigned to specific projects. +- **Base domain** (needed for Auto Review Apps and Auto Deploy) - You will need + a domain configured with wildcard DNS which is going to be used by all of your + Auto DevOps applications. [Read the specifics](#auto-devops-base-domain). +- **Kubernetes** (needed for Auto Review Apps, Auto Deploy, and Auto Monitoring) - + To enable deployments, you will need Kubernetes 1.5+. You need a [Kubernetes cluster][kubernetes-clusters] + for the project, or a Kubernetes [default service template](../../user/project/integrations/services_templates.md) + for the entire GitLab installation. + - **A load balancer** - You can use NGINX ingress by deploying it to your + Kubernetes cluster using the + [`nginx-ingress`](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress) + Helm chart. +- **Prometheus** (needed for Auto Monitoring) - To enable Auto Monitoring, you + will need Prometheus installed somewhere (inside or outside your cluster) and + configured to scrape your Kubernetes cluster. To get response metrics + (in addition to system metrics), you need to + [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-nginx-ingress-monitoring). + The [Prometheus service](../../user/project/integrations/prometheus.md) + integration needs to be enabled for the project, or enabled as a + [default service template](../../user/project/integrations/services_templates.md) + for the entire GitLab installation. If you do not have Kubernetes or Prometheus installed, then Auto Review Apps, Auto Deploy, and Auto Monitoring will be silently skipped. @@ -147,7 +148,7 @@ NOTE: **Note** A wildcard DNS A record matching the base domain(s) is required, for example, given a base domain of `example.com`, you'd need a DNS entry like: -``` +```text *.example.com 3600 A 1.2.3.4 ``` @@ -224,7 +225,7 @@ full use of Auto DevOps are available. If this is your fist time, we recommend y GitLab.com users can enable/disable Auto DevOps at the project-level only. Self-managed users can enable/disable Auto DevOps at the project-level, group-level or instance-level. -### Enabling/disabling Auto DevOps at the instance-level (Administrators only) +### At the instance level (Administrators only) Even when disabled at the instance level, group owners and project maintainers can still enable Auto DevOps at the group and project level, respectively. @@ -234,7 +235,7 @@ Auto DevOps at the group and project level, respectively. 1. If enabling, optionally set up the Auto DevOps [base domain](#auto-devops-base-domain) which will be used for Auto Deploy and Auto Review Apps. 1. Click **Save changes** for the changes to take effect. -### Enabling/disabling Auto DevOps at the group-level +### At the group level > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52447) in GitLab 11.10. @@ -250,7 +251,7 @@ When enabling or disabling Auto DevOps at group-level, group configuration will the subgroups and projects inside that group, unless Auto DevOps is specifically enabled or disabled on the subgroup or project. -### Enabling/disabling Auto DevOps at the project-level +### At the project level If enabling, check that your project doesn't have a `.gitlab-ci.yml`, or if one exists, remove it. @@ -263,7 +264,7 @@ If enabling, check that your project doesn't have a `.gitlab-ci.yml`, or if one When the feature has been enabled, an Auto DevOps pipeline is triggered on the default branch. -### Feature flag to enable for a percentage of projects +### Enable for a percentage of projects There is also a feature flag to enable Auto DevOps by default to your chosen percentage of projects. @@ -371,7 +372,7 @@ Any differences between the source and target branches are also Static Application Security Testing (SAST) uses the [SAST Docker image](https://gitlab.com/gitlab-org/security-products/sast) to run static analysis on the current code and checks for potential security issues. The -the Auto SAST stage will be skipped on licenses other than Ultimate and requires GitLab Runner 11.5 or above. +Auto SAST stage will be skipped on licenses other than Ultimate and requires GitLab Runner 11.5 or above. Once the report is created, it's uploaded as an artifact which you can later download and check out. @@ -488,7 +489,7 @@ Any security warnings are also shown in the merge request widget. Read how Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/) to measure the performance of a web page. A JSON report is created and uploaded as an artifact, which includes the overall performance score for each page. By default, the root page of Review and Production environments will be tested. If you would like to add additional URL's to test, simply add the paths to a file named `.gitlab-urls.txt` in the root directory, one per line. For example: -``` +```text / /features /direction @@ -749,7 +750,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. | | `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | | `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). | -| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> | +| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | | `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | | `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. | @@ -1009,7 +1010,7 @@ multi-buildpack does not. As of GitLab 10.0, the supported buildpacks are: -``` +```text - heroku-buildpack-multi v1.0.0 - heroku-buildpack-ruby v168 - heroku-buildpack-nodejs v99 diff --git a/doc/user/abuse_reports.md b/doc/user/abuse_reports.md index 41ee7e62b2c..e6c86cc8f2e 100644 --- a/doc/user/abuse_reports.md +++ b/doc/user/abuse_reports.md @@ -1,6 +1,12 @@ # Abuse reports -Report abuse from users to GitLab administrators. +You can report abuse from other GitLab users to GitLab administrators. + +A GitLab administrator [can then choose](admin_area/abuse_reports.md) to: + +- Remove the user, which deletes them from the instance. +- Block the user, which denies them access to the instance. +- Or remove the report, which retains the users access to the instance. You can report a user through their: @@ -12,7 +18,8 @@ You can report a user through their: To report abuse from a user's profile page: -1. Click on the exclamation point report abuse button at the top right of the user's profile. +1. Click on the exclamation point report abuse button at the top right of the + user's profile. 1. Complete an abuse report. 1. Click the **Send report** button. @@ -26,15 +33,18 @@ To report abuse from a user's comment: 1. Click the **Send report** button. NOTE: **Note:** -A URL to the reported user's comment will be -pre-filled in the abuse report's **Message** field. +A URL to the reported user's comment will be pre-filled in the abuse report's +**Message** field. ## Reporting abuse through a user's issue or merge request The **Report abuse** button is displayed at the top right of the issue or merge request: -- When **Report abuse** is selected from the menu that appears when the **Close issue** or **Close merge request** button is clicked, for users that have permission to close the issue or merge request. -- When viewing the issue or merge request, for users that don't have permission to close the issue or merge request. +- When **Report abuse** is selected from the menu that appears when the + **Close issue** or **Close merge request** button is clicked, for users that + have permission to close the issue or merge request. +- When viewing the issue or merge request, for users that don't have permission + to close the issue or merge request. With the **Report abuse** button displayed, to submit an abuse report: diff --git a/doc/user/admin_area/abuse_reports.md b/doc/user/admin_area/abuse_reports.md index 01c2d9607f5..8088c33fc9c 100644 --- a/doc/user/admin_area/abuse_reports.md +++ b/doc/user/admin_area/abuse_reports.md @@ -2,30 +2,60 @@ View and resolve abuse reports from GitLab users. -Admins can view abuse reports in the admin area and are able to -resolve the reports by removing the reported user, blocking the reported user, or removing the report. +GitLab administrators can view and [resolve](#resolving-abuse-reports) abuse +reports in the Admin Area. ## Reporting abuse -To find out more about reporting abuse, see [abuse reports user documentation](../abuse_reports.md). +To find out more about reporting abuse, see [abuse reports user +documentation](../abuse_reports.md). ## Resolving abuse reports -To access abuse reports, go to **Admin area > Abuse Reports**. +To access abuse reports, go to **Admin Area > Abuse Reports**. There are 3 ways to resolve an abuse report, with a button for each method: -- Remove user & report: [Deletes the reported user](../profile/account/delete_account.md) from the instance and removes the abuse report from the list. -- Block user: Blocks the reported user from the instance and does not remove the abuse report from the list. -- Remove report: Removes the abuse report from the list and does not restrict the access for the reported user. +- Remove user & report. This will: + - [Delete the reported user](../profile/account/delete_account.md) from the + instance. + - Remove the abuse report from the list. +- [Block user](#blocking-users). +- Remove report. This will: + - Remove the abuse report from the list. + - Remove access restrictions for the reported user. + +The following is an example of the **Abuse Reports** page: ![abuse-reports-page-image](img/abuse_reports_page.png) -## Blocked users +### Blocking users + +A blocked user cannot log in or access any repositories, but all of their data +remains. + +Blocking a user: + +- Leaves them in the abuse report list. +- Changes the **Block user** button to a disabled **Already blocked** button. -Blocking a user will not remove the abuse report from the list. +The user will be notified with the +[following message](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/workers/email_receiver_worker.rb#L38): -Instead, the block button will be disabled and explain that the user is "Already blocked". -You are still able to remove the user and/or report if necessary. +```text +Your account has been blocked. If you believe this is in error, contact a staff member. +``` + +After blocking, you can still either: + +- Remove the user and report if necessary. +- Remove the report. + +The following is an example of a blocked user listed on the **Abuse Reports** +page: ![abuse-report-blocked-user-image](img/abuse_report_blocked_user.png) + +NOTE: **Note:** +Users can be [blocked](../../api/users.md#block-user) and +[unblocked](../../api/users.md#unblock-user) using the GitLab API. diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md index f698e0a1608..516600c9d99 100644 --- a/doc/user/admin_area/settings/usage_statistics.md +++ b/doc/user/admin_area/settings/usage_statistics.md @@ -52,8 +52,8 @@ You can view the exact JSON payload in the administration panel. To view the pay 1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**. 1. Expand **Usage statistics** and click on the **Preview payload** button. -You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv). - +You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv). + ### Deactivate the usage ping The usage ping is opt-out. If you want to deactivate this feature, go to diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 7473647f129..a2f0584e8dc 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -331,7 +331,7 @@ project's dependencies with their versions. This list can be generated only for [languages and package managers](#supported-languages-and-package-managers) supported by Gemnasium. -To see the generated dependency list, navigate to your project's **Project > Dependency List**. +To see the generated dependency list, navigate to your project's **Security & Compliance > Dependency List**. ## Versioning and release process diff --git a/doc/user/application_security/security_dashboard/img/dashboard.png b/doc/user/application_security/security_dashboard/img/dashboard.png Binary files differdeleted file mode 100644 index a75168b1ce4..00000000000 --- a/doc/user/application_security/security_dashboard/img/dashboard.png +++ /dev/null diff --git a/doc/user/application_security/security_dashboard/img/group_security_dashboard.png b/doc/user/application_security/security_dashboard/img/group_security_dashboard.png Binary files differnew file mode 100644 index 00000000000..40689861e2a --- /dev/null +++ b/doc/user/application_security/security_dashboard/img/group_security_dashboard.png diff --git a/doc/user/application_security/security_dashboard/img/project_security_dashboard.png b/doc/user/application_security/security_dashboard/img/project_security_dashboard.png Binary files differindex f0dad6c54d0..89b310895d3 100644 --- a/doc/user/application_security/security_dashboard/img/project_security_dashboard.png +++ b/doc/user/application_security/security_dashboard/img/project_security_dashboard.png diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md index 2a2385c00ae..4cd3fc5f735 100644 --- a/doc/user/application_security/security_dashboard/index.md +++ b/doc/user/application_security/security_dashboard/index.md @@ -49,7 +49,7 @@ The group Security Dashboard gives an overview of the vulnerabilities of all the projects in a group and its subgroups. First, navigate to the Security Dashboard found under your group's -**Overview > Security Dashboard**. +**Security** tab. Once you're on the dashboard, at the top you should see a series of filters for: @@ -58,7 +58,7 @@ Once you're on the dashboard, at the top you should see a series of filters for: - Report type - Project -![dashboard with action buttons and metrics](img/dashboard.png) +![dashboard with action buttons and metrics](img/group_security_dashboard.png) Selecting one or more filters will filter the results in this page. The first section is an overview of all the vulnerabilities, grouped by severity. diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md index 99589cb1915..625c5440ec0 100644 --- a/doc/user/group/clusters/index.md +++ b/doc/user/group/clusters/index.md @@ -137,6 +137,13 @@ The result will then be: - The Staging cluster will be used for the `deploy to staging` job. - The Production cluster will be used for the `deploy to production` job. +## Security of Runners + +For important information about securely configuring GitLab Runners, see +[Security of +Runners](../../project/clusters/index.md#security-of-gitlab-runners) +documentation for project-level clusters. + <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/doc/user/group/saml_sso/img/scim_attribute_mapping.png b/doc/user/group/saml_sso/img/scim_attribute_mapping.png Binary files differindex c9f6b71f5b0..dad459d8c28 100644 --- a/doc/user/group/saml_sso/img/scim_attribute_mapping.png +++ b/doc/user/group/saml_sso/img/scim_attribute_mapping.png diff --git a/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png b/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png Binary files differnew file mode 100644 index 00000000000..85e5648816e --- /dev/null +++ b/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png diff --git a/doc/user/group/saml_sso/img/scim_provisioning_status.png b/doc/user/group/saml_sso/img/scim_provisioning_status.png Binary files differnew file mode 100644 index 00000000000..4b8887b5418 --- /dev/null +++ b/doc/user/group/saml_sso/img/scim_provisioning_status.png diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 55c5a18db7d..bc74725bbc9 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -45,7 +45,7 @@ The following identity providers are supported: Feature.enable(:group_scim, group) ``` -### GitLab configuration +## GitLab configuration Once [Single sign-on](index.md) has been configured, we can: @@ -55,41 +55,48 @@ Once [Single sign-on](index.md) has been configured, we can: ![SCIM token configuration](img/scim_token.png) -## SCIM IdP configuration +## Identity Provider configuration -### Configuration on Azure +### Azure -In the [Single sign-on](index.md) configuration for the group, make sure -that the **Name identifier value** (NameID) points to a unique identifier, such -as the `user.objectid`. This will match the `extern_uid` used on GitLab. +First, double check the [Single sign-on](index.md) configuration for your group and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab. -The GitLab app in Azure needs to be configured following -[Azure's SCIM setup](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#getting-started). +![Name identifier value mapping](img/scim_name_identifier_mapping.png) -Note the following: +#### Set up admin credentials + +Next, configure your GitLab application in Azure by following the +[Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim) +section in Azure's SCIM setup documentation. + +During this configuration, note the following: - The `Tenant URL` and `secret token` are the ones retrieved in the [previous step](#gitlab-configuration). - Should there be any problems with the availability of GitLab or similar errors, the notification email set will get those. +- It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox. - For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled. -You can then test the connection clicking on `Test Connection`. +You can then test the connection by clicking on **Test Connection**. If the connection is successful, be sure to save your configuration before moving on. -### Synchronize Azure Active Directory users +#### Configure attribute mapping -1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure - the attribute mapping. -1. Select the unique identifier (in the example `objectId`) as the `id` and `externalId`, - and enable the `Create`, `Update`, and `Delete` actions. -1. Map the `userPricipalName` to `emails[type eq "work"].value` and `mailNickname` to - `userName`. +1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure the attribute mapping. +1. Click **Delete** next to the `mail` mapping. +1. Map `userPrincipalName` to `emails[type eq "work"].value` and change it's **Matching precedence** to `2`. +1. Map `mailNickname` to `userName`. +1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, **Target attribute** to `id`, **Match objects using this attribute** to `Yes`, and **Matching precedence** to `1`. +1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, and **Target attribute** to `externalId`. +1. Click the `userPrincipalName` mapping and change **Match objects using this attribute** to `No`. - Example configuration: + Save your changes and you should have the following configuration: ![Azure's attribute mapping configuration](img/scim_attribute_mapping.png) -1. Click on **Show advanced options > Edit attribute list for AppName**. + NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`. + +1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**. 1. Leave the `id` as the primary and only required field. NOTE: **Note:** @@ -99,12 +106,14 @@ You can then test the connection clicking on `Test Connection`. ![Azure's attribute advanced configuration](img/scim_advanced.png) 1. Save all the screens and, in the **Provisioning** step, set - the `Provisioning Status` to `ON`. + the `Provisioning Status` to `On`. + + ![Provisioning status toggle switch](img/scim_provisioning_status.png) NOTE: **Note:** You can control what is actually synced by selecting the `Scope`. For example, `Sync only assigned users and groups` will only sync the users assigned to - the application (`Users and groups`), otherwise it will sync the whole Active Directory. + the application (`Users and groups`), otherwise, it will sync the whole Active Directory. Once enabled, the synchronization details and any errors will appear on the bottom of the **Provisioning** screen, together with a link to the audit logs. diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 37f3f21f539..9380dcf2a32 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -136,26 +136,26 @@ Supported formats (named colors are not supported): Color written inside backticks will be followed by a color "chip": ```markdown -`#F00` -`#F00A` -`#FF0000` -`#FF0000AA` -`RGB(0,255,0)` -`RGB(0%,100%,0%)` -`RGBA(0,255,0,0.3)` -`HSL(540,70%,50%)` -`HSLA(540,70%,50%,0.3)` -``` - -`#F00` -`#F00A` -`#FF0000` -`#FF0000AA` -`RGB(0,255,0)` -`RGB(0%,100%,0%)` -`RGBA(0,255,0,0.3)` -`HSL(540,70%,50%)` -`HSLA(540,70%,50%,0.3)` +`#F00` +`#F00A` +`#FF0000` +`#FF0000AA` +`RGB(0,255,0)` +`RGB(0%,100%,0%)` +`RGBA(0,255,0,0.3)` +`HSL(540,70%,50%)` +`HSLA(540,70%,50%,0.3)` +``` + +`#F00` +`#F00A` +`#FF0000` +`#FF0000AA` +`RGB(0,255,0)` +`RGB(0%,100%,0%)` +`RGBA(0,255,0,0.3)` +`HSL(540,70%,50%)` +`HSLA(540,70%,50%,0.3)` ### Diagrams and flowcharts using Mermaid @@ -397,6 +397,7 @@ unordered or ordered lists: - [ ] Sub-task 1 - [x] Sub-task 2 - [ ] Sub-task 3 + 1. [x] Completed task 1. [ ] Incomplete task 1. [ ] Sub-task 1 @@ -408,6 +409,7 @@ unordered or ordered lists: - [ ] Sub-task 1 - [x] Sub-task 2 - [ ] Sub-task 3 + 1. [x] Completed task 1. [ ] Incomplete task 1. [ ] Sub-task 1 @@ -976,7 +978,7 @@ after the `</summary>` tag and before the `</details>` tag, as shown in the exam These details _will_ remain **hidden** until expanded. - PASTE LOGS HERE +PASTE LOGS HERE </details> ``` @@ -988,7 +990,7 @@ These details _will_ remain **hidden** until expanded. These details <em>will</em> remain <b>hidden</b> until expanded. - PASTE LOGS HERE +PASTE LOGS HERE </details> @@ -1047,14 +1049,14 @@ A new line due to the previous backslash. First paragraph. Another line in the same paragraph. -A third line in the same paragraph, but this time ending with two spaces. +A third line in the same paragraph, but this time ending with two spaces. A new line directly under the first paragraph. <!-- (Do *NOT* remove the two ending whitespaces in the second line) --> <!-- (They are needed for the Markdown text to render correctly on docs.gitlab.com, the backslash works fine inside GitLab itself) --> Second paragraph. -Another line, this time ending with a backslash. +Another line, this time ending with a backslash. A new line due to the previous backslash. ### Links @@ -1135,13 +1137,13 @@ GFM will autolink almost any URL you put into your text: ### Lists Ordered and unordered lists can be easily created. Add the number you want the list -to start with, like `1. ` (with a space) at the start of each line for ordered lists. +to start with, like `1.`, followed by a space, at the start of each line for ordered lists. After the first number, it does not matter what number you use, ordered lists will be -numbered automatically by vertical order, so repeating `1. ` for all items in the -same list is common. If you start with a number other than `1. `, it will use that as the first +numbered automatically by vertical order, so repeating `1.` for all items in the +same list is common. If you start with a number other than `1.`, it will use that as the first number, and count up from there. -Add a `* `, `- ` or `+ ` (with a space) at the start of each line for unordered lists, but +Add a `*`, `-` or `+`, followed by a space, at the start of each line for unordered lists, but you should not use a mix of them. Examples: @@ -1156,7 +1158,9 @@ Examples: 4. And another item. * Unordered lists can use asterisks + - Or minuses + + Or pluses ``` @@ -1170,9 +1174,11 @@ Examples: 1. Next ordered sub-list item 1. And another item. -* Unordered lists can use asterisks +- Unordered lists can use asterisks + - Or minuses -+ Or pluses + +- Or pluses --- diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 044be05b932..e6822f0c52c 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -47,6 +47,7 @@ The following table depicts the various user permission levels in a project. | View approved/blacklisted licenses **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ | | View license management reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | View Security reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | +| View [Design Management](project/issues/design_management.md) pages **(PREMIUM)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ | @@ -74,6 +75,7 @@ The following table depicts the various user permission levels in a project. | View Error Tracking list | | ✓ | ✓ | ✓ | ✓ | | Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ | | Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ || +| Upload [Design Management](project/issues/design_management.md) files **(PREMIUM)** | | | ✓ | ✓ | ✓ | | Create new branches | | | ✓ | ✓ | ✓ | | Push to non-protected branches | | | ✓ | ✓ | ✓ | | Force push to non-protected branches | | | ✓ | ✓ | ✓ | diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md index 304a7984191..cbee79de493 100644 --- a/doc/user/profile/account/delete_account.md +++ b/doc/user/profile/account/delete_account.md @@ -1,37 +1,71 @@ -# Deleting a User Account +# Deleting a User account + +Users can be deleted from a GitLab instance, either by: + +- The user themselves. +- An administrator. NOTE: **Note:** Deleting a user will delete all projects in that user namespace. -- As a user, you can delete your own account by navigating to **Settings** > **Account** and selecting **Delete account** -- As an admin, you can delete a user account by navigating to the **Admin Area**, selecting the **Users** tab, selecting a user, and clicking on **Delete user** +## As a user + +As a user, you can delete your own account by: + +1. Clicking on your avatar. +1. Navigating to **Settings > Account**. +1. Selecting **Delete account**. + +## As an administrator + +As an administrator, you can delete a user account by: + +1. Navigating to **Admin Area > Overview > Users**. +1. Selecting a user. +1. Under the **Account** tab, clicking: + - **Delete user** to delete only the user but maintaining their + [associated records](#associated-records). + - **Delete user and contributions** to delete the user and + their associated records. + +### Blocking a user + +In addition to blocking a user +[via an abuse report](../../admin_area/abuse_reports.md#blocking-users), +a user can be blocked directly from the Admin area. To do this: + +1. Navigate to **Admin Area > Overview > Users**. +1. Selecting a user. +1. Under the **Account** tab, click **Block user**. ## Associated Records -> Introduced for issues in [GitLab 9.0][ce-7393], and for merge requests, award - emoji, notes, and abuse reports in [GitLab 9.1][ce-10467]. - Hard deletion from abuse reports and spam logs was introduced in - [GitLab 9.1][ce-10273], and from the API in [GitLab 9.3][ce-11853]. +> - Introduced for issues in +> [GitLab 9.0](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7393). +> - Introduced for merge requests, award emoji, notes, and abuse reports in +> [GitLab 9.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10467). +> - Hard deletion from abuse reports and spam logs was introduced in +> [GitLab 9.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10273), +> and from the API in +> [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11853). When a user account is deleted, not all associated records are deleted with it. Here's a list of things that will **not** be deleted: -- Issues that the user created -- Merge requests that the user created -- Notes that the user created -- Abuse reports that the user reported -- Award emoji that the user created +- Issues that the user created. +- Merge requests that the user created. +- Notes that the user created. +- Abuse reports that the user reported. +- Award emoji that the user created. Instead of being deleted, these records will be moved to a system-wide -user with the username "Ghost User", whose sole purpose is to act as a container for such records. Any commits made by a deleted user will still display the username of the original user. - -When a user is deleted from an [abuse report](../../admin_area/abuse_reports.md) or spam log, these associated -records are not ghosted and will be removed, along with any groups the user -is a sole owner of. Administrators can also request this behaviour when -deleting users from the [API](../../../api/users.md#user-deletion) or the -admin area. - -[ce-7393]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7393 -[ce-10273]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10273 -[ce-10467]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10467 -[ce-11853]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11853 +user with the username "Ghost User", whose sole purpose is to act as a container +for such records. Any commits made by a deleted user will still display the +username of the original user. + +When a user is deleted from an [abuse report](../../admin_area/abuse_reports.md) +or spam log, these associated records are not ghosted and will be removed, along +with any groups the user is a sole owner of. + +Administrators can also request this behavior when deleting users from the +[API](../../../api/users.md#user-deletion) or the Admin Area. diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md index 55a9fbabf98..28f3420de35 100644 --- a/doc/user/project/clusters/eks_and_gitlab/index.md +++ b/doc/user/project/clusters/eks_and_gitlab/index.md @@ -253,7 +253,7 @@ With RBAC disabled and services deployed, [Auto DevOps](../../../../topics/autodevops/index.md) can now be leveraged to build, test, and deploy the app. -[Enable Auto DevOps](../../../../topics/autodevops/index.md#enablingdisabling-auto-devops-at-the-project-level) +[Enable Auto DevOps](../../../../topics/autodevops/index.md#at-the-project-level) if not already enabled. If a wildcard DNS entry was created resolving to the Load Balancer, enter it in the `domain` field under the Auto DevOps settings. Otherwise, the deployed app will not be externally available outside of the cluster. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 4c247691757..35f2976899b 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -343,10 +343,15 @@ turn can do almost everything that the host can do. Be aware of the inherent security risk associated with performing `docker run` operations on arbitrary images as they effectively have root access. -If you don't want to use GitLab Runner in privileged mode, first make sure that -you don't have it installed via the applications, and then use the -[Runner's Helm chart](../../../install/kubernetes/gitlab_runner_chart.md) to -install it manually. +If you don't want to use GitLab Runner in privileged mode, either: + +- Use shared Runners on GitLab.com. They don't have this security issue. +- Set up your own Runners using configuration described at + [Shared Runners](../../gitlab_com/index.md#shared-runners). This involves: + 1. Making sure that you don't have it installed via + [the applications](#installing-applications). + 1. Installing a Runner + [using `docker+machine`](https://docs.gitlab.com/runner/executors/docker_machine.html). ## Installing applications diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md index 6be8fc82431..90a725f271b 100644 --- a/doc/user/project/integrations/prometheus_library/haproxy.md +++ b/doc/user/project/integrations/prometheus_library/haproxy.md @@ -1,4 +1,5 @@ # Monitoring HAProxy + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4 GitLab has support for automatically detecting and monitoring HAProxy. This is provided by leveraging the [HAProxy Exporter](https://github.com/prometheus/haproxy_exporter), which translates HAProxy statistics into a Prometheus readable form. diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md new file mode 100644 index 00000000000..2327fa84998 --- /dev/null +++ b/doc/user/project/issues/design_management.md @@ -0,0 +1,58 @@ +# Design Management **(PREMIUM)** + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/660) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2. + +CAUTION: **Warning:** +This an __alpha__ feature and is subject to change at any time without +prior notice. + +## Overview + +Design Management allows you to upload design assets (wireframes, mockups, etc.) +to GitLab issues and keep them stored in one single place, accessed by the Design +Management's page within an issue, giving product designers, product managers, and engineers a +way to collaborate on designs over one single source of truth. + +You can easily share mock-ups of designs with your team, or visual regressions can be easily +viewed and addressed. + +<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> +For an overview, see the video [Design Management (GitLab 12.2)](https://www.youtube.com/watch?v=CCMtCqdK_aM). + +## Requirements + +Design Management requires +[Large File Storage (LFS)](../../../workflow/lfs/manage_large_binaries_with_git_lfs.md) +to be enabled: + +- For GitLab.com, LFS is already enabled. +- For self-managed instances, a GitLab administrator must have + [enabled LFS globally](../../../workflow/lfs/lfs_administration.md). +- For both GitLab.com and self-managed instances: LFS must be enabled for the project itself. + If enabled globally, LFS will be enabled by default to all projects. To enable LFS on the + project level, navigate to your project's **Settings > General**, expand **Visibility, project features, permissions** + and enable **Git Large File Storage**. + +## Limitations + +- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`. The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab-ee/issues/12771). +- [Designs cannot yet be deleted](https://gitlab.com/gitlab-org/gitlab-ee/issues/11089). +- Design Management is [not yet supported in the project export](https://gitlab.com/gitlab-org/gitlab-ee/issues/11090). + +## The Design Management page + +Navigate to the **Design Management** page from any issue by clicking the **Designs** tab: + +![Designs tab](img/design_management_v12_2.png) + +## Adding designs + +To upload design images, click the **Upload Designs** button and select images to upload. + +Designs with the same filename as an existing uploaded design will create a new version +of the design, and will replace the previous version. + +## Viewing designs + +Images on the Design Management page can be enlarged by clicking on them. + diff --git a/doc/user/project/issues/img/design_management_v12_2.png b/doc/user/project/issues/img/design_management_v12_2.png Binary files differnew file mode 100644 index 00000000000..6da747a3f21 --- /dev/null +++ b/doc/user/project/issues/img/design_management_v12_2.png diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md index 6706e1c2e2b..bf04ed2d2d0 100644 --- a/doc/user/project/issues/index.md +++ b/doc/user/project/issues/index.md @@ -120,6 +120,12 @@ associated label or assignee will change to match that of the new column. The en board can also be filtered to only include issues from a certain milestone or an overarching label. +### Design Management **(PREMIUM)** + +With [Design Management](design_management.md), you can upload design +assets to issues and view them all together to easily share and +collaborate with your team. + ### Epics **(ULTIMATE)** [Epics](../../group/epics/index.md) let you manage your portfolio of projects more diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md index ad1d79ae5b1..eb6e454062a 100644 --- a/doc/user/project/merge_requests/code_quality.md +++ b/doc/user/project/merge_requests/code_quality.md @@ -9,8 +9,18 @@ in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3. With the help of [GitLab CI/CD](../../../ci/README.md), you can analyze your source code quality using GitLab Code Quality. -Code Quality uses [Code Climate Engines](https://codeclimate.com), which are -free and open source. Code Quality doesn't require a Code Climate subscription. + +Code Quality: + +- Uses [Code Climate Engines](https://codeclimate.com), which are + free and open source. Code Quality doesn't require a Code Climate + subscription. +- Runs in [pipelines](../../../ci/pipelines.md) using an Docker image built in + [GitLab Code + Quality](https://gitlab.com/gitlab-org/security-products/codequality) project. +- Can make use of a [template](#template-and-examples). +- Is available with [Auto + DevOps](../../../topics/autodevops/index.md#auto-code-quality-starter). Going a step further, GitLab can show the Code Quality report right in the merge request widget area: @@ -21,22 +31,48 @@ in the merge request widget area: For instance, consider the following workflow: -1. Your backend team member starts a new implementation for making a certain feature in your app faster -1. With Code Quality reports, they analyze how their implementation is impacting the code quality -1. The metrics show that their code degrade the quality in 10 points -1. You ask a co-worker to help them with this modification -1. They both work on the changes until Code Quality report displays no degradations, only improvements -1. You approve the merge request and authorize its deployment to staging -1. Once verified, their changes are deployed to production +1. Your backend team member starts a new implementation for making a certain + feature in your app faster. +1. With Code Quality reports, they analyze how their implementation is impacting + the code quality. +1. The metrics show that their code degrade the quality in 10 points. +1. You ask a co-worker to help them with this modification. +1. They both work on the changes until Code Quality report displays no + degradations, only improvements. +1. You approve the merge request and authorize its deployment to staging. +1. Once verified, their changes are deployed to production. + +## Template and examples + +For most GitLab instances, the supplied template is the preferred method of +implementing Code Quality. See +[Analyze your project's Code Quality](../../../ci/examples/code_quality.md) for: + +- Information on the builtin GitLab Code Quality template. +- Examples of manual GitLab configuration for earlier GitLab versions. -## How it works +## Configuring jobs using variables -First of all, you need to define a job in your `.gitlab-ci.yml` file that generates the -[Code Quality report artifact](../../../ci/yaml/README.md#artifactsreportscodequality-starter). +The Code Quality job supports environment variables that users can set to +configure job execution at runtime. -The Code Quality report artifact is a subset of the -[Code Climate spec](https://github.com/codeclimate/spec/blob/master/SPEC.md#data-types). -It must be a JSON file containing an array of objects with the following properties: +For a list of available environment variables, see +[Environment variables](https://gitlab.com/gitlab-org/security-products/codequality/blob/master/README.md#environment-variables). + +## Implementing a custom tool + +It's possible to have a custom tool provide Code Quality reports in GitLab. To +do this: + +1. Define a job in your `.gitlab-ci.yml` file that generates the + [Code Quality report + artifact](../../../ci/yaml/README.md#artifactsreportscodequality-starter). +1. Configure your tool to generate the Code Quality report artifact as a JSON + file that implements subset of the [Code Climate + spec](https://github.com/codeclimate/spec/blob/master/SPEC.md#data-types). + +The Code Quality report artifact JSON file must contain an array of objects +with the following properties: | Name | Description | | ---------------------- | -------------------------------------------------------------------------------------- | @@ -63,13 +99,16 @@ Example: ``` NOTE: **Note:** -Although the Code Climate spec supports more properties, those are ignored by GitLab. +Although the Code Climate spec supports more properties, those are ignored by +GitLab. + +## Code Quality reports -For more information on what the Code Quality job should look like, check the -example on [analyzing a project's code quality](../../../ci/examples/code_quality.md). +Once the Code Quality job has completed, GitLab: -GitLab then checks this report, compares the metrics between the source and target -branches, and shows the information right on the merge request. +- Checks the generated report. +- Compares the metrics between the source and target branches. +- Shows the information right on the merge request. If multiple jobs in a pipeline generate a code quality artifact, only the artifact from the last created job (the job with the largest job ID) is used. To avoid confusion, diff --git a/lib/gitlab/action_rate_limiter.rb b/lib/gitlab/action_rate_limiter.rb index c442211e073..fdb06d00548 100644 --- a/lib/gitlab/action_rate_limiter.rb +++ b/lib/gitlab/action_rate_limiter.rb @@ -33,16 +33,48 @@ module Gitlab # Increments the given key and returns true if the action should # be throttled. # - # key - An array of ActiveRecord instances - # threshold_value - The maximum number of times this action should occur in the given time interval + # key - An array of ActiveRecord instances or strings + # threshold_value - The maximum number of times this action should occur in the given time interval. If number is zero is considered disabled. def throttled?(key, threshold_value) - self.increment(key) > threshold_value + threshold_value > 0 && + self.increment(key) > threshold_value + end + + # Logs request into auth.log + # + # request - Web request to be logged + # type - A symbol key that represents the request. + # current_user - Current user of the request, it can be nil. + def log_request(request, type, current_user) + request_information = { + message: 'Action_Rate_Limiter_Request', + env: type, + ip: request.ip, + request_method: request.request_method, + fullpath: request.fullpath + } + + if current_user + request_information.merge!({ + user_id: current_user.id, + username: current_user.username + }) + end + + Gitlab::AuthLogger.error(request_information) end private def action_key(key) - serialized = key.map { |obj| "#{obj.class.model_name.to_s.underscore}:#{obj.id}" }.join(":") + serialized = key.map do |obj| + if obj.is_a?(String) + "#{obj}" + else + "#{obj.class.model_name.to_s.underscore}:#{obj.id}" + end + end.join(":") + "action_rate_limiter:#{action}:#{serialized}" end end diff --git a/lib/gitlab/database/count.rb b/lib/gitlab/database/count.rb index f3d37ccd72a..eac61254bdf 100644 --- a/lib/gitlab/database/count.rb +++ b/lib/gitlab/database/count.rb @@ -37,16 +37,14 @@ module Gitlab # @return [Hash] of Model -> count mapping def self.approximate_counts(models, strategies: [TablesampleCountStrategy, ReltuplesCountStrategy, ExactCountStrategy]) strategies.each_with_object({}) do |strategy, counts_by_model| - if strategy.enabled? - models_with_missing_counts = models - counts_by_model.keys + models_with_missing_counts = models - counts_by_model.keys - break counts_by_model if models_with_missing_counts.empty? + break counts_by_model if models_with_missing_counts.empty? - counts = strategy.new(models_with_missing_counts).count + counts = strategy.new(models_with_missing_counts).count - counts.each do |model, count| - counts_by_model[model] = count - end + counts.each do |model, count| + counts_by_model[model] = count end end end diff --git a/lib/gitlab/database/count/exact_count_strategy.rb b/lib/gitlab/database/count/exact_count_strategy.rb index fa6951eda22..0b8fe640bf8 100644 --- a/lib/gitlab/database/count/exact_count_strategy.rb +++ b/lib/gitlab/database/count/exact_count_strategy.rb @@ -23,10 +23,6 @@ module Gitlab rescue *CONNECTION_ERRORS {} end - - def self.enabled? - true - end end end end diff --git a/lib/gitlab/database/count/reltuples_count_strategy.rb b/lib/gitlab/database/count/reltuples_count_strategy.rb index 695f6fa766e..6cd90c01ab2 100644 --- a/lib/gitlab/database/count/reltuples_count_strategy.rb +++ b/lib/gitlab/database/count/reltuples_count_strategy.rb @@ -31,10 +31,6 @@ module Gitlab {} end - def self.enabled? - Gitlab::Database.postgresql? - end - private # Models using single-type inheritance (STI) don't work with diff --git a/lib/gitlab/database/count/tablesample_count_strategy.rb b/lib/gitlab/database/count/tablesample_count_strategy.rb index 7777f31f702..e9387a91a14 100644 --- a/lib/gitlab/database/count/tablesample_count_strategy.rb +++ b/lib/gitlab/database/count/tablesample_count_strategy.rb @@ -28,10 +28,6 @@ module Gitlab {} end - def self.enabled? - Gitlab::Database.postgresql? && Feature.enabled?(:tablesample_counts) - end - private def perform_count(model, estimate) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 6e8aa5d578e..27032602828 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -55,6 +55,10 @@ module Gitlab @name = @relative_path.split("/").last end + def to_s + "<#{self.class.name}: #{self.gl_project_path}>" + end + def ==(other) other.is_a?(self.class) && [storage, relative_path] == [other.storage, other.relative_path] end diff --git a/lib/gitlab/git/rugged_impl/blob.rb b/lib/gitlab/git/rugged_impl/blob.rb index 9aea736527b..5c73c0c66a9 100644 --- a/lib/gitlab/git/rugged_impl/blob.rb +++ b/lib/gitlab/git/rugged_impl/blob.rb @@ -16,7 +16,7 @@ module Gitlab override :tree_entry def tree_entry(repository, sha, path, limit) if use_rugged?(repository, :rugged_tree_entry) - wrap_rugged_call { rugged_tree_entry(repository, sha, path, limit) } + execute_rugged_call(:rugged_tree_entry, repository, sha, path, limit) else super end diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb index 29ae9bdd851..0eff35ab1c4 100644 --- a/lib/gitlab/git/rugged_impl/commit.rb +++ b/lib/gitlab/git/rugged_impl/commit.rb @@ -36,7 +36,7 @@ module Gitlab override :find_commit def find_commit(repo, commit_id) if use_rugged?(repo, :rugged_find_commit) - wrap_rugged_call { rugged_find(repo, commit_id) } + execute_rugged_call(:rugged_find, repo, commit_id) else super end @@ -45,7 +45,7 @@ module Gitlab override :batch_by_oid def batch_by_oid(repo, oids) if use_rugged?(repo, :rugged_list_commits_by_oid) - wrap_rugged_call { rugged_batch_by_oid(repo, oids) } + execute_rugged_call(:rugged_batch_by_oid, repo, oids) else super end @@ -68,7 +68,7 @@ module Gitlab override :commit_tree_entry def commit_tree_entry(path) if use_rugged?(@repository, :rugged_commit_tree_entry) - wrap_rugged_call { rugged_tree_entry(path) } + execute_rugged_call(:rugged_tree_entry, path) else super end diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb index 7bed553393c..8fde93e71e2 100644 --- a/lib/gitlab/git/rugged_impl/repository.rb +++ b/lib/gitlab/git/rugged_impl/repository.rb @@ -48,7 +48,7 @@ module Gitlab override :ancestor? def ancestor?(from, to) if use_rugged?(self, :rugged_commit_is_ancestor) - wrap_rugged_call { rugged_is_ancestor?(from, to) } + execute_rugged_call(:rugged_is_ancestor?, from, to) else super end diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb index 479c5f9d8b7..389c9d32ccb 100644 --- a/lib/gitlab/git/rugged_impl/tree.rb +++ b/lib/gitlab/git/rugged_impl/tree.rb @@ -16,7 +16,7 @@ module Gitlab override :tree_entries def tree_entries(repository, sha, path, recursive) if use_rugged?(repository, :rugged_tree_entries) - wrap_rugged_call { tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive) } + execute_rugged_call(:tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive) else super end diff --git a/lib/gitlab/git/rugged_impl/use_rugged.rb b/lib/gitlab/git/rugged_impl/use_rugged.rb index badf943e39c..80b75689334 100644 --- a/lib/gitlab/git/rugged_impl/use_rugged.rb +++ b/lib/gitlab/git/rugged_impl/use_rugged.rb @@ -11,17 +11,23 @@ module Gitlab Gitlab::GitalyClient.can_use_disk?(repo.storage) end - def wrap_rugged_call(&block) + def execute_rugged_call(method_name, *args) Gitlab::GitalyClient::StorageSettings.allow_disk_access do start = Gitlab::Metrics::System.monotonic_time - result = yield + result = send(method_name, *args) # rubocop:disable GitlabSecurity/PublicSend duration = Gitlab::Metrics::System.monotonic_time - start if Gitlab::RuggedInstrumentation.active? Gitlab::RuggedInstrumentation.increment_query_count Gitlab::RuggedInstrumentation.query_time += duration + + Gitlab::RuggedInstrumentation.add_call_details( + feature: method_name, + args: args, + duration: duration, + backtrace: Gitlab::Profiler.clean_backtrace(caller)) end result diff --git a/lib/gitlab/import_export/attributes_finder.rb b/lib/gitlab/import_export/attributes_finder.rb index 409243e68a5..42cd94add79 100644 --- a/lib/gitlab/import_export/attributes_finder.rb +++ b/lib/gitlab/import_export/attributes_finder.rb @@ -45,7 +45,7 @@ module Gitlab end def key_from_hash(value) - value.is_a?(Hash) ? value.keys.first : value + value.is_a?(Hash) ? value.first.first : value end end end diff --git a/lib/gitlab/import_export/json_hash_builder.rb b/lib/gitlab/import_export/json_hash_builder.rb index b145f37c052..a92e3862361 100644 --- a/lib/gitlab/import_export/json_hash_builder.rb +++ b/lib/gitlab/import_export/json_hash_builder.rb @@ -27,7 +27,7 @@ module Gitlab # {:merge_requests=>[:merge_request_diff, :notes]} def process_model_objects(model_object_hash) json_config_hash = {} - current_key = model_object_hash.keys.first + current_key = model_object_hash.first.first model_object_hash.values.flatten.each do |model_object| @attributes_finder.parse(current_key) { |hash| json_config_hash[current_key] ||= hash } diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb index ab19a509310..4e976cfca3a 100644 --- a/lib/gitlab/import_export/members_mapper.rb +++ b/lib/gitlab/import_export/members_mapper.rb @@ -35,7 +35,7 @@ module Gitlab end def include?(old_author_id) - map.keys.include?(old_author_id) && map[old_author_id] != default_user_id + map.has_key?(old_author_id) && map[old_author_id] != default_user_id end private diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 1b545b1d049..0be49e27acb 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -185,7 +185,7 @@ module Gitlab return unless EXISTING_OBJECT_CHECK.include?(@relation_name) return unless @relation_hash['group_id'] - @relation_hash['group_id'] = @project.group&.id + @relation_hash['group_id'] = @project.namespace_id end def reset_tokens! diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index 890228e5e78..615c0ec374c 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -166,7 +166,7 @@ module Gitlab [model, times.count, times.sum] end - summarised_load_times.sort_by(&:last).reverse.each do |(model, query_count, time)| + summarised_load_times.sort_by(&:last).reverse_each do |(model, query_count, time)| logger.info("#{model} total (#{query_count}): #{time.round(2)}ms") end end diff --git a/lib/gitlab/rugged_instrumentation.rb b/lib/gitlab/rugged_instrumentation.rb index 70c06e8b308..8bb8c547ae1 100644 --- a/lib/gitlab/rugged_instrumentation.rb +++ b/lib/gitlab/rugged_instrumentation.rb @@ -24,7 +24,24 @@ module Gitlab end def self.active? - Gitlab::SafeRequestStore.active? + SafeRequestStore.active? + end + + def self.peek_enabled? + SafeRequestStore[:peek_enabled] + end + + def self.add_call_details(details) + return unless peek_enabled? + + Gitlab::SafeRequestStore[:rugged_call_details] ||= [] + Gitlab::SafeRequestStore[:rugged_call_details] << details + end + + def self.list_call_details + return [] unless peek_enabled? + + Gitlab::SafeRequestStore[:rugged_call_details] || [] end end end diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb index f6b2e2acf16..eab6762cab7 100644 --- a/lib/gitlab/url_blocker.rb +++ b/lib/gitlab/url_blocker.rb @@ -45,18 +45,21 @@ module Gitlab ascii_only: ascii_only ) + normalized_hostname = uri.normalized_host hostname = uri.hostname port = get_port(uri) address_info = get_address_info(hostname, port) return [uri, nil] unless address_info - protected_uri_with_hostname = enforce_uri_hostname(address_info, uri, hostname, dns_rebind_protection) + ip_address = ip_address(address_info) + protected_uri_with_hostname = enforce_uri_hostname(ip_address, uri, hostname, dns_rebind_protection) # Allow url from the GitLab instance itself but only for the configured hostname and ports return protected_uri_with_hostname if internal?(uri) validate_local_request( + normalized_hostname: normalized_hostname, address_info: address_info, allow_localhost: allow_localhost, allow_local_network: allow_local_network @@ -83,10 +86,7 @@ module Gitlab # # The original hostname is used to validate the SSL, given in that scenario # we'll be making the request to the IP address, instead of using the hostname. - def enforce_uri_hostname(addrs_info, uri, hostname, dns_rebind_protection) - address = addrs_info.first - ip_address = address&.ip_address - + def enforce_uri_hostname(ip_address, uri, hostname, dns_rebind_protection) return [uri, nil] unless dns_rebind_protection && ip_address && ip_address != hostname uri = uri.dup @@ -94,6 +94,10 @@ module Gitlab [uri, hostname] end + def ip_address(address_info) + address_info.first&.ip_address + end + def validate_uri(uri:, schemes:, ports:, enforce_sanitization:, enforce_user:, ascii_only:) validate_html_tags(uri) if enforce_sanitization @@ -113,9 +117,19 @@ module Gitlab rescue SocketError end - def validate_local_request(address_info:, allow_localhost:, allow_local_network:) + def validate_local_request( + normalized_hostname:, + address_info:, + allow_localhost:, + allow_local_network:) return if allow_local_network && allow_localhost + ip_whitelist, domain_whitelist = + Gitlab::CurrentSettings.outbound_local_requests_whitelist_arrays + + return if local_domain_whitelisted?(domain_whitelist, normalized_hostname) || + local_ip_whitelisted?(ip_whitelist, ip_address(address_info)) + unless allow_localhost validate_localhost(address_info) validate_loopback(address_info) @@ -231,6 +245,16 @@ module Gitlab (uri.port.blank? || uri.port == config.gitlab_shell.ssh_port) end + def local_ip_whitelisted?(ip_whitelist, ip_string) + ip_obj = Gitlab::Utils.string_to_ip_object(ip_string) + + ip_whitelist.any? { |ip| ip.include?(ip_obj) } + end + + def local_domain_whitelisted?(domain_whitelist, domain_string) + domain_whitelist.include?(domain_string) + end + def config Gitlab.config end diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb index 16ec8a8bb28..31c9e18c996 100644 --- a/lib/gitlab/utils.rb +++ b/lib/gitlab/utils.rb @@ -131,5 +131,12 @@ module Gitlab data end end + + def string_to_ip_object(str) + return unless str + + IPAddr.new(str) + rescue IPAddr::InvalidAddressError + end end end diff --git a/lib/peek/views/detailed_view.rb b/lib/peek/views/detailed_view.rb new file mode 100644 index 00000000000..ebaf46478df --- /dev/null +++ b/lib/peek/views/detailed_view.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Peek + module Views + class DetailedView < View + def results + { + duration: formatted_duration, + calls: calls, + details: details + } + end + + private + + def duration + raise NotImplementedError + end + + def calls + raise NotImplementedError + end + + def call_details + raise NotImplementedError + end + + def format_call_details(call) + raise NotImplementedError + end + + def details + call_details + .sort { |a, b| b[:duration] <=> a[:duration] } + .map(&method(:format_call_details)) + end + + def formatted_duration + ms = duration * 1000 + + if ms >= 1000 + "%.2fms" % ms + else + "%.0fms" % ms + end + end + end + end +end diff --git a/lib/peek/views/gitaly.rb b/lib/peek/views/gitaly.rb index 30f95a10024..067aaf31fbc 100644 --- a/lib/peek/views/gitaly.rb +++ b/lib/peek/views/gitaly.rb @@ -2,7 +2,9 @@ module Peek module Views - class Gitaly < View + class Gitaly < DetailedView + private + def duration ::Gitlab::GitalyClient.query_time end @@ -11,20 +13,8 @@ module Peek ::Gitlab::GitalyClient.get_request_count end - def results - { - duration: formatted_duration, - calls: calls, - details: details - } - end - - private - - def details + def call_details ::Gitlab::GitalyClient.list_call_details - .sort { |a, b| b[:duration] <=> a[:duration] } - .map(&method(:format_call_details)) end def format_call_details(call) @@ -34,15 +24,6 @@ module Peek request: pretty_request || {}) end - def formatted_duration - ms = duration * 1000 - if ms >= 1000 - "%.2fms" % ms - else - "%.0fms" % ms - end - end - def setup_subscribers subscribe 'start_processing.action_controller' do ::Gitlab::GitalyClient.query_time = 0 diff --git a/lib/peek/views/redis_detailed.rb b/lib/peek/views/redis_detailed.rb index 12760c9b75e..c61a1e91282 100644 --- a/lib/peek/views/redis_detailed.rb +++ b/lib/peek/views/redis_detailed.rb @@ -35,36 +35,19 @@ end module Peek module Views - class RedisDetailed < View + class RedisDetailed < DetailedView REDACTED_MARKER = "<redacted>" def key 'redis' end - def results - { - calls: calls, - duration: formatted_duration, - details: details - } - end - def detail_store ::Gitlab::SafeRequestStore['redis_call_details'] ||= [] end private - def formatted_duration - ms = duration * 1000 - if ms >= 1000 - "%.2fms" % ms - else - "%.0fms" % ms - end - end - def duration detail_store.map { |entry| entry[:duration] }.sum # rubocop:disable CodeReuse/ActiveRecord end @@ -73,10 +56,8 @@ module Peek detail_store.count end - def details + def call_details detail_store - .sort { |a, b| b[:duration] <=> a[:duration] } - .map(&method(:format_call_details)) end def format_call_details(call) diff --git a/lib/peek/views/rugged.rb b/lib/peek/views/rugged.rb new file mode 100644 index 00000000000..f0cd520fb8b --- /dev/null +++ b/lib/peek/views/rugged.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Peek + module Views + class Rugged < DetailedView + def results + return {} unless calls > 0 + + super + end + + private + + def duration + ::Gitlab::RuggedInstrumentation.query_time + end + + def calls + ::Gitlab::RuggedInstrumentation.query_count + end + + def call_details + ::Gitlab::RuggedInstrumentation.list_call_details + end + + def format_call_details(call) + call.merge(duration: (call[:duration] * 1000).round(3), + args: format_args(call[:args])) + end + + def format_args(args) + args.map do |arg| + # Needed to avoid infinite as_json calls + if arg.is_a?(Gitlab::Git::Repository) + arg.to_s + else + arg + end + end + end + end + end +end diff --git a/lib/quality/test_level.rb b/lib/quality/test_level.rb index 24d8eac200c..60d79b52680 100644 --- a/lib/quality/test_level.rb +++ b/lib/quality/test_level.rb @@ -14,6 +14,7 @@ module Quality finders frontend graphql + haml_lint helpers initializers javascripts diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index c881ad4cf12..bf18332a8eb 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -2,8 +2,6 @@ return if Rails.env.production? -Rake::Task["spec"].clear if Rake::Task.task_defined?('spec') - namespace :spec do desc 'GitLab | RSpec | Run unit tests' RSpec::Core::RakeTask.new(:unit, :rspec_opts) do |t, args| @@ -26,63 +24,8 @@ namespace :spec do t.rspec_opts = args[:rspec_opts] end - desc '[Deprecated] Use the "bin/rspec --tag api" instead' - task :api do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag @api) - ] - run_commands(cmds) - end - - desc '[Deprecated] Use the "spec:system" task instead' - task :feature do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag @feature) - ] - run_commands(cmds) - end - - desc '[Deprecated] Use "bin/rspec spec/models" instead' - task :models do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag @models) - ] - run_commands(cmds) - end - - desc '[Deprecated] Use "bin/rspec spec/services" instead' - task :services do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag @services) - ] - run_commands(cmds) - end - - desc '[Deprecated] Use "bin/rspec spec/lib" instead' - task :lib do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag @lib) - ] - run_commands(cmds) - end -end - -desc "GitLab | Run specs" -task :spec do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec) - ] - run_commands(cmds) -end - -def run_commands(cmds) - cmds.each do |cmd| - system({ 'RAILS_ENV' => 'test', 'force' => 'yes' }, *cmd) || raise("#{cmd} failed!") + desc 'Run the code examples in spec/requests/api' + RSpec::Core::RakeTask.new(:api) do |t| + t.pattern = 'spec/requests/api/**/*_spec.rb' end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7b1a3fd4d2c..cbab027d4a3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -95,11 +95,21 @@ msgid_plural "%d metrics" msgstr[0] "" msgstr[1] "" +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" +msgstr[1] "" + msgid "%d more comment" msgid_plural "%d more comments" msgstr[0] "" msgstr[1] "" +msgid "%d second" +msgid_plural "%d seconds" +msgstr[0] "" +msgstr[1] "" + msgid "%d staged change" msgid_plural "%d staged changes" msgstr[0] "" @@ -5288,6 +5298,9 @@ msgstr[1] "" msgid "Hide values" msgstr "" +msgid "Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0." +msgstr "" + msgid "Highest role:" msgstr "" @@ -5696,12 +5709,6 @@ msgstr "" msgid "Invocations" msgstr "" -msgid "Invoke Count" -msgstr "" - -msgid "Invoke Time" -msgstr "" - msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})" msgstr "" @@ -8713,6 +8720,9 @@ msgstr "" msgid "Rake Tasks Help" msgstr "" +msgid "Raw blob request rate limit per minute" +msgstr "" + msgid "Read more" msgstr "" @@ -8994,6 +9004,9 @@ msgstr "" msgid "Requests Profiles" msgstr "" +msgid "Requests to these domain(s)/address(es) on the local network will be allowed when local requests from hooks and services are disabled. IP ranges such as 1:0:0:0:0:0:0:0/124 or 127.0.0.0/28 are supported. Domain wildcards are not supported currently. Use comma, semicolon, or newline to separate multiple entries. The whitelist can hold a maximum of 4000 entries. Domains should use IDNA encoding. Ex: domain.com, 192.168.1.1, 127.0.0.0/28." +msgstr "" + msgid "Require all users in this group to setup Two-factor authentication" msgstr "" @@ -12129,6 +12142,9 @@ msgstr[1] "" msgid "When:" msgstr "" +msgid "Whitelist to allow requests to the local network from hooks and services" +msgstr "" + msgid "Who can see this group?" msgstr "" @@ -12432,6 +12448,9 @@ msgstr "" msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}." msgstr "" +msgid "You cannot access the raw file. Please wait a minute." +msgstr "" + msgid "You cannot impersonate a blocked user" msgstr "" @@ -12908,6 +12927,9 @@ msgstr "" msgid "is not an email you own" msgstr "" +msgid "is too long (maximum is 1000 entries)" +msgstr "" + msgid "issue" msgstr "" diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb index 0918445d119..64aab9be056 100644 --- a/qa/qa/page/project/new.rb +++ b/qa/qa/page/project/new.rb @@ -59,7 +59,7 @@ module QA end def set_visibility(visibility) - choose visibility + choose visibility.capitalize end def click_github_link diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index c0a6004fe27..93a82094776 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -8,6 +8,7 @@ module QA include Events::Project attr_writer :initialize_with_readme + attr_writer :visibility attribute :id attribute :name @@ -44,6 +45,7 @@ module QA @standalone = false @description = 'My awesome project' @initialize_with_readme = false + @visibility = 'public' end def name=(raw_name) @@ -60,7 +62,7 @@ module QA page.choose_test_namespace page.choose_name(@name) page.add_description(@description) - page.set_visibility('Public') + page.set_visibility(@visibility) page.enable_initialize_with_readme if @initialize_with_readme page.create_new_project end @@ -88,7 +90,7 @@ module QA post_body = { name: name, description: description, - visibility: 'public', + visibility: @visibility, initialize_with_readme: @initialize_with_readme } diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb index 21bfd2876a9..363980acc33 100644 --- a/qa/spec/spec_helper.rb +++ b/qa/spec/spec_helper.rb @@ -46,7 +46,7 @@ RSpec.configure do |config| if ENV['CI'] config.around do |example| - retry_times = example.metadata.keys.include?(:quarantine) ? 1 : 2 + retry_times = example.metadata.key?(:quarantine) ? 1 : 2 example.run_with_retry retry: retry_times end end diff --git a/scripts/lint-rugged b/scripts/lint-rugged index e1605b4501b..f40f0489c1a 100755 --- a/scripts/lint-rugged +++ b/scripts/lint-rugged @@ -13,9 +13,11 @@ ALLOWED = [ 'lib/gitlab/gitaly_client/storage_settings.rb', # Needed for logging + 'config/initializers/peek.rb', 'config/initializers/lograge.rb', 'lib/gitlab/grape_logging/loggers/perf_logger.rb', - 'lib/gitlab/rugged_instrumentation.rb' + 'lib/gitlab/rugged_instrumentation.rb', + 'lib/peek/views/rugged.rb' ].freeze rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 363ab2c6540..a1f6fb6bc0f 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -201,7 +201,7 @@ function deploy() { HELM_CMD=$(cat << EOF helm upgrade --install \ --wait \ - --timeout 600 \ + --timeout 900 \ --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ --set global.appConfig.enableUsagePing=false \ --set global.imagePullPolicy=Always \ diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 059354870b5..5675798ac33 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -33,6 +33,16 @@ describe Import::GithubController do expect(response).to have_http_status(200) end + + context 'when importing a CI/CD project' do + it 'always prompts for an access token' do + allow(controller).to receive(:github_import_configured?).and_return(true) + + get :new, params: { ci_cd_only: true } + + expect(response).to render_template(:new) + end + end end describe "GET callback" do diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 97acd47b4da..8ee3168273f 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe Projects::RawController do + include RepoHelpers + let(:project) { create(:project, :public, :repository) } describe 'GET #show' do @@ -46,5 +48,98 @@ describe Projects::RawController do let(:filename) { 'lfs_object.iso' } let(:filepath) { "be93687/files/lfs/#{filename}" } end + + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + let(:file_path) { 'master/README.md' } + + before do + stub_application_setting(raw_blob_request_limit: 5) + end + + it 'prevents from accessing the raw file' do + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + end + + it 'logs the event on auth.log' do + attributes = { + message: 'Action_Rate_Limiter_Request', + env: :raw_blob_request_limit, + ip: '0.0.0.0', + request_method: 'GET', + fullpath: "/#{project.full_path}/raw/#{file_path}" + } + + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + + execute_raw_requests(requests: 6, project: project, file_path: file_path) + end + + context 'when the request uses a different version of a commit' do + it 'prevents from accessing the raw file' do + # 3 times with the normal sha + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: file_path) + + # 3 times with the modified version + modified_sha = commit_sha.gsub(commit_sha[0..5], commit_sha[0..5].upcase) + modified_path = "#{modified_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: modified_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, modified_path)) + end + end + + context 'when the throttling has been disabled' do + before do + stub_application_setting(raw_blob_request_limit: 0) + end + + it 'does not prevent from accessing the raw file' do + execute_raw_requests(requests: 10, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + + context 'with case-sensitive files' do + it 'prevents from accessing the specific file' do + create_file_in_repo(project, 'master', 'master', 'readme.md', 'Add readme.md') + create_file_in_repo(project, 'master', 'master', 'README.md', 'Add README.md') + + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/readme.md" + + # Accessing downcase version of readme + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + + # Accessing upcase version of readme + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 1, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + end + end + + def execute_raw_requests(requests:, project:, file_path:) + requests.times do + get :show, params: { + namespace_id: project.namespace, + project_id: project, + id: file_path + } + end end end diff --git a/spec/controllers/user_callouts_controller_spec.rb b/spec/controllers/user_callouts_controller_spec.rb index babc93a83e5..07eaff2da09 100644 --- a/spec/controllers/user_callouts_controller_spec.rb +++ b/spec/controllers/user_callouts_controller_spec.rb @@ -13,7 +13,7 @@ describe UserCalloutsController do subject { post :create, params: { feature_name: feature_name }, format: :json } context 'with valid feature name' do - let(:feature_name) { UserCallout.feature_names.keys.first } + let(:feature_name) { UserCallout.feature_names.first.first } context 'when callout entry does not exist' do it 'creates a callout entry with dismissed state' do @@ -28,7 +28,7 @@ describe UserCalloutsController do end context 'when callout entry already exists' do - let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.keys.first, user: user) } + let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.first.first, user: user) } it 'returns success' do subject diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js index dc886d0db3b..b6f1aef9ce4 100644 --- a/spec/frontend/lib/utils/text_utility_spec.js +++ b/spec/frontend/lib/utils/text_utility_spec.js @@ -29,20 +29,6 @@ describe('text_utility', () => { }); }); - describe('pluralize', () => { - it('should pluralize given string', () => { - expect(textUtils.pluralize('test', 2)).toBe('tests'); - }); - - it('should pluralize when count is 0', () => { - expect(textUtils.pluralize('test', 0)).toBe('tests'); - }); - - it('should not pluralize when count is 1', () => { - expect(textUtils.pluralize('test', 1)).toBe('test'); - }); - }); - describe('dasherize', () => { it('should replace underscores with dashes', () => { expect(textUtils.dasherize('foo_bar_foo')).toEqual('foo-bar-foo'); diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 314305d7a8e..4f1cab38f34 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -3,10 +3,8 @@ require 'spec_helper' describe LabelsHelper do describe '#show_label_issuables_link?' do shared_examples 'a valid response to show_label_issuables_link?' do |issuables_type, when_enabled = true, when_disabled = false| - let(:context_project) { project } - context "when asking for a #{issuables_type} link" do - subject { show_label_issuables_link?(label.present(issuable_subject: nil), issuables_type, project: context_project) } + subject { show_label_issuables_link?(label.present(issuable_subject: nil), issuables_type) } context "when #{issuables_type} are enabled for the project" do let(:project) { create(:project, "#{issuables_type}_access_level": ProjectFeature::ENABLED) } @@ -39,27 +37,11 @@ describe LabelsHelper do let(:label) { create(:group_label, group: group, title: 'bug') } context 'when asking for an issue link' do - context 'in the context of a project' do - it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true - end - - context 'in the context of a group' do - let(:context_project) { nil } - - it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true - end + it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true end context 'when asking for a merge requests link' do - context 'in the context of a project' do - it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true - end - - context 'in the context of a group' do - let(:context_project) { nil } - - it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true - end + it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true end end end diff --git a/spec/lib/gitlab/action_rate_limiter_spec.rb b/spec/lib/gitlab/action_rate_limiter_spec.rb index 542fc03e555..cf266a25819 100644 --- a/spec/lib/gitlab/action_rate_limiter_spec.rb +++ b/spec/lib/gitlab/action_rate_limiter_spec.rb @@ -1,11 +1,9 @@ require 'spec_helper' -describe Gitlab::ActionRateLimiter do +describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do let(:redis) { double('redis') } let(:user) { create(:user) } let(:project) { create(:project) } - let(:key) { [user, project] } - let(:cache_key) { "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" } subject { described_class.new(action: :test_action, expiry_time: 100) } @@ -13,17 +11,98 @@ describe Gitlab::ActionRateLimiter do allow(Gitlab::Redis::Cache).to receive(:with).and_yield(redis) end - it 'increases the throttle count and sets the expire time' do - expect(redis).to receive(:incr).with(cache_key).and_return(1) - expect(redis).to receive(:expire).with(cache_key, 100) + shared_examples 'action rate limiter' do + it 'increases the throttle count and sets the expiration time' do + expect(redis).to receive(:incr).with(cache_key).and_return(1) + expect(redis).to receive(:expire).with(cache_key, 100) - expect(subject.throttled?(key, 1)).to be false + expect(subject.throttled?(key, 1)).to be_falsy + end + + it 'returns true if the key is throttled' do + expect(redis).to receive(:incr).with(cache_key).and_return(2) + expect(redis).not_to receive(:expire) + + expect(subject.throttled?(key, 1)).to be_truthy + end + + context 'when throttling is disabled' do + it 'returns false and does not set expiration time' do + expect(redis).not_to receive(:incr) + expect(redis).not_to receive(:expire) + + expect(subject.throttled?(key, 0)).to be_falsy + end + end + end + + context 'when the key is an array of only ActiveRecord models' do + let(:key) { [user, project] } + + let(:cache_key) do + "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" + end + + it_behaves_like 'action rate limiter' + end + + context 'when they key a combination of ActiveRecord models and strings' do + let(:project) { create(:project, :public, :repository) } + let(:commit) { project.repository.commit } + let(:path) { 'app/controllers/groups_controller.rb' } + let(:key) { [project, commit, path] } + + let(:cache_key) do + "action_rate_limiter:test_action:project:#{project.id}:commit:#{commit.sha}:#{path}" + end + + it_behaves_like 'action rate limiter' end - it 'returns true if the key is throttled' do - expect(redis).to receive(:incr).with(cache_key).and_return(2) - expect(redis).not_to receive(:expire) + describe '#log_request' do + let(:file_path) { 'master/README.md' } + let(:type) { :raw_blob_request_limit } + let(:fullpath) { "/#{project.full_path}/raw/#{file_path}" } + + let(:request) do + double('request', ip: '127.0.0.1', request_method: 'GET', fullpath: fullpath) + end + + let(:base_attributes) do + { + message: 'Action_Rate_Limiter_Request', + env: type, + ip: '127.0.0.1', + request_method: 'GET', + fullpath: fullpath + } + end + + context 'without a current user' do + let(:current_user) { nil } + + it 'logs information to auth.log' do + expect(Gitlab::AuthLogger).to receive(:error).with(base_attributes).once + + subject.log_request(request, type, current_user) + end + end + + context 'with a current_user' do + let(:current_user) { create(:user) } + + let(:attributes) do + base_attributes.merge({ + user_id: current_user.id, + username: current_user.username + }) + end + + it 'logs information to auth.log' do + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once - expect(subject.throttled?(key, 1)).to be true + subject.log_request(request, type, current_user) + end + end end end diff --git a/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb index 3991c737a26..0c1be4b4610 100644 --- a/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb @@ -23,18 +23,4 @@ describe Gitlab::Database::Count::ExactCountStrategy do expect(subject).to eq({}) end end - - describe '.enabled?' do - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is enabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_truthy - end - end end diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb index bd3c66d0548..a528707c9dc 100644 --- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb @@ -48,18 +48,4 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do end end end - - describe '.enabled?' do - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is disabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_falsey - end - end end diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb index 40d810b195b..a57f033b5ed 100644 --- a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb @@ -56,22 +56,4 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do end end end - - describe '.enabled?' do - before do - stub_feature_flags(tablesample_counts: true) - end - - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is disabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_falsey - end - end end diff --git a/spec/lib/gitlab/database/count_spec.rb b/spec/lib/gitlab/database/count_spec.rb index 1d096b8fa7c..71d6633f62f 100644 --- a/spec/lib/gitlab/database/count_spec.rb +++ b/spec/lib/gitlab/database/count_spec.rb @@ -9,24 +9,13 @@ describe Gitlab::Database::Count do let(:models) { [Project, Identity] } context '.approximate_counts' do - context 'selecting strategies' do - let(:strategies) { [double('s1', enabled?: true), double('s2', enabled?: false)] } - - it 'uses only enabled strategies' do - expect(strategies[0]).to receive(:new).and_return(double('strategy1', count: {})) - expect(strategies[1]).not_to receive(:new) - - described_class.approximate_counts(models, strategies: strategies) - end - end - context 'fallbacks' do subject { described_class.approximate_counts(models, strategies: strategies) } let(:strategies) do [ - double('s1', enabled?: true, new: first_strategy), - double('s2', enabled?: true, new: second_strategy) + double('s1', new: first_strategy), + double('s2', new: second_strategy) ] end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 41b898df112..dccd50bc472 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -186,6 +186,12 @@ describe Gitlab::Git::Repository, :seed_helper do it { is_expected.to be < 2 } end + describe '#to_s' do + subject { repository.to_s } + + it { is_expected.to eq("<Gitlab::Git::Repository: group/project>") } + end + describe '#object_directory_size' do before do allow(repository.gitaly_repository_client) diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb index e437647c258..1a4168f7317 100644 --- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb +++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb @@ -16,7 +16,13 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do end subject(:wrapper) do - klazz = Class.new { include Gitlab::Git::RuggedImpl::UseRugged } + klazz = Class.new do + include Gitlab::Git::RuggedImpl::UseRugged + + def rugged_test(ref, test_number) + end + end + klazz.new end @@ -25,6 +31,23 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {}) end + context '#execute_rugged_call', :request_store do + let(:args) { ['refs/heads/master', 1] } + + before do + allow(Gitlab::RuggedInstrumentation).to receive(:peek_enabled?).and_return(true) + end + + it 'instruments Rugged call' do + expect(subject).to receive(:rugged_test).with(args) + + subject.execute_rugged_call(:rugged_test, args) + + expect(Gitlab::RuggedInstrumentation.query_count).to eq(1) + expect(Gitlab::RuggedInstrumentation.list_call_details.count).to eq(1) + end + end + context 'when feature flag is not persisted' do before do allow(Feature).to receive(:persisted?).with(feature_flag).and_return(false) diff --git a/spec/lib/gitlab/import_export/project.group.json b/spec/lib/gitlab/import_export/project.group.json index 1a561e81e4a..66f5bb4c87b 100644 --- a/spec/lib/gitlab/import_export/project.group.json +++ b/spec/lib/gitlab/import_export/project.group.json @@ -19,7 +19,7 @@ "labels": [ { "id": 2, - "title": "project label", + "title": "A project label", "color": "#428bca", "project_id": 8, "created_at": "2016-07-22T08:55:44.161Z", @@ -105,7 +105,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 6, - "title": "project label", + "title": "A project label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", @@ -162,7 +162,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 2, - "title": "project label", + "title": "A project label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 3b7de185cf1..b9f6595762b 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -272,7 +272,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end it 'has label priorities' do - expect(project.labels.first.priorities).not_to be_empty + expect(project.labels.find_by(title: 'A project label').priorities).not_to be_empty end it 'has milestones' do @@ -325,7 +325,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project correctly', issues: 1, - labels: 1, + labels: 2, milestones: 1, first_issue_labels: 1, services: 1 @@ -402,7 +402,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project successfully' it_behaves_like 'restores project correctly', issues: 2, - labels: 1, + labels: 2, milestones: 2, first_issue_labels: 1 diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index f8b0cbfb6f6..93194de4a1b 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -220,53 +220,53 @@ describe Gitlab::UrlBlocker do end let(:fake_domain) { 'www.fakedomain.fake' } - context 'true (default)' do + shared_examples 'allows local requests' do |url_blocker_attributes| it 'does not block urls from private networks' do local_ips.each do |ip| - stub_domain_resolv(fake_domain, ip) - - expect(described_class).not_to be_blocked_url("http://#{fake_domain}") - - unstub_domain_resolv + stub_domain_resolv(fake_domain, ip) do + expect(described_class).not_to be_blocked_url("http://#{fake_domain}", url_blocker_attributes) + end - expect(described_class).not_to be_blocked_url("http://#{ip}") + expect(described_class).not_to be_blocked_url("http://#{ip}", url_blocker_attributes) end end it 'allows localhost endpoints' do - expect(described_class).not_to be_blocked_url('http://0.0.0.0', allow_localhost: true) - expect(described_class).not_to be_blocked_url('http://localhost', allow_localhost: true) - expect(described_class).not_to be_blocked_url('http://127.0.0.1', allow_localhost: true) + expect(described_class).not_to be_blocked_url('http://0.0.0.0', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://localhost', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://127.0.0.1', url_blocker_attributes) end it 'allows loopback endpoints' do - expect(described_class).not_to be_blocked_url('http://127.0.0.2', allow_localhost: true) + expect(described_class).not_to be_blocked_url('http://127.0.0.2', url_blocker_attributes) end it 'allows IPv4 link-local endpoints' do - expect(described_class).not_to be_blocked_url('http://169.254.169.254') - expect(described_class).not_to be_blocked_url('http://169.254.168.100') + expect(described_class).not_to be_blocked_url('http://169.254.169.254', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://169.254.168.100', url_blocker_attributes) end it 'allows IPv6 link-local endpoints' do - expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]') - expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.169.254]') - expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a9fe]') - expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]') - expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.168.100]') - expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a864]') - expect(described_class).not_to be_blocked_url('http://[fe80::c800:eff:fe74:8]') + expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.169.254]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a9fe]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.168.100]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a864]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[fe80::c800:eff:fe74:8]', url_blocker_attributes) end end + context 'true (default)' do + it_behaves_like 'allows local requests', { allow_localhost: true, allow_local_network: true } + end + context 'false' do it 'blocks urls from private networks' do local_ips.each do |ip| - stub_domain_resolv(fake_domain, ip) - - expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false) - - unstub_domain_resolv + stub_domain_resolv(fake_domain, ip) do + expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false) + end expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false) end @@ -286,15 +286,169 @@ describe Gitlab::UrlBlocker do expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a864]', allow_local_network: false) expect(described_class).to be_blocked_url('http://[fe80::c800:eff:fe74:8]', allow_local_network: false) end + + context 'when local domain/IP is whitelisted' do + let(:url_blocker_attributes) do + { + allow_localhost: false, + allow_local_network: false + } + end + + before do + stub_application_setting(outbound_local_requests_whitelist: whitelist) + end + + context 'with IPs in whitelist' do + let(:whitelist) do + [ + '0.0.0.0', + '127.0.0.1', + '127.0.0.2', + '192.168.1.1', + '192.168.1.2', + '0:0:0:0:0:ffff:192.168.1.2', + '::ffff:c0a8:102', + '10.0.0.2', + '0:0:0:0:0:ffff:10.0.0.2', + '::ffff:a00:2', + '172.16.0.2', + '0:0:0:0:0:ffff:172.16.0.2', + '::ffff:ac10:20', + 'feef::1', + 'fee2::', + 'fc00:bf8b:e62c:abcd:abcd:aaaa:aaaa:aaaa', + '0:0:0:0:0:ffff:169.254.169.254', + '::ffff:a9fe:a9fe', + '::ffff:169.254.168.100', + '::ffff:a9fe:a864', + 'fe80::c800:eff:fe74:8', + + # garbage IPs + '45645632345', + 'garbage456:more345gar:bage' + ] + end + + it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false } + + it 'whitelists IP when dns_rebind_protection is disabled' do + stub_domain_resolv('example.com', '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://example.com", + url_blocker_attributes.merge(dns_rebind_protection: false)) + end + end + end + + context 'with domains in whitelist' do + let(:whitelist) do + [ + 'www.example.com', + 'example.com', + 'xn--itlab-j1a.com', + 'garbage$^$%#$^&$' + ] + end + + it 'allows domains present in whitelist' do + domain = 'example.com' + subdomain1 = 'www.example.com' + subdomain2 = 'subdomain.example.com' + + stub_domain_resolv(domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{domain}", + url_blocker_attributes) + end + + stub_domain_resolv(subdomain1, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{subdomain1}", + url_blocker_attributes) + end + + # subdomain2 is not part of the whitelist so it should be blocked + stub_domain_resolv(subdomain2, '192.168.1.1') do + expect(described_class).to be_blocked_url("http://#{subdomain2}", + url_blocker_attributes) + end + end + + it 'works with unicode and idna encoded domains' do + unicode_domain = 'ğitlab.com' + idna_encoded_domain = 'xn--itlab-j1a.com' + + stub_domain_resolv(unicode_domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{unicode_domain}", + url_blocker_attributes) + end + + stub_domain_resolv(idna_encoded_domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{idna_encoded_domain}", + url_blocker_attributes) + end + end + end + + context 'with ip ranges in whitelist' do + let(:ipv4_range) { '127.0.0.0/28' } + let(:ipv6_range) { 'fd84:6d02:f6d8:c89e::/124' } + + let(:whitelist) do + [ + ipv4_range, + ipv6_range + ] + end + + it 'blocks ipv4 range when not in whitelist' do + stub_application_setting(outbound_local_requests_whitelist: []) + + IPAddr.new(ipv4_range).to_range.to_a.each do |ip| + expect(described_class).to be_blocked_url("http://#{ip}", + url_blocker_attributes) + end + end + + it 'allows all ipv4s in the range when in whitelist' do + IPAddr.new(ipv4_range).to_range.to_a.each do |ip| + expect(described_class).not_to be_blocked_url("http://#{ip}", + url_blocker_attributes) + end + end + + it 'blocks ipv6 range when not in whitelist' do + stub_application_setting(outbound_local_requests_whitelist: []) + + IPAddr.new(ipv6_range).to_range.to_a.each do |ip| + expect(described_class).to be_blocked_url("http://[#{ip}]", + url_blocker_attributes) + end + end + + it 'allows all ipv6s in the range when in whitelist' do + IPAddr.new(ipv6_range).to_range.to_a.each do |ip| + expect(described_class).not_to be_blocked_url("http://[#{ip}]", + url_blocker_attributes) + end + end + + it 'blocks IPs outside the range' do + expect(described_class).to be_blocked_url("http://[fd84:6d02:f6d8:c89e:0:0:1:f]", + url_blocker_attributes) + + expect(described_class).to be_blocked_url("http://127.0.1.15", + url_blocker_attributes) + end + end + end end - def stub_domain_resolv(domain, ip) + def stub_domain_resolv(domain, ip, &block) address = double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false, ipv4_loopback?: false, ipv6_loopback?: false, ipv4?: false) allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([address]) allow(address).to receive(:ipv6_v4mapped?).and_return(false) - end - def unstub_domain_resolv + yield + allow(Addrinfo).to receive(:getaddrinfo).and_call_original end end diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index 4645339f439..0c20b3aa4c8 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -231,4 +231,23 @@ describe Gitlab::Utils do end end end + + describe '.string_to_ip_object' do + it 'returns nil when string is nil' do + expect(described_class.string_to_ip_object(nil)).to eq(nil) + end + + it 'returns nil when string is invalid IP' do + expect(described_class.string_to_ip_object('invalid ip')).to eq(nil) + expect(described_class.string_to_ip_object('')).to eq(nil) + end + + it 'returns IP object when string is valid IP' do + expect(described_class.string_to_ip_object('192.168.1.1')).to eq(IPAddr.new('192.168.1.1')) + expect(described_class.string_to_ip_object('::ffff:a9fe:a864')).to eq(IPAddr.new('::ffff:a9fe:a864')) + expect(described_class.string_to_ip_object('[::ffff:a9fe:a864]')).to eq(IPAddr.new('::ffff:a9fe:a864')) + expect(described_class.string_to_ip_object('127.0.0.0/28')).to eq(IPAddr.new('127.0.0.0/28')) + expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124')) + end + end end diff --git a/spec/lib/peek/views/rugged_spec.rb b/spec/lib/peek/views/rugged_spec.rb new file mode 100644 index 00000000000..8bf996fc6bc --- /dev/null +++ b/spec/lib/peek/views/rugged_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Peek::Views::Rugged, :request_store do + subject { described_class.new } + + let(:project) { create(:project) } + + before do + allow(Gitlab::RuggedInstrumentation).to receive(:peek_enabled?).and_return(true) + end + + it 'returns no results' do + expect(subject.results).to eq({}) + end + + it 'returns aggregated results' do + ::Gitlab::RuggedInstrumentation.query_time += 1.234 + ::Gitlab::RuggedInstrumentation.increment_query_count + ::Gitlab::RuggedInstrumentation.increment_query_count + + ::Gitlab::RuggedInstrumentation.add_call_details(feature: :rugged_test, + args: [project.repository.raw, 'HEAD'], + duration: 0.123) + ::Gitlab::RuggedInstrumentation.add_call_details(feature: :rugged_test2, + args: [project.repository.raw, 'refs/heads/master'], + duration: 0.456) + + results = subject.results + expect(results[:calls]).to eq(2) + expect(results[:duration]).to eq('1234.00ms') + expect(results[:details].count).to eq(2) + + expect(results[:details][0][:args]).to eq([project.repository.raw.to_s, "refs/heads/master"]) + expect(results[:details][1][:args]).to eq([project.repository.raw.to_s, "HEAD"]) + end +end diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb index 3465c3a050b..59870ce44a7 100644 --- a/spec/lib/quality/test_level_spec.rb +++ b/spec/lib/quality/test_level_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a pattern' do expect(subject.pattern(:unit)) - .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") + .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") end end @@ -47,7 +47,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a regexp' do expect(subject.regexp(:unit)) - .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) + .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index ab6f6dfe720..bd87bbd8d68 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -37,6 +37,17 @@ describe ApplicationSetting do it { is_expected.not_to allow_value("myemail@example.com").for(:lets_encrypt_notification_email) } it { is_expected.to allow_value("myemail@test.example.com").for(:lets_encrypt_notification_email) } + it { is_expected.to allow_value(['192.168.1.1'] * 1_000).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['192.168.1.1'] * 1_001).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['1' * 255]).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['1' * 256]).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['ğitlab.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['xn--itlab-j1a.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['<h1></h1>']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['gitlab.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(nil).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value([]).for(:outbound_local_requests_whitelist) } + context "when user accepted let's encrypt terms of service" do before do setting.update(lets_encrypt_terms_of_service_accepted: true) diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 5174c590a10..c2e2298823e 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -193,4 +193,17 @@ describe Label do expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2]) end end + + describe '#templates' do + context 'with invalid template labels' do + it 'returns only valid template labels' do + create(:label) + # Project labels should not have template set to true + create(:label, template: true) + valid_template_label = described_class.create!(title: 'test', template: true, type: nil) + + expect(described_class.templates).to eq([valid_template_label]) + end + end + end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7bdd2367a68..da9e56ef897 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -15,7 +15,7 @@ describe ProjectAutoDevops do it { is_expected.to respond_to(:updated_at) } describe '#predefined_variables' do - let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: domain) } + let(:auto_devops) { build_stubbed(:project_auto_devops, project: project) } context 'when deploy_strategy is manual' do let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a083eee05e..7d458324c20 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3117,11 +3117,8 @@ describe Project do let(:project) { create(:project) } it 'shows full error updating an invalid MR' do - error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\ - ' Validate fork Source project is not a fork of the target project' - expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) } - .to raise_error(ActiveRecord::RecordNotSaved, error_message) + .to raise_error(ActiveRecord::RecordInvalid, /Failed to set merge_requests:/) end it 'updates the project successfully' do diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb index a641828faa5..33cd1f37ff6 100644 --- a/spec/services/application_settings/update_service_spec.rb +++ b/spec/services/application_settings/update_service_spec.rb @@ -180,4 +180,20 @@ describe ApplicationSettings::UpdateService do described_class.new(application_settings, admin, { home_page_url: 'http://foo.bar' }).execute end end + + context 'when raw_blob_request_limit is passsed' do + let(:params) do + { + raw_blob_request_limit: 600 + } + end + + it 'updates raw_blob_request_limit value' do + subject.execute + + application_settings.reload + + expect(application_settings.raw_blob_request_limit).to eq(600) + end + end end diff --git a/spec/support/shared_examples/application_setting_examples.rb b/spec/support/shared_examples/application_setting_examples.rb index e7ec24c5b7e..2c600785ad3 100644 --- a/spec/support/shared_examples/application_setting_examples.rb +++ b/spec/support/shared_examples/application_setting_examples.rb @@ -1,58 +1,54 @@ # frozen_string_literal: true -RSpec.shared_examples 'application settings examples' do - context 'restricted signup domains' do - it 'sets single domain' do - setting.domain_whitelist_raw = 'example.com' - expect(setting.domain_whitelist).to eq(['example.com']) - end +RSpec.shared_examples 'string of domains' do |attribute| + it 'sets single domain' do + setting.method("#{attribute}_raw=").call('example.com') + expect(setting.method(attribute).call).to eq(['example.com']) + end - it 'sets multiple domains with spaces' do - setting.domain_whitelist_raw = 'example.com *.example.com' - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with spaces' do + setting.method("#{attribute}_raw=").call('example.com *.example.com') + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) + end - it 'sets multiple domains with newlines and a space' do - setting.domain_whitelist_raw = "example.com\n *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with newlines and a space' do + setting.method("#{attribute}_raw=").call("example.com\n *.example.com") + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) + end - it 'sets multiple domains with commas' do - setting.domain_whitelist_raw = "example.com, *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with commas' do + setting.method("#{attribute}_raw=").call("example.com, *.example.com") + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) end - context 'blacklisted signup domains' do - it 'sets single domain' do - setting.domain_blacklist_raw = 'example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com') - end + it 'sets multiple domains with semicolon' do + setting.method("#{attribute}_raw=").call("example.com; *.example.com") + expect(setting.method(attribute).call).to contain_exactly('example.com', '*.example.com') + end - it 'sets multiple domains with spaces' do - setting.domain_blacklist_raw = 'example.com *.example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'sets multiple domains with mixture of everything' do + setting.method("#{attribute}_raw=").call("example.com; *.example.com\n test.com\sblock.com yes.com") + expect(setting.method(attribute).call).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') + end - it 'sets multiple domains with newlines and a space' do - setting.domain_blacklist_raw = "example.com\n *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'removes duplicates' do + setting.method("#{attribute}_raw=").call("example.com; example.com; 127.0.0.1; 127.0.0.1") + expect(setting.method(attribute).call).to contain_exactly('example.com', '127.0.0.1') + end - it 'sets multiple domains with commas' do - setting.domain_blacklist_raw = "example.com, *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'does not fail with garbage values' do + setting.method("#{attribute}_raw=").call("example;34543:garbage:fdh5654;") + expect(setting.method(attribute).call).to contain_exactly('example', '34543:garbage:fdh5654') + end +end - it 'sets multiple domains with semicolon' do - setting.domain_blacklist_raw = "example.com; *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end +RSpec.shared_examples 'application settings examples' do + context 'restricted signup domains' do + it_behaves_like 'string of domains', :domain_whitelist + end - it 'sets multiple domains with mixture of everything' do - setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') - end + context 'blacklisted signup domains' do + it_behaves_like 'string of domains', :domain_blacklist it 'sets multiple domain with file' do setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt')) @@ -60,6 +56,27 @@ RSpec.shared_examples 'application settings examples' do end end + context 'outbound_local_requests_whitelist' do + it_behaves_like 'string of domains', :outbound_local_requests_whitelist + end + + context 'outbound_local_requests_whitelist_arrays' do + it 'separates the IPs and domains' do + setting.outbound_local_requests_whitelist = [ + '192.168.1.1', '127.0.0.0/28', 'www.example.com', 'example.com', + '::ffff:a00:2', '1:0:0:0:0:0:0:0/124', 'subdomain.example.com' + ] + + ip_whitelist = [ + IPAddr.new('192.168.1.1'), IPAddr.new('127.0.0.0/8'), + IPAddr.new('::ffff:a00:2'), IPAddr.new('1:0:0:0:0:0:0:0/124') + ] + domain_whitelist = ['www.example.com', 'example.com', 'subdomain.example.com'] + + expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(ip_whitelist, domain_whitelist) + end + end + describe 'usage ping settings' do context 'when the usage ping is disabled in gitlab.yml' do before do |