summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/branches.rb5
-rw-r--r--lib/api/entities.rb28
-rw-r--r--lib/api/files.rb6
-rw-r--r--lib/api/group_members.rb80
-rw-r--r--lib/api/groups.rb51
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/internal.rb17
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/repositories.rb5
-rw-r--r--lib/api/services.rb38
-rw-r--r--lib/backup/repository.rb2
-rw-r--r--lib/disable_email_interceptor.rb8
-rw-r--r--lib/gitlab/app_logger.rb4
-rw-r--r--lib/gitlab/auth.rb16
-rw-r--r--lib/gitlab/backend/grack_auth.rb2
-rw-r--r--lib/gitlab/backend/shell.rb32
-rw-r--r--lib/gitlab/closing_issue_extractor.rb2
-rw-r--r--lib/gitlab/git.rb5
-rw-r--r--lib/gitlab/git_access.rb45
-rw-r--r--lib/gitlab/git_access_wiki.rb7
-rw-r--r--lib/gitlab/git_logger.rb4
-rw-r--r--lib/gitlab/issues_labels.rb1
-rw-r--r--lib/gitlab/ldap/access.rb36
-rw-r--r--lib/gitlab/ldap/adapter.rb69
-rw-r--r--lib/gitlab/ldap/authentication.rb71
-rw-r--r--lib/gitlab/ldap/config.rb120
-rw-r--r--lib/gitlab/ldap/person.rb26
-rw-r--r--lib/gitlab/ldap/user.rb93
-rw-r--r--lib/gitlab/logger.rb4
-rw-r--r--lib/gitlab/markdown.rb142
-rw-r--r--lib/gitlab/markdown_helper.rb4
-rw-r--r--lib/gitlab/oauth/auth_hash.rb2
-rw-r--r--lib/gitlab/oauth/user.rb94
-rw-r--r--lib/gitlab/production_logger.rb7
-rw-r--r--lib/gitlab/reference_extractor.rb52
-rw-r--r--lib/gitlab/sidekiq_logger.rb7
-rw-r--r--lib/gitlab/url_builder.rb2
-rw-r--r--lib/redcarpet/render/gitlab_html.rb18
-rw-r--r--lib/support/nginx/gitlab-ssl24
-rw-r--r--lib/tasks/gitlab/check.rake40
-rw-r--r--lib/tasks/gitlab/import.rake14
-rw-r--r--lib/tasks/gitlab/shell.rake10
43 files changed, 800 insertions, 404 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 2c7cd9038c3..d26667ba3f7 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -27,6 +27,7 @@ module API
helpers APIHelpers
mount Groups
+ mount GroupMembers
mount Users
mount Projects
mount Repositories
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 14f8b20f6b2..6ec1a753a69 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -82,6 +82,7 @@ module API
authorize_push_project
result = CreateBranchService.new(user_project, current_user).
execute(params[:branch_name], params[:ref])
+
if result[:status] == :success
present result[:branch],
with: Entities::RepoObject,
@@ -104,7 +105,9 @@ module API
execute(params[:branch])
if result[:status] == :success
- true
+ {
+ branch_name: params[:branch]
+ }
else
render_api_error!(result[:message], result[:return_code])
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 80e9470195e..40696489ba2 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -16,7 +16,8 @@ module API
class UserFull < User
expose :email
- expose :theme_id, :color_scheme_id, :extern_uid, :provider
+ expose :theme_id, :color_scheme_id, :extern_uid, :provider, \
+ :projects_limit
expose :can_create_group?, as: :can_create_group
expose :can_create_project?, as: :can_create_project
end
@@ -73,6 +74,25 @@ module API
end
end
+ class RepoTag < Grape::Entity
+ expose :name
+ expose :message do |repo_obj, _options|
+ if repo_obj.respond_to?(:message)
+ repo_obj.message
+ else
+ nil
+ end
+ end
+
+ expose :commit do |repo_obj, options|
+ if repo_obj.respond_to?(:commit)
+ repo_obj.commit
+ elsif options[:project]
+ options[:project].repository.commit(repo_obj.target)
+ end
+ end
+ end
+
class RepoObject < Grape::Entity
expose :name
@@ -164,6 +184,12 @@ module API
expose :target_id, :target_type, :author_id
expose :data, :target_title
expose :created_at
+
+ expose :author_username do |event, options|
+ if event.author
+ event.author.username
+ end
+ end
end
class Namespace < Grape::Entity
diff --git a/lib/api/files.rb b/lib/api/files.rb
index e63e635a4d3..84e1d311781 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -85,7 +85,7 @@ module API
branch_name: branch_name
}
else
- render_api_error!(result[:error], 400)
+ render_api_error!(result[:message], 400)
end
end
@@ -117,7 +117,7 @@ module API
branch_name: branch_name
}
else
- render_api_error!(result[:error], 400)
+ render_api_error!(result[:message], 400)
end
end
@@ -149,7 +149,7 @@ module API
branch_name: branch_name
}
else
- render_api_error!(result[:error], 400)
+ render_api_error!(result[:message], 400)
end
end
end
diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb
new file mode 100644
index 00000000000..d596517c816
--- /dev/null
+++ b/lib/api/group_members.rb
@@ -0,0 +1,80 @@
+module API
+ class GroupMembers < Grape::API
+ before { authenticate! }
+
+ resource :groups do
+ helpers do
+ def find_group(id)
+ group = Group.find(id)
+
+ if can?(current_user, :read_group, group)
+ group
+ else
+ render_api_error!("403 Forbidden - #{current_user.username} lacks sufficient access to #{group.name}", 403)
+ end
+ end
+
+ def validate_access_level?(level)
+ Gitlab::Access.options_with_owner.values.include? level.to_i
+ end
+ end
+
+ # Get a list of group members viewable by the authenticated user.
+ #
+ # Example Request:
+ # GET /groups/:id/members
+ get ":id/members" do
+ group = find_group(params[:id])
+ members = group.group_members
+ users = (paginate members).collect(&:user)
+ present users, with: Entities::GroupMember, group: group
+ end
+
+ # Add a user to the list of group members
+ #
+ # Parameters:
+ # id (required) - group id
+ # user_id (required) - the users id
+ # access_level (required) - Project access level
+ # Example Request:
+ # POST /groups/:id/members
+ post ":id/members" do
+ group = find_group(params[:id])
+ authorize! :manage_group, group
+ required_attributes! [:user_id, :access_level]
+
+ unless validate_access_level?(params[:access_level])
+ render_api_error!("Wrong access level", 422)
+ end
+
+ if group.group_members.find_by(user_id: params[:user_id])
+ render_api_error!("Already exists", 409)
+ end
+
+ group.add_users([params[:user_id]], params[:access_level])
+ member = group.group_members.find_by(user_id: params[:user_id])
+ present member.user, with: Entities::GroupMember, group: group
+ end
+
+ # Remove member.
+ #
+ # Parameters:
+ # id (required) - group id
+ # user_id (required) - the users id
+ #
+ # Example Request:
+ # DELETE /groups/:id/members/:user_id
+ delete ":id/members/:user_id" do
+ group = find_group(params[:id])
+ authorize! :manage_group, group
+ member = group.group_members.find_by(user_id: params[:user_id])
+
+ if member.nil?
+ render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404)
+ else
+ member.destroy
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 4841e04689d..f0ab6938b1c 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -97,57 +97,6 @@ module API
not_found!
end
end
-
- # Get a list of group members viewable by the authenticated user.
- #
- # Example Request:
- # GET /groups/:id/members
- get ":id/members" do
- group = find_group(params[:id])
- members = group.group_members
- users = (paginate members).collect(&:user)
- present users, with: Entities::GroupMember, group: group
- end
-
- # Add a user to the list of group members
- #
- # Parameters:
- # id (required) - group id
- # user_id (required) - the users id
- # access_level (required) - Project access level
- # Example Request:
- # POST /groups/:id/members
- post ":id/members" do
- required_attributes! [:user_id, :access_level]
- unless validate_access_level?(params[:access_level])
- render_api_error!("Wrong access level", 422)
- end
- group = find_group(params[:id])
- if group.group_members.find_by(user_id: params[:user_id])
- render_api_error!("Already exists", 409)
- end
- group.add_users([params[:user_id]], params[:access_level])
- member = group.group_members.find_by(user_id: params[:user_id])
- present member.user, with: Entities::GroupMember, group: group
- end
-
- # Remove member.
- #
- # Parameters:
- # id (required) - group id
- # user_id (required) - the users id
- #
- # Example Request:
- # DELETE /groups/:id/members/:user_id
- delete ":id/members/:user_id" do
- group = find_group(params[:id])
- member = group.group_members.find_by(user_id: params[:user_id])
- if member.nil?
- render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404)
- else
- member.destroy
- end
- end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 3262884f6d3..027fb20ec46 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -67,6 +67,10 @@ module API
unauthorized! unless current_user
end
+ def authenticate_by_gitlab_shell_token!
+ unauthorized! unless secret_token == params['secret_token']
+ end
+
def authenticated_as_admin!
forbidden! unless current_user.is_admin?
end
@@ -193,5 +197,9 @@ module API
abilities
end
end
+
+ def secret_token
+ File.read(Rails.root.join('.gitlab_shell_secret'))
+ end
end
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 5f484f63418..ebf2296097d 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -1,6 +1,10 @@
module API
# Internal access API
class Internal < Grape::API
+ before {
+ authenticate_by_gitlab_shell_token!
+ }
+
namespace 'internal' do
# Check if git command is allowed to project
#
@@ -14,13 +18,20 @@ module API
#
post "/allowed" do
status 200
+ project_path = params[:project]
# Check for *.wiki repositories.
# Strip out the .wiki from the pathname before finding the
# project. This applies the correct project permissions to
# the wiki repository as well.
- project_path = params[:project]
- project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
+ access =
+ if project_path =~ /\.wiki\Z/
+ project_path.sub!(/\.wiki\Z/, '')
+ Gitlab::GitAccessWiki.new
+ else
+ Gitlab::GitAccess.new
+ end
+
project = Project.find_with_namespace(project_path)
return false unless project
@@ -32,7 +43,7 @@ module API
return false unless actor
- Gitlab::GitAccess.new.allowed?(
+ access.allowed?(
actor,
params[:action],
project,
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 7f7d2f8e9a8..7fcf97d1ad6 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -178,7 +178,7 @@ module API
# DELETE /projects/:id
delete ":id" do
authorize! :remove_project, user_project
- user_project.destroy
+ ::Projects::DestroyService.new(user_project, current_user, {}).execute
end
# Mark this project as forked from another
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 626d99c2649..a1a7721b288 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -23,7 +23,8 @@ module API
# Example Request:
# GET /projects/:id/repository/tags
get ":id/repository/tags" do
- present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject, project: user_project
+ present user_project.repo.tags.sort_by(&:name).reverse,
+ with: Entities::RepoTag, project: user_project
end
# Create tag
@@ -43,7 +44,7 @@ module API
if result[:status] == :success
present result[:tag],
- with: Entities::RepoObject,
+ with: Entities::RepoTag,
project: user_project
else
render_api_error!(result[:message], 400)
diff --git a/lib/api/services.rb b/lib/api/services.rb
index bde502e32e1..3ad59cf3adf 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -28,7 +28,7 @@ module API
# Delete GitLab CI service settings
#
# Example Request:
- # DELETE /projects/:id/keys/:id
+ # DELETE /projects/:id/services/gitlab-ci
delete ":id/services/gitlab-ci" do
if user_project.gitlab_ci_service
user_project.gitlab_ci_service.update_attributes(
@@ -38,7 +38,41 @@ module API
)
end
end
+
+ # Set Hipchat service for project
+ #
+ # Parameters:
+ # token (required) - Hipchat token
+ # room (required) - Hipchat room name
+ #
+ # Example Request:
+ # PUT /projects/:id/services/hipchat
+ put ':id/services/hipchat' do
+ required_attributes! [:token, :room]
+ attrs = attributes_for_keys [:token, :room]
+ user_project.build_missing_services
+
+ if user_project.hipchat_service.update_attributes(
+ attrs.merge(active: true))
+ true
+ else
+ not_found!
+ end
+ end
+
+ # Delete Hipchat service settings
+ #
+ # Example Request:
+ # DELETE /projects/:id/services/hipchat
+ delete ':id/services/hipchat' do
+ if user_project.hipchat_service
+ user_project.hipchat_service.update_attributes(
+ active: false,
+ token: nil,
+ room: nil
+ )
+ end
+ end
end
end
end
-
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 4e99d4bbe5c..380beac708d 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -30,7 +30,7 @@ module Backup
if File.exists?(path_to_repo(wiki))
print " * #{wiki.path_with_namespace} ... "
- if wiki.empty?
+ if wiki.repository.empty?
puts " [SKIPPED]".cyan
else
output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all))
diff --git a/lib/disable_email_interceptor.rb b/lib/disable_email_interceptor.rb
new file mode 100644
index 00000000000..1b80be112a4
--- /dev/null
+++ b/lib/disable_email_interceptor.rb
@@ -0,0 +1,8 @@
+# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
+class DisableEmailInterceptor
+
+ def self.delivering_email(message)
+ message.perform_deliveries = false
+ Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
+ end
+end
diff --git a/lib/gitlab/app_logger.rb b/lib/gitlab/app_logger.rb
index 8e4717b46e6..dddcb2538f9 100644
--- a/lib/gitlab/app_logger.rb
+++ b/lib/gitlab/app_logger.rb
@@ -1,7 +1,7 @@
module Gitlab
class AppLogger < Gitlab::Logger
- def self.file_name
- 'application.log'
+ def self.file_name_noext
+ 'application'
end
def format_message(severity, timestamp, progname, msg)
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 955abc1bedd..30509528b8b 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -1,24 +1,18 @@
module Gitlab
class Auth
def find(login, password)
- user = User.find_by(email: login) || User.find_by(username: login)
+ user = User.by_login(login)
+ # If no user is found, or it's an LDAP server, try LDAP.
+ # LDAP users are only authenticated via LDAP
if user.nil? || user.ldap_user?
# Second chance - try LDAP authentication
- return nil unless ldap_conf.enabled
+ return nil unless Gitlab::LDAP::Config.enabled?
- Gitlab::LDAP::User.authenticate(login, password)
+ Gitlab::LDAP::Authentication.login(login, password)
else
user if user.valid_password?(password)
end
end
-
- def log
- Gitlab::AppLogger
- end
-
- def ldap_conf
- @ldap_conf ||= Gitlab.config.ldap
- end
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index c2f3b851c07..df1461a45c9 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -90,7 +90,7 @@ module Grack
when *Gitlab::GitAccess::PUSH_COMMANDS
if user
# Skip user authorization on upload request.
- # It will be serverd by update hook in repository
+ # It will be done by the pre-receive hook in the repository.
true
else
false
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index f95bbde5b39..ddb1ac61bf5 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -16,7 +16,7 @@ module Gitlab
# add_repository("gitlab/gitlab-ci")
#
def add_repository(name)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "add-project", "#{name}.git"
+ system gitlab_shell_projects_path, 'add-project', "#{name}.git"
end
# Import repository
@@ -27,7 +27,7 @@ module Gitlab
# import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git")
#
def import_repository(name, url)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "import-project", "#{name}.git", url, '240'
+ system gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240'
end
# Move repository
@@ -39,7 +39,7 @@ module Gitlab
# mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git")
#
def mv_repository(path, new_path)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "mv-project", "#{path}.git", "#{new_path}.git"
+ system gitlab_shell_projects_path, 'mv-project', "#{path}.git", "#{new_path}.git"
end
# Update HEAD for repository
@@ -51,7 +51,7 @@ module Gitlab
# update_repository_head("gitlab/gitlab-ci", "3-1-stable")
#
def update_repository_head(path, branch)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "update-head", "#{path}.git", branch
+ system gitlab_shell_projects_path, 'update-head', "#{path}.git", branch
end
# Fork repository to new namespace
@@ -63,7 +63,7 @@ module Gitlab
# fork_repository("gitlab/gitlab-ci", "randx")
#
def fork_repository(path, fork_namespace)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "fork-project", "#{path}.git", fork_namespace
+ system gitlab_shell_projects_path, 'fork-project', "#{path}.git", fork_namespace
end
# Remove repository from file system
@@ -74,7 +74,7 @@ module Gitlab
# remove_repository("gitlab/gitlab-ci")
#
def remove_repository(name)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-project", "#{name}.git"
+ system gitlab_shell_projects_path, 'rm-project', "#{name}.git"
end
# Add repository branch from passed ref
@@ -87,7 +87,7 @@ module Gitlab
# add_branch("gitlab/gitlab-ci", "4-0-stable", "master")
#
def add_branch(path, branch_name, ref)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "create-branch", "#{path}.git", branch_name, ref
+ system gitlab_shell_projects_path, 'create-branch', "#{path}.git", branch_name, ref
end
# Remove repository branch
@@ -99,7 +99,7 @@ module Gitlab
# rm_branch("gitlab/gitlab-ci", "4-0-stable")
#
def rm_branch(path, branch_name)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-branch", "#{path}.git", branch_name
+ system gitlab_shell_projects_path, 'rm-branch', "#{path}.git", branch_name
end
# Add repository tag from passed ref
@@ -129,7 +129,7 @@ module Gitlab
# rm_tag("gitlab/gitlab-ci", "v4.0")
#
def rm_tag(path, tag_name)
- system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-tag", "#{path}.git", tag_name
+ system gitlab_shell_projects_path, 'rm-tag', "#{path}.git", tag_name
end
# Add new key to gitlab-shell
@@ -138,7 +138,7 @@ module Gitlab
# add_key("key-42", "sha-rsa ...")
#
def add_key(key_id, key_content)
- system "#{gitlab_shell_path}/bin/gitlab-keys", "add-key", key_id, key_content
+ system gitlab_shell_keys_path, 'add-key', key_id, key_content
end
# Batch-add keys to authorized_keys
@@ -157,7 +157,7 @@ module Gitlab
# remove_key("key-342", "sha-rsa ...")
#
def remove_key(key_id, key_content)
- system "#{gitlab_shell_path}/bin/gitlab-keys", "rm-key", key_id, key_content
+ system gitlab_shell_keys_path, 'rm-key', key_id, key_content
end
# Remove all ssh keys from gitlab shell
@@ -166,7 +166,7 @@ module Gitlab
# remove_all_keys
#
def remove_all_keys
- system "#{gitlab_shell_path}/bin/gitlab-keys", "clear"
+ system gitlab_shell_keys_path, 'clear'
end
# Add empty directory for storing repositories
@@ -249,5 +249,13 @@ module Gitlab
def exists?(dir_name)
File.exists?(full_path(dir_name))
end
+
+ def gitlab_shell_projects_path
+ File.join(gitlab_shell_path, 'bin', 'gitlab-projects')
+ end
+
+ def gitlab_shell_keys_path
+ File.join(gitlab_shell_path, 'bin', 'gitlab-keys')
+ end
end
end
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 90f1370c209..401e6e047b1 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -6,7 +6,7 @@ module Gitlab
md = ISSUE_CLOSING_REGEX.match(message)
if md
extractor = Gitlab::ReferenceExtractor.new
- extractor.analyze(md[0])
+ extractor.analyze(md[0], project)
extractor.issues_for(project)
else
[]
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
new file mode 100644
index 00000000000..67aca5e36e9
--- /dev/null
+++ b/lib/gitlab/git.rb
@@ -0,0 +1,5 @@
+module Gitlab
+ module Git
+ BLANK_SHA = '0' * 40
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 6247dd59867..129881060d5 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -49,25 +49,7 @@ module Gitlab
# Iterate over all changes to find if user allowed all of them to be applied
changes.each do |change|
- oldrev, newrev, ref = change.split(' ')
-
- action = if project.protected_branch?(branch_name(ref))
- # we dont allow force push to protected branch
- if forced_push?(project, oldrev, newrev)
- :force_push_code_to_protected_branches
- # and we dont allow remove of protected branch
- elsif newrev =~ /0000000/
- :remove_protected_branches
- else
- :push_code_to_protected_branches
- end
- elsif project.repository && project.repository.tag_names.include?(tag_name(ref))
- # Prevent any changes to existing git tag unless user has permissions
- :admin_project
- else
- :push_code
- end
- unless user.can?(action, project)
+ unless change_allowed?(user, project, change)
# If user does not have access to make at least one change - cancel all push
return false
end
@@ -77,10 +59,33 @@ module Gitlab
true
end
+ def change_allowed?(user, project, change)
+ oldrev, newrev, ref = change.split(' ')
+
+ action = if project.protected_branch?(branch_name(ref))
+ # we dont allow force push to protected branch
+ if forced_push?(project, oldrev, newrev)
+ :force_push_code_to_protected_branches
+ # and we dont allow remove of protected branch
+ elsif newrev == Gitlab::Git::BLANK_SHA
+ :remove_protected_branches
+ else
+ :push_code_to_protected_branches
+ end
+ elsif project.repository && project.repository.tag_names.include?(tag_name(ref))
+ # Prevent any changes to existing git tag unless user has permissions
+ :admin_project
+ else
+ :push_code
+ end
+
+ user.can?(action, project)
+ end
+
def forced_push?(project, oldrev, newrev)
return false if project.empty_repo?
- if oldrev !~ /00000000/ && newrev !~ /00000000/
+ if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA
missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read
missed_refs.split("\n").size > 0
else
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
new file mode 100644
index 00000000000..9f0eb3be20f
--- /dev/null
+++ b/lib/gitlab/git_access_wiki.rb
@@ -0,0 +1,7 @@
+module Gitlab
+ class GitAccessWiki < GitAccess
+ def change_allowed?(user, project, change)
+ user.can?(:write_wiki, project)
+ end
+ end
+end
diff --git a/lib/gitlab/git_logger.rb b/lib/gitlab/git_logger.rb
index fbfed205a0f..9e02ccc0f44 100644
--- a/lib/gitlab/git_logger.rb
+++ b/lib/gitlab/git_logger.rb
@@ -1,7 +1,7 @@
module Gitlab
class GitLogger < Gitlab::Logger
- def self.file_name
- 'githost.log'
+ def self.file_name_noext
+ 'githost'
end
def format_message(severity, timestamp, progname, msg)
diff --git a/lib/gitlab/issues_labels.rb b/lib/gitlab/issues_labels.rb
index 0d34976736f..1bec6088292 100644
--- a/lib/gitlab/issues_labels.rb
+++ b/lib/gitlab/issues_labels.rb
@@ -15,7 +15,6 @@ module Gitlab
{ title: "support", color: yellow },
{ title: "discussion", color: blue },
{ title: "suggestion", color: blue },
- { title: "feature", color: green },
{ title: "enhancement", color: green }
]
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index d2235d2e3bc..eb2c4e48ff2 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -1,18 +1,21 @@
+# LDAP authorization model
+#
+# * Check if we are allowed access (not blocked)
+#
module Gitlab
module LDAP
class Access
- attr_reader :adapter
+ attr_reader :adapter, :provider, :user
- def self.open(&block)
- Gitlab::LDAP::Adapter.open do |adapter|
- block.call(self.new(adapter))
+ def self.open(user, &block)
+ Gitlab::LDAP::Adapter.open(user.provider) do |adapter|
+ block.call(self.new(user, adapter))
end
end
def self.allowed?(user)
- self.open do |access|
- if access.allowed?(user)
- # GitLab EE LDAP code goes here
+ self.open(user) do |access|
+ if access.allowed?
user.last_credential_check_at = Time.now
user.save
true
@@ -22,21 +25,30 @@ module Gitlab
end
end
- def initialize(adapter=nil)
+ def initialize(user, adapter=nil)
@adapter = adapter
+ @user = user
+ @provider = user.provider
end
- def allowed?(user)
+ def allowed?
if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
- if Gitlab.config.ldap.active_directory
- !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter)
- end
+ return true unless ldap_config.active_directory
+ !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter)
else
false
end
rescue
false
end
+
+ def adapter
+ @adapter ||= Gitlab::LDAP::Adapter.new(provider)
+ end
+
+ def ldap_config
+ Gitlab::LDAP::Config.new(provider)
+ end
end
end
end
diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb
index 68ac1b22909..256cdb4c2f1 100644
--- a/lib/gitlab/ldap/adapter.rb
+++ b/lib/gitlab/ldap/adapter.rb
@@ -1,55 +1,28 @@
module Gitlab
module LDAP
class Adapter
- attr_reader :ldap
+ attr_reader :provider, :ldap
- def self.open(&block)
- Net::LDAP.open(adapter_options) do |ldap|
- block.call(self.new(ldap))
+ def self.open(provider, &block)
+ Net::LDAP.open(config(provider).adapter_options) do |ldap|
+ block.call(self.new(provider, ldap))
end
end
- def self.config
- Gitlab.config.ldap
+ def self.config(provider)
+ Gitlab::LDAP::Config.new(provider)
end
- def self.adapter_options
- encryption =
- case config['method'].to_s
- when 'ssl'
- :simple_tls
- when 'tls'
- :start_tls
- else
- nil
- end
-
- options = {
- host: config['host'],
- port: config['port'],
- encryption: encryption
- }
-
- auth_options = {
- auth: {
- method: :simple,
- username: config['bind_dn'],
- password: config['password']
- }
- }
-
- if config['password'] || config['bind_dn']
- options.merge!(auth_options)
- end
- options
+ def initialize(provider, ldap=nil)
+ @provider = provider
+ @ldap = ldap || Net::LDAP.new(config.adapter_options)
end
-
- def initialize(ldap=nil)
- @ldap = ldap || Net::LDAP.new(self.class.adapter_options)
+ def config
+ Gitlab::LDAP::Config.new(provider)
end
- def users(field, value)
+ def users(field, value, limit = nil)
if field.to_sym == :dn
options = {
base: value,
@@ -57,13 +30,13 @@ module Gitlab
}
else
options = {
- base: config['base'],
+ base: config.base,
filter: Net::LDAP::Filter.eq(field, value)
}
end
- if config['user_filter'].present?
- user_filter = Net::LDAP::Filter.construct(config['user_filter'])
+ if config.user_filter.present?
+ user_filter = Net::LDAP::Filter.construct(config.user_filter)
options[:filter] = if options[:filter]
Net::LDAP::Filter.join(options[:filter], user_filter)
@@ -72,12 +45,16 @@ module Gitlab
end
end
+ if limit.present?
+ options.merge!(size: limit)
+ end
+
entries = ldap_search(options).select do |entry|
entry.respond_to? config.uid
end
entries.map do |entry|
- Gitlab::LDAP::Person.new(entry)
+ Gitlab::LDAP::Person.new(entry, provider)
end
end
@@ -105,12 +82,6 @@ module Gitlab
results
end
end
-
- private
-
- def config
- @config ||= self.class.config
- end
end
end
end
diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb
new file mode 100644
index 00000000000..8af2c74e959
--- /dev/null
+++ b/lib/gitlab/ldap/authentication.rb
@@ -0,0 +1,71 @@
+# This calls helps to authenticate to LDAP by providing username and password
+#
+# Since multiple LDAP servers are supported, it will loop through all of them
+# until a valid bind is found
+#
+
+module Gitlab
+ module LDAP
+ class Authentication
+ def self.login(login, password)
+ return unless Gitlab::LDAP::Config.enabled?
+ return unless login.present? && password.present?
+
+ auth = nil
+ # loop through providers until valid bind
+ providers.find do |provider|
+ auth = new(provider)
+ auth.login(login, password) # true will exit the loop
+ end
+
+ # If (login, password) was invalid for all providers, the value of auth is now the last
+ # Gitlab::LDAP::Authentication instance we tried.
+ auth.user
+ end
+
+ def self.providers
+ Gitlab::LDAP::Config.providers
+ end
+
+ attr_accessor :provider, :ldap_user
+
+ def initialize(provider)
+ @provider = provider
+ end
+
+ def login(login, password)
+ @ldap_user = adapter.bind_as(
+ filter: user_filter(login),
+ size: 1,
+ password: password
+ )
+ end
+
+ def adapter
+ OmniAuth::LDAP::Adaptor.new(config.options.symbolize_keys)
+ end
+
+ def config
+ Gitlab::LDAP::Config.new(provider)
+ end
+
+ def user_filter(login)
+ filter = Net::LDAP::Filter.eq(config.uid, login)
+
+ # Apply LDAP user filter if present
+ if config.user_filter.present?
+ filter = Net::LDAP::Filter.join(
+ filter,
+ Net::LDAP::Filter.construct(config.user_filter)
+ )
+ end
+ filter
+ end
+
+ def user
+ return nil unless ldap_user
+ Gitlab::LDAP::User.find_by_uid_and_provider(ldap_user.dn, provider)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb
new file mode 100644
index 00000000000..0cb24d0ccc1
--- /dev/null
+++ b/lib/gitlab/ldap/config.rb
@@ -0,0 +1,120 @@
+# Load a specific server configuration
+module Gitlab
+ module LDAP
+ class Config
+ attr_accessor :provider, :options
+
+ def self.enabled?
+ Gitlab.config.ldap.enabled
+ end
+
+ def self.servers
+ Gitlab.config.ldap.servers.values
+ end
+
+ def self.providers
+ servers.map {|server| server['provider_name'] }
+ end
+
+ def self.valid_provider?(provider)
+ providers.include?(provider)
+ end
+
+ def self.invalid_provider(provider)
+ raise "Unknown provider (#{provider}). Available providers: #{providers}"
+ end
+
+ def initialize(provider)
+ if self.class.valid_provider?(provider)
+ @provider = provider
+ elsif provider == 'ldap'
+ @provider = self.class.providers.first
+ else
+ self.class.invalid_provider(provider)
+ end
+ @options = config_for(@provider) # Use @provider, not provider
+ end
+
+ def enabled?
+ base_config.enabled
+ end
+
+ def adapter_options
+ {
+ host: options['host'],
+ port: options['port'],
+ encryption: encryption
+ }.tap do |options|
+ options.merge!(auth_options) if has_auth?
+ end
+ end
+
+ def base
+ options['base']
+ end
+
+ def uid
+ options['uid']
+ end
+
+ def sync_ssh_keys?
+ sync_ssh_keys.present?
+ end
+
+ # The LDAP attribute in which the ssh keys are stored
+ def sync_ssh_keys
+ options['sync_ssh_keys']
+ end
+
+ def user_filter
+ options['user_filter']
+ end
+
+ def group_base
+ options['group_base']
+ end
+
+ def admin_group
+ options['admin_group']
+ end
+
+ def active_directory
+ options['active_directory']
+ end
+
+ protected
+ def base_config
+ Gitlab.config.ldap
+ end
+
+ def config_for(provider)
+ base_config.servers.values.find { |server| server['provider_name'] == provider }
+ end
+
+ def encryption
+ case options['method'].to_s
+ when 'ssl'
+ :simple_tls
+ when 'tls'
+ :start_tls
+ else
+ nil
+ end
+ end
+
+ def auth_options
+ {
+ auth: {
+ method: :simple,
+ username: options['bind_dn'],
+ password: options['password']
+ }
+ }
+ end
+
+ def has_auth?
+ options['password'] || options['bind_dn']
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
index 87c3d711db4..3e0b3e6cbf8 100644
--- a/lib/gitlab/ldap/person.rb
+++ b/lib/gitlab/ldap/person.rb
@@ -6,24 +6,24 @@ module Gitlab
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
- def self.find_by_uid(uid, adapter=nil)
- adapter ||= Gitlab::LDAP::Adapter.new
- adapter.user(config.uid, uid)
+ attr_accessor :entry, :provider
+
+ def self.find_by_uid(uid, adapter)
+ adapter.user(adapter.config.uid, uid)
end
- def self.find_by_dn(dn, adapter=nil)
- adapter ||= Gitlab::LDAP::Adapter.new
+ def self.find_by_dn(dn, adapter)
adapter.user('dn', dn)
end
- def self.disabled_via_active_directory?(dn, adapter=nil)
- adapter ||= Gitlab::LDAP::Adapter.new
+ def self.disabled_via_active_directory?(dn, adapter)
adapter.dn_matches_filter?(dn, AD_USER_DISABLED)
end
- def initialize(entry)
+ def initialize(entry, provider)
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry
+ @provider = provider
end
def name
@@ -38,6 +38,10 @@ module Gitlab
uid
end
+ def email
+ entry.try(:mail)
+ end
+
def dn
entry.dn
end
@@ -48,12 +52,8 @@ module Gitlab
@entry
end
- def adapter
- @adapter ||= Gitlab::LDAP::Adapter.new
- end
-
def config
- @config ||= Gitlab.config.ldap
+ @config ||= Gitlab::LDAP::Config.new(provider)
end
end
end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 25b5a702f9a..3176e9790a7 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -10,77 +10,52 @@ module Gitlab
module LDAP
class User < Gitlab::OAuth::User
class << self
- def find_or_create(auth_hash)
- self.auth_hash = auth_hash
- find(auth_hash) || find_and_connect_by_email(auth_hash) || create(auth_hash)
- end
-
- def find_and_connect_by_email(auth_hash)
- self.auth_hash = auth_hash
- user = model.find_by(email: self.auth_hash.email)
-
- if user
- user.update_attributes(extern_uid: auth_hash.uid, provider: auth_hash.provider)
- Gitlab::AppLogger.info("(LDAP) Updating legacy LDAP user #{self.auth_hash.email} with extern_uid => #{auth_hash.uid}")
- return user
- end
- end
-
- def authenticate(login, password)
- # Check user against LDAP backend if user is not authenticated
- # Only check with valid login and password to prevent anonymous bind results
- return nil unless ldap_conf.enabled && login.present? && password.present?
-
- ldap_user = adapter.bind_as(
- filter: user_filter(login),
- size: 1,
- password: password
- )
-
- find_by_uid(ldap_user.dn) if ldap_user
- end
-
- def adapter
- @adapter ||= OmniAuth::LDAP::Adaptor.new(ldap_conf)
+ def find_by_uid_and_provider(uid, provider)
+ # LDAP distinguished name is case-insensitive
+ ::User.
+ where(provider: [provider, :ldap]).
+ where('lower(extern_uid) = ?', uid.downcase).last
end
+ end
- protected
-
- def find_by_uid_and_provider
- find_by_uid(auth_hash.uid)
- end
+ def initialize(auth_hash)
+ super
+ update_user_attributes
+ end
- def find_by_uid(uid)
- # LDAP distinguished name is case-insensitive
- model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last
- end
+ # instance methods
+ def gl_user
+ @gl_user ||= find_by_uid_and_provider || find_by_email || build_new_user
+ end
- def provider
- 'ldap'
- end
+ def find_by_uid_and_provider
+ self.class.find_by_uid_and_provider(
+ auth_hash.uid.downcase, auth_hash.provider)
+ end
- def raise_error(message)
- raise OmniAuth::Error, "(LDAP) " + message
- end
+ def find_by_email
+ model.find_by(email: auth_hash.email)
+ end
- def ldap_conf
- Gitlab.config.ldap
- end
+ def update_user_attributes
+ gl_user.attributes = {
+ extern_uid: auth_hash.uid,
+ provider: auth_hash.provider,
+ email: auth_hash.email
+ }
+ end
- def user_filter(login)
- filter = Net::LDAP::Filter.eq(adapter.uid, login)
- # Apply LDAP user filter if present
- if ldap_conf['user_filter'].present?
- user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter'])
- filter = Net::LDAP::Filter.join(filter, user_filter)
- end
- filter
- end
+ def changed?
+ gl_user.changed?
end
def needs_blocking?
false
end
+
+ def allowed?
+ Gitlab::LDAP::Access.allowed?(gl_user)
+ end
end
end
end
diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb
index 8a73ec5038a..59b21149a9a 100644
--- a/lib/gitlab/logger.rb
+++ b/lib/gitlab/logger.rb
@@ -1,5 +1,9 @@
module Gitlab
class Logger < ::Logger
+ def self.file_name
+ file_name_noext + '.log'
+ end
+
def self.error(message)
build.error(message)
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index d346acf0d32..068c342398b 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -33,6 +33,11 @@ module Gitlab
attr_reader :html_options
+ def gfm_with_tasks(text, project = @project, html_options = {})
+ text = gfm(text, project, html_options)
+ parse_tasks(text)
+ end
+
# Public: Parse the provided text with GitLab-Flavored Markdown
#
# text - the source text
@@ -65,14 +70,22 @@ module Gitlab
insert_piece($1)
end
- # Context passed to the markdoqwn pipeline
+ # Used markdown pipelines in GitLab:
+ # GitlabEmojiFilter - performs emoji replacement.
+ #
+ # see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
+ filters = [
+ HTML::Pipeline::Gitlab::GitlabEmojiFilter
+ ]
+
markdown_context = {
- asset_root: File.join(root_url,
- Gitlab::Application.config.assets.prefix)
+ asset_root: Gitlab.config.gitlab.url,
+ asset_host: Gitlab::Application.config.asset_host
}
- result = HTML::Pipeline::Gitlab::MarkdownPipeline.call(text,
- markdown_context)
+ markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
+
+ result = markdown_pipeline.call(text, markdown_context)
text = result[:output].to_html(save_with: 0)
allowed_attributes = ActionView::Base.sanitized_allowed_attributes
@@ -108,15 +121,18 @@ module Gitlab
text
end
+ NAME_STR = '[a-zA-Z][a-zA-Z0-9_\-\.]*'
+ PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
+
REFERENCE_PATTERN = %r{
(?<prefix>\W)? # Prefix
( # Reference
- @(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name
+ @(?<user>#{NAME_STR}) # User name
|(?<issue>([A-Z\-]+-)\d+) # JIRA Issue ID
- |\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
- |!(?<merge_request>\d+) # MR ID
+ |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
+ |#{PROJ_STR}?!(?<merge_request>\d+) # MR ID
|\$(?<snippet>\d+) # Snippet ID
- |(?<commit>[\h]{6,40}) # Commit ID
+ |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
|(?<skip>gfm-extraction-[\h]{6,40}) # Skip gfm extractions. Otherwise will be parsed as commit
)
(?<suffix>\W)? # Suffix
@@ -127,82 +143,105 @@ module Gitlab
def parse_references(text, project = @project)
# parse reference links
text.gsub!(REFERENCE_PATTERN) do |match|
- prefix = $~[:prefix]
- suffix = $~[:suffix]
type = TYPES.select{|t| !$~[t].nil?}.first
- if type
- identifier = $~[type]
-
- # Avoid HTML entities
- if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
- match
- elsif ref_link = reference_link(type, identifier, project)
- "#{prefix}#{ref_link}#{suffix}"
- else
- match
- end
- else
- match
+ actual_project = project
+ project_prefix = nil
+ project_path = $LAST_MATCH_INFO[:project]
+ if project_path
+ actual_project = ::Project.find_with_namespace(project_path)
+ project_prefix = project_path
end
+
+ parse_result($LAST_MATCH_INFO, type,
+ actual_project, project_prefix) || match
+ end
+ end
+
+ # Called from #parse_references. Attempts to build a gitlab reference
+ # link. Returns nil if +type+ is nil, if the match string is an HTML
+ # entity, if the reference is invalid, or if the matched text includes an
+ # invalid project path.
+ def parse_result(match_info, type, project, project_prefix)
+ prefix = match_info[:prefix]
+ suffix = match_info[:suffix]
+
+ return nil if html_entity?(prefix, suffix) || type.nil?
+ return nil if project.nil? && !project_prefix.nil?
+
+ identifier = match_info[type]
+ ref_link = reference_link(type, identifier, project, project_prefix)
+
+ if ref_link
+ "#{prefix}#{ref_link}#{suffix}"
+ else
+ nil
end
end
+ # Return true if the +prefix+ and +suffix+ indicate that the matched string
+ # is an HTML entity like &amp;
+ def html_entity?(prefix, suffix)
+ prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
+ end
+
# Private: Dispatches to a dedicated processing method based on reference
#
# reference - Object reference ("@1234", "!567", etc.)
# identifier - Object identifier (Issue ID, SHA hash, etc.)
#
# Returns string rendered by the processing method
- def reference_link(type, identifier, project = @project)
- send("reference_#{type}", identifier, project)
+ def reference_link(type, identifier, project = @project, prefix_text = nil)
+ send("reference_#{type}", identifier, project, prefix_text)
end
- def reference_user(identifier, project = @project)
+ def reference_user(identifier, project = @project, _ = nil)
options = html_options.merge(
class: "gfm gfm-team_member #{html_options[:class]}"
)
if identifier == "all"
link_to("@all", project_url(project), options)
- elsif user = User.find_by(username: identifier)
+ elsif User.find_by(username: identifier)
link_to("@#{identifier}", user_url(identifier), options)
end
end
- def reference_issue(identifier, project = @project)
+ def reference_issue(identifier, project = @project, prefix_text = nil)
if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
if project.issue_exists? identifier
url = url_for_issue(identifier, project)
- title = title_for_issue(identifier)
+ title = title_for_issue(identifier, project)
options = html_options.merge(
title: "Issue: #{title}",
class: "gfm gfm-issue #{html_options[:class]}"
)
- link_to("##{identifier}", url, options)
+ link_to("#{prefix_text}##{identifier}", url, options)
end
else
config = Gitlab.config
external_issue_tracker = config.issues_tracker[project.issues_tracker]
if external_issue_tracker.present?
- reference_external_issue(identifier, external_issue_tracker, project)
+ reference_external_issue(identifier, external_issue_tracker, project,
+ prefix_text)
end
end
end
- def reference_merge_request(identifier, project = @project)
+ def reference_merge_request(identifier, project = @project,
+ prefix_text = nil)
if merge_request = project.merge_requests.find_by(iid: identifier)
options = html_options.merge(
title: "Merge Request: #{merge_request.title}",
class: "gfm gfm-merge_request #{html_options[:class]}"
)
url = project_merge_request_url(project, merge_request)
- link_to("!#{identifier}", url, options)
+ link_to("#{prefix_text}!#{identifier}", url, options)
end
end
- def reference_snippet(identifier, project = @project)
+ def reference_snippet(identifier, project = @project, _ = nil)
if snippet = project.snippets.find_by(id: identifier)
options = html_options.merge(
title: "Snippet: #{snippet.title}",
@@ -213,17 +252,23 @@ module Gitlab
end
end
- def reference_commit(identifier, project = @project)
+ def reference_commit(identifier, project = @project, prefix_text = nil)
if project.valid_repo? && commit = project.repository.commit(identifier)
options = html_options.merge(
title: commit.link_title,
class: "gfm gfm-commit #{html_options[:class]}"
)
- link_to(identifier, project_commit_url(project, commit), options)
+ prefix_text = "#{prefix_text}@" if prefix_text
+ link_to(
+ "#{prefix_text}#{identifier}",
+ project_commit_url(project, commit),
+ options
+ )
end
end
- def reference_external_issue(identifier, issue_tracker, project = @project)
+ def reference_external_issue(identifier, issue_tracker, project = @project,
+ prefix_text = nil)
url = url_for_issue(identifier, project)
title = issue_tracker['title']
@@ -231,7 +276,26 @@ module Gitlab
title: "Issue in #{title}",
class: "gfm gfm-issue #{html_options[:class]}"
)
- link_to("##{identifier}", url, options)
+ link_to("#{prefix_text}##{identifier}", url, options)
+ end
+
+ # Turn list items that start with "[ ]" into HTML checkbox inputs.
+ def parse_tasks(text)
+ li_tag = '<li class="task-list-item">'
+ unchecked_box = '<input type="checkbox" value="on" disabled />'
+ checked_box = unchecked_box.sub(/\/>$/, 'checked="checked" />')
+
+ # Regexp captures don't seem to work when +text+ is an
+ # ActiveSupport::SafeBuffer, hence the `String.new`
+ String.new(text).gsub(Taskable::TASK_PATTERN_HTML) do
+ checked = $LAST_MATCH_INFO[:checked].downcase == 'x'
+
+ if checked
+ "#{li_tag}#{checked_box}"
+ else
+ "#{li_tag}#{unchecked_box}"
+ end
+ end
end
end
end
diff --git a/lib/gitlab/markdown_helper.rb b/lib/gitlab/markdown_helper.rb
index abed12fe570..5e3cfc0585b 100644
--- a/lib/gitlab/markdown_helper.rb
+++ b/lib/gitlab/markdown_helper.rb
@@ -21,5 +21,9 @@ module Gitlab
def gitlab_markdown?(filename)
filename.downcase.end_with?(*%w(.mdown .md .markdown))
end
+
+ def previewable?(filename)
+ gitlab_markdown?(filename) || markup?(filename)
+ end
end
end
diff --git a/lib/gitlab/oauth/auth_hash.rb b/lib/gitlab/oauth/auth_hash.rb
index 0198f61f427..ce52beec78e 100644
--- a/lib/gitlab/oauth/auth_hash.rb
+++ b/lib/gitlab/oauth/auth_hash.rb
@@ -21,7 +21,7 @@ module Gitlab
end
def name
- (info.name || full_name).to_s.force_encoding('utf-8')
+ (info.try(:name) || full_name).to_s.force_encoding('utf-8')
end
def full_name
diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb
index b768eda185f..47f62153a50 100644
--- a/lib/gitlab/oauth/user.rb
+++ b/lib/gitlab/oauth/user.rb
@@ -6,55 +6,77 @@
module Gitlab
module OAuth
class User
- class << self
- attr_reader :auth_hash
+ attr_accessor :auth_hash, :gl_user
- def find(auth_hash)
- self.auth_hash = auth_hash
- find_by_uid_and_provider
- end
+ def initialize(auth_hash)
+ self.auth_hash = auth_hash
+ end
- def create(auth_hash)
- user = new(auth_hash)
- user.save_and_trigger_callbacks
- end
+ def persisted?
+ gl_user.try(:persisted?)
+ end
- def model
- ::User
- end
+ def new?
+ !persisted?
+ end
+
+ def valid?
+ gl_user.try(:valid?)
+ end
+
+ def save
+ unauthorized_to_create unless gl_user
- def auth_hash=(auth_hash)
- @auth_hash = AuthHash.new(auth_hash)
+ if needs_blocking?
+ gl_user.save!
+ gl_user.block
+ else
+ gl_user.save!
end
- protected
- def find_by_uid_and_provider
- model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last
+ log.info "(OAuth) saving user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}"
+ gl_user
+ rescue ActiveRecord::RecordInvalid => e
+ log.info "(OAuth) Error saving user: #{gl_user.errors.full_messages}"
+ return self, e.record.errors
+ end
+
+ def gl_user
+ @user ||= find_by_uid_and_provider
+
+ if signup_enabled?
+ @user ||= build_new_user
end
+
+ @user
end
- # Instance methods
- attr_accessor :auth_hash, :user
+ protected
- def initialize(auth_hash)
- self.auth_hash = auth_hash
- self.user = self.class.model.new(user_attributes)
- user.skip_confirmation!
+ def needs_blocking?
+ new? && block_after_signup?
+ end
+
+ def signup_enabled?
+ Gitlab.config.omniauth.allow_single_sign_on
+ end
+
+ def block_after_signup?
+ Gitlab.config.omniauth.block_auto_created_users
end
def auth_hash=(auth_hash)
@auth_hash = AuthHash.new(auth_hash)
end
- def save_and_trigger_callbacks
- user.save!
- log.info "(OAuth) Creating user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}"
- user.block if needs_blocking?
+ def find_by_uid_and_provider
+ model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last
+ end
- user
- rescue ActiveRecord::RecordInvalid => e
- log.info "(OAuth) Email #{e.record.errors[:email]}. Username #{e.record.errors[:username]}"
- return nil, e.record.errors
+ def build_new_user
+ model.new(user_attributes).tap do |user|
+ user.skip_confirmation!
+ end
end
def user_attributes
@@ -73,12 +95,12 @@ module Gitlab
Gitlab::AppLogger
end
- def raise_error(message)
- raise OmniAuth::Error, "(OAuth) " + message
+ def model
+ ::User
end
- def needs_blocking?
- Gitlab.config.omniauth['block_auto_created_users']
+ def raise_unauthorized_to_create
+ raise StandardError.new("Unauthorized to create user, signup disabled for #{auth_hash.provider}")
end
end
end
diff --git a/lib/gitlab/production_logger.rb b/lib/gitlab/production_logger.rb
new file mode 100644
index 00000000000..89ce7144b1b
--- /dev/null
+++ b/lib/gitlab/production_logger.rb
@@ -0,0 +1,7 @@
+module Gitlab
+ class ProductionLogger < Gitlab::Logger
+ def self.file_name_noext
+ 'production'
+ end
+ end
+end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 73b19ad55d5..99165950aef 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -9,51 +9,63 @@ module Gitlab
@users, @issues, @merge_requests, @snippets, @commits = [], [], [], [], []
end
- def analyze(string)
- parse_references(string.dup)
+ def analyze(string, project)
+ parse_references(string.dup, project)
end
# Given a valid project, resolve the extracted identifiers of the requested type to
# model objects.
def users_for(project)
- users.map do |identifier|
- project.users.where(username: identifier).first
+ users.map do |entry|
+ project.users.where(username: entry[:id]).first
end.reject(&:nil?)
end
- def issues_for(project)
- issues.map do |identifier|
- project.issues.where(iid: identifier).first
+ def issues_for(project = nil)
+ issues.map do |entry|
+ if should_lookup?(project, entry[:project])
+ entry[:project].issues.where(iid: entry[:id]).first
+ end
end.reject(&:nil?)
end
- def merge_requests_for(project)
- merge_requests.map do |identifier|
- project.merge_requests.where(iid: identifier).first
+ def merge_requests_for(project = nil)
+ merge_requests.map do |entry|
+ if should_lookup?(project, entry[:project])
+ entry[:project].merge_requests.where(iid: entry[:id]).first
+ end
end.reject(&:nil?)
end
def snippets_for(project)
- snippets.map do |identifier|
- project.snippets.where(id: identifier).first
+ snippets.map do |entry|
+ project.snippets.where(id: entry[:id]).first
end.reject(&:nil?)
end
- def commits_for(project)
- repo = project.repository
- return [] if repo.nil?
-
- commits.map do |identifier|
- repo.commit(identifier)
+ def commits_for(project = nil)
+ commits.map do |entry|
+ repo = entry[:project].repository if entry[:project]
+ if should_lookup?(project, entry[:project])
+ repo.commit(entry[:id]) if repo
+ end
end.reject(&:nil?)
end
private
- def reference_link(type, identifier, project)
+ def reference_link(type, identifier, project, _)
# Append identifier to the appropriate collection.
- send("#{type}s") << identifier
+ send("#{type}s") << { project: project, id: identifier }
+ end
+
+ def should_lookup?(project, entry_project)
+ if entry_project.nil?
+ false
+ else
+ project.nil? || project.id == entry_project.id
+ end
end
end
end
diff --git a/lib/gitlab/sidekiq_logger.rb b/lib/gitlab/sidekiq_logger.rb
new file mode 100644
index 00000000000..c1dab87a432
--- /dev/null
+++ b/lib/gitlab/sidekiq_logger.rb
@@ -0,0 +1,7 @@
+module Gitlab
+ class SidekiqLogger < Gitlab::Logger
+ def self.file_name_noext
+ 'sidekiq'
+ end
+ end
+end
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index de7e0404086..877488d8471 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -19,7 +19,7 @@ module Gitlab
issue = Issue.find(id)
project_issue_url(id: issue.iid,
project_id: issue.project,
- host: Settings.gitlab['url'])
+ host: Gitlab.config.gitlab['url'])
end
end
end
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index bb225f1acd8..54d740908d5 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -10,6 +10,17 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
super options
end
+ # If project has issue number 39, apostrophe will be linked in
+ # regular text to the issue as Redcarpet will convert apostrophe to
+ # #39;
+ # We replace apostrophe with right single quote before Redcarpet
+ # does the processing and put the apostrophe back in postprocessing.
+ # This only influences regular text, code blocks are untouched.
+ def normal_text(text)
+ return text unless text.present?
+ text.gsub("'", "&rsquo;")
+ end
+
def block_code(code, language)
# New lines are placed to fix an rendering issue
# with code wrapped inside <h1> tag for next case:
@@ -44,9 +55,14 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end
def postprocess(full_document)
+ full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document)
end
- h.gfm(full_document)
+ if @options[:parse_tasks]
+ h.gfm_with_tasks(full_document)
+ else
+ h.gfm(full_document)
+ end
end
end
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 5f1afe6575c..cbb198086b5 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -19,7 +19,7 @@
## - installing an old version of Nginx with the chunkin module [2] compiled in, or
## - using a newer version of Nginx.
##
-## At the time of writing we do not know if either of these theoretical solutions works.
+## At the time of writing we do not know if either of these theoretical solutions works.
## As a workaround users can use Git over SSH to push large files.
##
## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
@@ -42,7 +42,7 @@ server {
listen *:80 default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
-
+
## Redirects all traffic to the HTTPS host
root /nowhere; ## root doesn't have to be a valid path since we are redirecting
rewrite ^ https://$server_name$request_uri? permanent;
@@ -60,19 +60,18 @@ server {
client_max_body_size 20m;
## Strong SSL Security
- ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
+ ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
ssl on;
ssl_certificate /etc/nginx/ssl/gitlab.crt;
ssl_certificate_key /etc/nginx/ssl/gitlab.key;
- ssl_ciphers 'AES256+EECDH:AES256+EDH';
-
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_session_cache builtin:1000 shared:SSL:10m;
-
- ssl_prefer_server_ciphers on;
+ # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
+ ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_prefer_server_ciphers on;
+ ssl_session_cache shared:SSL:10m;
- ## [WARNING] The following header states that the browser should only communicate
+ ## [WARNING] The following header states that the browser should only communicate
## with your server over a secure connection for the next 24 months.
add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Options SAMEORIGIN;
@@ -87,11 +86,10 @@ server {
# ssl_stapling_verify on;
# ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt;
# resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired
- # resolver_timeout 10s;
+ # resolver_timeout 5s;
## [Optional] Generate a stronger DHE parameter:
- ## cd /etc/ssl/certs
- ## sudo openssl dhparam -out dhparam.pem 4096
+ ## sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
##
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 9ec368254ac..56e8ff44988 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -664,7 +664,7 @@ namespace :gitlab do
warn_user_is_not_gitlab
start_checking "LDAP"
- if ldap_config.enabled
+ if Gitlab::LDAP::Config.enabled?
print_users(args.limit)
else
puts 'LDAP is disabled in config/gitlab.yml'
@@ -675,39 +675,19 @@ namespace :gitlab do
def print_users(limit)
puts "LDAP users with access to your GitLab server (only showing the first #{limit} results)"
- ldap.search(attributes: attributes, filter: filter, size: limit, return_result: false) do |entry|
- puts "DN: #{entry.dn}\t#{ldap_config.uid}: #{entry[ldap_config.uid]}"
- end
- end
-
- def attributes
- [ldap_config.uid]
- end
- def filter
- uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
- if user_filter
- Net::LDAP::Filter.join(uid_filter, user_filter)
- else
- uid_filter
- end
- end
+ servers = Gitlab::LDAP::Config.providers
- def user_filter
- if ldap_config['user_filter'] && ldap_config.user_filter.present?
- Net::LDAP::Filter.construct(ldap_config.user_filter)
- else
- nil
+ servers.each do |server|
+ puts "Server: #{server}"
+ Gitlab::LDAP::Adapter.open(server) do |adapter|
+ users = adapter.users(adapter.config.uid, '*', 100)
+ users.each do |user|
+ puts "\tDN: #{user.dn}\t #{adapter.config.uid}: #{user.uid}"
+ end
+ end
end
end
-
- def ldap
- @ldap ||= OmniAuth::LDAP::Adaptor.new(ldap_config).connection
- end
-
- def ldap_config
- @ldap_config ||= Gitlab.config.ldap
- end
end
# Helper methods
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index cbfa736c84c..3c693546c09 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -15,23 +15,17 @@ namespace :gitlab do
git_base_path = Gitlab.config.gitlab_shell.repos_path
repos_to_import = Dir.glob(git_base_path + '/**/*.git')
- namespaces = Namespace.pluck(:path)
-
repos_to_import.each do |repo_path|
# strip repo base path
repo_path[0..git_base_path.length] = ''
path = repo_path.sub(/\.git$/, '')
- name = File.basename path
- group_name = File.dirname path
+ group_name, name = File.split(path)
group_name = nil if group_name == '.'
- # Skip if group or user
- next if namespaces.include?(name)
-
puts "Processing #{repo_path}".yellow
- if path =~ /.wiki\Z/
+ if path =~ /\.wiki\Z/
puts " * Skipping wiki repo"
next
end
@@ -50,9 +44,9 @@ namespace :gitlab do
# find group namespace
if group_name
- group = Group.find_by(path: group_name)
+ group = Namespace.find_by(path: group_name)
# create group namespace
- if !group
+ unless group
group = Group.new(:name => group_name)
group.path = group_name
group.owner = user
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index a8f26a7c029..55f338add6a 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -7,17 +7,17 @@ namespace :gitlab do
default_version = File.read(File.join(Rails.root, "GITLAB_SHELL_VERSION")).strip
args.with_defaults(tag: 'v' + default_version, repo: "https://gitlab.com/gitlab-org/gitlab-shell.git")
- user = Settings.gitlab.user
- home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Settings.gitlab.user_home
- gitlab_url = Settings.gitlab.url
+ user = Gitlab.config.gitlab.user
+ home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Gitlab.config.gitlab.user_home
+ gitlab_url = Gitlab.config.gitlab.url
# gitlab-shell requires a / at the end of the url
- gitlab_url += "/" unless gitlab_url.match(/\/$/)
+ gitlab_url += '/' unless gitlab_url.end_with?('/')
repos_path = Gitlab.config.gitlab_shell.repos_path
target_dir = Gitlab.config.gitlab_shell.path
# Clone if needed
unless File.directory?(target_dir)
- sh "git clone '#{args.repo}' '#{target_dir}'"
+ sh(*%W(git clone #{args.repo} #{target_dir}))
end
# Make sure we're on the right tag