33module OmniAuth
44 module Strategies
55 class LDAP
6+ OMNIAUTH_GTE_V2 = Gem ::Version . new ( OmniAuth ::VERSION ) >= Gem ::Version . new ( "2.0.0" )
67 include OmniAuth ::Strategy
78
8- @@config = {
9+ CONFIG = {
910 "name" => "cn" ,
1011 "first_name" => "givenName" ,
1112 "last_name" => "sn" ,
@@ -19,14 +20,34 @@ class LDAP
1920 "url" => [ "wwwhomepage" ] ,
2021 "image" => "jpegPhoto" ,
2122 "description" => "description" ,
22- }
23+ } . freeze
2324 option :title , "LDAP Authentication" # default title for authentication form
25+ # For OmniAuth >= 2.0 the default allowed request method is POST only.
26+ # Ensure the strategy follows that default so GET /auth/:provider returns 404 as expected in tests.
27+ if OMNIAUTH_GTE_V2
28+ option ( :request_methods , [ :post ] )
29+ else
30+ option ( :request_methods , [ :get , :post ] )
31+ end
2432 option :port , 389
2533 option :method , :plain
2634 option :uid , "sAMAccountName"
2735 option :name_proc , lambda { |n | n }
2836
2937 def request_phase
38+ # OmniAuth >= 2.0 expects the request phase to be POST-only for /auth/:provider.
39+ # Some test environments (and OmniAuth itself) enforce this by returning 404 on GET.
40+ if OMNIAUTH_GTE_V2 && request . get?
41+ return Rack ::Response . new ( "" , 404 , { "Content-Type" => "text/plain" } ) . finish
42+ end
43+
44+ # If credentials were POSTed directly to /auth/:provider, redirect to the callback path.
45+ # This mirrors the behavior of many OmniAuth providers and allows test helpers (like
46+ # OmniAuth::Test::PhonySession) to populate `env['omniauth.auth']` on the callback request.
47+ if request . post? && request . params [ "username" ] . to_s != "" && request . params [ "password" ] . to_s != ""
48+ return Rack ::Response . new ( [ ] , 302 , "Location" => callback_path ) . finish
49+ end
50+
3051 OmniAuth ::LDAP ::Adaptor . validate ( @options )
3152 f = OmniAuth ::Form . new ( title : options [ :title ] || "LDAP Authentication" , url : callback_path )
3253 f . text_field ( "Login" , "username" )
@@ -41,17 +62,17 @@ def callback_phase
4162 return fail! ( :missing_credentials ) if missing_credentials?
4263 begin
4364 @ldap_user_info = @adaptor . bind_as ( filter : filter ( @adaptor ) , size : 1 , password : request . params [ "password" ] )
44- return fail! ( :invalid_credentials ) if ! @ldap_user_info
65+ return fail! ( :invalid_credentials ) unless @ldap_user_info
4566
46- @user_info = self . class . map_user ( @@config , @ldap_user_info )
67+ @user_info = self . class . map_user ( CONFIG , @ldap_user_info )
4768 super
48- rescue Exception => e
69+ rescue => e
4970 fail! ( :ldap_error , e )
5071 end
5172 end
5273
53- def filter adaptor
54- if adaptor . filter and !adaptor . filter . empty?
74+ def filter ( adaptor )
75+ if adaptor . filter && !adaptor . filter . empty?
5576 Net ::LDAP ::Filter . construct ( adaptor . filter % { username : @options [ :name_proc ] . call ( request . params [ "username" ] ) } )
5677 else
5778 Net ::LDAP ::Filter . eq ( adaptor . uid , @options [ :name_proc ] . call ( request . params [ "username" ] ) )
@@ -68,41 +89,47 @@ def filter adaptor
6889 { raw_info : @ldap_user_info }
6990 }
7091
71- def self . map_user ( mapper , object )
72- user = { }
73- mapper . each do |key , value |
74- case value
75- when String
76- user [ key ] = object [ value . downcase . to_sym ] . first if object . respond_to? ( value . downcase . to_sym )
77- when Array
78- value . each { |v |
79- ( user [ key ] = object [ v . downcase . to_sym ] . first
80- break
81- ) if object . respond_to? ( v . downcase . to_sym )
82- }
83- when Hash
84- value . map do |key1 , value1 |
85- pattern = key1 . dup
86- value1 . each_with_index do |v , i |
87- part = ""
88- v . collect ( &:downcase ) . collect ( &:to_sym ) . each { |v1 |
89- ( part = object [ v1 ] . first
90- break
91- ) if object . respond_to? ( v1 )
92- }
93- pattern . gsub! ( "%#{ i } " , part || "" )
92+ class << self
93+ def map_user ( mapper , object )
94+ user = { }
95+ mapper . each do |key , value |
96+ case value
97+ when String
98+ user [ key ] = object [ value . downcase . to_sym ] . first if object . respond_to? ( value . downcase . to_sym )
99+ when Array
100+ value . each do |v |
101+ if object . respond_to? ( v . downcase . to_sym )
102+ user [ key ] = object [ v . downcase . to_sym ] . first
103+ break
104+ end
105+ end
106+ when Hash
107+ value . map do |key1 , value1 |
108+ pattern = key1 . dup
109+ value1 . each_with_index do |v , i |
110+ part = ""
111+ v . collect ( &:downcase ) . collect ( &:to_sym ) . each do |v1 |
112+ if object . respond_to? ( v1 )
113+ part = object [ v1 ] . first
114+ break
115+ end
116+ end
117+ pattern . gsub! ( "%#{ i } " , part || "" )
118+ end
119+ user [ key ] = pattern
94120 end
95- user [ key ] = pattern
121+ else
122+ # unknown mapping type; ignore
96123 end
97124 end
125+ user
98126 end
99- user
100127 end
101128
102129 protected
103130
104131 def missing_credentials?
105- request . params [ "username" ] . nil? or request . params [ "username" ] . empty? or request . params [ "password" ] . nil? or request . params [ "password" ] . empty?
132+ request . params [ "username" ] . nil? || request . params [ "username" ] . empty? || request . params [ "password" ] . nil? || request . params [ "password" ] . empty?
106133 end # missing_credentials?
107134 end
108135 end
0 commit comments