Commit 767f7d9ae7eb29d5feaa65863a8948ca60d3825f

Authored by Dmitriy Zaporozhets
2 parents 92a92846 c6d39a14

Merge branch 'ldap-code' into 'master'

LDAP code from EE
app/controllers/application_controller.rb
@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base @@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
6 before_filter :check_password_expiration 6 before_filter :check_password_expiration
7 around_filter :set_current_user_for_thread 7 around_filter :set_current_user_for_thread
8 before_filter :add_abilities 8 before_filter :add_abilities
  9 + before_filter :ldap_security_check
9 before_filter :dev_tools if Rails.env == 'development' 10 before_filter :dev_tools if Rails.env == 'development'
10 before_filter :default_headers 11 before_filter :default_headers
11 before_filter :add_gon_variables 12 before_filter :add_gon_variables
@@ -179,11 +180,28 @@ class ApplicationController < ActionController::Base @@ -179,11 +180,28 @@ class ApplicationController < ActionController::Base
179 end 180 end
180 end 181 end
181 182
  183 + def ldap_security_check
  184 + if current_user && current_user.requires_ldap_check?
  185 + if gitlab_ldap_access.allowed?(current_user)
  186 + current_user.last_credential_check_at = Time.now
  187 + current_user.save
  188 + else
  189 + sign_out current_user
  190 + flash[:alert] = "Access denied for your LDAP account."
  191 + redirect_to new_user_session_path
  192 + end
  193 + end
  194 + end
  195 +
182 def event_filter 196 def event_filter
183 filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? 197 filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
184 @event_filter ||= EventFilter.new(filters) 198 @event_filter ||= EventFilter.new(filters)
185 end 199 end
186 200
  201 + def gitlab_ldap_access
  202 + Gitlab::LDAP::Access.new
  203 + end
  204 +
