Commit b6aa7cbc authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents ef7c0945 87f9c475
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.
v 7.12.0 (unreleased) v 7.12.0 (unreleased)
- Fix timeout when rendering file with thousands of lines.
- Don't notify users mentioned in code blocks or blockquotes. - Don't notify users mentioned in code blocks or blockquotes.
- Omit link to generate labels if user does not have access to create them (Stan Hu) - Omit link to generate labels if user does not have access to create them (Stan Hu)
- Show warning when a comment will add 10 or more people to the discussion.
- Disable changing of the source branch in merge request update API (Stan Hu) - Disable changing of the source branch in merge request update API (Stan Hu)
- Shorten merge request WIP text. - Shorten merge request WIP text.
- Add option to disallow users from registering any application to use GitLab as an OAuth provider - Add option to disallow users from registering any application to use GitLab as an OAuth provider
...@@ -47,6 +49,7 @@ v 7.12.0 (unreleased) ...@@ -47,6 +49,7 @@ v 7.12.0 (unreleased)
- When remove project - move repository and schedule it removal - When remove project - move repository and schedule it removal
- Improve group removing logic - Improve group removing logic
- Trigger create-hooks on backup restore task - Trigger create-hooks on backup restore task
- Add option to automatically link omniauth and LDAP identities
v 7.11.4 v 7.11.4
- Fix missing bullets when creating lists - Fix missing bullets when creating lists
......
...@@ -34,7 +34,7 @@ gem "browser" ...@@ -34,7 +34,7 @@ gem "browser"
# 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", '~> 7.2.2' gem "gitlab_git", '~> 7.2.3'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc) # GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
......
...@@ -225,7 +225,7 @@ GEM ...@@ -225,7 +225,7 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.0) gitlab_emoji (0.1.0)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.2.2) gitlab_git (7.2.3)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -713,7 +713,7 @@ DEPENDENCIES ...@@ -713,7 +713,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2) gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.2) gitlab_git (~> 7.2.3)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1) gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
......
...@@ -10,12 +10,17 @@ class @DropzoneInput ...@@ -10,12 +10,17 @@ class @DropzoneInput
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>" iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>" btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_uploads_path = window.project_uploads_path or null project_uploads_path = window.project_uploads_path or null
markdown_preview_path = window.markdown_preview_path or null
max_file_size = gon.max_file_size or 10 max_file_size = gon.max_file_size or 10
form_textarea = $(form).find("textarea.markdown-area") form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>" form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_textarea.bind 'paste', (event) => form_textarea.on 'paste', (event) =>
handlePaste(event) handlePaste(event)
form_textarea.on "input", ->
hideReferencedUsers()
form_textarea.on "blur", ->
renderMarkdown()
form_dropzone = $(form).find('.div-dropzone') form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper" form_dropzone.parent().addClass "div-dropzone-wrapper"
...@@ -45,16 +50,7 @@ class @DropzoneInput ...@@ -45,16 +50,7 @@ class @DropzoneInput
form.find(".md-write-holder").hide() form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show() form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview") renderMarkdown()
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.post($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button # Write button
$(document).off "click", ".js-md-write-button" $(document).off "click", ".js-md-write-button"
...@@ -133,6 +129,40 @@ class @DropzoneInput ...@@ -133,6 +129,40 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea") child = $(dropzone[0]).children("textarea")
hideReferencedUsers = ->
referencedUsers = form.find(".referenced-users")
referencedUsers.hide()
renderReferencedUsers = (users) ->
referencedUsers = form.find(".referenced-users")
if referencedUsers.length
if users.length >= 10
referencedUsers.show()
referencedUsers.find(".js-referenced-users-count").text users.length
else
referencedUsers.hide()
renderMarkdown = ->
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
hideReferencedUsers()
else
preview.text "Loading..."
$.ajax(
type: "POST",
url: markdown_preview_path,
data: {
text: mdText
},
dataType: "json"
).success (data) ->
preview.html data.body
renderReferencedUsers data.references.users
formatLink = (link) -> formatLink = (link) ->
text = "[#{link.alt}](#{link.url})" text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image text = "!#{text}" if link.is_image
......
...@@ -10,7 +10,7 @@ GitLab.GfmAutoComplete = ...@@ -10,7 +10,7 @@ GitLab.GfmAutoComplete =
# Team Members # Team Members
Members: Members:
template: '<li>${username} <small>${name}</small></li>' template: '<li>${username} <small>${title}</small></li>'
# Issues and MergeRequests # Issues and MergeRequests
Issues: Issues:
...@@ -34,7 +34,13 @@ GitLab.GfmAutoComplete = ...@@ -34,7 +34,13 @@ GitLab.GfmAutoComplete =
searchKey: 'search' searchKey: 'search'
callbacks: callbacks:
beforeSave: (members) -> beforeSave: (members) ->
$.map members, (m) -> name: m.name, username: m.username, search: "#{m.username} #{m.name}" $.map members, (m) ->
title = m.name
title += " (#{m.count})" if m.count
username: m.username
title: sanitize(title)
search: sanitize("#{m.username} #{m.name}")
input.atwho input.atwho
at: '#' at: '#'
...@@ -44,7 +50,10 @@ GitLab.GfmAutoComplete = ...@@ -44,7 +50,10 @@ GitLab.GfmAutoComplete =
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
beforeSave: (issues) -> beforeSave: (issues) ->
$.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}" $.map issues, (i) ->
id: i.iid
title: sanitize(i.title)
search: "#{i.iid} #{i.title}"
input.atwho input.atwho
at: '!' at: '!'
...@@ -54,7 +63,10 @@ GitLab.GfmAutoComplete = ...@@ -54,7 +63,10 @@ GitLab.GfmAutoComplete =
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
beforeSave: (merges) -> beforeSave: (merges) ->
$.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}" $.map merges, (m) ->
id: m.iid
title: sanitize(m.title)
search: "#{m.iid} #{m.title}"
input.one 'focus', => input.one 'focus', =>
$.getJSON(@dataSource).done (data) -> $.getJSON(@dataSource).done (data) ->
......
...@@ -8,6 +8,11 @@ header { ...@@ -8,6 +8,11 @@ header {
&.navbar-empty { &.navbar-empty {
background: #FFF; background: #FFF;
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
.center-logo {
margin: 8px 0;
text-align: center;
}
} }
&.navbar-gitlab { &.navbar-gitlab {
......
...@@ -52,6 +52,22 @@ ...@@ -52,6 +52,22 @@
transition: opacity 200ms ease-in-out; transition: opacity 200ms ease-in-out;
} }
.md-area {
position: relative;
}
.md-header ul {
float: left;
}
.referenced-users {
padding: 10px 0;
color: #999;
margin-left: 10px;
margin-top: 1px;
margin-right: 130px;
}
.md-preview-holder { .md-preview-holder {
background: #FFF; background: #FFF;
border: 1px solid #ddd; border: 1px solid #ddd;
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
} }
} }
.referenced-users {
margin-right: 0;
}
.issues-filters, .issues-filters,
.dash-projects-filters, .dash-projects-filters,
.check-all-holder { .check-all-holder {
......
.zennable { .zennable {
position: relative;
.zen-toggle-comment { .zen-toggle-comment {
display: none; display: none;
} }
...@@ -8,8 +6,9 @@ ...@@ -8,8 +6,9 @@
.zen-enter-link { .zen-enter-link {
color: #888; color: #888;
position: absolute; position: absolute;
top: -26px; top: 0px;
right: 4px; right: 4px;
line-height: 40px;
} }
.zen-leave-link { .zen-leave-link {
......
...@@ -39,11 +39,8 @@ ...@@ -39,11 +39,8 @@
.new_note, .edit_note { .new_note, .edit_note {
.buttons { .buttons {
float: left;
margin-top: 8px; margin-top: 8px;
} margin-bottom: 3px;
.clearfix {
margin-bottom: 0;
} }
.note-preview-holder { .note-preview-holder {
...@@ -82,7 +79,6 @@ ...@@ -82,7 +79,6 @@
.note-form-actions { .note-form-actions {
background: #F9F9F9; background: #F9F9F9;
height: 45px;
.note-form-option { .note-form-option {
margin-top: 8px; margin-top: 8px;
......
...@@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController
before_action :commit, except: [:new, :create] before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create] before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update] before_action :from_merge_request, only: [:edit, :update]
before_action :after_edit_path, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :after_edit_path, only: [:edit, :update]
def new def new
commit unless @repository.empty? commit unless @repository.empty?
end end
def create def create
file_path = File.join(@path, File.basename(params[:file_name])) result = Files::CreateService.new(@project, current_user, @commit_params).execute
result = Files::CreateService.new(
@project,
current_user,
params.merge(new_branch: sanitized_new_branch_name),
@ref,
file_path
).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
ref = sanitized_new_branch_name.presence || @ref redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path))
redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path))
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :new render :new
...@@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController
end end
def update def update
result = Files::UpdateService. result = Files::UpdateService.new(@project, current_user, @commit_params).execute
new(
@project,
current_user,
params.merge(new_branch: sanitized_new_branch_name),
@ref,
@path
).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
if from_merge_request
from_merge_request.reload_code
end
redirect_to after_edit_path redirect_to after_edit_path
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
...@@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController
end end
def destroy def destroy
result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute result = Files::DeleteService.new(@project, current_user, @commit_params).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to namespace_project_tree_path(@project.namespace, @project, redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch)
@ref)
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :show render :show
...@@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController
@id = params[:id] @id = params[:id]
@ref, @path = extract_ref(@id) @ref, @path = extract_ref(@id)
rescue InvalidPathError rescue InvalidPathError
not_found! not_found!
end end
...@@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController
if from_merge_request if from_merge_request
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}" "#file-path-#{hexdigest(@path)}"
elsif sanitized_new_branch_name.present? elsif @target_branch.present?
namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path)) namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
else else
namespace_project_blob_path(@project.namespace, @project, @id) namespace_project_blob_path(@project.namespace, @project, @id)
end end
...@@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController
def sanitized_new_branch_name def sanitized_new_branch_name
@new_branch ||= sanitize(strip_tags(params[:new_branch])) @new_branch ||= sanitize(strip_tags(params[:new_branch]))
end end
def editor_variables
@current_branch = @ref
@target_branch = (sanitized_new_branch_name || @ref)
@file_path =
if action_name.to_s == 'create'
File.join(@path, File.basename(params[:file_name]))
else
@path
end
@commit_params = {
file_path: @file_path,
current_branch: @current_branch,
target_branch: @target_branch,
commit_message: params[:commit_message],
file_content: params[:content],
file_content_encoding: params[:encoding]
}
end
end end
...@@ -151,7 +151,17 @@ class ProjectsController < ApplicationController ...@@ -151,7 +151,17 @@ class ProjectsController < ApplicationController
end end
def markdown_preview def markdown_preview
render text: view_context.markdown(params[:md_text]) text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
ext.analyze(text)
render json: {
body: view_context.markdown(text),
references: {
users: ext.users.map(&:username)
}
}
end end
private private
......
module Files module Files
class BaseService < ::BaseService class BaseService < ::BaseService
attr_reader :ref, :path class ValidationError < StandardError; end
def initialize(project, user, params, ref, path = nil) def execute
@project, @current_user, @params = project, user, params.dup @current_branch = params[:current_branch]
@ref = ref @target_branch = params[:target_branch]
@path = path @commit_message = params[:commit_message]
@file_path = params[:file_path]
@file_content = if params[:file_content_encoding] == 'base64'
Base64.decode64(params[:file_content])
else
params[:file_content]
end
# Validate parameters
validate
# Create new branch if it different from current_branch
if @target_branch != @current_branch
create_target_branch
end
if sha = commit
after_commit(sha, @target_branch)
success
else
error("Something went wrong. Your changes were not committed")
end
rescue ValidationError => ex
error(ex.message)
end end
private private
...@@ -14,11 +37,51 @@ module Files ...@@ -14,11 +37,51 @@ module Files
project.repository project.repository
end end
def after_commit(sha) def after_commit(sha, branch)
commit = repository.commit(sha) commit = repository.commit(sha)
full_ref = 'refs/heads/' + (params[:new_branch] || ref) full_ref = 'refs/heads/' + branch
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref) GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
end end
def current_branch
@current_branch ||= params[:current_branch]
end
def target_branch
@target_branch ||= params[:target_branch]
end
def raise_error(message)
raise ValidationError.new(message)
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise_error("You are not allowed to push into this branch")
end
unless project.empty_repo?
unless repository.branch_names.include?(@current_branch)
raise_error("You can only create files if you are on top of a branch")
end
if @current_branch != @target_branch
if repository.branch_names.include?(@target_branch)
raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
end
end
end
end
def create_target_branch
result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch)
unless result[:status] == :success
raise_error("Something went wrong when we tried to create #{@target_branch} for you")
end
end
end end
end end
...@@ -2,58 +2,28 @@ require_relative "base_service" ...@@ -2,58 +2,28 @@ require_relative "base_service"
module Files module Files
class CreateService < Files::BaseService class CreateService < Files::BaseService
def execute def commit
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to create file in this branch")
end end
file_name = File.basename(path) def validate
file_path = path super
file_name = File.basename(@file_path)
unless file_name =~ Gitlab::Regex.file_name_regex unless file_name =~ Gitlab::Regex.file_name_regex
return error( raise_error(
'Your changes could not be committed, because the file name ' + 'Your changes could not be committed, because the file name ' +
Gitlab::Regex.file_name_regex_message Gitlab::Regex.file_name_regex_message
) )
end end
if project.empty_repo? unless project.empty_repo?
# everything is ok because repo does not have a commits yet blob = repository.blob_at_branch(@current_branch, @file_path)
else
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, file_path)
if blob if blob
return error("Your changes could not be committed, because file with such name exists") raise_error("Your changes could not be committed, because file with such name exists")
end
end end
content =
if params[:encoding] == 'base64'
Base64.decode64(params[:content])
else
params[:content]
end
sha = repository.commit_file(
current_user,
file_path,
content,
params[:commit_message],
params[:new_branch] || ref
)
if sha
after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
end end
end end
end end
......
...@@ -2,36 +2,8 @@ require_relative "base_service" ...@@ -2,36 +2,8 @@ require_relative "base_service"
module Files module Files
class DeleteService < Files::BaseService class DeleteService < Files::BaseService
def execute def commit
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.remove_file(current_user, @file_path, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, path)
unless blob
return error("You can only edit text files")
end
sha = repository.remove_file(
current_user,
path,
params[:commit_message],
ref
)
if sha
after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
end
end end
end end
end end
...@@ -2,46 +2,8 @@ require_relative "base_service" ...@@ -2,46 +2,8 @@ require_relative "base_service"
module Files module Files
class UpdateService < Files::BaseService class UpdateService < Files::BaseService
def execute def commit
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at_branch(ref, path)
unless blob
return error("You can only edit text files")
end
content =
if params[:encoding] == 'base64'
Base64.decode64(params[:content])
else
params[:content]
end
sha = repository.commit_file(
current_user,
path,
content,
params[:commit_message],
params[:new_branch] || ref
)
after_commit(sha)
success
rescue Gitlab::Satellite::CheckoutFailed => ex
error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
rescue Gitlab::Satellite::CommitFailed => ex
error("Your changes could not be committed. Maybe there was nothing to commit?", 409)
rescue Gitlab::Satellite::PushFailed => ex
error("Your changes could not be committed. Maybe the file was changed by another process?", 409)
end end
end end
end end
...@@ -38,13 +38,13 @@ module Projects ...@@ -38,13 +38,13 @@ module Projects
def groups def groups
current_user.authorized_groups.sort_by(&:path).map do |group| current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count count = group.users.count
{ username: group.path, name: "#{group.name} (#{count})" } { username: group.path, name: group.name, count: count }
end end
end end
def all_members def all_members
count = project.team.members.flatten.count count = project.team.members.flatten.count
[{ username: "all", name: "All Project and Group Members (#{count})" }] [{ username: "all", name: "All Project and Group Members", count: count }]
end end
end end
end end
...@@ -15,8 +15,10 @@ ...@@ -15,8 +15,10 @@
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
%meta{name: 'theme-color', content: '#474D57'} %meta{name: 'theme-color', content: '#474D57'}
= yield(:meta_tags) = yield :meta_tags
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id') = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/bootlint' if Rails.env.development? = render 'layouts/bootlint' if Rails.env.development?
= yield :scripts_head
...@@ -22,5 +22,3 @@ ...@@ -22,5 +22,3 @@
= render "layouts/flash" = render "layouts/flash"
.clearfix .clearfix
= yield = yield
= yield :embedded_scripts
...@@ -8,3 +8,5 @@ ...@@ -8,3 +8,5 @@
= render "layouts/header/public", title: header_title = render "layouts/header/public", title: header_title
= render 'layouts/page', sidebar: sidebar = render 'layouts/page', sidebar: sidebar
= yield :scripts_body
%header.navbar.navbar-fixed-top.navbar-empty %header.navbar.navbar-fixed-top.navbar-empty
.container .container
%h4.center .center-logo
= image_tag 'logo-white.png', width: 32, height: 32 = image_tag 'logo-white.png', width: 32, height: 32
...@@ -2,7 +2,13 @@ ...@@ -2,7 +2,13 @@
- header_title project_title(@project) - header_title project_title(@project)
- sidebar "project" unless sidebar - sidebar "project" unless sidebar
- content_for :embedded_scripts do - content_for :scripts_head do
-if current_user
:javascript
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
window.markdown_preview_path = "#{markdown_preview_namespace_project_path(@project.namespace, @project)}";
- content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user = render "layouts/init_auto_complete" if current_user
= render template: "layouts/application" = render template: "layouts/application"
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= f.label :description, 'Description', class: 'control-label' = f.label :description, 'Description', class: 'control-label'
.col-sm-10 .col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do = render layout: 'projects/md_preview', locals: { preview_class: "wiki", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, = render 'projects/zen', f: f, attr: :description,
classes: 'description form-control' classes: 'description form-control'
.col-sm-12.hint .col-sm-12.hint
......
%ul.nav.nav-tabs .md-area
.md-header.clearfix
%ul.nav.nav-tabs
%li.active %li.active
= link_to '#md-write-holder', class: 'js-md-write-button' do = link_to '#md-write-holder', class: 'js-md-write-button' do
Write Write
%li %li
= link_to '#md-preview-holder', class: 'js-md-preview-button', = link_to '#md-preview-holder', class: 'js-md-preview-button' do
data: { url: markdown_preview_namespace_project_path(@project.namespace, @project) } do
Preview Preview
%div
- if defined?(referenced_users) && referenced_users
%span.referenced-users.pull-left.hide
= icon('exclamation-triangle')
You are about to add
%strong
%span.js-referenced-users-count 0
people
to the discussion. Proceed with caution.
%div
.md-write-holder .md-write-holder
= yield = yield
.md.md-preview-holder.hide .md.md-preview-holder.hide
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
= render 'shared/commit_message_container', params: params, = render 'shared/commit_message_container', params: params,
placeholder: 'Add new file' placeholder: 'Add new file'
- unless @project.empty_repo?
.form-group.branch .form-group.branch
= label_tag 'branch', class: 'control-label' do = label_tag 'branch', class: 'control-label' do
Branch Branch
......
...@@ -10,5 +10,3 @@ ...@@ -10,5 +10,3 @@
$('#issue_assignee_id').val("#{current_user.id}").trigger("change"); $('#issue_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault(); e.preventDefault();
}); });
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
...@@ -8,5 +8,3 @@ ...@@ -8,5 +8,3 @@
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change"); $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault(); e.preventDefault();
}); });
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
...@@ -51,8 +51,6 @@ ...@@ -51,8 +51,6 @@
e.preventDefault(); e.preventDefault();
}); });
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
:javascript :javascript
var merge_request var merge_request
merge_request = new MergeRequest({ merge_request = new MergeRequest({
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
.automerge_widget.cannot_be_merged.hide .automerge_widget.cannot_be_merged.hide
%h4 %h4
This pull request contains merge conflicts that must be resolved. This merge request contains merge conflicts that must be resolved.
You can try it manually on the You can try it manually on the
%strong %strong
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
...@@ -63,14 +63,14 @@ ...@@ -63,14 +63,14 @@
.automerge_widget.work_in_progress.hide .automerge_widget.work_in_progress.hide
%h4 %h4
This request cannot be merged because it is marked as <strong>Work In Progress</strong>. This merge request cannot be accepted because it is marked as Work In Progress.
%p %p
%button.btn.disabled{:type => 'button'} %button.btn.disabled{:type => 'button'}
%i.fa.fa-warning %i.fa.fa-warning
Accept Merge Request Accept Merge Request
&nbsp; &nbsp;
When the merge request is ready, remove the "WIP" prefix from the title to allow merging. When the merge request is ready, remove the "WIP" prefix from the title to allow it to be accepted.
.automerge_widget.unchecked .automerge_widget.unchecked
%p %p
......
...@@ -50,5 +50,3 @@ ...@@ -50,5 +50,3 @@
dateFormat: "yy-mm-dd", dateFormat: "yy-mm-dd",
onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
}).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, = render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text' classes: 'note_text js-note-text'
...@@ -15,10 +15,7 @@ ...@@ -15,10 +15,7 @@
.error-alert .error-alert
.note-form-actions .note-form-actions
.buttons .buttons.clearfix
= f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button" = f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button"
= yield(:note_actions) = yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel %a.btn.grouped.js-close-discussion-note-form Cancel
:javascript
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
...@@ -41,6 +41,3 @@ ...@@ -41,6 +41,3 @@
- else - else
= f.submit 'Create page', class: "btn-create btn" = f.submit 'Create page', class: "btn-create btn"
= link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel" = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
:javascript
window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
- blob.data.lines.to_a.size.times do |index| - blob.data.lines.to_a.size.times do |index|
- offset = defined?(first_line_number) ? first_line_number : 1 - offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset - i = index + offset
= link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do / We're not using `link_to` because it is too slow once we get to thousands of lines.
%a{href: "#L#{i}", id: "L#{i}", rel: "#L#{i}"}
%i.fa.fa-link %i.fa.fa-link
= i = i
:preserve :preserve
......
...@@ -192,6 +192,9 @@ production: &base ...@@ -192,6 +192,9 @@ production: &base
allow_single_sign_on: false allow_single_sign_on: false
# Locks down those users until they have been cleared by the admin (default: true). # Locks down those users until they have been cleared by the admin (default: true).
block_auto_created_users: true block_auto_created_users: true
# Look up new users in LDAP servers. If a match is found (same uid), automatically
# link the omniauth identity with the LDAP account. (default: false)
auto_link_ldap_user: false
## Auth providers ## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use # Uncomment the following lines and fill in the data of the auth provider you want to use
......
...@@ -88,6 +88,9 @@ end ...@@ -88,6 +88,9 @@ end
Settings['omniauth'] ||= Settingslogic.new({}) Settings['omniauth'] ||= Settingslogic.new({})
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil? Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block_auto_created_users'].nil?
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['providers'] ||= [] Settings.omniauth['providers'] ||= []
......
...@@ -68,6 +68,8 @@ Bitbucket will generate an application ID and secret key for you to use. ...@@ -68,6 +68,8 @@ Bitbucket will generate an application ID and secret key for you to use.
1. Save the configuration file. 1. Save the configuration file.
1. If you're using the omnibus package, reconfigure GitLab (```gitlab-ctl reconfigure```).
1. Restart GitLab for the changes to take effect. 1. Restart GitLab for the changes to take effect.
On the sign in page there should now be a Bitbucket icon below the regular sign in form. On the sign in page there should now be a Bitbucket icon below the regular sign in form.
...@@ -80,43 +82,59 @@ To allow projects to be imported directly into GitLab, Bitbucket requires two ex ...@@ -80,43 +82,59 @@ To allow projects to be imported directly into GitLab, Bitbucket requires two ex
Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and instead requires GitLab to use SSH and identify itself using your GitLab server's SSH key. Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and instead requires GitLab to use SSH and identify itself using your GitLab server's SSH key.
### Step 1: Known hosts ### Step 1: Public key
To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org' to your GitLab server's known SSH hosts. Take the following steps to do so: To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa.pub`, which will expand to `/home/git/.ssh/bitbucket_rsa.pub` in most configurations.
1. Manually connect to 'bitbucket.org' over SSH, while logged in as the `git` account that GitLab will use: If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following:
1. Create a new SSH key:
```sh ```sh
ssh git@bitbucket.org sudo -u git -H ssh-keygen
``` ```
1. Verify the RSA key fingerprint you'll see in the response matches the one in the [Bitbucket documentation](https://confluence.atlassian.com/display/BITBUCKET/Use+the+SSH+protocol+with+Bitbucket#UsetheSSHprotocolwithBitbucket-KnownhostorBitbucket'spublickeyfingerprints) (the specific IP address doesn't matter): When asked `Enter file in which to save the key` specify the correct path, eg. `/home/git/.ssh/bitbucket_rsa`.
Make sure to use an **empty passphrase**.
1. Configure SSH client to use your new key:
Open the SSH configuration file of the git user.
```sh ```sh
The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established. sudo editor /home/git/.ssh/config
RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
Are you sure you want to continue connecting (yes/no)?
``` ```
1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts. Add a host configuration for `bitbucket.org`.
```sh
Host bitbucket.org
IdentityFile ~/.ssh/bitbucket_rsa
User git
```
1. Your GitLab server is now able to connect to Bitbucket over SSH. Continue to step 2: ### Step 2: Known hosts
### Step 2: Public key To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org' to your GitLab server's known SSH hosts. Take the following steps to do so:
To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa.pub`, which will expand to `/home/git/.ssh/bitbucket_rsa.pub` in most configurations. 1. Manually connect to 'bitbucket.org' over SSH, while logged in as the `git` account that GitLab will use:
If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following: ```sh
sudo -u git -H ssh bitbucket.org
```
1. Create a new SSH key: 1. Verify the RSA key fingerprint you'll see in the response matches the one in the [Bitbucket documentation](https://confluence.atlassian.com/display/BITBUCKET/Use+the+SSH+protocol+with+Bitbucket#UsetheSSHprotocolwithBitbucket-KnownhostorBitbucket'spublickeyfingerprints) (the specific IP address doesn't matter):
```sh ```sh
sudo -u git -H ssh-keygen The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established.
RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
Are you sure you want to continue connecting (yes/no)?
``` ```
When asked `Enter file in which to save the key` specify the correct path, eg. `/home/git/.ssh/bitbucket_rsa`. 1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts.
Make sure to use an **empty passphrase**.
1. Your GitLab server is now able to connect to Bitbucket over SSH.
2. Restart GitLab to allow it to find the new public key. 1. Restart GitLab to allow it to find the new public key.
You should now see the "Import projects from Bitbucket" option on the New Project page enabled. You should now see the "Import projects from Bitbucket" option on the New Project page enabled.
...@@ -16,3 +16,4 @@ ...@@ -16,3 +16,4 @@
- [Change your time zone](timezone.md) - [Change your time zone](timezone.md)
- [Keyboard shortcuts](shortcuts.md) - [Keyboard shortcuts](shortcuts.md)
- [Web Editor](web_editor.md) - [Web Editor](web_editor.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
# "Work In Progress" Merge Requests
To prevent merge requests from accidentally being accepted before they're completely ready, GitLab blocks the "Accept" button for merge requests that have been marked a **Work In Progress**.
![Blocked Accept Button](wip_merge_requests/blocked_accept_button.png)
To mark a merge request a Work In Progress, simply start its title with `[WIP]` or `WIP:`.
![Mark as WIP](wip_merge_requests/mark_as_wip.png)
To allow a Work In Progress merge request to be accepted again when it's ready, simply remove the `WIP` prefix.
![Unark as WIP](wip_merge_requests/unmark_as_wip.png)
...@@ -122,7 +122,7 @@ You can find all available options in [Omnibus GitLab documentation](https://git ...@@ -122,7 +122,7 @@ You can find all available options in [Omnibus GitLab documentation](https://git
### Upgrade GitLab with app and data images ### Upgrade GitLab with app and data images
To updgrade GitLab to new versions, stop running container, create new docker image and container from that image. To upgrade GitLab to new versions, stop running container, create new docker image and container from that image.
It Assumes that you're upgrading from 7.8.1 to 7.10.1 and you're in the updated GitLab repo root directory: It Assumes that you're upgrading from 7.8.1 to 7.10.1 and you're in the updated GitLab repo root directory:
...@@ -141,10 +141,12 @@ sudo docker rmi gitlab-app:7.8.1 ...@@ -141,10 +141,12 @@ sudo docker rmi gitlab-app:7.8.1
### Publish images to Dockerhub ### Publish images to Dockerhub
Login to Dockerhub with `sudo docker login` and run the following (replace '7.9.2' with the version you're using and 'Sytse Sijbrandij' with your name): - Ensure the containers are running
- Login to Dockerhub with `sudo docker login`
- Run the following (replace '7.9.2' with the version you're using and 'Sytse Sijbrandij' with your name):
```bash ```bash
sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab-app:7.10.1 sytse/gitlab-app:7.10.1 sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab-app sytse/gitlab-app:7.10.1
sudo docker push sytse/gitlab-app:7.10.1 sudo docker push sytse/gitlab-app:7.10.1
sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab_data sytse/gitlab_data sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab_data sytse/gitlab_data
sudo docker push sytse/gitlab_data sudo docker push sytse/gitlab_data
......
...@@ -3,6 +3,26 @@ module API ...@@ -3,6 +3,26 @@ module API
class Files < Grape::API class Files < Grape::API
before { authenticate! } before { authenticate! }
helpers do
def commit_params(attrs)
{
file_path: attrs[:file_path],
current_branch: attrs[:branch_name],
target_branch: attrs[:branch_name],
commit_message: attrs[:commit_message],
file_content: attrs[:content],
file_content_encoding: attrs[:encoding]
}
end
def commit_response(attrs)
{
file_path: attrs[:file_path],
branch_name: attrs[:branch_name],
}
end
end
resource :projects do resource :projects do
# Get file from repository # Get file from repository
# File content is Base64 encoded # File content is Base64 encoded
...@@ -73,17 +93,11 @@ module API ...@@ -73,17 +93,11 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message] required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding] attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
branch_name = attrs.delete(:branch_name) result = ::Files::CreateService.new(user_project, current_user, commit_params(attrs)).execute
file_path = attrs.delete(:file_path)
result = ::Files::CreateService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success if result[:status] == :success
status(201) status(201)
commit_response(attrs)
{
file_path: file_path,
branch_name: branch_name
}
else else
render_api_error!(result[:message], 400) render_api_error!(result[:message], 400)
end end
...@@ -105,17 +119,11 @@ module API ...@@ -105,17 +119,11 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message] required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding] attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
branch_name = attrs.delete(:branch_name) result = ::Files::UpdateService.new(user_project, current_user, commit_params(attrs)).execute
file_path = attrs.delete(:file_path)
result = ::Files::UpdateService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success if result[:status] == :success
status(200) status(200)
commit_response(attrs)
{
file_path: file_path,
branch_name: branch_name
}
else else
http_status = result[:http_status] || 400 http_status = result[:http_status] || 400
render_api_error!(result[:message], http_status) render_api_error!(result[:message], http_status)
...@@ -138,17 +146,11 @@ module API ...@@ -138,17 +146,11 @@ module API
required_attributes! [:file_path, :branch_name, :commit_message] required_attributes! [:file_path, :branch_name, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :commit_message] attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
branch_name = attrs.delete(:branch_name) result = ::Files::DeleteService.new(user_project, current_user, commit_params(attrs)).execute
file_path = attrs.delete(:file_path)
result = ::Files::DeleteService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success if result[:status] == :success
status(200) status(200)
commit_response(attrs)
{
file_path: file_path,
branch_name: branch_name
}
else else
render_api_error!(result[:message], 400) render_api_error!(result[:message], 400)
end end
......
...@@ -46,6 +46,10 @@ module Gitlab ...@@ -46,6 +46,10 @@ module Gitlab
def gl_user def gl_user
@user ||= find_by_uid_and_provider @user ||= find_by_uid_and_provider
if auto_link_ldap_user?
@user ||= find_or_create_ldap_user
end
if signup_enabled? if signup_enabled?
@user ||= build_new_user @user ||= build_new_user
end end
...@@ -55,6 +59,46 @@ module Gitlab ...@@ -55,6 +59,46 @@ module Gitlab
protected protected
def find_or_create_ldap_user
return unless ldap_person
# If a corresponding person exists with same uid in a LDAP server,
# set up a Gitlab user with dual LDAP and Omniauth identities.
if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn.downcase, ldap_person.provider)
# Case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
else
# No account in Gitlab yet: create it and add the LDAP identity
user = build_new_user
user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn)
end
user
end
def auto_link_ldap_user?
Gitlab.config.omniauth.auto_link_ldap_user
end
def creating_linked_ldap_user?
auto_link_ldap_user? && ldap_person
end
def ldap_person
return @ldap_person if defined?(@ldap_person)
# looks for a corresponding person with same uid in any of the configured LDAP providers
@ldap_person = Gitlab::LDAP::Config.providers.find do |provider|
adapter = Gitlab::LDAP::Adapter.new(provider)
Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
end
end
def ldap_config
Gitlab::LDAP::Config.new(ldap_person.provider) if ldap_person
end
def needs_blocking? def needs_blocking?
new? && block_after_signup? new? && block_after_signup?
end end
...@@ -64,8 +108,12 @@ module Gitlab ...@@ -64,8 +108,12 @@ module Gitlab
end end
def block_after_signup? def block_after_signup?
if creating_linked_ldap_user?
ldap_config.block_auto_created_users
else
Gitlab.config.omniauth.block_auto_created_users Gitlab.config.omniauth.block_auto_created_users
end end
end
def auth_hash=(auth_hash) def auth_hash=(auth_hash)
@auth_hash = AuthHash.new(auth_hash) @auth_hash = AuthHash.new(auth_hash)
...@@ -84,10 +132,19 @@ module Gitlab ...@@ -84,10 +132,19 @@ module Gitlab
end end
def user_attributes def user_attributes
# Give preference to LDAP for sensitive information when creating a linked account
if creating_linked_ldap_user?
username = ldap_person.username
email = ldap_person.email.first
else
username = auth_hash.username
email = auth_hash.email
end
{ {
name: auth_hash.name, name: auth_hash.name,
username: ::Namespace.clean_path(auth_hash.username), username: ::Namespace.clean_path(username),
email: auth_hash.email, email: email,
password: auth_hash.password, password: auth_hash.password,
password_confirmation: auth_hash.password, password_confirmation: auth_hash.password,
password_automatically_set: true password_automatically_set: true
......
...@@ -13,6 +13,7 @@ describe Gitlab::OAuth::User do ...@@ -13,6 +13,7 @@ describe Gitlab::OAuth::User do
email: 'john@mail.com' email: 'john@mail.com'
} }
end end
let(:ldap_user) { Gitlab::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
describe :persisted? do describe :persisted? do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
...@@ -32,6 +33,7 @@ describe Gitlab::OAuth::User do ...@@ -32,6 +33,7 @@ describe Gitlab::OAuth::User do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
describe 'signup' do describe 'signup' do
shared_examples "to verify compliance with allow_single_sign_on" do
context "with allow_single_sign_on enabled" do context "with allow_single_sign_on enabled" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: true } before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
...@@ -46,17 +48,79 @@ describe Gitlab::OAuth::User do ...@@ -46,17 +48,79 @@ describe Gitlab::OAuth::User do
end end
context "with allow_single_sign_on disabled (Default)" do context "with allow_single_sign_on disabled (Default)" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: false }
it "throws an error" do it "throws an error" do
expect{ oauth_user.save }.to raise_error StandardError expect{ oauth_user.save }.to raise_error StandardError
end end
end end
end end
context "with auto_link_ldap_user disabled (default)" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: false }
include_examples "to verify compliance with allow_single_sign_on"
end
context "with auto_link_ldap_user enabled" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: true }
context "and a corresponding LDAP person" do
before do
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end
context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
expect(gl_user.email).to eql 'johndoe@example.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(
[ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
{ provider: 'twitter', extern_uid: uid }
])
end
end
context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
expect(gl_user.email).to eql 'john@example.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(
[ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
{ provider: 'twitter', extern_uid: uid }
])
end
end
end
context "and no corresponding LDAP person" do
before { allow(oauth_user).to receive(:ldap_person).and_return(nil) }
include_examples "to verify compliance with allow_single_sign_on"
end
end
end
describe 'blocking' do describe 'blocking' do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
before { Gitlab.config.omniauth.stub allow_single_sign_on: true } before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
context 'signup' do context 'signup with omniauth only' do
context 'dont block on create' do context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false } before { Gitlab.config.omniauth.stub block_auto_created_users: false }
...@@ -78,6 +142,64 @@ describe Gitlab::OAuth::User do ...@@ -78,6 +142,64 @@ describe Gitlab::OAuth::User do
end end
end end
context 'signup with linked omniauth and LDAP account' do
before do
Gitlab.config.omniauth.stub auto_link_ldap_user: true
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end
context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
end
end
context 'and LDAP user has an account already' do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
end
end
context 'sign-in' do context 'sign-in' do
before do before do
oauth_user.save oauth_user.save
...@@ -103,6 +225,26 @@ describe Gitlab::OAuth::User do ...@@ -103,6 +225,26 @@ describe Gitlab::OAuth::User do
expect(gl_user).not_to be_blocked expect(gl_user).not_to be_blocked
end end
end end
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
it do
oauth_user.save
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
end
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment