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/entities.rb46
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/internal.rb54
-rw-r--r--lib/api/merge_requests.rb6
-rw-r--r--lib/api/project_hooks.rb9
-rw-r--r--lib/api/project_members.rb114
-rw-r--r--lib/api/projects.rb118
-rw-r--r--lib/gitlab/backend/grack_auth.rb106
-rw-r--r--lib/gitlab/backend/grack_helpers.rb28
-rw-r--r--lib/gitlab/backend/shell.rb16
-rw-r--r--lib/gitlab/git_access.rb74
-rw-r--r--lib/gitlab/ldap/access.rb23
-rw-r--r--lib/gitlab/ldap/adapter.rb86
-rw-r--r--lib/gitlab/ldap/person.rb50
-rw-r--r--lib/gitlab/ldap/user.rb32
-rw-r--r--lib/gitlab/markdown.rb4
-rw-r--r--lib/gitlab/regex.rb4
-rw-r--r--lib/gitlab/satellite/satellite.rb17
-rw-r--r--lib/gitlab/seeder.rb19
-rw-r--r--lib/gitlab/upgrader.rb1
-rw-r--r--lib/redcarpet/render/gitlab_html.rb6
-rwxr-xr-xlib/support/init.d/gitlab2
-rw-r--r--lib/support/logrotate/gitlab10
-rw-r--r--lib/support/nginx/gitlab8
-rw-r--r--lib/tasks/dev.rake6
-rw-r--r--lib/tasks/gitlab/check.rake17
-rw-r--r--lib/tasks/gitlab/setup.rake8
-rw-r--r--lib/tasks/gitlab/shell.rake12
-rw-r--r--lib/tasks/gitlab/task_helpers.rake2
-rw-r--r--lib/tasks/gitlab/test.rake4
-rw-r--r--lib/tasks/migrate/add_limits_mysql.rake14
-rw-r--r--lib/tasks/spec.rake14
-rw-r--r--lib/tasks/spinach.rake14
-rw-r--r--lib/tasks/test.rake6
35 files changed, 632 insertions, 301 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 6bec8368b12..7c4cdad7f0d 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -38,6 +38,7 @@ module API
mount Internal
mount SystemHooks
mount ProjectSnippets
+ mount ProjectMembers
mount DeployKeys
mount ProjectHooks
mount Services
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 8557fa074d4..9fa8506926c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -15,7 +15,7 @@ module API
end
class UserSafe < Grape::Entity
- expose :name
+ expose :name, :username
end
class UserBasic < Grape::Entity
@@ -44,7 +44,7 @@ module API
expose :id, :description, :default_branch
expose :public?, as: :public
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
- expose :owner, using: Entities::UserBasic
+ expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace
expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
@@ -58,18 +58,6 @@ module API
end
end
- class TeamMember < UserBasic
- expose :permission, as: :access_level do |user, options|
- options[:user_team].user_team_user_relationships.find_by(user_id: user.id).permission
- end
- end
-
- class TeamProject < Project
- expose :greatest_access, as: :greatest_access_level do |project, options|
- options[:user_team].user_team_project_relationships.find_by(project_id: project.id).greatest_access
- end
- end
-
class Group < Grape::Entity
expose :id, :name, :path, :owner_id
end
@@ -144,7 +132,7 @@ module API
end
class MergeRequest < ProjectEntity
- expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes
+ expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes, :description
expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id
end
@@ -175,5 +163,33 @@ module API
class Namespace < Grape::Entity
expose :id, :path, :kind
end
+
+ class ProjectAccess < Grape::Entity
+ expose :project_access, as: :access_level
+ expose :notification_level
+ end
+
+ class GroupAccess < Grape::Entity
+ expose :group_access, as: :access_level
+ expose :notification_level
+ end
+
+ class ProjectWithAccess < Project
+ expose :permissions do
+ expose :project_access, using: Entities::ProjectAccess do |project, options|
+ project.users_projects.find_by(user_id: options[:user].id)
+ end
+
+ expose :group_access, using: Entities::GroupAccess do |project, options|
+ if project.group
+ project.group.users_groups.find_by(user_id: options[:user].id)
+ end
+ end
+ end
+ end
+
+ class Label < Grape::Entity
+ expose :name
+ end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f8c48e2f3b2..fc309f65a56 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -47,7 +47,7 @@ module API
end
def find_project(id)
- project = Project.find_by(id: id) || Project.find_with_namespace(id)
+ project = Project.find_with_namespace(id) || Project.find_by(id: id)
if project && can?(current_user, :read_project, project)
project
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index ebc9fef07b4..bcf97574673 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -1,16 +1,12 @@
module API
# Internal access API
class Internal < Grape::API
-
- DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
- PUSH_COMMANDS = %w{ git-receive-pack }
-
namespace 'internal' do
- #
- # Check if ssh key has access to project code
+ # Check if git command is allowed to project
#
# Params:
- # key_id - SSH Key id
+ # key_id - ssh key id for Git over SSH
+ # user_id - user id for Git over HTTP
# project - project path with namespace
# action - git action (git-upload-pack or git-receive-pack)
# ref - branch name
@@ -22,37 +18,25 @@ module API
# the wiki repository as well.
project_path = params[:project]
project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
-
- key = Key.find(params[:key_id])
project = Project.find_with_namespace(project_path)
- git_cmd = params[:action]
return false unless project
-
- if key.is_a? DeployKey
- key.projects.include?(project) && DOWNLOAD_COMMANDS.include?(git_cmd)
- else
- user = key.user
-
- return false if user.blocked?
- if Gitlab.config.ldap.enabled
- return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid)
- end
-
- action = case git_cmd
- when *DOWNLOAD_COMMANDS
- then :download_code
- when *PUSH_COMMANDS
- then
- if project.protected_branch?(params[:ref])
- :push_code_to_protected_branches
- else
- :push_code
- end
- end
-
- user.can?(action, project)
- end
+ actor = if params[:key_id]
+ Key.find(params[:key_id])
+ elsif params[:user_id]
+ User.find(params[:user_id])
+ end
+
+ return false unless actor
+
+ Gitlab::GitAccess.new.allowed?(
+ actor,
+ params[:action],
+ project,
+ params[:ref],
+ params[:oldrev],
+ params[:newrev]
+ )
end
#
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 58d2f79faff..e2458198411 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -64,6 +64,7 @@ module API
# target_project - The target project of the merge request defaults to the :id of the project
# assignee_id - Assignee user ID
# title (required) - Title of MR
+ # description - Description of MR
#
# Example:
# POST /projects/:id/merge_requests
@@ -72,7 +73,7 @@ module API
set_current_user_for_thread do
authorize! :write_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title]
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
+ attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
merge_request = user_project.merge_requests.new(attrs)
merge_request.author = current_user
merge_request.source_project = user_project
@@ -105,12 +106,13 @@ module API
# assignee_id - Assignee user ID
# title - Title of MR
# state_event - Status of MR. (close|reopen|merge)
+ # description - Description of MR
# Example:
# PUT /projects/:id/merge_request/:merge_request_id
#
put ":id/merge_request/:merge_request_id" do
set_current_user_for_thread do
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
+ attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :modify_merge_request, merge_request
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index c271dd8b61b..79c3d122d32 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -5,15 +5,6 @@ module API
before { authorize_admin_project }
resource :projects do
- helpers do
- def handle_project_member_errors(errors)
- if errors[:project_access].any?
- error!(errors[:project_access], 422)
- end
- not_found!
- end
- end
-
# Get project hooks
#
# Parameters:
diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb
new file mode 100644
index 00000000000..47c4ddce163
--- /dev/null
+++ b/lib/api/project_members.rb
@@ -0,0 +1,114 @@
+module API
+ # Projects members API
+ class ProjectMembers < Grape::API
+ before { authenticate! }
+
+ resource :projects do
+ helpers do
+ def handle_project_member_errors(errors)
+ if errors[:project_access].any?
+ error!(errors[:project_access], 422)
+ end
+ not_found!
+ end
+ end
+
+ # Get a project team members
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # query - Query string
+ # Example Request:
+ # GET /projects/:id/members
+ get ":id/members" do
+ if params[:query].present?
+ @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
+ else
+ @members = paginate user_project.users
+ end
+ present @members, with: Entities::ProjectMember, project: user_project
+ end
+
+ # Get a project team members
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # user_id (required) - The ID of a user
+ # Example Request:
+ # GET /projects/:id/members/:user_id
+ get ":id/members/:user_id" do
+ @member = user_project.users.find params[:user_id]
+ present @member, with: Entities::ProjectMember, project: user_project
+ end
+
+ # Add a new project team member
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # user_id (required) - The ID of a user
+ # access_level (required) - Project access level
+ # Example Request:
+ # POST /projects/:id/members
+ post ":id/members" do
+ authorize! :admin_project, user_project
+ required_attributes! [:user_id, :access_level]
+
+ # either the user is already a team member or a new one
+ team_member = user_project.team_member_by_id(params[:user_id])
+ if team_member.nil?
+ team_member = user_project.users_projects.new(
+ user_id: params[:user_id],
+ project_access: params[:access_level]
+ )
+ end
+
+ if team_member.save
+ @member = team_member.user
+ present @member, with: Entities::ProjectMember, project: user_project
+ else
+ handle_project_member_errors team_member.errors
+ end
+ end
+
+ # Update project team member
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # user_id (required) - The ID of a team member
+ # access_level (required) - Project access level
+ # Example Request:
+ # PUT /projects/:id/members/:user_id
+ put ":id/members/:user_id" do
+ authorize! :admin_project, user_project
+ required_attributes! [:access_level]
+
+ team_member = user_project.users_projects.find_by(user_id: params[:user_id])
+ not_found!("User can not be found") if team_member.nil?
+
+ if team_member.update_attributes(project_access: params[:access_level])
+ @member = team_member.user
+ present @member, with: Entities::ProjectMember, project: user_project
+ else
+ handle_project_member_errors team_member.errors
+ end
+ end
+
+ # Remove a team member from project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # user_id (required) - The ID of a team member
+ # Example Request:
+ # DELETE /projects/:id/members/:user_id
+ delete ":id/members/:user_id" do
+ authorize! :admin_project, user_project
+ team_member = user_project.users_projects.find_by(user_id: params[:user_id])
+ unless team_member.nil?
+ team_member.destroy
+ else
+ {message: "Access revoked", id: params[:user_id].to_i}
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index bcca69ff49a..9d290c75ba9 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -5,13 +5,6 @@ module API
resource :projects do
helpers do
- def handle_project_member_errors(errors)
- if errors[:project_access].any?
- error!(errors[:project_access], 422)
- end
- not_found!
- end
-
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
@@ -55,7 +48,7 @@ module API
# Example Request:
# GET /projects/:id
get ":id" do
- present user_project, with: Entities::Project
+ present user_project, with: Entities::ProjectWithAccess, user: current_user
end
# Get a single project events
@@ -196,104 +189,6 @@ module API
user_project.forked_project_link.destroy
end
end
-
- # Get a project team members
- #
- # Parameters:
- # id (required) - The ID of a project
- # query - Query string
- # Example Request:
- # GET /projects/:id/members
- get ":id/members" do
- if params[:query].present?
- @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
- else
- @members = paginate user_project.users
- end
- present @members, with: Entities::ProjectMember, project: user_project
- end
-
- # Get a project team members
- #
- # Parameters:
- # id (required) - The ID of a project
- # user_id (required) - The ID of a user
- # Example Request:
- # GET /projects/:id/members/:user_id
- get ":id/members/:user_id" do
- @member = user_project.users.find params[:user_id]
- present @member, with: Entities::ProjectMember, project: user_project
- end
-
- # Add a new project team member
- #
- # Parameters:
- # id (required) - The ID of a project
- # user_id (required) - The ID of a user
- # access_level (required) - Project access level
- # Example Request:
- # POST /projects/:id/members
- post ":id/members" do
- authorize! :admin_project, user_project
- required_attributes! [:user_id, :access_level]
-
- # either the user is already a team member or a new one
- team_member = user_project.team_member_by_id(params[:user_id])
- if team_member.nil?
- team_member = user_project.users_projects.new(
- user_id: params[:user_id],
- project_access: params[:access_level]
- )
- end
-
- if team_member.save
- @member = team_member.user
- present @member, with: Entities::ProjectMember, project: user_project
- else
- handle_project_member_errors team_member.errors
- end
- end
-
- # Update project team member
- #
- # Parameters:
- # id (required) - The ID of a project
- # user_id (required) - The ID of a team member
- # access_level (required) - Project access level
- # Example Request:
- # PUT /projects/:id/members/:user_id
- put ":id/members/:user_id" do
- authorize! :admin_project, user_project
- required_attributes! [:access_level]
-
- team_member = user_project.users_projects.find_by(user_id: params[:user_id])
- not_found!("User can not be found") if team_member.nil?
-
- if team_member.update_attributes(project_access: params[:access_level])
- @member = team_member.user
- present @member, with: Entities::ProjectMember, project: user_project
- else
- handle_project_member_errors team_member.errors
- end
- end
-
- # Remove a team member from project
- #
- # Parameters:
- # id (required) - The ID of a project
- # user_id (required) - The ID of a team member
- # Example Request:
- # DELETE /projects/:id/members/:user_id
- delete ":id/members/:user_id" do
- authorize! :admin_project, user_project
- team_member = user_project.users_projects.find_by(user_id: params[:user_id])
- unless team_member.nil?
- team_member.destroy
- else
- {message: "Access revoked", id: params[:user_id].to_i}
- end
- end
-
# search for projects current_user has access to
#
# Parameters:
@@ -320,6 +215,17 @@ module API
@users = paginate @users
present @users, with: Entities::User
end
+
+ # Get a project labels
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # GET /projects/:id/labels
+ get ':id/labels' do
+ @labels = user_project.issues_labels
+ present @labels, with: Entities::Label
+ end
end
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 60c03ce1c04..c2f3b851c07 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -1,11 +1,9 @@
require_relative 'shell_env'
-require_relative 'grack_helpers'
module Grack
class Auth < Rack::Auth::Basic
- include Helpers
- attr_accessor :user, :project, :ref, :env
+ attr_accessor :user, :project, :env
def call(env)
@env = env
@@ -24,14 +22,16 @@ module Grack
@env['SCRIPT_NAME'] = ""
- auth!
+ if project
+ auth!
+ else
+ render_not_found
+ end
end
private
def auth!
- return render_not_found unless project
-
if @auth.provided?
return bad_request unless @auth.basic?
@@ -40,12 +40,8 @@ module Grack
# Allow authentication for GitLab CI service
# if valid token passed
- if login == "gitlab-ci-token" && project.gitlab_ci?
- token = project.gitlab_ci_service.token
-
- if token.present? && token == password && service_name == 'git-upload-pack'
- return @app.call(env)
- end
+ if gitlab_ci_request?(login, password)
+ return @app.call(env)
end
@user = authenticate_user(login, password)
@@ -53,23 +49,26 @@ module Grack
if @user
Gitlab::ShellEnv.set_env(@user)
@env['REMOTE_USER'] = @auth.username
- else
- return unauthorized
end
-
- else
- return unauthorized unless project.public?
end
- if authorized_git_request?
+ if authorized_request?
@app.call(env)
else
unauthorized
end
end
- def authorized_git_request?
- authorize_request(service_name)
+ def gitlab_ci_request?(login, password)
+ if login == "gitlab-ci-token" && project.gitlab_ci?
+ token = project.gitlab_ci_service.token
+
+ if token.present? && token == password && git_cmd == 'git-upload-pack'
+ return true
+ end
+ end
+
+ false
end
def authenticate_user(login, password)
@@ -77,31 +76,31 @@ module Grack
auth.find(login, password)
end
- def authorize_request(service)
- case service
- when 'git-upload-pack'
- can?(user, :download_code, project)
- when'git-receive-pack'
- refs.each do |ref|
- action = if project.protected_branch?(ref)
- :push_code_to_protected_branches
- else
- :push_code
- end
-
- return false unless can?(user, action, project)
+ def authorized_request?
+ case git_cmd
+ when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
+ if user
+ Gitlab::GitAccess.new.download_allowed?(user, project)
+ elsif project.public?
+ # Allow clone/fetch for public projects
+ true
+ else
+ false
+ end
+ when *Gitlab::GitAccess::PUSH_COMMANDS
+ if user
+ # Skip user authorization on upload request.
+ # It will be serverd by update hook in repository
+ true
+ else
+ false
end
-
- # Never let git-receive-pack trough unauthenticated; it's
- # harmless but git < 1.8 doesn't like it
- return false if user.nil?
- true
else
false
end
end
- def service_name
+ def git_cmd
if @request.get?
@request.params['service']
elsif @request.post?
@@ -115,28 +114,17 @@ module Grack
@project ||= project_by_path(@request.path_info)
end
- def refs
- @refs ||= parse_refs
- end
-
- def parse_refs
- input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
- Zlib::GzipReader.new(@request.body).read
- else
- @request.body.read
- end
-
- # Need to reset seek point
- @request.body.rewind
-
- # Parse refs
- refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
+ def project_by_path(path)
+ if m = /^([\w\.\/-]+)\.git/.match(path).to_a
+ path_with_namespace = m.last
+ path_with_namespace.gsub!(/\.wiki$/, '')
- # Cleanup grabare from refs
- # if push to multiple branches
- refs.map do |ref|
- ref.gsub(/00.*/, "")
+ Project.find_with_namespace(path_with_namespace)
end
end
+
+ def render_not_found
+ [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
+ end
end
end
diff --git a/lib/gitlab/backend/grack_helpers.rb b/lib/gitlab/backend/grack_helpers.rb
deleted file mode 100644
index cb747fe0137..00000000000
--- a/lib/gitlab/backend/grack_helpers.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module Grack
- module Helpers
- def project_by_path(path)
- if m = /^([\w\.\/-]+)\.git/.match(path).to_a
- path_with_namespace = m.last
- path_with_namespace.gsub!(/\.wiki$/, '')
-
- Project.find_with_namespace(path_with_namespace)
- end
- end
-
- def render_not_found
- [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
- end
-
- def can?(object, action, subject)
- abilities.allowed?(object, action, subject)
- end
-
- def abilities
- @abilities ||= begin
- abilities = Six.new
- abilities << Ability
- abilities
- end
- end
- end
-end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 7121c8e40d2..b93800e235f 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -2,6 +2,12 @@ module Gitlab
class Shell
class AccessDenied < StandardError; end
+ class KeyAdder < Struct.new(:io)
+ def add_key(id, key)
+ io.puts("#{id}\t#{key.strip}")
+ end
+ end
+
# Init new repository
#
# name - project path with namespace
@@ -130,6 +136,16 @@ module Gitlab
system "#{gitlab_shell_path}/bin/gitlab-keys", "add-key", key_id, key_content
end
+ # Batch-add keys to authorized_keys
+ #
+ # Ex.
+ # batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") }
+ def batch_add_keys(&block)
+ IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io|
+ block.call(KeyAdder.new(io))
+ end
+ end
+
# Remove ssh key from gitlab shell
#
# Ex.
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
new file mode 100644
index 00000000000..1ab8f9213a3
--- /dev/null
+++ b/lib/gitlab/git_access.rb
@@ -0,0 +1,74 @@
+module Gitlab
+ class GitAccess
+ DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
+ PUSH_COMMANDS = %w{ git-receive-pack }
+
+ attr_reader :params, :project, :git_cmd, :user
+
+ def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil)
+ case cmd
+ when *DOWNLOAD_COMMANDS
+ if actor.is_a? User
+ download_allowed?(actor, project)
+ elsif actor.is_a? DeployKey
+ actor.projects.include?(project)
+ elsif actor.is_a? Key
+ download_allowed?(actor.user, project)
+ else
+ raise 'Wrong actor'
+ end
+ when *PUSH_COMMANDS
+ if actor.is_a? User
+ push_allowed?(actor, project, ref, oldrev, newrev)
+ elsif actor.is_a? DeployKey
+ # Deploy key not allowed to push
+ return false
+ elsif actor.is_a? Key
+ push_allowed?(actor.user, project, ref, oldrev, newrev)
+ else
+ raise 'Wrong actor'
+ end
+ else
+ false
+ end
+ end
+
+ def download_allowed?(user, project)
+ if user && user_allowed?(user)
+ user.can?(:download_code, project)
+ else
+ false
+ end
+ end
+
+ def push_allowed?(user, project, ref, oldrev, newrev)
+ if user && user_allowed?(user)
+ action = if project.protected_branch?(ref)
+ :push_code_to_protected_branches
+ else
+ :push_code
+ end
+ user.can?(action, project)
+ else
+ false
+ end
+ end
+
+ private
+
+ def user_allowed?(user)
+ return false if user.blocked?
+
+ if Gitlab.config.ldap.enabled
+ if user.ldap_user?
+ # Check if LDAP user exists and match LDAP user_filter
+ unless Gitlab::LDAP::Access.new.allowed?(user)
+ return false
+ end
+ end
+ end
+
+ true
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
new file mode 100644
index 00000000000..8f492e5c012
--- /dev/null
+++ b/lib/gitlab/ldap/access.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module LDAP
+ class Access
+ attr_reader :adapter
+
+ def self.open(&block)
+ Gitlab::LDAP::Adapter.open do |adapter|
+ block.call(self.new(adapter))
+ end
+ end
+
+ def initialize(adapter=nil)
+ @adapter = adapter
+ end
+
+ def allowed?(user)
+ !!Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
+ rescue
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb
new file mode 100644
index 00000000000..983a2956a35
--- /dev/null
+++ b/lib/gitlab/ldap/adapter.rb
@@ -0,0 +1,86 @@
+module Gitlab
+ module LDAP
+ class Adapter
+ attr_reader :ldap
+
+ def self.open(&block)
+ Net::LDAP.open(adapter_options) do |ldap|
+ block.call(self.new(ldap))
+ end
+ end
+
+ def self.config
+ Gitlab.config.ldap
+ end
+
+ def self.adapter_options
+ encryption = config['method'].to_s == 'ssl' ? :simple_tls : nil
+
+ 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
+ end
+
+
+ def initialize(ldap=nil)
+ @ldap = ldap || Net::LDAP.new(self.class.adapter_options)
+ end
+
+ def users(field, value)
+ if field.to_sym == :dn
+ options = {
+ base: value
+ }
+ else
+ options = {
+ 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'])
+
+ options[:filter] = if options[:filter]
+ Net::LDAP::Filter.join(options[:filter], user_filter)
+ else
+ user_filter
+ end
+ end
+
+ entries = ldap.search(options).select do |entry|
+ entry.respond_to? config.uid
+ end
+
+ entries.map do |entry|
+ Gitlab::LDAP::Person.new(entry)
+ end
+ end
+
+ def user(*args)
+ users(*args).first
+ end
+
+ private
+
+ def config
+ @config ||= self.class.config
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
new file mode 100644
index 00000000000..06b17c58f8c
--- /dev/null
+++ b/lib/gitlab/ldap/person.rb
@@ -0,0 +1,50 @@
+module Gitlab
+ module LDAP
+ class Person
+ def self.find_by_uid(uid, adapter=nil)
+ adapter ||= Gitlab::LDAP::Adapter.new
+ adapter.user(config.uid, uid)
+ end
+
+ def self.find_by_dn(dn, adapter=nil)
+ adapter ||= Gitlab::LDAP::Adapter.new
+ adapter.user('dn', dn)
+ end
+
+ def initialize(entry)
+ Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
+ @entry = entry
+ end
+
+ def name
+ entry.cn.first
+ end
+
+ def uid
+ entry.send(config.uid).first
+ end
+
+ def username
+ uid
+ end
+
+ def dn
+ entry.dn
+ end
+
+ private
+
+ def entry
+ @entry
+ end
+
+ def adapter
+ @adapter ||= Gitlab::LDAP::Adapter.new
+ end
+
+ def config
+ @config ||= Gitlab.config.ldap
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index fd36dda7d22..456a61b9e43 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -13,8 +13,8 @@ module Gitlab
def find_or_create(auth)
@auth = auth
- if uid.blank? || email.blank?
- raise_error("Account must provide an uid and email address")
+ if uid.blank? || email.blank? || username.blank?
+ raise_error("Account must provide a dn, uid and email address")
end
user = find(auth)
@@ -62,8 +62,16 @@ module Gitlab
return nil unless ldap_conf.enabled && login.present? && password.present?
ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
+ filter = Net::LDAP::Filter.eq(ldap.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
+
ldap_user = ldap.bind_as(
- filter: Net::LDAP::Filter.eq(ldap.uid, login),
+ filter: filter,
size: 1,
password: password
)
@@ -71,22 +79,20 @@ module Gitlab
find_by_uid(ldap_user.dn) if ldap_user
end
- # Check LDAP user existance by dn. User in git over ssh check
- #
- # It covers 2 cases:
- # * when ldap account was removed
- # * when ldap account was deactivated by change of OU membership in 'dn'
- def blocked?(dn)
- ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
- ldap.connection.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, size: 1).blank?
- end
-
private
def find_by_uid(uid)
model.where(provider: provider, extern_uid: uid).last
end
+ def username
+ (auth.info.nickname || samaccountname).to_s.force_encoding("utf-8")
+ end
+
+ def samaccountname
+ (auth.extra[:raw_info][:samaccountname] || []).first
+ end
+
def provider
'ldap'
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index dc9c0e0ab2c..80bb00821f7 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -98,7 +98,7 @@ module Gitlab
(?<prefix>\W)? # Prefix
( # Reference
@(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name
- |\#(?<issue>([a-zA-Z]+-)?\d+) # Issue ID
+ |\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
|!(?<merge_request>\d+) # MR ID
|\$(?<snippet>\d+) # Snippet ID
|(?<commit>[\h]{6,40}) # Commit ID
@@ -152,7 +152,7 @@ module Gitlab
#
# Returns boolean
def valid_emoji?(emoji)
- Emoji.names.include? emoji
+ Emoji.find_by_name emoji
end
# Private: Dispatches to a dedicated processing method based on reference
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index d18fc8bf2ce..e932b64f4f0 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -7,7 +7,7 @@ module Gitlab
end
def project_name_regex
- /\A[a-zA-Z0-9][a-zA-Z0-9_\-\. ]*\z/
+ /\A[a-zA-Z0-9_][a-zA-Z0-9_\-\. ]*\z/
end
def name_regex
@@ -49,7 +49,7 @@ module Gitlab
protected
def default_regex
- /\A[.?]?[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
+ /\A[.?]?[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
end
end
end
diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb
index bcf3012bd92..bdfcf254e9e 100644
--- a/lib/gitlab/satellite/satellite.rb
+++ b/lib/gitlab/satellite/satellite.rb
@@ -1,5 +1,9 @@
module Gitlab
- class SatelliteNotExistError < StandardError; end
+ class SatelliteNotExistError < StandardError
+ def initialize(msg = "Satellite doesn't exist")
+ super
+ end
+ end
module Satellite
class Satellite
@@ -17,14 +21,9 @@ module Gitlab
Gitlab::Satellite::Logger.error(message)
end
- def raise_no_satellite
- raise SatelliteNotExistError.new("Satellite doesn't exist")
- end
-
def clear_and_update!
- raise_no_satellite unless exists?
+ raise SatelliteNotExistError unless exists?
- File.exists? path
@repo = nil
clear_working_dir!
delete_heads!
@@ -55,7 +54,7 @@ module Gitlab
# * Changes the current directory to the satellite's working dir
# * Yields
def lock
- raise_no_satellite unless exists?
+ raise SatelliteNotExistError unless exists?
File.open(lock_file, "w+") do |f|
begin
@@ -77,7 +76,7 @@ module Gitlab
end
def repo
- raise_no_satellite unless exists?
+ raise SatelliteNotExistError unless exists?
@repo ||= Grit::Repo.new(path)
end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 3aa3b2ba1e9..39de1223b18 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -1,10 +1,29 @@
module Gitlab
class Seeder
def self.quiet
+ mute_mailer
SeedFu.quiet = true
yield
SeedFu.quiet = false
puts "\nOK".green
end
+
+ def self.by_user(user)
+ begin
+ Thread.current[:current_user] = user
+ yield
+ ensure
+ Thread.current[:current_user] = nil
+ end
+ end
+
+ def self.mute_mailer
+ code = <<-eos
+def Notify.delay
+ self
+end
+ eos
+ eval(code)
+ end
end
end
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
index 0fe4888665d..0846359f9b1 100644
--- a/lib/gitlab/upgrader.rb
+++ b/lib/gitlab/upgrader.rb
@@ -1,3 +1,4 @@
+require_relative "popen"
require_relative "version_info"
module Gitlab
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 2e18b0592b5..86d8b69b0ef 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -46,8 +46,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end
def preprocess(full_document)
- if @project
- h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
+ if is_wiki?
+ full_document
+ elsif @project
+ h.create_relative_links(full_document, @project, @ref, @request_path)
else
full_document
end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index c6e570784e0..ff584e69058 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -40,7 +40,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then
- sudo -u "$app_user" -H -i $0 "$@"; exit;
+ eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit;
fi
# Switch to the gitlab path, exit on failure.
diff --git a/lib/support/logrotate/gitlab b/lib/support/logrotate/gitlab
index df9398d0795..d9b07b61ec3 100644
--- a/lib/support/logrotate/gitlab
+++ b/lib/support/logrotate/gitlab
@@ -2,21 +2,19 @@
# based on: http://stackoverflow.com/a/4883967
/home/git/gitlab/log/*.log {
- weekly
+ daily
missingok
- rotate 52
+ rotate 90
compress
- delaycompress
notifempty
copytruncate
}
/home/git/gitlab-shell/gitlab-shell.log {
- weekly
+ daily
missingok
- rotate 52
+ rotate 90
compress
- delaycompress
notifempty
copytruncate
}
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 7a0f3efbb53..5bff362da0e 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -54,6 +54,14 @@ server {
proxy_pass http://gitlab;
}
+ # Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
+ location ~ ^/(assets)/ {
+ root /home/git/gitlab/public;
+ gzip_static on; # to serve pre-gzipped version
+ expires max;
+ add_header Cache-Control public;
+ }
+
error_page 502 /502.html;
}
diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake
index 7d3602211c1..058c7417040 100644
--- a/lib/tasks/dev.rake
+++ b/lib/tasks/dev.rake
@@ -1,10 +1,10 @@
+task dev: ["dev:setup"]
+
namespace :dev do
desc "GITLAB | Setup developer environment (db, fixtures)"
task :setup => :environment do
ENV['force'] = 'yes'
- Rake::Task["db:setup"].invoke
- Rake::Task["db:seed_fu"].invoke
+ Rake::Task["gitlab:setup"].invoke
Rake::Task["gitlab:shell:setup"].invoke
end
end
-
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 767674e1e84..071760c0c36 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -17,6 +17,7 @@ namespace :gitlab do
check_database_config_exists
check_database_is_not_sqlite
check_migrations_are_up
+ check_orphaned_users_groups
check_gitlab_config_exists
check_gitlab_config_not_outdated
check_log_writable
@@ -65,6 +66,7 @@ namespace :gitlab do
puts "no".green
else
puts "yes".red
+ puts "Please fix this by removing the SQLite entry from the database.yml".blue
for_more_information(
"https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
see_database_guide
@@ -181,6 +183,19 @@ namespace :gitlab do
end
end
+ def check_orphaned_users_groups
+ print "Database contains orphaned UsersGroups? ... "
+ if UsersGroup.where("user_id not in (select id from users)").count > 0
+ puts "yes".red
+ try_fixing_it(
+ "You can delete the orphaned records using something along the lines of:",
+ sudo_gitlab("bundle exec rails runner -e production 'UsersGroup.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
+ )
+ else
+ puts "no".green
+ end
+ end
+
def check_satellites_exist
print "Projects have satellites? ... "
@@ -727,7 +742,7 @@ namespace :gitlab do
end
def check_gitlab_shell
- required_version = Gitlab::VersionInfo.new(1, 7, 9)
+ required_version = Gitlab::VersionInfo.new(1, 9, 1)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 2b730774e06..853994dd67d 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -15,6 +15,14 @@ namespace :gitlab do
end
Rake::Task["db:setup"].invoke
+
+ config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
+ success = case config["adapter"]
+ when /^mysql/ then
+ Rake::Task["add_limits_mysql"].invoke
+ when "postgresql" then
+ end
+
Rake::Task["db:seed_fu"].invoke
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 0d7a390bc92..08de0f2dd5d 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -34,14 +34,18 @@ namespace :gitlab do
Gitlab::Shell.new.remove_all_keys
- Key.find_each(batch_size: 1000) do |key|
- if Gitlab::Shell.new.add_key(key.shell_id, key.key)
+ Gitlab::Shell.new.batch_add_keys do |adder|
+ Key.find_each(batch_size: 1000) do |key|
+ adder.add_key(key.shell_id, key.key)
print '.'
- else
- print 'F'
end
end
+ unless $?.success?
+ puts "Failed to add keys...".red
+ exit 1
+ end
+
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
exit 1
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index d36b9682850..da61c6e007f 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -82,6 +82,8 @@ namespace :gitlab do
def run(command)
output, _ = Gitlab::Popen.popen(command)
output
+ rescue Errno::ENOENT
+ '' # if the command does not exist, return an empty string
end
def uid_for(user_name)
diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake
index f52af0c3ded..2c9b9978933 100644
--- a/lib/tasks/gitlab/test.rake
+++ b/lib/tasks/gitlab/test.rake
@@ -2,15 +2,13 @@ namespace :gitlab do
desc "GITLAB | Run all tests"
task :test do
cmds = [
- %W(rake db:setup),
- %W(rake db:seed_fu),
%W(rake spinach),
%W(rake spec),
%W(rake jasmine:ci)
]
cmds.each do |cmd|
- system({'RAILS_ENV' => 'test'}, *cmd)
+ system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero?
end
diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake
new file mode 100644
index 00000000000..46b6451752b
--- /dev/null
+++ b/lib/tasks/migrate/add_limits_mysql.rake
@@ -0,0 +1,14 @@
+desc "GITLAB | Add limits to strings in mysql database"
+task add_limits_mysql: :environment do
+ puts "Adding limits to schema.rb for mysql"
+ LimitsToMysql.new.up
+end
+
+class LimitsToMysql < ActiveRecord::Migration
+ def up
+ change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
+ change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
+ change_column :snippets, :content, :text, limit: 2147483647
+ change_column :notes, :st_diff, :text, limit: 2147483647
+ end
+end
diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake
new file mode 100644
index 00000000000..90a1809914b
--- /dev/null
+++ b/lib/tasks/spec.rake
@@ -0,0 +1,14 @@
+Rake::Task["spec"].clear if Rake::Task.task_defined?('spec')
+
+desc "GITLAB | Run specs"
+task :spec do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec),
+ ]
+
+ cmds.each do |cmd|
+ system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
+ raise "#{cmd} failed!" unless $?.exitstatus.zero?
+ end
+end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
new file mode 100644
index 00000000000..c23d0e0e188
--- /dev/null
+++ b/lib/tasks/spinach.rake
@@ -0,0 +1,14 @@
+Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach')
+
+desc "GITLAB | Run spinach"
+task :spinach do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(spinach),
+ ]
+
+ cmds.each do |cmd|
+ system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
+ raise "#{cmd} failed!" unless $?.exitstatus.zero?
+ end
+end
diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake
new file mode 100644
index 00000000000..f19da1bb437
--- /dev/null
+++ b/lib/tasks/test.rake
@@ -0,0 +1,6 @@
+Rake::Task["test"].clear
+
+desc "GITLAB | Run all tests"
+task :test do
+ Rake::Task["gitlab:test"].invoke
+end