maitre_d 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/README.textile +20 -32
- data/lib/maitre_d/api/change_plan.rb +2 -2
- data/lib/maitre_d/api/create.rb +1 -4
- data/lib/maitre_d/api/delete.rb +1 -1
- data/lib/maitre_d/api/sso.rb +1 -1
- data/lib/maitre_d/api/sso_guard.rb +2 -2
- data/maitre_d.gemspec +4 -6
- data/spec/api/heroku/provisioning_spec.rb +30 -20
- data/spec/api/heroku/single_sign_on_spec.rb +20 -10
- data/spec/internal/app/listeners/heroku_listener.rb +3 -3
- data/spec/internal/config/routes.rb +1 -2
- metadata +9 -19
- data/lib/maitre_d/cloud_control.rb +0 -41
- data/spec/api/cloud_control/provisioning_spec.rb +0 -94
- data/spec/api/cloud_control/single_sign_on_spec.rb +0 -49
- data/spec/internal/app/listeners/cloud_control_listener.rb +0 -29
- data/spec/internal/config/initializers/cloud_control.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 764dc0a04e766ea678f0bde9321fd3328dd98a138094a129f36845c409dfad27
|
4
|
+
data.tar.gz: ae4e4c87d345644f054f6b8ff2c56ede552d77ebe98012612f665a72dc31e520
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3a147e73fae54e2c366fd269aa1be216295e80dd909285b31e997501d592b2040591a8fc4e9204f4c034b7da36dc47cdcca89df2a07f0b089a0cb5e9102195a
|
7
|
+
data.tar.gz: 42b838f44359b580ef759979c7bf3fd67acfb477ffb6bc972b7ffe9585767ad09b1077619a01507bd358592020af2a01b5c17a784be9e3e3b29f37cb1e5c54e9
|
data/.travis.yml
CHANGED
data/README.textile
CHANGED
@@ -1,8 +1,8 @@
|
|
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 Sliver for managing Heroku
|
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
|
|
@@ -10,20 +10,17 @@ h2. Installing
|
|
10
10
|
|
11
11
|
Add the following to your Gemfile:
|
12
12
|
|
13
|
-
<pre><code>gem 'maitre_d', '~> 0.
|
13
|
+
<pre><code>gem 'maitre_d', '~> 0.7.0'</code></pre>
|
14
14
|
|
15
15
|
h3. With Rails
|
16
16
|
|
17
|
-
Add the
|
17
|
+
Add the Rack app to your routes file:
|
18
18
|
|
19
|
-
<pre><code
|
20
|
-
mount MaitreD::API.new(MaitreD::Heroku) => '/heroku'
|
21
|
-
# if you're supporting CloudControl
|
22
|
-
mount MaitreD::API.new(MaitreD::CloudControl) => '/cloudcontrol'</code></pre>
|
19
|
+
<pre><code>mount MaitreD::API.new(MaitreD::Heroku) => '/heroku'</code></pre>
|
23
20
|
|
24
21
|
h3. Without Rails
|
25
22
|
|
26
|
-
As shown above, you can use Maître d' Rack API
|
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.
|
27
24
|
|
28
25
|
h2. Configuration
|
29
26
|
|
@@ -36,24 +33,15 @@ MaitreD::Heroku.configure do |config|
|
|
36
33
|
config.password = 'random'
|
37
34
|
config.sso_salt = 'gibberish'
|
38
35
|
config.listener = HerokuListener
|
39
|
-
end
|
40
|
-
|
41
|
-
require 'maitre_d/cloud_control'
|
42
|
-
|
43
|
-
MaitreD::CloudControl.configure do |config|
|
44
|
-
config.id = 'addon-id'
|
45
|
-
config.password = 'random'
|
46
|
-
config.sso_salt = 'gibberish'
|
47
|
-
config.listener = CloudControlListener
|
48
36
|
end</code></pre>
|
49
37
|
|
50
|
-
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.
|
51
39
|
|
52
40
|
h2. Listeners
|
53
41
|
|
54
|
-
Your listener
|
42
|
+
Your listener class should handle the following four methods:
|
55
43
|
|
56
|
-
h3. @provision(
|
44
|
+
h3. @provision(params)@
|
57
45
|
|
58
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:
|
59
47
|
|
@@ -66,7 +54,7 @@ This gets called when the provider is requesting an app be provisioned within yo
|
|
66
54
|
<dd>An optional message that will be displayed when your add-on is added via the command-line.</dd>
|
67
55
|
</dl>
|
68
56
|
|
69
|
-
h3. @plan_change(resource_id,
|
57
|
+
h3. @plan_change(resource_id, plan)@
|
70
58
|
|
71
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:
|
72
60
|
|
@@ -95,13 +83,13 @@ Maître d' will check the token and timestamp provided, and sets up the nav-data
|
|
95
83
|
Here's a very basic example:
|
96
84
|
|
97
85
|
<pre><code>class HerokuListener
|
98
|
-
def provision(
|
99
|
-
plan = Plan.find_by_name plan
|
86
|
+
def provision(params)
|
87
|
+
plan = Plan.find_by_name params["plan"]
|
100
88
|
widget = Widget.create(
|
101
|
-
:heroku_id =>
|
102
|
-
:callback_url => callback_url,
|
89
|
+
:heroku_id => params["uuid"],
|
90
|
+
:callback_url => params["callback_url"],
|
103
91
|
:plan => plan,
|
104
|
-
:region => region
|
92
|
+
:region => params["region"]
|
105
93
|
)
|
106
94
|
|
107
95
|
{
|
@@ -111,9 +99,9 @@ Here's a very basic example:
|
|
111
99
|
}
|
112
100
|
end
|
113
101
|
|
114
|
-
def plan_change(resource_id,
|
102
|
+
def plan_change(resource_id, plan)
|
115
103
|
plan = Plan.find_by_name plan
|
116
|
-
widget = Widget.
|
104
|
+
widget = Widget.find_by! heroku_id: resource_id
|
117
105
|
widget.plan = plan
|
118
106
|
widget.save
|
119
107
|
|
@@ -121,12 +109,12 @@ Here's a very basic example:
|
|
121
109
|
end
|
122
110
|
|
123
111
|
def deprovision(resource_id)
|
124
|
-
widget = Widget.
|
112
|
+
widget = Widget.find_by! heroku_id: resource_id
|
125
113
|
widget.destroy
|
126
114
|
end
|
127
115
|
|
128
116
|
def single_sign_on(resource_id)
|
129
|
-
widget = Widget.
|
117
|
+
widget = Widget.find_by! heroku_id: resource_id
|
130
118
|
|
131
119
|
{
|
132
120
|
:uri => '/my/dashboard',
|
@@ -147,4 +135,4 @@ Contributions are very much welcome - but keep in mind the following:
|
|
147
135
|
|
148
136
|
h2. Credits
|
149
137
|
|
150
|
-
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.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class MaitreD::API::ChangePlan < MaitreD::API::Authenticated
|
2
2
|
def call
|
3
3
|
response.body = listener.plan_change(
|
4
|
-
resource_id,
|
4
|
+
resource_id, params['plan']
|
5
5
|
)
|
6
6
|
|
7
7
|
super
|
@@ -10,6 +10,6 @@ class MaitreD::API::ChangePlan < MaitreD::API::Authenticated
|
|
10
10
|
private
|
11
11
|
|
12
12
|
def resource_id
|
13
|
-
request.path[%r{resources/(\
|
13
|
+
request.path[%r{resources/([\w-]+)}, 1]
|
14
14
|
end
|
15
15
|
end
|
data/lib/maitre_d/api/create.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
class MaitreD::API::Create < MaitreD::API::Authenticated
|
2
2
|
def call
|
3
|
-
response.body = listener.provision(
|
4
|
-
provider_id, params['plan'], params['region'],
|
5
|
-
params['callback_url'], params['logplex_token'], params['options']
|
6
|
-
)
|
3
|
+
response.body = listener.provision(params)
|
7
4
|
|
8
5
|
super
|
9
6
|
end
|
data/lib/maitre_d/api/delete.rb
CHANGED
data/lib/maitre_d/api/sso.rb
CHANGED
@@ -13,7 +13,7 @@ class MaitreD::API::SSOGuard < Sliver::Hook
|
|
13
13
|
|
14
14
|
def expected_token
|
15
15
|
@expected_token ||= Digest::SHA1.hexdigest(
|
16
|
-
"#{params['
|
16
|
+
"#{params['resource_id']}:#{action.configuration.sso_salt}:#{params['timestamp']}"
|
17
17
|
).to_s
|
18
18
|
end
|
19
19
|
|
@@ -26,6 +26,6 @@ class MaitreD::API::SSOGuard < Sliver::Hook
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def valid_token?
|
29
|
-
expected_token == params['
|
29
|
+
expected_token == params['resource_token']
|
30
30
|
end
|
31
31
|
end
|
data/maitre_d.gemspec
CHANGED
@@ -3,15 +3,13 @@ $:.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.0'
|
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) }
|
@@ -20,8 +18,8 @@ Gem::Specification.new do |s|
|
|
20
18
|
s.add_runtime_dependency 'sliver', '~> 0.2.2'
|
21
19
|
s.add_runtime_dependency 'multi_json', '>= 1.3.0'
|
22
20
|
|
23
|
-
s.add_development_dependency 'combustion', '~>
|
21
|
+
s.add_development_dependency 'combustion', '~> 1.3'
|
24
22
|
s.add_development_dependency 'kensa', '2.1.0'
|
25
|
-
s.add_development_dependency 'rails', '~>
|
26
|
-
s.add_development_dependency 'rspec-rails', '~>
|
23
|
+
s.add_development_dependency 'rails', '~> 6.0'
|
24
|
+
s.add_development_dependency 'rspec-rails', '~> 4.0'
|
27
25
|
end
|
@@ -17,36 +17,41 @@ describe 'Heroku Provisioning API', :type => :request do
|
|
17
17
|
}
|
18
18
|
|
19
19
|
it "returns a 401 if the HTTP authorisation does not match" do
|
20
|
-
post '/heroku/resources',
|
21
|
-
|
20
|
+
post '/heroku/resources',
|
21
|
+
:params => JSON.dump(params),
|
22
|
+
:headers => {'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
22
23
|
|
23
24
|
expect(response.status).to eq(401)
|
24
25
|
end
|
25
26
|
|
26
27
|
it "returns the resource id" do
|
27
|
-
post '/heroku/resources',
|
28
|
-
|
28
|
+
post '/heroku/resources',
|
29
|
+
:params => JSON.dump(params),
|
30
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
29
31
|
|
30
32
|
expect(json_response['id']).to eq('123')
|
31
33
|
end
|
32
34
|
|
33
35
|
it "returns the resource configuration" do
|
34
|
-
post '/heroku/resources',
|
35
|
-
|
36
|
+
post '/heroku/resources',
|
37
|
+
:params => JSON.dump(params),
|
38
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
36
39
|
|
37
40
|
expect(json_response['config']).to eq({'FOO_PROVISIONED' => "true"})
|
38
41
|
end
|
39
42
|
|
40
43
|
it "returns a custom message" do
|
41
|
-
post '/heroku/resources',
|
42
|
-
|
44
|
+
post '/heroku/resources',
|
45
|
+
:params => JSON.dump(params),
|
46
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
43
47
|
|
44
48
|
expect(json_response['message']).to eq('Add-on provisioned!')
|
45
49
|
end
|
46
50
|
|
47
51
|
it "returns the region if it exists" do
|
48
|
-
post '/heroku/resources',
|
49
|
-
|
52
|
+
post '/heroku/resources',
|
53
|
+
:params => JSON.dump(params.merge(:region => 'us-west')),
|
54
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
50
55
|
|
51
56
|
expect(json_response['region']).to eq('us-west')
|
52
57
|
end
|
@@ -58,22 +63,25 @@ describe 'Heroku Provisioning API', :type => :request do
|
|
58
63
|
}
|
59
64
|
|
60
65
|
it "returns a 401 if the HTTP authorisation does not match" do
|
61
|
-
put '/heroku/resources/7',
|
62
|
-
|
66
|
+
put '/heroku/resources/7',
|
67
|
+
:params => JSON.dump(params),
|
68
|
+
:headers => {'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
63
69
|
|
64
70
|
expect(response.status).to eq(401)
|
65
71
|
end
|
66
72
|
|
67
73
|
it "returns the new resource configuration" do
|
68
|
-
put '/heroku/resources/7',
|
69
|
-
|
74
|
+
put '/heroku/resources/7',
|
75
|
+
:params => JSON.dump(params),
|
76
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
70
77
|
|
71
78
|
expect(json_response['config']).to eq({'FOO_PROVISIONED' => "false"})
|
72
79
|
end
|
73
80
|
|
74
81
|
it "returns a custom message" do
|
75
|
-
put '/heroku/resources/7',
|
76
|
-
|
82
|
+
put '/heroku/resources/7',
|
83
|
+
:params => JSON.dump(params),
|
84
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
77
85
|
|
78
86
|
expect(json_response['message']).to eq('Add-on upgraded or downgraded.')
|
79
87
|
end
|
@@ -81,20 +89,22 @@ describe 'Heroku Provisioning API', :type => :request do
|
|
81
89
|
|
82
90
|
describe 'Deprovisioning' do
|
83
91
|
it "returns a 401 if the HTTP authorisation does not match" do
|
84
|
-
delete '/heroku/resources/28',
|
85
|
-
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
92
|
+
delete '/heroku/resources/28',
|
93
|
+
:headers => {'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
86
94
|
|
87
95
|
expect(response.status).to eq(401)
|
88
96
|
end
|
89
97
|
|
90
98
|
it "returns with a status of 200" do
|
91
|
-
delete '/heroku/resources/28',
|
99
|
+
delete '/heroku/resources/28',
|
100
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
92
101
|
|
93
102
|
expect(response.status).to eq(200)
|
94
103
|
end
|
95
104
|
|
96
105
|
it "returns a custom message" do
|
97
|
-
delete '/heroku/resources/28',
|
106
|
+
delete '/heroku/resources/28',
|
107
|
+
:headers => {'HTTP_AUTHORIZATION' => authorisation}
|
98
108
|
|
99
109
|
expect(json_response['message']).to eq('Add-on removed.')
|
100
110
|
end
|
@@ -9,8 +9,10 @@ describe 'Heroku SSO API', :type => :request do
|
|
9
9
|
}
|
10
10
|
|
11
11
|
it "renders a 403 if the token is incorrect" do
|
12
|
-
post '/heroku/resources/sso', :
|
13
|
-
:
|
12
|
+
post '/heroku/resources/sso', :params => {
|
13
|
+
:resource_id => '789', :resource_token => 'foo', :timestamp => timestamp,
|
14
|
+
'nav-data' => nav_data
|
15
|
+
}
|
14
16
|
|
15
17
|
expect(response.status).to eq(403)
|
16
18
|
end
|
@@ -20,29 +22,37 @@ describe 'Heroku SSO API', :type => :request do
|
|
20
22
|
pre_token = "789:#{MaitreD::Heroku.sso_salt}:#{timestamp.to_s}"
|
21
23
|
token = Digest::SHA1.hexdigest(pre_token).to_s
|
22
24
|
|
23
|
-
post '/heroku/resources/sso', :
|
24
|
-
:
|
25
|
+
post '/heroku/resources/sso', :params => {
|
26
|
+
:resource_id => '789', :resource_token => token, :timestamp => timestamp,
|
27
|
+
'nav-data' => nav_data
|
28
|
+
}
|
25
29
|
|
26
30
|
expect(response.status).to eq(403)
|
27
31
|
end
|
28
32
|
|
29
33
|
it "sets the heroku nav data cookie" do
|
30
|
-
post '/heroku/resources/sso', :
|
31
|
-
:
|
34
|
+
post '/heroku/resources/sso', :params => {
|
35
|
+
:resource_id => '789', :resource_token => token, :timestamp => timestamp,
|
36
|
+
'nav-data' => nav_data
|
37
|
+
}
|
32
38
|
|
33
39
|
expect(cookies['heroku-nav-data']).to eq(nav_data)
|
34
40
|
end
|
35
41
|
|
36
42
|
it "redirects to the appropriate URL" do
|
37
|
-
post '/heroku/resources/sso', :
|
38
|
-
:
|
43
|
+
post '/heroku/resources/sso', :params => {
|
44
|
+
:resource_id => '789', :resource_token => token, :timestamp => timestamp,
|
45
|
+
'nav-data' => nav_data
|
46
|
+
}
|
39
47
|
|
40
48
|
expect(response).to redirect_to('/my/dashboard')
|
41
49
|
end
|
42
50
|
|
43
51
|
it "should set the provided session variables" do
|
44
|
-
post '/heroku/resources/sso', :
|
45
|
-
:
|
52
|
+
post '/heroku/resources/sso', :params => {
|
53
|
+
:resource_id => '789', :resource_token => token, :timestamp => timestamp,
|
54
|
+
'nav-data' => nav_data
|
55
|
+
}
|
46
56
|
|
47
57
|
expect(session[:app_id]).to eq('789')
|
48
58
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
class HerokuListener
|
2
|
-
def provision(
|
2
|
+
def provision(params)
|
3
3
|
{
|
4
4
|
:id => '123',
|
5
5
|
:config => {'FOO_PROVISIONED' => 'true'},
|
6
6
|
:message => 'Add-on provisioned!',
|
7
|
-
:region => region
|
7
|
+
:region => params["region"]
|
8
8
|
}
|
9
9
|
end
|
10
10
|
|
11
|
-
def plan_change(resource_id,
|
11
|
+
def plan_change(resource_id, plan)
|
12
12
|
{
|
13
13
|
:config => {'FOO_PROVISIONED' => 'false'},
|
14
14
|
:message => 'Add-on upgraded or downgraded.'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maitre_d
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sliver
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '1.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '1.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: kensa
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,28 +72,28 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '6.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '6.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec-rails
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '4.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '4.0'
|
97
97
|
description: A Rack API (through Grape) for Heroku add-on providers.
|
98
98
|
email:
|
99
99
|
- pat@freelancing-gods.com
|
@@ -119,16 +119,11 @@ files:
|
|
119
119
|
- lib/maitre_d/api/delete.rb
|
120
120
|
- lib/maitre_d/api/sso.rb
|
121
121
|
- lib/maitre_d/api/sso_guard.rb
|
122
|
-
- lib/maitre_d/cloud_control.rb
|
123
122
|
- lib/maitre_d/heroku.rb
|
124
123
|
- maitre_d.gemspec
|
125
|
-
- spec/api/cloud_control/provisioning_spec.rb
|
126
|
-
- spec/api/cloud_control/single_sign_on_spec.rb
|
127
124
|
- spec/api/heroku/provisioning_spec.rb
|
128
125
|
- spec/api/heroku/single_sign_on_spec.rb
|
129
|
-
- spec/internal/app/listeners/cloud_control_listener.rb
|
130
126
|
- spec/internal/app/listeners/heroku_listener.rb
|
131
|
-
- spec/internal/config/initializers/cloud_control.rb
|
132
127
|
- spec/internal/config/initializers/heroku.rb
|
133
128
|
- spec/internal/config/routes.rb
|
134
129
|
- spec/internal/log/.gitignore
|
@@ -152,19 +147,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
147
|
- !ruby/object:Gem::Version
|
153
148
|
version: '0'
|
154
149
|
requirements: []
|
155
|
-
|
156
|
-
rubygems_version: 2.7.6
|
150
|
+
rubygems_version: 3.1.2
|
157
151
|
signing_key:
|
158
152
|
specification_version: 4
|
159
153
|
summary: Rack APIs for Heroku add-ons
|
160
154
|
test_files:
|
161
|
-
- spec/api/cloud_control/provisioning_spec.rb
|
162
|
-
- spec/api/cloud_control/single_sign_on_spec.rb
|
163
155
|
- spec/api/heroku/provisioning_spec.rb
|
164
156
|
- spec/api/heroku/single_sign_on_spec.rb
|
165
|
-
- spec/internal/app/listeners/cloud_control_listener.rb
|
166
157
|
- spec/internal/app/listeners/heroku_listener.rb
|
167
|
-
- spec/internal/config/initializers/cloud_control.rb
|
168
158
|
- spec/internal/config/initializers/heroku.rb
|
169
159
|
- spec/internal/config/routes.rb
|
170
160
|
- spec/internal/log/.gitignore
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module MaitreD::CloudControl
|
2
|
-
def self.configure
|
3
|
-
yield self
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.listener
|
7
|
-
@listener
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.listener=(listener)
|
11
|
-
@listener = listener
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.id
|
15
|
-
@id
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.id=(id)
|
19
|
-
@id = id
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.password
|
23
|
-
@password
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.password=(password)
|
27
|
-
@password = password
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.sso_salt
|
31
|
-
@sso_salt
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.sso_salt=(salt)
|
35
|
-
@sso_salt = salt
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.provider_id_from(params)
|
39
|
-
params['cloudcontrol_id']
|
40
|
-
end
|
41
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'CloudControl Provisioning API', :type => :request do
|
4
|
-
let(:authorisation) { "Basic #{Base64.encode64('baz:qux')}" }
|
5
|
-
let(:json_response) { JSON.parse response.body }
|
6
|
-
|
7
|
-
describe 'Provisioning' do
|
8
|
-
let(:params) {
|
9
|
-
{
|
10
|
-
:plan => 'basic',
|
11
|
-
:callback_url => 'https://domain/vendor/apps/app123%40heroku.com',
|
12
|
-
:cloudcontrol_id => 'app123@heroku.com'
|
13
|
-
}
|
14
|
-
}
|
15
|
-
|
16
|
-
it "returns a 401 if the HTTP authorisation does not match" do
|
17
|
-
post '/cloudcontrol/resources', JSON.dump(params),
|
18
|
-
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
19
|
-
|
20
|
-
expect(response.status).to eq(401)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "returns the resource id" do
|
24
|
-
post '/cloudcontrol/resources', JSON.dump(params),
|
25
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
26
|
-
|
27
|
-
expect(json_response['id']).to eq('123')
|
28
|
-
end
|
29
|
-
|
30
|
-
it "returns the resource configuration" do
|
31
|
-
post '/cloudcontrol/resources', JSON.dump(params),
|
32
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
33
|
-
|
34
|
-
expect(json_response['config']).to eq('FOO_PROVISIONED' => "true")
|
35
|
-
end
|
36
|
-
|
37
|
-
it "returns a custom message" do
|
38
|
-
post '/cloudcontrol/resources', JSON.dump(params),
|
39
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
40
|
-
|
41
|
-
expect(json_response['message']).to eq('Add-on provisioned!')
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe 'Changing Plans' do
|
46
|
-
let(:params) {
|
47
|
-
{:cloudcontrol_id => 'app123@heroku.com', :plan => 'premium'}
|
48
|
-
}
|
49
|
-
|
50
|
-
it "returns a 401 if the HTTP authorisation does not match" do
|
51
|
-
put '/cloudcontrol/resources/7', JSON.dump(params),
|
52
|
-
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
53
|
-
|
54
|
-
expect(response.status).to eq(401)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "returns the new resource configuration" do
|
58
|
-
put '/cloudcontrol/resources/7', JSON.dump(params),
|
59
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
60
|
-
|
61
|
-
expect(json_response['config']).to eq('FOO_PROVISIONED' => "false")
|
62
|
-
end
|
63
|
-
|
64
|
-
it "returns a custom message" do
|
65
|
-
put '/cloudcontrol/resources/7', JSON.dump(params),
|
66
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
67
|
-
|
68
|
-
expect(json_response['message']).to eq('Add-on upgraded or downgraded.')
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe 'Deprovisioning' do
|
73
|
-
it "returns a 401 if the HTTP authorisation does not match" do
|
74
|
-
delete '/cloudcontrol/resources/28', {},
|
75
|
-
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
76
|
-
|
77
|
-
expect(response.status).to eq(401)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "returns with a status of 200" do
|
81
|
-
delete '/cloudcontrol/resources/28', {},
|
82
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
83
|
-
|
84
|
-
expect(response.status).to eq(200)
|
85
|
-
end
|
86
|
-
|
87
|
-
it "returns a custom message" do
|
88
|
-
delete '/cloudcontrol/resources/28', {},
|
89
|
-
{'HTTP_AUTHORIZATION' => authorisation}
|
90
|
-
|
91
|
-
expect(json_response['message']).to eq('Add-on removed.')
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'CloudControl SSO API', :type => :request do
|
4
|
-
let(:timestamp) { Time.now.to_i }
|
5
|
-
let(:nav_data) { 'heroku-nav-data-goes-here' }
|
6
|
-
let(:token) {
|
7
|
-
pre_token = "789:#{MaitreD::CloudControl.sso_salt}:#{timestamp.to_s}"
|
8
|
-
Digest::SHA1.hexdigest(pre_token).to_s
|
9
|
-
}
|
10
|
-
|
11
|
-
it "renders a 403 if the token is incorrect" do
|
12
|
-
post '/cloudcontrol/resources/sso', :id => '789', :token => 'foo',
|
13
|
-
:timestamp => timestamp, 'nav-data' => nav_data
|
14
|
-
|
15
|
-
expect(response.status).to eq(403)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "renders a 403 if the timestamp is older than 5 minutes" do
|
19
|
-
timestamp = 5.minutes.ago.to_i - 1
|
20
|
-
pre_token = "789:#{MaitreD::CloudControl.sso_salt}:#{timestamp.to_s}"
|
21
|
-
token = Digest::SHA1.hexdigest(pre_token).to_s
|
22
|
-
|
23
|
-
post '/cloudcontrol/resources/sso', :id => '789', :token => token,
|
24
|
-
:timestamp => timestamp, 'nav-data' => nav_data
|
25
|
-
|
26
|
-
expect(response.status).to eq(403)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "sets the heroku nav data cookie" do
|
30
|
-
post '/cloudcontrol/resources/sso', :id => '789', :token => token,
|
31
|
-
:timestamp => timestamp, 'nav-data' => nav_data
|
32
|
-
|
33
|
-
expect(cookies['heroku-nav-data']).to eq(nav_data)
|
34
|
-
end
|
35
|
-
|
36
|
-
it "redirects to the appropriate URL" do
|
37
|
-
post '/cloudcontrol/resources/sso', :id => '789', :token => token,
|
38
|
-
:timestamp => timestamp, 'nav-data' => nav_data
|
39
|
-
|
40
|
-
expect(response).to redirect_to('/my/dashboard')
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should set the provided session variables" do
|
44
|
-
post '/cloudcontrol/resources/sso', :id => '789', :token => token,
|
45
|
-
:timestamp => timestamp, 'nav-data' => nav_data
|
46
|
-
|
47
|
-
expect(session[:app_id]).to eq('789')
|
48
|
-
end
|
49
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class CloudControlListener
|
2
|
-
def provision(cloud_control_id, plan, region, callback_url, logplex_token, options)
|
3
|
-
{
|
4
|
-
:id => '123',
|
5
|
-
:config => {'FOO_PROVISIONED' => 'true'},
|
6
|
-
:message => 'Add-on provisioned!'
|
7
|
-
}
|
8
|
-
end
|
9
|
-
|
10
|
-
def plan_change(resource_id, cloud_control_id, plan)
|
11
|
-
{
|
12
|
-
:config => {'FOO_PROVISIONED' => 'false'},
|
13
|
-
:message => 'Add-on upgraded or downgraded.'
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
def deprovision(resource_id)
|
18
|
-
{
|
19
|
-
:message => 'Add-on removed.'
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
def single_sign_on(resource_id)
|
24
|
-
{
|
25
|
-
:uri => '/my/dashboard',
|
26
|
-
:session => {:app_id => resource_id}
|
27
|
-
}
|
28
|
-
end
|
29
|
-
end
|