187 # JSON for infinite scroll via Pager object 205 # JSON for infinite scroll via Pager object
188 def pager_json(partial, count) 206 def pager_json(partial, count)
189 html = render_to_string( 207 html = render_to_string(
app/models/user.rb
@@ -185,7 +185,7 @@ class User < ActiveRecord::Base @@ -185,7 +185,7 @@ class User < ActiveRecord::Base
185 where(conditions).first 185 where(conditions).first
186 end 186 end
187 end 187 end
188 - 188 +
189 def find_for_commit(email, name) 189 def find_for_commit(email, name)
190 # Prefer email match over name match 190 # Prefer email match over name match
191 User.where(email: email).first || 191 User.where(email: email).first ||
@@ -275,7 +275,9 @@ class User < ActiveRecord::Base @@ -275,7 +275,9 @@ class User < ActiveRecord::Base
275 # Projects user has access to 275 # Projects user has access to
276 def authorized_projects 276 def authorized_projects
277 @authorized_projects ||= begin 277 @authorized_projects ||= begin
278 - project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq 278 + project_ids = personal_projects.pluck(:id)
  279 + project_ids += groups_projects.pluck(:id)
  280 + project_ids += projects.pluck(:id).uniq
279 Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC') 281 Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC')
280 end 282 end
281 end 283 end
@@ -406,6 +408,14 @@ class User < ActiveRecord::Base @@ -406,6 +408,14 @@ class User < ActiveRecord::Base
406 end 408 end
407 end 409 end
408 410
  411 + def requires_ldap_check?
  412 + if ldap_user?
  413 + !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now
  414 + else
  415 + false
  416 + end
  417 + end
  418 +
409 def solo_owned_groups 419 def solo_owned_groups
410 @solo_owned_groups ||= owned_groups.select do |group| 420 @solo_owned_groups ||= owned_groups.select do |group|
411 group.owners == [self] 421 group.owners == [self]
config/gitlab.yml.example
@@ -121,7 +121,6 @@ production: &amp;base @@ -121,7 +121,6 @@ production: &amp;base
121 ldap: 121 ldap:
122 enabled: false 122 enabled: false
123 host: '_your_ldap_server' 123 host: '_your_ldap_server'
124 - base: '_the_base_where_you_search_for_users'  
125 port: 636 124 port: 636
126 uid: 'sAMAccountName' 125 uid: 'sAMAccountName'
127 method: 'ssl' # "tls" or "ssl" or "plain" 126 method: 'ssl' # "tls" or "ssl" or "plain"
@@ -138,6 +137,20 @@ production: &amp;base @@ -138,6 +137,20 @@ production: &amp;base
138 # disable this setting, because the userPrincipalName contains an '@'. 137 # disable this setting, because the userPrincipalName contains an '@'.
139 allow_username_or_email_login: true 138 allow_username_or_email_login: true
140 139
  140 + # Base where we can search for users
  141 + #
  142 + # Ex. ou=People,dc=gitlab,dc=example
  143 + #
  144 + base: ''
  145 +
  146 + # Filter LDAP users
  147 + #
  148 + # Format: RFC 4515
  149 + # Ex. (employeeType=developer)
  150 + #
  151 + user_filter: ''
  152 +
  153 +
141 ## OmniAuth settings 154 ## OmniAuth settings
142 omniauth: 155 omniauth:
143 # Allow login via Twitter, Google, etc. using OmniAuth providers 156 # Allow login via Twitter, Google, etc. using OmniAuth providers
db/migrate/20130809124851_add_permission_check_to_user.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddPermissionCheckToUser < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :last_credential_check_at, :datetime
  4 + end
  5 +end
@@ -76,8 +76,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -76,8 +76,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
76 t.integer "assignee_id" 76 t.integer "assignee_id"
77 t.integer "author_id" 77 t.integer "author_id"
78 t.integer "project_id" 78 t.integer "project_id"
79 - t.datetime "created_at", null: false  
80 - t.datetime "updated_at", null: false 79 + t.datetime "created_at"
  80 + t.datetime "updated_at"
81 t.integer "position", default: 0 81 t.integer "position", default: 0
82 t.string "branch_name" 82 t.string "branch_name"
83 t.text "description" 83 t.text "description"
@@ -95,8 +95,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -95,8 +95,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
95 95
96 create_table "keys", force: true do |t| 96 create_table "keys", force: true do |t|
97 t.integer "user_id" 97 t.integer "user_id"
98 - t.datetime "created_at", null: false  
99 - t.datetime "updated_at", null: false 98 + t.datetime "created_at"
  99 + t.datetime "updated_at"
100 t.text "key" 100 t.text "key"
101 t.string "title" 101 t.string "title"
102 t.string "type" 102 t.string "type"
@@ -123,8 +123,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -123,8 +123,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
123 t.integer "author_id" 123 t.integer "author_id"
124 t.integer "assignee_id" 124 t.integer "assignee_id"
125 t.string "title" 125 t.string "title"
126 - t.datetime "created_at", null: false  
127 - t.datetime "updated_at", null: false 126 + t.datetime "created_at"
  127 + t.datetime "updated_at"
128 t.integer "milestone_id" 128 t.integer "milestone_id"
129 t.string "state" 129 t.string "state"
130 t.string "merge_status" 130 t.string "merge_status"
@@ -176,8 +176,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -176,8 +176,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
176 t.text "note" 176 t.text "note"
177 t.string "noteable_type" 177 t.string "noteable_type"
178 t.integer "author_id" 178 t.integer "author_id"
179 - t.datetime "created_at", null: false  
180 - t.datetime "updated_at", null: false 179 + t.datetime "created_at"
  180 + t.datetime "updated_at"
181 t.integer "project_id" 181 t.integer "project_id"
182 t.string "attachment" 182 t.string "attachment"
183 t.string "line_code" 183 t.string "line_code"
@@ -199,8 +199,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -199,8 +199,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
199 t.string "name" 199 t.string "name"
200 t.string "path" 200 t.string "path"
201 t.text "description" 201 t.text "description"
202 - t.datetime "created_at", null: false  
203 - t.datetime "updated_at", null: false 202 + t.datetime "created_at"
  203 + t.datetime "updated_at"
204 t.integer "creator_id" 204 t.integer "creator_id"
205 t.boolean "issues_enabled", default: true, null: false 205 t.boolean "issues_enabled", default: true, null: false
206 t.boolean "wall_enabled", default: true, null: false 206 t.boolean "wall_enabled", default: true, null: false
@@ -252,8 +252,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -252,8 +252,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
252 t.text "content", limit: 2147483647 252 t.text "content", limit: 2147483647
253 t.integer "author_id", null: false 253 t.integer "author_id", null: false
254 t.integer "project_id" 254 t.integer "project_id"
255 - t.datetime "created_at", null: false  
256 - t.datetime "updated_at", null: false 255 + t.datetime "created_at"
  256 + t.datetime "updated_at"
257 t.string "file_name" 257 t.string "file_name"
258 t.datetime "expires_at" 258 t.datetime "expires_at"
259 t.boolean "private", default: true, null: false 259 t.boolean "private", default: true, null: false
@@ -275,45 +275,42 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -275,45 +275,42 @@ ActiveRecord::Schema.define(version: 20140304005354) do
275 t.datetime "created_at" 275 t.datetime "created_at"
276 end 276 end
277 277
278 - add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree  
279 - add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree  
280 -  
281 create_table "tags", force: true do |t| 278 create_table "tags", force: true do |t|
282 t.string "name" 279 t.string "name"
283 end 280 end
284 281
285 create_table "users", force: true do |t| 282 create_table "users", force: true do |t|
286 - t.string "email", default: "", null: false  
287 - t.string "encrypted_password", default: "", null: false 283 + t.string "email", default: "", null: false
  284 + t.string "encrypted_password", limit: 128, default: "", null: false
288 t.string "reset_password_token" 285 t.string "reset_password_token"
289 t.datetime "reset_password_sent_at" 286 t.datetime "reset_password_sent_at"
290 t.datetime "remember_created_at" 287 t.datetime "remember_created_at"
291 - t.integer "sign_in_count", default: 0 288 + t.integer "sign_in_count", default: 0
292 t.datetime "current_sign_in_at" 289 t.datetime "current_sign_in_at"
293 t.datetime "last_sign_in_at" 290 t.datetime "last_sign_in_at"
294 t.string "current_sign_in_ip" 291 t.string "current_sign_in_ip"
295 t.string "last_sign_in_ip" 292 t.string "last_sign_in_ip"
296 - t.datetime "created_at", null: false  
297 - t.datetime "updated_at", null: false 293 + t.datetime "created_at"
  294 + t.datetime "updated_at"
298 t.string "name" 295 t.string "name"
299 - t.boolean "admin", default: false, null: false  
300 - t.integer "projects_limit", default: 10  
301 - t.string "skype", default: "", null: false  
302 - t.string "linkedin", default: "", null: false  
303 - t.string "twitter", default: "", null: false 296 + t.boolean "admin", default: false, null: false
  297 + t.integer "projects_limit", default: 10
  298 + t.string "skype", default: "", null: false
  299 + t.string "linkedin", default: "", null: false
  300 + t.string "twitter", default: "", null: false
304 t.string "authentication_token" 301 t.string "authentication_token"
305 - t.integer "theme_id", default: 1, null: false 302 + t.integer "theme_id", default: 1, null: false
306 t.string "bio" 303 t.string "bio"
307 - t.integer "failed_attempts", default: 0 304 + t.integer "failed_attempts", default: 0
308 t.datetime "locked_at" 305 t.datetime "locked_at"
309 t.string "extern_uid" 306 t.string "extern_uid"
310 t.string "provider" 307 t.string "provider"
311 t.string "username" 308 t.string "username"
312 - t.boolean "can_create_group", default: true, null: false  
313 - t.boolean "can_create_team", default: true, null: false 309 + t.boolean "can_create_group", default: true, null: false
  310 + t.boolean "can_create_team", default: true, null: false
314 t.string "state" 311 t.string "state"
315 - t.integer "color_scheme_id", default: 1, null: false  
316 - t.integer "notification_level", default: 1, null: false 312 + t.integer "color_scheme_id", default: 1, null: false
  313 + t.integer "notification_level", default: 1, null: false
317 t.datetime "password_expires_at" 314 t.datetime "password_expires_at"
318 t.integer "created_by_id" 315 t.integer "created_by_id"
319 t.string "avatar" 316 t.string "avatar"
@@ -321,15 +318,15 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -321,15 +318,15 @@ ActiveRecord::Schema.define(version: 20140304005354) do
321 t.datetime "confirmed_at" 318 t.datetime "confirmed_at"
322 t.datetime "confirmation_sent_at" 319 t.datetime "confirmation_sent_at"
323 t.string "unconfirmed_email" 320 t.string "unconfirmed_email"
324 - t.boolean "hide_no_ssh_key", default: false  
325 - t.string "website_url", default: "", null: false 321 + t.boolean "hide_no_ssh_key", default: false
  322 + t.string "website_url", default: "", null: false
  323 + t.datetime "last_credential_check_at"
326 end 324 end
327 325
328 add_index "users", ["admin"], name: "index_users_on_admin", using: :btree 326 add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
329 add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree 327 add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
330 add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree 328 add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
331 add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree 329 add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
332 - add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree  
333 add_index "users", ["name"], name: "index_users_on_name", using: :btree 330 add_index "users", ["name"], name: "index_users_on_name", using: :btree
334 add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree 331 add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
335 add_index "users", ["username"], name: "index_users_on_username", using: :btree 332 add_index "users", ["username"], name: "index_users_on_username", using: :btree
@@ -348,8 +345,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -348,8 +345,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
348 create_table "users_projects", force: true do |t| 345 create_table "users_projects", force: true do |t|
349 t.integer "user_id", null: false 346 t.integer "user_id", null: false
350 t.integer "project_id", null: false 347 t.integer "project_id", null: false
351 - t.datetime "created_at", null: false  
352 - t.datetime "updated_at", null: false 348 + t.datetime "created_at"
  349 + t.datetime "updated_at"
353 t.integer "project_access", default: 0, null: false 350 t.integer "project_access", default: 0, null: false
354 t.integer "notification_level", default: 3, null: false 351 t.integer "notification_level", default: 3, null: false
355 end 352 end
@@ -361,8 +358,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do @@ -361,8 +358,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
361 create_table "web_hooks", force: true do |t| 358 create_table "web_hooks", force: true do |t|
362 t.string "url" 359 t.string "url"
363 t.integer "project_id" 360 t.integer "project_id"
364 - t.datetime "created_at", null: false  
365 - t.datetime "updated_at", null: false 361 + t.datetime "created_at"
  362 + t.datetime "updated_at"
366 t.string "type", default: "ProjectHook" 363 t.string "type", default: "ProjectHook"
367 t.integer "service_id" 364 t.integer "service_id"
368 t.boolean "push_events", default: true, null: false 365 t.boolean "push_events", default: true, null: false
lib/api/internal.rb
@@ -35,8 +35,14 @@ module API @@ -35,8 +35,14 @@ module API
35 user = key.user 35 user = key.user
36 36
37 return false if user.blocked? 37 return false if user.blocked?
  38 +
38 if Gitlab.config.ldap.enabled 39 if Gitlab.config.ldap.enabled
39 - return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid) 40 + if user.ldap_user?
  41 + # Check if LDAP user exists and match LDAP user_filter
  42 + unless Gitlab::LDAP::Access.new.allowed?(user)
  43 + return false
  44 + end
  45 + end
40 end 46 end
41 47
42 action = case git_cmd 48 action = case git_cmd
lib/gitlab/ldap/access.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +module Gitlab
  2 + module LDAP
  3 + class Access
  4 + def allowed?(user)
  5 + !!Gitlab::LDAP::Person.find_by_dn(user.extern_uid)
  6 + rescue
  7 + false
  8 + end
  9 + end
  10 + end
  11 +end
lib/gitlab/ldap/adapter.rb 0 → 100644
@@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
  1 +module Gitlab
  2 + module LDAP
  3 + class Adapter
  4 + attr_reader :ldap
  5 +
  6 + def initialize
  7 + encryption = config['method'].to_s == 'ssl' ? :simple_tls : nil
  8 +
  9 + options = {
  10 + host: config['host'],
  11 + port: config['port'],
  12 + encryption: encryption
  13 + }
  14 +
  15 + auth_options = {
  16 + auth: {
  17 + method: :simple,
  18 + username: config['bind_dn'],
  19 + password: config['password']
  20 + }
  21 + }
  22 +
  23 + if config['password'] || config['bind_dn']
  24 + options.merge!(auth_options)
  25 + end
  26 +
  27 + @ldap = Net::LDAP.new(options)
  28 + end
  29 +
  30 + def users(field, value)
  31 + if field.to_sym == :dn
  32 + options = {
  33 + base: value
  34 + }
  35 + else
  36 + options = {
  37 + base: config['base'],
  38 + filter: Net::LDAP::Filter.eq(field, value)
  39 + }
  40 + end
  41 +
  42 + if config['user_filter'].present?
  43 + user_filter = Net::LDAP::Filter.construct(config['user_filter'])
  44 +
  45 + options[:filter] = if options[:filter]
  46 + Net::LDAP::Filter.join(options[:filter], user_filter)
  47 + else
  48 + user_filter
  49 + end
  50 + end
  51 +
  52 + entries = ldap.search(options).select do |entry|
  53 + entry.respond_to? config.uid
  54 + end
  55 +
  56 + entries.map do |entry|
  57 + Gitlab::LDAP::Person.new(entry)
  58 + end
  59 + end
  60 +
  61 + def user(*args)
  62 + users(*args).first
  63 + end
  64 +
  65 + private
  66 +
  67 + def config
  68 + @config ||= Gitlab.config.ldap
  69 + end
  70 + end
  71 + end
  72 +end
