From 4d2f36118a3b21b6bb4ee702b98d032967ba463a Mon Sep 17 00:00:00 2001 From: Joel Koglin Date: Tue, 21 Jul 2015 14:03:26 -0700 Subject: [PATCH 01/18] Issue #993: Fixed login failure when extern_uid changes --- CHANGELOG | 1 + app/models/user.rb | 2 +- ...50817163600_deduplicate_user_identities.rb | 14 ++++++++++++ db/schema.rb | 2 +- lib/gitlab/ldap/user.rb | 10 ++++++--- spec/lib/gitlab/ldap/user_spec.rb | 22 +++++++++++++++++++ 6 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20150817163600_deduplicate_user_identities.rb diff --git a/CHANGELOG b/CHANGELOG index 6cbe51f1fb..53c7463ae7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -75,6 +75,7 @@ v 7.14.0 (unreleased) - Update Flowdock integration to support new Flowdock API (Boyan Tabakov) - Remove author from files view (Sven Strickroth) - Fix infinite loop when SAML was incorrectly configured. + - Fixed login failure when extern_uid changes (Joel Koglin) v 7.13.5 - Satellites reverted diff --git a/app/models/user.rb b/app/models/user.rb index f70761074c..1f0735a7e7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -104,7 +104,7 @@ class User < ActiveRecord::Base # Profile has_many :keys, dependent: :destroy has_many :emails, dependent: :destroy - has_many :identities, dependent: :destroy + has_many :identities, dependent: :destroy, autosave: true # Groups has_many :members, dependent: :destroy diff --git a/db/migrate/20150817163600_deduplicate_user_identities.rb b/db/migrate/20150817163600_deduplicate_user_identities.rb new file mode 100644 index 0000000000..fab669c290 --- /dev/null +++ b/db/migrate/20150817163600_deduplicate_user_identities.rb @@ -0,0 +1,14 @@ +class DeduplicateUserIdentities < ActiveRecord::Migration + def change + execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;' + execute 'CREATE TEMPORARY TABLE tt_migration_DeduplicateUserIdentities AS SELECT id,provider,user_id FROM identities;' + execute 'DELETE FROM identities WHERE id NOT IN ( SELECT MIN(id) FROM tt_migration_DeduplicateUserIdentities GROUP BY user_id, provider);' + execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;' + end + + def down + # This is an irreversible migration; + # If someone is trying to rollback for other reasons, we should not throw an Exception. + # raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/schema.rb b/db/schema.rb index fdf09b3afd..7a0c92bddc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150812080800) do +ActiveRecord::Schema.define(version: 20150817163600) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index f7f3ba9ad7..2ffb124196 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -44,9 +44,13 @@ module Gitlab gl_user.skip_reconfirmation! gl_user.email = auth_hash.email - # Build new identity only if we dont have have same one - gl_user.identities.find_or_initialize_by(provider: auth_hash.provider, - extern_uid: auth_hash.uid) + # If we don't have an identity for this provider yet, create one + if gl_user.identities.find_by(provider: auth_hash.provider).nil? + gl_user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider) + else # Update the UID attribute for this provider in case it has changed + identity = gl_user.identities.select { |identity| identity.provider == auth_hash.provider } + identity.first.extern_uid = auth_hash.uid + end gl_user end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 7cfca96f4e..84d9fb54b6 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -47,6 +47,28 @@ describe Gitlab::LDAP::User do expect(existing_user.ldap_identity.provider).to eql 'ldapmain' end + it 'connects to existing ldap user if the extern_uid changes' do + existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain') + expect{ ldap_user.save }.not_to change{ User.count } + + existing_user.reload + expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid' + expect(existing_user.ldap_identity.provider).to eql 'ldapmain' + expect(existing_user.id).to eql ldap_user.gl_user.id + end + + it 'maintains an identity per provider' do + existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') + expect(existing_user.identities.count).to eql(1) + + ldap_user.save + expect(ldap_user.gl_user.identities.count).to eql(2) + + # Expect that find_by provider only returns a single instance of an identity and not an Enumerable + expect(ldap_user.gl_user.identities.find_by(provider: 'twitter')).to be_instance_of Identity + expect(ldap_user.gl_user.identities.find_by(provider: auth_hash.provider)).to be_instance_of Identity + end + it "creates a new user if not found" do expect{ ldap_user.save }.to change{ User.count }.by(1) end From 4344b8d2d4367b19c6849c3cab0d02d17ddf2304 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 25 Aug 2015 14:31:33 -0700 Subject: [PATCH 02/18] Add Gitlab::ColorSchemes module Very similar to Gitlab::Theme, this contains all of the definitions for our syntax highlighting schemes. --- app/helpers/preferences_helper.rb | 25 +------- app/views/profiles/preferences/show.html.haml | 8 +-- lib/gitlab/color_schemes.rb | 62 +++++++++++++++++++ spec/lib/gitlab/color_schemes_spec.rb | 45 ++++++++++++++ spec/lib/gitlab/themes_spec.rb | 3 - 5 files changed, 113 insertions(+), 30 deletions(-) create mode 100644 lib/gitlab/color_schemes.rb create mode 100644 spec/lib/gitlab/color_schemes_spec.rb diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index ea774e28ec..b294ddc9d7 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -1,25 +1,5 @@ # Helper methods for per-User preferences module PreferencesHelper - COLOR_SCHEMES = { - 1 => 'white', - 2 => 'dark', - 3 => 'solarized-light', - 4 => 'solarized-dark', - 5 => 'monokai', - } - COLOR_SCHEMES.default = 'white' - - # Helper method to access the COLOR_SCHEMES - # - # The keys are the `color_scheme_ids` - # The values are the `name` of the scheme. - # - # The preview images are `name-scheme-preview.png` - # The stylesheets should use the css class `.name` - def color_schemes - COLOR_SCHEMES.freeze - end - # Maps `dashboard` values to more user-friendly option text DASHBOARD_CHOICES = { projects: 'Your Projects (default)', @@ -50,12 +30,11 @@ module PreferencesHelper end def user_application_theme - theme = Gitlab::Themes.by_id(current_user.try(:theme_id)) - theme.css_class + Gitlab::Themes.by_id(current_user.try(:theme_id)).css_class end def user_color_scheme_class - COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) + Gitlab::ColorSchemes.by_id(current_user.try(:color_scheme_id)).css_class end def prefer_readme? diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 1134317ee0..aa0361a0a1 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -22,11 +22,11 @@ .panel-heading Syntax highlighting theme .panel-body - - color_schemes.each do |color_scheme_id, color_scheme| + - Gitlab::ColorSchemes.each do |scheme| = label_tag do - .preview= image_tag "#{color_scheme}-scheme-preview.png" - = f.radio_button :color_scheme_id, color_scheme_id - = color_scheme.tr('-_', ' ').titleize + .preview= image_tag "#{scheme.css_class}-scheme-preview.png" + = f.radio_button :color_scheme_id, scheme.id + = scheme.name .panel.panel-default .panel-heading diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb new file mode 100644 index 0000000000..763853ab1c --- /dev/null +++ b/lib/gitlab/color_schemes.rb @@ -0,0 +1,62 @@ +module Gitlab + # Module containing GitLab's syntax color scheme definitions and helper + # methods for accessing them. + module ColorSchemes + # Struct class representing a single Scheme + Scheme = Struct.new(:id, :name, :css_class) + + SCHEMES = [ + Scheme.new(1, 'White', 'white'), + Scheme.new(2, 'Dark', 'dark'), + Scheme.new(3, 'Solarized Light', 'solarized-light'), + Scheme.new(4, 'Solarized Dark', 'solarized-dark'), + Scheme.new(5, 'Monokai', 'monokai') + ].freeze + + # Convenience method to get a space-separated String of all the color scheme + # classes that might be applied to a code block. + # + # Returns a String + def self.body_classes + SCHEMES.collect(&:css_class).uniq.join(' ') + end + + # Get a Scheme by its ID + # + # If the ID is invalid, returns the default Scheme. + # + # id - Integer ID + # + # Returns a Scheme + def self.by_id(id) + SCHEMES.detect { |s| s.id == id } || default + end + + # Get the default Scheme + # + # Returns a Scheme + def self.default + by_id(1) + end + + # Iterate through each Scheme + # + # Yields the Scheme object + def self.each(&block) + SCHEMES.each(&block) + end + + # Get the Scheme for the specified user, or the default + # + # user - User record + # + # Returns a Scheme + def self.for_user(user) + if user + by_id(user.color_scheme_id) + else + default + end + end + end +end diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb new file mode 100644 index 0000000000..c7be45dbcd --- /dev/null +++ b/spec/lib/gitlab/color_schemes_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe Gitlab::ColorSchemes do + describe '.body_classes' do + it 'returns a space-separated list of class names' do + css = described_class.body_classes + + expect(css).to include('white') + expect(css).to include(' solarized-light ') + expect(css).to include(' monokai') + end + end + + describe '.by_id' do + it 'returns a scheme by its ID' do + expect(described_class.by_id(1).name).to eq 'White' + expect(described_class.by_id(4).name).to eq 'Solarized Dark' + end + end + + describe '.default' do + it 'returns the default scheme' do + expect(described_class.default.id).to eq 1 + end + end + + describe '.each' do + it 'passes the block to the SCHEMES Array' do + ids = [] + described_class.each { |scheme| ids << scheme.id } + expect(ids).not_to be_empty + end + end + + describe '.for_user' do + it 'returns default when user is nil' do + expect(described_class.for_user(nil).id).to eq 1 + end + + it "returns user's preferred color scheme" do + user = double(color_scheme_id: 5) + expect(described_class.for_user(user).id).to eq 5 + end + end +end diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index 9c6c3fd810..e554458e41 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -43,9 +43,6 @@ describe Gitlab::Themes do ids = [] described_class.each { |theme| ids << theme.id } expect(ids).not_to be_empty - - # TODO (rspeicher): RSpec 3.x - # expect(described_class.each).to yield_with_arg(described_class::Theme) end end end From 59180c4f5a553938ee79968e7983aee6ce584ff5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 25 Aug 2015 15:23:41 -0700 Subject: [PATCH 03/18] Add highlight_word style definition to each color scheme Now we don't need to override the user color scheme on search results. They'll look good with any of them. --- app/assets/stylesheets/generic/common.scss | 4 ---- app/assets/stylesheets/highlight/dark.scss | 6 ++++++ app/assets/stylesheets/highlight/monokai.scss | 6 ++++++ app/assets/stylesheets/highlight/solarized_dark.scss | 5 +++++ app/assets/stylesheets/highlight/solarized_light.scss | 5 +++++ app/assets/stylesheets/highlight/white.scss | 5 +++++ 6 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index bf5c7a8d75..7281b26179 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -132,10 +132,6 @@ p.time { text-shadow: none; } -.highlight_word { - background: #fafe3d; -} - .thin_area{ height: 150px; } diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index c8cb18ec35..8323a8598e 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -21,6 +21,12 @@ pre.code.highlight.dark, background-color: #557 !important; } + // Search result highlight + span.highlight_word { + background: #ffe792; + color: #000000; + } + .hll { background-color: #373b41 } .c { color: #969896 } /* Comment */ .err { color: #cc6666 } /* Error */ diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 001e8b3102..e838167433 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -21,6 +21,12 @@ pre.code.monokai, background-color: #49483e !important; } + // Search result highlight + span.highlight_word { + background: #ffe792; + color: #000000; + } + .hll { background-color: #49483e } .c { color: #75715e } /* Comment */ .err { color: #960050; background-color: #1e0010 } /* Error */ diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index f5b827e7c0..bd41480aef 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -21,6 +21,11 @@ pre.code.highlight.solarized-dark, background-color: #174652 !important; } + // Search result highlight + span.highlight_word { + background: #094554; + } + /* Solarized Dark For use with Jekyll and Pygments diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 6b44c00c30..4cc6286387 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -21,6 +21,11 @@ pre.code.highlight.solarized-light, background-color: #ddd8c5 !important; } + // Search result highlight + span.highlight_word { + background: #eee8d5; + } + /* Solarized Light For use with Jekyll and Pygments diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index a52ffc971d..e0edfb80b4 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -21,6 +21,11 @@ pre.code.highlight.white, background-color: #f8eec7 !important; } + // Search result highlight + span.highlight_word { + background: #fafe3d; + } + .hll { background-color: #f8f8f8 } .c { color: #999988; font-style: italic; } .err { color: #a61717; background-color: #e3d2d2; } From 2c3e42e4a44e2f40e521cbafc8144e5d7c366b76 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 25 Aug 2015 15:24:53 -0700 Subject: [PATCH 04/18] Remove user_color_scheme_class Instead of rendering this value server-side, we use Javascript and Gon to apply the user's color scheme (or the default) to any syntax highlighted code blocks. This will make it easier to cache these blocks in the future because they're no longer state-dependent. --- .../javascripts/syntax_highlight.coffee | 9 ++++++++ app/controllers/application_controller.rb | 9 ++++---- app/helpers/gitlab_markdown_helper.rb | 2 +- app/helpers/preferences_helper.rb | 4 ---- app/views/search/results/_blob.html.haml | 2 +- .../search/results/_snippet_blob.html.haml | 2 +- app/views/search/results/_wiki_blob.html.haml | 2 +- app/views/shared/_file_highlight.html.haml | 2 +- lib/redcarpet/render/gitlab_html.rb | 5 ++--- spec/features/markdown_spec.rb | 4 ---- spec/helpers/preferences_helper_spec.rb | 21 ------------------- 11 files changed, 21 insertions(+), 41 deletions(-) create mode 100644 app/assets/javascripts/syntax_highlight.coffee diff --git a/app/assets/javascripts/syntax_highlight.coffee b/app/assets/javascripts/syntax_highlight.coffee new file mode 100644 index 0000000000..510f15d1b4 --- /dev/null +++ b/app/assets/javascripts/syntax_highlight.coffee @@ -0,0 +1,9 @@ +# Applies a syntax highlighting color scheme CSS class to any element with the +# `js-syntax-highlight` class +# +# ### Example Markup +# +#
+# +$(document).on 'ready page:load', -> + $('.js-syntax-highlight').addClass(gon.user_color_scheme) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 12d439b0b3..b51a9600c1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -190,11 +190,12 @@ class ApplicationController < ActionController::Base end def add_gon_variables + gon.api_version = API::API.version + gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s gon.default_issues_tracker = Project.new.default_issue_tracker.to_param - gon.api_version = API::API.version - gon.relative_url_root = Gitlab.config.gitlab.relative_url_root - gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s - gon.max_file_size = current_application_settings.max_attachment_size; + gon.max_file_size = current_application_settings.max_attachment_size + gon.relative_url_root = Gitlab.config.gitlab.relative_url_root + gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class if current_user gon.current_user_id = current_user.id diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index eb3f72a307..114730eb94 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -58,7 +58,7 @@ module GitlabMarkdownHelper @options = options # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch - rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options) + rend = Redcarpet::Render::GitlabHTML.new(self, options) # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS) diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index b294ddc9d7..51a6b3fa99 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -33,10 +33,6 @@ module PreferencesHelper Gitlab::Themes.by_id(current_user.try(:theme_id)).css_class end - def user_color_scheme_class - Gitlab::ColorSchemes.by_id(current_user.try(:color_scheme_id)).css_class - end - def prefer_readme? !current_user || current_user.project_view == 'readme' diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 58f58eff54..0fe8a3b490 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -7,4 +7,4 @@ %strong = blob.filename .file-content.code.term - = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, user_color_scheme_class: 'white' + = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 9509985391..19a7bfefef 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -23,7 +23,7 @@ .nothing-here-block Empty file - else .file-content.code - %div.highlighted-data{class: user_color_scheme_class} + %div.highlighted-data.js-syntax-highlight .line-numbers - snippet_blob[:snippet_chunks].each do |snippet| - unless snippet[:data].empty? diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index c03438eb95..f5859481d4 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -7,4 +7,4 @@ %strong = wiki_blob.filename .file-content.code.term - = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline, user_color_scheme_class: 'white' + = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index d6a2e177da..7b1c624d76 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,4 +1,4 @@ -.file-content.code{class: user_color_scheme_class} +.file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? - blob.data.lines.each_index do |index| diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index f57b56cbdf..9cb8e91d6e 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -4,9 +4,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML attr_reader :template alias_method :h, :template - def initialize(template, color_scheme, options = {}) + def initialize(template, options = {}) @template = template - @color_scheme = color_scheme @options = options.dup @options.reverse_merge!( @@ -35,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML end formatter = Rouge::Formatters::HTMLGitlab.new( - cssclass: "code highlight #{@color_scheme} #{lexer.tag}" + cssclass: "code highlight js-syntax-highlight #{lexer.tag}" ) formatter.format(lexer.lex(code)) end diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 3da4dfc2b2..d32bd4adb5 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -224,8 +224,4 @@ describe 'GitLab Markdown', feature: true do def current_user @feat.user end - - def user_color_scheme_class - :white - end end diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index d814b56211..b4dee272bf 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -48,25 +48,4 @@ describe PreferencesHelper do ] end end - - describe 'user_color_scheme_class' do - context 'with current_user is nil' do - it 'should return a string' do - allow(self).to receive(:current_user).and_return(nil) - expect(user_color_scheme_class).to be_kind_of(String) - end - end - - context 'with a current_user' do - (1..5).each do |color_scheme_id| - context "with color_scheme_id == #{color_scheme_id}" do - it 'should return a string' do - current_user = double(color_scheme_id: color_scheme_id) - allow(self).to receive(:current_user).and_return(current_user) - expect(user_color_scheme_class).to be_kind_of(String) - end - end - end - end - end end From c401543d02487888a71966518fa8cb4f17de1397 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 25 Aug 2015 15:57:55 -0700 Subject: [PATCH 05/18] Fix failing spec --- spec/features/markdown_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index d32bd4adb5..4fe019f834 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -64,8 +64,8 @@ describe 'GitLab Markdown', feature: true do it 'parses fenced code blocks' do aggregate_failures do - expect(doc).to have_selector('pre.code.highlight.white.c') - expect(doc).to have_selector('pre.code.highlight.white.python') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') end end From d6a145d4b0b8d2baa63f2c1699ba44ea203a4ae8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 25 Aug 2015 16:26:20 -0700 Subject: [PATCH 06/18] Fix two more failing specs --- spec/helpers/events_helper_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index da58ab9846..e68a5ec29a 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -28,8 +28,7 @@ describe EventsHelper do it 'should display the first line of a code block' do input = "```\nCode block\nwith two lines\n```" - expected = '
' \
-               'Code block...
' + expected = %r{Code block\.\.\.} expect(event_note(input)).to match(expected) end @@ -55,7 +54,7 @@ describe EventsHelper do it 'should preserve code color scheme' do input = "```ruby\ndef test\n 'hello world'\nend\n```" - expected = '
' \
+    expected = '
' \
       "def test\n" \
       "  \'hello world\'\n" \
       "end" \

