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.
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