roo_on_rails 1.11.1 → 1.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +28 -0
- data/lib/roo_on_rails/concerns/require_api_key.rb +108 -0
- data/lib/roo_on_rails/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05edf07734c3e18175c4b7f10d220b4443e8b23f
|
4
|
+
data.tar.gz: fd09c771c91cfb18b2c1cc31ec0566f4585eee19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bbcfae06c1983dddf2bf86a037207bd93dc59ec86ba9772320a8e90e1d16bf6b034b848cf23292f755eb6e50557a99a0c6952d3099d5070386838db3904e404
|
7
|
+
data.tar.gz: d7c935d825c03932d8cea692b4f9fefd8b971433283b4e4fe480143d3e4f043ae5260c4240a29369add01af2e62110d5fbef0062400dcca4b5b3daf88f366e14
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -256,6 +256,34 @@ When `ROUTEMASTER_ENABLED` is set to `true` we attempt to configure [`routemaste
|
|
256
256
|
|
257
257
|
If you then want to enable the publishing of events onto the event bus, you need to set `ROUTEMASTER_PUBLISHING_ENABLED` to `true` and implement publishers as needed. An example of how to do this is detailed in [`README.routemaster_client.md`](README.routemaster_client.md).
|
258
258
|
|
259
|
+
### API Authentication
|
260
|
+
|
261
|
+
RooOnRails provides a concern which will make adding rotatable API authentication to your service a breeze:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
require 'roo_on_rails/concerns/require_api_key'
|
265
|
+
|
266
|
+
class ThingController < ActionController::Base
|
267
|
+
require_api_key
|
268
|
+
# or
|
269
|
+
require_api_key(only: :update)
|
270
|
+
# or
|
271
|
+
require_api_key(only_services: %i(service_1 service_2))
|
272
|
+
|
273
|
+
def index
|
274
|
+
# etc
|
275
|
+
end
|
276
|
+
```
|
277
|
+
|
278
|
+
Keys are specified in environment variables ending with `_CLIENT_KEY`, where the value is a comma separated list of keys which the specified service can authenticate with. This means that if your service has the environment variables:
|
279
|
+
|
280
|
+
```
|
281
|
+
SERVICE_1_CLIENT_KEY=abc123abc123,def456def456
|
282
|
+
SERVICE_2_CLIENT_KEY=I-never-could-get-the-hang-of-Thursdays
|
283
|
+
```
|
284
|
+
|
285
|
+
Then, for any controller where this concern has been initiated, Basic Authentication will be required and only `service_1:abc123abc123`, `service_1:def456def456` and `service_2:I-never-could-get-the-hang-of-Thursdays` will be allowed access.
|
286
|
+
|
259
287
|
## Command features
|
260
288
|
|
261
289
|
### Usage
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'action_controller/metal/http_authentication'
|
3
|
+
|
4
|
+
module RooOnRails
|
5
|
+
module Concerns
|
6
|
+
# This concern allows API authentication in a consistent manner.
|
7
|
+
#
|
8
|
+
# If a service connects with basic auth using the username "service" then the
|
9
|
+
# `SERVICE_CLIENT_KEY` environment variable must have the given password as one of
|
10
|
+
# the comma separated strings within it or a 403 will be raised.
|
11
|
+
#
|
12
|
+
# @example: Any service with an acceptable key can access routes
|
13
|
+
#
|
14
|
+
# class ThingController < ApplicationController
|
15
|
+
# include RooOnRails::Concerns::RequireApiKey
|
16
|
+
# require_api_key
|
17
|
+
#
|
18
|
+
# # etc
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example: Only the specified clients can access specific routes in this controller
|
22
|
+
#
|
23
|
+
# class ThingController < ApplicationController
|
24
|
+
# include RooOnRails::Concerns::RequireApiKey
|
25
|
+
# require_api_key(only_services: :my_service, only: :create)
|
26
|
+
#
|
27
|
+
# # etc
|
28
|
+
# end
|
29
|
+
module RequireApiKey
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
include ActionController::HttpAuthentication::Basic::ControllerMethods
|
32
|
+
|
33
|
+
attr_reader :current_client
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
# Declares that routes on the controller must have access credentials specified
|
37
|
+
# in the request that match the approparite environment variables.
|
38
|
+
#
|
39
|
+
# @param :only_services (#to_s,Array<#to_s>) Restricts the services which will be accepted
|
40
|
+
# @see AbstractController::Callbacks::ClassMethods#before_action for additional scoping opts
|
41
|
+
def require_api_key(only_services: nil, **options)
|
42
|
+
before_action(**options) do
|
43
|
+
authenticate_or_request_with_http_basic('Authenitcation required') do |service_name, client_key|
|
44
|
+
Authenticator.new([*only_services]).valid?(service_name, client_key).tap do |is_valid|
|
45
|
+
@current_client = OpenStruct.new(name: service_name).freeze if is_valid
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# This functionality pulled out into a new class for testability
|
53
|
+
class Authenticator
|
54
|
+
def initialize(whitelisted_clients)
|
55
|
+
@whitelisted_clients = whitelisted_clients.map(&:to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid?(service_name, client_key)
|
59
|
+
return false unless whitelisted?(service_name)
|
60
|
+
|
61
|
+
NewRelic::Agent.add_custom_attributes(httpBasicUserId: service_name) if defined?(NewRelic)
|
62
|
+
ClientApiKeys.instance.valid?(service_name, client_key)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def whitelisted?(service_name)
|
68
|
+
return true if @whitelisted_clients.empty?
|
69
|
+
@whitelisted_clients.include?(service_name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ClientApiKeys
|
74
|
+
include Singleton
|
75
|
+
|
76
|
+
CLIENT_KEY_NAME_SUFFIX_REGEX = /_CLIENT_KEY\Z/
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
@cache = ENV.select { |key| key =~ CLIENT_KEY_NAME_SUFFIX_REGEX }
|
80
|
+
.map { |k, v| [service_name(k), parse_client_keys(v)] }
|
81
|
+
.to_h
|
82
|
+
.freeze
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid?(service_name, client_key)
|
86
|
+
return false if service_name == '' || client_key == ''
|
87
|
+
|
88
|
+
client_keys = @cache[normalize(service_name)]
|
89
|
+
client_keys && client_keys.include?(client_key)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def service_name(client_key_name)
|
95
|
+
normalize(client_key_name.sub(CLIENT_KEY_NAME_SUFFIX_REGEX, ''))
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize(service_name)
|
99
|
+
service_name.upcase.gsub(/[A-Z0-9]+/, '_')
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_client_keys(str)
|
103
|
+
(str || '').split(',').map(&:strip).to_set.freeze
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/roo_on_rails/version.rb
CHANGED
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.
|
4
|
+
version: 1.12.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-09-
|
11
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dotenv-rails
|
@@ -415,6 +415,7 @@ files:
|
|
415
415
|
- lib/roo_on_rails/checks/papertrail/token.rb
|
416
416
|
- lib/roo_on_rails/checks/sidekiq/settings.rb
|
417
417
|
- lib/roo_on_rails/checks/sidekiq/sidekiq.rb
|
418
|
+
- lib/roo_on_rails/concerns/require_api_key.rb
|
418
419
|
- lib/roo_on_rails/config.rb
|
419
420
|
- lib/roo_on_rails/context_logging.rb
|
420
421
|
- lib/roo_on_rails/default.env
|