castanet-testing 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba7386f0f2b4c9dcbd87159b1439b088de18c42b
4
+ data.tar.gz: 886b574886eb12ffeaa301fb9c9f88d2427437a9
5
+ SHA512:
6
+ metadata.gz: 1d325e9a92d0c3448c600a66b35ffc78c3aeb551451d355365808a68517a30c211dc2477111dc67860e3f6660c748e7fc7d21e837701cccc437ead067363547f
7
+ data.tar.gz: ecf4967bf03daff3194fa6033a67208aa9cf01f7f3d01f0aeaa80a84228e129d8ef589934d23343a6533b8ed84fceac43b371a19f425917e8e042df819a83528
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ .rbx
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sw?
19
+ foreman.log
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - jruby-19mode
4
+ - rbx-19mode
5
+ before_script:
6
+ - "bundle install"
7
+ script: ./test.sh 5000 6000
@@ -0,0 +1,4 @@
1
+ 0.0.1 (2013-09-10)
2
+ ------------------
3
+
4
+ Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in castanet-testing.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 David Yip
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ jasig: rake castanet:testing:jasig:start
2
+ callback: rake castanet:testing:callback:start
data/README ADDED
@@ -0,0 +1,50 @@
1
+ 1. castanet-testing
2
+
3
+ Castanet[1] contains Rake tasks to start the Jasig CAS Server[2] and a CAS
4
+ proxy callback in a test environment.
5
+
6
+ This gem contains an adaptation of those tasks for use in other projects.
7
+
8
+ 2. Requirements
9
+
10
+ * A Ruby environment: Ruby 1.9.3, JRuby 1.7.0, and Rubinius 2.0.0 are
11
+ regularly tested
12
+ * curl(1)
13
+ * openssl(1)
14
+ * patch(1)
15
+ * test(1)
16
+ * GNU tar
17
+
18
+ 3. Usage
19
+
20
+ In a Rakefile:
21
+
22
+ require 'castanet/testing'
23
+
24
+ Castanet::Testing::JasigServerTasks.new
25
+ Castanet::Testing::CallbackServerTasks.new
26
+
27
+ This will install the following tasks:
28
+
29
+ castanet:testing:jasig:start
30
+ castanet:testing:jasig:waitall
31
+ castanet:testing:jasig:cleanall
32
+ castanet:testing:callback:start
33
+ castanet:testing:callback:waitall
34
+ castanet:testing:callback:cleanall
35
+
36
+ The start tasks can be used on their own or as part of a Procfile. If used in
37
+ a Procfile, you may freely vary concurrency levels. (Often, however, you'll
38
+ want just one server instance.)
39
+
40
+ All *Tasks classes have optional parameters. See the class docs for more
41
+ information.
42
+
43
+ 4. License and authorship
44
+
45
+ Copyright (c) 2013 David Yip. Made available under the MIT license.
46
+
47
+ [1]: https://github.com/NUBIC/castanet
48
+ [2]: http://www.jasig.org/cas
49
+
50
+ # vim:ts=2:sw=2:et:tw=78
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "castanet/testing"
3
+
4
+ Castanet::Testing::JasigServerTasks.new
5
+ Castanet::Testing::CallbackServerTasks.new
@@ -0,0 +1,190 @@
1
+ require 'rack'
2
+ require 'webrick/https'
3
+ require 'openssl'
4
+
5
+ ##
6
+ # Rack code for handling the PGT callback part of the CAS proxy
7
+ # authentication protocol. The class itself is middleware; it can
8
+ # also generate an {.application endpoint}.
9
+ #
10
+ # ## Behavior
11
+ #
12
+ # As middleware, this class intercepts and handles two paths and
13
+ # passes all other requests down the chain. The paths are:
14
+ #
15
+ # * `/receive_pgt`: implements the PGT callback process per section
16
+ # 2.5.4 of the CAS protocol.
17
+ # * `/retrieve_pgt`: allows an application to retrieve the PGT for
18
+ # a PGTIOU. The PGTIOU is returned to the application as part of
19
+ # the CAS ticket validation process. It should be passed to
20
+ # `/receive_pgt` as the `pgtIou` query parameter. Note that a
21
+ # given PGT may only be retrieved once.
22
+ #
23
+ # As a full rack app, it handles the same two paths and returns `404
24
+ # Not Found` for all other requests.
25
+ #
26
+ # ## Middleware vs. Application
27
+ #
28
+ # It is **only** appropriate to use the class as middleware in a
29
+ # **multithreaded or multiprocessing deployment**. If your application
30
+ # only has one executor at a time, using this class as middleware
31
+ # **will cause a deadlock** during CAS authentication.
32
+ #
33
+ # ## Based on
34
+ #
35
+ # This class was heavily influenced by `CasProxyCallbackController`
36
+ # in rubycas-client. That class has approximately the same
37
+ # behavior, but is Rails-specific.
38
+ #
39
+ # @see http://www.jasig.org/cas/protocol
40
+ # CAS protocol, section 2.5.4
41
+ class RackProxyCallback
42
+ RETRIEVE_PATH = "/retrieve_pgt"
43
+ RECEIVE_PATH = "/receive_pgt"
44
+
45
+ ##
46
+ # Create a new instance of the middleware.
47
+ #
48
+ # @param [#call] app the next rack application in the chain.
49
+ # @param [Hash] options
50
+ # @option options [String] :store the file where the middleware
51
+ # will store the received PGTs until they are retrieved.
52
+ def initialize(app, options={})
53
+ @app = app
54
+ @pgts = {}
55
+ end
56
+
57
+ ##
58
+ # Handles a single request in the manner specified in the class
59
+ # overview.
60
+ #
61
+ # @param [Hash] env the rack environment for the request.
62
+ #
63
+ # @return [Array] an appropriate rack response.
64
+ def call(env)
65
+ return echo if env["PATH_INFO"] == "/"
66
+ return receive(env) if env["PATH_INFO"] == RECEIVE_PATH
67
+ return retrieve(env) if env["PATH_INFO"] == RETRIEVE_PATH
68
+ @app.call(env)
69
+ end
70
+
71
+ ##
72
+ # Creates a rack application which responds as described in the
73
+ # class overview.
74
+ #
75
+ # @param [Hash] options the same options that you can pass to
76
+ # {#initialize}.
77
+ #
78
+ # @return [#call] a full rack application
79
+ def self.application(options={})
80
+ app = lambda { |env|
81
+ [404, { "Content-Type" => "text/plain" }, ["Unknown resource #{env['PATH_INFO']}"]]
82
+ }
83
+ new(app, options)
84
+ end
85
+
86
+ protected
87
+
88
+ ##
89
+ # Associates the given PGTIOU and PGT.
90
+ #
91
+ # @param [String] pgt_iou
92
+ # @param [String] pgt
93
+ #
94
+ # @return [void]
95
+ def store_iou(pgt_iou, pgt)
96
+ @pgts[pgt_iou] = pgt
97
+ end
98
+
99
+ ##
100
+ # Finds the PGT for the given PGTIOU. If there isn't one, it
101
+ # returns nil. If there is one, it deletes it from the store
102
+ # before returning it.
103
+ #
104
+ # @param [String] pgt_iou
105
+ # @return [String,nil]
106
+ def resolve_iou(pgt_iou)
107
+ @pgts.delete(pgt_iou)
108
+ end
109
+
110
+ private
111
+
112
+ def echo
113
+ Rack::Response.new.tap do |r|
114
+ r.status = 200
115
+ end.finish
116
+ end
117
+
118
+ def receive(env)
119
+ req = Rack::Request.new(env)
120
+ resp = Rack::Response.new
121
+ resp.headers["Content-Type"] = "text/plain"
122
+
123
+ pgt = req.params["pgtId"]
124
+ pgt_iou = req.params["pgtIou"]
125
+
126
+ unless pgt && pgt_iou
127
+ missing = [("pgtId" unless pgt), ("pgtIou" unless pgt_iou)].compact
128
+ missing_msg =
129
+ if missing.size == 1
130
+ "#{missing.first} is a required query parameter."
131
+ else
132
+ "Both #{missing.join(' and ')} are required query parameters."
133
+ end
134
+ resp.status =
135
+ if missing.size == 2
136
+ #
137
+ # This oddity is required by the JA-SIG CAS Server.
138
+ #
139
+ 200
140
+ else
141
+ 400
142
+ end
143
+
144
+ resp.body = ["#{missing_msg}\nSee section 2.5.4 of the CAS protocol specification."]
145
+ else
146
+ store_iou(pgt_iou, pgt)
147
+
148
+ resp.body = ["PGT and PGTIOU received. Thanks, my robotic friend."]
149
+ end
150
+
151
+ resp.finish
152
+ end
153
+
154
+ def retrieve(env)
155
+ req = Rack::Request.new(env)
156
+ resp = Rack::Response.new
157
+ resp.headers["Content-Type"] = "text/plain"
158
+
159
+ pgt_iou = req.params["pgtIou"]
160
+
161
+ if pgt_iou
162
+ pgt = resolve_iou(pgt_iou)
163
+ if pgt
164
+ resp.body = [pgt]
165
+ else
166
+ resp.status = 404
167
+ resp.body = ["pgtIou=#{pgt_iou} does not exist. Perhaps it has already been retrieved."]
168
+ end
169
+ else
170
+ resp.status = 400
171
+ resp.body = ["pgtIou is a required query parameter."]
172
+ end
173
+
174
+ resp.finish
175
+ end
176
+ end
177
+
178
+ # ----------------------------------------------------------------------------
179
+
180
+ trap('INT') { exit! }
181
+ trap('TERM') { exit! }
182
+
183
+ # Start it up.
184
+ Rack::Handler::WEBrick.run(RackProxyCallback.application, {
185
+ :BindAddress => ENV['HOST'],
186
+ :Port => ENV['PORT'] || 9292,
187
+ :SSLEnable => true,
188
+ :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(ENV['SSL_CERT_PATH'])),
189
+ :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(ENV['SSL_KEY_PATH']))
190
+ })
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0"?>
2
+ <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
3
+ <Configure id="Server" class="org.eclipse.jetty.server.Server">
4
+ <New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
5
+ <Set name="KeyStore"><%= jetty_keystore %></Set>
6
+ <Set name="KeyStorePassword"><%= jetty_storepass %></Set>
7
+ <Set name="TrustStore"><%= jetty_keystore %></Set>
8
+ <Set name="TrustStorePassword"><%= jetty_storepass %></Set>
9
+ <Set name="IncludeCipherSuites">
10
+ <Array type="java.lang.String">
11
+ <Item>TLS_DHE_RSA_WITH_AES_128_CBC_SHA</Item>
12
+ </Array>
13
+ </Set>
14
+ </New>
15
+ <Call class="java.lang.System" name="setProperty">
16
+ <Arg>javax.net.ssl.trustStore</Arg>
17
+ <Arg><%= jetty_keystore %></Arg>
18
+ </Call>
19
+ <Call class="java.lang.System" name="setProperty">
20
+ <Arg>javax.net.ssl.trustStorePassword</Arg>
21
+ <Arg><%= jetty_storepass %></Arg>
22
+ </Call>
23
+ <Call name="addConnector">
24
+ <Arg>
25
+ <New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
26
+ <Arg><Ref id="sslContextFactory" /></Arg>
27
+ <Set name="Port">
28
+ <Property name="jetty.ssl_port" default="<%= port %>" />
29
+ </Set>
30
+ <Set name="maxIdleTime">30000</Set>
31
+ <Set name="Acceptors">2</Set>
32
+ <Set name="AcceptQueueSize">100</Set>
33
+ </New>
34
+ </Arg>
35
+ </Call>
36
+ </Configure>
@@ -0,0 +1,28 @@
1
+ --- a/jetty.xml 2012-10-24 18:06:23.000000000 -0500
2
+ +++ b/jetty.xml 2012-10-24 18:06:29.000000000 -0500
3
+ @@ -30,25 +30,6 @@
4
+ </Set>
5
+
6
+ <!-- =========================================================== -->
7
+ - <!-- Set connectors -->
8
+ - <!-- =========================================================== -->
9
+ -
10
+ - <Call name="addConnector">
11
+ - <Arg>
12
+ - <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
13
+ - <Set name="host"><Property name="jetty.host" /></Set>
14
+ - <Set name="port"><Property name="jetty.port" default="8080"/></Set>
15
+ - <Set name="maxIdleTime">300000</Set>
16
+ - <Set name="Acceptors">2</Set>
17
+ - <Set name="statsOn">false</Set>
18
+ - <Set name="confidentialPort">8443</Set>
19
+ - <Set name="lowResourcesConnections">20000</Set>
20
+ - <Set name="lowResourcesMaxIdleTime">5000</Set>
21
+ - </New>
22
+ - </Arg>
23
+ - </Call>
24
+ -
25
+ - <!-- =========================================================== -->
26
+ <!-- Set handler Collection Structure -->
27
+ <!-- =========================================================== -->
28
+ <Set name="handler">
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDGDCCAoGgAwIBAgIJAKFL1LzPfLCMMA0GCSqGSIb3DQEBBQUAMGYxCzAJBgNV
3
+ BAYTAlVTMREwDwYDVQQIEwhBbnlzdGF0ZTEQMA4GA1UEBxMHQW55Y2l0eTEeMBwG
4
+ A1UEChMVQ2FzdGFuZXQgQ2xpY2tlcnMgTExDMRIwEAYDVQQDEwlsb2NhbGhvc3Qw
5
+ IBcNMTEwMTI0MDYyNjQ0WhgPMjA2NzAyMTkwNjI2NDRaMGYxCzAJBgNVBAYTAlVT
6
+ MREwDwYDVQQIEwhBbnlzdGF0ZTEQMA4GA1UEBxMHQW55Y2l0eTEeMBwGA1UEChMV
7
+ Q2FzdGFuZXQgQ2xpY2tlcnMgTExDMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJ
8
+ KoZIhvcNAQEBBQADgY0AMIGJAoGBALGnCOPNqDcUuLpJ6+IDOq9Vfr16pOOdVZfC
9
+ 8YeeaQtii/UfeJztFzlYgnH0/Tvrld8n6EwQ8UhzXi0agJ3fGqBfEyUGs4eNOU/A
10
+ lIa3kltiPUhzxvZKAs8LqRuMamD3TY70ezPPdnkQSg2HGM2N++I8AHEBSCg6vKA0
11
+ N7UCquJ/AgMBAAGjgcswgcgwHQYDVR0OBBYEFLXPKVU6xeWwyxYPwA8/oxuNcaMV
12
+ MIGYBgNVHSMEgZAwgY2AFLXPKVU6xeWwyxYPwA8/oxuNcaMVoWqkaDBmMQswCQYD
13
+ VQQGEwJVUzERMA8GA1UECBMIQW55c3RhdGUxEDAOBgNVBAcTB0FueWNpdHkxHjAc
14
+ BgNVBAoTFUNhc3RhbmV0IENsaWNrZXJzIExMQzESMBAGA1UEAxMJbG9jYWxob3N0
15
+ ggkAoUvUvM98sIwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCIT5TQ
16
+ ZGiMkUBlDSfMcRl9vDazZnoNjKIKHl4+3ta4YsmhkAGXiFmkuKrNXd7f8UDNf6Sw
17
+ irJNGIg6x146DLe6ZXqoaBSiof0lR10A/H3dl5RvfS1y2ma2tMJJwR7prHvRHgBn
18
+ kfg5FSOWxkzANC/hYU8/9Q7ceXNkmKklzinG9w==
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXAIBAAKBgQCxpwjjzag3FLi6SeviAzqvVX69eqTjnVWXwvGHnmkLYov1H3ic
3
+ 7Rc5WIJx9P0765XfJ+hMEPFIc14tGoCd3xqgXxMlBrOHjTlPwJSGt5JbYj1Ic8b2
4
+ SgLPC6kbjGpg902O9Hszz3Z5EEoNhxjNjfviPABxAUgoOrygNDe1AqrifwIDAQAB
5
+ AoGAXYcp9/zC7dS7+F+IjyHSGJLzOcBC5Q5lDJP2Ysb0WKkWNAPQlRWBX5CIhIRN
6
+ eelqquSwuLNGxDTwxOAqDHNz6U/oNBpImap+IXJgPv+uGokCXclmbONQ57sJCC5u
7
+ 7Afznw9xMYMEI7pKwId+bfM9ehbJvaQC+PpxGTYrHhqf7AkCQQDaTC72cU7i8FFw
8
+ dNjT4MnasnptZiaitVFaJDLh/cp0wJ8B0YygFb9F2A/3qwDkCixKPa5SxFshh6Od
9
+ cHx9+56NAkEA0FXEWw7uAg25ye8waLCjsSFKodU+2KMwApw4Qp163YSY94kYk/Qm
10
+ CGVPxojv7o7Rup6G9JTYGywwhkZD7Ji4OwJBALt07GcoiguLPwQI8yGPSQeKeGN1
11
+ cvwKJB/6Mc+rNq3nsyPGpLHbuvLpRVzy9cLdkYb3TLk6cN9sMO5D6EPvTQkCQBwA
12
+ u0DmE9XQ1H0xGleoDoibifWQvT7PSH/BUcqack5eKVV0ZwpUEdylCYENHPr61XP5
13
+ JPixHQ8h9G/H+A9QQ8sCQCfMZZo6ozDUzMw59DQf4WfKuCSY4G3NGPblL2T+7Pt8
14
+ 0jzvfyJLtgShYx2SBYXa+oCwN9o3d0Y6W1Yzlm8n0dg=
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'castanet/testing/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "castanet-testing"
8
+ spec.version = Castanet::Testing::VERSION
9
+ spec.authors = ["David Yip"]
10
+ spec.email = ["yipdw@northwestern.edu"]
11
+ spec.summary = %q{Contains Rake tasks for managing CAS servers in test environments}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "foreman", "0.63.0"
22
+ spec.add_development_dependency "yard"
23
+ spec.add_dependency "json"
24
+ spec.add_dependency "rack"
25
+ spec.add_dependency "rake", "~> 10.0"
26
+ end
@@ -0,0 +1,9 @@
1
+ require 'castanet/testing/version'
2
+
3
+ module Castanet
4
+ module Testing
5
+ end
6
+ end
7
+
8
+ require 'castanet/testing/callback_server_tasks'
9
+ require 'castanet/testing/jasig_server_tasks'
@@ -0,0 +1,17 @@
1
+ require 'castanet/testing'
2
+
3
+ module Castanet::Testing
4
+ module AssetPaths
5
+ def asset_path(p)
6
+ File.expand_path("../../../../assets/#{p}", __FILE__)
7
+ end
8
+
9
+ def ssl_key_path
10
+ asset_path('test.key')
11
+ end
12
+
13
+ def ssl_cert_path
14
+ asset_path('test.crt')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,76 @@
1
+ require 'castanet/testing'
2
+ require 'castanet/testing/asset_paths'
3
+ require 'castanet/testing/common_tasks'
4
+ require 'castanet/testing/namespacing'
5
+ require 'rake'
6
+
7
+ module Castanet::Testing
8
+ class CallbackServerTasks
9
+ extend AssetPaths
10
+ include CommonTasks
11
+ include Namespacing
12
+ include Rake::DSL
13
+
14
+ DEFAULT_SCRATCH_DIR = '/tmp/castanet-testing/callback'
15
+ DEFAULT_HOST = 'localhost'
16
+ DEFAULT_SSL_KEY = ssl_key_path
17
+ DEFAULT_SSL_CERT = ssl_cert_path
18
+ DEFAULT_TIMEOUT = 120
19
+
20
+ CALLBACK_PATH = asset_path('callback/callback.rb')
21
+ RUNNER = asset_path('run.rb')
22
+
23
+ # @option opts [String] :root ('castanet:testing:callback') namespaces for
24
+ # this task; an empty string means toplevel namespace
25
+ # @option opts [String] :scratch_dir ('/tmp/castanet-testing/callback')
26
+ # sets a path for downloads, server scratch space, etc.
27
+ # @option opts [String] :host ('localhost') hostname to bind
28
+ # @option opts [String] :ssl_cert (DEFAULT_SSL_CERT) the SSL certificate
29
+ # file to use
30
+ # @option opts [String] :ssl_key (DEFAULT_SSL_KEY) the SSL key file to use
31
+ # @option opts [String] :timeout (DEFAULT_TIMEOUT) timeout for waitall
32
+ def initialize(options = {})
33
+ root = options[:root] || 'castanet:testing:callback'
34
+ scratch_dir = options[:scratch_dir] || DEFAULT_SCRATCH_DIR
35
+ host = options[:host] || DEFAULT_HOST
36
+ ssl_cert = options[:ssl_cert] || DEFAULT_SSL_CERT
37
+ ssl_key = options[:ssl_key] || DEFAULT_SSL_KEY
38
+ timeout = options[:timeout] || DEFAULT_TIMEOUT
39
+
40
+ port = ENV['PORT']
41
+ instance_dir = "#{scratch_dir}/callback.#{$$}"
42
+
43
+ namespaces(root.split(':')) do
44
+ file instance_dir do
45
+ mkdir_p instance_dir
46
+ end
47
+
48
+ task :ensure_port do
49
+ raise "PORT is not set" unless port
50
+ end
51
+
52
+ task :prep => [:ensure_port, instance_dir]
53
+
54
+ desc 'Start a CAS proxy callback instance (requires PORT to be set)'
55
+ task :start => :prep do
56
+ ENV['HOST'] = host
57
+ ENV['SSL_CERT_PATH'] = ssl_cert
58
+ ENV['SSL_KEY_PATH'] = ssl_key
59
+
60
+ cd(instance_dir) { load(CALLBACK_PATH) }
61
+ end
62
+
63
+ desc "Wait for all CAS proxy callback instances in #{scratch_dir} to become ready"
64
+ task(:waitall) { wait_all(scratch_dir, timeout) }
65
+
66
+ desc "Clean up all CAS proxy callback instances under #{scratch_dir}"
67
+ task(:cleanall) { clean_all(scratch_dir, 'callback') }
68
+
69
+ desc "Delete #{scratch_dir}"
70
+ task :delete_scratch_dir do
71
+ rm_rf scratch_dir
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ require 'castanet/testing'
2
+ require 'castanet/testing/connection_testing'
3
+ require 'json'
4
+ require 'rake'
5
+ require 'timeout'
6
+
7
+ module Castanet::Testing
8
+ module CommonTasks
9
+ include Castanet::Testing::ConnectionTesting
10
+ include Rake::DSL
11
+
12
+ def wait_all(scratch_dir, timeout)
13
+ Timeout.timeout(timeout) do
14
+ loop do
15
+ urls = Dir["#{scratch_dir}/**/.urls"].map { |x| JSON.parse(File.read(x))['status'] }
16
+ urls.reject! { |u| responding?(u) }
17
+ break true if urls.empty?
18
+ sleep 1
19
+ end
20
+ end
21
+ end
22
+
23
+ def clean_all(scratch_dir, prefix)
24
+ files = FileList["#{scratch_dir}/#{prefix}.*"]
25
+
26
+ rm_rf files unless files.empty?
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ require 'castanet/testing'
2
+ require 'logger'
3
+ require 'net/http'
4
+ require 'openssl'
5
+ require 'uri'
6
+
7
+ module Castanet::Testing
8
+ module ConnectionTesting
9
+ LOGGER = Logger.new($stderr)
10
+
11
+ def responding?(url, logger = LOGGER)
12
+ uri = URI(url)
13
+
14
+ begin
15
+ h = Net::HTTP.new(uri.host, uri.port)
16
+ h.use_ssl = (uri.scheme == 'https')
17
+ h.verify_mode = OpenSSL::SSL::VERIFY_NONE
18
+ resp = h.get(uri.request_uri)
19
+ code = resp.code.to_i
20
+
21
+ (200..399).include?(code)
22
+ rescue => e
23
+ logger.debug "#{url}: #{e.class} (#{e.message})"
24
+ false
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,190 @@
1
+ require 'castanet/testing'
2
+ require 'castanet/testing/asset_paths'
3
+ require 'castanet/testing/common_tasks'
4
+ require 'castanet/testing/namespacing'
5
+ require 'erb'
6
+ require 'json'
7
+ require 'rake'
8
+ require 'shellwords'
9
+ require 'openssl'
10
+ require 'uri'
11
+
12
+ module Castanet::Testing
13
+ class JasigServerTasks
14
+ extend AssetPaths
15
+ include CommonTasks
16
+ include Rake::DSL
17
+ include Namespacing
18
+ include Shellwords
19
+
20
+ DEFAULT_SCRATCH_DIR = '/tmp/castanet-testing/jasig'
21
+ DEFAULT_JASIG_URL = 'http://downloads.jasig.org/cas/cas-server-3.5.2-release.tar.gz'
22
+ DEFAULT_JETTY_URL = 'http://archive.eclipse.org/jetty/8.1.7.v20120910/dist/jetty-distribution-8.1.7.v20120910.tar.gz'
23
+ DEFAULT_JASIG_CHECKSUM = 'e93e05acad4975c5caa1c20dff7c57ff8e846253bbf68ea8a5fc0199ce608cba'
24
+ DEFAULT_JETTY_CHECKSUM = 'a65d20367839bf3df7d32a05992678487ba8daeebb41d9833975aee46ffe86c2'
25
+ DEFAULT_MOUNT_POINT = '/cas'
26
+ DEFAULT_HOST = 'localhost'
27
+ DEFAULT_SSL_KEY = ssl_key_path
28
+ DEFAULT_SSL_CERT = ssl_cert_path
29
+ DEFAULT_TIMEOUT = 120
30
+
31
+ JETTY_SSL_CONFIG_TEMPLATE = asset_path('jasig/jetty.xml.erb')
32
+ JETTY_CONFIG_PATCHFILE = asset_path('jasig/jetty.xml.patch')
33
+ RUNNER = asset_path('jasig/run.sh')
34
+
35
+ ##
36
+ # This is a ridiculous amount of setup for a CAS server.
37
+ #
38
+ # @option opts [String] :root ('castanet:testing:jasig') namespaces for
39
+ # this task; an empty string means toplevel namespace
40
+ # @option opts [String] :scratch_dir ('/tmp/castanet-testing/jasig') sets
41
+ # a path for downloads, server scratch space, etc.
42
+ # @option opts [String] :jasig_url (DEFAULT_JASIG_URL) the Jasig CAS Server
43
+ # package to download
44
+ # @option opts [String] :jasig_checksum (DEFAULT_JASIG_CHECKSUM) SHA256
45
+ # checksum of the CAS package
46
+ # @option opts [String] :jetty_url (DEFAULT_JETTY_URL) the Jetty
47
+ # distribution to download
48
+ # @option opts [String] :jetty_checksum (DEFAULT_JETTY_CHECKSUM) SHA256
49
+ # checksum of the Jetty package
50
+ # @option opts [String] :mount_point ('/cas') where the CAS server will be
51
+ # mounted
52
+ # @option opts [String] :host ('localhost') hostname to bind
53
+ # @option opts [String] :ssl_cert (DEFAULT_SSL_CERT) the SSL certificate
54
+ # file to use
55
+ # @option opts [String] :ssl_key (DEFAULT_SSL_KEY) the SSL key file to use
56
+ # @option opts [String] :timeout (DEFAULT_TIMEOUT) timeout for waitall
57
+ def initialize(options = {})
58
+ root = options[:root] || 'castanet:testing:jasig'
59
+ scratch_dir = options[:scratch_dir] || DEFAULT_SCRATCH_DIR
60
+ jasig_url = options[:jasig_url] || DEFAULT_JASIG_URL
61
+ jasig_checksum = options[:jasig_checksum] || DEFAULT_JASIG_CHECKSUM
62
+ jetty_url = options[:jetty_url] || DEFAULT_JETTY_URL
63
+ jetty_checksum = options[:jetty_checksum] || DEFAULT_JETTY_CHECKSUM
64
+ mount_point = options[:mount_point] || DEFAULT_MOUNT_POINT
65
+ host = options[:host] || DEFAULT_HOST
66
+ ssl_cert = options[:ssl_cert] || DEFAULT_SSL_CERT
67
+ ssl_key = options[:ssl_key] || DEFAULT_SSL_KEY
68
+ timeout = options[:timeout] || DEFAULT_TIMEOUT
69
+
70
+ instance_dir = "#{scratch_dir}/jasig.#{$$}"
71
+ port = ENV['PORT']
72
+
73
+ jasig_fn = URI.parse(jasig_url).path.split('/').last
74
+ jasig_package_dest = "#{scratch_dir}/#{jasig_fn}"
75
+ jasig_extract_dest = "#{scratch_dir}/#{jasig_fn}-extract"
76
+ jasig_war_filename = mount_point.split('/').last + '.war'
77
+
78
+ # Older versions of the Jasig CAS server used the cas-server-webapp*.war
79
+ # pattern. There's usually only one WAR in a Jasig CAS server
80
+ # distribution, though, so we just look for all of them and pick the first one.
81
+ jasig_package_name = FileList["#{jasig_extract_dest}/modules/*.war"]
82
+
83
+ jetty_fn = URI.parse(jetty_url).path.split('/').last
84
+ jetty_package_dest = "#{scratch_dir}/#{jetty_fn}"
85
+ jetty_war_filename = mount_point.split('/').last + '.war'
86
+
87
+ jetty_keystore = "#{instance_dir}/jetty.ks"
88
+ jetty_storepass = "secret"
89
+ jetty_ssl_config = "#{jetty_package_dest}/etc/jetty-cas-ssl.xml"
90
+
91
+ namespaces(root.split(':')) do
92
+ file jasig_package_dest do
93
+ mkdir_p scratch_dir
94
+ sh "curl -s #{e jasig_url} > #{e jasig_package_dest}"
95
+ verify_checksum(jasig_package_dest, jasig_checksum)
96
+ end
97
+
98
+ file jetty_package_dest do
99
+ mkdir_p scratch_dir
100
+ sh "curl -s #{e jetty_url} > #{e jetty_package_dest}"
101
+ verify_checksum(jetty_package_dest, jetty_checksum)
102
+ end
103
+
104
+ file jasig_extract_dest do
105
+ mkdir_p jasig_extract_dest
106
+ sh "tar xf #{e jasig_package_dest} -C #{e jasig_extract_dest} --strip-components 1"
107
+ end
108
+
109
+ file instance_dir do
110
+ mkdir_p instance_dir
111
+ sh "tar xf #{e jetty_package_dest} -C #{e instance_dir} --strip-components 1"
112
+ end
113
+
114
+ task :ensure_port do
115
+ raise "PORT is not set" unless port
116
+ end
117
+
118
+ desc 'Download the CAS server'
119
+ task :download => [jasig_package_dest, jasig_extract_dest, jetty_package_dest]
120
+
121
+ task :prep => [:download, :ensure_port, instance_dir] do
122
+ mkdir_p instance_dir
123
+ cp jasig_package_name.compact.first, "#{instance_dir}/webapps/#{jasig_war_filename}"
124
+
125
+ sh %Q{openssl pkcs12 -inkey #{e ssl_key} -in #{e ssl_cert} -export \
126
+ -out #{e "#{instance_dir}/jetty.pkcs12"} \
127
+ -password #{e "pass:#{jetty_storepass}"}}
128
+
129
+ sh %Q{keytool -destkeystore #{e jetty_keystore} -importkeystore \
130
+ -srckeystore #{e "#{instance_dir}/jetty.pkcs12"} \
131
+ -srcstoretype PKCS12 -srcstorepass #{e jetty_storepass} \
132
+ -storepass #{e jetty_storepass} -noprompt}
133
+
134
+ ssl_config = ERB.new(File.read(JETTY_SSL_CONFIG_TEMPLATE)).result(binding)
135
+ ssl_file = "#{instance_dir}/etc/jetty-cas-ssl.xml"
136
+
137
+ File.open(ssl_file, 'w') { |f| f.write(ssl_config) }
138
+
139
+ ini = File.read("#{instance_dir}/start.ini")
140
+
141
+ unless ini.include?(ssl_file)
142
+ File.open("#{instance_dir}/start.ini", 'a+') do |f|
143
+ f.write(ssl_file)
144
+ end
145
+ end
146
+
147
+ # Delete the default connector.
148
+ patchfile = File.expand_path('../jetty.xml.patch', __FILE__)
149
+
150
+ cd "#{instance_dir}/etc" do
151
+ sh "patch -p1 < #{e JETTY_CONFIG_PATCHFILE}"
152
+ end
153
+ end
154
+
155
+ desc 'Start a Jasig CAS Server instance (requires PORT to be set)'
156
+ task :start => :prep do
157
+ exec "cd #{e instance_dir} && exec java -jar start.jar"
158
+ end
159
+
160
+ desc "Wait for all Jasig CAS Server instances in #{scratch_dir} to become ready"
161
+ task(:waitall) { wait_all(scratch_dir, timeout) }
162
+
163
+ desc "Clean up all Jasig CAS Server instances under #{scratch_dir}"
164
+ task(:cleanall) { clean_all(scratch_dir, 'jasig') }
165
+
166
+ desc "Delete #{scratch_dir}"
167
+ task :delete_scratch_dir do
168
+ rm_rf scratch_dir
169
+ end
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ alias_method :e, :shellescape
176
+
177
+ def verify_checksum(fn, expected)
178
+ sha = OpenSSL::Digest::SHA256.new
179
+
180
+ File.open(fn, 'r') do |f|
181
+ until f.eof?
182
+ sha << f.read(65536)
183
+ end
184
+ end
185
+
186
+ actual = sha.to_s
187
+ raise "checksum mismatch: #{actual} != #{expected}" if actual != expected
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,15 @@
1
+ require 'castanet/testing'
2
+
3
+ module Castanet::Testing
4
+ module Namespacing
5
+ def namespaces(chain, &block)
6
+ if chain.empty?
7
+ block.call
8
+ else
9
+ namespace chain.shift do
10
+ namespaces(chain, &block)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Castanet
2
+ module Testing
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/test.sh ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -xe
4
+
5
+ JASIG_PORT=$1
6
+ CALLBACK_PORT=$2
7
+
8
+ if [ -z $CALLBACK_PORT ]; then
9
+ echo "Usage: $0 [CAS port] [callback port]"
10
+ exit 1
11
+ fi
12
+
13
+ bundle exec rake castanet:testing:jasig:delete_scratch_dir castanet:testing:callback:delete_scratch_dir
14
+ bundle exec rake castanet:testing:jasig:download
15
+
16
+ PORT=$JASIG_PORT bundle exec rake castanet:testing:jasig:start &
17
+ JASIG_PID=$!
18
+
19
+ PORT=$CALLBACK_PORT bundle exec rake castanet:testing:callback:start &
20
+ CALLBACK_PID=$!
21
+
22
+ sleep 10
23
+
24
+ # CAS and the callback should start.
25
+ RETVAL=bundle exec rake castanet:testing:jasig:waitall castanet:testing:callback:waitall
26
+
27
+ # Shut things down.
28
+ kill -TERM $JASIG_PID $CALLBACK_PID && wait $JASIG_PID $CALLBACK_PID
29
+ exit $RETVAL
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: castanet-testing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Yip
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: foreman
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.63.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.63.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ description:
98
+ email:
99
+ - yipdw@northwestern.edu
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .travis.yml
106
+ - CHANGELOG
107
+ - Gemfile
108
+ - LICENSE
109
+ - Procfile
110
+ - README
111
+ - Rakefile
112
+ - assets/callback/callback.rb
113
+ - assets/jasig/jetty.xml.erb
114
+ - assets/jasig/jetty.xml.patch
115
+ - assets/test.crt
116
+ - assets/test.key
117
+ - castanet-testing.gemspec
118
+ - lib/castanet/testing.rb
119
+ - lib/castanet/testing/asset_paths.rb
120
+ - lib/castanet/testing/callback_server_tasks.rb
121
+ - lib/castanet/testing/common_tasks.rb
122
+ - lib/castanet/testing/connection_testing.rb
123
+ - lib/castanet/testing/jasig_server_tasks.rb
124
+ - lib/castanet/testing/namespacing.rb
125
+ - lib/castanet/testing/version.rb
126
+ - test.sh
127
+ homepage: ''
128
+ licenses:
129
+ - MIT
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.0.6
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Contains Rake tasks for managing CAS servers in test environments
151
+ test_files: []
152
+ has_rdoc: