kensa 1.2.0rc7 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/Gemfile +5 -4
- data/Gemfile.lock +21 -27
- data/README.md +1 -1
- data/Rakefile +18 -10
- data/bin/kensa +10 -30
- data/kensa.gemspec +27 -20
- data/lib/heroku/kensa.rb +7 -9
- data/lib/heroku/kensa/check.rb +499 -0
- data/lib/heroku/kensa/client.rb +67 -89
- data/lib/heroku/kensa/git.rb +39 -0
- data/lib/heroku/kensa/manifest.rb +41 -8
- data/lib/heroku/kensa/screen.rb +37 -0
- data/lib/heroku/kensa/sso.rb +22 -22
- data/lib/heroku/kensa/version.rb +1 -2
- data/test/all_check_test.rb +25 -0
- data/test/create_test.rb +40 -6
- data/test/deprovision_check_test.rb +39 -0
- data/test/helper.rb +74 -11
- data/test/init_test.rb +54 -0
- data/test/manifest_check_test.rb +94 -0
- data/test/manifest_test.rb +37 -33
- data/test/plan_change_check_test.rb +31 -0
- data/test/provision_check_test.rb +51 -0
- data/test/provision_response_check_test.rb +81 -0
- data/test/resources/runner.rb +1 -0
- data/test/resources/server.rb +227 -0
- data/test/sso_check_test.rb +58 -0
- data/test/sso_test.rb +113 -53
- metadata +97 -91
- data/test.rb +0 -1
- data/test/deprovision_test.rb +0 -30
- data/test/lib/dependencies.rb +0 -12
- data/test/lib/formatter.rb +0 -84
- data/test/lib/http.rb +0 -60
- data/test/lib/response.rb +0 -12
- data/test/manifest_generation_test.rb +0 -32
- data/test/plan_change_test.rb +0 -30
- data/test/provision_test.rb +0 -84
- data/test/resources/provider_server.rb +0 -81
- data/test/resources/views/index.haml +0 -6
- data/test/sso_launch_test.rb +0 -130
data/test.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
puts ARGV
|
data/test/deprovision_test.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
$:.unshift(File.expand_path("../..",__FILE__))
|
2
|
-
require 'test/lib/dependencies'
|
3
|
-
class DeprovisionTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
super
|
7
|
-
@params = {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def deprovision(auth = nil)
|
11
|
-
delete "/heroku/resources/123", auth
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_working_deprovision_call
|
15
|
-
response = deprovision
|
16
|
-
assert_equal 200, response.code, "Expects a 200 - OK response/status code when successfully deprovisioned."
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_detects_missing_auth
|
20
|
-
response = deprovision(auth = false)
|
21
|
-
assert_equal 401, response.code, "Provisioning request should require authentication."
|
22
|
-
|
23
|
-
response = deprovision(auth = [manifest["id"]+"a", manifest["api"]["password"]])
|
24
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any username, should require '#{manifest["id"]}'."
|
25
|
-
|
26
|
-
response = deprovision(auth = [manifest["id"], manifest["api"]["password"]+"a"])
|
27
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any password, should require '#{manifest["api"]["password"]}'."
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
data/test/lib/dependencies.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'artifice'
|
3
|
-
require 'rest-client'
|
4
|
-
require 'yajl'
|
5
|
-
require 'lib/heroku/kensa/manifest'
|
6
|
-
%w{response http formatter}.each do |lib|
|
7
|
-
require "test/lib/#{lib}"
|
8
|
-
end
|
9
|
-
|
10
|
-
class Test::Unit::TestCase
|
11
|
-
include Heroku::Kensa::HTTPTest
|
12
|
-
end
|
data/test/lib/formatter.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'test/unit/ui/console/testrunner'
|
3
|
-
|
4
|
-
def format_kensa_test_name(name)
|
5
|
-
name.sub(/\Atest_/,"").match(/\A([^\(]*)/)[1].gsub("_", " ")
|
6
|
-
end
|
7
|
-
|
8
|
-
module Test
|
9
|
-
module Unit
|
10
|
-
class TestCase
|
11
|
-
alias_method :add_error_with_connection_exception, :add_error
|
12
|
-
alias_method :add_failure_with_connection_exception, :add_failure
|
13
|
-
|
14
|
-
private
|
15
|
-
def add_error(exception)
|
16
|
-
if exception.is_a? Heroku::Kensa::UserError
|
17
|
-
@test_passed = false
|
18
|
-
@_result.add_failure(Failure.new(name, filter_backtrace(caller()), exception.message))
|
19
|
-
else
|
20
|
-
add_error_with_connection_exception(exception)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Failure
|
26
|
-
def long_display
|
27
|
-
name = format_kensa_test_name(@test_name)
|
28
|
-
"#{name} - FAILED: #@message"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Error
|
33
|
-
def long_display
|
34
|
-
backtrace = filter_backtrace(@exception.backtrace).join("\n ")
|
35
|
-
name = format_kensa_test_name(@test_name)
|
36
|
-
"#{@exception.class.name} in #{name}:\n#{message}\n #{backtrace}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module UI
|
41
|
-
module Console
|
42
|
-
class TestRunner
|
43
|
-
|
44
|
-
alias_method :test_started_old, :test_started
|
45
|
-
|
46
|
-
def add_fault(fault)
|
47
|
-
@faults << fault
|
48
|
-
@already_outputted = true
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_started(name)
|
52
|
-
if name =~ /\((.*)::([^\)]*)/
|
53
|
-
ctx, should = [$1, $2]
|
54
|
-
end
|
55
|
-
unless ctx.nil? or should.nil?
|
56
|
-
if ctx != @ctx
|
57
|
-
nl
|
58
|
-
output("#{ctx}:")
|
59
|
-
end
|
60
|
-
@ctx = ctx
|
61
|
-
@current_test_text = " ==> #{should}"
|
62
|
-
else
|
63
|
-
test_started_old(name)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def test_finished(name)
|
68
|
-
@current_test_text = name.sub(/\Atest_/,"").match(/\A([^\(]*)/)[1].gsub("_", " ")
|
69
|
-
if fault = @faults.find {|f| f.test_name == name}
|
70
|
-
fault_type = fault.is_a?(Test::Unit::Failure) ? "FAILED" : "ERROR!"
|
71
|
-
# NOTE -- Concatenation because "\e[0m]" does funky stuff.
|
72
|
-
output("[\e[0;31m#{fault_type}\e[0m" + "] #{@current_test_text}.")
|
73
|
-
else
|
74
|
-
output("[ \e[0;32mOK\e[0m ] #{@current_test_text}.")
|
75
|
-
end
|
76
|
-
@already_outputted = false
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
data/test/lib/http.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
module Heroku::Kensa::HTTPTest
|
2
|
-
def make_token(id, salt, timestamp)
|
3
|
-
Digest::SHA1.hexdigest([id, salt, timestamp].join(':'))
|
4
|
-
end
|
5
|
-
|
6
|
-
def provider_request(meth, path, params = {}, auth_credentials = nil)
|
7
|
-
if auth_credentials.nil?
|
8
|
-
auth_credentials = [manifest["id"], manifest["api"]["password"]]
|
9
|
-
end
|
10
|
-
if path =~ /http/
|
11
|
-
uri = URI.parse(path)
|
12
|
-
else
|
13
|
-
uri = URI.parse(base_url)
|
14
|
-
uri.path = path
|
15
|
-
end
|
16
|
-
if auth_credentials
|
17
|
-
uri.userinfo = auth_credentials
|
18
|
-
end
|
19
|
-
opts = meth == :get ? { :params => params } : params
|
20
|
-
response = RestClient.send(meth, "#{uri.to_s}", opts)
|
21
|
-
Response.new(response.code, response.body, response.cookies)
|
22
|
-
rescue Errno::ECONNREFUSED
|
23
|
-
raise UserError.new("Unable to connect to your API.")
|
24
|
-
rescue RestClient::Forbidden
|
25
|
-
Response.new(403)
|
26
|
-
rescue RestClient::Unauthorized
|
27
|
-
Response.new(401)
|
28
|
-
rescue RestClient::InternalServerError
|
29
|
-
raise UserError.new("HTTP 500 Internal Server Error")
|
30
|
-
end
|
31
|
-
|
32
|
-
def get(path, params = {})
|
33
|
-
provider_request(:get, path, params, auth = false)
|
34
|
-
end
|
35
|
-
|
36
|
-
def delete(path, auth_credentials = nil)
|
37
|
-
provider_request(:delete, path, params = nil, auth_credentials)
|
38
|
-
end
|
39
|
-
|
40
|
-
def post(path, params = {}, auth_credentials = nil)
|
41
|
-
provider_request(:post, path, params, auth_credentials)
|
42
|
-
end
|
43
|
-
|
44
|
-
def put(path, params = {}, auth_credentials = nil)
|
45
|
-
provider_request(:put, path, params, auth_credentials)
|
46
|
-
end
|
47
|
-
|
48
|
-
def manifest
|
49
|
-
return @manifest if @manifest
|
50
|
-
@manifest ||= $manifest || Heroku::Kensa::Manifest.new.skeleton
|
51
|
-
end
|
52
|
-
|
53
|
-
def base_url
|
54
|
-
if manifest["api"]["test"].is_a?(Hash)
|
55
|
-
manifest["api"]["test"]["base_url"].chomp("/")
|
56
|
-
else
|
57
|
-
manifest["api"]["test"].chomp("/")
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
data/test/lib/response.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
Response = Struct.new(:code, :body, :cookies) do
|
2
|
-
def json_body
|
3
|
-
Yajl::Parser.parse(self.body)
|
4
|
-
rescue Yajl::ParseError
|
5
|
-
if !self.body || self.body.empty?
|
6
|
-
raise Heroku::Kensa::UserError.new("response body empty")
|
7
|
-
else
|
8
|
-
raise Heroku::Kensa::UserError.new("Could not parse json: #{self.body}")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
@@ -1,32 +0,0 @@
|
|
1
|
-
$:.unshift(File.expand_path("../..",__FILE__))
|
2
|
-
require 'test/helper'
|
3
|
-
class ManifestGenerationTest < Test::Unit::TestCase
|
4
|
-
include Heroku::Kensa
|
5
|
-
|
6
|
-
def setup
|
7
|
-
super
|
8
|
-
@manifest = Manifest.new
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_generates_a_new_sso_salt_every_time
|
12
|
-
assert @manifest.skeleton['api']['sso_salt'] != Manifest.new.skeleton['api']['sso_salt']
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_generates_a_new_password_every_time
|
16
|
-
assert @manifest.skeleton['api']['password'] != Manifest.new.skeleton['api']['password']
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ManifestGenerationWithoutSSOTest < Test::Unit::TestCase
|
21
|
-
include Heroku::Kensa
|
22
|
-
|
23
|
-
def setup
|
24
|
-
super
|
25
|
-
options = { :sso => false }
|
26
|
-
@manifest = Manifest.new 'test.txt', options
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_exclude_sso_salt
|
30
|
-
assert_nil @manifest.skeleton['api']['sso_salt']
|
31
|
-
end
|
32
|
-
end
|
data/test/plan_change_test.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
$:.unshift(File.expand_path("../..",__FILE__))
|
2
|
-
require 'test/lib/dependencies'
|
3
|
-
class PlanChangeTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
super
|
7
|
-
@params = { :plan => "new_plan" }
|
8
|
-
end
|
9
|
-
|
10
|
-
def plan_change(auth = nil, params = @params)
|
11
|
-
response = put "/heroku/resources/123", params, auth
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_working_plan_change_call
|
15
|
-
response = plan_change
|
16
|
-
assert_equal 200, response.code, "Expected a 200 response code on successful plan change."
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_detects_missing_auth
|
20
|
-
response = plan_change(auth = false)
|
21
|
-
assert_equal 401, response.code, "Provisioning request should require authentication."
|
22
|
-
|
23
|
-
response = plan_change(auth = [manifest["id"]+"a", manifest["api"]["password"]])
|
24
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any username, should require '#{manifest["id"]}'."
|
25
|
-
|
26
|
-
response = plan_change(auth = [manifest["id"], manifest["api"]["password"]+"a"])
|
27
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any password, should require '#{manifest["api"]["password"]}'."
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
data/test/provision_test.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
$:.unshift(File.expand_path("../..",__FILE__))
|
2
|
-
require 'test/lib/dependencies'
|
3
|
-
class ProvisionTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
super
|
7
|
-
@params = {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def provision(auth = nil, params = @params)
|
11
|
-
post "/heroku/resources", params, auth
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_working_provision_call
|
15
|
-
response = provision
|
16
|
-
assert_equal 201, response.code, "Expects a 201 - Created response/status code when successfully provisioned."
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_allows_the_definition_of_a_custom_provisioning_endpoint
|
20
|
-
#Artifice.activate_with(KensaServer.new)
|
21
|
-
#@data['api']['test'] = {
|
22
|
-
# "base_url" => "https://example.org/providers/provision",
|
23
|
-
# "sso_url" => "https://example.org/sso"
|
24
|
-
#}
|
25
|
-
#assert_valid
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_expects_a_valid_json_response
|
29
|
-
response = provision
|
30
|
-
assert response.json_body, "Expects a valid JSON object as response body."
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_detects_missing_id
|
34
|
-
response = provision
|
35
|
-
assert response.json_body["id"], "Expects JSON response to contain the Provider's unique ID for this app."
|
36
|
-
assert response.json_body["id"].to_s.strip != "", "Expects JSON response to contain the Provider's unique ID for this app."
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_provides_app_config
|
40
|
-
response = provision
|
41
|
-
assert response.json_body["config"].is_a?(Hash), "Expects JSON response to contain a hash of config variables."
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_all_config_values_are_strings
|
45
|
-
response = provision
|
46
|
-
response.json_body["config"].each do |k,v|
|
47
|
-
assert k.is_a?(String), "Expect all config names to be strings ('#{k}' is not)."
|
48
|
-
assert v.is_a?(String), "Expect all config values to be strings ('#{v}' is not)."
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_all_config_vars_are_defined_in_the_manifest
|
53
|
-
response = provision
|
54
|
-
response.json_body["config"].each do |k,v|
|
55
|
-
assert manifest["api"]["config_vars"].include?(k), "Only config vars defined in the manfiest can be set ('#{k}' is not)."
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_all_config_url_values_are_valid
|
60
|
-
response = provision
|
61
|
-
response.json_body["config"].each do |k,v|
|
62
|
-
next unless k =~ /_URL\z/
|
63
|
-
begin
|
64
|
-
uri = URI.parse(v)
|
65
|
-
assert uri.host, "#{v} is not a valid URI - missing host"
|
66
|
-
assert uri.scheme, "#{v} is not a valid URI - missing scheme"
|
67
|
-
rescue URI::InvalidURIError
|
68
|
-
assert false, "#{v} is not a valud URI"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def test_detects_missing_auth
|
74
|
-
response = provision(auth = false)
|
75
|
-
assert_equal 401, response.code, "Provisioning request should require authentication."
|
76
|
-
|
77
|
-
response = provision(auth = [manifest["id"]+"a", manifest["api"]["password"]])
|
78
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any username, should require '#{manifest["id"]}'."
|
79
|
-
|
80
|
-
response = provision(auth = [manifest["id"], manifest["api"]["password"]+"a"])
|
81
|
-
assert_equal 401, response.code, "Provisioning request appears to allow any password, should require '#{manifest["api"]["password"]}'."
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
require 'sinatra'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
class ProviderServer < Sinatra::Base
|
5
|
-
set :views, File.dirname(__FILE__) + "/views"
|
6
|
-
|
7
|
-
def initialize(manifest = nil)
|
8
|
-
@manifest = manifest
|
9
|
-
super
|
10
|
-
end
|
11
|
-
|
12
|
-
helpers do
|
13
|
-
def unauthorized!(status=403)
|
14
|
-
halt status, "Not authorized\n"
|
15
|
-
end
|
16
|
-
|
17
|
-
def check_timestamp!
|
18
|
-
unauthorized! if params[:timestamp].to_i < (Time.now-60*2).to_i
|
19
|
-
end
|
20
|
-
|
21
|
-
def check_token!
|
22
|
-
salt = @manifest && @manifest['api']["sso_salt"]
|
23
|
-
token = Digest::SHA1.hexdigest([params[:id], salt, params[:timestamp]].join(':'))
|
24
|
-
unauthorized! if params[:token] != token
|
25
|
-
end
|
26
|
-
|
27
|
-
def authenticate!
|
28
|
-
unless auth_heroku?
|
29
|
-
response['WWW-Authenticate'] = %(Basic realm="Kensa Test Server")
|
30
|
-
unauthorized!(401)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def auth_heroku?
|
35
|
-
auth = Rack::Auth::Basic::Request.new(request.env)
|
36
|
-
return false unless auth.provided? && auth.basic? && auth.credentials
|
37
|
-
if @manifest
|
38
|
-
auth.credentials == [@manifest["id"], @manifest["api"]["password"]]
|
39
|
-
else
|
40
|
-
auth.credentials == ['myaddon', 'secret']
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
delete '/heroku/resources/:id' do
|
46
|
-
authenticate!
|
47
|
-
status 200
|
48
|
-
end
|
49
|
-
|
50
|
-
put '/heroku/resources/:id' do
|
51
|
-
authenticate!
|
52
|
-
status 200
|
53
|
-
end
|
54
|
-
|
55
|
-
post '/heroku/resources' do
|
56
|
-
authenticate!
|
57
|
-
status 201
|
58
|
-
{ "id" => 52343.to_s,
|
59
|
-
"config" => {
|
60
|
-
"MYADDON_USER" => "1",
|
61
|
-
"MYADDON_URL" => "http://host.example.org/"
|
62
|
-
}
|
63
|
-
}.to_json
|
64
|
-
end
|
65
|
-
|
66
|
-
get '/heroku/resources/:id' do
|
67
|
-
check_timestamp!
|
68
|
-
check_token!
|
69
|
-
response.set_cookie('heroku-nav-data', params['nav-data'])
|
70
|
-
session[:heroku] = true
|
71
|
-
haml :index
|
72
|
-
end
|
73
|
-
|
74
|
-
post '/sso/login' do
|
75
|
-
check_timestamp!
|
76
|
-
check_token!
|
77
|
-
response.set_cookie('heroku-nav-data', params['nav-data'])
|
78
|
-
session[:heroku] = true
|
79
|
-
haml :index
|
80
|
-
end
|
81
|
-
end
|