aker-cas_cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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