kensa 1.1.3 → 1.1.4
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.
- data/Gemfile +9 -0
- data/Gemfile.lock +58 -0
- data/Rakefile +3 -2
- data/bin/kensa +7 -0
- data/kensa.gemspec +26 -23
- data/lib/heroku/kensa/check.rb +28 -14
- data/lib/heroku/kensa/client.rb +3 -3
- data/lib/heroku/kensa/post_proxy.rb +33 -0
- data/lib/heroku/kensa/sso.rb +71 -4
- data/lib/heroku/kensa.rb +3 -2
- data/test/deprovision_check_test.rb +3 -3
- data/test/helper.rb +8 -3
- data/test/resources/server.rb +68 -8
- data/test/sso_check_test.rb +45 -35
- data/test/sso_test.rb +76 -8
- metadata +82 -29
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kensa (1.1.4)
|
5
|
+
kensa
|
6
|
+
launchy (>= 0.3.2)
|
7
|
+
mechanize (~> 1.0.0)
|
8
|
+
rest-client (< 1.7.0, >= 1.4.0)
|
9
|
+
term-ansicolor (~> 1.0)
|
10
|
+
yajl-ruby (~> 0.6)
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: http://rubygems.org/
|
14
|
+
specs:
|
15
|
+
addressable (2.2.6)
|
16
|
+
ansi (1.3.0)
|
17
|
+
contest (0.1.3)
|
18
|
+
git (1.2.5)
|
19
|
+
haml (3.1.2)
|
20
|
+
jeweler (1.6.4)
|
21
|
+
bundler (~> 1.0)
|
22
|
+
git (>= 1.2.5)
|
23
|
+
rake
|
24
|
+
json (1.5.3)
|
25
|
+
launchy (2.0.5)
|
26
|
+
addressable (~> 2.2.6)
|
27
|
+
mechanize (1.0.0)
|
28
|
+
nokogiri (>= 1.2.1)
|
29
|
+
mime-types (1.16)
|
30
|
+
nokogiri (1.5.0)
|
31
|
+
rack (1.3.2)
|
32
|
+
rake (0.9.2)
|
33
|
+
rest-client (1.6.3)
|
34
|
+
mime-types (>= 1.16)
|
35
|
+
rr (1.0.3)
|
36
|
+
sinatra (1.2.6)
|
37
|
+
rack (~> 1.1)
|
38
|
+
tilt (>= 1.2.2, < 2.0)
|
39
|
+
term-ansicolor (1.0.6)
|
40
|
+
tilt (1.3.2)
|
41
|
+
timecop (0.3.5)
|
42
|
+
turn (0.8.2)
|
43
|
+
ansi (>= 1.2.2)
|
44
|
+
yajl-ruby (0.8.3)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
contest
|
51
|
+
haml
|
52
|
+
jeweler
|
53
|
+
json
|
54
|
+
kensa!
|
55
|
+
rr
|
56
|
+
sinatra (>= 0.9)
|
57
|
+
timecop (>= 0.3.5)
|
58
|
+
turn
|
data/Rakefile
CHANGED
@@ -2,8 +2,9 @@ desc 'Run all unit tests'
|
|
2
2
|
task :test do
|
3
3
|
fork do
|
4
4
|
exec "ruby test/resources/server.rb > /dev/null 2>&1"
|
5
|
+
#exec "ruby test/resources/server.rb > log.txt 2>&1"
|
5
6
|
end
|
6
|
-
system "turn"
|
7
|
+
system "turn test"
|
7
8
|
system "ps -ax | grep test/resources/server.rb | grep -v grep | awk '{print $1}' | xargs kill"
|
8
9
|
end
|
9
10
|
|
@@ -19,7 +20,7 @@ begin
|
|
19
20
|
gemspec.description = "Kensa is a command-line tool to help add-on providers integrating their services with Heroku. It manages manifest files, and provides a TDD-like approach for programmers to test and develop their APIs."
|
20
21
|
gemspec.email = "pedro@heroku.com"
|
21
22
|
gemspec.homepage = "http://provider.heroku.com/resources"
|
22
|
-
gemspec.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins"]
|
23
|
+
gemspec.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins", "Chris Continanza"]
|
23
24
|
|
24
25
|
gemspec.add_development_dependency(%q<turn>, [">= 0"])
|
25
26
|
gemspec.add_development_dependency(%q<contest>, [">= 0"])
|
data/bin/kensa
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'rubygems'
|
3
7
|
require 'optparse'
|
4
8
|
require 'heroku/kensa'
|
5
9
|
require 'heroku/kensa/client'
|
@@ -56,6 +60,9 @@ OPTIONS
|
|
56
60
|
--without-sso
|
57
61
|
Skip single sign-on authentication when doing provision calls
|
58
62
|
|
63
|
+
--post
|
64
|
+
Use HTTP POST for single sign-on instead of GET
|
65
|
+
|
59
66
|
COMMANDS
|
60
67
|
|
61
68
|
init Creates a skeleton manifest
|
data/kensa.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{kensa}
|
8
|
-
s.version = "1.1.
|
8
|
+
s.version = "1.1.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins"]
|
12
|
-
s.date = %q{2011-
|
11
|
+
s.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins", 'Chris Continanza']
|
12
|
+
s.date = %q{2011-08-22}
|
13
13
|
s.default_executable = %q{kensa}
|
14
14
|
s.description = %q{Kensa is a command-line tool to help add-on providers integrating their services with Heroku. It manages manifest files, and provides a TDD-like approach for programmers to test and develop their APIs.}
|
15
15
|
s.email = %q{pedro@heroku.com}
|
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
"README.md"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
21
23
|
"README.md",
|
22
24
|
"Rakefile",
|
23
25
|
"bin/kensa",
|
@@ -27,6 +29,7 @@ Gem::Specification.new do |s|
|
|
27
29
|
"lib/heroku/kensa/client.rb",
|
28
30
|
"lib/heroku/kensa/http.rb",
|
29
31
|
"lib/heroku/kensa/manifest.rb",
|
32
|
+
"lib/heroku/kensa/post_proxy.rb",
|
30
33
|
"lib/heroku/kensa/sso.rb",
|
31
34
|
"set-env.sh",
|
32
35
|
"test/all_check_test.rb",
|
@@ -44,33 +47,23 @@ Gem::Specification.new do |s|
|
|
44
47
|
]
|
45
48
|
s.homepage = %q{http://provider.heroku.com/resources}
|
46
49
|
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = %q{1.
|
50
|
+
s.rubygems_version = %q{1.6.2}
|
48
51
|
s.summary = %q{Tool to help Heroku add-on providers integrating their services}
|
49
|
-
s.test_files = [
|
50
|
-
"test/all_check_test.rb",
|
51
|
-
"test/deprovision_check_test.rb",
|
52
|
-
"test/helper.rb",
|
53
|
-
"test/manifest_check_test.rb",
|
54
|
-
"test/manifest_test.rb",
|
55
|
-
"test/plan_change_check_test.rb",
|
56
|
-
"test/provision_check_test.rb",
|
57
|
-
"test/provision_response_check_test.rb",
|
58
|
-
"test/resources/runner.rb",
|
59
|
-
"test/resources/server.rb",
|
60
|
-
"test/sso_check_test.rb",
|
61
|
-
"test/sso_test.rb"
|
62
|
-
]
|
63
52
|
|
64
53
|
if s.respond_to? :specification_version then
|
65
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
66
54
|
s.specification_version = 3
|
67
55
|
|
68
|
-
if Gem::Version.new(Gem::
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
69
57
|
s.add_development_dependency(%q<turn>, [">= 0"])
|
70
58
|
s.add_development_dependency(%q<contest>, [">= 0"])
|
71
59
|
s.add_development_dependency(%q<timecop>, [">= 0.3.5"])
|
72
60
|
s.add_development_dependency(%q<sinatra>, [">= 0.9"])
|
73
|
-
s.
|
61
|
+
s.add_development_dependency(%q<json>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<contest>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<haml>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
66
|
+
s.add_runtime_dependency(%q<rest-client>, ["< 1.7.0", ">= 1.4.0"])
|
74
67
|
s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.6"])
|
75
68
|
s.add_runtime_dependency(%q<term-ansicolor>, ["~> 1.0"])
|
76
69
|
s.add_runtime_dependency(%q<launchy>, [">= 0.3.2"])
|
@@ -80,7 +73,12 @@ Gem::Specification.new do |s|
|
|
80
73
|
s.add_dependency(%q<contest>, [">= 0"])
|
81
74
|
s.add_dependency(%q<timecop>, [">= 0.3.5"])
|
82
75
|
s.add_dependency(%q<sinatra>, [">= 0.9"])
|
83
|
-
s.add_dependency(%q<
|
76
|
+
s.add_dependency(%q<json>, [">= 0"])
|
77
|
+
s.add_dependency(%q<contest>, [">= 0"])
|
78
|
+
s.add_dependency(%q<haml>, [">= 0"])
|
79
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
80
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
81
|
+
s.add_dependency(%q<rest-client>, ["< 1.7.0", ">= 1.4.0"])
|
84
82
|
s.add_dependency(%q<yajl-ruby>, ["~> 0.6"])
|
85
83
|
s.add_dependency(%q<term-ansicolor>, ["~> 1.0"])
|
86
84
|
s.add_dependency(%q<launchy>, [">= 0.3.2"])
|
@@ -91,7 +89,12 @@ Gem::Specification.new do |s|
|
|
91
89
|
s.add_dependency(%q<contest>, [">= 0"])
|
92
90
|
s.add_dependency(%q<timecop>, [">= 0.3.5"])
|
93
91
|
s.add_dependency(%q<sinatra>, [">= 0.9"])
|
94
|
-
s.add_dependency(%q<
|
92
|
+
s.add_dependency(%q<json>, [">= 0"])
|
93
|
+
s.add_dependency(%q<contest>, [">= 0"])
|
94
|
+
s.add_dependency(%q<haml>, [">= 0"])
|
95
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
96
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
97
|
+
s.add_dependency(%q<rest-client>, ["< 1.7.0", ">= 1.4.0"])
|
95
98
|
s.add_dependency(%q<yajl-ruby>, ["~> 0.6"])
|
96
99
|
s.add_dependency(%q<term-ansicolor>, ["~> 1.0"])
|
97
100
|
s.add_dependency(%q<launchy>, [">= 0.3.2"])
|
data/lib/heroku/kensa/check.rb
CHANGED
@@ -8,7 +8,6 @@ module Heroku
|
|
8
8
|
module Kensa
|
9
9
|
|
10
10
|
class NilScreen
|
11
|
-
|
12
11
|
def test(msg)
|
13
12
|
end
|
14
13
|
|
@@ -25,6 +24,11 @@ module Heroku
|
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
27
|
+
class STDOUTScreen
|
28
|
+
[:test, :check, :error, :result, :message].each do |method|
|
29
|
+
eval %{ def #{method}(*args)\n STDOUT.puts *args\n end }
|
30
|
+
end
|
31
|
+
end
|
28
32
|
|
29
33
|
class Check
|
30
34
|
attr_accessor :screen, :data
|
@@ -376,8 +380,12 @@ module Heroku
|
|
376
380
|
@agent ||= Mechanize.new
|
377
381
|
end
|
378
382
|
|
379
|
-
def mechanize_get
|
380
|
-
|
383
|
+
def mechanize_get
|
384
|
+
if @sso.POST?
|
385
|
+
page = agent.post(@sso.post_url, @sso.query_params)
|
386
|
+
else
|
387
|
+
page = agent.get(@sso.get_url)
|
388
|
+
end
|
381
389
|
return page, 200
|
382
390
|
rescue Mechanize::ResponseCodeError => error
|
383
391
|
return nil, error.response_code.to_i
|
@@ -385,37 +393,43 @@ module Heroku
|
|
385
393
|
error("connection refused to #{url}")
|
386
394
|
end
|
387
395
|
|
396
|
+
def check(msg)
|
397
|
+
@sso = Sso.new(data)
|
398
|
+
super
|
399
|
+
end
|
400
|
+
|
388
401
|
def call!
|
389
402
|
error("need an sso salt to perform sso test") unless data['api']['sso_salt']
|
390
403
|
|
391
|
-
sso
|
392
|
-
|
404
|
+
sso = Sso.new(data)
|
405
|
+
verb = sso.POST? ? 'POST' : 'GET'
|
406
|
+
test "#{verb} #{sso.path}"
|
393
407
|
|
394
|
-
test "GET #{sso.path}"
|
395
408
|
check "validates token" do
|
396
|
-
|
397
|
-
|
409
|
+
@sso.token = 'invalid'
|
410
|
+
page, respcode = mechanize_get
|
411
|
+
error("expected 403, got #{respcode}") unless respcode == 403
|
398
412
|
true
|
399
413
|
end
|
400
414
|
|
401
415
|
check "validates timestamp" do
|
402
|
-
|
403
|
-
page, respcode = mechanize_get
|
404
|
-
error("expected 403, got
|
416
|
+
@sso.timestamp = (Time.now - 60*6).to_i
|
417
|
+
page, respcode = mechanize_get
|
418
|
+
error("expected 403, got #{respcode}") unless respcode == 403
|
405
419
|
true
|
406
420
|
end
|
407
421
|
|
408
422
|
page_logged_in = nil
|
409
423
|
check "logs in" do
|
410
|
-
page_logged_in, respcode = mechanize_get
|
424
|
+
page_logged_in, respcode = mechanize_get
|
411
425
|
error("expected 200, got #{respcode}") unless respcode == 200
|
412
426
|
true
|
413
427
|
end
|
414
428
|
|
415
429
|
check "creates the heroku-nav-data cookie" do
|
416
|
-
cookie = agent.cookie_jar.cookies(URI.parse(sso.full_url)).detect { |c| c.name == 'heroku-nav-data' }
|
430
|
+
cookie = agent.cookie_jar.cookies(URI.parse(@sso.full_url)).detect { |c| c.name == 'heroku-nav-data' }
|
417
431
|
error("could not find cookie heroku-nav-data") unless cookie
|
418
|
-
error("expected #{sso.sample_nav_data}, got #{cookie.value}") unless cookie.value == sso.sample_nav_data
|
432
|
+
error("expected #{@sso.sample_nav_data}, got #{cookie.value}") unless cookie.value == @sso.sample_nav_data
|
419
433
|
true
|
420
434
|
end
|
421
435
|
|
data/lib/heroku/kensa/client.rb
CHANGED
@@ -58,9 +58,9 @@ module Heroku
|
|
58
58
|
def sso
|
59
59
|
id = @args.shift || abort("! no id specified; see usage")
|
60
60
|
data = Yajl::Parser.parse(resolve_manifest).merge(:id => id)
|
61
|
-
sso = Sso.new(data.merge(@options))
|
62
|
-
puts
|
63
|
-
Launchy.open sso.
|
61
|
+
sso = Sso.new(data.merge(@options)).start
|
62
|
+
puts sso.message
|
63
|
+
Launchy.open sso.sso_url
|
64
64
|
end
|
65
65
|
|
66
66
|
def push
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
|
3
|
+
class Heroku::Kensa::PostProxy < WEBrick::HTTPServer
|
4
|
+
def initialize(sso)
|
5
|
+
@params = sso.query_params
|
6
|
+
@sso = sso
|
7
|
+
super :Port => sso.proxy_port, :AccessLog => WEBrick::Log.new(StringIO.new),
|
8
|
+
:Logger => WEBrick::Log.new(StringIO.new)
|
9
|
+
end
|
10
|
+
|
11
|
+
def service(req, res)
|
12
|
+
res.status = 200
|
13
|
+
res.body = <<-HTML
|
14
|
+
<html>
|
15
|
+
<head>
|
16
|
+
<script type="text/javascript">
|
17
|
+
window.onload = function() { document.forms[0].submit() }
|
18
|
+
</script>
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
<form action="#{@sso.post_url}" method="POST">
|
22
|
+
#{ @params.map do |key, value|
|
23
|
+
%|<input type="hidden" name="#{key}" value="#{value}" />|
|
24
|
+
end.join("\n")
|
25
|
+
}
|
26
|
+
</form>
|
27
|
+
</body>
|
28
|
+
</html>
|
29
|
+
HTML
|
30
|
+
res["Content-Lengh"] = res.body.size
|
31
|
+
@status = :Shutdown
|
32
|
+
end
|
33
|
+
end
|
data/lib/heroku/kensa/sso.rb
CHANGED
@@ -3,7 +3,7 @@ require 'restclient'
|
|
3
3
|
module Heroku
|
4
4
|
module Kensa
|
5
5
|
class Sso
|
6
|
-
attr_accessor :id, :url
|
6
|
+
attr_accessor :id, :url, :proxy_port, :timestamp, :token
|
7
7
|
|
8
8
|
def initialize(data)
|
9
9
|
@id = data[:id]
|
@@ -11,15 +11,42 @@ module Heroku
|
|
11
11
|
|
12
12
|
env = data.fetch :env, 'test'
|
13
13
|
@url = data["api"][env].chomp('/')
|
14
|
+
@use_post = data['api']['sso'].to_s.match(/post/i)
|
15
|
+
@proxy_port = find_available_port
|
16
|
+
@timestamp = Time.now.to_i
|
17
|
+
@token = make_token(@timestamp)
|
14
18
|
end
|
15
19
|
|
16
20
|
def path
|
17
|
-
|
21
|
+
extra = self.POST? ? '/sso' : ''
|
22
|
+
"/heroku/resources/#{id}#{extra}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def POST?
|
26
|
+
@use_post
|
27
|
+
end
|
28
|
+
|
29
|
+
def sso_url
|
30
|
+
if self.POST?
|
31
|
+
"http://localhost:#{@proxy_port}/"
|
32
|
+
else
|
33
|
+
full_url
|
34
|
+
end
|
18
35
|
end
|
19
36
|
|
20
37
|
def full_url
|
21
38
|
[ url, path, querystring ].join
|
22
39
|
end
|
40
|
+
alias get_url full_url
|
41
|
+
|
42
|
+
def post_url
|
43
|
+
[ url, path ].join
|
44
|
+
end
|
45
|
+
|
46
|
+
def timestamp=(other)
|
47
|
+
@timestamp = other
|
48
|
+
@token = make_token(@timestamp)
|
49
|
+
end
|
23
50
|
|
24
51
|
def make_token(t)
|
25
52
|
Digest::SHA1.hexdigest([@id, @salt, t].join(':'))
|
@@ -27,9 +54,18 @@ module Heroku
|
|
27
54
|
|
28
55
|
def querystring
|
29
56
|
return '' unless @salt
|
57
|
+
'?' + query_data
|
58
|
+
end
|
59
|
+
|
60
|
+
def query_data
|
61
|
+
query_params.map{|p| p.join('=')}.join('&')
|
62
|
+
end
|
30
63
|
|
31
|
-
|
32
|
-
|
64
|
+
def query_params
|
65
|
+
{ 'token' => @token,
|
66
|
+
'timestamp' => @timestamp.to_s,
|
67
|
+
'nav-data' => sample_nav_data,
|
68
|
+
'user' => 'username@example.com' }
|
33
69
|
end
|
34
70
|
|
35
71
|
def sample_nav_data
|
@@ -50,6 +86,37 @@ module Heroku
|
|
50
86
|
base64.tr('+/','-_')
|
51
87
|
end
|
52
88
|
|
89
|
+
def message
|
90
|
+
if self.POST?
|
91
|
+
"POSTing #{query_data} to #{post_url} via proxy on port #{@proxy_port}"
|
92
|
+
else
|
93
|
+
"Opening #{full_url}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def start
|
98
|
+
run_proxy
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_available_port
|
103
|
+
server = TCPServer.new('127.0.0.1', 0)
|
104
|
+
server.addr[1]
|
105
|
+
ensure
|
106
|
+
server.close if server
|
107
|
+
end
|
108
|
+
|
109
|
+
def run_proxy
|
110
|
+
return unless self.POST?
|
111
|
+
server = PostProxy.new self
|
112
|
+
@proxy = server
|
113
|
+
|
114
|
+
trap("INT") { server.stop }
|
115
|
+
pid = fork do
|
116
|
+
server.start
|
117
|
+
end
|
118
|
+
at_exit { server.stop; Process.waitpid pid }
|
119
|
+
end
|
53
120
|
end
|
54
121
|
end
|
55
122
|
end
|
data/lib/heroku/kensa.rb
CHANGED
@@ -15,21 +15,21 @@ class DeprovisionCheckTest < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
test "valid on 200" do
|
17
17
|
assert_valid do |check|
|
18
|
-
|
18
|
+
kensa_stub :delete, check, @responses
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
test "status other than 200" do
|
23
23
|
@responses[0] = [500, ""]
|
24
24
|
assert_invalid do |check|
|
25
|
-
|
25
|
+
kensa_stub :delete, check, @responses
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
test "runs auth check" do
|
30
30
|
@responses[1] = [200, ""]
|
31
31
|
assert_invalid do |check|
|
32
|
-
|
32
|
+
kensa_stub :delete, check, @responses
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
data/test/helper.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
require 'heroku/kensa'
|
2
2
|
require 'contest'
|
3
3
|
require 'timecop'
|
4
|
+
require 'rr'
|
4
5
|
|
5
6
|
class Test::Unit::TestCase
|
7
|
+
include RR::Adapters::TestUnit
|
6
8
|
|
9
|
+
# in your test, do
|
10
|
+
# @screen = STDOUTScreen.new
|
7
11
|
def assert_valid(data=@data, &blk)
|
8
12
|
check = create_check(data, &blk)
|
13
|
+
check.screen = @screen if @screen
|
9
14
|
assert check.call
|
10
15
|
end
|
11
16
|
|
12
17
|
def assert_invalid(data=@data, &blk)
|
13
18
|
check = create_check(data, &blk)
|
19
|
+
check.screen = @screen if @screen
|
14
20
|
assert !check.call
|
15
21
|
end
|
16
22
|
|
@@ -37,13 +43,12 @@ class Test::Unit::TestCase
|
|
37
43
|
o
|
38
44
|
end
|
39
45
|
|
40
|
-
def
|
46
|
+
def kensa_stub(meth, o, returns)
|
41
47
|
o.instance_eval { @returns = Array(returns) }
|
42
48
|
eval <<-EVAL
|
43
49
|
def o.#{meth}(*args)
|
44
|
-
@returns.shift
|
50
|
+
@returns.shift or fail("Nothing else to return from stub'ed method")
|
45
51
|
end
|
46
52
|
EVAL
|
47
53
|
end
|
48
|
-
|
49
54
|
end
|
data/test/resources/server.rb
CHANGED
@@ -85,8 +85,7 @@ delete '/working/heroku/resources/:id' do
|
|
85
85
|
"Ok"
|
86
86
|
end
|
87
87
|
|
88
|
-
|
89
|
-
get '/working/heroku/resources/:id' do
|
88
|
+
def sso
|
90
89
|
unauthorized! unless params[:id] && params[:token]
|
91
90
|
unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
|
92
91
|
unauthorized! unless params[:token] == make_token
|
@@ -94,21 +93,45 @@ get '/working/heroku/resources/:id' do
|
|
94
93
|
login
|
95
94
|
end
|
96
95
|
|
97
|
-
get '/
|
96
|
+
get '/working/heroku/resources/:id' do
|
97
|
+
sso
|
98
|
+
end
|
99
|
+
|
100
|
+
post '/working/heroku/resources/:id/sso' do
|
101
|
+
sso
|
102
|
+
end
|
103
|
+
|
104
|
+
def notoken
|
98
105
|
unauthorized! unless params[:id] && params[:token]
|
99
106
|
unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
|
100
107
|
response.set_cookie('heroku-nav-data', params['nav-data'])
|
101
108
|
login
|
102
109
|
end
|
103
110
|
|
104
|
-
get '/
|
111
|
+
get '/notoken/heroku/resources/:id' do
|
112
|
+
notoken
|
113
|
+
end
|
114
|
+
|
115
|
+
post '/notoken/heroku/resources/:id/sso' do
|
116
|
+
notoken
|
117
|
+
end
|
118
|
+
|
119
|
+
def notimestamp
|
105
120
|
unauthorized! unless params[:id] && params[:token]
|
106
121
|
unauthorized! unless params[:token] == make_token
|
107
122
|
response.set_cookie('heroku-nav-data', params['nav-data'])
|
108
123
|
login
|
109
124
|
end
|
110
125
|
|
111
|
-
get '/
|
126
|
+
get '/notimestamp/heroku/resources/:id' do
|
127
|
+
notimestamp
|
128
|
+
end
|
129
|
+
|
130
|
+
post '/notimestamp/heroku/resources/:id/sso' do
|
131
|
+
notimestamp
|
132
|
+
end
|
133
|
+
|
134
|
+
def nolayout
|
112
135
|
unauthorized! unless params[:id] && params[:token]
|
113
136
|
unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
|
114
137
|
unauthorized! unless params[:token] == make_token
|
@@ -116,14 +139,30 @@ get '/nolayout/heroku/resources/:id' do
|
|
116
139
|
login(false)
|
117
140
|
end
|
118
141
|
|
119
|
-
get '/
|
142
|
+
get '/nolayout/heroku/resources/:id' do
|
143
|
+
nolayout
|
144
|
+
end
|
145
|
+
|
146
|
+
post '/nolayout/heroku/resources/:id/sso' do
|
147
|
+
nolayout
|
148
|
+
end
|
149
|
+
|
150
|
+
def nocookie
|
120
151
|
unauthorized! unless params[:id] && params[:token]
|
121
152
|
unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
|
122
153
|
unauthorized! unless params[:token] == make_token
|
123
154
|
login
|
124
155
|
end
|
125
156
|
|
126
|
-
get '/
|
157
|
+
get '/nocookie/heroku/resources/:id' do
|
158
|
+
nocookie
|
159
|
+
end
|
160
|
+
|
161
|
+
post '/nocookie/heroku/resources/:id/sso' do
|
162
|
+
nocookie
|
163
|
+
end
|
164
|
+
|
165
|
+
def badcookie
|
127
166
|
unauthorized! unless params[:id] && params[:token]
|
128
167
|
unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
|
129
168
|
unauthorized! unless params[:token] == make_token
|
@@ -131,6 +170,27 @@ get '/badcookie/heroku/resources/:id' do
|
|
131
170
|
login
|
132
171
|
end
|
133
172
|
|
173
|
+
get '/badcookie/heroku/resources/:id' do
|
174
|
+
badcookie
|
175
|
+
end
|
176
|
+
|
177
|
+
post '/badcookie/heroku/resources/:id/sso' do
|
178
|
+
badcookie
|
179
|
+
end
|
180
|
+
|
181
|
+
def sso_user
|
182
|
+
head 404 unless params[:user] == 'username@example.com'
|
183
|
+
sso
|
184
|
+
end
|
185
|
+
|
186
|
+
get '/user/heroku/resources/:id' do
|
187
|
+
sso_user
|
188
|
+
end
|
189
|
+
|
190
|
+
post '/user/heroku/resources/:id/sso' do
|
191
|
+
sso_user
|
192
|
+
end
|
193
|
+
|
134
194
|
get '/' do
|
135
195
|
unauthorized! unless session[:logged_in]
|
136
196
|
haml :index
|
@@ -144,4 +204,4 @@ __END__
|
|
144
204
|
- if session[:heroku]
|
145
205
|
#heroku-header
|
146
206
|
%h1 Heroku
|
147
|
-
%h1 Sample Addon
|
207
|
+
%h1 Sample Addon
|
data/test/sso_check_test.rb
CHANGED
@@ -10,40 +10,50 @@ class SsoCheckTest < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
def check ; SsoCheck ; end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
['POST', 'GET'].each do |method|
|
14
|
+
context "via #{method}" do
|
15
|
+
setup { @data['api']['sso'] = method }
|
16
|
+
|
17
|
+
test "working sso request" do
|
18
|
+
@data['api']['test'] += "working"
|
19
|
+
assert_valid
|
20
|
+
end
|
21
|
+
|
22
|
+
test "rejects bad token" do
|
23
|
+
@data['api']['test'] += "notoken"
|
24
|
+
assert_invalid
|
25
|
+
end
|
26
|
+
|
27
|
+
test "rejects old timestamp" do
|
28
|
+
@data['api']['test'] += "notimestamp"
|
29
|
+
assert_invalid
|
30
|
+
end
|
31
|
+
|
32
|
+
test "reject omitted sso salt" do
|
33
|
+
@data['api'].delete 'sso_salt'
|
34
|
+
@data['api']['test'] += "working"
|
35
|
+
assert_invalid
|
36
|
+
end
|
37
|
+
|
38
|
+
test "reject missing heroku layout" do
|
39
|
+
@data['api']['test'] += "nolayout"
|
40
|
+
assert_invalid
|
41
|
+
end
|
42
|
+
|
43
|
+
test "reject missing cookie" do
|
44
|
+
@data['api']['test'] += "nocookie"
|
45
|
+
assert_invalid
|
46
|
+
end
|
47
|
+
|
48
|
+
test "reject invalid cookie value" do
|
49
|
+
@data['api']['test'] += "badcookie"
|
50
|
+
assert_invalid
|
51
|
+
end
|
52
|
+
|
53
|
+
test "sends user param" do
|
54
|
+
@data['api']['test'] += "user"
|
55
|
+
assert_valid
|
56
|
+
end
|
57
|
+
end
|
16
58
|
end
|
17
|
-
|
18
|
-
test "rejects bad token" do
|
19
|
-
@data['api']['test'] += "notoken"
|
20
|
-
assert_invalid
|
21
|
-
end
|
22
|
-
|
23
|
-
test "rejects old timestamp" do
|
24
|
-
@data['api']['test'] += "notimestamp"
|
25
|
-
assert_invalid
|
26
|
-
end
|
27
|
-
|
28
|
-
test "reject omitted sso salt" do
|
29
|
-
@data['api'].delete 'sso_salt'
|
30
|
-
@data['api']['test'] += "working"
|
31
|
-
assert_invalid
|
32
|
-
end
|
33
|
-
|
34
|
-
test "reject missing heroku layout" do
|
35
|
-
@data['api']['test'] += "nolayout"
|
36
|
-
assert_invalid
|
37
|
-
end
|
38
|
-
|
39
|
-
test "reject missing cookie" do
|
40
|
-
@data['api']['test'] += "nocookie"
|
41
|
-
assert_invalid
|
42
|
-
end
|
43
|
-
|
44
|
-
test "reject invalid cookie value" do
|
45
|
-
@data['api']['test'] += "badcookie"
|
46
|
-
assert_invalid
|
47
|
-
end
|
48
|
-
|
49
59
|
end
|
data/test/sso_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'test/helper'
|
2
|
+
require 'cgi'
|
2
3
|
|
3
4
|
class SsoTest < Test::Unit::TestCase
|
4
5
|
include Heroku::Kensa
|
@@ -11,18 +12,87 @@ class SsoTest < Test::Unit::TestCase
|
|
11
12
|
|
12
13
|
teardown { Timecop.return }
|
13
14
|
|
15
|
+
def builds_full_url(env)
|
16
|
+
url, query = @sso.full_url.split('?')
|
17
|
+
data = CGI.parse(query)
|
18
|
+
|
19
|
+
|
20
|
+
assert_equal "#{@data['api'][env]}heroku/resources/1", url
|
21
|
+
assert_equal 'b6010f6fbb850887a396c2bc0ab23974003008f6', data['token'].first
|
22
|
+
assert_equal '1262304000', data['timestamp'].first
|
23
|
+
assert_equal 'username@example.com', data['user'].first
|
24
|
+
end
|
25
|
+
|
14
26
|
context 'sso' do
|
15
|
-
setup
|
27
|
+
setup do
|
28
|
+
Timecop.freeze Time.utc(2010, 1)
|
29
|
+
@sso = Sso.new @data
|
30
|
+
end
|
16
31
|
|
17
32
|
test 'builds path' do
|
18
33
|
assert_equal '/heroku/resources/1', @sso.path
|
19
34
|
end
|
20
35
|
|
21
36
|
test 'builds full url' do
|
22
|
-
|
23
|
-
|
37
|
+
builds_full_url('test')
|
38
|
+
end
|
24
39
|
|
25
|
-
|
40
|
+
context 'with no "sso" field specified' do
|
41
|
+
test "defaults to GET" do
|
42
|
+
assert_equal @sso.full_url, @sso.sso_url
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when sso method is GET' do
|
47
|
+
setup do
|
48
|
+
@data['api']['sso'] = 'GET'
|
49
|
+
@sso = Sso.new(@data).start
|
50
|
+
end
|
51
|
+
|
52
|
+
test "#sso_url should be the #full_url" do
|
53
|
+
assert_equal @sso.full_url, @sso.sso_url
|
54
|
+
end
|
55
|
+
|
56
|
+
test "#message is Opening <full_url>" do
|
57
|
+
assert_equal "Opening #{@sso.full_url}", @sso.message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when sso method is POST' do
|
62
|
+
setup do
|
63
|
+
Timecop.freeze Time.utc(2010, 1)
|
64
|
+
@data['api']['sso'] = 'post'
|
65
|
+
end
|
66
|
+
|
67
|
+
test "it starts the proxy server" do
|
68
|
+
@sso = Sso.new(@data).start
|
69
|
+
body = RestClient.get(@sso.sso_url)
|
70
|
+
|
71
|
+
assert body.include? @sso.path
|
72
|
+
assert body.include? 'b6010f6fbb850887a396c2bc0ab23974003008f6'
|
73
|
+
assert body.include? '1262304000'
|
74
|
+
assert body.include? @sso.url
|
75
|
+
assert body.include? @sso.sample_nav_data
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with the proxy working" do
|
79
|
+
setup do
|
80
|
+
any_instance_of(Sso, :run_proxy => false)
|
81
|
+
@sso = Sso.new(@data).start
|
82
|
+
end
|
83
|
+
|
84
|
+
test "#sso_url should point to the proxy" do
|
85
|
+
assert_equal "http://localhost:#{@sso.proxy_port}/", @sso.sso_url
|
86
|
+
end
|
87
|
+
|
88
|
+
test "#post_url contains url and path" do
|
89
|
+
assert_equal "http://localhost:4567/heroku/resources/1/sso", @sso.post_url
|
90
|
+
end
|
91
|
+
|
92
|
+
test "#message is Posting <data> to <post_url> via proxy on port <proxy_port>" do
|
93
|
+
assert_equal "POSTing #{@sso.query_data} to http://localhost:4567/heroku/resources/1/sso via proxy on port #{@sso.proxy_port}", @sso.message
|
94
|
+
end
|
95
|
+
end
|
26
96
|
end
|
27
97
|
end
|
28
98
|
|
@@ -41,6 +111,7 @@ class SsoTest < Test::Unit::TestCase
|
|
41
111
|
|
42
112
|
context 'sso in a specific environment' do
|
43
113
|
setup do
|
114
|
+
Timecop.freeze Time.utc(2010, 1)
|
44
115
|
env = 'production'
|
45
116
|
@data[:env] = env
|
46
117
|
@data['api'][env] = 'http://localhost:7654/'
|
@@ -49,10 +120,7 @@ class SsoTest < Test::Unit::TestCase
|
|
49
120
|
end
|
50
121
|
|
51
122
|
test 'builds full url' do
|
52
|
-
|
53
|
-
expected = 'http://localhost:7654/heroku/resources/1?token=b6010f6fbb850887a396c2bc0ab23974003008f6×tamp=1262304000'
|
54
|
-
|
55
|
-
assert @sso.full_url.include?(expected)
|
123
|
+
builds_full_url('production')
|
56
124
|
end
|
57
125
|
end
|
58
126
|
end
|
metadata
CHANGED
@@ -5,18 +5,19 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 1.1.
|
8
|
+
- 4
|
9
|
+
version: 1.1.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Blake Mizerany
|
13
13
|
- Pedro Belo
|
14
14
|
- Adam Wiggins
|
15
|
+
- Chris Continanza
|
15
16
|
autorequire:
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2011-
|
20
|
+
date: 2011-08-22 00:00:00 -07:00
|
20
21
|
default_executable: kensa
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -71,17 +72,70 @@ dependencies:
|
|
71
72
|
type: :development
|
72
73
|
version_requirements: *id004
|
73
74
|
- !ruby/object:Gem::Dependency
|
74
|
-
name:
|
75
|
+
name: json
|
75
76
|
prerelease: false
|
76
77
|
requirement: &id005 !ruby/object:Gem::Requirement
|
77
78
|
requirements:
|
78
79
|
- - ">="
|
79
80
|
- !ruby/object:Gem::Version
|
80
81
|
segments:
|
81
|
-
- 1
|
82
|
-
- 4
|
83
82
|
- 0
|
84
|
-
version:
|
83
|
+
version: "0"
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id005
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: contest
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
type: :development
|
97
|
+
version_requirements: *id006
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: haml
|
100
|
+
prerelease: false
|
101
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
type: :development
|
109
|
+
version_requirements: *id007
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: jeweler
|
112
|
+
prerelease: false
|
113
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
type: :development
|
121
|
+
version_requirements: *id008
|
122
|
+
- !ruby/object:Gem::Dependency
|
123
|
+
name: rr
|
124
|
+
prerelease: false
|
125
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
type: :development
|
133
|
+
version_requirements: *id009
|
134
|
+
- !ruby/object:Gem::Dependency
|
135
|
+
name: rest-client
|
136
|
+
prerelease: false
|
137
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
85
139
|
- - <
|
86
140
|
- !ruby/object:Gem::Version
|
87
141
|
segments:
|
@@ -89,12 +143,19 @@ dependencies:
|
|
89
143
|
- 7
|
90
144
|
- 0
|
91
145
|
version: 1.7.0
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
segments:
|
149
|
+
- 1
|
150
|
+
- 4
|
151
|
+
- 0
|
152
|
+
version: 1.4.0
|
92
153
|
type: :runtime
|
93
|
-
version_requirements: *
|
154
|
+
version_requirements: *id010
|
94
155
|
- !ruby/object:Gem::Dependency
|
95
156
|
name: yajl-ruby
|
96
157
|
prerelease: false
|
97
|
-
requirement: &
|
158
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
98
159
|
requirements:
|
99
160
|
- - ~>
|
100
161
|
- !ruby/object:Gem::Version
|
@@ -103,11 +164,11 @@ dependencies:
|
|
103
164
|
- 6
|
104
165
|
version: "0.6"
|
105
166
|
type: :runtime
|
106
|
-
version_requirements: *
|
167
|
+
version_requirements: *id011
|
107
168
|
- !ruby/object:Gem::Dependency
|
108
169
|
name: term-ansicolor
|
109
170
|
prerelease: false
|
110
|
-
requirement: &
|
171
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
111
172
|
requirements:
|
112
173
|
- - ~>
|
113
174
|
- !ruby/object:Gem::Version
|
@@ -116,11 +177,11 @@ dependencies:
|
|
116
177
|
- 0
|
117
178
|
version: "1.0"
|
118
179
|
type: :runtime
|
119
|
-
version_requirements: *
|
180
|
+
version_requirements: *id012
|
120
181
|
- !ruby/object:Gem::Dependency
|
121
182
|
name: launchy
|
122
183
|
prerelease: false
|
123
|
-
requirement: &
|
184
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
124
185
|
requirements:
|
125
186
|
- - ">="
|
126
187
|
- !ruby/object:Gem::Version
|
@@ -130,11 +191,11 @@ dependencies:
|
|
130
191
|
- 2
|
131
192
|
version: 0.3.2
|
132
193
|
type: :runtime
|
133
|
-
version_requirements: *
|
194
|
+
version_requirements: *id013
|
134
195
|
- !ruby/object:Gem::Dependency
|
135
196
|
name: mechanize
|
136
197
|
prerelease: false
|
137
|
-
requirement: &
|
198
|
+
requirement: &id014 !ruby/object:Gem::Requirement
|
138
199
|
requirements:
|
139
200
|
- - ~>
|
140
201
|
- !ruby/object:Gem::Version
|
@@ -144,7 +205,7 @@ dependencies:
|
|
144
205
|
- 0
|
145
206
|
version: 1.0.0
|
146
207
|
type: :runtime
|
147
|
-
version_requirements: *
|
208
|
+
version_requirements: *id014
|
148
209
|
description: Kensa is a command-line tool to help add-on providers integrating their services with Heroku. It manages manifest files, and provides a TDD-like approach for programmers to test and develop their APIs.
|
149
210
|
email: pedro@heroku.com
|
150
211
|
executables:
|
@@ -154,6 +215,8 @@ extensions: []
|
|
154
215
|
extra_rdoc_files:
|
155
216
|
- README.md
|
156
217
|
files:
|
218
|
+
- Gemfile
|
219
|
+
- Gemfile.lock
|
157
220
|
- README.md
|
158
221
|
- Rakefile
|
159
222
|
- bin/kensa
|
@@ -163,6 +226,7 @@ files:
|
|
163
226
|
- lib/heroku/kensa/client.rb
|
164
227
|
- lib/heroku/kensa/http.rb
|
165
228
|
- lib/heroku/kensa/manifest.rb
|
229
|
+
- lib/heroku/kensa/post_proxy.rb
|
166
230
|
- lib/heroku/kensa/sso.rb
|
167
231
|
- set-env.sh
|
168
232
|
- test/all_check_test.rb
|
@@ -207,16 +271,5 @@ rubygems_version: 1.3.6
|
|
207
271
|
signing_key:
|
208
272
|
specification_version: 3
|
209
273
|
summary: Tool to help Heroku add-on providers integrating their services
|
210
|
-
test_files:
|
211
|
-
|
212
|
-
- test/deprovision_check_test.rb
|
213
|
-
- test/helper.rb
|
214
|
-
- test/manifest_check_test.rb
|
215
|
-
- test/manifest_test.rb
|
216
|
-
- test/plan_change_check_test.rb
|
217
|
-
- test/provision_check_test.rb
|
218
|
-
- test/provision_response_check_test.rb
|
219
|
-
- test/resources/runner.rb
|
220
|
-
- test/resources/server.rb
|
221
|
-
- test/sso_check_test.rb
|
222
|
-
- test/sso_test.rb
|
274
|
+
test_files: []
|
275
|
+
|