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 ADDED
@@ -0,0 +1,9 @@
1
+ source :gemcutter
2
+ gemspec
3
+ group :development do
4
+ gem 'json'
5
+ gem 'contest'
6
+ gem 'haml'
7
+ gem 'jeweler'
8
+ gem 'rr'
9
+ end
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.3"
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-02-21}
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.3.6}
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::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
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.add_runtime_dependency(%q<rest-client>, [">= 1.4.0", "< 1.7.0"])
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<rest-client>, [">= 1.4.0", "< 1.7.0"])
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<rest-client>, [">= 1.4.0", "< 1.7.0"])
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"])
@@ -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 url
380
- page = agent.get(url)
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 = Sso.new(data)
392
- t = Time.now.to_i
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
- page, respcode = mechanize_get sso.url + sso.path + "?token=invalid&timestamp=#{t}"
397
- error("expected 403, got 200") unless respcode == 403
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
- prev = (Time.now - 60*6).to_i
403
- page, respcode = mechanize_get sso.url + sso.path + "?token=#{sso.make_token(prev)}&timestamp=#{prev}"
404
- error("expected 403, got 200") unless respcode == 403
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 sso.url + sso.path + sso.querystring
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
 
@@ -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 "Opening #{sso.full_url}"
63
- Launchy.open sso.full_url
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
@@ -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
- "/heroku/resources/#{id}"
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
- t = Time.now.to_i
32
- "?token=#{make_token(t)}&timestamp=#{t}&nav-data=#{sample_nav_data}"
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
@@ -2,9 +2,10 @@ require 'heroku/kensa/http'
2
2
  require 'heroku/kensa/manifest'
3
3
  require 'heroku/kensa/check'
4
4
  require 'heroku/kensa/sso'
5
+ require 'heroku/kensa/post_proxy'
5
6
 
6
7
  module Heroku
7
8
  module Kensa
8
- VERSION = "1.1.3"
9
+ VERSION = "1.1.4"
9
10
  end
10
- end
11
+ end
@@ -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
- stub :delete, check, @responses
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
- stub :delete, check, @responses
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
- stub :delete, check, @responses
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 stub(meth, o, returns)
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 || fail("Nothing else to return from stub'ed method")
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
@@ -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 '/notoken/heroku/resources/:id' do
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 '/notimestamp/heroku/resources/:id' do
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 '/nolayout/heroku/resources/:id' do
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 '/nocookie/heroku/resources/:id' do
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 '/badcookie/heroku/resources/:id' do
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
@@ -10,40 +10,50 @@ class SsoCheckTest < Test::Unit::TestCase
10
10
 
11
11
  def check ; SsoCheck ; end
12
12
 
13
- test "working sso request" do
14
- @data['api']['test'] += "working"
15
- assert_valid
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 { @sso = Sso.new @data }
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
- Timecop.freeze Time.utc(2010, 1)
23
- expected = 'http://localhost:4567/heroku/resources/1?token=b6010f6fbb850887a396c2bc0ab23974003008f6&timestamp=1262304000'
37
+ builds_full_url('test')
38
+ end
24
39
 
25
- assert @sso.full_url.include?(expected)
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
- Timecop.freeze Time.utc(2010, 1)
53
- expected = 'http://localhost:7654/heroku/resources/1?token=b6010f6fbb850887a396c2bc0ab23974003008f6&timestamp=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
- - 3
9
- version: 1.1.3
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-02-21 00:00:00 -08:00
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: rest-client
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: 1.4.0
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: *id005
154
+ version_requirements: *id010
94
155
  - !ruby/object:Gem::Dependency
95
156
  name: yajl-ruby
96
157
  prerelease: false
97
- requirement: &id006 !ruby/object:Gem::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: *id006
167
+ version_requirements: *id011
107
168
  - !ruby/object:Gem::Dependency
108
169
  name: term-ansicolor
109
170
  prerelease: false
110
- requirement: &id007 !ruby/object:Gem::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: *id007
180
+ version_requirements: *id012
120
181
  - !ruby/object:Gem::Dependency
121
182
  name: launchy
122
183
  prerelease: false
123
- requirement: &id008 !ruby/object:Gem::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: *id008
194
+ version_requirements: *id013
134
195
  - !ruby/object:Gem::Dependency
135
196
  name: mechanize
136
197
  prerelease: false
137
- requirement: &id009 !ruby/object:Gem::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: *id009
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
- - test/all_check_test.rb
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
+