els_token 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/README.rdoc +12 -2
  2. data/lib/els_token.rb +209 -113
  3. data/lib/els_token/{els_user.rb → els_identity.rb} +15 -6
  4. data/lib/els_token/version.rb +1 -1
  5. data/test/dummy/Gemfile +9 -0
  6. data/test/dummy/Gemfile.lock +17 -1
  7. data/test/dummy/app/assets/javascripts/application.js +1 -2
  8. data/test/dummy/app/assets/javascripts/els_session.js.coffee +7 -0
  9. data/test/dummy/app/controllers/application_controller.rb +45 -6
  10. data/test/dummy/app/controllers/els_session_controller.rb +91 -0
  11. data/test/dummy/app/models/els_faker.rb +8 -0
  12. data/test/dummy/app/views/els_session/new.html.erb +41 -0
  13. data/test/dummy/app/views/els_session/show.html.erb +4 -0
  14. data/test/dummy/app/views/layouts/application.html.erb +6 -1
  15. data/test/dummy/config/application.rb +3 -39
  16. data/test/dummy/config/els_token.yml +21 -0
  17. data/test/dummy/config/environments/development.rb +1 -1
  18. data/test/dummy/config/environments/uat.rb +67 -0
  19. data/test/dummy/config/initializers/els_token.rb +1 -1
  20. data/test/dummy/config/routes.rb +9 -55
  21. data/test/dummy/log/development.log +2170 -4
  22. data/test/dummy/tmp/cache/assets/C65/500/sprockets%2F53516304d6d54f9499b5a7788631e5c5 +0 -0
  23. data/test/dummy/tmp/cache/assets/CA6/630/sprockets%2F8af9c2296e34c4f01a58747a12c07130 +0 -0
  24. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  25. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  26. data/test/dummy/tmp/cache/assets/D49/140/sprockets%2Fd128539bcb29d65cf7e28a43f8563a2f +0 -0
  27. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  28. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  29. data/test/dummy/tmp/cache/assets/D9C/D00/sprockets%2F66ef3d03a58dfcc273e3453b04fdf81b +0 -0
  30. data/test/dummy/tmp/cache/assets/D9F/AA0/sprockets%2Fab395cb72ff1e290f0de1f739a87bd50 +0 -0
  31. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  32. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  33. metadata +41 -54
  34. data/test/dummy/app/assets/javascripts/hello.js +0 -2
  35. data/test/dummy/app/assets/stylesheets/hello.css +0 -4
  36. data/test/dummy/app/controllers/hello_controller.rb +0 -4
  37. data/test/dummy/app/views/hello/index.html.erb +0 -1
  38. data/test/dummy/config/els.yml +0 -12
  39. data/test/els_token_test.rb.old +0 -7
  40. data/test/hello_controller_test.rb +0 -32
@@ -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
- Refactor to remove explicit Rack based session interrogation (drop rails dependency).
19
- Refactor and fix tests
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 :)
@@ -1,5 +1,5 @@
1
1
  require 'els_token/module_inheritable_attributes'
2
- require 'els_token/els_user'
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 nice hash telling
18
- # it if it will have a fake user (handy for dev)
19
- # and the url of the openAM REST API.
20
- # { faker => { user => 'username',
21
- # :environments => ['dev','test'] },
22
- # uri => 'https://openam.url' }
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
- @els_options = options
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
- end
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
- # authenticates against ELS and returns the user token
35
- def authenticate(username,password)
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
- begin
38
- response = els_http_request("/authenticate","uri=realm=aolcorporate&username=#{username}&password=#{password}")
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
- # return the token
41
- response.body.chomp.sub(/token\.id=/,"")
99
+ true
42
100
  else
43
- raise response.error!
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
- end
53
-
54
- def is_token_valid?(token)
55
- response = els_http_request("/isTokenValid","tokenid=#{token}")
56
- if response.code.eql? "200"
57
- true
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
- #extract the token from the rack cookie
64
- def is_cookie_token_valid?
65
- return true if fake_it?
66
- token = cookies[self.class.els_options['cookie']]
67
- if token.nil? || !is_token_valid?(token)
68
- false
69
- else
70
- true
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
- # obtain a full ElsIdentity object
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
- response = els_http_request("/attributes","subjectid=#{token}")
77
- if response.code.eql? "200"
78
- ElsIdentity.new(response.body)
79
- else
80
- response.error!
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
- return fake_id if fake_it?
90
- begin
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
- def els_http_request(url_base_extension, query_string)
108
- uri = URI.parse(self.class.els_options['uri'] + url_base_extension)
109
- uri.query=query_string
110
- http = Net::HTTP.new(uri.host,uri.port)
111
- http.use_ssl = true
112
-
113
- # Use a known certificate if supplied
114
- if rootca = self.class.els_options[:cert]
115
- if File.exist? rootca
116
- http.ca_file = rootca
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
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
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
- def fake_it?
134
- self.class.els_options.has_key? 'faker'
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 fake_id
138
- unless @fake_id
139
- id = ElsIdentity.new
140
- id.instance_variable_set("@roles",self.class.els_options['faker']['roles'])
141
- id.instance_variable_set("@mail",self.class.els_options['faker']['mail'])
142
- id.instance_variable_set("@last_name",self.class.els_options['faker']['last_name'])
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
- def cdid
10
- @name
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
@@ -1,3 +1,3 @@
1
1
  module ElsToken
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -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'
@@ -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.1)
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