From 6d43308b5a5ff7663e1782fd6f5493f48c0f7e2a Mon Sep 17 00:00:00 2001
From: Robert Speicher 
Date: Wed, 26 Aug 2015 11:30:11 -0700
Subject: [PATCH 07/18] Add `Gitlab::Themes.for_user`

---
 lib/gitlab/themes.rb | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index 5209df9279..37a36b9599 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -51,6 +51,19 @@ module Gitlab
       THEMES.each(&block)
     end
 
+    # Get the Theme for the specified user, or the default
+    #
+    # user - User record
+    #
+    # Returns a Theme
+    def self.for_user(user)
+      if user
+        by_id(user.theme_id)
+      else
+        default
+      end
+    end
+
     private
 
     def self.default_id

From 2d72efcd9fb63f4bb0c6aed5a472a2eca2d6abac Mon Sep 17 00:00:00 2001
From: Robert Speicher 
Date: Wed, 26 Aug 2015 11:30:38 -0700
Subject: [PATCH 08/18] Add `count` to Themes and ColorSchemes

---
 lib/gitlab/color_schemes.rb | 5 +++++
 lib/gitlab/themes.rb        | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb
index 763853ab1c..9c4664df90 100644
--- a/lib/gitlab/color_schemes.rb
+++ b/lib/gitlab/color_schemes.rb
@@ -32,6 +32,11 @@ module Gitlab
       SCHEMES.detect { |s| s.id == id } || default
     end
 
