roo_on_rails 1.14.0 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 101bbe2f7ca6f86ba6dc9813c5871be5e18933f2fe75634763eec5b1cfe5adcc
4
- data.tar.gz: 2efc21d30c33572d62183d2f80456c046442b9f2cce8d74d6120fe6e01e029c2
2
+ SHA1:
3
+ metadata.gz: 350514c8b5824a6829103a912a01e155aa18074f
4
+ data.tar.gz: 827cb9bec23c0d69bab1e2df74024e63861853ce
5
5
  SHA512:
6
- metadata.gz: 625ef2e2f32eadc97628075838a991e5271728b1ca282c63cd40b53da344e2bf3e7cd451cd10ec7de8a3025c1e0509eacf5ef498b9e9d7580091b4528b83306c
7
- data.tar.gz: 745e97920deb29280dafe9cc4ed9a72180547823a93f4ea4debbd582a64faa58188b515112443f480087b7a566355bf3bd21c2217ab5082bf3894cc42dc1fd25
6
+ metadata.gz: 5954c887ece5accaeaf6b9acb328ef24978f6d5673ae2ba2ae2cb8d29f0bcc83f1b55346fb500371cba87ad3e8ebeeba2f44d6b0503c96ae7c696a594e03e768
7
+ data.tar.gz: 38aaf68d5cf8271b15597ba0145088f272ceb52cdbeb03602b38fb215dbf4b2220cb6d95008676bfad9062c2a4e831994b6bc1837d450fdc9366819943a701fe
data/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # HEAD
2
2
 
