New upstream version 8.13.6+dfsg1
This commit is contained in:
parent
dbe389e2f0
commit
819c14ecb5
153 changed files with 1837 additions and 563 deletions
38
CHANGELOG.md
38
CHANGELOG.md
|
@ -1,5 +1,43 @@
|
||||||
Please view this file on the master branch, on stable branches it's out of date.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
|
## 8.13.6 (2016-11-17)
|
||||||
|
|
||||||
|
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
|
||||||
|
- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
|
||||||
|
- Fix cache for commit status in commits list to respect branches. !7372
|
||||||
|
- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
|
||||||
|
- Limit labels returned for a specific project as an administrator. !7496
|
||||||
|
- Clicking "force remove source branch" label now toggles the checkbox again.
|
||||||
|
- Allow commit note to be visible if repo is visible.
|
||||||
|
- Fix project Visibility Level selector not using default values.
|
||||||
|
|
||||||
|
## 8.13.5 (2016-11-08)
|
||||||
|
|
||||||
|
- Restore unauthenticated access to public container registries
|
||||||
|
|
||||||
|
## 8.13.4 (2016-11-07)
|
||||||
|
|
||||||
|
- Fix showing pipeline status for a given commit from correct branch. !7034
|
||||||
|
- Only skip group when it's actually a group in the "Share with group" select. !7262
|
||||||
|
- Introduce round-robin project creation to spread load over multiple shards. !7266
|
||||||
|
- Ensure merge request's "remove branch" accessors return booleans. !7267
|
||||||
|
- Ensure external users are not able to clone disabled repositories.
|
||||||
|
- Fix XSS issue in Markdown autolinker.
|
||||||
|
- Respect event visibility in Gitlab::ContributionsCalendar.
|
||||||
|
- Honour issue and merge request visibility in their respective finders.
|
||||||
|
- Disable reference Markdown for unavailable features.
|
||||||
|
- Fix lightweight tags not processed correctly by GitTagPushService. !6532
|
||||||
|
- Allow owners to fetch source code in CI builds. !6943
|
||||||
|
- Return conflict error in label API when title is taken by group label. !7014
|
||||||
|
- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project. !7123
|
||||||
|
- Fix builds tab visibility. !7178
|
||||||
|
- Fix project features default values. !7181
|
||||||
|
|
||||||
|
## 8.13.3 (2016-11-02)
|
||||||
|
|
||||||
|
- Removes any symlinks before importing a project export file. CVE-2016-9086
|
||||||
|
- Fixed Import/Export foreign key issue to do with project members.
|
||||||
|
|
||||||
## 8.13.2 (2016-10-31)
|
## 8.13.2 (2016-10-31)
|
||||||
|
|
||||||
- Fix encoding issues on pipeline commits. !6832
|
- Fix encoding issues on pipeline commits. !6832
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -51,7 +51,7 @@ gem 'browser', '~> 2.2'
|
||||||
|
|
||||||
# Extracting information from a git repository
|
# Extracting information from a git repository
|
||||||
# Provide access to Gitlab::Git library
|
# Provide access to Gitlab::Git library
|
||||||
gem 'gitlab_git', '~> 10.6.8'
|
gem 'gitlab_git', '~> 10.7.0'
|
||||||
|
|
||||||
# LDAP Auth
|
# LDAP Auth
|
||||||
# GitLab fork with several improvements to original library. For full list of changes
|
# GitLab fork with several improvements to original library. For full list of changes
|
||||||
|
|
|
@ -283,7 +283,7 @@ GEM
|
||||||
mime-types (>= 1.16, < 3)
|
mime-types (>= 1.16, < 3)
|
||||||
posix-spawn (~> 0.3)
|
posix-spawn (~> 0.3)
|
||||||
gitlab-markup (1.5.0)
|
gitlab-markup (1.5.0)
|
||||||
gitlab_git (10.6.8)
|
gitlab_git (10.7.0)
|
||||||
activesupport (~> 4.0)
|
activesupport (~> 4.0)
|
||||||
charlock_holmes (~> 0.7.3)
|
charlock_holmes (~> 0.7.3)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
|
@ -867,7 +867,7 @@ DEPENDENCIES
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||||
gitlab-markup (~> 1.5.0)
|
gitlab-markup (~> 1.5.0)
|
||||||
gitlab_git (~> 10.6.8)
|
gitlab_git (~> 10.7.0)
|
||||||
gitlab_omniauth-ldap (~> 1.2.1)
|
gitlab_omniauth-ldap (~> 1.2.1)
|
||||||
gollum-lib (~> 4.2)
|
gollum-lib (~> 4.2)
|
||||||
gollum-rugged_adapter (~> 0.4.2)
|
gollum-rugged_adapter (~> 0.4.2)
|
||||||
|
@ -994,4 +994,4 @@ DEPENDENCIES
|
||||||
wikicloth (= 0.8.1)
|
wikicloth (= 0.8.1)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.13.2
|
1.13.5
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.13.3
|
8.13.6
|
||||||
|
|
|
@ -127,19 +127,30 @@
|
||||||
return $(document).off('scroll');
|
return $(document).off('scroll');
|
||||||
};
|
};
|
||||||
|
|
||||||
window.shiftWindow = function() {
|
|
||||||
return scrollBy(0, -100);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("page:fetch", unbindEvents);
|
document.addEventListener("page:fetch", unbindEvents);
|
||||||
|
|
||||||
window.addEventListener("hashchange", shiftWindow);
|
// automatically adjust scroll position for hash urls taking the height of the navbar into account
|
||||||
|
// https://github.com/twitter/bootstrap/issues/1768
|
||||||
|
window.adjustScroll = function() {
|
||||||
|
var navbar = document.querySelector('.navbar-gitlab');
|
||||||
|
var subnav = document.querySelector('.layout-nav');
|
||||||
|
var fixedTabs = document.querySelector('.js-tabs-affix');
|
||||||
|
|
||||||
window.onload = function() {
|
adjustment = 0;
|
||||||
|
if (navbar) adjustment -= navbar.offsetHeight;
|
||||||
|
if (subnav) adjustment -= subnav.offsetHeight;
|
||||||
|
if (fixedTabs) adjustment -= fixedTabs.offsetHeight;
|
||||||
|
|
||||||
|
return scrollBy(0, adjustment);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("hashchange", adjustScroll);
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
// Scroll the window to avoid the topnav bar
|
// Scroll the window to avoid the topnav bar
|
||||||
// https://github.com/twitter/bootstrap/issues/1768
|
// https://github.com/twitter/bootstrap/issues/1768
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
return setTimeout(shiftWindow, 100);
|
return setTimeout(adjustScroll, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@
|
||||||
MergeRequestTabs.prototype.scrollToElement = function(container) {
|
MergeRequestTabs.prototype.scrollToElement = function(container) {
|
||||||
var $el, navBarHeight;
|
var $el, navBarHeight;
|
||||||
if (window.location.hash) {
|
if (window.location.hash) {
|
||||||
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
|
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + document.querySelector('.js-tabs-affix').offsetHeight;
|
||||||
$el = $(container + " " + window.location.hash + ":not(.match)");
|
$el = $(container + " " + window.location.hash + ":not(.match)");
|
||||||
if ($el.length) {
|
if ($el.length) {
|
||||||
return $.scrollTo(container + " " + window.location.hash + ":not(.match)", {
|
return $.scrollTo(container + " " + window.location.hash + ":not(.match)", {
|
||||||
|
|
|
@ -116,8 +116,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
:metrics_packet_size,
|
:metrics_packet_size,
|
||||||
:send_user_confirmation_email,
|
:send_user_confirmation_email,
|
||||||
:container_registry_token_expire_delay,
|
:container_registry_token_expire_delay,
|
||||||
:repository_storage,
|
|
||||||
:enabled_git_access_protocol,
|
:enabled_git_access_protocol,
|
||||||
|
repository_storages: [],
|
||||||
restricted_visibility_levels: [],
|
restricted_visibility_levels: [],
|
||||||
import_sources: [],
|
import_sources: [],
|
||||||
disabled_oauth_sign_in_sources: []
|
disabled_oauth_sign_in_sources: []
|
||||||
|
|
|
@ -192,9 +192,10 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
# JSON for infinite scroll via Pager object
|
# JSON for infinite scroll via Pager object
|
||||||
def pager_json(partial, count)
|
def pager_json(partial, count, locals = {})
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
partial,
|
partial,
|
||||||
|
locals: locals,
|
||||||
layout: false,
|
layout: false,
|
||||||
formats: [:html]
|
formats: [:html]
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,7 @@ class JwtController < ApplicationController
|
||||||
return head :not_found unless service
|
return head :not_found unless service
|
||||||
|
|
||||||
result = service.new(@authentication_result.project, @authentication_result.actor, auth_params).
|
result = service.new(@authentication_result.project, @authentication_result.actor, auth_params).
|
||||||
execute(authentication_abilities: @authentication_result.authentication_abilities || [])
|
execute(authentication_abilities: @authentication_result.authentication_abilities)
|
||||||
|
|
||||||
render json: result, status: result[:http_status]
|
render json: result, status: result[:http_status]
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ class JwtController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def authenticate_project_or_user
|
def authenticate_project_or_user
|
||||||
@authentication_result = Gitlab::Auth::Result.new
|
@authentication_result = Gitlab::Auth::Result.new(nil, nil, :none, Gitlab::Auth.read_authentication_abilities)
|
||||||
|
|
||||||
authenticate_with_http_basic do |login, password|
|
authenticate_with_http_basic do |login, password|
|
||||||
@authentication_result = Gitlab::Auth.find_for_git_client(login, password, project: nil, ip: request.ip)
|
@authentication_result = Gitlab::Auth.find_for_git_client(login, password, project: nil, ip: request.ip)
|
||||||
|
|
|
@ -26,8 +26,15 @@ class Projects::CommitsController < Projects::ApplicationController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.json { pager_json("projects/commits/_commits", @commits.size) }
|
|
||||||
format.atom { render layout: false }
|
format.atom { render layout: false }
|
||||||
|
|
||||||
|
format.json do
|
||||||
|
pager_json(
|
||||||
|
'projects/commits/_commits',
|
||||||
|
@commits.size,
|
||||||
|
project: @project,
|
||||||
|
ref: @ref)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,10 +21,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
||||||
def authenticate_user
|
def authenticate_user
|
||||||
@authentication_result = Gitlab::Auth::Result.new
|
@authentication_result = Gitlab::Auth::Result.new
|
||||||
|
|
||||||
if project && project.public? && download_request?
|
|
||||||
return # Allow access
|
|
||||||
end
|
|
||||||
|
|
||||||
if allow_basic_auth? && basic_auth_provided?
|
if allow_basic_auth? && basic_auth_provided?
|
||||||
login, password = user_name_and_password(request)
|
login, password = user_name_and_password(request)
|
||||||
|
|
||||||
|
@ -41,6 +37,10 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
||||||
send_final_spnego_response
|
send_final_spnego_response
|
||||||
return # Allow access
|
return # Allow access
|
||||||
end
|
end
|
||||||
|
elsif project && download_request? && Guest.can?(:download_code, project)
|
||||||
|
@authentication_result = Gitlab::Auth::Result.new(nil, project, :none, [:download_code])
|
||||||
|
|
||||||
|
return # Allow access
|
||||||
end
|
end
|
||||||
|
|
||||||
send_challenges
|
send_challenges
|
||||||
|
|
|
@ -78,11 +78,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
|
||||||
def upload_pack_allowed?
|
def upload_pack_allowed?
|
||||||
return false unless Gitlab.config.gitlab_shell.upload_pack
|
return false unless Gitlab.config.gitlab_shell.upload_pack
|
||||||
|
|
||||||
if user
|
access_check.allowed? || ci?
|
||||||
access_check.allowed?
|
|
||||||
else
|
|
||||||
ci? || project.public?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def access
|
def access
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
|
||||||
@group_links = project.project_group_links.all
|
@group_links = project.project_group_links.all
|
||||||
|
|
||||||
@skip_groups = @group_links.pluck(:group_id)
|
@skip_groups = @group_links.pluck(:group_id)
|
||||||
@skip_groups << project.group.try(:id)
|
@skip_groups << project.namespace_id unless project.personal?
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -126,7 +126,7 @@ class Projects::LabelsController < Projects::ApplicationController
|
||||||
alias_method :subscribable_resource, :label
|
alias_method :subscribable_resource, :label
|
||||||
|
|
||||||
def find_labels
|
def find_labels
|
||||||
@available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute.includes(:priorities)
|
@available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_admin_labels!
|
def authorize_admin_labels!
|
||||||
|
|
|
@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def objects
|
|
||||||
@objects ||= (params[:objects] || []).to_a
|
|
||||||
end
|
|
||||||
|
|
||||||
def existing_oids
|
def existing_oids
|
||||||
@existing_oids ||= begin
|
@existing_oids ||= begin
|
||||||
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
|
||||||
|
|
|
@ -352,13 +352,23 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
def branch_from
|
def branch_from
|
||||||
# This is always source
|
# This is always source
|
||||||
@source_project = @merge_request.nil? ? @project : @merge_request.source_project
|
@source_project = @merge_request.nil? ? @project : @merge_request.source_project
|
||||||
@commit = @repository.commit(params[:ref]) if params[:ref].present?
|
|
||||||
|
if params[:ref].present?
|
||||||
|
@ref = params[:ref]
|
||||||
|
@commit = @repository.commit(@ref)
|
||||||
|
end
|
||||||
|
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def branch_to
|
def branch_to
|
||||||
@target_project = selected_target_project
|
@target_project = selected_target_project
|
||||||
@commit = @target_project.commit(params[:ref]) if params[:ref].present?
|
|
||||||
|
if params[:ref].present?
|
||||||
|
@ref = params[:ref]
|
||||||
|
@commit = @target_project.commit(@ref)
|
||||||
|
end
|
||||||
|
|
||||||
render layout: false
|
render layout: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -497,6 +507,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
@merge_request.close
|
@merge_request.close
|
||||||
end
|
end
|
||||||
|
|
||||||
|
labels
|
||||||
define_pipelines_vars
|
define_pipelines_vars
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Projects::TagsController < Projects::ApplicationController
|
||||||
return render_404 unless @tag
|
return render_404 unless @tag
|
||||||
|
|
||||||
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
|
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
|
||||||
@commit = @repository.commit(@tag.target)
|
@commit = @repository.commit(@tag.dereferenced_target)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -287,7 +287,8 @@ class ProjectsController < Projects::ApplicationController
|
||||||
render 'projects/empty' if @project.empty_repo?
|
render 'projects/empty' if @project.empty_repo?
|
||||||
else
|
else
|
||||||
if @project.wiki_enabled?
|
if @project.wiki_enabled?
|
||||||
@wiki_home = @project.wiki.find_page('home', params[:version_id])
|
@project_wiki = @project.wiki
|
||||||
|
@wiki_home = @project_wiki.find_page('home', params[:version_id])
|
||||||
elsif @project.feature_available?(:issues, current_user)
|
elsif @project.feature_available?(:issues, current_user)
|
||||||
@issues = issues_collection
|
@issues = issues_collection
|
||||||
@issues = @issues.page(params[:page])
|
@issues = @issues.page(params[:page])
|
||||||
|
|
|
@ -104,8 +104,7 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def contributions_calendar
|
def contributions_calendar
|
||||||
@contributions_calendar ||= Gitlab::ContributionsCalendar.
|
@contributions_calendar ||= Gitlab::ContributionsCalendar.new(user, current_user)
|
||||||
new(contributed_projects, user)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_events
|
def load_events
|
||||||
|
|
|
@ -61,31 +61,26 @@ class IssuableFinder
|
||||||
def project
|
def project
|
||||||
return @project if defined?(@project)
|
return @project if defined?(@project)
|
||||||
|
|
||||||
if project?
|
project = Project.find(params[:project_id])
|
||||||
@project = Project.find(params[:project_id])
|
project = nil unless Ability.allowed?(current_user, :"read_#{klass.to_ability_name}", project)
|
||||||
|
|
||||||
unless Ability.allowed?(current_user, :read_project, @project)
|
@project = project
|
||||||
@project = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@project = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
@project
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects
|
def projects
|
||||||
return @projects if defined?(@projects)
|
return @projects if defined?(@projects)
|
||||||
|
return @projects = project if project?
|
||||||
|
|
||||||
if project?
|
projects =
|
||||||
@projects = project
|
if current_user && params[:authorized_only].presence && !current_user_related?
|
||||||
elsif current_user && params[:authorized_only].presence && !current_user_related?
|
current_user.authorized_projects
|
||||||
@projects = current_user.authorized_projects.reorder(nil)
|
elsif group
|
||||||
elsif group
|
GroupProjectsFinder.new(group).execute(current_user)
|
||||||
@projects = GroupProjectsFinder.new(group).execute(current_user).reorder(nil)
|
else
|
||||||
else
|
ProjectsFinder.new.execute(current_user)
|
||||||
@projects = ProjectsFinder.new.execute(current_user).reorder(nil)
|
end
|
||||||
end
|
|
||||||
|
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search
|
def search
|
||||||
|
@ -126,7 +121,7 @@ class IssuableFinder
|
||||||
|
|
||||||
@labels =
|
@labels =
|
||||||
if labels? && !filter_by_no_label?
|
if labels? && !filter_by_no_label?
|
||||||
LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute
|
LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true)
|
||||||
else
|
else
|
||||||
Label.none
|
Label.none
|
||||||
end
|
end
|
||||||
|
@ -273,7 +268,7 @@ class IssuableFinder
|
||||||
items = items.with_label(label_names, params[:sort])
|
items = items.with_label(label_names, params[:sort])
|
||||||
|
|
||||||
if projects
|
if projects
|
||||||
label_ids = LabelsFinder.new(current_user, project_ids: projects).execute.select(:id)
|
label_ids = LabelsFinder.new(current_user, project_ids: projects).execute(skip_authorization: true).select(:id)
|
||||||
items = items.where(labels: { id: label_ids })
|
items = items.where(labels: { id: label_ids })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder
|
||||||
|
|
||||||
def execute(skip_authorization: false)
|
def execute(skip_authorization: false)
|
||||||
@skip_authorization = skip_authorization
|
@skip_authorization = skip_authorization
|
||||||
items = find_union(label_ids, Label)
|
items = find_union(label_ids, Label) || Label.none
|
||||||
items = with_title(items)
|
items = with_title(items)
|
||||||
sort(items)
|
sort(items)
|
||||||
end
|
end
|
||||||
|
@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder
|
||||||
def label_ids
|
def label_ids
|
||||||
label_ids = []
|
label_ids = []
|
||||||
|
|
||||||
if project
|
if project?
|
||||||
label_ids << project.group.labels if project.group.present?
|
if project
|
||||||
label_ids << project.labels
|
label_ids << project.group.labels if project.group.present?
|
||||||
|
label_ids << project.labels
|
||||||
|
end
|
||||||
else
|
else
|
||||||
label_ids << Label.where(group_id: projects.group_ids)
|
label_ids << Label.where(group_id: projects.group_ids)
|
||||||
label_ids << Label.where(project_id: projects.select(:id))
|
label_ids << Label.where(project_id: projects.select(:id))
|
||||||
|
@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder
|
||||||
items.where(title: title)
|
items.where(title: title)
|
||||||
end
|
end
|
||||||
|
|
||||||
def group_id
|
def group?
|
||||||
params[:group_id].presence
|
params[:group_id].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_id
|
def project?
|
||||||
params[:project_id].presence
|
params[:project_id].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects_ids
|
def projects?
|
||||||
params[:project_ids]
|
params[:project_ids].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
|
@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder
|
||||||
def project
|
def project
|
||||||
return @project if defined?(@project)
|
return @project if defined?(@project)
|
||||||
|
|
||||||
if project_id
|
if project?
|
||||||
@project = find_project
|
@project = Project.find(params[:project_id])
|
||||||
|
@project = nil unless authorized_to_read_labels?(@project)
|
||||||
else
|
else
|
||||||
@project = nil
|
@project = nil
|
||||||
end
|
end
|
||||||
|
@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder
|
||||||
@project
|
@project
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_project
|
|
||||||
if skip_authorization
|
|
||||||
Project.find_by(id: project_id)
|
|
||||||
else
|
|
||||||
available_projects.find_by(id: project_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def projects
|
def projects
|
||||||
return @projects if defined?(@projects)
|
return @projects if defined?(@projects)
|
||||||
|
|
||||||
@projects = skip_authorization ? Project.all : available_projects
|
@projects = skip_authorization ? Project.all : ProjectsFinder.new.execute(current_user)
|
||||||
@projects = @projects.in_namespace(group_id) if group_id
|
@projects = @projects.in_namespace(params[:group_id]) if group?
|
||||||
@projects = @projects.where(id: projects_ids) if projects_ids
|
@projects = @projects.where(id: params[:project_ids]) if projects?
|
||||||
@projects = @projects.reorder(nil)
|
@projects = @projects.reorder(nil)
|
||||||
|
|
||||||
@projects
|
@projects
|
||||||
end
|
end
|
||||||
|
|
||||||
def available_projects
|
def authorized_to_read_labels?(project)
|
||||||
@available_projects ||= ProjectsFinder.new.execute(current_user)
|
return true if skip_authorization
|
||||||
|
|
||||||
|
Ability.allowed?(current_user, :read_label, project)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,11 +93,11 @@ module ApplicationSettingsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def repository_storage_options_for_select
|
def repository_storages_options_for_select
|
||||||
options = Gitlab.config.repositories.storages.map do |name, path|
|
options = Gitlab.config.repositories.storages.map do |name, path|
|
||||||
["#{name} - #{path}", name]
|
["#{name} - #{path}", name]
|
||||||
end
|
end
|
||||||
|
|
||||||
options_for_select(options, @application_setting.repository_storage)
|
options_for_select(options, @application_setting.repository_storages)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,10 +54,18 @@ module CiStatusHelper
|
||||||
custom_icon(icon_name)
|
custom_icon(icon_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_commit_status(commit, tooltip_placement: 'auto left')
|
def render_commit_status(commit, ref: nil, tooltip_placement: 'auto left')
|
||||||
project = commit.project
|
project = commit.project
|
||||||
path = pipelines_namespace_project_commit_path(project.namespace, project, commit)
|
path = pipelines_namespace_project_commit_path(
|
||||||
render_status_with_link('commit', commit.status, path, tooltip_placement: tooltip_placement)
|
project.namespace,
|
||||||
|
project,
|
||||||
|
commit)
|
||||||
|
|
||||||
|
render_status_with_link(
|
||||||
|
'commit',
|
||||||
|
commit.status(ref),
|
||||||
|
path,
|
||||||
|
tooltip_placement: tooltip_placement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
|
def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
|
||||||
|
|
|
@ -25,9 +25,11 @@ module CommitsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_to_html(commit, project, inline = true)
|
def commit_to_html(commit, ref, project)
|
||||||
template = inline ? "inline_commit" : "commit"
|
render 'projects/commits/commit',
|
||||||
render "projects/commits/#{template}", commit: commit, project: project unless commit.nil?
|
commit: commit,
|
||||||
|
ref: ref,
|
||||||
|
project: project
|
||||||
end
|
end
|
||||||
|
|
||||||
# Breadcrumb links for a Project and, if applicable, a tree path
|
# Breadcrumb links for a Project and, if applicable, a tree path
|
||||||
|
|
|
@ -80,7 +80,7 @@ module EventsHelper
|
||||||
elsif event.merge_request?
|
elsif event.merge_request?
|
||||||
namespace_project_merge_request_url(event.project.namespace,
|
namespace_project_merge_request_url(event.project.namespace,
|
||||||
event.project, event.merge_request)
|
event.project, event.merge_request)
|
||||||
elsif event.note? && event.commit_note?
|
elsif event.commit_note?
|
||||||
namespace_project_commit_url(event.project.namespace, event.project,
|
namespace_project_commit_url(event.project.namespace, event.project,
|
||||||
event.note_target)
|
event.note_target)
|
||||||
elsif event.note?
|
elsif event.note?
|
||||||
|
@ -121,7 +121,7 @@ module EventsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def event_note_target_path(event)
|
def event_note_target_path(event)
|
||||||
if event.note? && event.commit_note?
|
if event.commit_note?
|
||||||
namespace_project_commit_path(event.project.namespace,
|
namespace_project_commit_path(event.project.namespace,
|
||||||
event.project,
|
event.project,
|
||||||
event.note_target,
|
event.note_target,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module LfsHelper
|
module LfsHelper
|
||||||
include Gitlab::Routing.url_helpers
|
include Gitlab::Routing.url_helpers
|
||||||
|
|
||||||
def require_lfs_enabled!
|
def require_lfs_enabled!
|
||||||
return if Gitlab.config.lfs.enabled
|
return if Gitlab.config.lfs.enabled
|
||||||
|
|
||||||
|
@ -27,7 +27,11 @@ module LfsHelper
|
||||||
def lfs_download_access?
|
def lfs_download_access?
|
||||||
return false unless project.lfs_enabled?
|
return false unless project.lfs_enabled?
|
||||||
|
|
||||||
project.public? || ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
|
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
|
||||||
|
end
|
||||||
|
|
||||||
|
def objects
|
||||||
|
@objects ||= (params[:objects] || []).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_can_download_code?
|
def user_can_download_code?
|
||||||
|
|
|
@ -174,10 +174,14 @@ module ProjectsHelper
|
||||||
nav_tabs << :merge_requests
|
nav_tabs << :merge_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
if can?(current_user, :read_build, project)
|
if can?(current_user, :read_pipeline, project)
|
||||||
nav_tabs << :pipelines
|
nav_tabs << :pipelines
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if can?(current_user, :read_build, project)
|
||||||
|
nav_tabs << :builds
|
||||||
|
end
|
||||||
|
|
||||||
if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
|
if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
|
||||||
nav_tabs << :container_registry
|
nav_tabs << :container_registry
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,7 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
serialize :disabled_oauth_sign_in_sources, Array
|
serialize :disabled_oauth_sign_in_sources, Array
|
||||||
serialize :domain_whitelist, Array
|
serialize :domain_whitelist, Array
|
||||||
serialize :domain_blacklist, Array
|
serialize :domain_blacklist, Array
|
||||||
|
serialize :repository_storages
|
||||||
|
|
||||||
cache_markdown_field :sign_in_text
|
cache_markdown_field :sign_in_text
|
||||||
cache_markdown_field :help_page_text
|
cache_markdown_field :help_page_text
|
||||||
|
@ -74,9 +75,8 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
presence: true,
|
presence: true,
|
||||||
numericality: { only_integer: true, greater_than: 0 }
|
numericality: { only_integer: true, greater_than: 0 }
|
||||||
|
|
||||||
validates :repository_storage,
|
validates :repository_storages, presence: true
|
||||||
presence: true,
|
validate :check_repository_storages
|
||||||
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
|
|
||||||
|
|
||||||
validates :enabled_git_access_protocol,
|
validates :enabled_git_access_protocol,
|
||||||
inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
|
inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
|
||||||
|
@ -166,7 +166,7 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
disabled_oauth_sign_in_sources: [],
|
disabled_oauth_sign_in_sources: [],
|
||||||
send_user_confirmation_email: false,
|
send_user_confirmation_email: false,
|
||||||
container_registry_token_expire_delay: 5,
|
container_registry_token_expire_delay: 5,
|
||||||
repository_storage: 'default',
|
repository_storages: ['default'],
|
||||||
user_default_external: false,
|
user_default_external: false,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -201,6 +201,25 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
self.domain_blacklist_raw = file.read
|
self.domain_blacklist_raw = file.read
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def repository_storages
|
||||||
|
Array(read_attribute(:repository_storages))
|
||||||
|
end
|
||||||
|
|
||||||
|
# repository_storage is still required in the API. Remove in 9.0
|
||||||
|
def repository_storage
|
||||||
|
repository_storages.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def repository_storage=(value)
|
||||||
|
self.repository_storages = [value]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Choose one of the available repository storage options. Currently all have
|
||||||
|
# equal weighting.
|
||||||
|
def pick_repository_storage
|
||||||
|
repository_storages.sample
|
||||||
|
end
|
||||||
|
|
||||||
def runners_registration_token
|
def runners_registration_token
|
||||||
ensure_runners_registration_token!
|
ensure_runners_registration_token!
|
||||||
end
|
end
|
||||||
|
@ -208,4 +227,12 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
def health_check_access_token
|
def health_check_access_token
|
||||||
ensure_health_check_access_token!
|
ensure_health_check_access_token!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_repository_storages
|
||||||
|
invalid = repository_storages - Gitlab.config.repositories.storages.keys
|
||||||
|
errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
|
||||||
|
invalid.empty?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -226,12 +226,19 @@ class Commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def pipelines
|
def pipelines
|
||||||
@pipeline ||= project.pipelines.where(sha: sha)
|
project.pipelines.where(sha: sha)
|
||||||
end
|
end
|
||||||
|
|
||||||
def status
|
def status(ref = nil)
|
||||||
return @status if defined?(@status)
|
@statuses ||= {}
|
||||||
@status ||= pipelines.status
|
|
||||||
|
if @statuses.key?(ref)
|
||||||
|
@statuses[ref]
|
||||||
|
elsif ref
|
||||||
|
@statuses[ref] = pipelines.where(ref: ref).status
|
||||||
|
else
|
||||||
|
@statuses[ref] = pipelines.status
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def revert_branch_name
|
def revert_branch_name
|
||||||
|
|
|
@ -182,6 +182,10 @@ module Issuable
|
||||||
|
|
||||||
grouping_columns
|
grouping_columns
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_ability_name
|
||||||
|
model_name.singular
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def today?
|
def today?
|
||||||
|
@ -243,7 +247,7 @@ module Issuable
|
||||||
# issuable.class # => MergeRequest
|
# issuable.class # => MergeRequest
|
||||||
# issuable.to_ability_name # => "merge_request"
|
# issuable.to_ability_name # => "merge_request"
|
||||||
def to_ability_name
|
def to_ability_name
|
||||||
self.class.to_s.underscore
|
self.class.to_ability_name
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a Hash of attributes to be used for Twitter card metadata
|
# Returns a Hash of attributes to be used for Twitter card metadata
|
||||||
|
|
|
@ -31,7 +31,7 @@ module ProjectFeaturesCompatibility
|
||||||
def write_feature_attribute(field, value)
|
def write_feature_attribute(field, value)
|
||||||
build_project_feature unless project_feature
|
build_project_feature unless project_feature
|
||||||
|
|
||||||
access_level = value == "true" ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
|
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
|
||||||
project_feature.update_attribute(field, access_level)
|
project_feature.update_attribute(field, access_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,6 +48,7 @@ class Event < ActiveRecord::Base
|
||||||
update_all(updated_at: Time.now)
|
update_all(updated_at: Time.now)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Update Gitlab::ContributionsCalendar#activity_dates if this changes
|
||||||
def contributions
|
def contributions
|
||||||
where("action = ? OR (target_type in (?) AND action in (?))",
|
where("action = ? OR (target_type in (?) AND action in (?))",
|
||||||
Event::PUSHED, ["MergeRequest", "Issue"],
|
Event::PUSHED, ["MergeRequest", "Issue"],
|
||||||
|
@ -60,8 +61,8 @@ class Event < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_to_user?(user = nil)
|
def visible_to_user?(user = nil)
|
||||||
if push?
|
if push? || commit_note?
|
||||||
true
|
Ability.allowed?(user, :download_code, project)
|
||||||
elsif membership_changed?
|
elsif membership_changed?
|
||||||
true
|
true
|
||||||
elsif created_project?
|
elsif created_project?
|
||||||
|
@ -275,7 +276,7 @@ class Event < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_note?
|
def commit_note?
|
||||||
target.for_commit?
|
note? && target && target.for_commit?
|
||||||
end
|
end
|
||||||
|
|
||||||
def issue_note?
|
def issue_note?
|
||||||
|
@ -287,7 +288,7 @@ class Event < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_snippet_note?
|
def project_snippet_note?
|
||||||
target.for_snippet?
|
note? && target && target.for_snippet?
|
||||||
end
|
end
|
||||||
|
|
||||||
def note_target
|
def note_target
|
||||||
|
|
|
@ -5,6 +5,10 @@ class GroupLabel < Label
|
||||||
|
|
||||||
alias_attribute :subject, :group
|
alias_attribute :subject, :group
|
||||||
|
|
||||||
|
def subject_foreign_key
|
||||||
|
'group_id'
|
||||||
|
end
|
||||||
|
|
||||||
def to_reference(source_project = nil, target_project = nil, format: :id)
|
def to_reference(source_project = nil, target_project = nil, format: :id)
|
||||||
super(source_project, target_project, format: format)
|
super(source_project, target_project, format: format)
|
||||||
end
|
end
|
||||||
|
|
7
app/models/guest.rb
Normal file
7
app/models/guest.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class Guest
|
||||||
|
class << self
|
||||||
|
def can?(action, subject)
|
||||||
|
Ability.allowed?(nil, action, subject)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -245,31 +245,11 @@ class Issue < ActiveRecord::Base
|
||||||
# Returns `true` if the current issue can be viewed by either a logged in User
|
# Returns `true` if the current issue can be viewed by either a logged in User
|
||||||
# or an anonymous user.
|
# or an anonymous user.
|
||||||
def visible_to_user?(user = nil)
|
def visible_to_user?(user = nil)
|
||||||
|
return false unless project.feature_available?(:issues, user)
|
||||||
|
|
||||||
user ? readable_by?(user) : publicly_visible?
|
user ? readable_by?(user) : publicly_visible?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns `true` if the given User can read the current Issue.
|
|
||||||
def readable_by?(user)
|
|
||||||
if user.admin?
|
|
||||||
true
|
|
||||||
elsif project.owner == user
|
|
||||||
true
|
|
||||||
elsif confidential?
|
|
||||||
author == user ||
|
|
||||||
assignee == user ||
|
|
||||||
project.team.member?(user, Gitlab::Access::REPORTER)
|
|
||||||
else
|
|
||||||
project.public? ||
|
|
||||||
project.internal? && !user.external? ||
|
|
||||||
project.team.member?(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns `true` if this Issue is visible to everybody.
|
|
||||||
def publicly_visible?
|
|
||||||
project.public? && !confidential?
|
|
||||||
end
|
|
||||||
|
|
||||||
def overdue?
|
def overdue?
|
||||||
due_date.try(:past?) || false
|
due_date.try(:past?) || false
|
||||||
end
|
end
|
||||||
|
@ -290,4 +270,32 @@ class Issue < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Returns `true` if the given User can read the current Issue.
|
||||||
|
#
|
||||||
|
# This method duplicates the same check of issue_policy.rb
|
||||||
|
# for performance reasons, check commit: 002ad215818450d2cbbc5fa065850a953dc7ada8
|
||||||
|
# Make sure to sync this method with issue_policy.rb
|
||||||
|
def readable_by?(user)
|
||||||
|
if user.admin?
|
||||||
|
true
|
||||||
|
elsif project.owner == user
|
||||||
|
true
|
||||||
|
elsif confidential?
|
||||||
|
author == user ||
|
||||||
|
assignee == user ||
|
||||||
|
project.team.member?(user, Gitlab::Access::REPORTER)
|
||||||
|
else
|
||||||
|
project.public? ||
|
||||||
|
project.internal? && !user.external? ||
|
||||||
|
project.team.member?(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns `true` if this Issue is visible to everybody.
|
||||||
|
def publicly_visible?
|
||||||
|
project.public? && !confidential?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,16 +92,23 @@ class Label < ActiveRecord::Base
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_issues_count(user = nil, project = nil)
|
def open_issues_count(user = nil)
|
||||||
issues_count(user, project_id: project.try(:id) || project_id, state: 'opened')
|
issues_count(user, state: 'opened')
|
||||||
end
|
end
|
||||||
|
|
||||||
def closed_issues_count(user = nil, project = nil)
|
def closed_issues_count(user = nil)
|
||||||
issues_count(user, project_id: project.try(:id) || project_id, state: 'closed')
|
issues_count(user, state: 'closed')
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_merge_requests_count(user = nil, project = nil)
|
def open_merge_requests_count(user = nil)
|
||||||
merge_requests_count(user, project_id: project.try(:id) || project_id, state: 'opened')
|
params = {
|
||||||
|
subject_foreign_key => subject.id,
|
||||||
|
label_name: title,
|
||||||
|
scope: 'all',
|
||||||
|
state: 'opened'
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeRequestsFinder.new(user, params.with_indifferent_access).execute.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def prioritize!(project, value)
|
def prioritize!(project, value)
|
||||||
|
@ -167,15 +174,8 @@ class Label < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def issues_count(user, params = {})
|
def issues_count(user, params = {})
|
||||||
IssuesFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
|
params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all')
|
||||||
.execute
|
IssuesFinder.new(user, params.with_indifferent_access).execute.count
|
||||||
.count
|
|
||||||
end
|
|
||||||
|
|
||||||
def merge_requests_count(user, params = {})
|
|
||||||
MergeRequestsFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
|
|
||||||
.execute
|
|
||||||
.count
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def label_format_reference(format = :id)
|
def label_format_reference(format = :id)
|
||||||
|
|
|
@ -442,11 +442,11 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_remove_source_branch?
|
def should_remove_source_branch?
|
||||||
merge_params['should_remove_source_branch'].present?
|
Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def force_remove_source_branch?
|
def force_remove_source_branch?
|
||||||
merge_params['force_remove_source_branch'].present?
|
Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_source_branch?
|
def remove_source_branch?
|
||||||
|
|
|
@ -28,8 +28,13 @@ class Project < ActiveRecord::Base
|
||||||
default_value_for :archived, false
|
default_value_for :archived, false
|
||||||
default_value_for :visibility_level, gitlab_config_features.visibility_level
|
default_value_for :visibility_level, gitlab_config_features.visibility_level
|
||||||
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
||||||
default_value_for(:repository_storage) { current_application_settings.repository_storage }
|
default_value_for(:repository_storage) { current_application_settings.pick_repository_storage }
|
||||||
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
|
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
|
||||||
|
default_value_for :issues_enabled, gitlab_config_features.issues
|
||||||
|
default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
|
||||||
|
default_value_for :builds_enabled, gitlab_config_features.builds
|
||||||
|
default_value_for :wiki_enabled, gitlab_config_features.wiki
|
||||||
|
default_value_for :snippets_enabled, gitlab_config_features.snippets
|
||||||
|
|
||||||
after_create :ensure_dir_exist
|
after_create :ensure_dir_exist
|
||||||
after_create :create_project_feature, unless: :project_feature
|
after_create :create_project_feature, unless: :project_feature
|
||||||
|
@ -202,8 +207,38 @@ class Project < ActiveRecord::Base
|
||||||
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
|
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
|
||||||
scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
|
scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
|
||||||
|
|
||||||
scope :with_builds_enabled, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id').where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0') }
|
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
|
||||||
scope :with_issues_enabled, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id').where('project_features.issues_access_level IS NULL or project_features.issues_access_level > 0') }
|
|
||||||
|
# "enabled" here means "not disabled". It includes private features!
|
||||||
|
scope :with_feature_enabled, ->(feature) {
|
||||||
|
access_level_attribute = ProjectFeature.access_level_attribute(feature)
|
||||||
|
with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] })
|
||||||
|
}
|
||||||
|
|
||||||
|
# Picks a feature where the level is exactly that given.
|
||||||
|
scope :with_feature_access_level, ->(feature, level) {
|
||||||
|
access_level_attribute = ProjectFeature.access_level_attribute(feature)
|
||||||
|
with_project_feature.where(project_features: { access_level_attribute => level })
|
||||||
|
}
|
||||||
|
|
||||||
|
scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
|
||||||
|
scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
|
||||||
|
|
||||||
|
# project features may be "disabled", "internal" or "enabled". If "internal",
|
||||||
|
# they are only available to team members. This scope returns projects where
|
||||||
|
# the feature is either enabled, or internal with permission for the user.
|
||||||
|
def self.with_feature_available_for_user(feature, user)
|
||||||
|
return with_feature_enabled(feature) if user.try(:admin?)
|
||||||
|
|
||||||
|
unconditional = with_feature_access_level(feature, [nil, ProjectFeature::ENABLED])
|
||||||
|
return unconditional if user.nil?
|
||||||
|
|
||||||
|
conditional = with_feature_access_level(feature, ProjectFeature::PRIVATE)
|
||||||
|
authorized = user.authorized_projects.merge(conditional.reorder(nil))
|
||||||
|
|
||||||
|
union = Gitlab::SQL::Union.new([unconditional.select(:id), authorized.select(:id)])
|
||||||
|
where(arel_table[:id].in(Arel::Nodes::SqlLiteral.new(union.to_sql)))
|
||||||
|
end
|
||||||
|
|
||||||
scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
|
scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
|
||||||
scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) }
|
scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) }
|
||||||
|
@ -390,7 +425,7 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def group_ids
|
def group_ids
|
||||||
joins(:namespace).where(namespaces: { type: 'Group' }).pluck(:namespace_id)
|
joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1062,10 +1097,6 @@ class Project < ActiveRecord::Base
|
||||||
forks.count
|
forks.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_label(name)
|
|
||||||
labels.find_by(name: name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def origin_merge_requests
|
def origin_merge_requests
|
||||||
merge_requests.where(source_project_id: self.id)
|
merge_requests.where(source_project_id: self.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,15 @@ class ProjectFeature < ActiveRecord::Base
|
||||||
|
|
||||||
FEATURES = %i(issues merge_requests wiki snippets builds repository)
|
FEATURES = %i(issues merge_requests wiki snippets builds repository)
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def access_level_attribute(feature)
|
||||||
|
feature = feature.model_name.plural.to_sym if feature.respond_to?(:model_name)
|
||||||
|
raise ArgumentError, "invalid project feature: #{feature}" unless FEATURES.include?(feature)
|
||||||
|
|
||||||
|
"#{feature}_access_level".to_sym
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Default scopes force us to unscope here since a service may need to check
|
# Default scopes force us to unscope here since a service may need to check
|
||||||
# permissions for a project in pending_delete
|
# permissions for a project in pending_delete
|
||||||
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
|
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
|
||||||
|
@ -35,9 +44,8 @@ class ProjectFeature < ActiveRecord::Base
|
||||||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||||
|
|
||||||
def feature_available?(feature, user)
|
def feature_available?(feature, user)
|
||||||
raise ArgumentError, 'invalid project feature' unless FEATURES.include?(feature)
|
access_level = public_send(ProjectFeature.access_level_attribute(feature))
|
||||||
|
get_permission(user, access_level)
|
||||||
get_permission(user, public_send("#{feature}_access_level"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def builds_enabled?
|
def builds_enabled?
|
||||||
|
|
|
@ -12,6 +12,10 @@ class ProjectLabel < Label
|
||||||
|
|
||||||
alias_attribute :subject, :project
|
alias_attribute :subject, :project
|
||||||
|
|
||||||
|
def subject_foreign_key
|
||||||
|
'project_id'
|
||||||
|
end
|
||||||
|
|
||||||
def to_reference(target_project = nil, format: :id)
|
def to_reference(target_project = nil, format: :id)
|
||||||
super(project, target_project, format: format)
|
super(project, target_project, format: format)
|
||||||
end
|
end
|
||||||
|
|
|
@ -178,7 +178,7 @@ class Repository
|
||||||
before_remove_branch
|
before_remove_branch
|
||||||
|
|
||||||
branch = find_branch(branch_name)
|
branch = find_branch(branch_name)
|
||||||
oldrev = branch.try(:target).try(:id)
|
oldrev = branch.try(:dereferenced_target).try(:id)
|
||||||
newrev = Gitlab::Git::BLANK_SHA
|
newrev = Gitlab::Git::BLANK_SHA
|
||||||
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
||||||
|
|
||||||
|
@ -294,10 +294,10 @@ class Repository
|
||||||
# Rugged seems to throw a `ReferenceError` when given branch_names rather
|
# Rugged seems to throw a `ReferenceError` when given branch_names rather
|
||||||
# than SHA-1 hashes
|
# than SHA-1 hashes
|
||||||
number_commits_behind = raw_repository.
|
number_commits_behind = raw_repository.
|
||||||
count_commits_between(branch.target.sha, root_ref_hash)
|
count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
|
||||||
|
|
||||||
number_commits_ahead = raw_repository.
|
number_commits_ahead = raw_repository.
|
||||||
count_commits_between(root_ref_hash, branch.target.sha)
|
count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
|
||||||
|
|
||||||
{ behind: number_commits_behind, ahead: number_commits_ahead }
|
{ behind: number_commits_behind, ahead: number_commits_ahead }
|
||||||
end
|
end
|
||||||
|
@ -679,11 +679,11 @@ class Repository
|
||||||
branches.sort_by(&:name)
|
branches.sort_by(&:name)
|
||||||
when 'updated_desc'
|
when 'updated_desc'
|
||||||
branches.sort do |a, b|
|
branches.sort do |a, b|
|
||||||
commit(b.target).committed_date <=> commit(a.target).committed_date
|
commit(b.dereferenced_target).committed_date <=> commit(a.dereferenced_target).committed_date
|
||||||
end
|
end
|
||||||
when 'updated_asc'
|
when 'updated_asc'
|
||||||
branches.sort do |a, b|
|
branches.sort do |a, b|
|
||||||
commit(a.target).committed_date <=> commit(b.target).committed_date
|
commit(a.dereferenced_target).committed_date <=> commit(b.dereferenced_target).committed_date
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
branches
|
branches
|
||||||
|
@ -858,7 +858,7 @@ class Repository
|
||||||
branch = find_branch(ref)
|
branch = find_branch(ref)
|
||||||
|
|
||||||
if branch
|
if branch
|
||||||
last_commit = branch.target
|
last_commit = branch.dereferenced_target
|
||||||
index.read_tree(last_commit.raw_commit.tree)
|
index.read_tree(last_commit.raw_commit.tree)
|
||||||
parents = [last_commit.sha]
|
parents = [last_commit.sha]
|
||||||
end
|
end
|
||||||
|
@ -945,7 +945,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def revert(user, commit, base_branch, revert_tree_id = nil)
|
def revert(user, commit, base_branch, revert_tree_id = nil)
|
||||||
source_sha = find_branch(base_branch).target.sha
|
source_sha = find_branch(base_branch).dereferenced_target.sha
|
||||||
revert_tree_id ||= check_revert_content(commit, base_branch)
|
revert_tree_id ||= check_revert_content(commit, base_branch)
|
||||||
|
|
||||||
return false unless revert_tree_id
|
return false unless revert_tree_id
|
||||||
|
@ -962,7 +962,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
|
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
|
||||||
source_sha = find_branch(base_branch).target.sha
|
source_sha = find_branch(base_branch).dereferenced_target.sha
|
||||||
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
|
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
|
||||||
|
|
||||||
return false unless cherry_pick_tree_id
|
return false unless cherry_pick_tree_id
|
||||||
|
@ -991,7 +991,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_revert_content(commit, base_branch)
|
def check_revert_content(commit, base_branch)
|
||||||
source_sha = find_branch(base_branch).target.sha
|
source_sha = find_branch(base_branch).dereferenced_target.sha
|
||||||
args = [commit.id, source_sha]
|
args = [commit.id, source_sha]
|
||||||
args << { mainline: 1 } if commit.merge_commit?
|
args << { mainline: 1 } if commit.merge_commit?
|
||||||
|
|
||||||
|
@ -1005,7 +1005,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_cherry_pick_content(commit, base_branch)
|
def check_cherry_pick_content(commit, base_branch)
|
||||||
source_sha = find_branch(base_branch).target.sha
|
source_sha = find_branch(base_branch).dereferenced_target.sha
|
||||||
args = [commit.id, source_sha]
|
args = [commit.id, source_sha]
|
||||||
args << 1 if commit.merge_commit?
|
args << 1 if commit.merge_commit?
|
||||||
|
|
||||||
|
@ -1078,7 +1078,7 @@ class Repository
|
||||||
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
|
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
|
||||||
oldrev = Gitlab::Git::BLANK_SHA
|
oldrev = Gitlab::Git::BLANK_SHA
|
||||||
else
|
else
|
||||||
oldrev = rugged.merge_base(newrev, target_branch.target.sha)
|
oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha)
|
||||||
end
|
end
|
||||||
|
|
||||||
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
|
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
|
||||||
|
@ -1138,7 +1138,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def tags_sorted_by_committed_date
|
def tags_sorted_by_committed_date
|
||||||
tags.sort_by { |tag| tag.target.committed_date }
|
tags.sort_by { |tag| tag.dereferenced_target.committed_date }
|
||||||
end
|
end
|
||||||
|
|
||||||
def keep_around_ref_name(sha)
|
def keep_around_ref_name(sha)
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
class IssuePolicy < IssuablePolicy
|
class IssuePolicy < IssuablePolicy
|
||||||
|
# This class duplicates the same check of Issue#readable_by? for performance reasons
|
||||||
|
# Make sure to sync this class checks with issue.rb to avoid security problems.
|
||||||
|
# Check commit 002ad215818450d2cbbc5fa065850a953dc7ada8 for more information.
|
||||||
|
|
||||||
def issue
|
def issue
|
||||||
@subject
|
@subject
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@ class ProjectPolicy < BasePolicy
|
||||||
def rules
|
def rules
|
||||||
team_access!(user)
|
team_access!(user)
|
||||||
|
|
||||||
owner = user.admin? ||
|
owner = project.owner == user ||
|
||||||
project.owner == user ||
|
|
||||||
(project.group && project.group.has_owner?(user))
|
(project.group && project.group.has_owner?(user))
|
||||||
|
|
||||||
owner_access! if owner
|
owner_access! if user.admin? || owner
|
||||||
|
team_member_owner_access! if owner
|
||||||
|
|
||||||
if project.public? || (project.internal? && !user.external?)
|
if project.public? || (project.internal? && !user.external?)
|
||||||
guest_access!
|
guest_access!
|
||||||
|
@ -16,7 +16,7 @@ class ProjectPolicy < BasePolicy
|
||||||
can! :read_build if project.public_builds?
|
can! :read_build if project.public_builds?
|
||||||
|
|
||||||
if project.request_access_enabled &&
|
if project.request_access_enabled &&
|
||||||
!(owner || project.team.member?(user) || project_group_member?(user))
|
!(owner || user.admin? || project.team.member?(user) || project_group_member?(user))
|
||||||
can! :request_access
|
can! :request_access
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -135,6 +135,10 @@ class ProjectPolicy < BasePolicy
|
||||||
can! :destroy_issue
|
can! :destroy_issue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def team_member_owner_access!
|
||||||
|
team_member_reporter_access!
|
||||||
|
end
|
||||||
|
|
||||||
# Push abilities on the users team role
|
# Push abilities on the users team role
|
||||||
def team_access!(user)
|
def team_access!(user)
|
||||||
access = project.team.max_member_access(user.id)
|
access = project.team.max_member_access(user.id)
|
||||||
|
|
|
@ -9,8 +9,8 @@ module Auth
|
||||||
|
|
||||||
return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled
|
return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled
|
||||||
|
|
||||||
unless current_user || project
|
unless scope || current_user || project
|
||||||
return error('DENIED', status: 403, message: 'access forbidden') unless scope
|
return error('DENIED', status: 403, message: 'access forbidden')
|
||||||
end
|
end
|
||||||
|
|
||||||
{ token: authorized_token(scope).encoded }
|
{ token: authorized_token(scope).encoded }
|
||||||
|
@ -76,7 +76,7 @@ module Auth
|
||||||
|
|
||||||
case requested_action
|
case requested_action
|
||||||
when 'pull'
|
when 'pull'
|
||||||
requested_project.public? || build_can_pull?(requested_project) || user_can_pull?(requested_project)
|
build_can_pull?(requested_project) || user_can_pull?(requested_project)
|
||||||
when 'push'
|
when 'push'
|
||||||
build_can_push?(requested_project) || user_can_push?(requested_project)
|
build_can_push?(requested_project) || user_can_push?(requested_project)
|
||||||
else
|
else
|
||||||
|
@ -92,23 +92,23 @@ module Auth
|
||||||
# Build can:
|
# Build can:
|
||||||
# 1. pull from its own project (for ex. a build)
|
# 1. pull from its own project (for ex. a build)
|
||||||
# 2. read images from dependent projects if creator of build is a team member
|
# 2. read images from dependent projects if creator of build is a team member
|
||||||
@authentication_abilities.include?(:build_read_container_image) &&
|
has_authentication_ability?(:build_read_container_image) &&
|
||||||
(requested_project == project || can?(current_user, :build_read_container_image, requested_project))
|
(requested_project == project || can?(current_user, :build_read_container_image, requested_project))
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_can_pull?(requested_project)
|
def user_can_pull?(requested_project)
|
||||||
@authentication_abilities.include?(:read_container_image) &&
|
has_authentication_ability?(:read_container_image) &&
|
||||||
can?(current_user, :read_container_image, requested_project)
|
can?(current_user, :read_container_image, requested_project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_can_push?(requested_project)
|
def build_can_push?(requested_project)
|
||||||
# Build can push only to the project from which it originates
|
# Build can push only to the project from which it originates
|
||||||
@authentication_abilities.include?(:build_create_container_image) &&
|
has_authentication_ability?(:build_create_container_image) &&
|
||||||
requested_project == project
|
requested_project == project
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_can_push?(requested_project)
|
def user_can_push?(requested_project)
|
||||||
@authentication_abilities.include?(:create_container_image) &&
|
has_authentication_ability?(:create_container_image) &&
|
||||||
can?(current_user, :create_container_image, requested_project)
|
can?(current_user, :create_container_image, requested_project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -118,5 +118,9 @@ module Auth
|
||||||
http_status: status
|
http_status: status
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_authentication_ability?(capability)
|
||||||
|
(@authentication_abilities || []).include?(capability)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,7 +42,7 @@ class DeleteBranchService < BaseService
|
||||||
Gitlab::DataBuilder::Push.build(
|
Gitlab::DataBuilder::Push.build(
|
||||||
project,
|
project,
|
||||||
current_user,
|
current_user,
|
||||||
branch.target.sha,
|
branch.dereferenced_target.sha,
|
||||||
Gitlab::Git::BLANK_SHA,
|
Gitlab::Git::BLANK_SHA,
|
||||||
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}",
|
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}",
|
||||||
[])
|
[])
|
||||||
|
|
|
@ -36,7 +36,7 @@ class DeleteTagService < BaseService
|
||||||
Gitlab::DataBuilder::Push.build(
|
Gitlab::DataBuilder::Push.build(
|
||||||
project,
|
project,
|
||||||
current_user,
|
current_user,
|
||||||
tag.target.sha,
|
tag.dereferenced_target.sha,
|
||||||
Gitlab::Git::BLANK_SHA,
|
Gitlab::Git::BLANK_SHA,
|
||||||
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
|
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
|
||||||
[])
|
[])
|
||||||
|
|
|
@ -27,8 +27,8 @@ class GitTagPushService < BaseService
|
||||||
tag_name = Gitlab::Git.ref_name(params[:ref])
|
tag_name = Gitlab::Git.ref_name(params[:ref])
|
||||||
tag = project.repository.find_tag(tag_name)
|
tag = project.repository.find_tag(tag_name)
|
||||||
|
|
||||||
if tag && tag.object_sha == params[:newrev]
|
if tag && tag.target == params[:newrev]
|
||||||
commit = project.commit(tag.target)
|
commit = project.commit(tag.dereferenced_target)
|
||||||
commits = [commit].compact
|
commits = [commit].compact
|
||||||
message = tag.message
|
message = tag.message
|
||||||
end
|
end
|
||||||
|
|
|
@ -353,9 +353,9 @@
|
||||||
%fieldset
|
%fieldset
|
||||||
%legend Repository Storage
|
%legend Repository Storage
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :repository_storage, 'Storage path for new projects', class: 'control-label col-sm-2'
|
= f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2'
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control'
|
= f.select :repository_storages, repository_storages_options_for_select, {include_hidden: false}, multiple: true, class: 'form-control'
|
||||||
.help-block
|
.help-block
|
||||||
Manage repository storage paths. Learn more in the
|
Manage repository storage paths. Learn more in the
|
||||||
= succeed "." do
|
= succeed "." do
|
||||||
|
|
|
@ -8,3 +8,6 @@
|
||||||
- if signin_enabled?
|
- if signin_enabled?
|
||||||
%li
|
%li
|
||||||
= link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
|
= link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
|
||||||
|
- if signin_enabled? && signup_enabled?
|
||||||
|
%li
|
||||||
|
= link_to 'Register', '#register-pane', 'data-toggle' => 'tab'
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
.other-labels
|
.other-labels
|
||||||
- if @labels.present?
|
- if @labels.present?
|
||||||
%ul.content-list.manage-labels-list.js-other-labels
|
%ul.content-list.manage-labels-list.js-other-labels
|
||||||
= render partial: 'shared/label', collection: @labels, as: :label
|
= render partial: 'shared/label', subject: @group, collection: @labels, as: :label
|
||||||
= paginate @labels, theme: 'gitlab'
|
= paginate @labels, theme: 'gitlab'
|
||||||
- else
|
- else
|
||||||
.nothing-here-block
|
.nothing-here-block
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
- if commit.status
|
- ref = local_assigns.fetch(:ref)
|
||||||
= link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{commit.status}" do
|
- status = commit.status(ref)
|
||||||
= ci_icon_for_status(commit.status)
|
- if status
|
||||||
= ci_label_for_status(commit.status)
|
= link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do
|
||||||
|
= ci_icon_for_status(status)
|
||||||
|
= ci_label_for_status(status)
|
||||||
|
|
||||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
|
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
|
||||||
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
|
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
%ul.blob-commit-info.hidden-xs
|
%ul.blob-commit-info.hidden-xs
|
||||||
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
|
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
|
||||||
= render blob_commit, project: @project
|
= render blob_commit, project: @project, ref: @ref
|
||||||
|
|
||||||
%div#blob-content-holder.blob-content-holder
|
%div#blob-content-holder.blob-content-holder
|
||||||
%article.file-holder
|
%article.file-holder
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
- commit = @repository.commit(branch.target)
|
- commit = @repository.commit(branch.dereferenced_target)
|
||||||
- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
|
- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
|
||||||
- diverging_commit_counts = @repository.diverging_commit_counts(branch)
|
- diverging_commit_counts = @repository.diverging_commit_counts(branch)
|
||||||
- number_commits_behind = diverging_commit_counts[:behind]
|
- number_commits_behind = diverging_commit_counts[:behind]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
- ref = local_assigns.fetch(:ref)
|
||||||
- if @note_counts
|
- if @note_counts
|
||||||
- note_count = @note_counts.fetch(commit.id, 0)
|
- note_count = @note_counts.fetch(commit.id, 0)
|
||||||
- else
|
- else
|
||||||
|
@ -5,7 +6,7 @@
|
||||||
- note_count = notes.user.count
|
- note_count = notes.user.count
|
||||||
|
|
||||||
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
|
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
|
||||||
- cache_key.push(commit.status) if commit.status
|
- cache_key.push(commit.status(ref)) if commit.status(ref)
|
||||||
|
|
||||||
= cache(cache_key, expires_in: 1.day) do
|
= cache(cache_key, expires_in: 1.day) do
|
||||||
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
|
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
|
||||||
|
@ -18,15 +19,15 @@
|
||||||
%span.commit-row-message.visible-xs-inline
|
%span.commit-row-message.visible-xs-inline
|
||||||
·
|
·
|
||||||
= commit.short_id
|
= commit.short_id
|
||||||
- if commit.status
|
- if commit.status(ref)
|
||||||
.visible-xs-inline
|
.visible-xs-inline
|
||||||
= render_commit_status(commit)
|
= render_commit_status(commit, ref: ref)
|
||||||
- if commit.description?
|
- if commit.description?
|
||||||
%a.text-expander.hidden-xs.js-toggle-button ...
|
%a.text-expander.hidden-xs.js-toggle-button ...
|
||||||
|
|
||||||
.commit-actions.hidden-xs
|
.commit-actions.hidden-xs
|
||||||
- if commit.status
|
- if commit.status(ref)
|
||||||
= render_commit_status(commit)
|
= render_commit_status(commit, ref: ref)
|
||||||
= clipboard_button(clipboard_text: commit.id)
|
= clipboard_button(clipboard_text: commit.id)
|
||||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
|
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
|
||||||
= link_to_browse_code(project, commit)
|
= link_to_browse_code(project, commit)
|
||||||
|
|
|
@ -11,4 +11,4 @@
|
||||||
%li.warning-row.unstyled
|
%li.warning-row.unstyled
|
||||||
#{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
|
#{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
|
||||||
- else
|
- else
|
||||||
%ul.content-list= render commits, project: @project
|
%ul.content-list= render commits, project: @project, ref: @ref
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
- unless defined?(project)
|
- ref = local_assigns.fetch(:ref)
|
||||||
- project = @project
|
|
||||||
|
|
||||||
- commits, hidden = limited_commits(@commits)
|
- commits, hidden = limited_commits(@commits)
|
||||||
|
|
||||||
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
|
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
|
||||||
%li.commit-header= "#{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}"
|
%li.commit-header= "#{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}"
|
||||||
%li.commits-row
|
%li.commits-row
|
||||||
%ul.list-unstyled.commit-list
|
%ul.list-unstyled.commit-list
|
||||||
= render commits, project: project
|
= render commits, project: project, ref: ref
|
||||||
|
|
||||||
- if hidden > 0
|
- if hidden > 0
|
||||||
%li.alert.alert-warning
|
%li.alert.alert-warning
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
%div{id: dom_id(@project)}
|
%div{id: dom_id(@project)}
|
||||||
%ol#commits-list.list-unstyled.content_list
|
%ol#commits-list.list-unstyled.content_list
|
||||||
= render "commits", project: @project
|
= render 'commits', project: @project, ref: @ref
|
||||||
= spinner
|
= spinner
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
%ul.unstyled-list.related-merge-requests
|
%ul.unstyled-list.related-merge-requests
|
||||||
- @related_branches.each do |branch|
|
- @related_branches.each do |branch|
|
||||||
%li
|
%li
|
||||||
- target = @project.repository.find_branch(branch).target
|
- target = @project.repository.find_branch(branch).dereferenced_target
|
||||||
- pipeline = @project.pipeline_for(branch, target.sha) if target
|
- pipeline = @project.pipeline_for(branch, target.sha) if target
|
||||||
- if pipeline
|
- if pipeline
|
||||||
%span.related-branch-ci-status
|
%span.related-branch-ci-status
|
||||||
|
|
|
@ -22,14 +22,14 @@
|
||||||
%ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
|
%ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
|
||||||
%p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet
|
%p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet
|
||||||
- if @prioritized_labels.present?
|
- if @prioritized_labels.present?
|
||||||
= render partial: 'shared/label', collection: @prioritized_labels, as: :label
|
= render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label
|
||||||
|
|
||||||
.other-labels
|
.other-labels
|
||||||
- if can?(current_user, :admin_label, @project)
|
- if can?(current_user, :admin_label, @project)
|
||||||
%h5{ class: ('hide' if hide) } Other Labels
|
%h5{ class: ('hide' if hide) } Other Labels
|
||||||
%ul.content-list.manage-labels-list.js-other-labels
|
%ul.content-list.manage-labels-list.js-other-labels
|
||||||
- if @labels.present?
|
- if @labels.present?
|
||||||
= render partial: 'shared/label', collection: @labels, as: :label
|
= render partial: 'shared/label', subject: @project, collection: @labels, as: :label
|
||||||
= paginate @labels, theme: 'gitlab'
|
= paginate @labels, theme: 'gitlab'
|
||||||
- if @labels.blank?
|
- if @labels.blank?
|
||||||
.nothing-here-block
|
.nothing-here-block
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
= commit_to_html(@commit, @source_project, false)
|
- if @commit
|
||||||
|
= commit_to_html(@commit, @ref, @source_project)
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
= commit_to_html(@commit, @target_project, false)
|
- if @commit
|
||||||
|
= commit_to_html(@commit, @ref, @target_project)
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
Most recent commits displayed first
|
Most recent commits displayed first
|
||||||
|
|
||||||
%ol#commits-list.list-unstyled
|
%ol#commits-list.list-unstyled
|
||||||
= render "projects/commits/commits", project: @merge_request.project
|
= render "projects/commits/commits", project: @merge_request.source_project, ref: @merge_request.source_branch
|
||||||
|
|
|
@ -90,7 +90,8 @@
|
||||||
= f.label :visibility_level, class: 'label-light' do
|
= f.label :visibility_level, class: 'label-light' do
|
||||||
Visibility Level
|
Visibility Level
|
||||||
= link_to "(?)", help_page_path("public_access/public_access")
|
= link_to "(?)", help_page_path("public_access/public_access")
|
||||||
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
|
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
|
||||||
|
|
||||||
|
|
||||||
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
|
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
|
||||||
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
|
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
= render 'shared/notifications/button', notification_setting: @notification_setting
|
= render 'shared/notifications/button', notification_setting: @notification_setting
|
||||||
- if @repository.commit
|
- if @repository.commit
|
||||||
.project-last-commit{ class: container_class }
|
.project-last-commit{ class: container_class }
|
||||||
= render 'projects/last_commit', commit: @repository.commit, project: @project
|
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
|
||||||
|
|
||||||
%div{ class: container_class }
|
%div{ class: container_class }
|
||||||
- if @project.archived?
|
- if @project.archived?
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
- commit = @repository.commit(tag.target)
|
- commit = @repository.commit(tag.dereferenced_target)
|
||||||
- release = @releases.find { |release| release.tag == tag.name }
|
- release = @releases.find { |release| release.tag == tag.name }
|
||||||
%li
|
%li
|
||||||
%div
|
%div
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
= render 'projects/commits/commit', project: @project, commit: commit
|
= render 'projects/commits/commit', project: @project, commit: commit, ref: nil
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
- label_css_id = dom_id(label)
|
- label_css_id = dom_id(label)
|
||||||
- open_issues_count = label.open_issues_count(current_user, @project)
|
- open_issues_count = label.open_issues_count(current_user)
|
||||||
- open_merge_requests_count = label.open_merge_requests_count(current_user, @project)
|
- open_merge_requests_count = label.open_merge_requests_count(current_user)
|
||||||
|
- subject = local_assigns[:subject]
|
||||||
|
|
||||||
%li{id: label_css_id, data: { id: label.id } }
|
%li{id: label_css_id, data: { id: label.id } }
|
||||||
= render "shared/label_row", label: label
|
= render "shared/label_row", label: label
|
||||||
|
@ -12,10 +13,10 @@
|
||||||
.dropdown-menu.dropdown-menu-align-right
|
.dropdown-menu.dropdown-menu-align-right
|
||||||
%ul
|
%ul
|
||||||
%li
|
%li
|
||||||
= link_to_label(label, subject: @project, type: :merge_request) do
|
= link_to_label(label, subject: subject, type: :merge_request) do
|
||||||
= pluralize open_merge_requests_count, 'merge request'
|
= pluralize open_merge_requests_count, 'merge request'
|
||||||
%li
|
%li
|
||||||
= link_to_label(label, subject: @project) do
|
= link_to_label(label, subject: subject) do
|
||||||
= pluralize open_issues_count, 'open issue'
|
= pluralize open_issues_count, 'open issue'
|
||||||
- if current_user
|
- if current_user
|
||||||
%li.label-subscription{ data: toggle_subscription_data(label) }
|
%li.label-subscription{ data: toggle_subscription_data(label) }
|
||||||
|
@ -28,9 +29,9 @@
|
||||||
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, remote: true, data: {confirm: 'Remove this label? Are you sure?'}
|
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, remote: true, data: {confirm: 'Remove this label? Are you sure?'}
|
||||||
|
|
||||||
.pull-right.hidden-xs.hidden-sm.hidden-md
|
.pull-right.hidden-xs.hidden-sm.hidden-md
|
||||||
= link_to_label(label, subject: @project, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
|
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
|
||||||
= pluralize open_merge_requests_count, 'merge request'
|
= pluralize open_merge_requests_count, 'merge request'
|
||||||
= link_to_label(label, subject: @project, css_class: 'btn btn-transparent btn-action') do
|
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
|
||||||
= pluralize open_issues_count, 'open issue'
|
= pluralize open_issues_count, 'open issue'
|
||||||
|
|
||||||
- if current_user
|
- if current_user
|
||||||
|
|
|
@ -142,6 +142,7 @@
|
||||||
.col-sm-10.col-sm-offset-2
|
.col-sm-10.col-sm-offset-2
|
||||||
.checkbox
|
.checkbox
|
||||||
= label_tag 'merge_request[force_remove_source_branch]' do
|
= label_tag 'merge_request[force_remove_source_branch]' do
|
||||||
|
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
|
||||||
= check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
|
= check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
|
||||||
Remove source branch when merge request is accepted.
|
Remove source branch when merge request is accepted.
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
- project = @target_project || @project
|
- project = @target_project || @project
|
||||||
- extra_class = extra_class || ''
|
- extra_class = extra_class || ''
|
||||||
- show_menu_above = show_menu_above || false
|
- show_menu_above = show_menu_above || false
|
||||||
- selected_text = selected.try(:title)
|
- selected_text = selected.try(:title) || params[:milestone_title]
|
||||||
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
|
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
|
||||||
- if selected.present?
|
- if selected.present?
|
||||||
= hidden_field_tag(name, name == :milestone_title ? selected.title : selected.id)
|
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
|
||||||
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
|
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
|
||||||
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
|
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
|
||||||
- if project
|
- if project
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class RenameRepositoryStorageColumn < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
# Set this constant to true if this migration requires downtime.
|
||||||
|
DOWNTIME = true
|
||||||
|
|
||||||
|
# When a migration requires downtime you **must** uncomment the following
|
||||||
|
# constant and define a short and easy to understand explanation as to why the
|
||||||
|
# migration requires downtime.
|
||||||
|
DOWNTIME_REASON = 'Renaming the application_settings.repository_storage column'
|
||||||
|
|
||||||
|
# When using the methods "add_concurrent_index" or "add_column_with_default"
|
||||||
|
# you must disable the use of transactions as these methods can not run in an
|
||||||
|
# existing transaction. When using "add_concurrent_index" make sure that this
|
||||||
|
# method is the _only_ method called in the migration, any other changes
|
||||||
|
# should go in a separate migration. This ensures that upon failure _only_ the
|
||||||
|
# index creation fails and can be retried or reverted easily.
|
||||||
|
#
|
||||||
|
# To disable transactions uncomment the following line and remove these
|
||||||
|
# comments:
|
||||||
|
# disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
rename_column :application_settings, :repository_storage, :repository_storages
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20161024042317) do
|
ActiveRecord::Schema.define(version: 20161103171205) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -88,7 +88,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do
|
||||||
t.integer "container_registry_token_expire_delay", default: 5
|
t.integer "container_registry_token_expire_delay", default: 5
|
||||||
t.text "after_sign_up_text"
|
t.text "after_sign_up_text"
|
||||||
t.boolean "user_default_external", default: false, null: false
|
t.boolean "user_default_external", default: false, null: false
|
||||||
t.string "repository_storage", default: "default"
|
t.string "repository_storages", default: "default"
|
||||||
t.string "enabled_git_access_protocol"
|
t.string "enabled_git_access_protocol"
|
||||||
t.boolean "domain_blacklist_enabled", default: false
|
t.boolean "domain_blacklist_enabled", default: false
|
||||||
t.text "domain_blacklist"
|
t.text "domain_blacklist"
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 53 KiB |
|
@ -91,6 +91,9 @@ be stored via the **Application Settings** in the Admin area.
|
||||||
|
|
||||||
![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
|
![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
|
||||||
|
|
||||||
|
Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
|
||||||
|
randomly placed on one of the selected paths.
|
||||||
|
|
||||||
[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
|
[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
|
||||||
[restart gitlab]: restart_gitlab.md#installations-from-source
|
[restart gitlab]: restart_gitlab.md#installations-from-source
|
||||||
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
|
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
|
||||||
|
|
|
@ -42,6 +42,7 @@ Example response:
|
||||||
"sign_in_text" : null,
|
"sign_in_text" : null,
|
||||||
"container_registry_token_expire_delay": 5,
|
"container_registry_token_expire_delay": 5,
|
||||||
"repository_storage": "default",
|
"repository_storage": "default",
|
||||||
|
"repository_storages": ["default"],
|
||||||
"koding_enabled": false,
|
"koding_enabled": false,
|
||||||
"koding_url": null
|
"koding_url": null
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,8 @@ PUT /application/settings
|
||||||
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
|
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
|
||||||
| `after_sign_out_path` | string | no | Where to redirect users after logout |
|
| `after_sign_out_path` | string | no | Where to redirect users after logout |
|
||||||
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
|
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
|
||||||
| `repository_storage` | string | no | Storage path for new projects. The value should be the name of one of the repository storage paths defined in your gitlab.yml |
|
| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
|
||||||
|
| `repository_storage` | string | no | The first entry in `repository_storages`. Deprecated, but retained for compatibility reasons |
|
||||||
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
|
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
|
||||||
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
|
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
|
||||||
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
|
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
|
||||||
|
|
|
@ -66,6 +66,12 @@ producing errors whenever it tries to use the `dummy` column.
|
||||||
As a result of the above downtime _is_ required when removing a column, even
|
As a result of the above downtime _is_ required when removing a column, even
|
||||||
when using PostgreSQL.
|
when using PostgreSQL.
|
||||||
|
|
||||||
|
## Renaming Columns
|
||||||
|
|
||||||
|
Renaming columns requires downtime as running GitLab instances will continue
|
||||||
|
using the old column name until a new version is deployed. This can result
|
||||||
|
in the instance producing errors, which in turn can impact the user experience.
|
||||||
|
|
||||||
## Changing Column Constraints
|
## Changing Column Constraints
|
||||||
|
|
||||||
Generally changing column constraints requires checking all rows in the table to
|
Generally changing column constraints requires checking all rows in the table to
|
||||||
|
|
|
@ -138,7 +138,7 @@ module API
|
||||||
expose :name
|
expose :name
|
||||||
|
|
||||||
expose :commit do |repo_branch, options|
|
expose :commit do |repo_branch, options|
|
||||||
options[:project].repository.commit(repo_branch.target)
|
options[:project].repository.commit(repo_branch.dereferenced_target)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :protected do |repo_branch, options|
|
expose :protected do |repo_branch, options|
|
||||||
|
@ -510,6 +510,7 @@ module API
|
||||||
expose :after_sign_out_path
|
expose :after_sign_out_path
|
||||||
expose :container_registry_token_expire_delay
|
expose :container_registry_token_expire_delay
|
||||||
expose :repository_storage
|
expose :repository_storage
|
||||||
|
expose :repository_storages
|
||||||
expose :koding_enabled
|
expose :koding_enabled
|
||||||
expose :koding_url
|
expose :koding_url
|
||||||
end
|
end
|
||||||
|
@ -523,7 +524,7 @@ module API
|
||||||
expose :name, :message
|
expose :name, :message
|
||||||
|
|
||||||
expose :commit do |repo_tag, options|
|
expose :commit do |repo_tag, options|
|
||||||
options[:project].repository.commit(repo_tag.target)
|
options[:project].repository.commit(repo_tag.dereferenced_target)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :release, using: Entities::Release do |repo_tag, options|
|
expose :release, using: Entities::Release do |repo_tag, options|
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
module API
|
module API
|
||||||
module Helpers
|
module Helpers
|
||||||
|
include Gitlab::Utils
|
||||||
|
|
||||||
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
|
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
|
||||||
PRIVATE_TOKEN_PARAM = :private_token
|
PRIVATE_TOKEN_PARAM = :private_token
|
||||||
SUDO_HEADER = "HTTP_SUDO"
|
SUDO_HEADER = "HTTP_SUDO"
|
||||||
SUDO_PARAM = :sudo
|
SUDO_PARAM = :sudo
|
||||||
|
|
||||||
def to_boolean(value)
|
|
||||||
return value if [true, false].include?(value)
|
|
||||||
return true if value =~ /^(true|t|yes|y|1|on)$/i
|
|
||||||
return false if value =~ /^(false|f|no|n|0|off)$/i
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def private_token
|
def private_token
|
||||||
params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]
|
params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,8 +29,8 @@ module API
|
||||||
required_attributes! [:name, :color]
|
required_attributes! [:name, :color]
|
||||||
|
|
||||||
attrs = attributes_for_keys [:name, :color, :description]
|
attrs = attributes_for_keys [:name, :color, :description]
|
||||||
label = user_project.find_label(attrs[:name])
|
|
||||||
|
|
||||||
|
label = available_labels.find_by(title: attrs[:name])
|
||||||
conflict!('Label already exists') if label
|
conflict!('Label already exists') if label
|
||||||
|
|
||||||
label = user_project.labels.create(attrs)
|
label = user_project.labels.create(attrs)
|
||||||
|
@ -54,7 +54,7 @@ module API
|
||||||
authorize! :admin_label, user_project
|
authorize! :admin_label, user_project
|
||||||
required_attributes! [:name]
|
required_attributes! [:name]
|
||||||
|
|
||||||
label = user_project.find_label(params[:name])
|
label = user_project.labels.find_by(title: params[:name])
|
||||||
not_found!('Label') unless label
|
not_found!('Label') unless label
|
||||||
|
|
||||||
label.destroy
|
label.destroy
|
||||||
|
@ -75,7 +75,7 @@ module API
|
||||||
authorize! :admin_label, user_project
|
authorize! :admin_label, user_project
|
||||||
required_attributes! [:name]
|
required_attributes! [:name]
|
||||||
|
|
||||||
label = user_project.find_label(params[:name])
|
label = user_project.labels.find_by(title: params[:name])
|
||||||
not_found!('Label not found') unless label
|
not_found!('Label not found') unless label
|
||||||
|
|
||||||
attrs = attributes_for_keys [:new_name, :color, :description]
|
attrs = attributes_for_keys [:new_name, :color, :description]
|
||||||
|
|
|
@ -17,12 +17,12 @@ module API
|
||||||
present current_settings, with: Entities::ApplicationSetting
|
present current_settings, with: Entities::ApplicationSetting
|
||||||
end
|
end
|
||||||
|
|
||||||
# Modify applicaiton settings
|
# Modify application settings
|
||||||
#
|
#
|
||||||
# Example Request:
|
# Example Request:
|
||||||
# PUT /application/settings
|
# PUT /application/settings
|
||||||
put "application/settings" do
|
put "application/settings" do
|
||||||
attributes = current_settings.attributes.keys - ["id"]
|
attributes = ["repository_storage"] + current_settings.attributes.keys - ["id"]
|
||||||
attrs = attributes_for_keys(attributes)
|
attrs = attributes_for_keys(attributes)
|
||||||
|
|
||||||
if current_settings.update_attributes(attrs)
|
if current_settings.update_attributes(attrs)
|
||||||
|
|
|
@ -71,6 +71,14 @@ module Banzai
|
||||||
@doc = parse_html(rinku)
|
@doc = parse_html(rinku)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return true if any of the UNSAFE_PROTOCOLS strings are included in the URI scheme
|
||||||
|
def contains_unsafe?(scheme)
|
||||||
|
return false unless scheme
|
||||||
|
|
||||||
|
scheme = scheme.strip.downcase
|
||||||
|
Banzai::Filter::SanitizationFilter::UNSAFE_PROTOCOLS.any? { |protocol| scheme.include?(protocol) }
|
||||||
|
end
|
||||||
|
|
||||||
# Autolinks any text matching LINK_PATTERN that Rinku didn't already
|
# Autolinks any text matching LINK_PATTERN that Rinku didn't already
|
||||||
# replace
|
# replace
|
||||||
def text_parse
|
def text_parse
|
||||||
|
@ -89,17 +97,27 @@ module Banzai
|
||||||
doc
|
doc
|
||||||
end
|
end
|
||||||
|
|
||||||
def autolink_filter(text)
|
def autolink_match(match)
|
||||||
text.gsub(LINK_PATTERN) do |match|
|
# start by stripping out dangerous links
|
||||||
# Remove any trailing HTML entities and store them for appending
|
begin
|
||||||
# outside the link element. The entity must be marked HTML safe in
|
uri = Addressable::URI.parse(match)
|
||||||
# order to be output literally rather than escaped.
|
return match if contains_unsafe?(uri.scheme)
|
||||||
match.gsub!(/((?:&[\w#]+;)+)\z/, '')
|
rescue Addressable::URI::InvalidURIError
|
||||||
dropped = ($1 || '').html_safe
|
return match
|
||||||
|
|
||||||
options = link_options.merge(href: match)
|
|
||||||
content_tag(:a, match, options) + dropped
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Remove any trailing HTML entities and store them for appending
|
||||||
|
# outside the link element. The entity must be marked HTML safe in
|
||||||
|
# order to be output literally rather than escaped.
|
||||||
|
match.gsub!(/((?:&[\w#]+;)+)\z/, '')
|
||||||
|
dropped = ($1 || '').html_safe
|
||||||
|
|
||||||
|
options = link_options.merge(href: match)
|
||||||
|
content_tag(:a, match, options) + dropped
|
||||||
|
end
|
||||||
|
|
||||||
|
def autolink_filter(text)
|
||||||
|
text.gsub(LINK_PATTERN) { |match| autolink_match(match) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_options
|
def link_options
|
||||||
|
|
|
@ -63,12 +63,7 @@ module Banzai
|
||||||
nodes.select do |node|
|
nodes.select do |node|
|
||||||
if node.has_attribute?(project_attr)
|
if node.has_attribute?(project_attr)
|
||||||
node_id = node.attr(project_attr).to_i
|
node_id = node.attr(project_attr).to_i
|
||||||
|
can_read_reference?(user, projects[node_id])
|
||||||
if project && project.id == node_id
|
|
||||||
true
|
|
||||||
else
|
|
||||||
can?(user, :read_project, projects[node_id])
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
@ -226,6 +221,15 @@ module Banzai
|
||||||
|
|
||||||
attr_reader :current_user, :project
|
attr_reader :current_user, :project
|
||||||
|
|
||||||
|
# When a feature is disabled or visible only for
|
||||||
|
# team members we should not allow team members
|
||||||
|
# see reference comments.
|
||||||
|
# Override this method on subclasses
|
||||||
|
# to check if user can read resource
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
def lazy(&block)
|
def lazy(&block)
|
||||||
Gitlab::Lazy.new(&block)
|
Gitlab::Lazy.new(&block)
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,12 @@ module Banzai
|
||||||
|
|
||||||
commits
|
commits
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :download_code, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,12 @@ module Banzai
|
||||||
|
|
||||||
range.valid_commits? ? range : nil
|
range.valid_commits? ? range : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :download_code, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,12 @@ module Banzai
|
||||||
def issue_ids_per_project(nodes)
|
def issue_ids_per_project(nodes)
|
||||||
gather_attributes_per_project(nodes, self.class.data_attribute)
|
gather_attributes_per_project(nodes, self.class.data_attribute)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_issue, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,12 @@ module Banzai
|
||||||
def references_relation
|
def references_relation
|
||||||
Label
|
Label
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_label, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,12 @@ module Banzai
|
||||||
def references_relation
|
def references_relation
|
||||||
MergeRequest.includes(:author, :assignee, :target_project)
|
MergeRequest.includes(:author, :assignee, :target_project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_merge_request, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,12 @@ module Banzai
|
||||||
def references_relation
|
def references_relation
|
||||||
Milestone
|
Milestone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_milestone, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,12 @@ module Banzai
|
||||||
def references_relation
|
def references_relation
|
||||||
Snippet
|
Snippet
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_project_snippet, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,22 +30,36 @@ module Banzai
|
||||||
|
|
||||||
nodes.each do |node|
|
nodes.each do |node|
|
||||||
if node.has_attribute?(group_attr)
|
if node.has_attribute?(group_attr)
|
||||||
node_group = groups[node.attr(group_attr).to_i]
|
next unless can_read_group_reference?(node, user, groups)
|
||||||
|
visible << node
|
||||||
if node_group &&
|
elsif can_read_project_reference?(node)
|
||||||
can?(user, :read_group, node_group)
|
visible << node
|
||||||
visible << node
|
|
||||||
end
|
|
||||||
# Remaining nodes will be processed by the parent class'
|
|
||||||
# implementation of this method.
|
|
||||||
else
|
else
|
||||||
remaining << node
|
remaining << node
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If project does not belong to a group
|
||||||
|
# and does not have the same project id as the current project
|
||||||
|
# base class will check if user can read the project that contains
|
||||||
|
# the user reference.
|
||||||
visible + super(current_user, remaining)
|
visible + super(current_user, remaining)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check if project belongs to a group which
|
||||||
|
# user can read.
|
||||||
|
def can_read_group_reference?(node, user, groups)
|
||||||
|
node_group = groups[node.attr('data-group').to_i]
|
||||||
|
|
||||||
|
node_group && can?(user, :read_group, node_group)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_read_project_reference?(node)
|
||||||
|
node_id = node.attr('data-project').to_i
|
||||||
|
|
||||||
|
project && project.id == node_id
|
||||||
|
end
|
||||||
|
|
||||||
def nodes_user_can_reference(current_user, nodes)
|
def nodes_user_can_reference(current_user, nodes)
|
||||||
project_attr = 'data-project'
|
project_attr = 'data-project'
|
||||||
author_attr = 'data-author'
|
author_attr = 'data-author'
|
||||||
|
@ -88,6 +102,10 @@ module Banzai
|
||||||
collection_objects_for_ids(Project, ids).
|
collection_objects_for_ids(Project, ids).
|
||||||
flat_map { |p| p.team.members.to_a }
|
flat_map { |p| p.team.members.to_a }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_read_reference?(user, ref_project)
|
||||||
|
can?(user, :read_project, ref_project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,7 +46,7 @@ module Banzai
|
||||||
return html if html.present?
|
return html if html.present?
|
||||||
|
|
||||||
html = cacheless_render_field(object, field)
|
html = cacheless_render_field(object, field)
|
||||||
object.update_column(html_field, html) unless object.new_record? || object.destroyed?
|
update_object(object, html_field, html) unless object.new_record? || object.destroyed?
|
||||||
|
|
||||||
html
|
html
|
||||||
end
|
end
|
||||||
|
@ -166,5 +166,9 @@ module Banzai
|
||||||
return unless cache_key
|
return unless cache_key
|
||||||
Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
|
Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_object(object, html_field, html)
|
||||||
|
object.update_column(html_field, html)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,45 +1,44 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class ContributionsCalendar
|
class ContributionsCalendar
|
||||||
attr_reader :activity_dates, :projects, :user
|
attr_reader :contributor
|
||||||
|
attr_reader :current_user
|
||||||
|
attr_reader :projects
|
||||||
|
|
||||||
def initialize(projects, user)
|
def initialize(contributor, current_user = nil)
|
||||||
@projects = projects
|
@contributor = contributor
|
||||||
@user = user
|
@current_user = current_user
|
||||||
|
@projects = ContributedProjectsFinder.new(contributor).execute(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity_dates
|
def activity_dates
|
||||||
return @activity_dates if @activity_dates.present?
|
return @activity_dates if @activity_dates.present?
|
||||||
|
|
||||||
@activity_dates = {}
|
# Can't use Event.contributions here because we need to check 3 different
|
||||||
|
# project_features for the (currently) 3 different contribution types
|
||||||
date_from = 1.year.ago
|
date_from = 1.year.ago
|
||||||
|
repo_events = event_counts(date_from, :repository).
|
||||||
|
having(action: Event::PUSHED)
|
||||||
|
issue_events = event_counts(date_from, :issues).
|
||||||
|
having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue")
|
||||||
|
mr_events = event_counts(date_from, :merge_requests).
|
||||||
|
having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest")
|
||||||
|
|
||||||
events = Event.reorder(nil).contributions.where(author_id: user.id).
|
union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events])
|
||||||
where("created_at > ?", date_from).where(project_id: projects).
|
events = Event.find_by_sql(union.to_sql).map(&:attributes)
|
||||||
group('date(created_at)').
|
|
||||||
select('date(created_at) as date, count(id) as total_amount').
|
|
||||||
map(&:attributes)
|
|
||||||
|
|
||||||
activity_dates = (1.year.ago.to_date..Date.today).to_a
|
@activity_events = events.each_with_object(Hash.new {|h, k| h[k] = 0 }) do |event, activities|
|
||||||
|
activities[event["date"]] += event["total_amount"]
|
||||||
activity_dates.each do |date|
|
|
||||||
day_events = events.find { |day_events| day_events["date"] == date }
|
|
||||||
|
|
||||||
if day_events
|
|
||||||
@activity_dates[date] = day_events["total_amount"]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@activity_dates
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def events_by_date(date)
|
def events_by_date(date)
|
||||||
events = Event.contributions.where(author_id: user.id).
|
events = Event.contributions.where(author_id: contributor.id).
|
||||||
where("created_at > ? AND created_at < ?", date.beginning_of_day, date.end_of_day).
|
where(created_at: date.beginning_of_day..date.end_of_day).
|
||||||
where(project_id: projects)
|
where(project_id: projects)
|
||||||
|
|
||||||
events.select do |event|
|
# Use visible_to_user? instead of the complicated logic in activity_dates
|
||||||
event.push? || event.issue? || event.merge_request?
|
# because we're only viewing the events for a single day.
|
||||||
end
|
events.select {|event| event.visible_to_user?(current_user) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def starting_year
|
def starting_year
|
||||||
|
@ -49,5 +48,30 @@ module Gitlab
|
||||||
def starting_month
|
def starting_month
|
||||||
Date.today.month
|
Date.today.month
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def event_counts(date_from, feature)
|
||||||
|
t = Event.arel_table
|
||||||
|
|
||||||
|
# re-running the contributed projects query in each union is expensive, so
|
||||||
|
# use IN(project_ids...) instead. It's the intersection of two users so
|
||||||
|
# the list will be (relatively) short
|
||||||
|
@contributed_project_ids ||= projects.uniq.pluck(:id)
|
||||||
|
authed_projects = Project.where(id: @contributed_project_ids).
|
||||||
|
with_feature_available_for_user(feature, current_user).
|
||||||
|
reorder(nil).
|
||||||
|
select(:id)
|
||||||
|
|
||||||
|
conditions = t[:created_at].gteq(date_from.beginning_of_day).
|
||||||
|
and(t[:created_at].lteq(Date.today.end_of_day)).
|
||||||
|
and(t[:author_id].eq(contributor.id))
|
||||||
|
|
||||||
|
Event.reorder(nil).
|
||||||
|
select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount').
|
||||||
|
group(t[:project_id], t[:target_type], t[:action], 'date(created_at)').
|
||||||
|
where(conditions).
|
||||||
|
having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,7 +83,7 @@ module Gitlab
|
||||||
tag = repository.find_tag(tag_name)
|
tag = repository.find_tag(tag_name)
|
||||||
|
|
||||||
if tag
|
if tag
|
||||||
commit = repository.commit(tag.target)
|
commit = repository.commit(tag.dereferenced_target)
|
||||||
commit.try(:sha)
|
commit.try(:sha)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -2,8 +2,18 @@
|
||||||
# class return an instance of `GitlabAccessStatus`
|
# class return an instance of `GitlabAccessStatus`
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class GitAccess
|
class GitAccess
|
||||||
|
UnauthorizedError = Class.new(StandardError)
|
||||||
|
|
||||||
|
ERROR_MESSAGES = {
|
||||||
|
upload: 'You are not allowed to upload code for this project.',
|
||||||
|
download: 'You are not allowed to download code from this project.',
|
||||||
|
deploy_key: 'Deploy keys are not allowed to push code.',
|
||||||
|
no_repo: 'A repository for this project does not exist yet.'
|
||||||
|
}
|
||||||
|
|
||||||
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
|
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
|
||||||
PUSH_COMMANDS = %w{ git-receive-pack }
|
PUSH_COMMANDS = %w{ git-receive-pack }
|
||||||
|
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
|
||||||
|
|
||||||
attr_reader :actor, :project, :protocol, :user_access, :authentication_abilities
|
attr_reader :actor, :project, :protocol, :user_access, :authentication_abilities
|
||||||
|
|
||||||
|
@ -16,56 +26,43 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def check(cmd, changes)
|
def check(cmd, changes)
|
||||||
return build_status_object(false, "Git access over #{protocol.upcase} is not allowed") unless protocol_allowed?
|
check_protocol!
|
||||||
|
check_active_user!
|
||||||
unless actor
|
check_project_accessibility!
|
||||||
return build_status_object(false, "No user or key was provided.")
|
check_command_existence!(cmd)
|
||||||
end
|
|
||||||
|
|
||||||
if user && !user_access.allowed?
|
|
||||||
return build_status_object(false, "Your account has been blocked.")
|
|
||||||
end
|
|
||||||
|
|
||||||
unless project && (user_access.can_read_project? || deploy_key_can_read_project?)
|
|
||||||
return build_status_object(false, 'The project you were looking for could not be found.')
|
|
||||||
end
|
|
||||||
|
|
||||||
case cmd
|
case cmd
|
||||||
when *DOWNLOAD_COMMANDS
|
when *DOWNLOAD_COMMANDS
|
||||||
download_access_check
|
download_access_check
|
||||||
when *PUSH_COMMANDS
|
when *PUSH_COMMANDS
|
||||||
push_access_check(changes)
|
push_access_check(changes)
|
||||||
else
|
|
||||||
build_status_object(false, "The command you're trying to execute is not allowed.")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
build_status_object(true)
|
||||||
|
rescue UnauthorizedError => ex
|
||||||
|
build_status_object(false, ex.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def download_access_check
|
def download_access_check
|
||||||
if user
|
if user
|
||||||
user_download_access_check
|
user_download_access_check
|
||||||
elsif deploy_key
|
elsif deploy_key.nil? && !Guest.can?(:download_code, project)
|
||||||
build_status_object(true)
|
raise UnauthorizedError, ERROR_MESSAGES[:download]
|
||||||
else
|
|
||||||
raise 'Wrong actor'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def push_access_check(changes)
|
def push_access_check(changes)
|
||||||
if user
|
if user
|
||||||
user_push_access_check(changes)
|
user_push_access_check(changes)
|
||||||
elsif deploy_key
|
|
||||||
build_status_object(false, "Deploy keys are not allowed to push code.")
|
|
||||||
else
|
else
|
||||||
raise 'Wrong actor'
|
raise UnauthorizedError, ERROR_MESSAGES[deploy_key ? :deploy_key : :upload]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_download_access_check
|
def user_download_access_check
|
||||||
unless user_can_download_code? || build_can_download_code?
|
unless user_can_download_code? || build_can_download_code?
|
||||||
return build_status_object(false, "You are not allowed to download code from this project.")
|
raise UnauthorizedError, ERROR_MESSAGES[:download]
|
||||||
end
|
end
|
||||||
|
|
||||||
build_status_object(true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_can_download_code?
|
def user_can_download_code?
|
||||||
|
@ -78,15 +75,15 @@ module Gitlab
|
||||||
|
|
||||||
def user_push_access_check(changes)
|
def user_push_access_check(changes)
|
||||||
unless authentication_abilities.include?(:push_code)
|
unless authentication_abilities.include?(:push_code)
|
||||||
return build_status_object(false, "You are not allowed to upload code for this project.")
|
raise UnauthorizedError, ERROR_MESSAGES[:upload]
|
||||||
end
|
end
|
||||||
|
|
||||||
if changes.blank?
|
if changes.blank?
|
||||||
return build_status_object(true)
|
return # Allow access.
|
||||||
end
|
end
|
||||||
|
|
||||||
unless project.repository.exists?
|
unless project.repository.exists?
|
||||||
return build_status_object(false, "A repository for this project does not exist yet.")
|
raise UnauthorizedError, ERROR_MESSAGES[:no_repo]
|
||||||
end
|
end
|
||||||
|
|
||||||
changes_list = Gitlab::ChangesList.new(changes)
|
changes_list = Gitlab::ChangesList.new(changes)
|
||||||
|
@ -96,11 +93,9 @@ module Gitlab
|
||||||
status = change_access_check(change)
|
status = change_access_check(change)
|
||||||
unless status.allowed?
|
unless status.allowed?
|
||||||
# If user does not have access to make at least one change - cancel all push
|
# If user does not have access to make at least one change - cancel all push
|
||||||
return status
|
raise UnauthorizedError, status.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
build_status_object(true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def change_access_check(change)
|
def change_access_check(change)
|
||||||
|
@ -113,6 +108,30 @@ module Gitlab
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def check_protocol!
|
||||||
|
unless protocol_allowed?
|
||||||
|
raise UnauthorizedError, "Git access over #{protocol.upcase} is not allowed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_active_user!
|
||||||
|
if user && !user_access.allowed?
|
||||||
|
raise UnauthorizedError, "Your account has been blocked."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_project_accessibility!
|
||||||
|
if project.blank? || !can_read_project?
|
||||||
|
raise UnauthorizedError, 'The project you were looking for could not be found.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_command_existence!(cmd)
|
||||||
|
unless ALL_COMMANDS.include?(cmd)
|
||||||
|
raise UnauthorizedError, "The command you're trying to execute is not allowed."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def matching_merge_request?(newrev, branch_name)
|
def matching_merge_request?(newrev, branch_name)
|
||||||
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
|
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
|
||||||
end
|
end
|
||||||
|
@ -130,6 +149,16 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_read_project?
|
||||||
|
if user
|
||||||
|
user_access.can_read_project?
|
||||||
|
elsif deploy_key
|
||||||
|
deploy_key_can_read_project?
|
||||||
|
else
|
||||||
|
Guest.can?(:read_project, project)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def user
|
def user
|
||||||
|
|
|
@ -102,6 +102,8 @@ module Gitlab
|
||||||
Gitlab::LDAP::Config.providers.each do |provider|
|
Gitlab::LDAP::Config.providers.each do |provider|
|
||||||
adapter = Gitlab::LDAP::Adapter.new(provider)
|
adapter = Gitlab::LDAP::Adapter.new(provider)
|
||||||
@ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
|
@ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
|
||||||
|
# The `uid` might actually be a DN. Try it next.
|
||||||
|
@ldap_person ||= Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter)
|
||||||
break if @ldap_person
|
break if @ldap_person
|
||||||
end
|
end
|
||||||
@ldap_person
|
@ldap_person
|
||||||
|
|
|
@ -13,5 +13,13 @@ module Gitlab
|
||||||
def force_utf8(str)
|
def force_utf8(str)
|
||||||
str.force_encoding(Encoding::UTF_8)
|
str.force_encoding(Encoding::UTF_8)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_boolean(value)
|
||||||
|
return value if [true, false].include?(value)
|
||||||
|
return true if value =~ /^(true|t|yes|y|1|on)$/i
|
||||||
|
return false if value =~ /^(false|f|no|n|0|off)$/i
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,6 +39,17 @@ describe Projects::MergeRequestsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
shared_examples "loads labels" do |action|
|
||||||
|
it "loads labels into the @labels variable" do
|
||||||
|
get action,
|
||||||
|
namespace_id: project.namespace.to_param,
|
||||||
|
project_id: project.to_param,
|
||||||
|
id: merge_request.iid,
|
||||||
|
format: 'html'
|
||||||
|
expect(assigns(:labels)).not_to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "GET show" do
|
describe "GET show" do
|
||||||
shared_examples "export merge as" do |format|
|
shared_examples "export merge as" do |format|
|
||||||
it "does generally work" do
|
it "does generally work" do
|
||||||
|
@ -51,6 +62,8 @@ describe Projects::MergeRequestsController do
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like "loads labels", :show
|
||||||
|
|
||||||
it "generates it" do
|
it "generates it" do
|
||||||
expect_any_instance_of(MergeRequest).to receive(:"to_#{format}")
|
expect_any_instance_of(MergeRequest).to receive(:"to_#{format}")
|
||||||
|
|
||||||
|
@ -340,6 +353,8 @@ describe Projects::MergeRequestsController do
|
||||||
get :diffs, params.merge(extra_params)
|
get :diffs, params.merge(extra_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like "loads labels", :diffs
|
||||||
|
|
||||||
context 'with default params' do
|
context 'with default params' do
|
||||||
context 'as html' do
|
context 'as html' do
|
||||||
before { go(format: 'html') }
|
before { go(format: 'html') }
|
||||||
|
@ -546,6 +561,8 @@ describe Projects::MergeRequestsController do
|
||||||
format: format
|
format: format
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like "loads labels", :commits
|
||||||
|
|
||||||
context 'as html' do
|
context 'as html' do
|
||||||
it 'renders the show template' do
|
it 'renders the show template' do
|
||||||
go
|
go
|
||||||
|
@ -564,6 +581,14 @@ describe Projects::MergeRequestsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET builds' do
|
||||||
|
it_behaves_like "loads labels", :builds
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET pipelines' do
|
||||||
|
it_behaves_like "loads labels", :pipelines
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET conflicts' do
|
describe 'GET conflicts' do
|
||||||
let(:json_response) { JSON.parse(response.body) }
|
let(:json_response) { JSON.parse(response.body) }
|
||||||
|
|
||||||
|
|
|
@ -49,13 +49,17 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:create) do |project, evaluator|
|
after(:create) do |project, evaluator|
|
||||||
|
# Builds and MRs can't have higher visibility level than repository access level.
|
||||||
|
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
|
||||||
|
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
|
||||||
|
|
||||||
project.project_feature.
|
project.project_feature.
|
||||||
update_attributes(
|
update_attributes!(
|
||||||
wiki_access_level: evaluator.wiki_access_level,
|
wiki_access_level: evaluator.wiki_access_level,
|
||||||
builds_access_level: evaluator.builds_access_level,
|
builds_access_level: builds_access_level,
|
||||||
snippets_access_level: evaluator.snippets_access_level,
|
snippets_access_level: evaluator.snippets_access_level,
|
||||||
issues_access_level: evaluator.issues_access_level,
|
issues_access_level: evaluator.issues_access_level,
|
||||||
merge_requests_access_level: evaluator.merge_requests_access_level,
|
merge_requests_access_level: merge_requests_access_level,
|
||||||
repository_access_level: evaluator.repository_access_level
|
repository_access_level: evaluator.repository_access_level
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,11 +12,15 @@ describe 'Commits' do
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:pipeline) do
|
let!(:pipeline) do
|
||||||
FactoryGirl.create :ci_pipeline, project: project, sha: project.commit.sha
|
create(:ci_pipeline,
|
||||||
|
project: project,
|
||||||
|
ref: project.default_branch,
|
||||||
|
sha: project.commit.sha,
|
||||||
|
status: :success)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'commit status is Generic Commit Status' do
|
context 'commit status is Generic Commit Status' do
|
||||||
let!(:status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
|
let!(:status) { create(:generic_commit_status, pipeline: pipeline) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.team << [@user, :reporter]
|
project.team << [@user, :reporter]
|
||||||
|
@ -39,7 +43,7 @@ describe 'Commits' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'commit status is Ci Build' do
|
context 'commit status is Ci Build' do
|
||||||
let!(:build) { FactoryGirl.create :ci_build, pipeline: pipeline }
|
let!(:build) { create(:ci_build, pipeline: pipeline) }
|
||||||
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
|
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
|
||||||
|
|
||||||
context 'when logged as developer' do
|
context 'when logged as developer' do
|
||||||
|
@ -48,13 +52,22 @@ describe 'Commits' do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Project commits' do
|
describe 'Project commits' do
|
||||||
|
let!(:pipeline_from_other_branch) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
project: project,
|
||||||
|
ref: 'fix',
|
||||||
|
sha: project.commit.sha,
|
||||||
|
status: :failed)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
visit namespace_project_commits_path(project.namespace, project, :master)
|
visit namespace_project_commits_path(project.namespace, project, :master)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows build status' do
|
it 'shows correct build status from default branch' do
|
||||||
page.within("//li[@id='commit-#{pipeline.short_sha}']") do
|
page.within("//li[@id='commit-#{pipeline.short_sha}']") do
|
||||||
expect(page).to have_css(".ci-status-link")
|
expect(page).to have_css('.ci-status-link')
|
||||||
|
expect(page).to have_css('.ci-status-icon-success')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
8
spec/features/groups/issues_spec.rb
Normal file
8
spec/features/groups/issues_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'Group issues page', feature: true do
|
||||||
|
let(:path) { issues_group_path(group) }
|
||||||
|
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
|
||||||
|
|
||||||
|
include_examples 'project features apply to issuables', Issue
|
||||||
|
end
|
8
spec/features/groups/merge_requests_spec.rb
Normal file
8
spec/features/groups/merge_requests_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'Group merge requests page', feature: true do
|
||||||
|
let(:path) { merge_requests_group_path(group) }
|
||||||
|
let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: "this is my created issuable")}
|
||||||
|
|
||||||
|
include_examples 'project features apply to issuables', MergeRequest
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue