summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue22
-rw-r--r--app/assets/javascripts/ide/components/preview/navigator.vue6
-rw-r--r--app/assets/javascripts/notebook/cells/output/html.vue9
-rw-r--r--app/controllers/jwt_controller.rb45
-rw-r--r--app/controllers/repositories/git_http_client_controller.rb23
-rw-r--r--app/graphql/resolvers/paginated_tree_resolver.rb6
-rw-r--r--app/graphql/types/incident_management/timeline_event_type.rb7
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/labels_helper.rb2
-rw-r--r--app/models/integrations/zentao.rb4
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/repository.rb10
-rw-r--r--app/models/snippet.rb15
-rw-r--r--app/models/tree.rb4
-rw-r--r--app/presenters/commit_presenter.rb10
-rw-r--r--app/validators/bytesize_validator.rb30
-rw-r--r--app/views/projects/commits/_commit.html.haml2
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)