maitre_d 0.6.1 → 0.7.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/.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
         |