castanet-testing 0.0.1

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.
@@ -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: