els_token 1.0.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -2
- data/lib/els_token.rb +209 -113
- data/lib/els_token/{els_user.rb → els_identity.rb} +15 -6
- data/lib/els_token/version.rb +1 -1
- data/test/dummy/Gemfile +9 -0
- data/test/dummy/Gemfile.lock +17 -1
- data/test/dummy/app/assets/javascripts/application.js +1 -2
- data/test/dummy/app/assets/javascripts/els_session.js.coffee +7 -0
- data/test/dummy/app/controllers/application_controller.rb +45 -6
- data/test/dummy/app/controllers/els_session_controller.rb +91 -0
- data/test/dummy/app/models/els_faker.rb +8 -0
- data/test/dummy/app/views/els_session/new.html.erb +41 -0
- data/test/dummy/app/views/els_session/show.html.erb +4 -0
- data/test/dummy/app/views/layouts/application.html.erb +6 -1
- data/test/dummy/config/application.rb +3 -39
- data/test/dummy/config/els_token.yml +21 -0
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/uat.rb +67 -0
- data/test/dummy/config/initializers/els_token.rb +1 -1
- data/test/dummy/config/routes.rb +9 -55
- data/test/dummy/log/development.log +2170 -4
- data/test/dummy/tmp/cache/assets/C65/500/sprockets%2F53516304d6d54f9499b5a7788631e5c5 +0 -0
- data/test/dummy/tmp/cache/assets/CA6/630/sprockets%2F8af9c2296e34c4f01a58747a12c07130 +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D49/140/sprockets%2Fd128539bcb29d65cf7e28a43f8563a2f +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D9C/D00/sprockets%2F66ef3d03a58dfcc273e3453b04fdf81b +0 -0
- data/test/dummy/tmp/cache/assets/D9F/AA0/sprockets%2Fab395cb72ff1e290f0de1f739a87bd50 +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- metadata +41 -54
- data/test/dummy/app/assets/javascripts/hello.js +0 -2
- data/test/dummy/app/assets/stylesheets/hello.css +0 -4
- data/test/dummy/app/controllers/hello_controller.rb +0 -4
- data/test/dummy/app/views/hello/index.html.erb +0 -1
- data/test/dummy/config/els.yml +0 -12
- data/test/els_token_test.rb.old +0 -7
- data/test/hello_controller_test.rb +0 -32
data/README.rdoc
CHANGED
@@ -4,8 +4,16 @@ A simple library for interfacing with forgeRock OpenAM REST Interface.
|
|
4
4
|
|
5
5
|
Still pretty much in development but kind of workable.
|
6
6
|
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
include ElsToken into your class or module
|
10
|
+
|
7
11
|
change log
|
8
12
|
|
13
|
+
1.2.0
|
14
|
+
Removed Rails dependency.
|
15
|
+
Cleaned up instructions - getting ready to bare all :)
|
16
|
+
|
9
17
|
1.0.1
|
10
18
|
Removed AOLMemberCA certificate as it was mostly pointless
|
11
19
|
SSL without verification is used as default
|
@@ -15,5 +23,7 @@ change log
|
|
15
23
|
Initial release :)
|
16
24
|
|
17
25
|
= TODO
|
18
|
-
|
19
|
-
|
26
|
+
With the correct level of priviledge it's possible to perform granular LDAP searches against the OpenAM REST interface.
|
27
|
+
By Default one can only pull identity details for a provided token but it might be nice to search against other attributes.
|
28
|
+
|
29
|
+
Erm some tests might be nice :)
|
data/lib/els_token.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'els_token/module_inheritable_attributes'
|
2
|
-
require 'els_token/
|
2
|
+
require 'els_token/els_identity'
|
3
3
|
require 'net/http'
|
4
4
|
require 'uri'
|
5
5
|
|
@@ -14,142 +14,238 @@ module ElsToken
|
|
14
14
|
|
15
15
|
module ClassMethods
|
16
16
|
|
17
|
-
# els_config expects a
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
17
|
+
# els_config expects a hash with environmental
|
18
|
+
# parameters including the els gateway and expected
|
19
|
+
# cookie name (when used in a Rack environment)
|
20
|
+
# An optional fake identity can be supplied which
|
21
|
+
# will override any active authentication. This can
|
22
|
+
# be especially useful during automated testing.
|
23
|
+
# The fake ID can take any of the ElsIdentity properties
|
24
|
+
#
|
25
|
+
# A typical setup would initialize a options hash to
|
26
|
+
# include the following
|
27
|
+
#
|
28
|
+
# faker:
|
29
|
+
# name: neilcuk
|
30
|
+
# employee_number: 09095
|
31
|
+
# roles:
|
32
|
+
# - App Admins
|
33
|
+
# - Domain Users
|
34
|
+
# uri: https://els-admin.corp.aol.com:443/opensso/identity
|
35
|
+
# cookie: iPlanetDirectoryPro
|
36
|
+
# cert: /path/to/certs
|
37
|
+
#
|
38
|
+
# Do not include the faker object in your production
|
39
|
+
# configuration :)
|
40
|
+
#
|
41
|
+
# only the uri option is required if you are not worried
|
42
|
+
# about cookies and do not plan on using them. If you want
|
43
|
+
# to include a certificate for interacting with the ELS
|
44
|
+
# server then you can specify a file or directory to find
|
45
|
+
# the cert. By default Certificate validiation is off!
|
23
46
|
#
|
24
|
-
# class MyController
|
25
|
-
# include ElsToken
|
26
|
-
# els_config options_hash
|
27
|
-
# end
|
28
47
|
def els_config(options = {})
|
29
|
-
|
48
|
+
unless options["uri"]
|
49
|
+
raise "I need a uri to authenticate against" unless options["faker"]
|
50
|
+
end
|
51
|
+
els_options.merge!(options)
|
30
52
|
end
|
31
|
-
|
32
|
-
|
53
|
+
|
54
|
+
def els_uri(uri = nil)
|
55
|
+
return els_options["uri"] unless uri
|
56
|
+
els_options["uri"] = uri
|
57
|
+
end
|
58
|
+
|
59
|
+
def els_cookie_name(cookie_name = nil)
|
60
|
+
return els_options["cookie"] unless cookie_name
|
61
|
+
els_options["cookie_name"] = uri
|
62
|
+
end
|
63
|
+
|
64
|
+
def els_faker(faker = {})
|
65
|
+
els_options["faker"] = faker
|
66
|
+
end
|
67
|
+
|
68
|
+
def els_options
|
69
|
+
@els_options
|
70
|
+
end
|
71
|
+
|
72
|
+
# authenticates against ELS and returns the user token
|
73
|
+
def authenticate(username,password,options={})
|
33
74
|
|
34
|
-
|
35
|
-
|
75
|
+
begin
|
76
|
+
response = els_http_request("/authenticate",
|
77
|
+
"uri=realm=aolcorporate&username=#{username}&password=#{password}",
|
78
|
+
options)
|
79
|
+
if response.code.eql? "200"
|
80
|
+
# return the token
|
81
|
+
response.body.chomp.sub(/token\.id=/,"")
|
82
|
+
else
|
83
|
+
raise response.error!
|
84
|
+
end
|
85
|
+
rescue Net::HTTPExceptions => e1
|
86
|
+
raise e1, "token retrieval failed for #{username}"
|
87
|
+
rescue Exception => e
|
88
|
+
# Do not expect these. Wrapping the exception so
|
89
|
+
# as to not reveal the passed in password
|
90
|
+
puts e.backtrace
|
91
|
+
raise e, "unable to fetch token for #{username}"
|
92
|
+
end
|
93
|
+
end
|
36
94
|
|
37
|
-
|
38
|
-
|
95
|
+
# passes a token to els to see if it is still valid
|
96
|
+
def is_token_valid?(token, options={})
|
97
|
+
response = els_http_request("/isTokenValid","tokenid=#{token}",options)
|
39
98
|
if response.code.eql? "200"
|
40
|
-
|
41
|
-
response.body.chomp.sub(/token\.id=/,"")
|
99
|
+
true
|
42
100
|
else
|
43
|
-
|
101
|
+
false
|
44
102
|
end
|
45
|
-
rescue Net::HTTPExceptions => e1
|
46
|
-
raise e1, "token retrieval failed for #{username}"
|
47
|
-
rescue Exception => e
|
48
|
-
# Do not expect these. Wrapping the exception so
|
49
|
-
# as to not reveal the passed in password
|
50
|
-
raise e, "unable to fetch token for #{username}"
|
51
103
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
else
|
59
|
-
false
|
104
|
+
|
105
|
+
|
106
|
+
# obtain a friendly ElsIdentity object by passing
|
107
|
+
# in a token
|
108
|
+
def get_token_identity(token,options={})
|
109
|
+
ElsIdentity.new(get_raw_token_identity(token,options))
|
60
110
|
end
|
61
|
-
end
|
62
111
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
token
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
112
|
+
# get_token_identity wraps the ELS identity response
|
113
|
+
# in a nice, friendly, object. If you don't like that object
|
114
|
+
# or need the raw data, then use this.
|
115
|
+
def get_raw_token_identity(token,options={})
|
116
|
+
response = els_http_request("/attributes","subjectid=#{token}",options)
|
117
|
+
if response.code.eql? "200"
|
118
|
+
response.body
|
119
|
+
else
|
120
|
+
response.error!
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# When used inside a rack environment
|
125
|
+
# will attempt to retrieve the user token
|
126
|
+
# from the session cookie and return a full
|
127
|
+
# identity. This is pretty much a convenience
|
128
|
+
# method that chains is_cookie_token_valid?
|
129
|
+
# then get_token_identity
|
130
|
+
def get_identity(token, options ={})
|
131
|
+
return fake_id if fake_it?
|
132
|
+
begin
|
133
|
+
if is_token_valid?(token, options)
|
134
|
+
get_token_identity(token, options)
|
135
|
+
else
|
136
|
+
raise "token is invalid"
|
137
|
+
end
|
138
|
+
rescue Exception => e
|
139
|
+
raise e
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def els_http_request(url_base_extension, query_string, options)
|
146
|
+
options = els_options.dup.merge(options)
|
147
|
+
uri = URI.parse(options['uri'] + url_base_extension)
|
148
|
+
uri.query=query_string
|
149
|
+
http = Net::HTTP.new(uri.host,uri.port)
|
150
|
+
http.use_ssl = true
|
151
|
+
|
152
|
+
# Use a known certificate if supplied
|
153
|
+
if rootca = options[:cert]
|
154
|
+
if File.exist? rootca
|
155
|
+
http.ca_file = rootca
|
156
|
+
elsif Dir.exist? rootca
|
157
|
+
http.ca.path = rootca
|
158
|
+
else
|
159
|
+
raise "${rootca} cannot be found"
|
160
|
+
end
|
161
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
162
|
+
http.verify_depth = 5
|
163
|
+
else
|
164
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
165
|
+
end
|
166
|
+
|
167
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
168
|
+
|
169
|
+
http.request(request)
|
170
|
+
end
|
171
|
+
|
172
|
+
def fake_it?
|
173
|
+
els_options.has_key? 'faker'
|
71
174
|
end
|
175
|
+
|
176
|
+
def fake_id
|
177
|
+
unless @fake_id
|
178
|
+
id = ElsIdentity.new
|
179
|
+
id.instance_variable_set("@roles",els_options['faker']['roles'])
|
180
|
+
id.instance_variable_set("@mail",els_options['faker']['mail'])
|
181
|
+
id.instance_variable_set("@last_name",els_options['faker']['last_name'])
|
182
|
+
id.instance_variable_set("@first_name",els_options['faker']['first_name'])
|
183
|
+
id.instance_variable_set("@uac",els_options['faker']['uac'])
|
184
|
+
id.instance_variable_set("@dn",els_options['faker']['dn'])
|
185
|
+
id.instance_variable_set("@common_name",els_options['faker']['common_name'])
|
186
|
+
id.instance_variable_set("@employee_number",els_options['faker']['employee_number'])
|
187
|
+
id.instance_variable_set("@display_name",els_options['faker']['display_name'])
|
188
|
+
id.instance_variable_set("@token_id",els_options['faker']['token_id'])
|
189
|
+
id.instance_variable_set("@user_status",els_options['faker']['user_status'])
|
190
|
+
@fake_id = id
|
191
|
+
end
|
192
|
+
@fake_id
|
193
|
+
end
|
72
194
|
end
|
73
195
|
|
74
|
-
#
|
196
|
+
# Instance methods
|
197
|
+
|
198
|
+
class Runner
|
199
|
+
include ElsToken
|
200
|
+
end
|
201
|
+
|
202
|
+
def authenticate(username,password)
|
203
|
+
Runner.authenticate(username,password,self.class.els_options)
|
204
|
+
end
|
205
|
+
|
206
|
+
def is_token_valid?(token)
|
207
|
+
Runner.is_token_valid?(token,self.class.els_options)
|
208
|
+
end
|
209
|
+
|
75
210
|
def get_token_identity(token)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
211
|
+
Runner.get_token_identity(token,self.class.els_options)
|
212
|
+
end
|
213
|
+
|
214
|
+
def get_raw_token_identity(token)
|
215
|
+
Runner.get_raw_token_identity(token,self.class.els_options)
|
82
216
|
end
|
83
217
|
|
84
|
-
# When used inside a rack environment
|
85
|
-
# will attempt to retrieve the user token
|
86
|
-
# from the session cookie and return a full
|
87
|
-
# identity
|
88
218
|
def get_identity
|
89
|
-
|
90
|
-
|
91
|
-
if is_cookie_token_valid?
|
92
|
-
get_token_identity cookies[self.class.els_options['cookie']]
|
93
|
-
else
|
94
|
-
raise "token is invalid"
|
95
|
-
end
|
96
|
-
rescue Exception => e
|
97
|
-
raise e
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def method_missing(m, *args, &block)
|
102
|
-
puts "Drop the crack pipe - There is no method called #{m}"
|
219
|
+
token = cookies[self.class.els_options['cookie']]
|
220
|
+
Runner.get_identity(token,self.class.els_options)
|
103
221
|
end
|
104
|
-
|
105
|
-
private
|
106
222
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
elsif Dir.exist? rootca
|
118
|
-
http.ca.path = rootca
|
119
|
-
else
|
120
|
-
raise "${rootca} cannot be found"
|
121
|
-
end
|
122
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
123
|
-
http.verify_depth = 5
|
223
|
+
# extract the token from a cookie
|
224
|
+
# This method expects a hash called cookies
|
225
|
+
# to be present. It will look for a cookie with
|
226
|
+
# the key of the cookie value in the config hash
|
227
|
+
def is_cookie_token_valid?
|
228
|
+
return true if self.class.els_options.has_key? 'faker'
|
229
|
+
raise "No cookies instance found" if cookies.nil?
|
230
|
+
token = cookies[self.class.els_options['cookie']]
|
231
|
+
if token.nil? || !Runner.is_token_valid?(token,self.class.els_options)
|
232
|
+
false
|
124
233
|
else
|
125
|
-
|
234
|
+
true
|
126
235
|
end
|
127
|
-
|
128
|
-
request = Net::HTTP::Get.new(uri.request_uri)
|
129
|
-
|
130
|
-
http.request(request)
|
131
236
|
end
|
132
237
|
|
133
|
-
|
134
|
-
|
238
|
+
# How about a few class methods of our own?
|
239
|
+
|
240
|
+
def self.authenticate(username,password,options)
|
241
|
+
Runner.authenticate(username,password,options)
|
135
242
|
end
|
136
|
-
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
id.instance_variable_set("@first_name",self.class.els_options['faker']['first_name'])
|
144
|
-
id.instance_variable_set("@uac",self.class.els_options['faker']['uac'])
|
145
|
-
id.instance_variable_set("@dn",self.class.els_options['faker']['dn'])
|
146
|
-
id.instance_variable_set("@common_name",self.class.els_options['faker']['common_name'])
|
147
|
-
id.instance_variable_set("@employee_number",self.class.els_options['faker']['employee_number'])
|
148
|
-
id.instance_variable_set("@display_name",self.class.els_options['faker']['display_name'])
|
149
|
-
id.instance_variable_set("@token_id",self.class.els_options['faker']['token_id'])
|
150
|
-
@fake_id = id
|
151
|
-
end
|
152
|
-
@fake_id
|
243
|
+
|
244
|
+
def self.is_token_valid?(token, options)
|
245
|
+
Runner.is_token_valid?(token,options)
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.get_identity(token, options)
|
249
|
+
Runner.get_identity(token,options)
|
153
250
|
end
|
154
|
-
|
155
251
|
end
|
@@ -4,12 +4,19 @@ module ElsToken
|
|
4
4
|
|
5
5
|
class ElsIdentity
|
6
6
|
attr_reader :roles, :mail, :last_name, :first_name, :uac, :dn, :common_name
|
7
|
-
attr_reader :employee_number, :display_name, :name, :token_id
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
attr_reader :employee_number, :display_name, :name, :token_id, :user_status
|
8
|
+
|
9
|
+
alias :cdid :name
|
10
|
+
|
11
|
+
# The Active Directory User Account Control contains lots of
|
12
|
+
# interesting info about the user account state. This method
|
13
|
+
# does a very simple check to see if the account is enabled
|
14
|
+
# and return either "enabled" or "disabled". There are many
|
15
|
+
# other states but I don't use them
|
16
|
+
def friendly_uac
|
17
|
+
@friendly_uac ||= (@uac.to_i & 2 == 0) ? "enabled" : "disabled"
|
11
18
|
end
|
12
|
-
|
19
|
+
|
13
20
|
def has_role?(role)
|
14
21
|
@roles.include? role
|
15
22
|
end
|
@@ -43,7 +50,6 @@ module ElsToken
|
|
43
50
|
|
44
51
|
when line =~ /name=useraccountcontrol/
|
45
52
|
self_set :uac
|
46
|
-
@uac = (uac.to_i & 2 == 0) ? "enabled" : "disabled"
|
47
53
|
|
48
54
|
when line =~ /name=givenname/
|
49
55
|
self_set :first_name
|
@@ -62,6 +68,9 @@ module ElsToken
|
|
62
68
|
|
63
69
|
when line =~ /name=displayname/
|
64
70
|
self_set :display_name
|
71
|
+
|
72
|
+
when line =~ /name=inetUserStatus/
|
73
|
+
self_set :user_status
|
65
74
|
end
|
66
75
|
end
|
67
76
|
rescue
|
data/lib/els_token/version.rb
CHANGED
data/test/dummy/Gemfile
CHANGED
@@ -1,3 +1,12 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
3
|
gem 'rails', '~> 3.2.0'
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem 'sqlite3'
|
7
|
+
end
|
8
|
+
|
9
|
+
gem 'coffee-rails', '~> 3.2.1'
|
10
|
+
gem 'jquery-rails'
|
11
|
+
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
12
|
+
#gem 'therubyracer'
|
data/test/dummy/Gemfile.lock
CHANGED
@@ -30,10 +30,22 @@ GEM
|
|
30
30
|
multi_json (~> 1.0)
|
31
31
|
arel (3.0.2)
|
32
32
|
builder (3.0.3)
|
33
|
+
coffee-rails (3.2.2)
|
34
|
+
coffee-script (>= 2.2.0)
|
35
|
+
railties (~> 3.2.0)
|
36
|
+
coffee-script (2.2.0)
|
37
|
+
coffee-script-source
|
38
|
+
execjs
|
39
|
+
coffee-script-source (1.3.3)
|
33
40
|
erubis (2.7.0)
|
41
|
+
execjs (1.4.0)
|
42
|
+
multi_json (~> 1.0)
|
34
43
|
hike (1.2.1)
|
35
44
|
i18n (0.6.1)
|
36
45
|
journey (1.0.4)
|
46
|
+
jquery-rails (2.1.3)
|
47
|
+
railties (>= 3.1.0, < 5.0)
|
48
|
+
thor (~> 0.14)
|
37
49
|
json (1.7.5)
|
38
50
|
mail (2.4.4)
|
39
51
|
i18n (>= 0.4.0)
|
@@ -47,7 +59,7 @@ GEM
|
|
47
59
|
rack (>= 0.4)
|
48
60
|
rack-ssl (1.3.2)
|
49
61
|
rack
|
50
|
-
rack-test (0.6.
|
62
|
+
rack-test (0.6.2)
|
51
63
|
rack (>= 1.0)
|
52
64
|
rails (3.2.8)
|
53
65
|
actionmailer (= 3.2.8)
|
@@ -71,6 +83,7 @@ GEM
|
|
71
83
|
hike (~> 1.2)
|
72
84
|
rack (~> 1.0)
|
73
85
|
tilt (~> 1.1, != 1.3.0)
|
86
|
+
sqlite3 (1.3.6)
|
74
87
|
thor (0.16.0)
|
75
88
|
tilt (1.3.3)
|
76
89
|
treetop (1.4.10)
|
@@ -82,4 +95,7 @@ PLATFORMS
|
|
82
95
|
ruby
|
83
96
|
|
84
97
|
DEPENDENCIES
|
98
|
+
coffee-rails (~> 3.2.1)
|
99
|
+
jquery-rails
|
85
100
|
rails (~> 3.2.0)
|
101
|
+
sqlite3
|