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.
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +6 -0
- data/aker-cas_cli.gemspec +27 -0
- data/lib/aker/cas_cli.rb +82 -0
- data/lib/aker/cas_cli/version.rb +5 -0
- data/spec/aker/cas_cli/version_spec.rb +13 -0
- data/spec/aker/cas_cli_spec.rb +61 -0
- data/spec/disable_ssl_verify.rb +5 -0
- data/spec/patch_castanet_7.rb +19 -0
- data/spec/servers/integrated-test-ssl.crt +15 -0
- data/spec/servers/integrated-test-ssl.key +15 -0
- data/spec/servers/proxy_callback.ru.erb +4 -0
- data/spec/servers/rubycas_server.rb +114 -0
- data/spec/servers/rubycas_server_config.yml.erb +23 -0
- data/spec/servers/spawned_http_server.rb +101 -0
- data/spec/servers/spawned_rack_server.rb +93 -0
- data/spec/spec_helper.rb +40 -0
- metadata +176 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm_gemset_create_on_use_flag=1; rvm gemset use aker-cas_cli
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
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.
|
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/aker/cas_cli.rb
ADDED
@@ -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,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,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
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -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
|