diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/blacklist.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/diff/file.rb | 49 | ||||
-rw-r--r-- | lib/gitlab/diff/line.rb | 12 | ||||
-rw-r--r-- | lib/gitlab/diff/line_code.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/diff/parser.rb | 81 | ||||
-rw-r--r-- | lib/gitlab/diff_parser.rb | 83 | ||||
-rw-r--r-- | lib/gitlab/ldap/user.rb | 74 | ||||
-rw-r--r-- | lib/gitlab/oauth/auth_hash.rb | 54 | ||||
-rw-r--r-- | lib/gitlab/oauth/user.rb | 134 | ||||
-rw-r--r-- | lib/gitlab/project_search_results.rb | 23 | ||||
-rw-r--r-- | lib/gitlab/snippet_search_results.rb | 131 |
11 files changed, 445 insertions, 206 deletions
diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb index 65efb6e4407..43145e0ee1b 100644 --- a/lib/gitlab/blacklist.rb +++ b/lib/gitlab/blacklist.rb @@ -27,6 +27,7 @@ module Gitlab notes unsubscribes all + ci ) 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/ldap/user.rb b/lib/gitlab/ldap/user.rb index e6aa3890992..25b5a702f9a 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -10,37 +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) + 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 - 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 = 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 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 + 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) @@ -48,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 ) @@ -66,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) @@ -88,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/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 9670aad2c5d..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 - auth.uid.to_s - end + # Instance methods + attr_accessor :auth_hash, :user - def email - return unless auth.info.respond_to?(: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 - return unless auth.info.respond_to?(:nickname) - 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 index 90511662b20..9dc8b34d9c7 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -14,13 +14,16 @@ module Gitlab 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 + @total_count ||= issues_count + merge_requests_count + blobs_count + + notes_count + wiki_blobs_count end def blobs_count @@ -31,6 +34,10 @@ module Gitlab @notes_count ||= notes.count end + def wiki_blobs_count + @wiki_blobs_count ||= wiki_blobs.count + end + private def blobs @@ -41,6 +48,20 @@ module Gitlab 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 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 |