aker-cas_cli 1.0.0

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,16 @@
1
+ *.gem
2
+ *.rbc
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
+ spec/tmp
16
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm_gemset_create_on_use_flag=1; rvm gemset use aker-cas_cli
@@ -0,0 +1,5 @@
1
+ # Aker CAS CLI history
2
+
3
+ ## 1.0.0
4
+
5
+ - Initial version.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aker-cas_cli.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Rhett Sutphin
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,78 @@
1
+ Aker CAS CLI
2
+ ============
3
+
4
+ Aker CAS CLI is a library that addresses a very specific problem: you
5
+ have a ruby application which uses a service that authenticates only
6
+ with CAS proxy tickets. You need to perform offline (non-interactive)
7
+ tasks that hit this PT-protected service.
8
+
9
+ Aker CAS CLI takes a username and password, screen-scrapes its way
10
+ through an interactive CAS login, and gives you an `Aker::User` just
11
+ as if a user of your application had done an interactive CAS login.
12
+
13
+ ## Sample use
14
+
15
+ # E.g., in a rake task in an Aker-protected Rails app
16
+ task :some_job => :environment do |t|
17
+ cas_cli = Aker::CasCli.new(Aker.configuration)
18
+ username, password = get_username_and_password_from_somewhere
19
+ user = cas_cli.authenticate(username, password)
20
+ if user
21
+ run_some_job_as(user)
22
+ else
23
+ fail "Could not authenticate #{user} for #{t.name}"
24
+ end
25
+ end
26
+
27
+ ## Assumptions
28
+
29
+ * Your CAS server only requires a username and password. It doesn't
30
+ use X509 certificates, two-factor authentication, or any additional
31
+ custom fields on the login form.
32
+
33
+ ## Use
34
+
35
+ Aker CAS CLI relies on the Aker CAS authority. The CAS authority must
36
+ be configured in the Aker::Configuration you pass to Aker CAS CLI. If
37
+ you're using Aker CAS CLI for a job implemented within your
38
+ Aker-protected application, the application's Aker configuration is
39
+ probably fine. This is what's used under "sample use," above.
40
+
41
+ Otherwise, you'll first need to create an appropriate Aker
42
+ configuration. Example:
43
+
44
+ Aker.configure do
45
+ authority :cas
46
+
47
+ cas_parameters :cas_base_url => https://cas.myinst.edu/cas,
48
+ :proxy_retrieval_url => https://cas.myinst.edu/cas-callback/retrieve_pgt,
49
+ :proxy_callback_url => https://cas.myinst.edu/cas-callback/receive_pgt
50
+ end
51
+
52
+ See [Aker's documentation][aker-doc] for more information about
53
+ configuring Aker, Aker authorities, etc.
54
+
55
+ [aker-doc]: http://rubydoc.info/gems/aker/file/README.md
56
+
57
+ ## Why isn't Aker CAS CLI an Aker authority?
58
+
59
+ An Aker authority also has the form of a module/class providing a
60
+ method which takes a username and password, validates the pair, and
61
+ returns an Aker::User. However, Aker CAS CLI is _not_ intended to be
62
+ used as part of the security configuration for Aker-protected
63
+ applications. Aker provides features (multiple API modes, e.g.) which
64
+ should obviate the need to ever scrape a CAS server's interactive
65
+ login page as part of the regular operation of an Aker-protected
66
+ application. This library is for interacting with non-Aker-protected
67
+ services which have no alternatives to CAS PTs.
68
+
69
+ ## Credits
70
+
71
+ Aker CAS CLI was developed at and for the [Northwestern University
72
+ Biomedical Informatics Center][NUBIC].
73
+
74
+ [NUBIC]: http://www.nucats.northwestern.edu/centers/nubic/index.html
75
+
76
+ ### Copyright
77
+
78
+ Copyright (c) 2012 Rhett Sutphin. See LICENSE for details.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.pattern = "spec/**/*_spec.rb"
6
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/aker/cas_cli/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Rhett Sutphin"]
6
+ gem.email = ["rhett@detailedbalance.net"]
7
+ gem.summary = %q{Provides an Aker-compatible way to acquire CAS proxy tickets in a non-interactive context.}
8
+ gem.description = gem.summary + " See README.md for more information."
9
+ gem.homepage = "https://github.com/NUBIC/aker-cas_cli"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "aker-cas_cli"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Aker::CasCli::VERSION
17
+
18
+ gem.add_dependency 'aker', '~> 3.0'
19
+ gem.add_dependency 'mechanize', '~> 2.1.0'
20
+
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'rspec', '~> 2.6'
23
+ gem.add_development_dependency 'rubycas-server', '~> 1.0'
24
+ gem.add_development_dependency 'childprocess', '~> 0.2.9'
25
+ gem.add_development_dependency 'sqlite3'
26
+ gem.add_development_dependency 'thin'
27
+ end
@@ -0,0 +1,82 @@
1
+ require 'aker'
2
+ require 'mechanize'
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ module Aker
7
+ class CasCli
8
+ autoload :VERSION, 'aker/cas_cli/version'
9
+
10
+ include Aker::Cas::ConfigurationHelper
11
+
12
+ CLI_SERVICE = 'https://cas-cli.example.edu'
13
+
14
+ ##
15
+ # @return [Aker::Configuration] the Aker parameters governing this
16
+ # instance.
17
+ attr_reader :configuration
18
+
19
+ ##
20
+ # Creates a new instance.
21
+ #
22
+ # @param [Aker::Configuration] configuration the Aker
23
+ # configuration to use. This configuration should have the :cas
24
+ # authority (or an appropriate substitute) configured into its
25
+ # authority chain.
26
+ # @param [Hash] mechanize_options attribute values for the
27
+ # mechanize agent used to do the scraping. Use this, e.g., to
28
+ # specify the SSL CA to use.
29
+ def initialize(configuration, mechanize_options={})
30
+ @configuration = configuration
31
+ @agent = Mechanize.new do |a|
32
+ mechanize_options.each do |attr, value|
33
+ a.send("#{attr}=", value)
34
+ end
35
+ a.redirect_ok = false
36
+ end
37
+ end
38
+
39
+ ##
40
+ # Attempts to verify the provided credentials. Verification is
41
+ # attempted through screen-scraping the login form provided by the
42
+ # CAS server configured in {#configuration}.
43
+ #
44
+ # @return [Aker::User,nil] the authenticated user, or nil if the
45
+ # credentials are invalid.
46
+ def authenticate(username, password)
47
+ login_result = do_login(username, password)
48
+ return unless login_result
49
+
50
+ if st = extract_service_ticket_if_successful(login_result)
51
+ configuration.composite_authority.valid_credentials?(:cas, st, CLI_SERVICE)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # @return [Mechanize::Page]
58
+ def do_login(username, password)
59
+ login_page = @agent.get cas_login_url, :service => CLI_SERVICE
60
+ login_form = login_page.forms.find { |f| f.field_with(:name => 'username') }
61
+ login_form['username'] = username
62
+ login_form['password'] = password
63
+ begin
64
+ login_form.submit
65
+ rescue Mechanize::UnauthorizedError
66
+ nil
67
+ end
68
+ end
69
+
70
+ def extract_service_ticket_if_successful(result_page)
71
+ if result_page.code =~ /^3\d\d$/
72
+ location = result_page.header['Location']
73
+ return unless location && location =~ %r{^#{Regexp.escape CLI_SERVICE}}
74
+
75
+ target = URI.parse(location)
76
+ return unless target.query
77
+
78
+ CGI.parse(target.query)['ticket'].first
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,5 @@
1
+ module Aker
2
+ class CasCli
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module Aker
4
+ describe CasCli, "::VERSION" do
5
+ it "exists" do
6
+ lambda { CasCli::VERSION }.should_not raise_error
7
+ end
8
+
9
+ it "has 3 or 4 dot separated parts" do
10
+ CasCli::VERSION.split('.').size.should be_between(3, 4)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'aker'
3
+
4
+ module Aker
5
+ describe CasCli, :integrated do
6
+ let(:cas_cli) { CasCli.new(aker_config, mechanize_options) }
7
+
8
+ let(:username) { 'mr261' }
9
+ let(:correct_password) { 's3r3nity' }
10
+
11
+ let(:aker_config) {
12
+ ex = self
13
+ callback_server = spawned_rack_servers['proxy_callback']
14
+ logfile = File.join(tmpdir, 'aker.log')
15
+ Aker::Configuration.new do
16
+ authority :cas
17
+ cas_parameters :base_url => ex.cas_server.base_url,
18
+ :proxy_retrieval_url => File.join(callback_server.base_url, 'retrieve_pgt'),
19
+ :proxy_callback_url => File.join(callback_server.base_url, 'receive_pgt')
20
+ logger Logger.new(File.open(logfile, 'w'))
21
+ end
22
+ }
23
+
24
+ let(:mechanize_options) {
25
+ { :verify_mode => OpenSSL::SSL::VERIFY_NONE }
26
+ }
27
+
28
+ before do
29
+ cas_server.add_user(username, correct_password)
30
+ end
31
+
32
+ context 'when authenticating' do
33
+ it 'returns an Aker::User for valid credentials' do
34
+ cas_cli.authenticate(username, correct_password).should be_an Aker::User
35
+ end
36
+
37
+ it 'returns nil for invalid credentials' do
38
+ cas_cli.authenticate(username, 'bilbo').should be_nil
39
+ end
40
+ end
41
+
42
+ context 'an authenticated user' do
43
+ let(:user) { cas_cli.authenticate(username, correct_password) }
44
+ let(:service_url) { 'https://srvc.example.net/mail' }
45
+
46
+ it 'has the correct username' do
47
+ user.username.should == username
48
+ end
49
+
50
+ it 'can request PTs' do
51
+ lambda { user.cas_proxy_ticket(service_url) }.should_not raise_error
52
+ end
53
+
54
+ it 'receives valid PTs' do
55
+ pt = user.cas_proxy_ticket(service_url)
56
+ proxied = aker_config.composite_authority.valid_credentials?(:cas_proxy, pt, service_url)
57
+ proxied.username.should == user.username
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ require 'openssl'
2
+ # This is necessary because there doesn't seem to be a consistent way
3
+ # to specify a CA to trust across all the various uses of Net::HTTP in
4
+ # all the libraries everywhere.
5
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
@@ -0,0 +1,19 @@
1
+ # A monkeypatch for Castanet issue #7.
2
+
3
+ require 'castanet/service_ticket'
4
+
5
+ module Castanet
6
+ class ServiceTicket
7
+ def retrieve_pgt!
8
+ uri = URI.parse(proxy_retrieval_url).tap do |u|
9
+ u.query = query(['pgtIou', pgt_iou])
10
+ end
11
+
12
+ net_http(uri).start do |h|
13
+ u = uri.dup
14
+ u.scheme = u.host = u.port = nil
15
+ self.pgt = h.get(u.to_s).body
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICZzCCAdACCQCQNK6TkFeYcjANBgkqhkiG9w0BAQUFADB4MQswCQYDVQQGEwJV
3
+ UzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xIDAeBgNVBAoT
4
+ F05vcnRod2VzdGVybiBVbml2ZXJzaXR5MQ4wDAYDVQQLEwVOVUJJQzESMBAGA1UE
5
+ AxMJbG9jYWxob3N0MB4XDTEwMDUwNDAwMjQyMVoXDTIwMDUwMzAwMjQyMVoweDEL
6
+ MAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdv
7
+ MSAwHgYDVQQKExdOb3J0aHdlc3Rlcm4gVW5pdmVyc2l0eTEOMAwGA1UECxMFTlVC
8
+ SUMxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
9
+ gYEAwaBsmiJw9zsK79bzFXUkFUqMVVQrQ0DOFY8bGDsO2S52RdrLGT4185xVyYlY
10
+ YrPDMtRPvByd+jtxK9JsR8wUgU5PXSMgiww19abliZPlnhw2ZJTemnAaxxFmTSxC
11
+ 9q1WD7QUdeyjlPHIpiy6gfNUGHx1Bwegt7b8txX+V2GSOzkCAwEAATANBgkqhkiG
12
+ 9w0BAQUFAAOBgQBDWYtAHTZLCFS/CA0TE3ioQpMQDqv6UsirnE+oKFucPBbaxorF
13
+ eZ3O2UK0crd2SA33Ko7ZS3F0u6sq13BquYMaZ9cZ0lkdh/b0oLxjSWQNUVy5pFlx
14
+ dG4jRfMer6xYfm9398bmI5xtWGrng3wO2nvwdVrO0eFHwWBXmvEBlbt8ug==
15
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXgIBAAKBgQDBoGyaInD3Owrv1vMVdSQVSoxVVCtDQM4VjxsYOw7ZLnZF2ssZ
3
+ PjXznFXJiVhis8My1E+8HJ36O3Er0mxHzBSBTk9dIyCLDDX1puWJk+WeHDZklN6a
4
+ cBrHEWZNLEL2rVYPtBR17KOU8cimLLqB81QYfHUHB6C3tvy3Ff5XYZI7OQIDAQAB
5
+ AoGAbzM469R32CTahR+Hf31E+c1UhvTN29PuB0emoeXZAzXByyB6n8awqXXRdusg
6
+ DZ97rUdte3Vb7QgSWL6CXUGBTDm2GZ+4dmrCldxFp+2lFkHMQt8AfhTCJA273Kqa
7
+ u7zE/AvXzIGfBPQaoofp8G2uVqZippFhj5GpErNFlQEWGrkCQQDo6Oy+a5L93y4X
8
+ fhJ8dp1PmtG1iUYyFVkHO3dMDZAqE1Qhsk/p9I2yKJ+uN7SD2sQQV/yRoP+aojha
9
+ Kdkw7oV/AkEA1NKFhT7PXDz0z/tws+e4H+IoE/70W57UFCcXgijel5sz7Y23pqyk
10
+ 0jr51uNiJiWnXsKBLS4BMV2+aIWNHzFLRwJBALUwvzxEI84sWYcdJPR+slLDdnFr
11
+ oZhE00W1FVGtG4IgF0s/lLvE7Ja008SMwXnyLqUoTexc+3woxv4doEFYzbECQQCF
12
+ d3Ec2wMYCXJObJWFfbBO7nnL8Hw2aSj/anSnwBG4ajDqrZGbCXJkFXBRf1AyNDL+
13
+ jmSMfOlqmCutSPPzt+pJAkEA5xUkO2t7G38LCeY8HkxlJvKoendV5tJVgTu2qD3L
14
+ JESaQtvBesARboJqx1eFw1h3VWjbIuv/GqDuroqUfDbe6g==
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,4 @@
1
+ require 'aker'
2
+
3
+ run Aker::Cas::RackProxyCallback.application(
4
+ :store => '<%= File.join(tmpdir, "proxy_callback.pstore") %>')
@@ -0,0 +1,114 @@
1
+ require 'sqlite3'
2
+ require 'pathname'
3
+
4
+ require 'servers/spawned_http_server'
5
+
6
+ module Aker
7
+ module Spec
8
+ class RubycasServer < SpawnedHttpServer
9
+ module Setup
10
+ attr_accessor :cas_server
11
+ end
12
+
13
+ class << self
14
+ def run_with_rspec_tag(tag, rspec_config)
15
+ rspec_config.include Setup
16
+
17
+ rspec_config.before(:each, tag) do
18
+ self.cas_server = Aker::Spec::RubycasServer.new(
19
+ :port => 6003,
20
+ :tmpdir => tmpdir
21
+ )
22
+ self.cas_server.start
23
+ end
24
+
25
+ rspec_config.after(:each, tag) do
26
+ if self.cas_server
27
+ self.cas_server.stop
28
+ self.cas_server.clear_users
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def initialize(options={})
35
+ super({ :name => 'rubycas-server', :ssl => true }.merge(options))
36
+
37
+ init_user_db
38
+ end
39
+
40
+ def add_user(username, password)
41
+ with_user_db do |db|
42
+ db.execute("INSERT INTO users (username, password) VALUES ('#{username}', '#{password}')")
43
+ end
44
+ end
45
+
46
+ def clear_users
47
+ with_user_db do |db|
48
+ db.execute("DELETE FROM users")
49
+ end
50
+ end
51
+
52
+ def server_command
53
+ [
54
+ 'ruby',
55
+ '-r',
56
+ File.expand_path('../../disable_ssl_verify.rb', __FILE__),
57
+ '-S',
58
+ 'rubycas-server',
59
+ '-c',
60
+ config_file
61
+ ]
62
+ end
63
+
64
+ def config_file
65
+ @config_file ||= write_config_file
66
+ end
67
+
68
+ private
69
+
70
+ def users_db_file
71
+ Pathname.new File.join(tmpdir, 'rubycas_users.sqlite')
72
+ end
73
+
74
+ def cas_db_file
75
+ Pathname.new File.join(tmpdir, 'rubycas_db.sqlite')
76
+ end
77
+
78
+ def cas_log_file
79
+ Pathname.new File.join(tmpdir, 'rubycas.log')
80
+ end
81
+
82
+ def with_user_db(&block)
83
+ SQLite3::Database.new(users_db_file.to_s) do |db|
84
+ yield db
85
+ end
86
+ end
87
+
88
+ def init_user_db
89
+ with_user_db do |db|
90
+ db.execute(%q{
91
+ CREATE TABLE IF NOT EXISTS users (
92
+ username VARCHAR(50) NOT NULL, password VARCHAR(32) NOT NULL)
93
+ })
94
+ end
95
+ end
96
+
97
+ def write_config_file
98
+ File.open(config_file_name, 'w') do |f|
99
+ f.write config_file_template.result(binding)
100
+ end
101
+ config_file_name
102
+ end
103
+
104
+ def config_file_name
105
+ File.join(tmpdir, 'rubycas_server_config.yml')
106
+ end
107
+
108
+ def config_file_template
109
+ @config_file_template ||= ERB.new(
110
+ File.read(File.expand_path('../rubycas_server_config.yml.erb', __FILE__)))
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,23 @@
1
+ server: webrick
2
+ port: <%= port %>
3
+ ssl_cert: <%= ssl_cert %>
4
+ ssl_key: <%= ssl_key %>
5
+
6
+ database:
7
+ adapter: sqlite3
8
+ database: <%= cas_db_file %>
9
+
10
+ authenticator:
11
+ class: CASServer::Authenticators::SQL
12
+ database:
13
+ adapter: sqlite3
14
+ database: <%= users_db_file %>
15
+ user_table: users
16
+ username_column: username
17
+ password_column: password
18
+
19
+ default_locale: en
20
+
21
+ log:
22
+ file: <%= cas_log_file %>
23
+ level: INFO
@@ -0,0 +1,101 @@
1
+ require 'net/http'
2
+ require 'fileutils'
3
+ require 'childprocess'
4
+
5
+ require 'disable_ssl_verify'
6
+
7
+ module Aker
8
+ module Spec
9
+ class SpawnedHttpServer
10
+ include FileUtils
11
+
12
+ attr_reader :host, :port, :tmpdir, :name
13
+
14
+ def initialize(options={})
15
+ @port = options.delete(:port) or raise 'Please specify a port'
16
+ @host = options.delete(:host) || 'localhost'
17
+ @timeout = options.delete(:timeout) || 30
18
+ @tmpdir = options.delete(:tmpdir) or raise 'Please specify tmpdir'
19
+ @ssl = options.delete(:ssl) || false
20
+ @name = options.delete(:name) || "http-#{@port}"
21
+ end
22
+
23
+ # @return [Array]
24
+ def server_command
25
+ raise NoMethodError.new 'Need to implement server_command'
26
+ end
27
+
28
+ def process
29
+ @process ||= ChildProcess.build(*server_command).tap do |p|
30
+ p.io.stdout = File.open(File.join(tmpdir, "#{name}.out"), 'w')
31
+ p.io.stderr = File.open(File.join(tmpdir, "#{name}.err"), 'w')
32
+ end
33
+ end
34
+
35
+ def start
36
+ wait_for(
37
+ "port #{port} to be available",
38
+ lambda { !http_available?(base_url) },
39
+ 5)
40
+
41
+ process.start
42
+
43
+ wait_for(
44
+ "#{name} to start responding",
45
+ lambda { http_available?(base_url) },
46
+ 5)
47
+ end
48
+
49
+ def stop
50
+ process.stop
51
+ wait_for(
52
+ "the process #{name} (#{process.pid}) to stop",
53
+ lambda { !http_available?(base_url) },
54
+ @timeout)
55
+ end
56
+
57
+ def base_url
58
+ "http#{ssl? ? 's' : ''}://#{host}:#{port}/"
59
+ end
60
+
61
+ def ssl?
62
+ @ssl
63
+ end
64
+
65
+ protected
66
+
67
+ def ssl_cert
68
+ Pathname.new File.expand_path('../integrated-test-ssl.crt', __FILE__)
69
+ end
70
+
71
+ def ssl_key
72
+ Pathname.new File.expand_path('../integrated-test-ssl.key', __FILE__)
73
+ end
74
+
75
+ def http_available?(url)
76
+ url = URI.parse(url)
77
+ begin
78
+ session = Net::HTTP.new(url.host, url.port)
79
+ session.use_ssl = ssl?
80
+ session.start do |http|
81
+ status = http.get(url.request_uri).code
82
+ # anything indicating a functioning server
83
+ return status =~ /[1234]\d\d/
84
+ end
85
+ rescue => e
86
+ false
87
+ end
88
+ end
89
+
90
+ def wait_for(what, proc, timeout)
91
+ start = Time.now
92
+ until proc.call || (Time.now - start > timeout)
93
+ sleep 1
94
+ end
95
+ unless proc.call
96
+ raise "Wait for #{what} expired (took more than #{timeout} seconds)"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,93 @@
1
+ module Aker
2
+ module Spec
3
+ class SpawnedRackServer < SpawnedHttpServer
4
+ attr_reader :rackup_file_template
5
+
6
+ module Setup
7
+ def spawned_rack_servers
8
+ @spawned_rack_servers ||= {}
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def run_with_rspec_tag(tag, rspec_config, options={})
14
+ name = options[:name] || fail('Please specify a name')
15
+
16
+ rspec_config.include Setup
17
+
18
+ rspec_config.before(:each, tag) do
19
+ server = SpawnedRackServer.new(options.dup)
20
+ spawned_rack_servers[name] = server
21
+
22
+ server.start
23
+ end
24
+
25
+ rspec_config.after(:each, tag) do
26
+ spawned_rack_servers[name].stop
27
+ end
28
+ end
29
+ end
30
+
31
+ def initialize(options={})
32
+ super(options)
33
+ @rackup_file_template = options.delete(:rackup_file) or fail('Please specify a rackup file')
34
+ end
35
+
36
+ def server_command
37
+ [
38
+ 'bundle',
39
+ 'exec',
40
+ 'thin',
41
+ '--rackup', rackup_file,
42
+ '--pid', pid_file,
43
+ '--log', log_file,
44
+ '--address', host,
45
+ '--port', port,
46
+ '--require', File.expand_path('../../disable_ssl_verify.rb', __FILE__),
47
+ '--trace'
48
+ ].tap do |cmd|
49
+ if ssl?
50
+ cmd.concat([
51
+ '--ssl',
52
+ '--ssl-key-file', ssl_key.to_s,
53
+ '--ssl-cert-file', ssl_cert.to_s,
54
+ '--ssl-verify'
55
+ ])
56
+ end
57
+ cmd.concat(%w(start))
58
+ end
59
+ end
60
+
61
+ protected
62
+
63
+ def rackup_file
64
+ @rackup_file ||= create_rackup_file
65
+ end
66
+
67
+ def create_rackup_file
68
+ File.open(rackup_file_name, 'w') do |f|
69
+ f.write ERB.new(File.read(rackup_file_template)).result
70
+ end
71
+
72
+ rackup_file_name
73
+ end
74
+
75
+ def rackup_file_name
76
+ @rackup_file_name ||= tmpfile('ru')
77
+ end
78
+
79
+ def pid_file
80
+ @pid_file ||= tmpfile('pid')
81
+ end
82
+
83
+ def log_file
84
+ @log_file ||= tmpfile('log')
85
+ end
86
+
87
+ def tmpfile(ext)
88
+ File.join(tmpdir, [name, ext].join('.'))
89
+ end
90
+ end
91
+ end
92
+ end
93
+
@@ -0,0 +1,40 @@
1
+ require 'pathname'
2
+
3
+ require 'servers/rubycas_server'
4
+ require 'servers/spawned_rack_server'
5
+ require 'patch_castanet_7'
6
+
7
+ require 'aker/cas_cli'
8
+
9
+ # This file was generated by the `rspec --init` command. Conventionally, all
10
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
11
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
12
+ # loaded once.
13
+ #
14
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
15
+ RSpec.configure do |config|
16
+ config.treat_symbols_as_metadata_keys_with_true_values = true
17
+ config.run_all_when_everything_filtered = true
18
+ config.filter_run :focus
19
+
20
+ def tmpdir
21
+ @tmpdir ||= create_tmpdir
22
+ end
23
+
24
+ def create_tmpdir
25
+ Pathname.new(File.expand_path('../tmp', __FILE__)).tap { |p| p.mkpath }
26
+ end
27
+
28
+ config.after(:each) do
29
+ @tmpdir.rmtree if (@tmpdir && !ENV['KEEP_TMPDIR'])
30
+ end
31
+
32
+ Aker::Spec::RubycasServer.run_with_rspec_tag(:integrated, config)
33
+ Aker::Spec::SpawnedRackServer.run_with_rspec_tag(:integrated, config,
34
+ :name => 'proxy_callback',
35
+ :rackup_file => File.expand_path('../servers/proxy_callback.ru.erb', __FILE__),
36
+ :ssl => true,
37
+ :port => '6012',
38
+ :tmpdir => tmpdir
39
+ );
40
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aker-cas_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rhett Sutphin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aker
16
+ requirement: &70174410929520 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70174410929520
25
+ - !ruby/object:Gem::Dependency
26
+ name: mechanize
27
+ requirement: &70174410928960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.1.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70174410928960
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70174410926280 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70174410926280
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70174410902700 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70174410902700
58
+ - !ruby/object:Gem::Dependency
59
+ name: rubycas-server
60
+ requirement: &70174410901580 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '1.0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70174410901580
69
+ - !ruby/object:Gem::Dependency
70
+ name: childprocess
71
+ requirement: &70174410900540 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.2.9
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70174410900540
80
+ - !ruby/object:Gem::Dependency
81
+ name: sqlite3
82
+ requirement: &70174410899720 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70174410899720
91
+ - !ruby/object:Gem::Dependency
92
+ name: thin
93
+ requirement: &70174410898640 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70174410898640
102
+ description: Provides an Aker-compatible way to acquire CAS proxy tickets in a non-interactive
103
+ context. See README.md for more information.
104
+ email:
105
+ - rhett@detailedbalance.net
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - .gitignore
111
+ - .rspec
112
+ - .rvmrc
113
+ - CHANGELOG.md
114
+ - Gemfile
115
+ - LICENSE
116
+ - README.md
117
+ - Rakefile
118
+ - aker-cas_cli.gemspec
119
+ - lib/aker/cas_cli.rb
120
+ - lib/aker/cas_cli/version.rb
121
+ - spec/aker/cas_cli/version_spec.rb
122
+ - spec/aker/cas_cli_spec.rb
123
+ - spec/disable_ssl_verify.rb
124
+ - spec/patch_castanet_7.rb
125
+ - spec/servers/integrated-test-ssl.crt
126
+ - spec/servers/integrated-test-ssl.key
127
+ - spec/servers/proxy_callback.ru.erb
128
+ - spec/servers/rubycas_server.rb
129
+ - spec/servers/rubycas_server_config.yml.erb
130
+ - spec/servers/spawned_http_server.rb
131
+ - spec/servers/spawned_rack_server.rb
132
+ - spec/spec_helper.rb
133
+ homepage: https://github.com/NUBIC/aker-cas_cli
134
+ licenses: []
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ segments:
146
+ - 0
147
+ hash: 302306881262572391
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ segments:
155
+ - 0
156
+ hash: 302306881262572391
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 1.8.15
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: Provides an Aker-compatible way to acquire CAS proxy tickets in a non-interactive
163
+ context.
164
+ test_files:
165
+ - spec/aker/cas_cli/version_spec.rb
166
+ - spec/aker/cas_cli_spec.rb
167
+ - spec/disable_ssl_verify.rb
168
+ - spec/patch_castanet_7.rb
169
+ - spec/servers/integrated-test-ssl.crt
170
+ - spec/servers/integrated-test-ssl.key
171
+ - spec/servers/proxy_callback.ru.erb
172
+ - spec/servers/rubycas_server.rb
173
+ - spec/servers/rubycas_server_config.yml.erb
174
+ - spec/servers/spawned_http_server.rb
175
+ - spec/servers/spawned_rack_server.rb
176
+ - spec/spec_helper.rb