+    # Returns the number of defined Schemes
+    def self.count
+      SCHEMES.size
+    end
+
     # Get the default Scheme
     #
     # Returns a Scheme
diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index 37a36b9599..83f91de810 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -37,6 +37,11 @@ module Gitlab
       THEMES.detect { |t| t.id == id } || default
     end
 
+    # Returns the number of defined Themes
+    def self.count
+      THEMES.size
+    end
+
     # Get the default Theme
     #
     # Returns a Theme

From 7a81dc65d9013dff4d8bf36cf78e027693703990 Mon Sep 17 00:00:00 2001
From: Robert Speicher 
Date: Wed, 26 Aug 2015 11:33:44 -0700
Subject: [PATCH 09/18] Re-add user_color_scheme helper

Update PreferencesHelper specs
---
 app/helpers/preferences_helper.rb       |  6 +-
 spec/helpers/preferences_helper_spec.rb | 95 ++++++++++++++++---------
 2 files changed, 68 insertions(+), 33 deletions(-)

diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 51a6b3fa99..7f1b6a6992 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -30,7 +30,11 @@ module PreferencesHelper
   end
 
   def user_application_theme
-    Gitlab::Themes.by_id(current_user.try(:theme_id)).css_class
+    Gitlab::Themes.for_user(current_user).css_class
+  end
+
+  def user_color_scheme
+    Gitlab::ColorSchemes.for_user(current_user).css_class
   end
 
   def prefer_readme?
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index b4dee272bf..06f69262b7 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -1,51 +1,82 @@
 require 'spec_helper'
 
 describe PreferencesHelper do
