diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/ide/components/preview/clientside.vue | 22 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/preview/navigator.vue | 6 | ||||
-rw-r--r-- | app/assets/javascripts/notebook/cells/output/html.vue | 9 | ||||
-rw-r--r-- | app/controllers/jwt_controller.rb | 45 | ||||
-rw-r--r-- | app/controllers/repositories/git_http_client_controller.rb | 23 | ||||
-rw-r--r-- | app/graphql/resolvers/paginated_tree_resolver.rb | 6 | ||||
-rw-r--r-- | app/graphql/types/incident_management/timeline_event_type.rb | 7 | ||||
-rw-r--r-- | app/helpers/commits_helper.rb | 2 | ||||
-rw-r--r-- | app/helpers/labels_helper.rb | 2 | ||||
-rw-r--r-- | app/models/integrations/zentao.rb | 4 | ||||
-rw-r--r-- | app/models/issue.rb | 8 | ||||
-rw-r--r-- | app/models/repository.rb | 10 | ||||
-rw-r--r-- | app/models/snippet.rb | 15 | ||||
-rw-r--r-- | app/models/tree.rb | 4 | ||||
-rw-r--r-- | app/presenters/commit_presenter.rb | 10 | ||||
-rw-r--r-- | app/validators/bytesize_validator.rb | 30 | ||||
-rw-r--r-- | app/views/projects/commits/_commit.html.haml | 2 |
17 files changed, 134 insertions, 71 deletions
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue index b1f6f2c87b9..70b881b6ff6 100644 --- a/app/assets/javascripts/ide/components/preview/clientside.vue +++ b/app/assets/javascripts/ide/components/preview/clientside.vue @@ -2,7 +2,7 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { listen } from 'codesandbox-api'; import { isEmpty, debounce } from 'lodash'; -import { Manager } from 'smooshpack'; +import { SandpackClient } from '@codesandbox/sandpack-client'; import { mapActions, mapGetters, mapState } from 'vuex'; import { packageJsonPath, @@ -21,7 +21,7 @@ export default { }, data() { return { - manager: {}, + client: {}, loading: false, sandpackReady: false, }; @@ -94,11 +94,11 @@ export default { this.sandpackReady = false; eventHub.$off('ide.files.change', this.onFilesChangeCallback); - if (!isEmpty(this.manager)) { - this.manager.listener(); + if (!isEmpty(this.client)) { + this.client.cleanup(); } - this.manager = {}; + this.client = {}; if (this.listener) { this.listener(); @@ -120,7 +120,7 @@ export default { return this.loadFileContent(this.mainEntry) .then(() => this.$nextTick()) .then(() => { - this.initManager(); + this.initClient(); this.listener = listen((e) => { switch (e.type) { @@ -136,15 +136,15 @@ export default { update() { if (!this.sandpackReady) return; - if (isEmpty(this.manager)) { + if (isEmpty(this.client)) { this.initPreview(); return; } - this.manager.updatePreview(this.sandboxOpts); + this.client.updatePreview(this.sandboxOpts); }, - initManager() { + initClient() { const { codesandboxBundlerUrl: bundlerURL } = this; const settings = { @@ -155,7 +155,7 @@ export default { ...(bundlerURL ? { bundlerURL } : {}), }; - this.manager = new Manager('#ide-preview', this.sandboxOpts, settings); + this.client = new SandpackClient('#ide-preview', this.sandboxOpts, settings); }, }, }; @@ -164,7 +164,7 @@ export default { <template> <div class="preview h-100 w-100 d-flex flex-column gl-bg-white"> <template v-if="showPreview"> - <navigator :manager="manager" /> + <navigator :client="client" /> <div id="ide-preview"></div> </template> <div diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue index 96f9a85c23f..852de16d508 100644 --- a/app/assets/javascripts/ide/components/preview/navigator.vue +++ b/app/assets/javascripts/ide/components/preview/navigator.vue @@ -8,7 +8,7 @@ export default { GlLoadingIcon, }, props: { - manager: { + client: { type: Object, required: true, }, @@ -51,7 +51,7 @@ export default { onUrlChange(e) { const lastPath = this.path; - this.path = e.url.replace(this.manager.bundlerURL, '') || '/'; + this.path = e.url.replace(this.client.bundlerURL, '') || '/'; if (lastPath !== this.path) { this.currentBrowsingIndex = @@ -79,7 +79,7 @@ export default { }, visitPath(path) { // eslint-disable-next-line vue/no-mutating-props - this.manager.iframe.src = `${this.manager.bundlerURL}${path}`; + this.client.iframe.src = `${this.client.bundlerURL}${path}`; }, }, }; diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue index 2d1d8845e41..fdcea300388 100644 --- a/app/assets/javascripts/notebook/cells/output/html.vue +++ b/app/assets/javascripts/notebook/cells/output/html.vue @@ -40,6 +40,13 @@ export default { <template> <div class="output"> <prompt type="Out" :count="count" :show-output="showOutput" /> - <div v-safe-html:[$options.safeHtmlConfig]="rawCode" class="gl-overflow-auto"></div> + <iframe + sandbox + :srcdoc="rawCode" + frameborder="0" + scrolling="no" + width="100%" + class="gl-overflow-auto" + ></iframe> </div> </template> diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 8eebf9fbf6b..84f5632854b 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -36,31 +36,40 @@ class JwtController < ApplicationController @authentication_result = Gitlab::Auth.find_for_git_client(login, password, project: nil, ip: request.ip) if @authentication_result.failed? - render_unauthorized + log_authentication_failed(login, @authentication_result) + render_access_denied end end rescue Gitlab::Auth::MissingPersonalAccessTokenError - render_missing_personal_access_token + render_access_denied end - def render_missing_personal_access_token - render json: { - errors: [ - { code: 'UNAUTHORIZED', - message: _('HTTP Basic: Access denied\n' \ - 'You must use a personal access token with \'api\' scope for Git over HTTP.\n' \ - 'You can generate one at %{profile_personal_access_tokens_url}') % { profile_personal_access_tokens_url: profile_personal_access_tokens_url } } - ] - }, status: :unauthorized + def log_authentication_failed(login, result) + log_info = { + message: 'JWT authentication failed', + http_user: login, + remote_ip: request.ip, + auth_service: params[:service], + 'auth_result.type': result.type, + 'auth_result.actor_type': result.actor&.class + }.merge(::Gitlab::ApplicationContext.current) + + Gitlab::AuthLogger.warn(log_info) end - def render_unauthorized - render json: { - errors: [ - { code: 'UNAUTHORIZED', - message: 'HTTP Basic: Access denied' } - ] - }, status: :unauthorized + def render_access_denied + help_page = help_page_url( + 'user/profile/account/two_factor_authentication', + anchor: 'troubleshooting' + ) + + render( + json: { errors: [{ + code: 'UNAUTHORIZED', + message: format(_("HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"), help_page_url: help_page) + }] }, + status: :unauthorized + ) end def auth_params diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb index 8d7ba3e38c0..fbf5d82a45b 100644 --- a/app/controllers/repositories/git_http_client_controller.rb +++ b/app/controllers/repositories/git_http_client_controller.rb @@ -67,9 +67,21 @@ module Repositories end send_challenges - render plain: "HTTP Basic: Access denied\n", status: :unauthorized + render_access_denied rescue Gitlab::Auth::MissingPersonalAccessTokenError - render_missing_personal_access_token + render_access_denied + end + + def render_access_denied + help_page = help_page_url( + 'topics/git/troubleshooting_git', + anchor: 'error-on-git-fetch-http-basic-access-denied' + ) + + render( + plain: format(_("HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"), help_page_url: help_page), + status: :unauthorized + ) end def basic_auth_provided? @@ -103,13 +115,6 @@ module Repositories @container, @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(repository_path) end - def render_missing_personal_access_token - render plain: "HTTP Basic: Access denied\n" \ - "You must use a personal access token with 'read_repository' or 'write_repository' scope for Git over HTTP.\n" \ - "You can generate one at #{profile_personal_access_tokens_url}", - status: :unauthorized - end - def repository strong_memoize(:repository) do repo_type.repository_for(container) diff --git a/app/graphql/resolvers/paginated_tree_resolver.rb b/app/graphql/resolvers/paginated_tree_resolver.rb index 1b4211366e0..c7e9e522c25 100644 --- a/app/graphql/resolvers/paginated_tree_resolver.rb +++ b/app/graphql/resolvers/paginated_tree_resolver.rb @@ -32,7 +32,11 @@ module Resolvers page_token: cursor } - tree = repository.tree(args[:ref], args[:path], recursive: args[:recursive], pagination_params: pagination_params) + tree = repository.tree( + args[:ref], args[:path], recursive: args[:recursive], + skip_flat_paths: false, + pagination_params: pagination_params + ) next_cursor = tree.cursor&.next_cursor Gitlab::Graphql::ExternallyPaginatedArray.new(cursor, next_cursor, *tree) diff --git a/app/graphql/types/incident_management/timeline_event_type.rb b/app/graphql/types/incident_management/timeline_event_type.rb index a6d3f57404b..690facc8732 100644 --- a/app/graphql/types/incident_management/timeline_event_type.rb +++ b/app/graphql/types/incident_management/timeline_event_type.rb @@ -33,11 +33,6 @@ module Types null: true, description: 'Text note of the timeline event.' - field :note_html, - GraphQL::Types::String, - null: true, - description: 'HTML note of the timeline event.' - field :promoted_from_note, Types::Notes::NoteType, null: true, @@ -67,6 +62,8 @@ module Types Types::TimeType, null: false, description: 'Timestamp when the event updated.' + + markdown_field :note_html, null: true, description: 'HTML note of the timeline event.' end end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 1920650bc93..4493bc2bc6d 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -171,7 +171,7 @@ module CommitsHelper ref, { merge_request: merge_request&.cache_key, - pipeline_status: commit.status_for(ref)&.cache_key, + pipeline_status: commit.detailed_status_for(ref)&.cache_key, xhr: request.xhr?, controller: controller.controller_path, path: @path # referred to in #link_to_browse_code diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 2d0bc1bc63f..e865db128c1 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -247,7 +247,7 @@ module LabelsHelper class="#{css_class}" data-container="body" data-html="true" - #{"style=\"background-color: #{bg_color}\"" if bg_color} + #{"style=\"background-color: #{h bg_color}\"" if bg_color} >#{ERB::Util.html_escape_once(name)}#{suffix}</span> HTML end diff --git a/app/models/integrations/zentao.rb b/app/models/integrations/zentao.rb index 53194089296..459756c865b 100644 --- a/app/models/integrations/zentao.rb +++ b/app/models/integrations/zentao.rb @@ -69,6 +69,10 @@ module Integrations } end + def client_url + api_url.presence || url + end + def self.to_param name.demodulize.downcase end diff --git a/app/models/issue.rb b/app/models/issue.rb index 4114467eb25..df8ee34b3c3 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -458,7 +458,13 @@ class Issue < ApplicationRecord return to_branch_name unless project.repository.branch_exists?(to_branch_name) start_counting_from = 2 - Uniquify.new(start_counting_from).string(-> (counter) { "#{to_branch_name}-#{counter}" }) do |suggested_branch_name| + + branch_name_generator = -> (counter) do + suffix = counter > 5 ? SecureRandom.hex(8) : counter + "#{to_branch_name}-#{suffix}" + end + + Uniquify.new(start_counting_from).string(branch_name_generator) do |suggested_branch_name| project.repository.branch_exists?(suggested_branch_name) end end diff --git a/app/models/repository.rb b/app/models/repository.rb index eb8e45877f3..26c3b01a46e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -677,24 +677,24 @@ class Repository @head_commit ||= commit(self.root_ref) end - def head_tree + def head_tree(skip_flat_paths: true) if head_commit - @head_tree ||= Tree.new(self, head_commit.sha, nil) + @head_tree ||= Tree.new(self, head_commit.sha, nil, skip_flat_paths: skip_flat_paths) end end - def tree(sha = :head, path = nil, recursive: false, pagination_params: nil) + def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil) if sha == :head return unless head_commit if path.nil? - return head_tree + return head_tree(skip_flat_paths: skip_flat_paths) else sha = head_commit.sha end end - Tree.new(self, sha, path, recursive: recursive, pagination_params: pagination_params) + Tree.new(self, sha, path, recursive: recursive, skip_flat_paths: skip_flat_paths, pagination_params: pagination_params) end def blob_at_branch(branch_name, path) diff --git a/app/models/snippet.rb b/app/models/snippet.rb index fd882633a44..943d09d983b 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -22,6 +22,8 @@ class Snippet < ApplicationRecord MAX_FILE_COUNT = 10 + DESCRIPTION_LENGTH_MAX = 1.megabyte + cache_markdown_field :title, pipeline: :single_line cache_markdown_field :description cache_markdown_field :content @@ -57,19 +59,10 @@ class Snippet < ApplicationRecord validates :title, presence: true, length: { maximum: 255 } validates :file_name, length: { maximum: 255 } + validates :description, bytesize: { maximum: -> { DESCRIPTION_LENGTH_MAX } }, if: :description_changed? validates :content, presence: true - validates :content, - length: { - maximum: ->(_) { Gitlab::CurrentSettings.snippet_size_limit }, - message: -> (_, data) do - current_value = ActiveSupport::NumberHelper.number_to_human_size(data[:value].size) - max_size = ActiveSupport::NumberHelper.number_to_human_size(Gitlab::CurrentSettings.snippet_size_limit) - - _("is too long (%{current_value}). The maximum size is %{max_size}.") % { current_value: current_value, max_size: max_size } - end - }, - if: :content_changed? + validates :content, bytesize: { maximum: -> { Gitlab::CurrentSettings.snippet_size_limit } }, if: :content_changed? after_create :create_statistics diff --git a/app/models/tree.rb b/app/models/tree.rb index fd416ebdedc..941d0394b94 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -6,7 +6,7 @@ class Tree attr_accessor :repository, :sha, :path, :entries, :cursor - def initialize(repository, sha, path = '/', recursive: false, pagination_params: nil) + def initialize(repository, sha, path = '/', recursive: false, skip_flat_paths: true, pagination_params: nil) path = '/' if path.blank? @repository = repository @@ -14,7 +14,7 @@ class Tree @path = path git_repo = @repository.raw_repository - @entries, @cursor = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive, pagination_params) + @entries, @cursor = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive, skip_flat_paths, pagination_params) end def readme_path diff --git a/app/presenters/commit_presenter.rb b/app/presenters/commit_presenter.rb index 7df45ca03bb..2cb88179845 100644 --- a/app/presenters/commit_presenter.rb +++ b/app/presenters/commit_presenter.rb @@ -5,12 +5,20 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated presents ::Commit, as: :commit - def status_for(ref) + def detailed_status_for(ref) + return unless can?(current_user, :read_pipeline, commit.latest_pipeline(ref)) return unless can?(current_user, :read_commit_status, commit.project) commit.latest_pipeline(ref)&.detailed_status(current_user) end + def status_for(ref = nil) + return unless can?(current_user, :read_pipeline, commit.latest_pipeline(ref)) + return unless can?(current_user, :read_commit_status, commit.project) + + commit.status(ref) + end + def any_pipelines? return false unless can?(current_user, :read_pipeline, commit.project) diff --git a/app/validators/bytesize_validator.rb b/app/validators/bytesize_validator.rb new file mode 100644 index 00000000000..adbdd81d5c4 --- /dev/null +++ b/app/validators/bytesize_validator.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# BytesizeValidator +# +# Custom validator for verifying that bytesize of a field doesn't exceed the specified limit. +# It is different from Rails length validator because it takes .bytesize into account instead of .size/.length +# +# Example: +# +# class Snippet < ActiveRecord::Base +# validates :content, bytesize: { maximum: -> { Gitlab::CurrentSettings.snippet_size_limit } } +# end +# +# Configuration options: +# * <tt>maximum</tt> - Proc that evaluates the bytesize limit that cannot be exceeded +class BytesizeValidator < ActiveModel::EachValidator + def validate_each(record, attr, value) + size = value.to_s.bytesize + max_size = options[:maximum].call + + return if size <= max_size + + error_message = format(_('is too long (%{size}). The maximum size is %{max_size}.'), { + size: ActiveSupport::NumberHelper.number_to_human_size(size), + max_size: ActiveSupport::NumberHelper.number_to_human_size(max_size) + }) + + record.errors.add(attr, error_message) + end +end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 71485e203db..6f44c130603 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -14,7 +14,7 @@ - project = local_assigns.fetch(:project) { merge_request&.project } - ref = local_assigns.fetch(:ref) { merge_request&.source_branch } - commit = commit.present(current_user: current_user) -- commit_status = commit.status_for(ref) +- commit_status = commit.detailed_status_for(ref) - collapsible = local_assigns.fetch(:collapsible, true) - link_data_attrs = local_assigns.fetch(:link_data_attrs, {}) - link = commit_path(project, commit, merge_request: merge_request) |