oa-enterprise 0.1.6 → 0.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +41 -25
- data/lib/omniauth/strategies/cas.rb +2 -2
- data/lib/omniauth/strategies/ldap/adaptor.rb +220 -199
- data/lib/omniauth/strategies/ldap.rb +59 -44
- metadata +18 -14
data/README.rdoc
CHANGED
@@ -12,10 +12,9 @@ For the full auth suite:
|
|
12
12
|
|
13
13
|
gem install omniauth
|
14
14
|
|
15
|
-
CAS
|
16
|
-
== Stand-Alone Example
|
15
|
+
== CAS
|
17
16
|
|
18
|
-
Use the strategy as a middleware in your application:
|
17
|
+
Use the CAS strategy as a middleware in your application:
|
19
18
|
|
20
19
|
require 'omniauth/enterprise'
|
21
20
|
|
@@ -23,13 +22,47 @@ Use the strategy as a middleware in your application:
|
|
23
22
|
|
24
23
|
Then simply direct users to '/auth/cas' to have them sign in via your company's CAS server.
|
25
24
|
See OmniAuth::Strategies::CAS::Configuration for more configuration options.
|
26
|
-
|
27
|
-
Then simply direct users to '/auth/cas' to have them sign in via your company's CAS server.
|
28
|
-
See OmniAuth::Strategies::CAS::Configuration for more configuration options.
|
29
25
|
|
30
|
-
==
|
26
|
+
== LDAP
|
31
27
|
|
32
|
-
|
28
|
+
Use the LDAP strategy as a middleware in your applicaiton:
|
29
|
+
|
30
|
+
require 'omniauth/enterprise'
|
31
|
+
use OmniAuth::Strategies::LDAP,
|
32
|
+
:title => "My LDAP",
|
33
|
+
:host => '10.101.10.1',
|
34
|
+
:port => 389,
|
35
|
+
:method => :plain,
|
36
|
+
:base => 'dc=intridea, dc=com',
|
37
|
+
:uid => 'sAMAccountName',
|
38
|
+
:name_proc => Proc.new {|name| name.gsub(/@.*$/,''}}
|
39
|
+
|
40
|
+
All of the listed options are required, with the exception of :name_proc.
|
41
|
+
Allowed values of :method are: :plain, :ssl, :tls.
|
42
|
+
|
43
|
+
:uid is the LDAP attribute name for the user name in the login form. typically
|
44
|
+
AD would be 'sAMAccountName' or 'UserPrincipalName', while OpenLDAP is 'uid'.
|
45
|
+
You can also use 'dn', if your user choose the put in the dn in the login form
|
46
|
+
(but usually is too long for user to remember or know).
|
47
|
+
|
48
|
+
:name_proc allows you to match the user name entered with the format of the
|
49
|
+
:uid attributes. For example, value of 'sAMAccountName' in AD contains only the
|
50
|
+
windows user name. If your user prefers use email to login, a name_proc as
|
51
|
+
above will trim the email string down to just the windows name. In summary,
|
52
|
+
:name_proc helps you to fill the gap between the authentication and user lookup
|
53
|
+
process.
|
54
|
+
|
55
|
+
:try_sasl and :sasl_mechanisms are optional. Use them to initialize a SASL
|
56
|
+
connection to server. Allowed values are 'DIGEST-MD5' and 'GSS-SPNEGO'. If you
|
57
|
+
are not familiar with these authentication methods, please just avoid them.
|
58
|
+
|
59
|
+
Direct users to '/auth/ldap' to have them authenticated via your
|
60
|
+
company's LDAP server.
|
61
|
+
|
62
|
+
== Multiple Strategies
|
63
|
+
|
64
|
+
If you're using multiple strategies together, use OmniAuth's Builder. That's
|
65
|
+
what it's there for:
|
33
66
|
|
34
67
|
require 'omniauth/enterprise'
|
35
68
|
require 'omniauth/oauth' # for Campfire
|
@@ -39,20 +72,3 @@ If CAS is one of several authentication strategies, use the OmniAuth Builder:
|
|
39
72
|
provider :cas, :server => 'http://cas.mycompany.com/cas'
|
40
73
|
provider :campfire
|
41
74
|
end
|
42
|
-
|
43
|
-
LDAP strategy
|
44
|
-
|
45
|
-
use OmniAuth::Strategies::LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
|
46
|
-
or
|
47
|
-
use OmniAuth::Builder do
|
48
|
-
provider :LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
|
49
|
-
end
|
50
|
-
|
51
|
-
LDAP server's :host and :port are required, :method is also a required field, and allowed values are :plain, :ssl, and :tls.
|
52
|
-
:base is required, it is the distinguish name (DN) for your organization, all users should be searchable under this base.
|
53
|
-
:uid is required, it is the LDAP attribute name for the user name in the login form. typically AD would be 'sAMAccountName' or 'UniquePersonalIdentifier', while
|
54
|
-
OpenLDAP is 'uid'. You can also use 'dn', if your user choose the put in the dn in the login form (but usually is too long for user to remember or know).
|
55
|
-
:try_sasl and :sasl_mechanisms are optional, use it to initial SASL connection to server. mechanism supported are DIGEST-MD5 and GSS-SPNEGO.
|
56
|
-
|
57
|
-
Then simply direct users to '/auth/ldap' to have them authenticated via your company's LDAP server.
|
58
|
-
|
@@ -8,8 +8,8 @@ module OmniAuth
|
|
8
8
|
autoload :Configuration, 'omniauth/strategies/cas/configuration'
|
9
9
|
autoload :ServiceTicketValidator, 'omniauth/strategies/cas/service_ticket_validator'
|
10
10
|
|
11
|
-
def initialize(app, options = {})
|
12
|
-
super(app, options.delete(:name) || :cas)
|
11
|
+
def initialize(app, options = {}, &block)
|
12
|
+
super(app, options.delete(:name) || :cas, options, &block)
|
13
13
|
@configuration = OmniAuth::Strategies::CAS::Configuration.new(options)
|
14
14
|
end
|
15
15
|
|
@@ -1,57 +1,66 @@
|
|
1
1
|
#this code boughts pieces from activeldap and net-ldap
|
2
|
+
|
2
3
|
require 'rack'
|
3
4
|
require 'net/ldap'
|
4
5
|
require 'net/ntlm'
|
5
6
|
require 'uri'
|
7
|
+
|
6
8
|
module OmniAuth
|
7
9
|
module Strategies
|
8
10
|
class LDAP
|
9
11
|
class Adaptor
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
12
|
+
class LdapError < StandardError; end
|
13
|
+
class ConfigurationError < StandardError; end
|
14
|
+
class AuthenticationError < StandardError; end
|
15
|
+
class ConnectionError < StandardError; end
|
16
|
+
|
17
|
+
VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password,
|
18
|
+
:try_sasl, :sasl_mechanisms, :uid, :base]
|
19
|
+
|
20
|
+
MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
|
21
|
+
|
22
|
+
METHOD = {
|
23
|
+
:ssl => :simple_tls,
|
24
|
+
:tls => :start_tls,
|
25
|
+
:plain => nil
|
26
|
+
}
|
27
|
+
|
28
|
+
attr_accessor :bind_dn, :password
|
29
|
+
attr_reader :connection, :uid, :base
|
30
|
+
|
31
|
+
def initialize(configuration={})
|
32
|
+
@connection = nil
|
33
|
+
@disconnected = false
|
34
|
+
@bound = false
|
35
|
+
@configuration = configuration.dup
|
36
|
+
@logger = @configuration.delete(:logger)
|
37
|
+
message = []
|
38
|
+
MUST_HAVE_KEYS.each do |name|
|
39
|
+
message << name if configuration[name].nil?
|
40
|
+
end
|
41
|
+
raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
|
42
|
+
VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
|
43
|
+
instance_variable_set("@#{name}", configuration[name])
|
44
|
+
end
|
45
|
+
end
|
39
46
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
def connect(options={})
|
48
|
+
host = options[:host] || @host
|
49
|
+
method = options[:method] || @method || :plain
|
50
|
+
port = options[:port] || @port || ensure_port(method)
|
51
|
+
method = ensure_method(method)
|
52
|
+
@disconnected = false
|
53
|
+
@bound = false
|
54
|
+
@bind_tried = false
|
55
|
+
|
48
56
|
config = {
|
49
57
|
:host => host,
|
50
58
|
:port => port,
|
51
59
|
}
|
60
|
+
|
52
61
|
config[:encryption] = {:method => method} if method
|
53
|
-
|
54
|
-
begin
|
62
|
+
|
63
|
+
@connection, @uri, @with_start_tls = begin
|
55
64
|
uri = construct_uri(host, port, method == :simple_tls)
|
56
65
|
with_start_tls = method == :start_tls
|
57
66
|
puts ({:uri => uri, :with_start_tls => with_start_tls}).inspect
|
@@ -59,189 +68,201 @@ module OmniAuth
|
|
59
68
|
rescue Net::LDAP::LdapError
|
60
69
|
raise ConnectionError, $!.message
|
61
70
|
end
|
62
|
-
|
71
|
+
|
72
|
+
end
|
63
73
|
|
64
|
-
|
65
|
-
|
66
|
-
|
74
|
+
def unbind(options={})
|
75
|
+
@connection.close # Net::LDAP doesn't implement unbind.
|
76
|
+
end
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# Rough bind loop:
|
77
|
-
# Attempt 1: SASL if available
|
78
|
-
# Attempt 2: SIMPLE with credentials if password block
|
79
|
-
if try_sasl and sasl_bind(bind_dn, options)
|
80
|
-
puts "bind with sasl"
|
81
|
-
elsif simple_bind(bind_dn, options)
|
82
|
-
puts "bind with simple"
|
83
|
-
else
|
84
|
-
message = yield if block_given?
|
85
|
-
message ||= ('All authentication methods for %s exhausted.') % target
|
86
|
-
raise AuthenticationError, message
|
87
|
-
end
|
78
|
+
def bind(options={})
|
79
|
+
connect(options) unless connecting?
|
80
|
+
begin
|
81
|
+
@bind_tried = true
|
82
|
+
|
83
|
+
bind_dn = (options[:bind_dn] || @bind_dn).to_s
|
84
|
+
try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
|
88
85
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
86
|
+
# Rough bind loop:
|
87
|
+
# Attempt 1: SASL if available
|
88
|
+
# Attempt 2: SIMPLE with credentials if password block
|
89
|
+
if try_sasl and sasl_bind(bind_dn, options)
|
90
|
+
puts "bind with sasl"
|
91
|
+
elsif simple_bind(bind_dn, options)
|
92
|
+
puts "bind with simple"
|
93
|
+
else
|
94
|
+
message = yield if block_given?
|
95
|
+
message ||= ('All authentication methods for %s exhausted.') % target
|
96
|
+
raise AuthenticationError, message
|
97
|
+
end
|
98
|
+
|
99
|
+
@bound = true
|
100
|
+
rescue Net::LDAP::LdapError
|
101
|
+
raise AuthenticationError, $!.message
|
93
102
|
end
|
103
|
+
end
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
105
|
+
def disconnect!(options={})
|
106
|
+
unbind(options)
|
107
|
+
@connection = @uri = @with_start_tls = nil
|
108
|
+
@disconnected = true
|
109
|
+
end
|
100
110
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
111
|
+
def rebind(options={})
|
112
|
+
unbind(options) if bound?
|
113
|
+
connect(options)
|
114
|
+
end
|
105
115
|
|
106
|
-
|
107
|
-
|
108
|
-
|
116
|
+
def connecting?
|
117
|
+
!@connection.nil? and !@disconnected
|
118
|
+
end
|
109
119
|
|
110
|
-
|
111
|
-
|
112
|
-
|
120
|
+
def bound?
|
121
|
+
connecting? and @bound
|
122
|
+
end
|
113
123
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
124
|
+
def search(options={}, &block)
|
125
|
+
base = options[:base]
|
126
|
+
filter = options[:filter]
|
127
|
+
limit = options[:limit]
|
118
128
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
129
|
+
args = {
|
130
|
+
:base => @base,
|
131
|
+
:filter => filter,
|
132
|
+
:size => limit
|
133
|
+
}
|
134
|
+
|
135
|
+
puts args.inspect
|
136
|
+
|
125
137
|
attributes = {}
|
126
138
|
execute(:search, args) do |entry|
|
127
139
|
entry.attribute_names.each do |name|
|
128
140
|
attributes[name] = entry[name]
|
129
141
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
end
|
143
|
+
attributes
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def execute(method, *args, &block)
|
149
|
+
result = @connection.send(method, *args, &block)
|
150
|
+
message = nil
|
151
|
+
|
152
|
+
if result.is_a?(Hash)
|
153
|
+
message = result[:errorMessage]
|
154
|
+
result = result[:resultCode]
|
155
|
+
end
|
156
|
+
|
157
|
+
unless result.zero?
|
158
|
+
message = [Net::LDAP.result2string(result), message].compact.join(": ")
|
159
|
+
raise LdapError, message
|
160
|
+
end
|
161
|
+
end
|
146
162
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def prepare_connection(options)
|
163
|
+
def ensure_port(method)
|
164
|
+
if method == :ssl
|
165
|
+
URI::LDAPS::DEFAULT_PORT
|
166
|
+
else
|
167
|
+
URI::LDAP::DEFAULT_PORT
|
156
168
|
end
|
169
|
+
end
|
157
170
|
|
158
|
-
|
159
|
-
|
160
|
-
normalized_method = method.to_s.downcase.to_sym
|
161
|
-
return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
|
171
|
+
def prepare_connection(options)
|
172
|
+
end
|
162
173
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
174
|
+
def ensure_method(method)
|
175
|
+
method ||= "plain"
|
176
|
+
normalized_method = method.to_s.downcase.to_sym
|
177
|
+
return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
|
178
|
+
|
179
|
+
available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
|
180
|
+
format = "%s is not one of the available connect methods: %s"
|
181
|
+
raise ConfigurationError, format % [method.inspect, available_methods]
|
182
|
+
end
|
167
183
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
184
|
+
def sasl_bind(bind_dn, options={})
|
185
|
+
sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
|
186
|
+
sasl_mechanisms.each do |mechanism|
|
187
|
+
begin
|
188
|
+
normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
|
189
|
+
sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
|
190
|
+
next unless respond_to?(sasl_bind_setup, true)
|
191
|
+
initial_credential, challenge_response = send(sasl_bind_setup, bind_dn, options)
|
192
|
+
|
193
|
+
args = {
|
194
|
+
:method => :sasl,
|
195
|
+
:initial_credential => initial_credential,
|
196
|
+
:mechanism => mechanism,
|
197
|
+
:challenge_response => challenge_response,
|
198
|
+
}
|
199
|
+
|
200
|
+
info = {
|
201
|
+
:name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism,
|
202
|
+
}
|
203
|
+
puts info.inspect
|
204
|
+
|
205
|
+
execute(:bind, args)
|
206
|
+
return true
|
207
|
+
|
208
|
+
rescue Exception => e
|
209
|
+
puts e.message
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
false
|
214
|
+
end
|
215
|
+
|
216
|
+
def sasl_bind_setup_digest_md5(bind_dn, options)
|
217
|
+
initial_credential = ""
|
218
|
+
challenge_response = Proc.new do |cred|
|
219
|
+
pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password
|
220
|
+
sasl = SASL.new("DIGEST-MD5", pref)
|
221
|
+
response = sasl.receive("challenge", cred)
|
222
|
+
response[1]
|
223
|
+
end
|
224
|
+
[initial_credential, challenge_response]
|
225
|
+
end
|
226
|
+
|
227
|
+
def sasl_bind_setup_gss_spnego(bind_dn, options)
|
228
|
+
puts options.inspect
|
229
|
+
user,psw = [bind_dn, options[:password]||@password]
|
230
|
+
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
231
|
+
|
232
|
+
nego = proc {|challenge|
|
233
|
+
t2_msg = Net::NTLM::Message.parse( challenge )
|
234
|
+
user, domain = user.split('\\').reverse
|
235
|
+
t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
|
236
|
+
t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} )
|
237
|
+
t3_msg.serialize
|
238
|
+
}
|
239
|
+
[Net::NTLM::Message::Type1.new.serialize, nego]
|
203
240
|
end
|
204
|
-
[initial_credential, challenge_response]
|
205
|
-
end
|
206
|
-
def sasl_bind_setup_gss_spnego(bind_dn, options)
|
207
|
-
puts options.inspect
|
208
|
-
user,psw = [bind_dn, options[:password]||@password]
|
209
|
-
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
210
|
-
|
211
|
-
nego = proc {|challenge|
|
212
|
-
t2_msg = Net::NTLM::Message.parse( challenge )
|
213
|
-
user, domain = user.split('\\').reverse
|
214
|
-
t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
|
215
|
-
t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} )
|
216
|
-
t3_msg.serialize
|
217
|
-
}
|
218
|
-
[Net::NTLM::Message::Type1.new.serialize, nego]
|
219
|
-
end
|
220
241
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
242
|
+
def simple_bind(bind_dn, options={})
|
243
|
+
args = {
|
244
|
+
:method => :simple,
|
245
|
+
:username => bind_dn,
|
246
|
+
:password => options[:password]||@password,
|
247
|
+
}
|
248
|
+
execute(:bind, args)
|
249
|
+
true
|
250
|
+
end
|
230
251
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
252
|
+
def construct_uri(host, port, ssl)
|
253
|
+
protocol = ssl ? "ldaps" : "ldap"
|
254
|
+
URI.parse("#{protocol}://#{host}:#{port}").to_s
|
255
|
+
end
|
256
|
+
|
257
|
+
def target
|
258
|
+
return nil if @uri.nil?
|
259
|
+
if @with_start_tls
|
260
|
+
"#{@uri}(StartTLS)"
|
261
|
+
else
|
262
|
+
@uri
|
263
|
+
end
|
264
|
+
end
|
244
265
|
end
|
245
266
|
end
|
246
267
|
end
|
247
|
-
end
|
268
|
+
end
|
@@ -2,25 +2,34 @@ require 'omniauth/enterprise'
|
|
2
2
|
require 'net/ldap'
|
3
3
|
require 'sasl/base'
|
4
4
|
require 'sasl'
|
5
|
+
|
5
6
|
module OmniAuth
|
6
7
|
module Strategies
|
7
8
|
class LDAP
|
8
9
|
include OmniAuth::Strategy
|
9
10
|
|
10
11
|
autoload :Adaptor, 'omniauth/strategies/ldap/adaptor'
|
11
|
-
@@config =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
@@config = {'name' => 'cn',
|
13
|
+
'first_name' => 'givenName',
|
14
|
+
'last_name' => 'sn',
|
15
|
+
'email' => ['mail', "email", 'userPrincipalName'],
|
16
|
+
'phone' => ['telephoneNumber', 'homePhone', 'facsimileTelephoneNumber'],
|
17
|
+
'mobile_number' => ['mobile', 'mobileTelephoneNumber'],
|
18
|
+
'nickname' => ['uid', 'userid', 'sAMAccountName'],
|
19
|
+
'title' => 'title',
|
20
|
+
'location' => {"%0, %1, %2, %3 %4" => [['address', 'postalAddress', 'homePostalAddress', 'street', 'streetAddress'], ['l'], ['st'],['co'],['postOfficeBox']]},
|
21
|
+
'uid' => 'dn',
|
22
|
+
'url' => ['wwwhomepage'],
|
23
|
+
'image' => 'jpegPhoto',
|
24
|
+
'description' => 'description'}
|
25
|
+
|
26
|
+
# Initialize the LDAP Middleware
|
27
|
+
#
|
28
|
+
# @param [Rack Application] app Standard Rack middleware argument.
|
29
|
+
# @option options [String, 'LDAP Authentication'] :title A title for the authentication form.
|
30
|
+
def initialize(app, options = {}, &block)
|
31
|
+
super(app, options[:name] || :ldap, options.dup, &block)
|
32
|
+
@name_proc = (@options.delete(:name_proc) || Proc.new {|name| name})
|
24
33
|
@adaptor = OmniAuth::Strategies::LDAP::Adaptor.new(options)
|
25
34
|
end
|
26
35
|
|
@@ -34,23 +43,29 @@ module OmniAuth
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|
37
|
-
|
38
|
-
OmniAuth::Form.build(
|
46
|
+
def get_credentials
|
47
|
+
OmniAuth::Form.build(options[:title] || "LDAP Authentication") do
|
39
48
|
text_field 'Login', 'username'
|
40
49
|
password_field 'Password', 'password'
|
41
50
|
end.to_response
|
42
51
|
end
|
52
|
+
|
43
53
|
def perform
|
44
54
|
begin
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
55
|
+
bind_dn = "#{@adaptor.uid}=#{request.POST['username']}"
|
56
|
+
bind_dn << ",#{@adaptor.base}" unless @adaptor.base == ''
|
57
|
+
|
58
|
+
@adaptor.bind(:bind_dn => bind_dn, :password => request.POST['password'])
|
59
|
+
@ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(request.POST['username'])),:limit => 1)
|
60
|
+
@user_info = self.class.map_user(@@config, @ldap_user_info)
|
61
|
+
|
62
|
+
@env['omniauth.auth'] = auth_hash
|
63
|
+
@env['REQUEST_METHOD'] = 'GET'
|
64
|
+
@env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback"
|
50
65
|
|
51
|
-
|
66
|
+
call_app!
|
52
67
|
rescue Exception => e
|
53
|
-
|
68
|
+
fail!(:invalid_credentials, e)
|
54
69
|
end
|
55
70
|
end
|
56
71
|
|
@@ -66,28 +81,28 @@ module OmniAuth
|
|
66
81
|
})
|
67
82
|
end
|
68
83
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
def self.map_user(mapper, object)
|
85
|
+
user = {}
|
86
|
+
mapper.each do |key, value|
|
87
|
+
case value
|
88
|
+
when String
|
89
|
+
user[key] = object[value.downcase.to_sym].to_s if object[value.downcase.to_sym]
|
90
|
+
when Array
|
91
|
+
value.each {|v| (user[key] = object[v.downcase.to_sym].to_s; break;) if object[v.downcase.to_sym]}
|
92
|
+
when Hash
|
93
|
+
value.map do |key1, value1|
|
94
|
+
pattern = key1.dup
|
95
|
+
value1.each_with_index do |v,i|
|
96
|
+
part = '';
|
97
|
+
v.each {|v1| (part = object[v1.downcase.to_sym].to_s; break;) if object[v1.downcase.to_sym]}
|
98
|
+
pattern.gsub!("%#{i}",part||'')
|
99
|
+
end
|
100
|
+
user[key] = pattern
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
user
|
105
|
+
end
|
91
106
|
end
|
92
107
|
end
|
93
108
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oa-enterprise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: -1848230051
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
- beta1
|
11
|
+
version: 0.2.0.beta1
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- James A. Rosen
|
@@ -16,7 +17,7 @@ autorequire:
|
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2010-
|
20
|
+
date: 2010-11-29 00:00:00 -06:00
|
20
21
|
default_executable:
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -25,12 +26,13 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - "="
|
27
28
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
29
|
+
hash: -1848230051
|
29
30
|
segments:
|
30
31
|
- 0
|
31
|
-
-
|
32
|
-
-
|
33
|
-
|
32
|
+
- 2
|
33
|
+
- 0
|
34
|
+
- beta1
|
35
|
+
version: 0.2.0.beta1
|
34
36
|
requirement: *id001
|
35
37
|
name: oa-core
|
36
38
|
prerelease: false
|
@@ -233,12 +235,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
233
235
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
234
236
|
none: false
|
235
237
|
requirements:
|
236
|
-
- - "
|
238
|
+
- - ">"
|
237
239
|
- !ruby/object:Gem::Version
|
238
|
-
hash:
|
240
|
+
hash: 25
|
239
241
|
segments:
|
240
|
-
-
|
241
|
-
|
242
|
+
- 1
|
243
|
+
- 3
|
244
|
+
- 1
|
245
|
+
version: 1.3.1
|
242
246
|
requirements: []
|
243
247
|
|
244
248
|
rubyforge_project:
|