-  describe 'user_application_theme' do
-    context 'with a user' do
-      it "returns user's theme's css_class" do
-        user = double('user', theme_id: 3)
-        allow(self).to receive(:current_user).and_return(user)
-        expect(user_application_theme).to eq 'ui_green'
-      end
-
-      it 'returns the default when id is invalid' do
-        user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5)
-
-        allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
-        allow(self).to receive(:current_user).and_return(user)
-
-        expect(user_application_theme).to eq 'ui_charcoal'
-      end
-    end
-
-    context 'without a user' do
-      before do
-        allow(self).to receive(:current_user).and_return(nil)
-      end
-
-      it 'returns the default theme' do
-        expect(user_application_theme).to eq Gitlab::Themes.default.css_class
-      end
-    end
-  end
-
   describe 'dashboard_choices' do
     it 'raises an exception when defined choices may be missing' do
       expect(User).to receive(:dashboards).and_return(foo: 'foo')
-      expect { dashboard_choices }.to raise_error(RuntimeError)
+      expect { helper.dashboard_choices }.to raise_error(RuntimeError)
     end
 
     it 'raises an exception when defined choices may be using the wrong key' do
       expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
-      expect { dashboard_choices }.to raise_error(KeyError)
+      expect { helper.dashboard_choices }.to raise_error(KeyError)
     end
 
     it 'provides better option descriptions' do
