diff options
Diffstat (limited to 'vendor/gems/omniauth-cas3/spec')
7 files changed, 479 insertions, 0 deletions
diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml new file mode 100644 index 00000000000..f8238a18014 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml @@ -0,0 +1,4 @@ +<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> + <cas:authenticationFailure> + </cas:authenticationFailure> +</cas:serviceResponse> diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml new file mode 100644 index 00000000000..18904f64b35 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml @@ -0,0 +1,14 @@ +<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> + <cas:authenticationSuccess> + <cas:user>psegel</cas:user> + <cas:employeeid>54</cas:employeeid> + <cas:first_name>P. Segel</cas:first_name> + <cas:first_name>Peter</cas:first_name> + <cas:last_name>Segel</cas:last_name> + <cas:email>psegel@intridea.com</cas:email> + <cas:location>Washington, D.C.</cas:location> + <cas:image>/images/user.jpg</cas:image> + <cas:phone>555-555-5555</cas:phone> + <cas:hire_date>2004-07-13</cas:hire_date> + </cas:authenticationSuccess> +</cas:serviceResponse> diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml new file mode 100644 index 00000000000..72f58edfb46 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml @@ -0,0 +1,16 @@ +<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> + <cas:authenticationSuccess> + <cas:user>psegel</cas:user> + <cas:attributes> + <cas:employeeid>54</cas:employeeid> + <cas:first_name>P. Segel</cas:first_name> + <cas:first_name>Peter</cas:first_name> + <cas:last_name>Segel</cas:last_name> + <cas:email>psegel@intridea.com</cas:email> + <cas:location>Washington, D.C.</cas:location> + <cas:image>/images/user.jpg</cas:image> + <cas:phone>555-555-5555</cas:phone> + <cas:hire_date>2004-07-13</cas:hire_date> + </cas:attributes> + </cas:authenticationSuccess> +</cas:serviceResponse> diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb new file mode 100644 index 00000000000..4834347fa03 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe OmniAuth::Strategies::CAS3::LogoutRequest do + let(:strategy) { double('strategy') } + let(:env) do + { 'rack.input' => StringIO.new('','r') } + end + let(:request) { double('request', params:params, env:env) } + let(:params) { { 'url' => url, 'logoutRequest' => logoutRequest } } + let(:url) { 'http://notes.dev/signed_in' } + let(:logoutRequest) do + %Q[ + <samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion\" ID="123abc-1234-ab12-cd34-1234abcd" Version="2.0" IssueInstant="#{Time.now.to_s}"> + <saml:NameID>@NOT_USED@</saml:NameID> + <samlp:SessionIndex>ST-123456-123abc456def</samlp:SessionIndex> + </samlp:LogoutRequest> + ] + end + + subject { described_class.new(strategy, request).call(options) } + + describe 'SAML attributes' do + let(:callback) { Proc.new{} } + let(:options) do + { on_single_sign_out: callback } + end + + before do + @rack_input = nil + allow(callback).to receive(:call) do |req| + @rack_input = req.env['rack.input'].read + true + end + end + + it 'are parsed and injected into the Rack Request parameters', :skip => true do + subject + expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-123456-123abc456def' + end + + it 'are parsed and injected even if saml defined inside NameID', :skip => true do + request.params['logoutRequest'] = + %Q[ + <samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="foobarbaz" Version="2.0" IssueInstant="2014-10-19T17:13:50Z"> + <saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">@NOT_USED@</saml:NameID> + <samlp:SessionIndex>ST-foo-bar</samlp:SessionIndex> + </samlp:LogoutRequest> + ] + subject + expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-foo-bar' + end + + it 'are parsed and injected even if saml and samlp namespaces not defined', :skip => true do + request.params['logoutRequest'] = + %Q[ + <samlp:LogoutRequest ID="123abc-1234-ab12-cd34-1234abcd" Version="2.0" IssueInstant="#{Time.now.to_s}"> + <saml:NameID>@NOT_USED@</saml:NameID> + <samlp:SessionIndex>ST-789000-456def789ghi</samlp:SessionIndex> + </samlp:LogoutRequest> + ] + subject + expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-789000-456def789ghi' + end + + context 'that raise when parsed' do + let(:env) { { 'rack.input' => nil } } + + before do + allow(strategy).to receive(:fail!) + subject + expect(strategy).to have_received(:fail!) + end + + it 'responds with an error', skip: true do + expect(strategy).to have_received(:fail!) + end + end + end + + describe 'with a configured callback' do + let(:options) do + { on_single_sign_out: callback } + end + + context 'that returns TRUE' do + let(:callback) { Proc.new{true} } + + it 'responds with OK', skip: true do + expect(subject[0]).to eq 200 + expect(subject[2].body).to eq ['OK'] + end + end + + context 'that returns Nil' do + let(:callback) { Proc.new{} } + + it 'responds with OK', skip: true do + expect(subject[0]).to eq 200 + expect(subject[2].body).to eq ['OK'] + end + end + + context 'that returns a tuple' do + let(:callback) { Proc.new{ [400,{},'Bad Request'] } } + + it 'responds with OK', skip: true do + expect(subject[0]).to eq 400 + expect(subject[2].body).to eq ['Bad Request'] + end + end + + context 'that raises an error' do + let(:exception) { RuntimeError.new('error' )} + let(:callback) { Proc.new{raise exception} } + + before do + allow(strategy).to receive(:fail!) + subject + end + + it 'responds with an error', skip: true do + expect(strategy).to have_received(:fail!) + .with(:logout_request, exception) + end + end + end +end diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb new file mode 100644 index 00000000000..b031d1d68fc --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe OmniAuth::Strategies::CAS3::ServiceTicketValidator do + let(:strategy) do + double('strategy', + service_validate_url: 'https://example.org/serviceValidate' + ) + end + let(:provider_options) do + double('provider_options', + disable_ssl_verification?: false, + ca_path: '/etc/ssl/certsZOMG' + ) + end + let(:validator) do + OmniAuth::Strategies::CAS3::ServiceTicketValidator.new( strategy, provider_options, '/foo', nil ) + end + + describe '#call' do + before do + stub_request(:get, 'https://example.org/serviceValidate?') + .to_return(status: 200, body: '') + end + + subject { validator.call } + + it 'returns itself' do + expect(subject).to eq validator + end + + it 'uses the configured CA path' do + subject + expect(provider_options).to have_received :ca_path + end + end + + describe '#user_info' do + let(:ok_fixture) do + File.expand_path(File.join(File.dirname(__FILE__), '../../../fixtures/cas_success.xml')) + end + let(:service_response) { File.read(ok_fixture) } + + before do + stub_request(:get, 'https://example.org/serviceValidate?') + .to_return(status: 200, body:service_response) + validator.call + end + + subject { validator.user_info } + + it 'parses user info from the response' do + expect(subject).to include 'user' => 'psegel' + end + end +end diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb new file mode 100644 index 00000000000..fd61fc79580 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb @@ -0,0 +1,250 @@ +require 'spec_helper' + +describe OmniAuth::Strategies::CAS3, type: :strategy do + include Rack::Test::Methods + + let(:my_cas_provider) { Class.new(OmniAuth::Strategies::CAS3) } + before do + stub_const 'MyCasProvider', my_cas_provider + end + let(:app) do + Rack::Builder.new { + use OmniAuth::Test::PhonySession + use MyCasProvider, name: :cas3, host: 'cas.example.org', ssl: false, port: 8080, uid_field: :employeeid + run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] } + }.to_app + end + + # TODO: Verify that these are even useful tests + shared_examples_for 'a CAS redirect response' do + let(:redirect_params) { 'service=' + Rack::Utils.escape("http://example.org/auth/cas3/callback?url=#{Rack::Utils.escape(return_url)}") } + + before { get url, nil, request_env } + + subject { last_response } + + it { should be_redirect } + + it 'redirects to the CAS server' do + expect(subject.headers).to include 'Location' => "http://cas.example.org:8080/login?#{redirect_params}" + end + end + + describe '#cas_url' do + let(:params) { Hash.new } + let(:provider) { MyCasProvider.new(nil, params) } + + subject { provider.cas_url } + + it 'raises an ArgumentError' do + expect{subject}.to raise_error ArgumentError, %r{:host and :login_url MUST be provided} + end + + context 'with an explicit :url option' do + let(:url) { 'https://example.org:8080/my_cas' } + let(:params) { super().merge url:url } + + before { subject } + + it { should eq url } + + it 'parses the URL into it the appropriate strategy options' do + expect(provider.options).to include ssl:true + expect(provider.options).to include host:'example.org' + expect(provider.options).to include port:8080 + expect(provider.options).to include path:'/my_cas' + end + end + + context 'with explicit URL component' do + let(:params) { super().merge host:'example.org', port:1234, ssl:true, path:'/a/path' } + + before { subject } + + it { should eq 'https://example.org:1234/a/path' } + + it 'parses the URL into it the appropriate strategy options' do + expect(provider.options).to include ssl:true + expect(provider.options).to include host:'example.org' + expect(provider.options).to include port:1234 + expect(provider.options).to include path:'/a/path' + end + end + end + + describe 'defaults' do + subject { MyCasProvider.default_options.to_hash } + + it { should include('ssl' => true) } + end + + describe 'GET /auth/cas3' do + let(:return_url) { 'http://myapp.com/admin/foo' } + + context 'with a referer' do + let(:url) { '/auth/cas3' } + + let(:request_env) { { 'HTTP_REFERER' => return_url } } + + it_behaves_like 'a CAS redirect response' + end + + context 'with an explicit return URL' do + let(:url) { "/auth/cas3?url=#{return_url}" } + + let(:request_env) { {} } + + it_behaves_like 'a CAS redirect response' + end + end + + describe 'GET /auth/cas3/callback' do + context 'without a ticket' do + before { get '/auth/cas3/callback' } + + subject { last_response } + + it { should be_redirect } + + it 'redirects with a failure message' do + expect(subject.headers).to include 'Location' => '/auth/failure?message=no_ticket&strategy=cas3' + end + end + + context 'with an invalid ticket' do + before do + stub_request(:get, /^http:\/\/cas.example.org:8080?\/p3\/serviceValidate\?([^&]+&)?ticket=9391d/). + to_return( body: File.read('spec/fixtures/cas_failure.xml') ) + get '/auth/cas3/callback?ticket=9391d' + end + + subject { last_response } + + it { should be_redirect } + + it 'redirects with a failure message' do + expect(subject.headers).to include 'Location' => '/auth/failure?message=invalid_ticket&strategy=cas3' + end + end + + describe 'with a valid ticket' do + shared_examples :successful_validation do + before do + stub_request(:get, /^http:\/\/cas.example.org:8080?\/p3\/serviceValidate\?([^&]+&)?ticket=593af/) + .with { |request| @request_uri = request.uri.to_s } + .to_return( body: File.read("spec/fixtures/#{xml_file_name}") ) + + get "/auth/cas3/callback?ticket=593af&url=#{return_url}" + end + + it 'strips the ticket parameter from the callback URL' do + expect(@request_uri.scan('ticket=').size).to eq 1 + end + + it 'properly encodes the service URL' do + expect(WebMock).to have_requested(:get, 'http://cas.example.org:8080/p3/serviceValidate') + .with(query: { + ticket: '593af', + service: 'http://example.org/auth/cas3/callback?url=' + Rack::Utils.escape('http://127.0.0.10/?some=parameter') + }) + end + + context "request.env['omniauth.auth']" do + subject { last_request.env['omniauth.auth'] } + + it { should be_kind_of Hash } + + it 'identifes the provider' do + expect(subject.provider).to eq :cas3 + end + + it 'returns the UID of the user' do + expect(subject.uid).to eq '54' + end + + context 'the info hash' do + subject { last_request.env['omniauth.auth']['info'] } + + it 'includes user info attributes' do + expect(subject.name).to eq 'Peter Segel' + expect(subject.first_name).to eq 'Peter' + expect(subject.last_name).to eq 'Segel' + expect(subject.nickname).to eq 'psegel' + expect(subject.email).to eq 'psegel@intridea.com' + expect(subject.location).to eq 'Washington, D.C.' + expect(subject.image).to eq '/images/user.jpg' + expect(subject.phone).to eq '555-555-5555' + end + end + + context 'the extra hash' do + subject { last_request.env['omniauth.auth']['extra'] } + + it 'includes additional user attributes' do + expect(subject.user).to eq 'psegel' + expect(subject.employeeid).to eq '54' + expect(subject.hire_date).to eq '2004-07-13' + end + end + + context 'the credentials hash' do + subject { last_request.env['omniauth.auth']['credentials'] } + + it 'has a ticket value' do + expect(subject.ticket).to eq '593af' + end + end + end + + it 'calls through to the master app' do + expect(last_response.body).to eq 'true' + end + end + + let(:return_url) { 'http://127.0.0.10/?some=parameter' } + + context 'with JASIG flavored XML' do + let(:xml_file_name) { 'cas_success_jasig.xml' } + + it_behaves_like :successful_validation + end + + context 'with classic XML' do + let(:xml_file_name) { 'cas_success.xml' } + + it_behaves_like :successful_validation + end + end + end + + describe 'POST /auth/cas3/callback' do + describe 'with a Single Sign-Out logoutRequest' do + let(:logoutRequest) do + %Q[ + <samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion\" ID="123abc-1234-ab12-cd34-1234abcd" Version="2.0" IssueInstant="#{Time.now.to_s}"> + <saml:NameID>@NOT_USED@</saml:NameID> + <samlp:SessionIndex>ST-123456-123abc456def</samlp:SessionIndex> + </samlp:LogoutRequest> + ] + end + + let(:logout_request) { double('logout_request', call:[200,{},'OK']) } + + subject do + post 'auth/cas3/callback', logoutRequest:logoutRequest + end + + before do + allow_any_instance_of(MyCasProvider) + .to receive(:logout_request_service) + .and_return double('LogoutRequest', new:logout_request) + + subject + end + + it 'initializes a LogoutRequest' do + expect(logout_request).to have_received :call + end + end + end +end diff --git a/vendor/gems/omniauth-cas3/spec/spec_helper.rb b/vendor/gems/omniauth-cas3/spec/spec_helper.rb new file mode 100644 index 00000000000..75231268ff3 --- /dev/null +++ b/vendor/gems/omniauth-cas3/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'bundler/setup' +require 'awesome_print' + +RSpec.configure do |c| + c.filter_run focus: true + c.run_all_when_everything_filtered = true +end + +require 'rack/test' +require 'webmock/rspec' +require 'omniauth-cas3' + +OmniAuth.config.logger = Logger.new( '/dev/null' ) |