kensa 1.1.3 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|