lib/gitlab/ldap/person.rb 0 → 100644
@@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
  1 +module Gitlab
  2 + module LDAP
  3 + class Person
  4 + def self.find_by_uid(uid)
  5 + Gitlab::LDAP::Adapter.new.user(config.uid, uid)
  6 + end
  7 +
  8 + def self.find_by_dn(dn)
  9 + Gitlab::LDAP::Adapter.new.user('dn', dn)
  10 + end
  11 +
  12 + def initialize(entry)
  13 + Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
  14 + @entry = entry
  15 + end
  16 +
  17 + def name
  18 + entry.cn.first
  19 + end
  20 +
  21 + def uid
  22 + entry.send(config.uid).first
  23 + end
  24 +
  25 + def username
  26 + uid
  27 + end
  28 +
  29 + def dn
  30 + entry.dn
  31 + end
  32 +
  33 + private
  34 +
  35 + def entry
  36 + @entry
  37 + end
  38 +
  39 + def adapter
  40 + @adapter ||= Gitlab::LDAP::Adapter.new
  41 + end
  42 +
  43 + def config
  44 + @config ||= Gitlab.config.ldap
  45 + end
  46 + end
  47 + end
  48 +end
lib/gitlab/ldap/user.rb
@@ -13,8 +13,8 @@ module Gitlab @@ -13,8 +13,8 @@ module Gitlab
13 def find_or_create(auth) 13 def find_or_create(auth)
14 @auth = auth 14 @auth = auth
15 15
16 - if uid.blank? || email.blank?  
17 - raise_error("Account must provide an uid and email address") 16 + if uid.blank? || email.blank? || username.blank?
  17 + raise_error("Account must provide a dn, uid and email address")
