xplenty-kensa 1.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ require 'test/helper'
2
+
3
+ class OptionParsingTest < Test::Unit::TestCase
4
+ include Xplenty::Kensa
5
+ include FsMock
6
+
7
+ def options_for_cmd(string)
8
+ client = Client.new(string.split)
9
+ options = client.options[:options]
10
+ end
11
+
12
+ test "parameters get forwarded to provider" do
13
+ Artifice.activate_with(lambda { |env|
14
+ params = OkJson.decode env['rack.input'].read
15
+ options = params['options']
16
+ assert_equal 'true', options['foo']
17
+ assert_equal 'baz', options['bar']
18
+ assert_equal 'baz', options['fap']
19
+ [201, {}, 'hello']
20
+ }) do
21
+ kensa "init --sso get"
22
+ kensa "test provision --foo --bar=baz --fap baz"
23
+ end
24
+ end
25
+
26
+ def assert_normal_options(options)
27
+ assert_equal true, options[:async]
28
+ assert_equal 'true', options[:options]['foo']
29
+ assert_equal 'foo', options[:plan]
30
+ assert_equal 'foo.json', options[:filename]
31
+ assert_equal 'production', options[:env]
32
+ end
33
+
34
+ test "leaves normal args alone" do
35
+ cmd = "test provision --foo --production --async --file foo.json --plan foo"
36
+ assert_normal_options Client.new(cmd.split).options
37
+ end
38
+
39
+ test "works with single dash -s tyle flags" do
40
+ cmd = "test provision --foo --production --async -f foo.json -p foo"
41
+ assert_normal_options Client.new(cmd.split).options
42
+ end
43
+
44
+ test "parsing --flag" do
45
+ options = options_for_cmd("test provision --foo")
46
+ assert_equal 'true', options['foo']
47
+ end
48
+
49
+ test "parsing --flag=value" do
50
+ options = options_for_cmd("test provision --foo=bar")
51
+ assert_equal 'bar', options['foo']
52
+ end
53
+
54
+ test "parsing --flag value" do
55
+ options = options_for_cmd("test provision --foo bar")
56
+ assert_equal 'bar', options['foo']
57
+ end
58
+
59
+ test "parsing mixed" do
60
+ options = options_for_cmd("test provision --foo --bar foo --baz")
61
+ assert_equal 'true', options['foo']
62
+ assert_equal 'true', options['baz']
63
+ assert_equal 'foo', options['bar']
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ require 'test/helper'
2
+
3
+ class PlanChangeCheckTest < Test::Unit::TestCase
4
+ include Xplenty::Kensa
5
+ include ProviderMock
6
+
7
+ %w{get post}.each do |method|
8
+ context "with sso #{method}" do
9
+ setup do
10
+ @data = Manifest.new(:method => method).skeleton.merge :id => 123, :plan => 'premium'
11
+ @data['api']['password'] = 'secret'
12
+ end
13
+
14
+ def check ; PlanChangeCheck ; end
15
+
16
+ test "working plan change call" do
17
+ use_provider_endpoint "working"
18
+ assert_valid
19
+ end
20
+
21
+ test "detects invalid status" do
22
+ use_provider_endpoint "invalid-status"
23
+ assert_invalid
24
+ end
25
+
26
+ test "detects missing auth" do
27
+ use_provider_endpoint "invalid-missing-auth"
28
+ assert_invalid
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ require 'test/helper'
2
+
3
+ class ProvisionCheckTest < Test::Unit::TestCase
4
+ include Xplenty::Kensa
5
+ include ProviderMock
6
+
7
+ def check ; ProvisionCheck ; end
8
+
9
+ ['get', 'post'].each do |method|
10
+ context "with sso #{method}" do
11
+ setup do
12
+ @data = Manifest.new(:method => method).skeleton
13
+ @data['api']['password'] = 'secret'
14
+ end
15
+
16
+ test "trims url" do
17
+ c = check.new(@data)
18
+ assert_equal c.url, 'http://localhost:4567'
19
+ end
20
+
21
+ test "working provision call" do
22
+ use_provider_endpoint "working"
23
+ assert_valid
24
+ end
25
+
26
+ test "provision call with extra params" do
27
+ use_provider_endpoint "cmd-line-options"
28
+ @data[:options] = {:foo => 'bar', :bar => 'baz'}
29
+ assert_valid
30
+ end
31
+
32
+ # OkJson doesn't handle short strings correctly
33
+ test "doesn't choke on foo" do
34
+ use_provider_endpoint "foo"
35
+ assert_invalid
36
+ end
37
+
38
+ test "detects invalid JSON" do
39
+ use_provider_endpoint "invalid-json"
40
+ assert_invalid
41
+ end
42
+
43
+ test "detects invalid response" do
44
+ use_provider_endpoint "invalid-response"
45
+ assert_invalid
46
+ end
47
+
48
+ test "detects invalid status" do
49
+ use_provider_endpoint "invalid-status"
50
+ assert_invalid
51
+ end
52
+
53
+ test "detects missing id" do
54
+ use_provider_endpoint "invalid-missing-id"
55
+ assert_invalid
56
+ end
57
+
58
+ test "detects missing auth" do
59
+ use_provider_endpoint "invalid-missing-auth"
60
+ assert_invalid
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,81 @@
1
+ require 'test/helper'
2
+
3
+ class ProvisionResponseCheckTest < Test::Unit::TestCase
4
+ include Xplenty::Kensa
5
+
6
+ def check ; ProvisionResponseCheck ; end
7
+
8
+ setup do
9
+ @response = { "id" => "123",
10
+ "config" => {
11
+ "MYADDON_URL" => "http://example.com/resource",
12
+ "MYADDON_CONFIG" => "value"
13
+ }}
14
+ @data = Manifest.new.skeleton.merge(:provision_response => @response)
15
+ @data['api']['config_vars'] << "MYADDON_CONFIG"
16
+ end
17
+
18
+ test "is valid if no errors" do
19
+ assert_valid
20
+ end
21
+
22
+ test "has an id" do
23
+ @response.delete("id")
24
+ assert_invalid
25
+ end
26
+
27
+ describe "when config is present" do
28
+
29
+ test "is a hash" do
30
+ @response["config"] = ""
31
+ assert_invalid
32
+ end
33
+
34
+ test "each key is previously set in the manifest" do
35
+ @response["config"]["MYSQL_URL"] = "http://..."
36
+ assert_invalid
37
+ end
38
+
39
+ test "each value is a string" do
40
+ @response["config"]["MYADDON_URL"] = {}
41
+ assert_invalid
42
+ end
43
+
44
+ test "asserts _URL vars are valid URIs" do
45
+ @response["config"]["MYADDON_URL"] = "abc:"
46
+ assert_invalid
47
+ end
48
+
49
+ test "asserts _URL vars have a host" do
50
+ @response["config"]["MYADDON_URL"] = "path"
51
+ assert_invalid
52
+ end
53
+
54
+ test "asserts _URL vars have a scheme" do
55
+ @response["config"]["MYADDON_URL"] = "//host/path"
56
+ assert_invalid
57
+ end
58
+
59
+ test "doesn't run URI test against other vars" do
60
+ @response["config"]['MYADDON_CONFIG'] = "abc:"
61
+ assert_valid
62
+ end
63
+
64
+ test "doesn't allow localhost URIs on production" do
65
+ @data[:env] = 'production'
66
+ @response["config"]["MYADDON_URL"] = "http://localhost/abc"
67
+ assert_invalid
68
+ end
69
+
70
+ test "asserts all vars in manifest are in response" do
71
+ @response["config"].delete('MYADDON_CONFIG')
72
+ assert_invalid
73
+ end
74
+
75
+ test "is valid otherwise" do
76
+ @response["config"]["MYADDON_URL"] = "http://localhost/abc"
77
+ assert_valid
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1 @@
1
+ exit(1) if ARGV.first == 'fail'
@@ -0,0 +1,237 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+
4
+ class Hash
5
+ def to_json
6
+ OkJson.encode(self)
7
+ end
8
+ end
9
+
10
+ class ProviderServer < Sinatra::Base
11
+ helpers do
12
+ def xplenty_only!
13
+ unless auth_xplenty?
14
+ response['WWW-Authenticate'] = %(Basic realm="Kensa Test Server")
15
+ unauthorized!(401)
16
+ end
17
+ end
18
+
19
+ def auth_xplenty?
20
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
21
+ @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ['myaddon', 'secret']
22
+ end
23
+
24
+ def unauthorized!(status=403)
25
+ throw(:halt, [status, "Not authorized\n"])
26
+ end
27
+
28
+ def make_token
29
+ Digest::SHA1.hexdigest([params[:id], 'SSO_SALT', params[:timestamp]].join(':'))
30
+ end
31
+
32
+ def json_must_include(keys)
33
+ params = OkJson.decode(request.body.read)
34
+ keys.each do |param|
35
+ raise "#{param} not included with request" unless params.keys.include? param
36
+ end
37
+ end
38
+
39
+ def login(xplenty_user=true)
40
+ @header = xplenty_user
41
+ haml <<-HAML
42
+ %html
43
+ %body
44
+ - if @header
45
+ #xplenty-header
46
+ %h1 Xplenty
47
+ %h1 Sample Addon
48
+ HAML
49
+ end
50
+ end
51
+
52
+ post '/xplenty/resources' do
53
+ xplenty_only!
54
+ { :id => 123 }.to_json
55
+ end
56
+
57
+ post '/working/xplenty/resources' do
58
+ json_must_include(%w{xplenty_id plan callback_url logplex_token options})
59
+ xplenty_only!
60
+ { :id => 123 }.to_json
61
+ end
62
+
63
+ post '/cmd-line-options/xplenty/resources' do
64
+ xplenty_only!
65
+ options = OkJson.decode(request.body.read)['options']
66
+ raise "Where are my options?" unless options['foo'] && options['bar']
67
+ { :id => 123 }.to_json
68
+ end
69
+
70
+ post '/foo/xplenty/resources' do
71
+ xplenty_only!
72
+ 'foo'
73
+ end
74
+
75
+ post '/invalid-json/xplenty/resources' do
76
+ xplenty_only!
77
+ 'invalidjson'
78
+ end
79
+
80
+ post '/invalid-response/xplenty/resources' do
81
+ xplenty_only!
82
+ 'null'
83
+ end
84
+
85
+ post '/invalid-status/xplenty/resources' do
86
+ xplenty_only!
87
+ status 422
88
+ { :id => 123 }.to_json
89
+ end
90
+
91
+ post '/invalid-missing-id/xplenty/resources' do
92
+ xplenty_only!
93
+ { :noid => 123 }.to_json
94
+ end
95
+
96
+ post '/invalid-missing-auth/xplenty/resources' do
97
+ { :id => 123 }.to_json
98
+ end
99
+
100
+
101
+ put '/working/xplenty/resources/:id' do
102
+ json_must_include(%w{xplenty_id plan})
103
+ xplenty_only!
104
+ {}.to_json
105
+ end
106
+
107
+ put '/invalid-missing-auth/xplenty/resources/:id' do
108
+ { :id => 123 }.to_json
109
+ end
110
+
111
+ put '/invalid-status/xplenty/resources/:id' do
112
+ xplenty_only!
113
+ status 422
114
+ {}.to_json
115
+ end
116
+
117
+
118
+ delete '/working/xplenty/resources/:id' do
119
+ xplenty_only!
120
+ "Ok"
121
+ end
122
+
123
+ def sso
124
+ unauthorized! unless params[:id] && params[:token]
125
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
126
+ unauthorized! unless params[:token] == make_token
127
+ response.set_cookie('xplenty-nav-data', params['nav-data'])
128
+ login
129
+ end
130
+
131
+ get '/working/xplenty/resources/:id' do
132
+ sso
133
+ end
134
+
135
+ post '/working/sso/login' do
136
+ #puts params.inspect
137
+ sso
138
+ end
139
+
140
+ def notoken
141
+ unauthorized! unless params[:id] && params[:token]
142
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
143
+ response.set_cookie('xplenty-nav-data', params['nav-data'])
144
+ login
145
+ end
146
+
147
+ get '/notoken/xplenty/resources/:id' do
148
+ notoken
149
+ end
150
+
151
+ post '/notoken/sso/login' do
152
+ notoken
153
+ end
154
+
155
+ def notimestamp
156
+ unauthorized! unless params[:id] && params[:token]
157
+ unauthorized! unless params[:token] == make_token
158
+ response.set_cookie('xplenty-nav-data', params['nav-data'])
159
+ login
160
+ end
161
+
162
+ get '/notimestamp/xplenty/resources/:id' do
163
+ notimestamp
164
+ end
165
+
166
+ post '/notimestamp/sso/login' do
167
+ notimestamp
168
+ end
169
+
170
+ def nolayout
171
+ unauthorized! unless params[:id] && params[:token]
172
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
173
+ unauthorized! unless params[:token] == make_token
174
+ response.set_cookie('xplenty-nav-data', params['nav-data'])
175
+ login(false)
176
+ end
177
+
178
+ get '/nolayout/xplenty/resources/:id' do
179
+ nolayout
180
+ end
181
+
182
+ post '/nolayout/sso/login' do
183
+ nolayout
184
+ end
185
+
186
+ def nocookie
187
+ unauthorized! unless params[:id] && params[:token]
188
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
189
+ unauthorized! unless params[:token] == make_token
190
+ login
191
+ end
192
+
193
+ get '/nocookie/xplenty/resources/:id' do
194
+ nocookie
195
+ end
196
+
197
+ post '/nocookie/sso/login' do
198
+ nocookie
199
+ end
200
+
201
+ def badcookie
202
+ unauthorized! unless params[:id] && params[:token]
203
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
204
+ unauthorized! unless params[:token] == make_token
205
+ response.set_cookie('xplenty-nav-data', 'wrong value')
206
+ login
207
+ end
208
+
209
+ get '/badcookie/xplenty/resources/:id' do
210
+ badcookie
211
+ end
212
+
213
+ post '/badcookie/sso/login' do
214
+ badcookie
215
+ end
216
+
217
+ def sso_user
218
+ head 404 unless params[:email] == 'username@example.com'
219
+ sso
220
+ end
221
+
222
+ get '/user/xplenty/resources/:id' do
223
+ sso_user
224
+ end
225
+
226
+ post '/user/sso/login' do
227
+ sso_user
228
+ end
229
+
230
+ get '/' do
231
+ unauthorized! unless session[:logged_in]
232
+ end
233
+
234
+ if $0 == __FILE__
235
+ self.run!
236
+ end
237
+ end
@@ -0,0 +1,58 @@
1
+ require 'test/helper'
2
+
3
+ class SsoCheckTest < Test::Unit::TestCase
4
+ include Xplenty::Kensa
5
+ include ProviderMock
6
+
7
+ def check ; SsoCheck ; end
8
+ %w{get post}.each do |method|
9
+ context "via #{method}" do
10
+ setup do
11
+ @data = Manifest.new(:sso => true, :method => method).
12
+ skeleton.merge :id => 123
13
+ @data['api']['sso_salt'] = 'SSO_SALT'
14
+ end
15
+
16
+ test "working sso request" do
17
+ use_provider_endpoint('working', 'sso')
18
+ assert_valid
19
+ end
20
+
21
+ test "rejects bad token" do
22
+ use_provider_endpoint("notoken", 'sso')
23
+ assert_invalid
24
+ end
25
+
26
+ test "rejects old timestamp" do
27
+ use_provider_endpoint("notimestamp", 'sso')
28
+ assert_invalid
29
+ end
30
+
31
+ test "reject omitted sso salt" do
32
+ @data['api'].delete 'sso_salt'
33
+ use_provider_endpoint("working", 'sso')
34
+ assert_invalid
35
+ end
36
+
37
+ test "reject missing xplenty layout" do
38
+ use_provider_endpoint("nolayout", 'sso')
39
+ assert_invalid
40
+ end
41
+
42
+ test "reject missing cookie" do
43
+ use_provider_endpoint("nocookie", 'sso')
44
+ assert_invalid
45
+ end
46
+
47
+ test "reject invalid cookie value" do
48
+ use_provider_endpoint("badcookie", 'sso')
49
+ assert_invalid
50
+ end
51
+
52
+ test "sends email param" do
53
+ use_provider_endpoint("user", 'sso')
54
+ assert_valid
55
+ end
56
+ end
57
+ end
58
+ end
data/test/sso_test.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'test/helper'
2
+ require 'cgi'
3
+
4
+ class SsoTest < Test::Unit::TestCase
5
+ include Xplenty::Kensa
6
+
7
+ context "with GET" do
8
+ setup do
9
+ @data = Manifest.new.skeleton.merge(:id => 1)
10
+ @data['api']['test'] = 'http://localhost:4567/'
11
+ @data['api']['sso_salt'] = 'SSO_SALT'
12
+ end
13
+
14
+ teardown { Timecop.return }
15
+
16
+ def builds_full_url(env)
17
+ url, query = @sso.full_url.split('?')
18
+ data = CGI.parse(query)
19
+
20
+
21
+ assert_equal "#{@data['api'][env]}xplenty/resources/1", url
22
+ assert_equal 'b6010f6fbb850887a396c2bc0ab23974003008f6', data['token'].first
23
+ assert_equal '1262304000', data['timestamp'].first
24
+ assert_equal 'username@example.com', data['email'].first
25
+ assert_equal 'myapp', data['app'].first
26
+ end
27
+
28
+ context 'sso' do
29
+ setup do
30
+ Timecop.freeze Time.utc(2010, 1)
31
+ @sso = Sso.new @data
32
+ end
33
+
34
+ test 'builds path' do
35
+ assert_equal '/xplenty/resources/1', @sso.path
36
+ end
37
+
38
+ test 'builds full url' do
39
+ builds_full_url('test')
40
+ end
41
+
42
+ context 'with no "sso" field specified' do
43
+ test "defaults to GET" do
44
+ assert_equal @sso.full_url, @sso.sso_url
45
+ end
46
+ end
47
+
48
+ context 'when sso method is GET' do
49
+ setup do
50
+ @data['api']['sso'] = 'GET'
51
+ @sso = Sso.new(@data).start
52
+ end
53
+
54
+ test "#sso_url should be the #full_url" do
55
+ assert_equal @sso.full_url, @sso.sso_url
56
+ end
57
+
58
+ test "#message is Opening <full_url>" do
59
+ assert_equal "Opening #{@sso.full_url}", @sso.message
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ context 'sso without salt' do
66
+ setup do
67
+ @data['api'].delete 'sso_salt'
68
+ @sso = Sso.new @data
69
+ end
70
+
71
+ test 'builds full url' do
72
+ expected = 'http://localhost:4567/xplenty/resources/1'
73
+
74
+ assert @sso.full_url.include?(expected)
75
+ end
76
+ end
77
+
78
+ context 'sso in production' do
79
+ setup do
80
+ Timecop.freeze Time.utc(2010, 1)
81
+ env = 'production'
82
+ @data[:env] = env
83
+ @data['api'][env] = 'http://localhost:7654/'
84
+
85
+ @sso = Sso.new @data
86
+ end
87
+
88
+ test 'builds full url' do
89
+ builds_full_url('production')
90
+ end
91
+ end
92
+ end
93
+
94
+ context "via POST" do
95
+ include FsMock
96
+ setup do
97
+ Timecop.freeze Time.utc(2010, 1)
98
+ @data = Manifest.new(:method => :post).skeleton
99
+ @data['api']['sso_salt'] = 'SSO_SALT'
100
+ end
101
+
102
+ test "command line" do
103
+ any_instance_of(Client) { |c| stub(c).puts }
104
+ stub(Launchy).open
105
+ start = Object.new
106
+ stub(start).message
107
+ stub(start).sso_url
108
+ stub(Sso).new.stub!.start.returns(start)
109
+
110
+ kensa "init"
111
+ kensa "sso 1234"
112
+
113
+ assert_received(Sso) { |sso| sso.new(hash_including(:id => '1234')) }
114
+ end
115
+
116
+ test "it starts the proxy server" do
117
+ @sso = Sso.new(@data.merge(:id => 1)).start
118
+ body = RestClient.get(@sso.sso_url)
119
+
120
+ assert body.include? @sso.path
121
+ assert body.include? 'b6010f6fbb850887a396c2bc0ab23974003008f6'
122
+ assert body.include? '1262304000'
123
+ assert body.include? @sso.url
124
+ assert body.include? @sso.sample_nav_data
125
+ end
126
+
127
+ context "with the proxy working" do
128
+ setup do
129
+ any_instance_of(Sso, :run_proxy => false)
130
+ @sso = Sso.new(@data).start
131
+ end
132
+
133
+ test "#sso_url should point to the proxy" do
134
+ assert_equal "http://localhost:#{@sso.proxy_port}/", @sso.sso_url
135
+ end
136
+
137
+ test "#post_url contains url and path" do
138
+ assert_equal "http://localhost:4567/sso/login", @sso.post_url
139
+ end
140
+
141
+ test "#message is Posting <data> to <post_url> via proxy on port <proxy_port>" do
142
+ assert_equal "POSTing #{@sso.query_data} to http://localhost:4567/sso/login via proxy on port #{@sso.proxy_port}", @sso.message
143
+ end
144
+ end
145
+ end
146
+ end
Binary file