diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-22 12:08:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-22 12:08:09 +0300 |
commit | 4a3ba3e5f261eb09e6b2b4fd44373e7a1c454a72 (patch) | |
tree | 1a94467252ebcc5575c7de6a3590360ce05b9967 /app | |
parent | 707c0eca50cf9a5290806ce637be30f4c7288def (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
52 files changed, 320 insertions, 124 deletions
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index c72a8b2b0d0..2a8ed076ae2 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -14,6 +14,7 @@ import Editor from '../lib/editor'; import FileTemplatesBar from './file_templates/bar.vue'; import { __ } from '~/locale'; import { extractMarkdownImagesFromEntries } from '../stores/utils'; +import { addFinalNewline } from '../utils'; export default { components: { @@ -31,6 +32,7 @@ export default { return { content: '', images: {}, + addFinalNewline: true, }; }, computed: { @@ -247,13 +249,14 @@ export default { this.model.onChange(model => { const { file } = model; + if (!file.active) return; - if (file.active) { - this.changeFileContent({ - path: file.path, - content: model.getModel().getValue(), - }); - } + const monacoModel = model.getModel(); + const content = monacoModel.getValue(); + this.changeFileContent({ + path: file.path, + content: this.addFinalNewline ? addFinalNewline(content, monacoModel.getEOL()) : content, + }); }); // Handle Cursor Position diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index da7d4a44bde..03146e8a315 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -4,7 +4,7 @@ import eventHub from '../../eventhub'; import service from '../../services'; import * as types from '../mutation_types'; import router from '../../ide_router'; -import { addFinalNewlineIfNeeded, setPageTitleForFile } from '../utils'; +import { setPageTitleForFile } from '../utils'; import { viewerTypes, stageKeys } from '../../constants'; export const closeFile = ({ commit, state, dispatch }, file) => { @@ -152,7 +152,7 @@ export const changeFileContent = ({ commit, state, getters }, { path, content }) const file = state.entries[path]; commit(types.UPDATE_FILE_CONTENT, { path, - content: addFinalNewlineIfNeeded(content), + content, }); const indexOfChangedFile = state.changedFiles.findIndex(f => f.path === path); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 56671142bd4..34148df821f 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -272,10 +272,6 @@ export const pathsAreEqual = (a, b) => { return cleanA === cleanB; }; -// if the contents of a file dont end with a newline, this function adds a newline -export const addFinalNewlineIfNeeded = content => - content.charAt(content.length - 1) !== '\n' ? `${content}\n` : content; - export function extractMarkdownImagesFromEntries(mdFile, entries) { /** * Regex to identify an image tag in markdown, like: diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js index 7573bfd963c..64be1f59887 100644 --- a/app/assets/javascripts/ide/utils.js +++ b/app/assets/javascripts/ide/utils.js @@ -76,3 +76,21 @@ export function registerLanguages(def, ...defs) { } export const otherSide = side => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT); + +export function addFinalNewline(content, eol = '\n') { + return content.slice(-eol.length) !== eol ? `${content}${eol}` : content; +} + +export function getPathParents(path) { + const pathComponents = path.split('/'); + const paths = []; + while (pathComponents.length) { + pathComponents.pop(); + + let parentPath = pathComponents.join('/'); + if (parentPath.startsWith('/')) parentPath = parentPath.slice(1); + if (parentPath) paths.push(parentPath); + } + + return paths; +} diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss index 2de319b51bb..295af0311ae 100644 --- a/app/assets/stylesheets/page_bundles/ide.scss +++ b/app/assets/stylesheets/page_bundles/ide.scss @@ -899,7 +899,7 @@ $ide-commit-header-height: 48px; @include ide-trace-view(); svg { - --svg-status-bg: var(--ide-background, $white); + --svg-status-bg: var(--ide-background, #{$white}); } .empty-state { diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index b8663bc59f2..fef3c6cf424 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -113,7 +113,7 @@ class Projects::ArtifactsController < Projects::ApplicationController def build @build ||= begin - build = build_from_id || build_from_ref + build = build_from_id || build_from_sha || build_from_ref build&.present(current_user: current_user) end end @@ -127,7 +127,8 @@ class Projects::ArtifactsController < Projects::ApplicationController project.builds.find_by_id(params[:job_id]) if params[:job_id] end - def build_from_ref + def build_from_sha + return if params[:job].blank? return unless @ref_name commit = project.commit(@ref_name) @@ -136,6 +137,13 @@ class Projects::ArtifactsController < Projects::ApplicationController project.latest_successful_build_for_sha(params[:job], commit.id) end + def build_from_ref + return if params[:job].blank? + return unless @ref_name + + project.latest_successful_build_for_ref(params[:job], @ref_name) + end + def artifacts_file @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive) end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index b9f0e3582df..d3bdac5be69 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -261,6 +261,8 @@ module ApplicationSettingsHelper :sourcegraph_enabled, :sourcegraph_url, :sourcegraph_public_only, + :spam_check_endpoint_enabled, + :spam_check_endpoint_url, :terminal_max_session_time, :terms, :throttle_authenticated_api_enabled, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index b29d6731b08..e88d9ba4e63 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -301,6 +301,13 @@ class ApplicationSetting < ApplicationRecord numericality: { greater_than: 0, less_than_or_equal_to: 10 }, if: :external_authorization_service_enabled + validates :spam_check_endpoint_url, + addressable_url: true, allow_blank: true + + validates :spam_check_endpoint_url, + presence: true, + if: :spam_check_endpoint_enabled + validates :external_auth_client_key, presence: true, if: -> (setting) { setting.external_auth_client_cert.present? } diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 221e4d5e0c6..69292fd11fb 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -115,6 +115,8 @@ module ApplicationSettingImplementation sourcegraph_enabled: false, sourcegraph_url: nil, sourcegraph_public_only: true, + spam_check_endpoint_enabled: false, + spam_check_endpoint_url: nil, minimum_password_length: DEFAULT_MINIMUM_PASSWORD_LENGTH, namespace_storage_size_limit: 0, terminal_max_session_time: 0, @@ -151,7 +153,7 @@ module ApplicationSettingImplementation snowplow_app_id: nil, snowplow_iglu_registry_url: nil, custom_http_clone_url_root: nil, - productivity_analytics_start_date: Time.now, + productivity_analytics_start_date: Time.current, snippet_size_limit: 50.megabytes } end diff --git a/app/models/board_group_recent_visit.rb b/app/models/board_group_recent_visit.rb index 2f1cd830791..979f0e1ab92 100644 --- a/app/models/board_group_recent_visit.rb +++ b/app/models/board_group_recent_visit.rb @@ -14,7 +14,7 @@ class BoardGroupRecentVisit < ApplicationRecord def self.visited!(user, board) visit = find_or_create_by(user: user, group: board.group, board: board) - visit.touch if visit.updated_at < Time.now + visit.touch if visit.updated_at < Time.current rescue ActiveRecord::RecordNotUnique retry end diff --git a/app/models/board_project_recent_visit.rb b/app/models/board_project_recent_visit.rb index 236d88e909c..509c8f97b83 100644 --- a/app/models/board_project_recent_visit.rb +++ b/app/models/board_project_recent_visit.rb @@ -14,7 +14,7 @@ class BoardProjectRecentVisit < ApplicationRecord def self.visited!(user, board) visit = find_or_create_by(user: user, project: board.project, board: board) - visit.touch if visit.updated_at < Time.now + visit.touch if visit.updated_at < Time.current rescue ActiveRecord::RecordNotUnique retry end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 645b87ce68c..d6f6515ed23 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -137,8 +137,8 @@ module Ci .includes(:metadata, :job_artifacts_metadata) end - scope :with_artifacts_not_expired, ->() { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) } - scope :with_expired_artifacts, ->() { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.now) } + scope :with_artifacts_not_expired, ->() { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.current) } + scope :with_expired_artifacts, ->() { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.current) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) } scope :scheduled_actions, ->() { where(when: :delayed, status: COMPLETED_STATUSES + %i[scheduled]) } @@ -259,7 +259,7 @@ module Ci end before_transition any => :waiting_for_resource do |build| - build.waiting_for_resource_at = Time.now + build.waiting_for_resource_at = Time.current end before_transition on: :enqueue_waiting_for_resource do |build| @@ -713,7 +713,7 @@ module Ci end def needs_touch? - Time.now - updated_at > 15.minutes.to_i + Time.current - updated_at > 15.minutes.to_i end def valid_token?(token) @@ -776,11 +776,11 @@ module Ci end def artifacts_expired? - artifacts_expire_at && artifacts_expire_at < Time.now + artifacts_expire_at && artifacts_expire_at < Time.current end def artifacts_expire_in - artifacts_expire_at - Time.now if artifacts_expire_at + artifacts_expire_at - Time.current if artifacts_expire_at end def artifacts_expire_in=(value) @@ -993,7 +993,7 @@ module Ci end def update_erased!(user = nil) - self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil) + self.update(erased_by: user, erased_at: Time.current, artifacts_expire_at: nil) end def unscoped_project @@ -1026,7 +1026,7 @@ module Ci end def has_expiring_artifacts? - artifacts_expire_at.present? && artifacts_expire_at > Time.now + artifacts_expire_at.present? && artifacts_expire_at > Time.current end def job_jwt_variables diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 9aa8526a9cb..e764bdd9133 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -148,7 +148,7 @@ module Ci where(file_type: types) end - scope :expired, -> (limit) { where('expire_at < ?', Time.now).limit(limit) } + scope :expired, -> (limit) { where('expire_at < ?', Time.current).limit(limit) } scope :locked, -> { where(locked: true) } scope :unlocked, -> { where(locked: [false, nil]) } @@ -244,7 +244,7 @@ module Ci end def expire_in - expire_at - Time.now if expire_at + expire_at - Time.current if expire_at end def expire_in=(value) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 5db1635f64d..2b072b22454 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -163,11 +163,11 @@ module Ci # Create a separate worker for each new operation before_transition [:created, :waiting_for_resource, :preparing, :pending] => :running do |pipeline| - pipeline.started_at = Time.now + pipeline.started_at = Time.current end before_transition any => [:success, :failed, :canceled] do |pipeline| - pipeline.finished_at = Time.now + pipeline.finished_at = Time.current pipeline.update_duration end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index d4e9217ff9f..03ff952b435 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -273,7 +273,7 @@ module Ci def update_cached_info(values) values = values&.slice(:version, :revision, :platform, :architecture, :ip_address) || {} - values[:contacted_at] = Time.now + values[:contacted_at] = Time.current cache_attributes(values) @@ -309,7 +309,7 @@ module Ci real_contacted_at = read_attribute(:contacted_at) real_contacted_at.nil? || - (Time.now - real_contacted_at) >= contacted_at_max_age + (Time.current - real_contacted_at) >= contacted_at_max_age end def tag_constraints diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index 3183318690c..46ca0d13b65 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -37,7 +37,7 @@ module Clusters end after_transition any => :updating do |application| - application.update(last_update_started_at: Time.now) + application.update(last_update_started_at: Time.current) end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 8f152280b51..5622a53bb5d 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -136,15 +136,15 @@ class CommitStatus < ApplicationRecord end before_transition [:created, :waiting_for_resource, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status| - commit_status.queued_at = Time.now + commit_status.queued_at = Time.current end before_transition [:created, :preparing, :pending] => :running do |commit_status| - commit_status.started_at = Time.now + commit_status.started_at = Time.current end before_transition any => [:success, :failed, :canceled] do |commit_status| - commit_status.finished_at = Time.now + commit_status.finished_at = Time.current end before_transition any => :failed do |commit_status, transition| diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb index 6314b46a7e3..af5f4e30d06 100644 --- a/app/models/concerns/each_batch.rb +++ b/app/models/concerns/each_batch.rb @@ -17,7 +17,7 @@ module EachBatch # Example: # # User.each_batch do |relation| - # relation.update_all(updated_at: Time.now) + # relation.update_all(updated_at: Time.current) # end # # The supplied block is also passed an optional batch index: diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index b80f8c2bbb2..904de3878e2 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -160,7 +160,7 @@ module HasStatus if started_at && finished_at finished_at - started_at elsif started_at - Time.now - started_at + Time.current - started_at end end end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index 933a0b167e2..183b902dd37 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -24,7 +24,7 @@ module Noteable # The timestamp of the note (e.g. the :created_at or :updated_at attribute if provided via # API call) def system_note_timestamp - @system_note_timestamp || Time.now # rubocop:disable Gitlab/ModuleWithInstanceVariables + @system_note_timestamp || Time.current # rubocop:disable Gitlab/ModuleWithInstanceVariables end attr_writer :system_note_timestamp diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb index 761a151a474..adb6a59e11c 100644 --- a/app/models/concerns/prometheus_adapter.rb +++ b/app/models/concerns/prometheus_adapter.rb @@ -44,7 +44,7 @@ module PrometheusAdapter { success: true, data: data, - last_update: Time.now.utc + last_update: Time.current.utc } rescue Gitlab::PrometheusClient::Error => err { success: false, result: err.message } diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb index 2d2d5fb7168..4e8a1bb643e 100644 --- a/app/models/concerns/resolvable_note.rb +++ b/app/models/concerns/resolvable_note.rb @@ -23,7 +23,7 @@ module ResolvableNote class_methods do # This method must be kept in sync with `#resolve!` def resolve!(current_user) - unresolved.update_all(resolved_at: Time.now, resolved_by_id: current_user.id) + unresolved.update_all(resolved_at: Time.current, resolved_by_id: current_user.id) end # This method must be kept in sync with `#unresolve!` @@ -57,7 +57,7 @@ module ResolvableNote return false unless resolvable? return false if resolved? - self.resolved_at = Time.now + self.resolved_at = Time.current self.resolved_by = current_user self.resolved_by_push = resolved_by_push diff --git a/app/models/deployment.rb b/app/models/deployment.rb index ba65acff7f3..aa3e3a8f66d 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -64,7 +64,7 @@ class Deployment < ApplicationRecord end before_transition any => [:success, :failed, :canceled] do |deployment| - deployment.finished_at = Time.now + deployment.finished_at = Time.current end after_transition any => :success do |deployment| diff --git a/app/models/environment.rb b/app/models/environment.rb index 21044771bbb..8dae2d760f5 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -339,7 +339,7 @@ class Environment < ApplicationRecord end def auto_stop_in - auto_stop_at - Time.now if auto_stop_at + auto_stop_at - Time.current if auto_stop_at end def auto_stop_in=(value) diff --git a/app/models/issue/metrics.rb b/app/models/issue/metrics.rb index d4e51dcfbca..a5e1957c096 100644 --- a/app/models/issue/metrics.rb +++ b/app/models/issue/metrics.rb @@ -11,11 +11,11 @@ class Issue::Metrics < ApplicationRecord def record! if issue.milestone_id.present? && self.first_associated_with_milestone_at.blank? - self.first_associated_with_milestone_at = Time.now + self.first_associated_with_milestone_at = Time.current end if issue_assigned_to_list_label? && self.first_added_to_board_at.blank? - self.first_added_to_board_at = Time.now + self.first_added_to_board_at = Time.current end self.save diff --git a/app/models/jira_import_state.rb b/app/models/jira_import_state.rb index 92147794e88..9737ebcf8fa 100644 --- a/app/models/jira_import_state.rb +++ b/app/models/jira_import_state.rb @@ -47,7 +47,7 @@ class JiraImportState < ApplicationRecord after_transition initial: :scheduled do |state, _| state.run_after_commit do job_id = Gitlab::JiraImport::Stage::StartImportWorker.perform_async(project.id) - state.update(jid: job_id, scheduled_at: Time.now) if job_id + state.update(jid: job_id, scheduled_at: Time.current) if job_id end end diff --git a/app/models/license_template.rb b/app/models/license_template.rb index 73e403f98b4..bd24259984b 100644 --- a/app/models/license_template.rb +++ b/app/models/license_template.rb @@ -39,7 +39,7 @@ class LicenseTemplate end # Populate placeholders in the LicenseTemplate content - def resolve!(project_name: nil, fullname: nil, year: Time.now.year.to_s) + def resolve!(project_name: nil, fullname: nil, year: Time.current.year.to_s) # Ensure the string isn't shared with any other instance of LicenseTemplate new_content = content.dup new_content.gsub!(YEAR_TEMPLATE_REGEX, year) if year.present? diff --git a/app/models/member.rb b/app/models/member.rb index 791073da095..f2926d32d47 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -320,7 +320,7 @@ class Member < ApplicationRecord return false unless invite? self.invite_token = nil - self.invite_accepted_at = Time.now.utc + self.invite_accepted_at = Time.current.utc self.user = new_user diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 8116f7a256f..f82edc72794 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -277,7 +277,7 @@ class Namespace < ApplicationRecord end def has_parent? - parent.present? + parent_id.present? || parent.present? end def root_ancestor diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index da5e4012f05..856496f0941 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -49,11 +49,11 @@ class PagesDomain < ApplicationRecord after_update :update_daemon, if: :saved_change_to_pages_config? after_destroy :update_daemon - scope :enabled, -> { where('enabled_until >= ?', Time.now ) } + scope :enabled, -> { where('enabled_until >= ?', Time.current ) } scope :needs_verification, -> do verified_at = arel_table[:verified_at] enabled_until = arel_table[:enabled_until] - threshold = Time.now + VERIFICATION_THRESHOLD + threshold = Time.current + VERIFICATION_THRESHOLD where(verified_at.eq(nil).or(enabled_until.eq(nil).or(enabled_until.lt(threshold)))) end @@ -69,7 +69,7 @@ class PagesDomain < ApplicationRecord from_union([user_provided, certificate_not_valid, certificate_expiring]) end - scope :for_removal, -> { where("remove_at < ?", Time.now) } + scope :for_removal, -> { where("remove_at < ?", Time.current) } scope :with_logging_info, -> { includes(project: [:namespace, :route]) } @@ -141,7 +141,7 @@ class PagesDomain < ApplicationRecord def expired? return false unless x509 - current = Time.new + current = Time.current current < x509.not_before || x509.not_after < current end diff --git a/app/models/pages_domain_acme_order.rb b/app/models/pages_domain_acme_order.rb index 63d7fbc8206..411456cc237 100644 --- a/app/models/pages_domain_acme_order.rb +++ b/app/models/pages_domain_acme_order.rb @@ -3,7 +3,7 @@ class PagesDomainAcmeOrder < ApplicationRecord belongs_to :pages_domain - scope :expired, -> { where("expires_at < ?", Time.now) } + scope :expired, -> { where("expires_at < ?", Time.current) } validates :pages_domain, presence: true validates :expires_at, presence: true diff --git a/app/models/project.rb b/app/models/project.rb index c9e6a8a49e8..c657e76c86f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -506,6 +506,10 @@ class Project < ApplicationRecord left_outer_joins(:pages_metadatum) .where(project_pages_metadata: { project_id: nil }) end + scope :with_api_entity_associations, -> { + preload(:project_feature, :route, :tags, + group: :ip_restrictions, namespace: [:route, :owner]) + } enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 } @@ -1036,7 +1040,7 @@ class Project < ApplicationRecord remote_mirrors.stuck.update_all( update_status: :failed, last_error: _('The remote mirror took to long to complete.'), - last_update_at: Time.now + last_update_at: Time.current ) end diff --git a/app/models/prometheus_alert_event.rb b/app/models/prometheus_alert_event.rb index 7e61f6d5e3c..25f58a0b9d5 100644 --- a/app/models/prometheus_alert_event.rb +++ b/app/models/prometheus_alert_event.rb @@ -34,10 +34,4 @@ class PrometheusAlertEvent < ApplicationRecord def self.status_value_for(name) state_machines[:status].states[name].value end - - def self.payload_key_for(gitlab_alert_id, started_at) - plain = [gitlab_alert_id, started_at].join('/') - - Digest::SHA1.hexdigest(plain) - end end diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 8e7612e63c8..8b15d481c1b 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -68,13 +68,13 @@ class RemoteMirror < ApplicationRecord after_transition any => :started do |remote_mirror, _| Gitlab::Metrics.add_event(:remote_mirrors_running) - remote_mirror.update(last_update_started_at: Time.now) + remote_mirror.update(last_update_started_at: Time.current) end after_transition started: :finished do |remote_mirror, _| Gitlab::Metrics.add_event(:remote_mirrors_finished) - timestamp = Time.now + timestamp = Time.current remote_mirror.update!( last_update_at: timestamp, last_successful_update_at: timestamp, @@ -86,7 +86,7 @@ class RemoteMirror < ApplicationRecord after_transition started: :failed do |remote_mirror| Gitlab::Metrics.add_event(:remote_mirrors_failed) - remote_mirror.update(last_update_at: Time.now) + remote_mirror.update(last_update_at: Time.current) remote_mirror.run_after_commit do RemoteMirrorNotificationWorker.perform_async(remote_mirror.id) @@ -144,9 +144,9 @@ class RemoteMirror < ApplicationRecord return unless sync? if recently_scheduled? - RepositoryUpdateRemoteMirrorWorker.perform_in(backoff_delay, self.id, Time.now) + RepositoryUpdateRemoteMirrorWorker.perform_in(backoff_delay, self.id, Time.current) else - RepositoryUpdateRemoteMirrorWorker.perform_async(self.id, Time.now) + RepositoryUpdateRemoteMirrorWorker.perform_async(self.id, Time.current) end end @@ -261,7 +261,7 @@ class RemoteMirror < ApplicationRecord def recently_scheduled? return false unless self.last_update_started_at - self.last_update_started_at >= Time.now - backoff_delay + self.last_update_started_at >= Time.current - backoff_delay end def reset_fields diff --git a/app/models/repository.rb b/app/models/repository.rb index 2673033ff1f..16b4fdabf48 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1171,7 +1171,7 @@ class Repository if target target.committed_date else - Time.now + Time.current end end end diff --git a/app/models/route.rb b/app/models/route.rb index 63a0461807b..706589e79b8 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -42,7 +42,7 @@ class Route < ApplicationRecord old_path = route.path # Callbacks must be run manually - route.update_columns(attributes.merge(updated_at: Time.now)) + route.update_columns(attributes.merge(updated_at: Time.current)) # We are not calling route.delete_conflicting_redirects here, in hopes # of avoiding deadlocks. The parent (self, in this method) already diff --git a/app/models/self_managed_prometheus_alert_event.rb b/app/models/self_managed_prometheus_alert_event.rb index d2d4a5c37d4..cf26563e92d 100644 --- a/app/models/self_managed_prometheus_alert_event.rb +++ b/app/models/self_managed_prometheus_alert_event.rb @@ -15,10 +15,4 @@ class SelfManagedPrometheusAlertEvent < ApplicationRecord yield event if block_given? end end - - def self.payload_key_for(started_at, alert_name, query_expression) - plain = [started_at, alert_name, query_expression].join('/') - - Digest::SHA1.hexdigest(plain) - end end diff --git a/app/models/snippet_input_action.rb b/app/models/snippet_input_action.rb new file mode 100644 index 00000000000..50de1b0c04e --- /dev/null +++ b/app/models/snippet_input_action.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class SnippetInputAction + include ActiveModel::Validations + + ACTIONS = %w[create update delete move].freeze + + ACTIONS.each do |action_const| + define_method "#{action_const}_action?" do + action == action_const + end + end + + attr_reader :action, :previous_path, :file_path, :content + + validates :action, inclusion: { in: ACTIONS, message: "%{value} is not a valid action" } + validates :previous_path, presence: true, if: :move_action? + validates :file_path, presence: true + validates :content, presence: true, if: :create_action? + + def initialize(action: nil, previous_path: nil, file_path: nil, content: nil) + @action = action + @previous_path = previous_path + @file_path = file_path + @content = content + end + + def to_commit_action + { + action: action&.to_sym, + previous_path: previous_path, + file_path: file_path, + content: content + } + end +end diff --git a/app/models/snippet_input_action_collection.rb b/app/models/snippet_input_action_collection.rb new file mode 100644 index 00000000000..bf20bce5b0a --- /dev/null +++ b/app/models/snippet_input_action_collection.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class SnippetInputActionCollection + include Gitlab::Utils::StrongMemoize + + attr_reader :actions + + delegate :empty?, to: :actions + + def initialize(actions = []) + @actions = actions.map { |action| SnippetInputAction.new(action) } + end + + def to_commit_actions + strong_memoize(:commit_actions) do + actions.map { |action| action.to_commit_action } + end + end + + def valid? + strong_memoize(:valid) do + actions.all?(&:valid?) + end + end +end diff --git a/app/models/todo.rb b/app/models/todo.rb index dc42551f0ab..b5ca2ec7466 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -110,7 +110,7 @@ class Todo < ApplicationRecord base = where.not(state: new_state).except(:order) ids = base.pluck(:id) - base.update_all(state: new_state, updated_at: Time.now) + base.update_all(state: new_state, updated_at: Time.current) ids end diff --git a/app/models/user.rb b/app/models/user.rb index b2d3978551e..1ba5b9cdf71 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -688,7 +688,7 @@ class User < ApplicationRecord @reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token) self.reset_password_token = enc - self.reset_password_sent_at = Time.now.utc + self.reset_password_sent_at = Time.current.utc @reset_token end @@ -1126,7 +1126,7 @@ class User < ApplicationRecord if !Gitlab.config.ldap.enabled false elsif ldap_user? - !last_credential_check_at || (last_credential_check_at + ldap_sync_time) < Time.now + !last_credential_check_at || (last_credential_check_at + ldap_sync_time) < Time.current else false end @@ -1373,7 +1373,7 @@ class User < ApplicationRecord def contributed_projects events = Event.select(:project_id) .contributions.where(author_id: self) - .where("created_at > ?", Time.now - 1.year) + .where("created_at > ?", Time.current - 1.year) .distinct .reorder(nil) @@ -1646,7 +1646,7 @@ class User < ApplicationRecord end def password_expired? - !!(password_expires_at && password_expires_at < Time.now) + !!(password_expires_at && password_expires_at < Time.current) end def can_be_deactivated? diff --git a/app/models/wiki_page/meta.rb b/app/models/wiki_page/meta.rb index 474968122b1..215d84dc463 100644 --- a/app/models/wiki_page/meta.rb +++ b/app/models/wiki_page/meta.rb @@ -120,7 +120,7 @@ class WikiPage end def insert_slugs(strings, is_new, canonical_slug) - creation = Time.now.utc + creation = Time.current.utc slug_attrs = strings.map do |slug| { diff --git a/app/models/wiki_page/slug.rb b/app/models/wiki_page/slug.rb index 246fa8d6442..c1725d34921 100644 --- a/app/models/wiki_page/slug.rb +++ b/app/models/wiki_page/slug.rb @@ -16,11 +16,11 @@ class WikiPage scope :canonical, -> { where(canonical: true) } def update_columns(attrs = {}) - super(attrs.reverse_merge(updated_at: Time.now.utc)) + super(attrs.reverse_merge(updated_at: Time.current.utc)) end def self.update_all(attrs = {}) - super(attrs.reverse_merge(updated_at: Time.now.utc)) + super(attrs.reverse_merge(updated_at: Time.current.utc)) end end end diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb index fe3ab884302..3f4de659c78 100644 --- a/app/services/groups/transfer_service.rb +++ b/app/services/groups/transfer_service.rb @@ -45,6 +45,7 @@ module Groups raise_transfer_error(:invalid_policies) unless valid_policies? raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path? raise_transfer_error(:group_contains_images) if group_projects_contain_registry_images? + raise_transfer_error(:cannot_transfer_to_subgroup) if transfer_to_subgroup? end def group_is_already_root? @@ -55,6 +56,11 @@ module Groups @new_parent_group && @new_parent_group.id == @group.parent_id end + def transfer_to_subgroup? + @new_parent_group && \ + @group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id) + end + def valid_policies? return false unless can?(current_user, :admin_group, @group) @@ -125,7 +131,8 @@ module Groups group_is_already_root: s_('TransferGroup|Group is already a root group.'), same_parent_as_current: s_('TransferGroup|Group is already associated to the parent group.'), invalid_policies: s_("TransferGroup|You don't have enough permissions."), - group_contains_images: s_('TransferGroup|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.') + group_contains_images: s_('TransferGroup|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.'), + cannot_transfer_to_subgroup: s_('TransferGroup|Cannot transfer group to one of its subgroup.') }.freeze end end diff --git a/app/services/projects/prometheus/alerts/create_events_service.rb b/app/services/projects/prometheus/alerts/create_events_service.rb index a29240947ff..4fcf841314b 100644 --- a/app/services/projects/prometheus/alerts/create_events_service.rb +++ b/app/services/projects/prometheus/alerts/create_events_service.rb @@ -40,17 +40,13 @@ module Projects def create_managed_prometheus_alert_event(parsed_alert) alert = find_alert(parsed_alert.metric_id) - payload_key = PrometheusAlertEvent.payload_key_for(parsed_alert.metric_id, parsed_alert.starts_at_raw) - - event = PrometheusAlertEvent.find_or_initialize_by_payload_key(parsed_alert.project, alert, payload_key) + event = PrometheusAlertEvent.find_or_initialize_by_payload_key(parsed_alert.project, alert, parsed_alert.gitlab_fingerprint) set_status(parsed_alert, event) end def create_self_managed_prometheus_alert_event(parsed_alert) - payload_key = SelfManagedPrometheusAlertEvent.payload_key_for(parsed_alert.starts_at_raw, parsed_alert.title, parsed_alert.full_query) - - event = SelfManagedPrometheusAlertEvent.find_or_initialize_by_payload_key(parsed_alert.project, payload_key) do |event| + event = SelfManagedPrometheusAlertEvent.find_or_initialize_by_payload_key(parsed_alert.project, parsed_alert.gitlab_fingerprint) do |event| event.environment = parsed_alert.environment event.title = parsed_alert.title event.query_expression = parsed_alert.full_query diff --git a/app/services/snippets/base_service.rb b/app/services/snippets/base_service.rb index 81d12997335..60ba9ff6683 100644 --- a/app/services/snippets/base_service.rb +++ b/app/services/snippets/base_service.rb @@ -6,12 +6,13 @@ module Snippets CreateRepositoryError = Class.new(StandardError) - attr_reader :uploaded_files + attr_reader :uploaded_assets, :snippet_files def initialize(project, user = nil, params = {}) super - @uploaded_files = Array(@params.delete(:files).presence) + @uploaded_assets = Array(@params.delete(:files).presence) + @snippet_files = SnippetInputActionCollection.new(Array(@params.delete(:snippet_files).presence)) filter_spam_check_params end @@ -22,12 +23,30 @@ module Snippets Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level) end - def error_forbidden_visibility(snippet) + def forbidden_visibility_error(snippet) deny_visibility_level(snippet) snippet_error_response(snippet, 403) end + def valid_params? + return true if snippet_files.empty? + + (params.keys & [:content, :file_name]).none? && snippet_files.valid? + end + + def invalid_params_error(snippet) + if snippet_files.valid? + [:content, :file_name].each do |key| + snippet.errors.add(key, 'and snippet files cannot be used together') if params.key?(key) + end + else + snippet.errors.add(:snippet_files, 'have invalid data') + end + + snippet_error_response(snippet, 403) + end + def snippet_error_response(snippet, http_status) ServiceResponse.error( message: snippet.errors.full_messages.to_sentence, @@ -52,5 +71,13 @@ module Snippets message end + + def files_to_commit + snippet_files.to_commit_actions.presence || build_actions_from_params + end + + def build_actions_from_params + raise NotImplementedError + end end end diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb index ed6da3a0ad0..b3af2d91e5c 100644 --- a/app/services/snippets/create_service.rb +++ b/app/services/snippets/create_service.rb @@ -5,8 +5,10 @@ module Snippets def execute @snippet = build_from_params + return invalid_params_error(@snippet) unless valid_params? + unless visibility_allowed?(@snippet, @snippet.visibility_level) - return error_forbidden_visibility(@snippet) + return forbidden_visibility_error(@snippet) end @snippet.author = current_user @@ -29,12 +31,23 @@ module Snippets def build_from_params if project - project.snippets.build(params) + project.snippets.build(create_params) else - PersonalSnippet.new(params) + PersonalSnippet.new(create_params) end end + # If the snippet_files param is present + # we need to fill content and file_name from + # the model + def create_params + return params if snippet_files.empty? + + first_file = snippet_files.actions.first + + params.merge(content: first_file.content, file_name: first_file.file_path) + end + def save_and_commit snippet_saved = @snippet.save @@ -75,19 +88,19 @@ module Snippets message: 'Initial commit' } - @snippet.snippet_repository.multi_files_action(current_user, snippet_files, commit_attrs) - end - - def snippet_files - [{ file_path: params[:file_name], content: params[:content] }] + @snippet.snippet_repository.multi_files_action(current_user, files_to_commit, commit_attrs) end def move_temporary_files return unless @snippet.is_a?(PersonalSnippet) - uploaded_files.each do |file| + uploaded_assets.each do |file| FileMover.new(file, from_model: current_user, to_model: @snippet).execute end end + + def build_actions_from_params + [{ file_path: params[:file_name], content: params[:content] }] + end end end diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb index 2dc9266dbd0..45b047af3d0 100644 --- a/app/services/snippets/update_service.rb +++ b/app/services/snippets/update_service.rb @@ -8,7 +8,7 @@ module Snippets def execute(snippet) if visibility_changed?(snippet) && !visibility_allowed?(snippet, visibility_level) - return error_forbidden_visibility(snippet) + return forbidden_visibility_error(snippet) end snippet.assign_attributes(params) diff --git a/app/services/spam/spam_constants.rb b/app/services/spam/spam_constants.rb index 085bac684c4..26526daa313 100644 --- a/app/services/spam/spam_constants.rb +++ b/app/services/spam/spam_constants.rb @@ -2,8 +2,24 @@ module Spam module SpamConstants - REQUIRE_RECAPTCHA = :recaptcha - DISALLOW = :disallow - ALLOW = :allow + REQUIRE_RECAPTCHA = "recaptcha" + DISALLOW = "disallow" + ALLOW = "allow" + BLOCK_USER = "block" + + SUPPORTED_VERDICTS = { + BLOCK_USER => { + priority: 1 + }, + DISALLOW => { + priority: 2 + }, + REQUIRE_RECAPTCHA => { + priority: 3 + }, + ALLOW => { + priority: 4 + } + }.freeze end end diff --git a/app/services/spam/spam_verdict_service.rb b/app/services/spam/spam_verdict_service.rb index 2b4d5f4a984..ef026d7f9af 100644 --- a/app/services/spam/spam_verdict_service.rb +++ b/app/services/spam/spam_verdict_service.rb @@ -5,13 +5,31 @@ module Spam include AkismetMethods include SpamConstants - def initialize(target:, request:, options:) + def initialize(target:, request:, options:, verdict_params: {}) @target = target @request = request @options = options + @verdict_params = assemble_verdict_params(verdict_params) end def execute + external_spam_check_result = spam_verdict + akismet_result = akismet_verdict + + # filter out anything we don't recognise, including nils. + valid_results = [external_spam_check_result, akismet_result].compact.select { |r| SUPPORTED_VERDICTS.key?(r) } + # Treat nils - such as service unavailable - as ALLOW + return ALLOW unless valid_results.any? + + # Favour the most restrictive result. + valid_results.min_by { |v| SUPPORTED_VERDICTS[v][:priority] } + end + + private + + attr_reader :target, :request, :options, :verdict_params + + def akismet_verdict if akismet.spam? Gitlab::Recaptcha.enabled? ? REQUIRE_RECAPTCHA : DISALLOW else @@ -19,8 +37,41 @@ module Spam end end - private + def spam_verdict + return unless Gitlab::CurrentSettings.spam_check_endpoint_enabled + return if endpoint_url.blank? + + result = Gitlab::HTTP.try_get(endpoint_url, verdict_params) + return unless result + + begin + json_result = Gitlab::Json.parse(result).with_indifferent_access + # @TODO metrics/logging + # Expecting: + # error: (string or nil) + # result: (string or nil) + verdict = json_result[:verdict] + return unless SUPPORTED_VERDICTS.include?(verdict) - attr_reader :target, :request, :options + # @TODO log if json_result[:error] + + json_result[:verdict] + rescue + # @TODO log + ALLOW + end + end + + def assemble_verdict_params(params) + return {} unless endpoint_url + + params.merge({ + user_id: target.author_id + }) + end + + def endpoint_url + @endpoint_url ||= Gitlab::CurrentSettings.current_application_settings.spam_check_endpoint_url + end end end diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml index f0a19075115..ab9368e723e 100644 --- a/app/views/admin/application_settings/_spam.html.haml +++ b/app/views/admin/application_settings/_spam.html.haml @@ -62,4 +62,13 @@ .form-text.text-muted How many seconds an IP will be counted towards the limit + .form-group + .form-check + = f.check_box :spam_check_endpoint_enabled, class: 'form-check-input' + = f.label :spam_check_endpoint_enabled, _('Enable Spam Check via external API endpoint'), class: 'form-check-label' + .form-text.text-muted= _('Define custom rules for what constitutes spam, independent of Akismet') + .form-group + = f.label :spam_check_endpoint_url, _('URL of the external Spam Check endpoint'), class: 'label-bold' + = f.text_field :spam_check_endpoint_url, class: 'form-control' + = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/workers/incident_management/process_prometheus_alert_worker.rb b/app/workers/incident_management/process_prometheus_alert_worker.rb index 768e049c60e..e405bc2c2d2 100644 --- a/app/workers/incident_management/process_prometheus_alert_worker.rb +++ b/app/workers/incident_management/process_prometheus_alert_worker.rb @@ -41,23 +41,11 @@ module IncidentManagement end def find_gitlab_managed_event(alert) - payload_key = payload_key_for_alert(alert) - - PrometheusAlertEvent.find_by_payload_key(payload_key) + PrometheusAlertEvent.find_by_payload_key(alert.gitlab_fingerprint) end def find_self_managed_event(alert) - payload_key = payload_key_for_alert(alert) - - SelfManagedPrometheusAlertEvent.find_by_payload_key(payload_key) - end - - def payload_key_for_alert(alert) - if alert.gitlab_managed? - PrometheusAlertEvent.payload_key_for(alert.metric_id, alert.starts_at_raw) - else - SelfManagedPrometheusAlertEvent.payload_key_for(alert.starts_at_raw, alert.title, alert.full_query) - end + SelfManagedPrometheusAlertEvent.find_by_payload_key(alert.gitlab_fingerprint) end def create_issue(project, alert) |