...
 
Commits (132)
This diff is collapsed.
......@@ -205,9 +205,6 @@ gem 'redis-rails', '~> 5.0.2'
gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0'
# Discord integration
gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
# HipChat integration
gem 'hipchat', '~> 1.5.0'
......
......@@ -188,8 +188,6 @@ GEM
rotp (~> 2.0)
diff-lcs (1.3)
diffy (3.1.0)
discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0)
docile (1.3.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
......@@ -1061,7 +1059,6 @@ DEPENDENCIES
devise (~> 4.6)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.5)
ed25519 (~> 1.2)
......
......@@ -56,7 +56,13 @@ export default {
return Api.branchSingle(projectId, currentBranchId);
},
commit(projectId, payload) {
return Api.commitMultiple(projectId, payload);
// Currently the `commit` endpoint does not support `start_sha` so we
// have to make the request in the FE. This is not ideal and will be
// resolved soon. https://gitlab.com/gitlab-org/gitlab-ce/issues/59023
const { branch, start_sha: ref } = payload;
const branchPromise = ref ? Api.createBranch(projectId, { ref, branch }) : Promise.resolve();
return branchPromise.then(() => Api.commitMultiple(projectId, payload));
},
getFiles(projectUrl, branchId) {
const url = `${projectUrl}/files/${branchId}`;
......
......@@ -142,6 +142,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
getters,
state,
rootState,
rootGetters,
});
return service.commit(rootState.currentProjectId, payload);
......
......@@ -135,7 +135,14 @@ export const getCommitFiles = stagedFiles =>
});
}, []);
export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
export const createCommitPayload = ({
branch,
getters,
newBranch,
state,
rootState,
rootGetters,
}) => ({
branch,
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
actions: getCommitFiles(rootState.stagedFiles).map(f => ({
......@@ -146,7 +153,7 @@ export const createCommitPayload = ({ branch, getters, newBranch, state, rootSta
encoding: f.base64 ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined,
});
export const createNewMergeRequestUrl = (projectUrl, source, target) =>
......
......@@ -102,13 +102,10 @@ export default {
</p>
<div class="text-center">
<a v-if="currentState.buttonPath" :href="currentState.buttonPath" class="btn btn-success">
{{ currentState.buttonText }}
</a>
<a
v-if="currentState.secondaryButtonPath"
:href="currentState.secondaryButtonPath"
class="btn"
class="btn btn-success"
>
{{ currentState.secondaryButtonText }}
</a>
......
......@@ -42,7 +42,7 @@ module IssuableCollections
@issuables = @issuables.page(params[:page])
@issuables = per_page_for_relative_position if params[:sort] == 'relative_position'
@issuable_meta_data = issuable_meta_data(@issuables, collection_type)
@issuable_meta_data = issuable_meta_data(@issuables, collection_type, current_user)
@total_pages = issuable_page_count
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
......
......@@ -11,7 +11,7 @@ module IssuableCollectionsAction
.non_archived
.page(params[:page])
@issuable_meta_data = issuable_meta_data(@issues, collection_type)
@issuable_meta_data = issuable_meta_data(@issues, collection_type, current_user)
respond_to do |format|
format.html
......@@ -22,7 +22,7 @@ module IssuableCollectionsAction
def merge_requests
@merge_requests = issuables_collection.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type, current_user)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
......
......@@ -203,17 +203,17 @@ module NotesActions
# These params are also sent by the client but we need to set these based on
# target_type and target_id because we're checking permissions based on that
create_params[:noteable_type] = params[:target_type].classify
create_params[:noteable_type] = noteable.class.name
case params[:target_type]
when 'commit'
create_params[:commit_id] = params[:target_id]
when 'merge_request'
create_params[:noteable_id] = params[:target_id]
case noteable
when Commit
create_params[:commit_id] = noteable.id
when MergeRequest
create_params[:noteable_id] = noteable.id
# Notes on MergeRequest can have an extra `commit_id` context
create_params[:commit_id] = params.dig(:note, :commit_id)
else
create_params[:noteable_id] = params[:target_id]
create_params[:noteable_id] = noteable.id
end
end
end
......
......@@ -24,6 +24,9 @@ class GroupsController < Groups::ApplicationController
before_action :user_actions, only: [:show]
before_action :validate_name, only: [:create, :update, :transfer]
skip_cross_project_access_check :index, :new, :create, :edit, :update,
:destroy, :projects
# When loading show as an atom feed, we render events that could leak cross
......@@ -220,6 +223,19 @@ class GroupsController < Groups::ApplicationController
end
end
def validate_name
group = Group.new(group_params)
unless !group.path || group.path.empty? || group.path.length > 4 || current_user.admin?
flash.now[:alert] = 'Path must have at least a length of 5.'
if action_name == 'update'
render action: 'edit'
else
@group = group
render action: 'new'
end
end
end
def build_canonical_path(group)
return group_path(group) if action_name == 'show' # root group path
......
......@@ -5,6 +5,7 @@ class PasswordsController < Devise::PasswordsController
before_action :resource_from_email, only: [:create]
before_action :check_password_authentication_available, only: [:create]
before_action :prevent_cas_reset, only: [:create]
before_action :throttle_reset, only: [:create]
# rubocop: disable CodeReuse/ActiveRecord
......@@ -55,6 +56,13 @@ class PasswordsController < Devise::PasswordsController
alert: _("Password authentication is unavailable.")
end
def prevent_cas_reset
return unless resource && resource.cas_user?
redirect_to after_sending_reset_password_instructions_path_for(resource_name),
alert: "Cannot reset password for CAS user."
end
def throttle_reset
return unless resource && resource.recently_sent_password_reset?
......
......@@ -8,20 +8,6 @@ class Profiles::AccountsController < Profiles::ApplicationController
end
# rubocop: disable CodeReuse/ActiveRecord
def unlink
provider = params[:provider]
identity = current_user.identities.find_by(provider: provider)
return render_404 unless identity
if unlink_provider_allowed?(provider)
identity.destroy
else
flash[:alert] = _("You are not allowed to unlink your primary login account")
end
redirect_to profile_account_path
end
# rubocop: enable CodeReuse/ActiveRecord
private
......
......@@ -12,6 +12,11 @@ class Projects::ApplicationController < ApplicationController
helper_method :repository, :can_collaborate_with_project?, :user_access
rescue_from Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError do |exception|
log_exception(exception)
render_404
end
private
def project
......
......@@ -3,7 +3,8 @@
class Projects::BadgesController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
before_action :no_cache_headers, only: [:pipeline, :coverage]
before_action :authorize_read_build!, only: [:pipeline, :coverage]
def pipeline
pipeline_status = Gitlab::Badge::Pipeline::Status
......
......@@ -45,7 +45,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
def set_pipeline_variables
@pipelines =
if can?(current_user, :read_pipeline, @project)
if can?(current_user, :read_pipeline, @merge_request.source_project)
@merge_request.all_pipelines
else
Ci::Pipeline.none
......
......@@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def pipelines
@pipelines = @merge_request.all_pipelines.page(params[:page]).per(30)
set_pipeline_variables
@pipelines = @pipelines.page(params[:page]).per(30)
Gitlab::PollingInterval.set_header(response, interval: 10_000)
......
# frozen_string_literal: true
class Projects::TemplatesController < Projects::ApplicationController
before_action :authenticate_user!, :get_template_class
before_action :authenticate_user!
before_action :authorize_can_read_issuable!
before_action :get_template_class
def show
template = @template_type.find(params[:key], project)
......@@ -13,9 +15,20 @@ class Projects::TemplatesController < Projects::ApplicationController
private
# User must have:
# - `read_merge_request` to see merge request templates, or
# - `read_issue` to see issue templates
#
# Note params[:template_type] has a route constraint to limit it to
# `merge_request` or `issue`
def authorize_can_read_issuable!
action = [:read_, params[:template_type]].join
authorize_action!(action)
end
def get_template_class
template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
@template_type = template_types[params[:template_type]]
render json: [], status: :not_found unless @template_type
end
end
......@@ -4,7 +4,7 @@ class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :authorize_manage_trigger!, except: [:index, :create]
before_action :authorize_admin_trigger!, only: [:edit, :update]
before_action :trigger, only: [:take_ownership, :edit, :update, :destroy]
before_action :trigger, only: [:edit, :update, :destroy]
layout 'project_settings'
......@@ -24,16 +24,6 @@ class Projects::TriggersController < Projects::ApplicationController
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def take_ownership
if trigger.update(owner: current_user)
flash[:notice] = _('Trigger was re-assigned.')
else
flash[:alert] = _('You could not take ownership of trigger.')
end
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def edit
end
......
......@@ -298,7 +298,7 @@ class ProjectsController < Projects::ApplicationController
elsif @project.feature_available?(:issues, current_user)
@issues = issuables_collection.page(params[:page])
@collection_type = 'Issue'
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
@issuable_meta_data = issuable_meta_data(@issues, @collection_type, current_user)
end
render :show
......
......@@ -5,8 +5,8 @@ class Snippets::NotesController < ApplicationController
include ToggleAwardEmoji
skip_before_action :authenticate_user!, only: [:index]
before_action :snippet
before_action :authorize_read_snippet!, only: [:show, :index, :create]
before_action :authorize_read_snippet!, only: [:show, :index]
before_action :authorize_create_note!, only: [:create]
private
......@@ -33,4 +33,8 @@ class Snippets::NotesController < ApplicationController
def authorize_read_snippet!
return render_404 unless can?(current_user, :read_personal_snippet, snippet)
end
def authorize_create_note!
access_denied! unless can?(current_user, :create_note, noteable)
end
end
......@@ -137,7 +137,7 @@ class SnippetsController < ApplicationController
def move_temporary_files
params[:files].each do |file|
FileMover.new(file, @snippet).execute
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
end
end
end
......@@ -41,7 +41,11 @@ class UploadsController < ApplicationController
when Note
can?(current_user, :read_project, model.project)
when User
true
# We validate the current user has enough (writing)
# access to itself when a secret is given.
# For instance, user avatars are readable by anyone,
# while temporary, user snippet uploads are not.
!secret? || can?(current_user, :update_user, model)
when Appearance
true
else
......@@ -56,9 +60,13 @@ class UploadsController < ApplicationController
def authorize_create_access!
return unless model
# for now we support only personal snippets comments. Only personal_snippet
# is allowed as a model to #create through routing.
authorized = can?(current_user, :create_note, model)
authorized =
case model
when User
can?(current_user, :update_user, model)
else
can?(current_user, :create_note, model)
end
render_unauthorized unless authorized
end
......@@ -75,6 +83,10 @@ class UploadsController < ApplicationController
User === model || Appearance === model
end
def secret?
params[:secret].present?
end
def upload_model_class
MODEL_CLASSES[params[:model]] || raise(UnknownUploadModelError)
end
......
......@@ -4,6 +4,8 @@ module Types
class LabelType < BaseObject
graphql_name 'Label'
authorize :read_label
field :description, GraphQL::STRING_TYPE, null: true
field :title, GraphQL::STRING_TYPE, null: false
field :color, GraphQL::STRING_TYPE, null: false
......
......@@ -4,6 +4,8 @@ module Types
class MetadataType < ::Types::BaseObject
graphql_name 'Metadata'
authorize :read_instance_metadata
field :version, GraphQL::STRING_TYPE, null: false
field :revision, GraphQL::STRING_TYPE, null: false
end
......
......@@ -4,6 +4,8 @@ module Types
class NamespaceType < BaseObject
graphql_name 'Namespace'
authorize :read_namespace
field :id, GraphQL::ID_TYPE, null: false
field :name, GraphQL::STRING_TYPE, null: false
......
......@@ -66,14 +66,14 @@ module Types
field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true
field :printing_merge_request_link_enabled, GraphQL::BOOLEAN_TYPE, null: true
field :namespace, Types::NamespaceType, null: false
field :namespace, Types::NamespaceType, null: true
field :group, Types::GroupType, null: true
field :statistics, Types::ProjectStatisticsType,
null: true,
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(obj.id).find }
field :repository, Types::RepositoryType, null: false
field :repository, Types::RepositoryType, null: true
field :merge_requests,
Types::MergeRequestType.connection_type,
......
......@@ -22,10 +22,7 @@ module Types
field :metadata, Types::MetadataType,
null: true,
resolver: Resolvers::MetadataResolver,
description: 'Metadata about GitLab' do |*args|
authorize :read_instance_metadata
end
description: 'Metadata about GitLab'
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end
......
......@@ -74,7 +74,7 @@ module IssuablesHelper
end
end
def serialize_issuable(issuable, serializer: nil)
def serialize_issuable(issuable, opts = {})
serializer_klass = case issuable
when Issue
IssueSerializer
......@@ -84,7 +84,7 @@ module IssuablesHelper
serializer_klass
.new(current_user: current_user, project: issuable.project)
.represent(issuable, serializer: serializer)
.represent(issuable, opts)
.to_json
end
......@@ -280,7 +280,7 @@ module IssuablesHelper
initialTaskStatus: issuable.task_status
}
data[:hasClosingMergeRequest] = issuable.merge_requests_count != 0 if issuable.is_a?(Issue)
data[:hasClosingMergeRequest] = issuable.merge_requests_count(current_user) != 0 if issuable.is_a?(Issue)
if parent.is_a?(Group)
data[:groupPath] = parent.path
......
......@@ -400,7 +400,7 @@ module ProjectsHelper
snippets: :read_project_snippet,
settings: :admin_project,
builds: :read_build,
clusters: :read_cluster,
#clusters: :read_cluster,
serverless: :read_cluster,
error_tracking: :read_sentry_issue,
labels: :read_label,
......@@ -462,11 +462,7 @@ module ProjectsHelper
end
def default_clone_protocol
if allowed_protocols_present?
enabled_protocol
else
extra_default_clone_protocol
end
extra_default_clone_protocol
end
def extra_default_clone_protocol
......
# frozen_string_literal: true
module SnippetsHelper
def snippets_upload_path(snippet, user)
return unless user
if snippet&.persisted?
upload_path('personal_snippet', id: snippet.id)
else
upload_path('user', id: user.id)
end
end
def reliable_snippet_path(snippet, opts = nil)
if snippet.project_id?
project_snippet_path(snippet.project, snippet, opts)
......
# frozen_string_literal: true
class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
default from: proc { default_sender_address.format }
default reply_to: proc { default_reply_to_address.format }
layout 'mailer/devise'
......@@ -17,4 +17,16 @@ class DeviseMailer < Devise::Mailer
subject.join(' | ')
end
def default_sender_address
address = Mail::Address.new(Gitlab.config.gitlab.email_from)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
def default_reply_to_address
address = Mail::Address.new(Gitlab.config.gitlab.email_reply_to)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
end
......@@ -60,7 +60,7 @@ module Emails
# `note_id` is a `Note` when originating in `NotifyPreview`
@note = note_id.is_a?(Note) ? note_id : Note.find(note_id)
@project = @note.project
@group = @note.noteable.try(:group)
@group = @project.try(:group) || @note.noteable.try(:group)
if (@project || @group) && @note.persisted?
@sent_notification = SentNotification.record_note(@note, recipient_id, reply_key)
......
......@@ -29,7 +29,11 @@ module Issuable
# This object is used to gather issuable meta data for displaying
# upvotes, downvotes, notes and closing merge requests count for issues and merge requests
# lists avoiding n+1 queries and improving performance.
IssuableMeta = Struct.new(:upvotes, :downvotes, :user_notes_count, :merge_requests_count)
IssuableMeta = Struct.new(:upvotes, :downvotes, :user_notes_count, :mrs_count) do
def merge_requests_count(user = nil)
mrs_count
end
end
included do
cache_markdown_field :title, pipeline: :single_line
......
......@@ -53,6 +53,7 @@ class Group < Namespace
validate :visibility_level_allowed_by_sub_groups
validate :visibility_level_allowed_by_parent
validates :variables, variable_duplicates: true
validates :path, format: { without: /\A([a-zA-Z]{4}[0-9]{2}|hg[0-9]+)\z/, message: "must not match the format of THM usernames" }
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
......
......@@ -250,8 +250,12 @@ class Issue < ApplicationRecord
end
# rubocop: enable CodeReuse/ServiceClass
def merge_requests_count
merge_requests_closing_issues.count
def merge_requests_count(user = nil)
::MergeRequestsClosingIssues.count_for_issue(self.id, user)
end
def labels_hook_attrs
labels.map(&:hook_attrs)
end
private
......
......@@ -7,11 +7,38 @@ class MergeRequestsClosingIssues < ApplicationRecord
validates :merge_request_id, uniqueness: { scope: :issue_id }, presence: true
validates :issue_id, presence: true
scope :with_issues, ->(ids) { where(issue_id: ids) }
scope :with_merge_requests_enabled, -> do
joins(:merge_request)
.joins('INNER JOIN project_features ON merge_requests.target_project_id = project_features.project_id')
.where('project_features.merge_requests_access_level >= :access', access: ProjectFeature::ENABLED)
end
scope :accessible_by, ->(user) do
joins(:merge_request)
.joins('INNER JOIN project_features ON merge_requests.target_project_id = project_features.project_id')
.where('project_features.merge_requests_access_level >= :access OR EXISTS(:authorizations)',
access: ProjectFeature::ENABLED,
authorizations: user.authorizations_for_projects(min_access_level: Gitlab::Access::REPORTER, related_project_column: "merge_requests.target_project_id")
)
end
class << self
def count_for_collection(ids)
group(:issue_id)
.where(issue_id: ids)
.pluck('issue_id', 'COUNT(*) as count')
def count_for_collection(ids, current_user)
closing_merge_requests(ids, current_user).group(:issue_id).pluck('issue_id', 'COUNT(*) as count')
end
def count_for_issue(id, current_user)
closing_merge_requests(id, current_user).count
end
private
def closing_merge_requests(ids, current_user)
return with_issues(ids) if current_user&.admin?
return with_issues(ids).with_merge_requests_enabled if current_user.blank?
with_issues(ids).accessible_by(current_user)
end
end
end
......@@ -457,7 +457,7 @@ class Note < ApplicationRecord
end
def banzai_render_context(field)
super.merge(noteable: noteable)
super.merge(noteable: noteable, system_note: system?)
end
def retrieve_upload(_identifier, paths)
......
......@@ -139,7 +139,6 @@ class Project < ApplicationRecord
# Project services
has_one :campfire_service
has_one :discord_service
has_one :drone_ci_service
has_one :emails_on_push_service
has_one :pipelines_email_service
......@@ -347,6 +346,7 @@ class Project < ApplicationRecord
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
validates :variables, variable_duplicates: { scope: :environment_scope }
validates :bfg_object_map, file_size: { maximum: :max_attachment_size }
validates :path, format: { without: /\A([a-zA-Z]{4}[0-9]{2}|hg[0-9]+)\z/, message: "must not match the format of THM usernames" }
# Scopes
scope :pending_delete, -> { where(pending_delete: true) }
......
# frozen_string_literal: true
require "discordrb/webhooks"
class DiscordService < ChatNotificationService
def title
s_("DiscordService|Discord Notifications")
end
def description
s_("DiscordService|Receive event notifications in Discord")
end
def self.to_param
"discord"
end
def help
"This service sends notifications about project events to Discord channels.<br />
To set up this service:
<ol>
<li><a href='https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks'>Setup a custom Incoming Webhook</a>.</li>
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
<li>Select events below to enable notifications.</li>
</ol>"
end
def event_field(event)
# No-op.
end
def default_channel_placeholder
# No-op.
end
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: "text", name: "webhook", placeholder: "e.g. https://discordapp.com/api/webhooks/…" },
{ type: "checkbox", name: "notify_only_broken_pipelines" },
{ type: "checkbox", name: "notify_only_default_branch" }
]
end
private
def notify(message, opts)
client = Discordrb::Webhooks::Client.new(url: webhook)
client.execute do |builder|
builder.content = message.pretext
end
end
def custom_data(data)
super(data).merge(markdown: true)
end
end
......@@ -35,6 +35,8 @@ class SlashCommandsService < Service
chat_user = find_chat_user(params)
if chat_user&.user
return Gitlab::SlashCommands::Presenters::Access.new.access_denied unless chat_user.user.can?(:use_slash_commands)
Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
else
url = authorize_chat_name_url(params)
......
......@@ -251,7 +251,6 @@ class Service < ApplicationRecord
bugzilla
campfire
custom_issue_tracker
discord
drone_ci
emails_on_push
external_wiki
......
......@@ -835,11 +835,11 @@ class User < ApplicationRecord
end
def allow_password_authentication_for_web?
Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user?
Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user? && !cas_user?
end
def allow_password_authentication_for_git?
Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user?
Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? && !cas_user?
end
def can_change_username?
......@@ -923,6 +923,14 @@ class User < ApplicationRecord
@ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def cas_user?
identities.exists?(["provider = ? AND extern_uid IS NOT NULL", "cas3"])
end
def cas_identity
@cas_identity ||= identities.find_by(["provider = ?", "cas3"])
end
def project_deploy_keys
DeployKey.unscoped.in_projects(authorized_projects.pluck(:id)).distinct(:id)
end
......
......@@ -33,6 +33,7 @@ class GlobalPolicy < BasePolicy
enable :access_git
enable :receive_notifications
enable :use_quick_actions
enable :use_slash_commands
end
rule { blocked | internal }.policy do
......@@ -40,6 +41,7 @@ class GlobalPolicy < BasePolicy
prevent :access_api
prevent :access_git
prevent :receive_notifications
prevent :use_slash_commands
end
rule { required_terms_not_accepted }.policy do
......@@ -57,6 +59,7 @@ class GlobalPolicy < BasePolicy
rule { access_locked }.policy do
prevent :log_in
prevent :use_slash_commands
end
rule { ~(anonymous & restricted_public_level) }.policy do
......
# frozen_string_literal: true
class RepositoryPolicy < BasePolicy
delegate { @subject.project }
end
......@@ -256,6 +256,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def autodevops_anchor_data(show_auto_devops_callout: false)
return
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
if auto_devops_enabled?
AnchorData.new(false,
......@@ -275,6 +276,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def kubernetes_cluster_anchor_data
return
if current_user && can?(current_user, :create_cluster, project)
if clusters.empty?
......
......@@ -16,9 +16,14 @@ class IssueEntity < IssuableEntity
expose :discussion_locked
expose :assignees, using: API::Entities::UserBasic
expose :due_date
expose :moved_to_id
expose :project_id
expose :moved_to_id do |issue|
if issue.moved_to_id.present? && can?(request.current_user, :read_issue, issue.moved_to)
issue.moved_to_id
end
end
expose :web_url do |issue|
project_issue_path(issue.project, issue)
end
......
......@@ -114,7 +114,10 @@ class MergeRequestWidgetEntity < IssuableEntity
presenter(merge_request).ci_status
end
expose :issues_links do
# Rendering and redacting Markdown can be expensive. These links are
# just nice to have in the merge request widget, so only
# include them if they are explicitly requested on first load.
expose :issues_links, if: -> (_, opts) { opts[:issues_links] } do
expose :assign_to_closing do |merge_request|
presenter(merge_request).assign_to_closing_issues_link
end
......
......@@ -7,7 +7,18 @@ module Ci
# Otherwise, multiple pipelines could be created in a short interval.
schedule.schedule_next_run!
RunPipelineScheduleWorker.perform_async(schedule.id, schedule.owner&.id)
if Feature.enabled?(:ci_pipeline_schedule_async)
RunPipelineScheduleWorker.perform_async(schedule.id, schedule.owner&.id)
else
begin
RunPipelineScheduleWorker.new.perform(schedule.id, schedule.owner&.id)