-      expect(dashboard_choices).to match_array [
+      expect(helper.dashboard_choices).to match_array [
         ['Your Projects (default)', 'projects'],
         ['Starred Projects',        'stars']
       ]
     end
   end
+
+  describe 'user_application_theme' do
+    context 'with a user' do
+      it "returns user's theme's css_class" do
+        stub_user(theme_id: 3)
+
+        expect(helper.user_application_theme).to eq 'ui_green'
+      end
+
+      it 'returns the default when id is invalid' do
+        stub_user(theme_id: Gitlab::Themes.count + 5)
+
+        allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
+
+        expect(helper.user_application_theme).to eq 'ui_charcoal'
+      end
+    end
+
+    context 'without a user' do
+      it 'returns the default theme' do
+        stub_user
+
+        expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class
+      end
+    end
+  end
+
+  describe 'user_color_scheme' do
+    context 'with a user' do
+      it "returns user's scheme's css_class" do
+        allow(helper).to receive(:current_user).
+          and_return(double(color_scheme_id: 3))
+
+        expect(helper.user_color_scheme).to eq 'solarized-light'
+      end
+
+      it 'returns the default when id is invalid' do
+        allow(helper).to receive(:current_user).
+          and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
+      end
+    end
+
+    context 'without a user' do
+      it 'returns the default theme' do
+        stub_user
+
+        expect(helper.user_color_scheme).
+          to eq Gitlab::ColorSchemes.default.css_class
+      end
+    end
+  end
+
+  def stub_user(messages = {})
+    if messages.empty?
+      allow(helper).to receive(:current_user).and_return(nil)
+    else
+      allow(helper).to receive(:current_user).
+        and_return(double('user', messages))
+    end
+  end
 end

From 54771100eb8693c7fa06ea31041b0bd15f78dcc7 Mon Sep 17 00:00:00 2001
From: Robert Speicher 
Date: Wed, 26 Aug 2015 11:34:28 -0700
Subject: [PATCH 10/18] Add user_color_scheme class to file_highlight

Prevents an unstyled flash that occurs when we were only applying the
syntax highlighting class in Javascript. Now for these uncached blobs
the server can add the syntax class, and for (possibly) cached fenced
code blocks, the Javascript will add it as needed.
---
 app/views/shared/_file_highlight.html.haml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 7b1c624d76..57c3aff3e1 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,4 +1,4 @@
-.file-content.code.js-syntax-highlight
+.file-content.code.js-syntax-highlight{ class: user_color_scheme }
   .line-numbers
     - if blob.data.present?
       - blob.data.lines.each_index do |index|

From ce0a0feff45b47b6c03b9ab01d815b651840a7b2 Mon Sep 17 00:00:00 2001
From: Robert Speicher 
Date: Wed, 26 Aug 2015 11:43:25 -0700
Subject: [PATCH 11/18] Make snippet_blob highlight server-side as well

---
 app/views/search/results/_snippet_blob.html.haml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index 19a7bfefef..9a4f9fb948 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -23,7 +23,7 @@
                 .nothing-here-block Empty file
       - else
         .file-content.code
-          %div.highlighted-data.js-syntax-highlight
+          %div.highlighted-data{ class: user_color_scheme }
             .line-numbers
               - snippet_blob[:snippet_chunks].each do |snippet|
                 - unless snippet[:data].empty?

From ea5da30326a0515f98d7276c9cff239232cafed4 Mon Sep 17 00:00:00 2001
From: Douwe Maan 
Date: Wed, 26 Aug 2015 17:23:22 -0700
Subject: [PATCH 12/18] Don't notify users without access to the project when
 they are (accidentally) mentioned in a note.

---
 CHANGELOG                                  |  1 +
 app/services/notification_service.rb       | 11 ++++++++---
 spec/services/notification_service_spec.rb | 11 +++++++++--
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 9593c16c9c..bf3b7bb8b0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -19,6 +19,7 @@ v 8.0.0 (unreleased)
   - Move dashboard activity to separate page
   - Improve performance of git blame
   - Limit content width to 1200px for most of pages to improve readability on big screens
+  - Don't notify users without access to the project when they are (accidentally) mentioned in a note.
 
 v 7.14.1
   - Improve abuse reports management from admin area
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 3735a13636..e294b23bc2 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -107,12 +107,17 @@ class NotificationService
 
     recipients = []
 
+    mentioned_users = note.mentioned_users
+    mentioned_users.select! do |user|
+      user.can?(:read_project, note.project)
+    end
+
     # Add all users participating in the thread (author, assignee, comment authors)
     participants = 
       if target.respond_to?(:participants)
         target.participants(note.author)
       else
-        note.mentioned_users
+        mentioned_users
       end
     recipients = recipients.concat(participants)
 
@@ -120,8 +125,8 @@ class NotificationService
     recipients = add_project_watchers(recipients, note.project)
 
     # Reject users with Mention notification level, except those mentioned in _this_ note.
-    recipients = reject_mention_users(recipients - note.mentioned_users, note.project)
-    recipients = recipients + note.mentioned_users
+    recipients = reject_mention_users(recipients - mentioned_users, note.project)
+    recipients = recipients + mentioned_users
 
     recipients = reject_muted_users(recipients, note.project)
 
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 9da6c9dc94..8865335d0d 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -31,13 +31,16 @@ describe NotificationService do
 
   describe 'Notes' do
     context 'issue note' do
-      let(:project) { create(:empty_project, :public) }
+      let(:project) { create(:empty_project, :private) }
       let(:issue) { create(:issue, project: project, assignee: create(:user)) }
       let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
-      let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
+      let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @outsider also') }
 
       before do
         build_team(note.project)
+        project.team << [issue.author, :master]
+        project.team << [issue.assignee, :master]
+        project.team << [note.author, :master]
       end
 
       describe :new_note do
@@ -53,6 +56,7 @@ describe NotificationService do
           should_not_email(@u_participating.id)
           should_not_email(@u_disabled.id)
           should_not_email(@unsubscriber.id)
+          should_not_email(@u_outsider_mentioned)
 
           notification.new_note(note)
         end
@@ -444,12 +448,15 @@ describe NotificationService do
     @u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_MENTION)
     @u_committer = create(:user, username: 'committer')
     @u_not_mentioned = create(:user, username: 'regular', notification_level: Notification::N_PARTICIPATING)
