els_token 1.0.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 (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)