3
- _A description of the awesome feature or bug fix you just wrote!_
3
+ _A description of your awesome new stuff here!_
4
+
5
+ # v1.15.0
6
+
7
+ Features:
8
+
9
+ - Process JWTs in `Authorization` headers and populate the request env's `roo.identity` key with the claims, if present and valid. (#79)
10
+ - RooOnRails::Logger is now compatible with ActiveSupport::Logger on Rails versions >= 4.2 (#77)
11
+
12
+ # v1.14.0
13
+
14
+ Bug Fix:
15
+
16
+ - Routemaster Publisher was sending epoch seconds instead of milliseconds (#78)
4
17
 
5
18
  # v1.13.1 (2017-10-18)
6
19
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,11 @@
1
+ # Contributing
2
+
3
+ PRs are welcome to this repo to add new features that will be useful across our Ruby services.
4
+
5
+ If you're putting a PR together, please ensure that:
6
+
7
+ - There are ample **tests** for your code (they'll be executed against all our supported versions automatically)
8
+ - You've made changes to the **`README.md`** file explaining how to make use of the work you're adding
9
+ - You've added a line to the **`CHANGELOG.md`** file, with a short explanation of what you've added (feel free to add this in a commit after you've created your PR so you can add the PR number)
10
+
11
+ Thanks!
data/README.md CHANGED
@@ -23,6 +23,7 @@
23
23
  - [Sidekiq](#sidekiq)
24
24
  - [HireFire](#hirefire)
25
25
  - [Logging](#logging)
26
+ - [Identity](#identity)
26
27
  - [Google OAuth authentication](#google-oauth-authentication)
27
28
  - [Datadog Integration](#datadog-integration)
28
29
  - [Routemaster Client](#routemaster-client)
@@ -216,6 +217,31 @@ logger.with(a: 1, b: 2).info('Stuff')
216
217
  See the [class documentation](lib/roo_on_rails/logger.rb) for further
217
218
  details.
218
219
 
220
+ ### Identity
221
+
222
+ If your service wants to accept JWTs for identity claims, then adding the `json-jwt` gem
223
+ to your `Gemfile` and the following railtie to your app will ensure any data presented is
224
+ available:
225
+
226
+ ```ruby
227
+ require 'roo_on_rails/railties/roo_identity'
228
+ ```
229
+
230
+ Any inbound request which has a valid JWT will have the claims made available:
231
+
232
+ ```ruby
233
+ class MyController
234
+ def index
235
+ customer_id = request.env['roo.identity']['cust']
236
+ request.env['roo.identity'].class
237
+ # => JSON::JWT
238
+ end
239
+ end
240
+ ```
241
+
242
+ Be aware that maliciously crafted JWTs will raise 401s that your other middleware can present
243
+ and poorly configured JWT set up will raise errors that you'll be able to catch in test.
244
+
219
245
  ### Google OAuth authentication
220
246
 
221
247
  When `GOOGLE_AUTH_ENABLED` is set to true we inject a `Omniauth` Rack middleware
@@ -1,6 +1,12 @@
1
- require 'logger'
2
1
  require 'delegate'
3
2
  require 'roo_on_rails/logfmt'
3
+ require 'rails/version'
4
+
5
+ if Rails::VERSION::MAJOR < 4
6
+ require 'logger'
7
+ else
8
+ require 'active_support/logger'
9
+ end
4
10
 
5
11
  module RooOnRails
6
12
  # A compatible replacement for the standard Logger to provide context, similar
@@ -30,7 +36,7 @@ module RooOnRails
30
36
  class Logger < SimpleDelegator
31
37
  def initialize(io = STDOUT)
32
38
  @show_timestamp = io.tty?
33
- logger = ::Logger.new(io).tap do |l|
39
+ logger = _default_logger_class.new(io).tap do |l|
34
40
  l.formatter = method(:_formatter)
35
41
  end
36
42
  super(logger)
@@ -100,5 +106,13 @@ module RooOnRails
100
106
  thread_key = @_context_stack_key ||= "roo_on_rails:logging_context:#{object_id}".freeze
101
107
  Thread.current[thread_key] ||= [{}]
102
108
  end
109
+
110
+ def _default_logger_class
111
+ if Rails::VERSION::MAJOR < 4
112
+ ::Logger
113
+ else
114
+ ActiveSupport::Logger
115
+ end
116
+ end
103
117
  end
104
118
  end
@@ -0,0 +1,101 @@
1
+ require 'json/jwt'
2
+ require 'faraday'
3
+ require 'faraday_middleware'
4
+
5
+ module RooOnRails
6
+ module Rack
7
+ class PopulateEnvFromJWT
8
+ UnacceptableKeyError = Class.new(RuntimeError)
9
+ # Hardcoded URLs for valid keys per environment. These will change very infrequently.
10
+ VALID_JWK_URL_PREFIXES = YAML.load(
11
+ File.read(File.expand_path('../valid_identity_service_prefixes.yml', __FILE__))
12
+ ).freeze
13
+
14
+ def initialize(app, logger:, skip_sig_verify: true)
15
+ @app = app
16
+ @keys = {}
17
+ @logger = logger
18
+
19
+ if skip_sig_verify && development?
20
+ @logger.warn "JWTs signature verifification has been switched off in development."
21
+ @verify_sigs = false
22
+ else
23
+ @verify_sigs = true
24
+ end
25
+ end
26
+
27
+ def call(env)
28
+ env['roo.identity'] = decode_authorization_header(env['HTTP_AUTHORIZATION'])
29
+ @app.call(env)
30
+
31
+ # Other exceptions will bubble up, allowing the higher middleware to return a 500, which is
32
+ # intentional.
33
+ rescue UnacceptableKeyError, JSON::JWT::Exception => e
34
+ # Identifying user is clearly attempting to hack or has been given a totally incorrect
35
+ # token, log this and flag as Forbidden, without executing the rest of the middleware stack.
36
+ ::NewRelic::Agent.notice_error(e) if defined?(NewRelic)
37
+ [401, {}, []]
38
+ end
39
+
40
+ private
41
+
42
+ def development?
43
+ if ENV['RACK_ENV'].nil?
44
+ @logger.warn "Your RACK_ENV isn't set. You probably want it set to 'development' in dev."
45
+ end
46
+
47
+ ENV['RACK_ENV'] == 'development'
48
+ end
49
+
50
+ # @raise [UnacceptableKeyError,Faraday::Error,OpenSSL::OpenSSLError] From `#public_key`
51
+ # @raise [JSON::JWT::Exception] Bubble ups from `JSON::JWT.decode`
52
+ # @return [JSON::JWT] The list of claims this header makes by way of a JWS token. Will be an
53
+ # empty hash for invalid or absent tokens.
54
+ def decode_authorization_header(header_value)
55
+ return JSON::JWT.new unless (header_value || '').starts_with?('Bearer ')
56
+ jws_token = header_value[7..-1]
57
+
58
+ JSON::JWT.decode(jws_token, :skip_verification).tap do |jwt|
59
+ jwt.verify!(public_key(jwt.header[:jku])) if @verify_sigs
60
+ end
61
+ end
62
+
63
+ def acceptable_key?(key_url)
64
+ return false if key_url.nil?
65
+ @key_prefixes ||= VALID_JWK_URL_PREFIXES[ENV['RACK_ENV']]
66
+ @key_prefixes.any? { |acceptable| key_url.starts_with?(acceptable) }
67
+ end
68
+
69
+ # @raise [UnacceptableKeyError] When the key URL is not from a trusted location
70
+ # @raise [Faraday::Error] When the JWK at the given URL is not retrievable for some reason.
71
+ # See: https://github.com/lostisland/faraday/blob/master/lib/faraday/error.rb
72
+ # @return [JSON::JWK] The JWK for the specified URL
73
+ def public_key(key_url)
74
+ unless acceptable_key?(key_url)
75
+ raise UnacceptableKeyError, "#{key_url} is not a valid Deliveroo Key URL"
76
+ end
77
+
78
+ # NB. don't use ||= memoization, or this middleware can be attacked by
79
+ # being asked to decode large numbers of non-existant key-ids, each of
80
+ # which would fill the @keys hash with a tuple.
81
+ return @keys[key_url] if @keys.key?(key_url)
82
+
83
+ @logger.info "Downloading identity public key from #{key_url}"
84
+ json = http_request.get(key_url).body
85
+ @keys[key_url] = JSON::JWK.new(json)
86
+ rescue Faraday::ParsingError
87
+ raise JSON::JWT::InvalidFormat, 'Downloaded JWK is not a valid JSON file'
88
+ end
89
+
90
+ def http_request
91
+ Faraday.new do |conf|
92
+ conf.response :json
93
+ conf.response :raise_error
94
+ conf.request :json
95
+
96
+ conf.adapter Faraday.default_adapter
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,6 @@
1
+ production:
2
+ - https://deliveroo.co.uk/identity-keys/
3
+ - https://identity.deliveroo.net/identity/keys/
4
+ staging:
5
+ - https://test.deliveroo.co.uk/identity-keys/
6
+ - https://identity-staging.deliveroo.net/identity-keys/
@@ -0,0 +1,24 @@
1
+ require 'roo_on_rails/config'
2
+
3
+ module RooOnRails
4
+ module Railties
5
+ class RooIdentity < Rails::Railtie
6
+ initializer 'roo_on_rails.roo_identity.middleware' do |app|
7
+ Rails.logger.with initializer: 'roo_on_rails.roo_identity' do |log|
8
+ log.debug 'loading'
9
+ _add_middleware(app, log)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def _add_middleware(app, log)
16
+ require 'roo_on_rails/rack/populate_env_from_jwt'
17
+
18
+ app.config.middleware.use RooOnRails::Rack::PopulateEnvFromJWT, logger: log
19
+ rescue LoadError
20
+ log.error 'the json-jwt gem is not in the bundle so Roo Identity will not be available'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module RooOnRails
2
- VERSION = '1.14.0'.freeze
2
+ VERSION = '1.15.0'.freeze
3
3
  end
data/roo_on_rails.gemspec CHANGED
@@ -37,6 +37,9 @@ Gem::Specification.new do |spec|
37
37
  spec.add_runtime_dependency 'faraday_middleware'
38
38
  spec.add_runtime_dependency 'routemaster-client'
39
39
 
40
+ # Optional gems you may add to your project
41
+ spec.add_development_dependency 'json-jwt', '~> 1.8'
42
+
40
43
  spec.add_development_dependency 'bundler', '~> 1.13'
41
44
  spec.add_development_dependency 'rake', '~> 10.0'
42
45
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -46,4 +49,5 @@ Gem::Specification.new do |spec|
46
49
  spec.add_development_dependency 'simplecov'
47
50
  spec.add_development_dependency 'codecov'
48
51
  spec.add_development_dependency 'rack-test'
52
+ spec.add_development_dependency 'rspec-its'
49
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roo_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.0
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien Letessier
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-02 00:00:00.000000000 Z
11
+ date: 2017-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv-rails
@@ -226,6 +226,20 @@ dependencies:
226
226
  - - ">="
227
227
  - !ruby/object:Gem::Version
228
228
  version: '0'
229
+ - !ruby/object:Gem::Dependency
230
+ name: json-jwt
231
+ requirement: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: '1.8'
236
+ type: :development
237
+ prerelease: false
238
+ version_requirements: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - "~>"
241
+ - !ruby/object:Gem::Version
242
+ version: '1.8'
229
243
  - !ruby/object:Gem::Dependency
230
244
  name: bundler
231
245
  requirement: !ruby/object:Gem::Requirement
@@ -352,6 +366,20 @@ dependencies:
352
366
  - - ">="
353
367
  - !ruby/object:Gem::Version
354
368
  version: '0'
369
+ - !ruby/object:Gem::Dependency
370
+ name: rspec-its
371
+ requirement: !ruby/object:Gem::Requirement
372
+ requirements:
373
+ - - ">="
374
+ - !ruby/object:Gem::Version
375
+ version: '0'
376
+ type: :development
377
+ prerelease: false
378
+ version_requirements: !ruby/object:Gem::Requirement
379
+ requirements:
380
+ - - ">="
381
+ - !ruby/object:Gem::Version
382
+ version: '0'
355
383
  description: Scaffolding for building services
356
384
  email:
357
385
  - julien.letessier@gmail.com
@@ -371,6 +399,7 @@ files:
371
399
  - ".travis.yml"
372
400
  - Appraisals
373
401
  - CHANGELOG.md
402
+ - CONTRIBUTING.md
374
403
  - Gemfile
375
404
  - Guardfile
376
405
  - LICENSE.txt
@@ -424,7 +453,9 @@ files:
424
453
  - lib/roo_on_rails/logfmt.rb
425
454
  - lib/roo_on_rails/logger.rb
426
455
  - lib/roo_on_rails/papertrail_client.rb
456
+ - lib/roo_on_rails/rack/populate_env_from_jwt.rb
427
457
  - lib/roo_on_rails/rack/safe_timeouts.rb
458
+ - lib/roo_on_rails/rack/valid_identity_service_prefixes.yml
428
459
  - lib/roo_on_rails/railties/database.rb
429
460
  - lib/roo_on_rails/railties/env.rb
430
461
  - lib/roo_on_rails/railties/google_oauth.rb
@@ -432,6 +463,7 @@ files:
432
463
  - lib/roo_on_rails/railties/logging.rb
433
464
  - lib/roo_on_rails/railties/new_relic.rb
434
465
  - lib/roo_on_rails/railties/rake_tasks.rb
466
+ - lib/roo_on_rails/railties/roo_identity.rb
435
467
  - lib/roo_on_rails/railties/routemaster.rb
436
468
  - lib/roo_on_rails/railties/sidekiq.rb
437
469
  - lib/roo_on_rails/routemaster/lifecycle_events.rb
@@ -470,7 +502,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
470
502
  version: '0'
471
503
  requirements: []
472
504
  rubyforge_project:
473
- rubygems_version: 2.7.0
505
+ rubygems_version: 2.6.11
474
506
  signing_key:
475
507
  specification_version: 4
476
508
  summary: Scaffolding for building services