+    @u_outsider_mentioned = create(:user, username: 'outsider')
 
     project.team << [@u_watcher, :master]
     project.team << [@u_participating, :master]
+    project.team << [@u_participant_mentioned, :master]
     project.team << [@u_disabled, :master]
     project.team << [@u_mentioned, :master]
     project.team << [@u_committer, :master]
+    project.team << [@u_not_mentioned, :master]
   end
 
   def add_users_with_subscription(project, issuable)

From ff30b407545b2b873ffbe9d5d3d603e550013cf3 Mon Sep 17 00:00:00 2001
From: Valery Sizov 
Date: Thu, 27 Aug 2015 11:21:57 +0300
Subject: [PATCH 13/18] Make all profile public

---
 CHANGELOG                           | 1 +
 app/controllers/users_controller.rb | 4 ----
 app/models/user.rb                  | 4 ----
 app/views/profiles/show.html.haml   | 5 -----
 features/user.feature               | 5 -----
 5 files changed, 1 insertion(+), 18 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 6ed1716f8a..d366e2a6f5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -19,6 +19,7 @@ v 8.0.0 (unreleased)
   - Move dashboard activity to separate page
   - Improve performance of git blame
   - Limit content width to 1200px for most of pages to improve readability on big screens
+  - Make all profiles public
 
 v 7.14.1
   - Improve abuse reports management from admin area
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2bb5c338cf..1484356a7f 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -51,10 +51,6 @@ class UsersController < ApplicationController
 
   def set_user
     @user = User.find_by_username!(params[:username])
