diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 59a4d1f..e48dfe7 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -103,6 +103,10 @@ production: &base bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' allow_username_or_email_login: true + mapping: + name: 'cn' + username: 'uid' + email: 'mail' ## OmniAuth settings omniauth: @@ -131,7 +135,14 @@ production: &base # - { name: 'github', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET' } - + ## User Mapping Procs + # These procs allow for custom mapping of user information from LDAP / Omniauth + # onto your user model. + # + # user_mapping: + # name: ->(auth) { auth.info.name.to_s } + # username: ->(auth) { auth.info.email.to_s.downcase.match(/^[^@]*/)[0] } + # email: ->(auth) { auth.info.email.to_s.downcase } # # 3. Advanced settings diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index e0207c6..c079cc4 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -36,6 +36,7 @@ end # Default settings Settings['ldap'] ||= Settingslogic.new({}) +Settings.ldap['mapping'] ||= Settingslogic.new({}) Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['allow_username_or_email_login'] = false if Settings.ldap['allow_username_or_email_login'].nil? diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index de70c5c..bea8191 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -33,13 +33,14 @@ module Gitlab def create_from_omniauth(auth, ldap = false) provider = auth.provider uid = auth.info.uid || auth.uid - uid = uid.to_s.force_encoding("utf-8") - name = auth.info.name.to_s.force_encoding("utf-8") - email = auth.info.email.to_s.downcase unless auth.info.email.nil? + uid = uid.to_s.force_encoding('utf-8') + name = extract(:name, auth, ldap).force_encoding('utf-8') + username = extract(:username, auth, ldap).force_encoding('utf-8') + email = extract(:email, auth, ldap).force_encoding('utf-8') ldap_prefix = ldap ? '(LDAP) ' : '' raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"\ - " address" if auth.info.email.blank? + " address" if email.blank? log.info "#{ldap_prefix}Creating user from #{provider} login"\ " {uid => #{uid}, name => #{name}, email => #{email}}" @@ -48,7 +49,7 @@ module Gitlab extern_uid: uid, provider: provider, name: name, - username: email.match(/^[^@]*/)[0], + username: username, email: email, password: password, password_confirmation: password, @@ -101,5 +102,31 @@ module Gitlab def ldap_conf @ldap_conf ||= Gitlab.config.ldap end + + private + + def extract(field_name, auth, ldap = false) + @ldap_mapper ||= Gitlab.config.ldap.mapping rescue Hash.new + @mapper ||= begin + defaults = { + name: ->(auth) { auth.info.name.to_s }, + username: ->(auth) { auth.info.email.to_s.downcase.match(/^[^@]*/)[0] }, + email: ->(auth) { auth.info.email.to_s.downcase }, + } + extras = Gitlab.config.user_mapping rescue Hash.new + defaults.merge(extras) + end + + if ldap && !@ldap_mapper[field_name].nil? + begin + ldap_attr = @ldap_mapper[field_name].to_sym + Array(auth.extra.raw_info[ldap_attr])[0] + rescue + raise "(LDAP) Failed to get '#{@ldap_mapper[field_name]}' for #{field_name} for #{auth.info.uid}" + end + else + @mapper[field_name].call(auth) + end + end end end diff --git a/spec/lib/auth_spec.rb b/spec/lib/auth_spec.rb index 903d276..cf9e7b7 100644 --- a/spec/lib/auth_spec.rb +++ b/spec/lib/auth_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Auth do let(:gl_auth) { Gitlab::Auth.new } before do - Gitlab.config.stub(omniauth: {}) + Gitlab.config.stub(omniauth: {}, ldap: {}) @info = mock( uid: '12djsak321', @@ -74,9 +74,22 @@ describe Gitlab::Auth do end describe :create_from_omniauth do + before do + @raw_info = { + cn: 'John Black', + mail: [ 'john.black@email.com' ], + nickname: 'jonny' + } + + @ldap_auth = mock( + info: @info, + extra: mock(raw_info: @raw_info), + provider: 'ldap' + ) + end + it "should create user from LDAP" do - @auth = mock(info: @info, provider: 'ldap') - user = gl_auth.create_from_omniauth(@auth, true) + user = gl_auth.create_from_omniauth(@ldap_auth, true) user.should be_valid user.extern_uid.should == @info.uid @@ -92,9 +105,59 @@ describe Gitlab::Auth do user.provider.should == 'twitter' end + it "should still import without extra mapping" do + Gitlab.config.stub(omniauth: {}) + user = gl_auth.create_from_omniauth(@ldap_auth, true) + + user.should be_valid + user.extern_uid.should == @info.uid + user.provider.should == 'ldap' + end + + it "should have user details from procs" do + Gitlab.config.stub(omniauth: {}, user_mapping: {}) + Gitlab.config.user_mapping[:name] = ->(auth) { 'TestName' } + Gitlab.config.user_mapping[:email] = ->(auth) { 'email@somewhere.com' } + Gitlab.config.user_mapping[:username] = ->(auth) { 'TestUsername' } + + user = gl_auth.create_from_omniauth(@ldap_auth, true) + user.should be_valid + user.extern_uid.should == @info.uid + user.name.should == 'TestName' + user.email.should == 'email@somewhere.com' + user.username.should == 'TestUsername' + end + + it "should modify value using proc" do + Gitlab.config.stub(omniauth: {}, user_mapping: {}) + Gitlab.config.user_mapping[:username] = ->(auth) { auth.info.email.to_s.downcase.split('@').first } + + user = gl_auth.create_from_omniauth(@ldap_auth, true) + user.should be_valid + user.extern_uid.should == @info.uid + user.username.should == 'john' + end + + it "should be able to use raw ldap information through simple ldap mapping" do + Gitlab.config.ldap.stub(mapping: { name: 'cn', email: 'mail' }) + + user = gl_auth.create_from_omniauth(@ldap_auth, true) + user.should be_valid + user.extern_uid.should == @info.uid + user.name.should == 'John Black' + user.email.should == 'john.black@email.com' + end + + it "should raise an error if an invalid field is in ldap mapping" do + Gitlab.config.ldap.stub(mapping: { name: 'invalid' }) + + expect { + gl_auth.create_from_omniauth(@ldap_auth, true) + }.to raise_error + end + it "should apply defaults to user" do - @auth = mock(info: @info, provider: 'ldap') - user = gl_auth.create_from_omniauth(@auth, true) + user = gl_auth.create_from_omniauth(@ldap_auth, true) user.should be_valid user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit