maitre_d 0.4.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +2 -1
- data/Gemfile +0 -5
- data/README.textile +24 -54
- data/addon-manifest.json +11 -4
- data/lib/maitre_d.rb +4 -1
- data/lib/maitre_d/api.rb +32 -0
- data/lib/maitre_d/api/authenticated.rb +30 -0
- data/lib/maitre_d/api/authentication_guard.rb +25 -0
- data/lib/maitre_d/api/change_plan.rb +15 -0
- data/lib/maitre_d/api/create.rb +7 -0
- data/lib/maitre_d/api/delete.rb +13 -0
- data/lib/maitre_d/api/sso.rb +45 -0
- data/lib/maitre_d/api/sso_guard.rb +31 -0
- data/lib/maitre_d/heroku.rb +4 -4
- data/maitre_d.gemspec +8 -7
- data/spec/api/heroku/provisioning_spec.rb +49 -30
- data/spec/api/heroku/single_sign_on_spec.rb +30 -20
- data/spec/internal/app/listeners/heroku_listener.rb +4 -4
- data/spec/internal/config/initializers/heroku.rb +2 -2
- data/spec/internal/config/routes.rb +2 -5
- data/spec/spec_helper.rb +1 -2
- metadata +57 -57
- data/lib/maitre_d/broadstack.rb +0 -40
- data/lib/maitre_d/broadstack/api.rb +0 -49
- data/lib/maitre_d/broadstack/api_helpers.rb +0 -44
- data/lib/maitre_d/cloud_control.rb +0 -40
- data/lib/maitre_d/cloud_control/api.rb +0 -47
- data/lib/maitre_d/cloud_control/api_helpers.rb +0 -28
- data/lib/maitre_d/heroku/api.rb +0 -47
- data/lib/maitre_d/heroku/api_helpers.rb +0 -46
- data/lib/maitre_d/heroku/listener.rb +0 -17
- data/lib/maitre_d/opperator.rb +0 -25
- data/lib/maitre_d/opperator/api.rb +0 -25
- data/lib/maitre_d/opperator/api_helpers.rb +0 -11
- data/lib/maitre_d/opperator/listener.rb +0 -13
- data/spec/api/broadstack/provisioning_spec.rb +0 -94
- data/spec/api/broadstack/single_sign_on_spec.rb +0 -49
- data/spec/api/cloud_control/provisioning_spec.rb +0 -94
- data/spec/api/cloud_control/single_sign_on_spec.rb +0 -49
- data/spec/api/opperator/provisioning_spec.rb +0 -55
- data/spec/internal/app/listeners/broadstack_listener.rb +0 -30
- data/spec/internal/app/listeners/cloud_control_listener.rb +0 -29
- data/spec/internal/app/listeners/opperator_listener.rb +0 -16
- data/spec/internal/config/initializers/broadstack.rb +0 -8
- data/spec/internal/config/initializers/cloud_control.rb +0 -8
- data/spec/internal/config/initializers/opperator.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5887c3acaa5919c45a510a84790bb775981f8671c217cdecb00e309ba77692d0
|
4
|
+
data.tar.gz: b4b5aff28f23c8f5c3babb222f625edd97e8dba7308397f97e7981931bef4d04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a22aa34223c9931835013c46379aecc12181deee3e8127f6e24e43d7ac819e50aabdcff6f32692a07ccf1d1a3d6e6d5ce3854cd3f0c06aea8d18c08efdb47515
|
7
|
+
data.tar.gz: 4be65747aeaa43a1c34845e3c9f3acbe9fec96466f2510eb926f935b3f9d34bd475ad65cc5662f8919ede68af7a1615a9f3f42b49df43aee9cf0595555571a34
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.textile
CHANGED
@@ -1,32 +1,26 @@
|
|
1
1
|
h1. Maître d'
|
2
2
|
|
3
|
-
"!https://secure.travis-ci.org/flying-sphinx/maitre_d.
|
3
|
+
"!https://secure.travis-ci.org/flying-sphinx/maitre_d.svg!":http://travis-ci.org/flying-sphinx/maitre_d
|
4
4
|
|
5
|
-
Rack APIs powered by
|
5
|
+
Rack APIs powered by Sliver for managing Heroku add-ons.
|
6
6
|
|
7
7
|
Maître d' manages all the authorisation checking for API requests and provides simple hooks for you to write just the code you need to handle provisioning, plan changes, deprovisioning and single-sign-on (SSO) requests.
|
8
8
|
|
9
9
|
h2. Installing
|
10
10
|
|
11
|
-
Add the following to your Gemfile
|
11
|
+
Add the following to your Gemfile:
|
12
12
|
|
13
|
-
<pre><code>gem '
|
14
|
-
:git => 'git://github.com/intridea/grape.git',
|
15
|
-
:ref => '212c96cdfb253a59bc93790808c568e559d04468'
|
16
|
-
gem 'maitre_d', '~> 0.4.0'</code></pre>
|
13
|
+
<pre><code>gem 'maitre_d', '~> 0.7.1'</code></pre>
|
17
14
|
|
18
15
|
h3. With Rails
|
19
16
|
|
20
|
-
Add the
|
17
|
+
Add the Rack app to your routes file:
|
21
18
|
|
22
|
-
<pre><code>mount MaitreD::
|
23
|
-
mount MaitreD::Heroku::API => '/heroku'
|
24
|
-
mount MaitreD::CloudControl::API => '/cloudcontrol'
|
25
|
-
mount MaitreD::Opperator::API => '/opperator'</code></pre>
|
19
|
+
<pre><code>mount MaitreD::API.new(MaitreD::Heroku) => '/heroku'</code></pre>
|
26
20
|
|
27
21
|
h3. Without Rails
|
28
22
|
|
29
|
-
|
23
|
+
As shown above, you can use Maître d' Rack API, mounting them to wherever you see fit. Once upon a time Heroku expected its endpoints to be at /heroku, but now things are a little more flexible.
|
30
24
|
|
31
25
|
h2. Configuration
|
32
26
|
|
@@ -39,44 +33,28 @@ MaitreD::Heroku.configure do |config|
|
|
39
33
|
config.password = 'random'
|
40
34
|
config.sso_salt = 'gibberish'
|
41
35
|
config.listener = HerokuListener
|
42
|
-
end
|
43
|
-
|
44
|
-
require 'maitre_d/cloud_control'
|
45
|
-
|
46
|
-
MaitreD::CloudControl.configure do |config|
|
47
|
-
config.id = 'addon-id'
|
48
|
-
config.password = 'random'
|
49
|
-
config.sso_salt = 'gibberish'
|
50
|
-
config.listener = CloudControlListener
|
51
|
-
end
|
52
|
-
|
53
|
-
require 'maitre_d/opperator'
|
54
|
-
|
55
|
-
MaitreD::Opperator.configure do |config|
|
56
|
-
config.shared_secret = 'something-special'
|
57
|
-
config.listener = OpperatorListener
|
58
36
|
end</code></pre>
|
59
37
|
|
60
|
-
The
|
38
|
+
The listener that is mentioned in the code above is a class, which will handle valid API requests. Read on for more details on how to set them up.
|
61
39
|
|
62
|
-
h2.
|
40
|
+
h2. Listeners
|
63
41
|
|
64
|
-
Your
|
42
|
+
Your listener class should handle the following four methods:
|
65
43
|
|
66
|
-
h3. @provision(
|
44
|
+
h3. @provision(params)@
|
67
45
|
|
68
|
-
This gets called when
|
46
|
+
This gets called when the provider is requesting an app be provisioned within your service, and expects a hash to be returned with the following keys:
|
69
47
|
|
70
48
|
<dl>
|
71
49
|
<dt><code>id</code></dt>
|
72
|
-
<dd>Your local resource id, which
|
50
|
+
<dd>Your local resource id, which the provider will use in related requests (to change plans, deprovision or initialise single-sign-on).</dd>
|
73
51
|
<dt><code>config</code></dt>
|
74
52
|
<dd>A hash of the newly provisioned resource's configuration values (that are provided as environment variables to the app in question).</dd>
|
75
53
|
<dt><code>message</code></dt>
|
76
54
|
<dd>An optional message that will be displayed when your add-on is added via the command-line.</dd>
|
77
55
|
</dl>
|
78
56
|
|
79
|
-
h3. @plan_change(resource_id,
|
57
|
+
h3. @plan_change(resource_id, plan)@
|
80
58
|
|
81
59
|
This gets called when an app is upgrading or downgrading from their current plan. You need to return a hash with the following keys:
|
82
60
|
|
@@ -105,13 +83,13 @@ Maître d' will check the token and timestamp provided, and sets up the nav-data
|
|
105
83
|
Here's a very basic example:
|
106
84
|
|
107
85
|
<pre><code>class HerokuListener
|
108
|
-
def provision(
|
109
|
-
plan = Plan.find_by_name plan
|
86
|
+
def provision(params)
|
87
|
+
plan = Plan.find_by_name params["plan"]
|
110
88
|
widget = Widget.create(
|
111
|
-
:heroku_id =>
|
112
|
-
:callback_url => callback_url,
|
89
|
+
:heroku_id => params["uuid"],
|
90
|
+
:callback_url => params["callback_url"],
|
113
91
|
:plan => plan,
|
114
|
-
:region => region
|
92
|
+
:region => params["region"]
|
115
93
|
)
|
116
94
|
|
117
95
|
{
|
@@ -121,9 +99,9 @@ Here's a very basic example:
|
|
121
99
|
}
|
122
100
|
end
|
123
101
|
|
124
|
-
def plan_change(resource_id,
|
102
|
+
def plan_change(resource_id, plan)
|
125
103
|
plan = Plan.find_by_name plan
|
126
|
-
widget = Widget.
|
104
|
+
widget = Widget.find_by! heroku_id: resource_id
|
127
105
|
widget.plan = plan
|
128
106
|
widget.save
|
129
107
|
|
@@ -131,12 +109,12 @@ Here's a very basic example:
|
|
131
109
|
end
|
132
110
|
|
133
111
|
def deprovision(resource_id)
|
134
|
-
widget = Widget.
|
112
|
+
widget = Widget.find_by! heroku_id: resource_id
|
135
113
|
widget.destroy
|
136
114
|
end
|
137
115
|
|
138
116
|
def single_sign_on(resource_id)
|
139
|
-
widget = Widget.
|
117
|
+
widget = Widget.find_by! heroku_id: resource_id
|
140
118
|
|
141
119
|
{
|
142
120
|
:uri => '/my/dashboard',
|
@@ -147,14 +125,6 @@ end</code></pre>
|
|
147
125
|
|
148
126
|
You can have the listener class wherever you like - as long as it's available within the context of your Rails/Rack site, it'll work as expected.
|
149
127
|
|
150
|
-
h2. CloudControl Listener
|
151
|
-
|
152
|
-
CloudControl is pretty much the same as Heroku - it has the same expectations for a listener, so perhaps you can use the same class if you like. Anything that Heroku sends through as the heroku_id becomes cloudcontrol_id - but that's just a matter of treating the given parameter appropriately.
|
153
|
-
|
154
|
-
h2. Opperator Listener
|
155
|
-
|
156
|
-
This listener is currently in progress as the Opperator API is established.
|
157
|
-
|
158
128
|
h2. Contributing
|
159
129
|
|
160
130
|
Contributions are very much welcome - but keep in mind the following:
|
@@ -165,4 +135,4 @@ Contributions are very much welcome - but keep in mind the following:
|
|
165
135
|
|
166
136
|
h2. Credits
|
167
137
|
|
168
|
-
Copyright (c) 2011-
|
138
|
+
Copyright (c) 2011-2020, Maître d' is developed and maintained by Pat Allan, and is released under the open MIT Licence.
|
data/addon-manifest.json
CHANGED
@@ -2,9 +2,16 @@
|
|
2
2
|
"id": "foo",
|
3
3
|
"api": {
|
4
4
|
"config_vars": [ "FOO_PROVISIONED" ],
|
5
|
-
"
|
6
|
-
"
|
7
|
-
"
|
8
|
-
"
|
5
|
+
"regions": [ "us" ],
|
6
|
+
"password": "056e8ff3d55ea6e1a7cc4aa7afeaf3ac",
|
7
|
+
"sso_salt": "db0ce73d4bd2dfa1d66c713a9aaf8f76",
|
8
|
+
"production": {
|
9
|
+
"base_url": "https://yourapp.com/heroku/resources",
|
10
|
+
"sso_url": "https://yourapp.com/sso/login"
|
11
|
+
},
|
12
|
+
"test": {
|
13
|
+
"base_url": "http://localhost:9292/heroku/resources",
|
14
|
+
"sso_url": "http://localhost:9292/heroku/resources/sso"
|
15
|
+
}
|
9
16
|
}
|
10
17
|
}
|
data/lib/maitre_d.rb
CHANGED
data/lib/maitre_d/api.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class MaitreD::API
|
2
|
+
def initialize(configuration)
|
3
|
+
@configuration = configuration
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(environment)
|
7
|
+
environment['maitre_d.configuration'] = configuration
|
8
|
+
|
9
|
+
endpoints.call environment
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :configuration
|
15
|
+
|
16
|
+
def endpoints
|
17
|
+
@endpoints ||= Sliver::API.new do |api|
|
18
|
+
api.connect :post, '/resources/sso', MaitreD::API::SSO
|
19
|
+
api.connect :post, '/resources', MaitreD::API::Create
|
20
|
+
api.connect :put, %r{/resources/[\w-]+}, MaitreD::API::ChangePlan
|
21
|
+
api.connect :delete, %r{/resources/[\w-]+}, MaitreD::API::Delete
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'maitre_d/api/authentication_guard'
|
27
|
+
require 'maitre_d/api/authenticated'
|
28
|
+
require 'maitre_d/api/change_plan'
|
29
|
+
require 'maitre_d/api/create'
|
30
|
+
require 'maitre_d/api/delete'
|
31
|
+
require 'maitre_d/api/sso_guard'
|
32
|
+
require 'maitre_d/api/sso'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class MaitreD::API::Authenticated
|
2
|
+
include Sliver::Action
|
3
|
+
|
4
|
+
def self.guards
|
5
|
+
[MaitreD::API::AuthenticationGuard]
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
response.body = [MultiJson.dump(response.body)]
|
10
|
+
response.status ||= 200
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration
|
14
|
+
environment['maitre_d.configuration']
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def listener
|
20
|
+
configuration.listener.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def params
|
24
|
+
@params ||= MultiJson.load request.body.read
|
25
|
+
end
|
26
|
+
|
27
|
+
def provider_id
|
28
|
+
configuration.provider_id_from params
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class MaitreD::API::AuthenticationGuard < Sliver::Hook
|
2
|
+
def continue?
|
3
|
+
valid_authorization?
|
4
|
+
end
|
5
|
+
|
6
|
+
def respond
|
7
|
+
response.status = 401
|
8
|
+
response.body = ['401 Unauthorized']
|
9
|
+
response.headers['Content-Length'] = response.body.first.length.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def expected_credentials
|
15
|
+
"#{action.configuration.id}:#{action.configuration.password}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def provided_credentials
|
19
|
+
Base64.decode64 action.request.env['HTTP_AUTHORIZATION'].gsub(/^Basic /, '')
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_authorization?
|
23
|
+
provided_credentials == expected_credentials
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class MaitreD::API::ChangePlan < MaitreD::API::Authenticated
|
2
|
+
def call
|
3
|
+
response.body = listener.plan_change(
|
4
|
+
resource_id, params['plan']
|
5
|
+
)
|
6
|
+
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def resource_id
|
13
|
+
request.path[%r{resources/([\w-]+)}, 1]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class MaitreD::API::SSO
|
2
|
+
include Sliver::Action
|
3
|
+
|
4
|
+
def self.guards
|
5
|
+
[MaitreD::API::SSOGuard]
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
hash = listener.single_sign_on params['resource_id']
|
10
|
+
|
11
|
+
hash[:session] ||= {}
|
12
|
+
hash[:session].each { |key, value| session[key] = value }
|
13
|
+
|
14
|
+
if environment['action_dispatch.cookies']
|
15
|
+
environment['action_dispatch.cookies']['heroku-nav-data'] =
|
16
|
+
params['nav-data']
|
17
|
+
else
|
18
|
+
Rack::Utils.set_cookie_header! response.headers, 'heroku-nav-data',
|
19
|
+
:value => params['nav-data']
|
20
|
+
end
|
21
|
+
|
22
|
+
response.status = 302
|
23
|
+
response.body = ["Redirect to #{hash[:uri]}"]
|
24
|
+
response.headers ||= {}
|
25
|
+
response.headers['Location'] = hash[:uri]
|
26
|
+
end
|
27
|
+
|
28
|
+
def configuration
|
29
|
+
environment['maitre_d.configuration']
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def listener
|
35
|
+
configuration.listener.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def params
|
39
|
+
request.params
|
40
|
+
end
|
41
|
+
|
42
|
+
def session
|
43
|
+
environment['rack.session'] ||= {}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class MaitreD::API::SSOGuard < Sliver::Hook
|
2
|
+
def continue?
|
3
|
+
valid_token? && valid_timestamp?
|
4
|
+
end
|
5
|
+
|
6
|
+
def respond
|
7
|
+
response.status = 403
|
8
|
+
response.body = ['403 Forbidden']
|
9
|
+
response.headers['Content-Length'] = response.body.first.length.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def expected_token
|
15
|
+
@expected_token ||= Digest::SHA1.hexdigest(
|
16
|
+
"#{params['resource_id']}:#{action.configuration.sso_salt}:#{params['timestamp']}"
|
17
|
+
).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def params
|
21
|
+
action.request.params
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_timestamp?
|
25
|
+
params['timestamp'].to_i >= (Time.now - 5*60).to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_token?
|
29
|
+
expected_token == params['resource_token']
|
30
|
+
end
|
31
|
+
end
|
data/lib/maitre_d/heroku.rb
CHANGED
@@ -34,8 +34,8 @@ module MaitreD::Heroku
|
|
34
34
|
def self.sso_salt=(salt)
|
35
35
|
@sso_salt = salt
|
36
36
|
end
|
37
|
-
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
def self.provider_id_from(params)
|
39
|
+
params['heroku_id']
|
40
|
+
end
|
41
|
+
end
|
data/maitre_d.gemspec
CHANGED
@@ -3,22 +3,23 @@ $:.push File.expand_path('../lib', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'maitre_d'
|
6
|
-
s.version = '0.
|
6
|
+
s.version = '0.7.1'
|
7
7
|
s.authors = ['Pat Allan']
|
8
8
|
s.email = ['pat@freelancing-gods.com']
|
9
9
|
s.homepage = 'http://github.com/flying-sphinx/maitre_d'
|
10
10
|
s.summary = 'Rack APIs for Heroku add-ons'
|
11
11
|
s.description = 'A Rack API (through Grape) for Heroku add-on providers.'
|
12
12
|
|
13
|
-
s.rubyforge_project = "maitre_d"
|
14
|
-
|
15
13
|
s.files = `git ls-files`.split("\n")
|
16
14
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
16
|
s.require_paths = ['lib']
|
19
17
|
|
20
|
-
s.
|
21
|
-
s.
|
22
|
-
|
23
|
-
s.add_development_dependency '
|
18
|
+
s.add_runtime_dependency 'sliver', '~> 0.2.2'
|
19
|
+
s.add_runtime_dependency 'multi_json', '>= 1.3.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'combustion', '~> 1.3'
|
22
|
+
s.add_development_dependency 'kensa', '2.1.0'
|
23
|
+
s.add_development_dependency 'rails', '~> 6.0'
|
24
|
+
s.add_development_dependency 'rspec-rails', '~> 4.0'
|
24
25
|
end
|