-
-    unless current_user || @user.public_profile?
-      return authenticate_user!
-    end
   end
 
   def authorized_projects_ids
diff --git a/app/models/user.rb b/app/models/user.rb
index f70761074c..b4e779370a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -637,10 +637,6 @@ class User < ActiveRecord::Base
     email.start_with?('temp-email-for-oauth')
   end
 
-  def public_profile?
-    authorized_projects.public_only.any?
-  end
-
   def avatar_url(size = nil)
     if avatar.present?
       [gitlab_config.url, avatar.url].join
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 9fdeddfcc7..c519e52e59 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -100,11 +100,6 @@
               %hr
               = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
 
-      - if @user.public_profile?
-        .alert.alert-info
-          %h4 Public profile
-          %p Your profile is publicly visible because you joined public project(s)
-
 
   .row
     .col-md-7
diff --git a/features/user.feature b/features/user.feature
index 69618e929c..35eae842e7 100644
--- a/features/user.feature
+++ b/features/user.feature
@@ -14,11 +14,6 @@ Feature: User
     And I should not see project "Internal"
     And I should see project "Community"
 
-  Scenario: I visit user "John Doe" page while not signed in when he is not authorized to a public project
-    Given "John Doe" owns internal project "Internal"
-    When I visit user "John Doe" page
-    Then I should be redirected to sign in page
-
   # Signed in as someone else
 
   Scenario: I visit user "John Doe" page while signed in as someone else when he owns a public project

From ef6c06c0f3341d9508693930af4979659d6a4b63 Mon Sep 17 00:00:00 2001
From: Stan Hu 
Date: Thu, 27 Aug 2015 13:07:14 -0700
Subject: [PATCH 14/18] Upgrade gitlab_git to 7.2.15 to fix `git blame` errors
 with ISO-encoded files

Closes https://github.com/gitlabhq/gitlabhq/issues/9577
---
 CHANGELOG    | 1 +
 Gemfile      | 2 +-
 Gemfile.lock | 4 ++--
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 386ce7ca6d..638a6aac03 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.0.0 (unreleased)
+  - Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu)
   - Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu)
   - Improve dropdown positioning on the project home page (Hannes Rosenögger)
   - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu)
diff --git a/Gemfile b/Gemfile
index 4696a961e8..e1b9ede17b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -38,7 +38,7 @@ gem "browser", '~> 1.0.0'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.2.14'
+gem "gitlab_git", '~> 7.2.15'
 
 # Ruby/Rack Git Smart-HTTP Server Handler
 # GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
diff --git a/Gemfile.lock b/Gemfile.lock
index 814249c7ff..ff01ad1014 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -276,7 +276,7 @@ GEM
       mime-types (~> 1.19)
     gitlab_emoji (0.1.0)
       gemojione (~> 2.0)
-    gitlab_git (7.2.14)
+    gitlab_git (7.2.15)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.6)
       gitlab-linguist (~> 3.0)
@@ -790,7 +790,7 @@ DEPENDENCIES
   gitlab-grack (~> 2.0.2)
   gitlab-linguist (~> 3.0.1)
   gitlab_emoji (~> 0.1)
-  gitlab_git (~> 7.2.14)
+  gitlab_git (~> 7.2.15)
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (= 1.2.1)
   gollum-lib (~> 4.0.2)

From 23aee0ca8ad3de5697f770696f3e55fbfdba2be8 Mon Sep 17 00:00:00 2001
From: Eric Maziade 
Date: Wed, 26 Aug 2015 22:28:24 -0400
Subject: [PATCH 15/18] fixed connection detection so settings can be read from
 the database

---
 lib/gitlab/current_settings.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 1a2a50a14d..7ad3ed8728 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -4,7 +4,7 @@ module Gitlab
       key = :current_application_settings
 
       RequestStore.store[key] ||= begin
-        if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('application_settings')
+        if ActiveRecord::Base.connection.active? && ActiveRecord::Base.connection.table_exists?('application_settings')
           ApplicationSetting.current || ApplicationSetting.create_from_defaults
         else
           fake_application_settings

From d92f428024b2878682bb23b6b03bc671636b5afe Mon Sep 17 00:00:00 2001
From: Douwe Maan 
Date: Fri, 7 Aug 2015 16:09:48 +0200
Subject: [PATCH 16/18] Minor refactor

---
 lib/gitlab/ldap/user.rb | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 2ffb124196..04a2223747 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -44,13 +44,14 @@ module Gitlab
         gl_user.skip_reconfirmation!
         gl_user.email = auth_hash.email
 
-        # If we don't have an identity for this provider yet, create one
-        if gl_user.identities.find_by(provider: auth_hash.provider).nil?
-          gl_user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider)
-        else # Update the UID attribute for this provider in case it has changed
-          identity = gl_user.identities.select { |identity|  identity.provider == auth_hash.provider }
-          identity.first.extern_uid = auth_hash.uid
-        end
+        # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
+        identity = gl_user.identities.find { |identity|  identity.provider == auth_hash.provider }
+        identity ||= gl_user.identities.build(provider: auth_hash.provider)
+        
+        # For a new user set extern_uid to the LDAP DN
+        # For an existing user with matching email but changed DN, update the DN.
+        # For an existing user with no change in DN, this line changes nothing.
+        identity.extern_uid = auth_hash.uid
 
         gl_user
       end

From 12d8b01eb2df532815306db602d116e3177d750e Mon Sep 17 00:00:00 2001
From: Douwe Maan 
Date: Sat, 29 Aug 2015 11:49:59 -0700
Subject: [PATCH 17/18] Move changelog item

---
 CHANGELOG | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG b/CHANGELOG
index 4946c68390..bbf44ca14c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,7 @@ v 8.0.0 (unreleased)
   - Improve search page usability
   - Bring more UI consistency in way how projects, snippets and groups lists are rendered
   - Make all profiles public
+  - Fixed login failure when extern_uid changes (Joel Koglin)
 
 v 7.14.1
   - Improve abuse reports management from admin area
@@ -98,7 +99,6 @@ v 7.14.0
   - Update Flowdock integration to support new Flowdock API (Boyan Tabakov)
   - Remove author from files view (Sven Strickroth)
   - Fix infinite loop when SAML was incorrectly configured.
-  - Fixed login failure when extern_uid changes (Joel Koglin)
 
 v 7.13.5
   - Satellites reverted

From e334ec0683b77f46f19e1474289a9edfd2fb8c13 Mon Sep 17 00:00:00 2001
From: Achilleas Pipinellis 
Date: Mon, 31 Aug 2015 08:24:50 +0300
Subject: [PATCH 18/18] Ordering of GitHub issues import is now fixed

See https://gitlab.com/gitlab-org/gitlab-ce/commit/4a203aab57c69ea402939cf8fc423a0e5aaa1cab
---
 doc/workflow/importing/import_projects_from_github.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 3efa92cb86..2d77c6d117 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -17,4 +17,4 @@ If you want to import from a GitHub Enterprise instance, you need to use GitLab
 * To import a project, you can simple click "Add". The importer will import your repository and issues. Once the importer is done, a new GitLab project will be created with your imported data.
 
 ### Note
-When you import your projects from GitHub, it is not possible to keep your labels and milestones and issue numbers won't match. We are working on improving this in the near future.
+When you import your projects from GitHub, it is not possible to keep your labels and milestones. We are working on improving this in the near future.