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