18 end 18 end
19 19
20 user = find(auth) 20 user = find(auth)
@@ -62,8 +62,16 @@ module Gitlab @@ -62,8 +62,16 @@ module Gitlab
62 return nil unless ldap_conf.enabled && login.present? && password.present? 62 return nil unless ldap_conf.enabled && login.present? && password.present?
63 63
64 ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) 64 ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
  65 + filter = Net::LDAP::Filter.eq(ldap.uid, login)
  66 +
  67 + # Apply LDAP user filter if present
  68 + if ldap_conf['user_filter'].present?
  69 + user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter'])
  70 + filter = Net::LDAP::Filter.join(filter, user_filter)
  71 + end
  72 +
65 ldap_user = ldap.bind_as( 73 ldap_user = ldap.bind_as(
66 - filter: Net::LDAP::Filter.eq(ldap.uid, login), 74 + filter: filter,
67 size: 1, 75 size: 1,
68 password: password 76 password: password
69 ) 77 )
@@ -71,22 +79,20 @@ module Gitlab @@ -71,22 +79,20 @@ module Gitlab
71 find_by_uid(ldap_user.dn) if ldap_user 79 find_by_uid(ldap_user.dn) if ldap_user
72 end 80 end
73 81
74 - # Check LDAP user existance by dn. User in git over ssh check  
75 - #  
76 - # It covers 2 cases:  
77 - # * when ldap account was removed  
78 - # * when ldap account was deactivated by change of OU membership in 'dn'  
79 - def blocked?(dn)  
80 - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)  
81 - ldap.connection.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, size: 1).blank?  
82 - end  
83 -  
84 private 82 private
85 83
86 def find_by_uid(uid) 84 def find_by_uid(uid)
87 model.where(provider: provider, extern_uid: uid).last 85 model.where(provider: provider, extern_uid: uid).last
88 end 86 end
89 87
  88 + def username
  89 + (auth.info.nickname || samaccountname).to_s.force_encoding("utf-8")
  90 + end
  91 +
  92 + def samaccountname
  93 + (auth.extra[:raw_info][:samaccountname] || []).first
  94 + end
  95 +
90 def provider 96 def provider
91 'ldap' 97 'ldap'
92 end 98 end
spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
@@ -9,7 +9,8 @@ describe Gitlab::LDAP do @@ -9,7 +9,8 @@ describe Gitlab::LDAP do
9 @info = double( 9 @info = double(
10 uid: '12djsak321', 10 uid: '12djsak321',
11 name: 'John', 11 name: 'John',
12 - email: 'john@mail.com' 12 + email: 'john@mail.com',
  13 + nickname: 'john'
13 ) 14 )
14 end 15 end
15 16