els_token 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +9 -0
  3. data/Rakefile +38 -0
  4. data/lib/cert/AOLMemberCA +47 -0
  5. data/lib/els_token.rb +161 -0
  6. data/lib/els_token/els_user.rb +81 -0
  7. data/lib/els_token/module_inheritable_attributes.rb +35 -0
  8. data/lib/els_token/version.rb +3 -0
  9. data/lib/tasks/els_token_tasks.rake +4 -0
  10. data/test/dummy/Gemfile +3 -0
  11. data/test/dummy/Gemfile.lock +85 -0
  12. data/test/dummy/README.rdoc +261 -0
  13. data/test/dummy/Rakefile +7 -0
  14. data/test/dummy/app/assets/javascripts/application.js +15 -0
  15. data/test/dummy/app/assets/javascripts/hello.js +2 -0
  16. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  17. data/test/dummy/app/assets/stylesheets/hello.css +4 -0
  18. data/test/dummy/app/controllers/application_controller.rb +18 -0
  19. data/test/dummy/app/controllers/hello_controller.rb +4 -0
  20. data/test/dummy/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy/app/helpers/hello_helper.rb +2 -0
  22. data/test/dummy/app/views/hello/index.html.erb +1 -0
  23. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/test/dummy/config.ru +4 -0
  25. data/test/dummy/config/application.rb +56 -0
  26. data/test/dummy/config/boot.rb +10 -0
  27. data/test/dummy/config/database.yml +25 -0
  28. data/test/dummy/config/els.yml +12 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +37 -0
  31. data/test/dummy/config/environments/production.rb +67 -0
  32. data/test/dummy/config/environments/test.rb +37 -0
  33. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/test/dummy/config/initializers/els_token.rb +6 -0
  35. data/test/dummy/config/initializers/inflections.rb +15 -0
  36. data/test/dummy/config/initializers/mime_types.rb +5 -0
  37. data/test/dummy/config/initializers/secret_token.rb +7 -0
  38. data/test/dummy/config/initializers/session_store.rb +8 -0
  39. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  40. data/test/dummy/config/locales/en.yml +5 -0
  41. data/test/dummy/config/routes.rb +60 -0
  42. data/test/dummy/db/development.sqlite3 +0 -0
  43. data/test/dummy/db/schema.rb +16 -0
  44. data/test/dummy/log/development.log +15 -0
  45. data/test/dummy/public/404.html +26 -0
  46. data/test/dummy/public/422.html +26 -0
  47. data/test/dummy/public/500.html +25 -0
  48. data/test/dummy/public/favicon.ico +0 -0
  49. data/test/dummy/script/rails +6 -0
  50. data/test/els_token_test.rb.old +7 -0
  51. data/test/hello_controller_test.rb +32 -0
  52. data/test/test_helper.rb +10 -0
  53. metadata +175 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ = ELS Token
