diff options
Diffstat (limited to 'lib')
35 files changed, 907 insertions, 349 deletions
diff --git a/lib/api/branches.rb b/lib/api/branches.rb index b32a4aa7bc2..4db5f61dd28 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -80,9 +80,17 @@ module API # POST /projects/:id/repository/branches post ":id/repository/branches" do authorize_push_project - @branch = CreateBranchService.new.execute(user_project, params[:branch_name], params[:ref], current_user) - - present @branch, with: Entities::RepoObject, project: user_project + result = CreateBranchService.new.execute(user_project, + params[:branch_name], + params[:ref], + current_user) + if result[:status] == :success + present result[:branch], + with: Entities::RepoObject, + project: user_project + else + render_api_error!(result[:message], 400) + end end # Delete branch @@ -99,7 +107,7 @@ module API if result[:state] == :success true else - render_api_error!(result[:message], 405) + render_api_error!(result[:message], result[:return_code]) end end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index d36b29a00b1..6af0f6d1b25 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -114,17 +114,21 @@ module API # Helper method for validating all labels against its names def validate_label_params(params) + errors = {} + if params[:labels].present? params[:labels].split(',').each do |label_name| label = user_project.labels.create_with( color: Label::DEFAULT_COLOR).find_or_initialize_by( title: label_name.strip) + if label.invalid? - return true + errors[label.title] = label.errors end end end - false + + errors end # error helpers diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 5850892df07..5f484f63418 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -12,7 +12,9 @@ module API # ref - branch name # forced_push - forced_push # - get "/allowed" do + post "/allowed" do + status 200 + # Check for *.wiki repositories. # Strip out the .wiki from the pathname before finding the # project. This applies the correct project permissions to @@ -34,10 +36,7 @@ module API actor, params[:action], project, - params[:ref], - params[:oldrev], - params[:newrev], - params[:forced_push] + params[:changes] ) end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 055529ccbd8..5369149cdfc 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -3,13 +3,41 @@ module API class Issues < Grape::API before { authenticate! } + helpers do + def filter_issues_state(issues, state = nil) + case state + when 'opened' then issues.opened + when 'closed' then issues.closed + else issues + end + end + + def filter_issues_labels(issues, labels) + issues.includes(:labels).where("labels.title" => labels.split(',')) + end + end + resource :issues do # Get currently authenticated user's issues # - # Example Request: + # Parameters: + # state (optional) - Return "opened" or "closed" issues + # labels (optional) - Comma-separated list of label names + + # Example Requests: # GET /issues + # GET /issues?state=opened + # GET /issues?state=closed + # GET /issues?labels=foo + # GET /issues?labels=foo,bar + # GET /issues?labels=foo,bar&state=opened get do - present paginate(current_user.issues), with: Entities::Issue + issues = current_user.issues + issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? + issues = issues.order('issues.id DESC') + + present paginate(issues), with: Entities::Issue end end @@ -18,10 +46,24 @@ module API # # Parameters: # id (required) - The ID of a project - # Example Request: + # state (optional) - Return "opened" or "closed" issues + # labels (optional) - Comma-separated list of label names + # + # Example Requests: # GET /projects/:id/issues + # GET /projects/:id/issues?state=opened + # GET /projects/:id/issues?state=closed + # GET /projects/:id/issues + # GET /projects/:id/issues?labels=foo + # GET /projects/:id/issues?labels=foo,bar + # GET /projects/:id/issues?labels=foo,bar&state=opened get ":id/issues" do - present paginate(user_project.issues), with: Entities::Issue + issues = user_project.issues + issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? + issues = issues.order('issues.id DESC') + + present paginate(issues), with: Entities::Issue end # Get a single project issue @@ -52,8 +94,8 @@ module API attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute @@ -90,8 +132,8 @@ module API attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event] # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue) @@ -99,7 +141,8 @@ module API if issue.valid? # Find or create labels and attach to issue. Labels are valid because # we already checked its name, so there can't be an error here - if params[:labels].present? + unless params[:labels].nil? + issue.remove_labels # Create and add labels to the new created issue issue.add_labels_by_names(params[:labels].split(',')) end diff --git a/lib/api/labels.rb b/lib/api/labels.rb index d1684b2293c..2fdf53ffec2 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -39,7 +39,7 @@ module API if label.valid? present label, with: Entities::Label else - render_api_error!(label.errors.full_messages.join(', '), 405) + render_api_error!(label.errors.full_messages.join(', '), 400) end end @@ -95,7 +95,7 @@ module API if label.update(attrs) present label, with: Entities::Label else - render_api_error!(label.errors.full_messages.join(', '), 405) + render_api_error!(label.errors.full_messages.join(', '), 400) end end end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 0d765f9280e..8726379bf3c 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -78,8 +78,8 @@ module API attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description] # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute @@ -117,15 +117,16 @@ module API authorize! :modify_merge_request, merge_request # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request) if merge_request.valid? # Find or create labels and attach to issue - if params[:labels].present? + unless params[:labels].nil? + merge_request.remove_labels merge_request.add_labels_by_names(params[:labels].split(",")) end diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 42068bb343d..07c29aa7b4c 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -32,14 +32,23 @@ module API # id (required) - The ID of a project # tag_name (required) - The name of the tag # ref (required) - Create tag from commit sha or branch + # message (optional) - Specifying a message creates an annotated tag. # Example Request: # POST /projects/:id/repository/tags post ':id/repository/tags' do authorize_push_project - @tag = CreateTagService.new.execute(user_project, params[:tag_name], - params[:ref], current_user) - - present @tag, with: Entities::RepoObject, project: user_project + message = params[:message] || nil + result = CreateTagService.new.execute(user_project, params[:tag_name], + params[:ref], message, + current_user) + + if result[:status] == :success + present result[:tag], + with: Entities::RepoObject, + project: user_project + else + render_api_error!(result[:message], 400) + end end # Get a project repository tree diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 6f7c4f7c909..ea05fa2c261 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -69,7 +69,7 @@ module Backup end print 'Put GitLab hooks in repositories dirs'.yellow - if system("#{Gitlab.config.gitlab_shell.path}/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path) + if system("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks") puts " [DONE]".green else puts " [FAILED]".red diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 53bff3037e5..907373ab991 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -107,12 +107,17 @@ module Gitlab # path - project path with namespace # tag_name - new tag name # ref - HEAD for new tag + # message - optional message for tag (annotated tag) # # Ex. # add_tag("gitlab/gitlab-ci", "v4.0", "master") + # add_tag("gitlab/gitlab-ci", "v4.0", "master", "message") # - def add_tag(path, tag_name, ref) - system "#{gitlab_shell_path}/bin/gitlab-projects", "create-tag", "#{path}.git", tag_name, ref + def add_tag(path, tag_name, ref, message = nil) + cmd = %W(#{gitlab_shell_path}/bin/gitlab-projects create-tag #{path}.git + #{tag_name} #{ref}) + cmd << message unless message.nil? || message.empty? + system *cmd end # Remove repository tag diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb index 6bc2c3b487c..43145e0ee1b 100644 --- a/lib/gitlab/blacklist.rb +++ b/lib/gitlab/blacklist.rb @@ -3,7 +3,32 @@ module Gitlab extend self def path - %w(admin dashboard files groups help profile projects search public assets u s teams merge_requests issues users snippets services repository hooks notes) + %w( + admin + dashboard + files + groups + help + profile + projects + search + public + assets + u + s + teams + merge_requests + issues + users + snippets + services + repository + hooks + notes + unsubscribes + all + ci + ) end end end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb new file mode 100644 index 00000000000..19a1198c68c --- /dev/null +++ b/lib/gitlab/diff/file.rb @@ -0,0 +1,49 @@ +module Gitlab + module Diff + class File + attr_reader :diff + + delegate :new_file, :deleted_file, :renamed_file, + :old_path, :new_path, to: :diff, prefix: false + + def initialize(diff) + @diff = diff + end + + # Array of Gitlab::DIff::Line objects + def diff_lines + @lines ||= parser.parse(raw_diff.lines) + end + + def mode_changed? + !!(diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode) + end + + def parser + Gitlab::Diff::Parser.new + end + + def raw_diff + diff.diff + end + + def next_line(index) + diff_lines[index + 1] + end + + def prev_line(index) + if index > 0 + diff_lines[index - 1] + end + end + + def file_path + if diff.new_path.present? + diff.new_path + elsif diff.old_path.present? + diff.old_path + end + end + end + end +end diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb new file mode 100644 index 00000000000..8ac1b15e88a --- /dev/null +++ b/lib/gitlab/diff/line.rb @@ -0,0 +1,12 @@ +module Gitlab + module Diff + class Line + attr_reader :type, :text, :index, :old_pos, :new_pos + + def initialize(text, type, index, old_pos, new_pos) + @text, @type, @index = text, type, index + @old_pos, @new_pos = old_pos, new_pos + end + end + end +end diff --git a/lib/gitlab/diff/line_code.rb b/lib/gitlab/diff/line_code.rb new file mode 100644 index 00000000000..f3578ab3d35 --- /dev/null +++ b/lib/gitlab/diff/line_code.rb @@ -0,0 +1,9 @@ +module Gitlab + module Diff + class LineCode + def self.generate(file_path, new_line_position, old_line_position) + "#{Digest::SHA1.hexdigest(file_path)}_#{old_line_position}_#{new_line_position}" + end + end + end +end diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb new file mode 100644 index 00000000000..9d6309954a4 --- /dev/null +++ b/lib/gitlab/diff/parser.rb @@ -0,0 +1,81 @@ +module Gitlab + module Diff + class Parser + include Enumerable + + def parse(lines) + @lines = lines, + lines_obj = [] + line_obj_index = 0 + line_old = 1 + line_new = 1 + type = nil + + lines_arr = ::Gitlab::InlineDiff.processing lines + + lines_arr.each do |line| + raw_line = line.dup + + next if filename?(line) + + full_line = html_escape(line.gsub(/\n/, '')) + full_line = ::Gitlab::InlineDiff.replace_markers full_line + + if line.match(/^@@ -/) + type = "match" + + line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 + line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 + + next if line_old == 1 && line_new == 1 #top of file + lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) + line_obj_index += 1 + next + else + type = identification_type(line) + lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) + line_obj_index += 1 + end + + + if line[0] == "+" + line_new += 1 + elsif line[0] == "-" + line_old += 1 + else + line_new += 1 + line_old += 1 + end + end + + lines_obj + end + + def empty? + @lines.empty? + end + + private + + def filename?(line) + line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', + '--- /tmp/diffy', '+++ /tmp/diffy') + end + + def identification_type(line) + if line[0] == "+" + "new" + elsif line[0] == "-" + "old" + else + nil + end + end + + def html_escape str + replacements = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } + str.gsub(/[&"'><]/, replacements) + end + end + end +end diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb deleted file mode 100644 index b244295027e..00000000000 --- a/lib/gitlab/diff_parser.rb +++ /dev/null @@ -1,83 +0,0 @@ -module Gitlab - class DiffParser - include Enumerable - - attr_reader :lines, :new_path - - def initialize(lines, new_path = '') - @lines = lines - @new_path = new_path - end - - def each - line_old = 1 - line_new = 1 - type = nil - - lines_arr = ::Gitlab::InlineDiff.processing lines - lines_arr.each do |line| - raw_line = line.dup - - next if filename?(line) - - full_line = html_escape(line.gsub(/\n/, '')) - full_line = ::Gitlab::InlineDiff.replace_markers full_line - - if line.match(/^@@ -/) - type = "match" - - line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 - line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 - - next if line_old == 1 && line_new == 1 #top of file - yield(full_line, type, nil, line_new, line_old) - next - else - type = identification_type(line) - line_code = generate_line_code(new_path, line_new, line_old) - yield(full_line, type, line_code, line_new, line_old, raw_line) - end - - - if line[0] == "+" - line_new += 1 - elsif line[0] == "-" - line_old += 1 - else - line_new += 1 - line_old += 1 - end - end - end - - def empty? - @lines.empty? - end - - private - - def filename?(line) - line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', - '--- /tmp/diffy', '+++ /tmp/diffy') - end - - def identification_type(line) - if line[0] == "+" - "new" - elsif line[0] == "-" - "old" - else - nil - end - end - - def generate_line_code(path, line_new, line_old) - "#{Digest::SHA1.hexdigest(path)}_#{line_old}_#{line_new}" - end - - def html_escape str - replacements = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } - str.gsub(/[&"'><]/, replacements) - end - end -end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 38b3d82e2f4..e75a5a1d62e 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -5,7 +5,7 @@ module Gitlab attr_reader :params, :project, :git_cmd, :user - def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil, forced_push = false) + def allowed?(actor, cmd, project, changes = nil) case cmd when *DOWNLOAD_COMMANDS if actor.is_a? User @@ -19,12 +19,12 @@ module Gitlab end when *PUSH_COMMANDS if actor.is_a? User - push_allowed?(actor, project, ref, oldrev, newrev, forced_push) + push_allowed?(actor, project, changes) 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, forced_push) + push_allowed?(actor.user, project, changes) else raise 'Wrong actor' end @@ -41,13 +41,21 @@ module Gitlab end end - def push_allowed?(user, project, ref, oldrev, newrev, forced_push) - if user && user_allowed?(user) + def push_allowed?(user, project, changes) + return false unless user && user_allowed?(user) + return true if changes.blank? + + changes = changes.lines if changes.kind_of?(String) + + # Iterate over all changes to find if user allowed all of them to be applied + changes.each do |change| + oldrev, newrev, ref = changes.split('') + action = if project.protected_branch?(ref) # we dont allow force push to protected branch - if forced_push.to_s == 'true' + if forced_push?(oldrev, newrev) :force_push_code_to_protected_branches - # and we dont allow remove of protected branch + # and we dont allow remove of protected branch elsif newrev =~ /0000000/ :remove_protected_branches else @@ -59,7 +67,22 @@ module Gitlab else :push_code end - user.can?(action, project) + unless user.can?(action, project) + # If user does not have access to make at least one change - cancel all push + return false + end + end + + # If user has access to make all changes + true + end + + def forced_push?(oldrev, newrev) + return false if project.empty_repo? + + if oldrev !~ /00000000/ && newrev !~ /00000000/ + 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 false end diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb new file mode 100644 index 00000000000..13cb08948bb --- /dev/null +++ b/lib/gitlab/git_ref_validator.rb @@ -0,0 +1,11 @@ +module Gitlab + module GitRefValidator + extend self + # Validates a given name against the git reference specification + # + # Returns true for a valid reference name, false otherwise + def validate(ref_name) + system *%W(git check-ref-format refs/#{ref_name}) + end + end +end diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index 62709a12942..c054b6f5865 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -28,7 +28,7 @@ module Gitlab def allowed?(user) if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) - !Gitlab::LDAP::Person.active_directory_disabled?(user.extern_uid, adapter) + !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter) else false end diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index ca239bea884..68ac1b22909 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -86,7 +86,8 @@ module Gitlab end def dn_matches_filter?(dn, filter) - ldap_search(base: dn, filter: filter, scope: Net::LDAP::SearchScope_BaseObject, attributes: %w{dn}).any? + ldap_search(base: dn, filter: filter, + scope: Net::LDAP::SearchScope_BaseObject, attributes: %w{dn}).any? end def ldap_search(*args) diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 9ad6618bd46..87c3d711db4 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -16,7 +16,7 @@ module Gitlab adapter.user('dn', dn) end - def self.active_directory_disabled?(dn, adapter=nil) + def self.disabled_via_active_directory?(dn, adapter=nil) adapter ||= Gitlab::LDAP::Adapter.new adapter.dn_matches_filter?(dn, AD_USER_DISABLED) end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index be3fcc4f035..25b5a702f9a 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -10,52 +10,20 @@ module Gitlab module LDAP class User < Gitlab::OAuth::User class << self - def find_or_create(auth) - @auth = auth - - if uid.blank? || email.blank? || username.blank? - raise_error("Account must provide a dn, uid and email address") - end - - user = find(auth) - - unless user - # Look for user with same emails - # - # Possible cases: - # * When user already has account and need to link their LDAP account. - # * LDAP uid changed for user with same email and we need to update their uid - # - user = find_user(email) - - if user - user.update_attributes(extern_uid: uid, provider: provider) - log.info("(LDAP) Updating legacy LDAP user #{email} with extern_uid => #{uid}") - else - # Create a new user inside GitLab database - # based on LDAP credentials - # - # - user = create(auth) - end - end - - user + 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_user(email) - user = model.find_by(email: email) + def find_and_connect_by_email(auth_hash) + self.auth_hash = auth_hash + user = model.find_by(email: self.auth_hash.email) - # If no user found and allow_username_or_email_login is true - # we look for user by extracting part of their email - if !user && email && ldap_conf['allow_username_or_email_login'] - uname = email.partition('@').first - # Strip apostrophes since they are disallowed as part of username - username = uname.gsub("'", "") - user = model.find_by(username: username) + 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 - - user end def authenticate(login, password) @@ -63,17 +31,8 @@ module Gitlab # Only check with valid login and password to prevent anonymous bind results 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: filter, + ldap_user = adapter.bind_as( + filter: user_filter(login), size: 1, password: password ) @@ -81,10 +40,14 @@ module Gitlab find_by_uid(ldap_user.dn) if ldap_user end - private + def adapter + @adapter ||= OmniAuth::LDAP::Adaptor.new(ldap_conf) + end + + protected def find_by_uid_and_provider - find_by_uid(uid) + find_by_uid(auth_hash.uid) end def find_by_uid(uid) @@ -92,10 +55,6 @@ module Gitlab model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last end - def username - auth.info.nickname.to_s.force_encoding("utf-8") - end - def provider 'ldap' end @@ -107,6 +66,20 @@ module Gitlab def ldap_conf Gitlab.config.ldap 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 + end + + def needs_blocking? + false end end end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b248d8f9436..6017a4c86c1 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -192,8 +192,12 @@ module Gitlab link_to("##{identifier}", url, options) end - elsif project.issues_tracker == 'jira' - reference_jira_issue(identifier, project) + 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) + end end end @@ -229,15 +233,15 @@ module Gitlab end end - def reference_jira_issue(identifier, project = @project) - url = url_for_issue(identifier) - title = Gitlab.config.issues_tracker[project.issues_tracker]["title"] + def reference_external_issue(identifier, issue_tracker, project = @project) + url = url_for_issue(identifier, project) + title = issue_tracker['title'] options = html_options.merge( title: "Issue in #{title}", class: "gfm gfm-issue #{html_options[:class]}" ) - link_to("#{identifier}", url, options) + link_to("##{identifier}", url, options) end end end diff --git a/lib/gitlab/oauth/auth_hash.rb b/lib/gitlab/oauth/auth_hash.rb new file mode 100644 index 00000000000..0198f61f427 --- /dev/null +++ b/lib/gitlab/oauth/auth_hash.rb @@ -0,0 +1,54 @@ +# Class to parse and transform the info provided by omniauth +# +module Gitlab + module OAuth + class AuthHash + attr_reader :auth_hash + def initialize(auth_hash) + @auth_hash = auth_hash + end + + def uid + auth_hash.uid.to_s + end + + def provider + auth_hash.provider + end + + def info + auth_hash.info + end + + def name + (info.name || full_name).to_s.force_encoding('utf-8') + end + + def full_name + "#{info.first_name} #{info.last_name}" + end + + def username + (info.try(:nickname) || generate_username).to_s.force_encoding('utf-8') + end + + def email + (info.try(:email) || generate_temporarily_email).downcase + end + + def password + @password ||= Devise.friendly_token[0, 8].downcase + end + + # Get the first part of the email address (before @) + # In addtion in removes illegal characters + def generate_username + email.match(/^[^@]*/)[0].parameterize + end + + def generate_temporarily_email + "temp-email-for-oauth-#{username}@gitlab.localhost" + end + end + end +end diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb index 0056eb3a28b..b768eda185f 100644 --- a/lib/gitlab/oauth/user.rb +++ b/lib/gitlab/oauth/user.rb @@ -7,106 +7,78 @@ module Gitlab module OAuth class User class << self - attr_reader :auth + attr_reader :auth_hash - def find(auth) - @auth = auth + def find(auth_hash) + self.auth_hash = auth_hash find_by_uid_and_provider end - def create(auth) - @auth = auth - password = Devise.friendly_token[0, 8].downcase - opts = { - extern_uid: uid, - provider: provider, - name: name, - username: username, - email: email, - password: password, - password_confirmation: password, - } - - user = model.build_user(opts) - user.skip_confirmation! - - # Services like twitter and github does not return email via oauth - # In this case we generate temporary email and force user to fill it later - if user.email.blank? - user.generate_tmp_oauth_email - elsif provider != "ldap" - # Google oauth returns email but dont return nickname - # So we use part of email as username for new user - # For LDAP, username is already set to the user's - # uid/userid/sAMAccountName. - email_username = email.match(/^[^@]*/)[0] - # Strip apostrophes since they are disallowed as part of username - user.username = email_username.gsub("'", "") - end - - begin - user.save! - rescue ActiveRecord::RecordInvalid => e - log.info "(OAuth) Email #{e.record.errors[:email]}. Username #{e.record.errors[:username]}" - return nil, e.record.errors - end - - log.info "(OAuth) Creating user #{email} from login with extern_uid => #{uid}" - - if Gitlab.config.omniauth['block_auto_created_users'] && !ldap? - user.block - end + def create(auth_hash) + user = new(auth_hash) + user.save_and_trigger_callbacks + end - user + def model + ::User end - private + def auth_hash=(auth_hash) + @auth_hash = AuthHash.new(auth_hash) + end + protected def find_by_uid_and_provider - model.where(provider: provider, extern_uid: uid).last + model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last end + end - def uid - uid = auth.info.uid || auth.uid - uid = uid.to_s unless uid.nil? - uid - end + # Instance methods + attr_accessor :auth_hash, :user - def email - auth.info.email.downcase unless auth.info.email.nil? - end + def initialize(auth_hash) + self.auth_hash = auth_hash + self.user = self.class.model.new(user_attributes) + user.skip_confirmation! + end - def name - if auth.info.name.nil? - "#{auth.info.first_name} #{auth.info.last_name}".force_encoding('utf-8') - else - auth.info.name.to_s.force_encoding('utf-8') - end - end + def auth_hash=(auth_hash) + @auth_hash = AuthHash.new(auth_hash) + end - def username - auth.info.nickname.to_s.force_encoding("utf-8") - 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 provider - auth.provider - end + user + rescue ActiveRecord::RecordInvalid => e + log.info "(OAuth) Email #{e.record.errors[:email]}. Username #{e.record.errors[:username]}" + return nil, e.record.errors + end - def log - Gitlab::AppLogger - end + def user_attributes + { + extern_uid: auth_hash.uid, + provider: auth_hash.provider, + name: auth_hash.name, + username: auth_hash.username, + email: auth_hash.email, + password: auth_hash.password, + password_confirmation: auth_hash.password, + } + end - def model - ::User - end + def log + Gitlab::AppLogger + end - def raise_error(message) - raise OmniAuth::Error, "(OAuth) " + message - end + def raise_error(message) + raise OmniAuth::Error, "(OAuth) " + message + end - def ldap? - provider == 'ldap' - end + def needs_blocking? + Gitlab.config.omniauth['block_auto_created_users'] end end end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb new file mode 100644 index 00000000000..9dc8b34d9c7 --- /dev/null +++ b/lib/gitlab/project_search_results.rb @@ -0,0 +1,73 @@ +module Gitlab + class ProjectSearchResults < SearchResults + attr_reader :project, :repository_ref + + def initialize(project_id, query, repository_ref = nil) + @project = Project.find(project_id) + @repository_ref = repository_ref + @query = Shellwords.shellescape(query) if query.present? + end + + def objects(scope, page = nil) + case scope + when 'notes' + notes.page(page).per(per_page) + when 'blobs' + Kaminari.paginate_array(blobs).page(page).per(per_page) + when 'wiki_blobs' + Kaminari.paginate_array(wiki_blobs).page(page).per(per_page) + else + super + end + end + + def total_count + @total_count ||= issues_count + merge_requests_count + blobs_count + + notes_count + wiki_blobs_count + end + + def blobs_count + @blobs_count ||= blobs.count + end + + def notes_count + @notes_count ||= notes.count + end + + def wiki_blobs_count + @wiki_blobs_count ||= wiki_blobs.count + end + + private + + def blobs + if project.empty_repo? + [] + else + project.repository.search_files(query, repository_ref) + end + end + + def wiki_blobs + if project.wiki_enabled? + wiki_repo = Repository.new(ProjectWiki.new(project).path_with_namespace) + + if wiki_repo.exists? + wiki_repo.search_files(query) + else + [] + end + else + [] + end + end + + def notes + Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC') + end + + def limit_project_ids + [project.id] + end + end +end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index 7c058b58c4c..f34d661c9fc 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -121,6 +121,7 @@ module Gitlab # # Note: this will only update remote branches (i.e. origin/*) def update_from_source! + repo.git.remote(default_options, 'set-url', :origin, project.repository.path_to_repo) repo.git.fetch(default_options, :origin) end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb new file mode 100644 index 00000000000..75a3dfe37c3 --- /dev/null +++ b/lib/gitlab/search_results.rb @@ -0,0 +1,69 @@ +module Gitlab + class SearchResults + attr_reader :query + + # Limit search results by passed project ids + # It allows us to search only for projects user has access to + attr_reader :limit_project_ids + + def initialize(limit_project_ids, query) + @limit_project_ids = limit_project_ids || Project.all + @query = Shellwords.shellescape(query) if query.present? + end + + def objects(scope, page = nil) + case scope + when 'projects' + projects.page(page).per(per_page) + when 'issues' + issues.page(page).per(per_page) + when 'merge_requests' + merge_requests.page(page).per(per_page) + else + Kaminari.paginate_array([]).page(page).per(per_page) + end + end + + def total_count + @total_count ||= projects_count + issues_count + merge_requests_count + end + + def projects_count + @projects_count ||= projects.count + end + + def issues_count + @issues_count ||= issues.count + end + + def merge_requests_count + @merge_requests_count ||= merge_requests.count + end + + def empty? + total_count.zero? + end + + private + + def projects + Project.where(id: limit_project_ids).search(query) + end + + def issues + Issue.where(project_id: limit_project_ids).full_search(query).order('updated_at DESC') + end + + def merge_requests + MergeRequest.in_projects(limit_project_ids).full_search(query).order('updated_at DESC') + end + + def default_scope + 'projects' + end + + def per_page + 20 + end + end +end diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb new file mode 100644 index 00000000000..938219efdb2 --- /dev/null +++ b/lib/gitlab/snippet_search_results.rb @@ -0,0 +1,131 @@ +module Gitlab + class SnippetSearchResults < SearchResults + attr_reader :limit_snippet_ids + + def initialize(limit_snippet_ids, query) + @limit_snippet_ids = limit_snippet_ids + @query = query + end + + def objects(scope, page = nil) + case scope + when 'snippet_titles' + Kaminari.paginate_array(snippet_titles).page(page).per(per_page) + when 'snippet_blobs' + Kaminari.paginate_array(snippet_blobs).page(page).per(per_page) + else + super + end + end + + def total_count + @total_count ||= snippet_titles_count + snippet_blobs_count + end + + def snippet_titles_count + @snippet_titles_count ||= snippet_titles.count + end + + def snippet_blobs_count + @snippet_blobs_count ||= snippet_blobs.count + end + + private + + def snippet_titles + Snippet.where(id: limit_snippet_ids).search(query).order('updated_at DESC') + end + + def snippet_blobs + search = Snippet.where(id: limit_snippet_ids).search_code(query) + search = search.order('updated_at DESC').to_a + snippets = [] + search.each { |e| snippets << chunk_snippet(e) } + snippets + end + + def default_scope + 'snippet_blobs' + end + + # Get an array of line numbers surrounding a matching + # line, bounded by min/max. + # + # @returns Array of line numbers + def bounded_line_numbers(line, min, max) + lower = line - surrounding_lines > min ? line - surrounding_lines : min + upper = line + surrounding_lines < max ? line + surrounding_lines : max + (lower..upper).to_a + end + + # Returns a sorted set of lines to be included in a snippet preview. + # This ensures matching adjacent lines do not display duplicated + # surrounding code. + # + # @returns Array, unique and sorted. + def matching_lines(lined_content) + used_lines = [] + lined_content.each_with_index do |line, line_number| + used_lines.concat bounded_line_numbers( + line_number, + 0, + lined_content.size + ) if line.include?(query) + end + + used_lines.uniq.sort + end + + # 'Chunkify' entire snippet. Splits the snippet data into matching lines + + # surrounding_lines() worth of unmatching lines. + # + # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}} + def chunk_snippet(snippet) + lined_content = snippet.content.split("\n") + used_lines = matching_lines(lined_content) + + snippet_chunk = [] + snippet_chunks = [] + snippet_start_line = 0 + last_line = -1 + + # Go through each used line, and add consecutive lines as a single chunk + # to the snippet chunk array. + used_lines.each do |line_number| + if last_line < 0 + # Start a new chunk. + snippet_start_line = line_number + snippet_chunk << lined_content[line_number] + elsif last_line == line_number - 1 + # Consecutive line, continue chunk. + snippet_chunk << lined_content[line_number] + else + # Non-consecutive line, add chunk to chunk array. + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Start a new chunk. + snippet_chunk = [lined_content[line_number]] + snippet_start_line = line_number + end + last_line = line_number + end + # Add final chunk to chunk array + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Return snippet with chunk array + { snippet_object: snippet, snippet_chunks: snippet_chunks } + end + + # Defines how many unmatching lines should be + # included around the matching lines in a snippet + def surrounding_lines + 3 + end + end +end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index 0846359f9b1..74b049b5143 100644 --- a/lib/gitlab/upgrader.rb +++ b/lib/gitlab/upgrader.rb @@ -43,7 +43,7 @@ module Gitlab end def latest_version_raw - remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags origin)) + remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git)) git_tags = remote_tags.split("\n").grep(/tags\/v#{current_version.major}/) git_tags = git_tags.select { |version| version =~ /v\d\.\d\.\d\Z/ } last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index ea1319268f8..d0b6cde3c7e 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -23,7 +23,21 @@ module Gitlab end def allowed_for?(user, level) - user.is_admin? || !Gitlab.config.gitlab.restricted_visibility_levels.include?(level) + user.is_admin? || allowed_level?(level) + end + + # Level can be a string `"public"` or a value `20`, first check if valid, + # then check if the corresponding string appears in the config + def allowed_level?(level) + if options.has_key?(level.to_s) + non_restricted_level?(level) + elsif options.has_value?(level.to_i) + non_restricted_level?(options.key(level.to_i).downcase) + end + end + + def non_restricted_level?(level) + ! Gitlab.config.gitlab.restricted_visibility_levels.include?(level) end end diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 49306fb63da..49a68c62293 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -1,69 +1,85 @@ -# GITLAB -# Maintainer: @randx - -# CHUNKED TRANSFER -# It is a known issue that Git-over-HTTP requires chunked transfer encoding [0] which is not -# supported by Nginx < 1.3.9 [1]. As a result, pushing a large object with Git (i.e. a single large file) -# can lead to a 411 error. In theory you can get around this by tweaking this configuration file and either -# - 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. 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 -# [1] https://github.com/agentzh/chunkin-nginx-module#status -# [2] https://github.com/agentzh/chunkin-nginx-module +## GitLab +## Maintainer: @randx +## +## Lines starting with two hashes (##) are comments with information. +## Lines starting with one hash (#) are configuration parameters that can be uncommented. +## +################################## +## CHUNKED TRANSFER ## +################################## +## +## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0] +## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object +## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get +## around this by tweaking this configuration file and either: +## - 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. +## 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 +## [1] https://github.com/agentzh/chunkin-nginx-module#status +## [2] https://github.com/agentzh/chunkin-nginx-module +## +################################### +## configuration ## +################################### +## upstream gitlab { - server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; + server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; } +## Normal HTTP host 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 root /home/git/gitlab/public; - - # Increase this if you want to upload large attachments - # Or if you want to accept large git objects over http + + ## Increase this if you want to upload large attachments + ## Or if you want to accept large git objects over http client_max_body_size 20m; - # individual nginx logs for this gitlab vhost + ## Individual nginx logs for this GitLab vhost access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; location / { - # serve static files from defined root folder;. - # @gitlab is a named location for the upstream fallback, see below + ## Serve static files from defined root folder. + ## @gitlab is a named location for the upstream fallback, see below. try_files $uri $uri/index.html $uri.html @gitlab; } - # if a file, which is not found in the root folder is requested, - # then the proxy pass the request to the upsteam (gitlab unicorn) + ## If a file, which is not found in the root folder is requested, + ## then the proxy passes the request to the upsteam (gitlab unicorn). location @gitlab { - # If you use https make sure you disable gzip compression - # to be safe against BREACH attack + ## If you use HTTPS make sure you disable gzip compression + ## to be safe against BREACH attack. # gzip off; - proxy_read_timeout 300; # Some requests take more than 30 seconds. - proxy_connect_timeout 300; # Some requests take more than 30 seconds. - proxy_redirect off; + ## https://github.com/gitlabhq/gitlabhq/issues/694 + ## Some requests take more than 30 seconds. + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Frame-Options SAMEORIGIN; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Frame-Options SAMEORIGIN; proxy_pass http://gitlab; } - # Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression - # WARNING: If you are using relative urls do remove the block below - # See config/application.rb under "Relative url support" for the list of - # other files that need to be changed for relative url support - location ~ ^/(assets)/ { + ## Enable gzip compression as per rails guide: + ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression + ## WARNING: If you are using relative urls remove the block below + ## See config/application.rb under "Relative url support" for the list of + ## other files that need to be changed for relative url support + location ~ ^/(assets)/ { root /home/git/gitlab/public; gzip_static on; # to serve pre-gzipped version expires max; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 54a4a080a9f..19409e41f40 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -6,8 +6,7 @@ ## Modified from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html ## ## Lines starting with two hashes (##) are comments with information. -## Lines starting with one hash (#) are configuration parameters. -## The last category can be commented/uncommented to your liking. +## Lines starting with one hash (#) are configuration parameters that can be uncommented. ## ################################## ## CHUNKED TRANSFER ## @@ -20,44 +19,36 @@ ## - 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. As a workaround -## users can use Git over SSH to push large files. +## 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 ## [1] https://github.com/agentzh/chunkin-nginx-module#status ## [2] https://github.com/agentzh/chunkin-nginx-module ## -################################### -## SSL file editing ## -################################### -## -## Edit `gitlab-shell/config.yml`: -## 1) Set "gitlab_url" param in `gitlab-shell/config.yml` to `https://git.example.com` -## 2) Set "ca_file" to `/etc/nginx/ssl/gitlab.crt` -## 3) Set "self_signed_cert" to `true` -## Edit `gitlab/config/gitlab.yml`: -## 1) Define port for http "port: 443" -## 2) Enable https "https: true" -## 3) Update ssl for gravatar "ssl_url: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm" ## ################################### ## SSL configuration ## ################################### ## +## See installation.md#using-https for additional HTTPS configuration details. upstream gitlab { - server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; + server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; } -## This is a normal HTTP host which redirects all traffic to the HTTPS host. +## Normal HTTP host 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; + rewrite ^ https://$server_name$request_uri? permanent; } +## HTTPS host server { listen 443 ssl; server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com @@ -74,7 +65,7 @@ server { ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate_key /etc/nginx/ssl/gitlab.key; - 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:!CAMELLIA:!DES:!MD5:!PSK:!RC4'; + ssl_ciphers 'AES256+EECDH:AES256+EDH'; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_session_cache builtin:1000 shared:SSL:10m; @@ -82,9 +73,26 @@ server { ssl_prefer_server_ciphers on; add_header Strict-Transport-Security max-age=63072000; - add_header X-Frame-Options DENY; + add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; + ## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL. + ## Replace with your ssl_trusted_certificate. For more info see: + ## - https://medium.com/devops-programming/4445f4862461 + ## - https://www.ruby-forum.com/topic/4419319 + ## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx + # ssl_stapling on; + # 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; + + ## [Optional] Generate a stronger DHE parameter: + ## cd /etc/ssl/certs + ## sudo openssl dhparam -out dhparam.pem 4096 + ## + # ssl_dhparam /etc/ssl/certs/dhparam.pem; + ## Individual nginx logs for this GitLab vhost access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; @@ -96,10 +104,9 @@ server { } ## If a file, which is not found in the root folder is requested, - ## then the proxy pass the request to the upsteam (gitlab unicorn). + ## then the proxy passes the request to the upsteam (gitlab unicorn). location @gitlab { - - ## If you use https make sure you disable gzip compression + ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. gzip off; @@ -121,7 +128,7 @@ server { ## Enable gzip compression as per rails guide: ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression - ## WARNING: If you are using relative urls do remove the block below + ## WARNING: If you are using relative urls remove the block below ## See config/application.rb under "Relative url support" for the list of ## other files that need to be changed for relative url support location ~ ^/(assets)/ { diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 032ed5ee370..9ea5c55abd6 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -322,7 +322,7 @@ namespace :gitlab do "core.autocrlf" => "input" } correct_options = options.map do |name, value| - run(%W(git config --global --get #{name})).try(:squish) == value + run(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value end if correct_options.all? @@ -330,9 +330,9 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("git config --global user.name \"#{options["user.name"]}\""), - sudo_gitlab("git config --global user.email \"#{options["user.email"]}\""), - sudo_gitlab("git config --global core.autocrlf \"#{options["core.autocrlf"]}\"") + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.name \"#{options["user.name"]}\""), + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.email \"#{options["user.email"]}\""), + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"") ) for_more_information( see_installation_guide_section "GitLab" @@ -541,7 +541,7 @@ namespace :gitlab do "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" ) for_more_information( - "#{gitlab_shell_path}/support/rewrite-hooks.sh" + "#{gitlab_shell_path}/bin/create-hooks" ) fix_and_rerun next @@ -556,7 +556,7 @@ namespace :gitlab do "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" ) for_more_information( - "lib/support/rewrite-hooks.sh" + "#{gitlab_shell_path}/bin/create-hooks" ) fix_and_rerun end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index ff27e6a3066..ece3ad58385 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -8,7 +8,7 @@ namespace :gitlab do args.with_defaults(tag: 'v' + default_version, repo: "https://gitlab.com/gitlab-org/gitlab-shell.git") user = Settings.gitlab.user - home_dir = Settings.gitlab.user_home + home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Settings.gitlab.user_home gitlab_url = Settings.gitlab.url # gitlab-shell requires a / at the end of the url gitlab_url += "/" unless gitlab_url.match(/\/$/) diff --git a/lib/tasks/gitlab/sidekiq.rake b/lib/tasks/gitlab/sidekiq.rake new file mode 100644 index 00000000000..7e2a6668e59 --- /dev/null +++ b/lib/tasks/gitlab/sidekiq.rake @@ -0,0 +1,47 @@ +namespace :gitlab do + namespace :sidekiq do + QUEUE = 'queue:post_receive' + + desc 'Drop all Sidekiq PostReceive jobs for a given project' + task :drop_post_receive , [:project] => :environment do |t, args| + unless args.project.present? + abort "Please specify the project you want to drop PostReceive jobs for:\n rake gitlab:sidekiq:drop_post_receive[group/project]" + end + project_path = Project.find_with_namespace(args.project).repository.path_to_repo + + Sidekiq.redis do |redis| + unless redis.exists(QUEUE) + abort "Queue #{QUEUE} is empty" + end + + temp_queue = "#{QUEUE}_#{Time.now.to_i}" + redis.rename(QUEUE, temp_queue) + + # At this point, then post_receive queue is empty. It may be receiving + # new jobs already. We will repopulate it with the old jobs, skipping the + # ones we want to drop. + dropped = 0 + while (job = redis.lpop(temp_queue)) do + if repo_path(job) == project_path + dropped += 1 + else + redis.rpush(QUEUE, job) + end + end + # The temp_queue will delete itself after we have popped all elements + # from it + + puts "Dropped #{dropped} jobs containing #{project_path} from #{QUEUE}" + end + end + + def repo_path(job) + job_args = JSON.parse(job)['args'] + if job_args + job_args.first + else + nil + end + end + end +end |