2
+
3
+ A simple library for interfacing with forgeRock OpenAM REST Interface.
4
+
5
+ Still pretty much in development but kind of workable.
6
+
7
+ = TODO
8
+ Refactor to remove explicit Rack based session interrogation.
9
+ Refactor and fix tests
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ElsToken'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task :default => :test
@@ -0,0 +1,47 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEKzCCAxOgAwIBAgIBBzANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
3
+ MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
4
+ bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTA0MDYwNDE3
5
+ MjYzOVoXDTI5MDYwNDE3MjYzOVowZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZp
6
+ cmdpbmlhMQ8wDQYDVQQHEwZEdWxsZXMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5l
7
+ IEluYy4xFjAUBgNVBAMTDUFPTCBNZW1iZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
8
+ A4IBDwAwggEKAoIBAQD4Y9jxzNYRbwXh1p9DJycl+q9nh3Ss3Nb8wpq9M3JOZbBa
9
+ zev4qWlIOW5oLnEWbp5ZfMJ8ze1uQ9gJQk4Nmn3utVojgdKkW6lRVBzf9oTfGcM+
10
+ lC2NuhD46EMIDzI1bDUx+NbT/Akx1qmheiAGWQzgK42EwzegCB7xNXMQ3U/9DHKT
11
+ Jm6vxRw548rzlW8wwoU9TYQgyD490EDW/gZKGHMLbldn24PBE2aX071ZvH76LzZF
12
+ FM28v6tod79I6xGJTmqE810c5WtqAOZrjUikCbkh3C1mKfRWnvAFaP/MwcmIvNIs
13
+ C68ddBqGaKRtFHTsJID4lbnzLjw9IG8JAjjqOjgFAgMBAAGjgeUwgeIwDgYDVR0P
14
+ AQH/BAQDAgGGMB0GA1UdDgQWBBRhppltJJ8OEYjmOeD+dNEFaVKpQzAfBgNVHSME
15
+ GDAWgBQArdmj9nn2bnSpfzM9gRfXTM8z3jAPBgNVHRMBAf8EBTADAQH/MEgGA1Ud
16
+ IARBMD8wPQYEVR0gADA1MDMGCCsGAQUFBwIBFidodHRwczovL3BraS1pbmZvLmFv
17
+ bC5jb20vQU9ML2luZGV4Lmh0bWwwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2Ny
18
+ bC5hb2wuY29tL0FPTC9NYXN0ZXJDUkwuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQAM
19
+ nbzMHdQukRuvoDrrzV78JSopjrMg4Bc3+vy7xbUUuxxmDm9YX8Zx0BOJx60jC+1M
20
+ uFjB48KkJExldg2zhmRPKLrPlvhlmg6CJvWChU41ILNFzGDuD04glDorL8sjEJtG
21
+ G37DVnVJJKS4TZ8caNTm8i+vju0rt+WWaxw9jb8g028tVC+ceTX92gbeaCAgS69d
22
+ q15mwxRke/cC5ieWrRgeq/OCYPxMX7YKUnuenDsuzjxCXzZta/6hdooiIf1b6L1/
23
+ n85RdEhsrLXRomr6B0Te0NupjRgf8bnF6Crruj07GIzADDCzySEcM0w6SVPUqLq6
24
+ OCM9OmWCXnlxFfglK30Z
25
+ -----END CERTIFICATE-----
26
+ -----BEGIN CERTIFICATE-----
27
+ MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
28
+ MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
29
+ bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
30
+ MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
31
+ ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
32
+ Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
33
+ ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
34
+ hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
35
+ 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
36
+ OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
37
+ 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
38
+ O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
39
+ AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
40
+ AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
41
+ BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
42
+ Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
43
+ LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
44
+ oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
45
+ MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
46
+ sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
47
+ -----END CERTIFICATE-----
@@ -0,0 +1,161 @@
1
+ require 'els_token/module_inheritable_attributes'
2
+ require 'els_token/els_user'
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module ElsToken
7
+
8
+ RootCA = "#{File.dirname(__FILE__)}/../cert/AOLMemberCA"
9
+
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ base.send :include, ElsToken::ModuleInheritableAttributes
13
+ base.send :mattr_inheritable, :els_options
14
+ base.instance_variable_set("@els_options", {})
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ # els_config expects a nice hash telling
20
+ # it if it will have a fake user (handy for dev)
21
+ # and the url of the openAM REST API.
22
+ # { faker => { user => 'username',
23
+ # :environments => ['dev','test'] },
24
+ # uri => 'https://openam.url' }
25
+ #
26
+ # class MyController
27
+ # include ElsToken
28
+ # els_config options_hash
29
+ # end
30
+ def els_config(options = {})
31
+ @els_options = options
32
+ end
33
+
34
+ end
35
+
36
+ # authenticates against ELS and returns the user token
37
+ def authenticate(username,password)
38
+
39
+ begin
40
+ response = els_http_request("/authenticate","uri=realm=aolcorporate&username=#{username}&password=#{password}")
41
+ if response.code.eql? "200"
42
+ # return the token
43
+ response.body.chomp.sub(/token\.id=/,"")
44
+ else
45
+ raise response.error!
46
+ end
47
+ rescue Net::HTTPExceptions => e1
48
+ raise e1, "token retrieval failed for #{username}"
49
+ rescue Exception => e
50
+ # Do not expect these. Wrapping the exception so
51
+ # as to not reveal the passed in password
52
+ raise e, "unable to fetch token for #{username}"
53
+ end
54
+ end
55
+
56
+ def is_token_valid?(token)
57
+ response = els_http_request("/isTokenValid","tokenid=#{token}")
58
+ if response.code.eql? "200"
59
+ true
60
+ else
61
+ false
62
+ end
63
+ end
64
+
65
+ #extract the token from the rack cookie
66
+ def is_cookie_token_valid?
67
+ return true if fake_it?
68
+ token = cookies[self.class.els_options['cookie']]
69
+ if token.nil? || !is_token_valid?(token)
70
+ false
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ # obtain a full ElsIdentity object
77
+ def get_token_identity(token)
78
+ response = els_http_request("/attributes","subjectid=#{token}")
79
+ if response.code.eql? "200"
80
+ ElsIdentity.new(response.body)
81
+ else
82
+ response.error!
83
+ end
84
+ end
85
+
86
+ # When used inside a rack environment
87
+ # will attempt to retrieve the user token
88
+ # from the session cookie and return a full
89
+ # identity
90
+ def get_identity
91
+ return fake_id if fake_it?
92
+ begin
93
+ if is_cookie_token_valid?
94
+ get_token_identity cookies[self.class.els_options['cookie']]
95
+ else
96
+ raise "token is invalid"
97
+ end
98
+ rescue Exception => e
99
+ raise e
100
+ end
101
+ end
102
+
103
+ def method_missing(m, *args, &block)
104
+ puts "Drop the crack pipe - There is no method called #{m}"
105
+ end
106
+
107
+ private
108
+
109
+ def els_http_request(url_base_extension, query_string)
110
+ uri = URI.parse(self.class.els_options['uri'] + url_base_extension)
111
+ uri.query=query_string
112
+ http = Net::HTTP.new(uri.host,uri.port)
113
+ http.use_ssl = true
114
+
115
+ # Override the default CA if option is
116
+ # passed in
117
+ if rootca = self.class.els_options[:cert]
118
+ if File.exist? rootca
119
+ http.ca_file = rootca
120
+ elsif Dir.exist? rootca
121
+ http.ca.path = rootca
122
+ else
123
+ # throw - if option passed in we are not
124
+ # going to attempt to use the default cert
125
+ raise "${rootca} cannot be found"
126
+ end
127
+ else
128
+ http.ca_file = RootCA
129
+ end
130
+
131
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
132
+ http.verify_depth = 5
133
+
134
+ request = Net::HTTP::Get.new(uri.request_uri)
135
+
136
+ http.request(request)
137
+ end
138
+
139
+ def fake_it?
140
+ self.class.els_options.has_key? 'faker'
141
+ end
142
+
143
+ def fake_id
144
+ unless @fake_id
145
+ id = ElsIdentity.new
146
+ id.instance_variable_set("@roles",self.class.els_options['faker']['roles'])
147
+ id.instance_variable_set("@mail",self.class.els_options['faker']['mail'])
148
+ id.instance_variable_set("@last_name",self.class.els_options['faker']['last_name'])
149
+ id.instance_variable_set("@first_name",self.class.els_options['faker']['first_name'])
150
+ id.instance_variable_set("@uac",self.class.els_options['faker']['uac'])
151
+ id.instance_variable_set("@dn",self.class.els_options['faker']['dn'])
152
+ id.instance_variable_set("@common_name",self.class.els_options['faker']['common_name'])
153
+ id.instance_variable_set("@employee_number",self.class.els_options['faker']['employee_number'])
154
+ id.instance_variable_set("@display_name",self.class.els_options['faker']['display_name'])
155
+ id.instance_variable_set("@token_id",self.class.els_options['faker']['token_id'])
156
+ @fake_id = id
157
+ end
158
+ @fake_id
159
+ end
160
+
161
+ end
@@ -0,0 +1,81 @@
1
+ module ElsToken
2
+
3
+
4
+
5
+ class ElsIdentity
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
11
+ end
12
+
13
+ def has_role?(role)
14
+ @roles.include? role
15
+ end
16
+
17
+ def initialize(rest_response = nil)
18
+ @roles = []
19
+ parse(rest_response) if rest_response
20
+ @lines = nil
21
+ end
22
+
23
+ private
24
+
25
+
26
+ def parse(response)
27
+ @lines = response.lines
28
+ begin
29
+ while @lines.peek
30
+ line = @lines.next.chomp
31
+ case
32
+ when line =~ /userdetails\.role/
33
+ @roles << line.split(",")[0].sub(/^userdetails\.role=id=/,"")
34
+
35
+ when line =~ /userdetails\.token\.id/
36
+ @token_id = line.sub(/userdetails\.token\.id=/,"")
37
+
38
+ when line =~ /name=mail/
39
+ self_set :mail
40
+
41
+ when line =~ /attribute\.name=sn/
42
+ self_set :last_name
43
+
44
+ when line =~ /name=useraccountcontrol/
45
+ self_set :uac
46
+ @uac = (uac.to_i & 2 == 0) ? "enabled" : "disabled"
47
+
48
+ when line =~ /name=givenname/
49
+ self_set :first_name
50
+
51
+ when line =~ /name=distinguishedname/
52
+ self_set :dn
53
+
54
+ when line =~ /name=employeenumber/
55
+ self_set :employee_number
56
+
57
+ when line =~ /name=cn/
58
+ self_set :common_name
59
+
60
+ when line =~ /name=name/
61
+ self_set :name
62
+
63
+ when line =~ /name=displayname/
64
+ self_set :display_name
65
+ end
66
+ end
67
+ rescue
68
+ # nadda
69
+ end
70
+ end
71
+
72
+ USER_ATTRIB_VALUE = /^userdetails\.attribute\.value=/
73
+
74
+ def self_set(v)
75
+ self.instance_variable_set("@#{v.to_s}",
76
+ @lines.next.chomp.sub(USER_ATTRIB_VALUE,""))
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,35 @@
1
+ # With thanks to HTTParty: https://github.com/jnunemaker/httparty/blob/master/lib/httparty/module_inheritable_attributes.rb
2
+ module ElsToken
3
+ module ModuleInheritableAttributes
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def mattr_inheritable(*args)
10
+ @mattr_inheritable_attrs ||= [:mattr_inheritable_attrs]
11
+ @mattr_inheritable_attrs += args
12
+ args.each do |arg|
13
+ module_eval %(class << self; attr_accessor :#{arg} end)
14
+ end
15
+ @mattr_inheritable_attrs
16
+ end
17
+
18
+ def inherited(subclass)
19
+ super
20
+ @mattr_inheritable_attrs.each do |inheritable_attribute|
21
+ ivar = "@#{inheritable_attribute}"
22
+ subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
23
+ if instance_variable_get(ivar).respond_to?(:merge)
24
+ method = <<-EOM
25
+ def self.#{inheritable_attribute}
26
+ #{ivar} = superclass.#{inheritable_attribute}.merge #{ivar}
27
+ end
28
+ EOM
29
+ subclass.class_eval method
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module ElsToken
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :els_token do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rails', '~> 3.2.0'
@@ -0,0 +1,85 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (3.2.8)
5
+ actionpack (= 3.2.8)
6
+ mail (~> 2.4.4)
7
+ actionpack (3.2.8)
8
+ activemodel (= 3.2.8)
9
+ activesupport (= 3.2.8)
10
+ builder (~> 3.0.0)
11
+ erubis (~> 2.7.0)
12
+ journey (~> 1.0.4)
13
+ rack (~> 1.4.0)
14
+ rack-cache (~> 1.2)
15
+ rack-test (~> 0.6.1)
16
+ sprockets (~> 2.1.3)
17
+ activemodel (3.2.8)
18
+ activesupport (= 3.2.8)
19
+ builder (~> 3.0.0)
20
+ activerecord (3.2.8)
21
+ activemodel (= 3.2.8)
22
+ activesupport (= 3.2.8)
23
+ arel (~> 3.0.2)
24
+ tzinfo (~> 0.3.29)
25
+ activeresource (3.2.8)
26
+ activemodel (= 3.2.8)
27
+ activesupport (= 3.2.8)
28
+ activesupport (3.2.8)
29
+ i18n (~> 0.6)
30
+ multi_json (~> 1.0)
31
+ arel (3.0.2)
32
+ builder (3.0.3)
33
+ erubis (2.7.0)
34
+ hike (1.2.1)
35
+ i18n (0.6.1)
36
+ journey (1.0.4)
37
+ json (1.7.5)
38
+ mail (2.4.4)
39
+ i18n (>= 0.4.0)
40
+ mime-types (~> 1.16)
41
+ treetop (~> 1.4.8)
42
+ mime-types (1.19)
43
+ multi_json (1.3.6)
44
+ polyglot (0.3.3)
45
+ rack (1.4.1)
46
+ rack-cache (1.2)
47
+ rack (>= 0.4)
48
+ rack-ssl (1.3.2)
49
+ rack
50
+ rack-test (0.6.1)
51
+ rack (>= 1.0)
52
+ rails (3.2.8)
53
+ actionmailer (= 3.2.8)
54
+ actionpack (= 3.2.8)
55
+ activerecord (= 3.2.8)
56
+ activeresource (= 3.2.8)
57
+ activesupport (= 3.2.8)
58
+ bundler (~> 1.0)
59
+ railties (= 3.2.8)
60
+ railties (3.2.8)
61
+ actionpack (= 3.2.8)
62
+ activesupport (= 3.2.8)
63
+ rack-ssl (~> 1.3.2)
64
+ rake (>= 0.8.7)
65
+ rdoc (~> 3.4)
66
+ thor (>= 0.14.6, < 2.0)
67
+ rake (0.9.2.2)
68
+ rdoc (3.12)
69
+ json (~> 1.4)
70
+ sprockets (2.1.3)
71
+ hike (~> 1.2)
72
+ rack (~> 1.0)
73
+ tilt (~> 1.1, != 1.3.0)
74
+ thor (0.16.0)
75
+ tilt (1.3.3)
76
+ treetop (1.4.10)
77
+ polyglot
78
+ polyglot (>= 0.3.1)
79
+ tzinfo (0.3.33)
80
+
81
+ PLATFORMS
82
+ ruby
83
+
84
+ DEPENDENCIES
85
+ rails (~> 3.2.0)