omniauth-cas3-1.1.3/0000755000175600017570000000000012645767255013253 5ustar pravipraviomniauth-cas3-1.1.3/spec/0000755000175600017570000000000012645767255014205 5ustar pravipraviomniauth-cas3-1.1.3/spec/fixtures/0000755000175600017570000000000012645767255016056 5ustar pravipraviomniauth-cas3-1.1.3/spec/fixtures/cas_failure.xml0000644000175600017570000000022112645767255021050 0ustar pravipravi omniauth-cas3-1.1.3/spec/fixtures/cas_success.xml0000644000175600017570000000107612645767255021102 0ustar pravipravi psegel 54 P. Segel Peter Segel psegel@intridea.com Washington, D.C. /images/user.jpg 555-555-5555 2004-07-13 omniauth-cas3-1.1.3/spec/fixtures/cas_success_jasig.xml0000644000175600017570000000117312645767255022255 0ustar pravipravi psegel 54 P. Segel Peter Segel psegel@intridea.com Washington, D.C. /images/user.jpg 555-555-5555 2004-07-13 omniauth-cas3-1.1.3/spec/spec_helper.rb0000644000175600017570000000041412645767255017022 0ustar pravipravirequire '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' ) omniauth-cas3-1.1.3/spec/omniauth/0000755000175600017570000000000012645767255016031 5ustar pravipraviomniauth-cas3-1.1.3/spec/omniauth/strategies/0000755000175600017570000000000012645767255020203 5ustar pravipraviomniauth-cas3-1.1.3/spec/omniauth/strategies/cas3_spec.rb0000644000175600017570000001744012645767255022401 0ustar pravipravirequire '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[ @NOT_USED@ ST-123456-123abc456def ] 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 omniauth-cas3-1.1.3/spec/omniauth/strategies/cas3/0000755000175600017570000000000012645767255021034 5ustar pravipraviomniauth-cas3-1.1.3/spec/omniauth/strategies/cas3/logout_request_spec.rb0000644000175600017570000000767312645767255025471 0ustar pravipravirequire '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[ @NOT_USED@ ST-123456-123abc456def ] 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[ @NOT_USED@ ST-foo-bar ] 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[ @NOT_USED@ ST-789000-456def789ghi ] 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 omniauth-cas3-1.1.3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb0000644000175600017570000000256212645767255027450 0ustar pravipravirequire '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 omniauth-cas3-1.1.3/LICENSE0000644000175600017570000000213212645767255014256 0ustar pravipraviCopyright (c) 2011 Derek Lindahl and CustomInk, LLC Copyright (c) 2015 tduehr MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.omniauth-cas3-1.1.3/.editorconfig0000644000175600017570000000056312645767255015734 0ustar pravipravi# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 2 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = trueomniauth-cas3-1.1.3/.travis.yml0000644000175600017570000000020112645767255015355 0ustar pravipravirvm: - 1.9.3 - 2.0.0 - 2.1 - 2.2 branches: only: - master - /.*-test$/ before_install: - gem install bundler omniauth-cas3-1.1.3/omniauth-cas3.gemspec0000644000175600017570000000220612645767255017273 0ustar pravipravi# -*- encoding: utf-8 -*- require File.expand_path('../lib/omniauth/cas3/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Derek Lindahl, tduehr"] gem.email = ["td@matasano.com"] gem.summary = %q{CAS 3.0 Strategy for OmniAuth} gem.description = gem.summary gem.homepage = "https://github.com/tduehr/omniauth-cas3" gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.name = "omniauth-cas3" gem.require_paths = ["lib"] gem.version = Omniauth::Cas3::VERSION gem.add_dependency 'omniauth', '~> 1.2' gem.add_dependency 'nokogiri', '~> 1.6.6' gem.add_dependency 'addressable', '~> 2.3' gem.add_development_dependency 'rake', '~> 10.0' gem.add_development_dependency 'webmock', '~> 1.19.0' gem.add_development_dependency 'rspec', '~> 3.1.0' gem.add_development_dependency 'rack-test', '~> 0.6' gem.add_development_dependency 'awesome_print' end omniauth-cas3-1.1.3/README.md0000644000175600017570000001213412645767255014533 0ustar pravipravi# OmniAuth CAS Strategy [![Gem Version][version_badge]][version] [![Build Status][travis_status]][travis] [version_badge]: https://badge.fury.io/rb/omniauth-cas3.png [version]: http://badge.fury.io/rb/omniauth-cas3 [travis]: http://travis-ci.org/tduehr/omniauth-cas3 [travis_status]: https://secure.travis-ci.org/dlindahl/omniauth-cas3.png [releases]: https://github.com/tduehr/omniauth-cas3/releases This is a OmniAuth 1.0 compatible port of the previously available [OmniAuth CAS strategy][old_omniauth_cas] that was bundled with OmniAuth 0.3. This strategy haas also been updated for CAS protocol version 3.0 and patched to deal with namespace issues. * [View the documentation][document_up] * [Changelog][releases] ## Installation Add this line to your application's Gemfile: gem 'omniauth-cas3' And then execute: $ bundle Or install it yourself as: $ gem install omniauth-cas3 ## Usage Use like any other OmniAuth strategy: ```ruby Rails.application.config.middleware.use OmniAuth::Builder do provider :cas3, host: 'cas.yourdomain.com' end ``` ### Configuration Options #### Required OmniAuth CAS requires at least one of the following two configuration options: * `url` - Defines the URL of your CAS server (i.e. `http://example.org:8080`) * `host` - Defines the host of your CAS server (i.e. `example.org`). #### Optional Other configuration options: * `port` - The port to use for your configured CAS `host`. Optional if using `url`. * `ssl` - TRUE to connect to your CAS server over SSL. Optional if using `url`. * `service_validate_url` - The URL to use to validate a user. Defaults to `'/serviceValidate'`. * `callback_url` - The URL custom URL path which CAS uses to call back to the service. Defaults to `/users/auth/cas3/callback`. * `logout_url` - The URL to use to logout a user. Defaults to `'/logout'`. * `login_url` - Defines the URL used to prompt users for their login information. Defaults to `/login` If no `host` is configured, the host application's domain will be used. * `uid_field` - The user data attribute to use as your user's unique identifier. Defaults to `'user'` (which usually contains the user's login name). * `ca_path` - Optional when `ssl` is `true`. Sets path of a CA certification directory. See [Net::HTTP][net_http] for more details. * `disable_ssl_verification` - Optional when `ssl` is true. Disables verification. * `on_single_sign_out` - Optional. Callback used when a [CAS 3.1 Single Sign Out][sso] request is received. * `fetch_raw_info` - Optional. Callback used to return additional "raw" user info from other sources. ```ruby provider :cas3, fetch_raw_info: lambda { |strategy, options, ticket, user_info| ExternalService.get(user_info[:user]).attributes } ``` Configurable options for values returned by CAS: * `uid_key` - The user ID data attribute to use as your user's unique identifier. Defaults to `'user'` (which usually contains the user's login name). * `name_key` - The data attribute containing user first and last name. Defaults to `'name'`. * `email_key` - The data attribute containing user email address. Defaults to `'email'`. * `nickname_key` - The data attribute containing user's nickname. Defaults to `'user'`. * `first_name_key` - The data attribute containing user first name. Defaults to `'first_name'`. * `last_name_key` - The data attribute containing user last name. Defaults to `'last_name'`. * `location_key` - The data attribute containing user location/address. Defaults to `'location'`. * `image_key` - The data attribute containing user image/picture. Defaults to `'image'`. * `phone_key` - The data attribute containing user contact phone number. Defaults to `'phone'`. ## Migrating from OmniAuth 0.3 Given the following OmniAuth 0.3 configuration: ```ruby provider :CAS, cas_server: 'https://cas.example.com/cas/' ``` Your new settings should look similar to this: ```ruby provider :cas3, host: 'cas.example.com', login_url: '/cas/login', service_validate_url: '/cas/p3/serviceValidate' ``` If you encounter problems wih SSL certificates you may want to set the `ca_path` parameter or activate `disable_ssl_verification` (not recommended). ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## Thanks Special thanks go out to the following people * @dlindahl For the original work in porting this from OmniAuth 0.3 * Phillip Aldridge (@iterateNZ) and JB Barth (@jbbarth) for helping out with Issue #3 * Elber Ribeiro (@dynaum) for Ubuntu SSL configuration support * @rbq for README updates and OmniAuth 0.3 migration guide [old_omniauth_cas]: https://github.com/intridea/omniauth/blob/0-3-stable/oa-enterprise/lib/omniauth/strategies/cas.rb [document_up]: http://tduehr.github.com/omniauth-cas3/ [net_http]: http://ruby-doc.org/stdlib-1.9.3/libdoc/net/http/rdoc/Net/HTTP.html [sso]: https://wiki.jasig.org/display/CASUM/Single+Sign+Out omniauth-cas3-1.1.3/Gemfile0000644000175600017570000000014212645767255014543 0ustar pravipravisource 'https://rubygems.org' # Specify your gem's dependencies in omniauth-cas3.gemspec gemspec omniauth-cas3-1.1.3/metadata.yml0000644000175600017570000001121112645767255015552 0ustar pravipravi--- !ruby/object:Gem::Specification name: omniauth-cas3 version: !ruby/object:Gem::Version version: 1.1.3 platform: ruby authors: - Derek Lindahl, tduehr autorequire: bindir: bin cert_chain: [] date: 2015-10-28 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: omniauth requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' - !ruby/object:Gem::Dependency name: nokogiri requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.6.6 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.6.6 - !ruby/object:Gem::Dependency name: addressable requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.3' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.3' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '10.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '10.0' - !ruby/object:Gem::Dependency name: webmock requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.19.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.19.0 - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.1.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.1.0 - !ruby/object:Gem::Dependency name: rack-test requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.6' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.6' - !ruby/object:Gem::Dependency name: awesome_print requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' description: CAS 3.0 Strategy for OmniAuth email: - td@matasano.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".editorconfig" - ".gitignore" - ".travis.yml" - Gemfile - LICENSE - README.md - Rakefile - lib/omniauth-cas3.rb - lib/omniauth/cas3.rb - lib/omniauth/cas3/version.rb - lib/omniauth/strategies/cas3.rb - lib/omniauth/strategies/cas3/logout_request.rb - lib/omniauth/strategies/cas3/service_ticket_validator.rb - omniauth-cas3.gemspec - spec/fixtures/cas_failure.xml - spec/fixtures/cas_success.xml - spec/fixtures/cas_success_jasig.xml - spec/omniauth/strategies/cas3/logout_request_spec.rb - spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb - spec/omniauth/strategies/cas3_spec.rb - spec/spec_helper.rb homepage: https://github.com/tduehr/omniauth-cas3 licenses: [] metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.8 signing_key: specification_version: 4 summary: CAS 3.0 Strategy for OmniAuth test_files: - spec/fixtures/cas_failure.xml - spec/fixtures/cas_success.xml - spec/fixtures/cas_success_jasig.xml - spec/omniauth/strategies/cas3/logout_request_spec.rb - spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb - spec/omniauth/strategies/cas3_spec.rb - spec/spec_helper.rb has_rdoc: omniauth-cas3-1.1.3/lib/0000755000175600017570000000000012645767255014021 5ustar pravipraviomniauth-cas3-1.1.3/lib/omniauth/0000755000175600017570000000000012645767255015645 5ustar pravipraviomniauth-cas3-1.1.3/lib/omniauth/strategies/0000755000175600017570000000000012645767255020017 5ustar pravipraviomniauth-cas3-1.1.3/lib/omniauth/strategies/cas3.rb0000644000175600017570000001630012645767255021175 0ustar pravipravirequire 'omniauth' require 'addressable/uri' module OmniAuth module Strategies class CAS3 include OmniAuth::Strategy # Custom Exceptions class MissingCASTicket < StandardError; end class InvalidCASTicket < StandardError; end autoload :ServiceTicketValidator, 'omniauth/strategies/cas3/service_ticket_validator' autoload :LogoutRequest, 'omniauth/strategies/cas3/logout_request' attr_accessor :raw_info alias_method :user_info, :raw_info option :name, :cas3 # Required property by OmniAuth::Strategy option :host, nil option :port, nil option :path, nil option :ssl, true option :service_validate_url, '/p3/serviceValidate' option :login_url, '/login' option :logout_url, '/logout' option :on_single_sign_out, Proc.new {} # A Proc or lambda that returns a Hash of additional user info to be # merged with the info returned by the CAS server. # # @param [Object] An instance of OmniAuth::Strategies::CAS for the current request # @param [String] The user's Service Ticket value # @param [Hash] The user info for the Service Ticket returned by the CAS server # # @return [Hash] Extra user info option :fetch_raw_info, Proc.new { Hash.new } # Make all the keys configurable with some defaults set here option :uid_field, 'user' option :name_key, 'name' option :email_key, 'email' option :nickname_key, 'user' option :first_name_key, 'first_name' option :last_name_key, 'last_name' option :location_key, 'location' option :image_key, 'image' option :phone_key, 'phone' # As required by https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema AuthHashSchemaKeys = %w{name email nickname first_name last_name location image phone} info do prune!({ name: raw_info[options[:name_key].to_s], email: raw_info[options[:email_key].to_s], nickname: raw_info[options[:nickname_key].to_s], first_name: raw_info[options[:first_name_key].to_s], last_name: raw_info[options[:last_name_key].to_s], location: raw_info[options[:location_key].to_s], image: raw_info[options[:image_key].to_s], phone: raw_info[options[:phone_key].to_s] }) end extra do prune!( raw_info.delete_if{ |k,v| AuthHashSchemaKeys.include?(k) } ) end uid do raw_info[options[:uid_field].to_s] end credentials do prune!({ ticket: @ticket }) end def callback_phase if on_sso_path? single_sign_out_phase else @ticket = request.params['ticket'] return fail!(:no_ticket, MissingCASTicket.new('No CAS Ticket')) unless @ticket fetch_raw_info(@ticket) return fail!(:invalid_ticket, InvalidCASTicket.new('Invalid CAS Ticket')) if raw_info.empty? super end end def request_phase service_url = append_params(callback_url, return_url) [ 302, { 'Location' => login_url(service_url), 'Content-Type' => 'text/plain' }, ["You are being redirected to CAS for sign-in."] ] end def on_sso_path? request.post? && request.params.has_key?('logoutRequest') end def single_sign_out_phase logout_request_service.new(self, request).call(options) end # Build a CAS host with protocol and port # # def cas_url extract_url if options['url'] validate_cas_setup @cas_url ||= begin uri = Addressable::URI.new uri.host = options.host uri.scheme = options.ssl ? 'https' : 'http' uri.port = options.port uri.path = options.path uri.to_s end end def extract_url url = Addressable::URI.parse(options.delete('url')) options.merge!( 'host' => url.host, 'port' => url.port, 'path' => url.path, 'ssl' => url.scheme == 'https' ) end def validate_cas_setup if options.host.nil? || options.login_url.nil? raise ArgumentError.new(":host and :login_url MUST be provided") end end # Build a service-validation URL from +service+ and +ticket+. # If +service+ has a ticket param, first remove it. URL-encode # +service+ and add it and the +ticket+ as paraemters to the # CAS serviceValidate URL. # # @param [String] service the service (a.k.a. return-to) URL # @param [String] ticket the ticket to validate # # @return [String] a URL like `http://cas.mycompany.com/serviceValidate?service=...&ticket=...` def service_validate_url(service_url, ticket) service_url = Addressable::URI.parse(service_url) service_url.query_values = service_url.query_values.tap { |qs| qs.delete('ticket') } cas_url + append_params(options.service_validate_url, { service: service_url.to_s, ticket: ticket }) end # Build a CAS login URL from +service+. # # @param [String] service the service (a.k.a. return-to) URL # # @return [String] a URL like `http://cas.mycompany.com/login?service=...` def login_url(service) cas_url + append_params(options.login_url, { service: service }) end # Adds URL-escaped +parameters+ to +base+. # # @param [String] base the base URL # @param [String] params the parameters to append to the URL # # @return [String] the new joined URL. def append_params(base, params) params = params.each { |k,v| v = Rack::Utils.escape(v) } Addressable::URI.parse(base).tap do |base_uri| base_uri.query_values = (base_uri.query_values || {}).merge(params) end.to_s end # Validate the Service Ticket # @return [Object] the validated Service Ticket def validate_service_ticket(ticket) ServiceTicketValidator.new(self, options, callback_url, ticket).call end private def fetch_raw_info(ticket) ticket_user_info = validate_service_ticket(ticket).user_info custom_user_info = options.fetch_raw_info.call(self, options, ticket, ticket_user_info) self.raw_info = ticket_user_info.merge(custom_user_info) end # Deletes Hash pairs with `nil` values. # From https://github.com/mkdynamic/omniauth-facebook/blob/972ed5e3456bcaed7df1f55efd7c05c216c8f48e/lib/omniauth/strategies/facebook.rb#L122-127 def prune!(hash) hash.delete_if do |_, value| prune!(value) if value.is_a?(Hash) value.nil? || (value.respond_to?(:empty?) && value.empty?) end end def return_url # If the request already has a `url` parameter, then it will already be appended to the callback URL. if request.params && request.params['url'] {} else { url: request.referer } end end def logout_request_service LogoutRequest end end end end OmniAuth.config.add_camelization 'cas3', 'CAS3' omniauth-cas3-1.1.3/lib/omniauth/strategies/cas3/0000755000175600017570000000000012645767255020650 5ustar pravipraviomniauth-cas3-1.1.3/lib/omniauth/strategies/cas3/logout_request.rb0000644000175600017570000000446312645767255024265 0ustar pravipravimodule OmniAuth module Strategies class CAS3 class LogoutRequest def initialize(strategy, request) @strategy, @request = strategy, request end def call(options = {}) @options = options begin result = single_sign_out_callback.call(*logout_request) rescue StandardError => err return @strategy.fail! :logout_request, err else result = [200,{},'OK'] if result == true || result.nil? ensure return unless result # TODO: Why does ActionPack::Response return [status,headers,body] # when Rack::Response#new wants [body,status,headers]? Additionally, # why does Rack::Response differ in argument order from the usual # Rack-like [status,headers,body] array? return Rack::Response.new(result[2],result[0],result[1]).finish end end private def logout_request @logout_request ||= begin saml = parse_and_ensure_namespaces(@request.params['logoutRequest']) ns = saml.collect_namespaces name_id = saml.xpath('//saml:NameID', ns).text sess_idx = saml.xpath('//samlp:SessionIndex', ns).text inject_params(name_id:name_id, session_index:sess_idx) @request end end def parse_and_ensure_namespaces(logout_request_xml) doc = Nokogiri.parse(logout_request_xml) ns = doc.collect_namespaces if ns.include?('xmlns:samlp') && ns.include?('xmlns:saml') doc else add_namespaces(doc) end end def add_namespaces(logout_request_doc) root = logout_request_doc.root root.add_namespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol') root.add_namespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion\\') # In order to add namespaces properly we need to re-parse the document Nokogiri.parse(logout_request_doc.to_s) end def inject_params(new_params) new_params.each do |key, val| @request.update_param(key, val) end end def single_sign_out_callback @options[:on_single_sign_out] end end end end end omniauth-cas3-1.1.3/lib/omniauth/strategies/cas3/service_ticket_validator.rb0000644000175600017570000000713112645767255026247 0ustar pravipravirequire 'net/http' require 'net/https' require 'nokogiri' module OmniAuth module Strategies class CAS3 class ServiceTicketValidator VALIDATION_REQUEST_HEADERS = { 'Accept' => '*/*' } # Build a validator from a +configuration+, a # +return_to+ URL, and a +ticket+. # # @param [Hash] options the OmniAuth Strategy options # @param [String] return_to_url the URL of this CAS client service # @param [String] ticket the service ticket to validate def initialize(strategy, options, return_to_url, ticket) @options = options @uri = URI.parse(strategy.service_validate_url(return_to_url, ticket)) end # Executes a network request to process the CAS Service Response def call @response_body = get_service_response_body @success_body = find_authentication_success(@response_body) self end # Request validation of the ticket from the CAS server's # serviceValidate (CAS 2.0) function. # # Swallows all XML parsing errors (and returns +nil+ in those cases). # # @return [Hash, nil] a user information hash if the response is valid; +nil+ otherwise. # # @raise any connection errors encountered. def user_info parse_user_info(@success_body) end private # turns an `` node into a Hash; # returns nil if given nil def parse_user_info(node) return nil if node.nil? {}.tap do |hash| node.children.each do |e| node_name = e.name.sub(/^cas:/, '') unless e.kind_of?(Nokogiri::XML::Text) || node_name == 'proxies' # There are no child elements if e.element_children.count == 0 hash[node_name] = e.content elsif e.element_children.count # JASIG style extra attributes if node_name == 'attributes' hash.merge!(parse_user_info(e)) else hash[node_name] = [] if hash[node_name].nil? hash[node_name].push(parse_user_info(e)) end end end end end end # finds an `` node in # a `` body if present; returns nil # if the passed body is nil or if there is no such node. def find_authentication_success(body) return nil if body.nil? || body == '' begin doc = Nokogiri::XML(body) begin doc.xpath('/cas:serviceResponse/cas:authenticationSuccess') rescue Nokogiri::XML::XPath::SyntaxError doc.xpath('/serviceResponse/authenticationSuccess') end rescue Nokogiri::XML::XPath::SyntaxError nil end end # retrieves the `` XML from the CAS server def get_service_response_body result = '' http = Net::HTTP.new(@uri.host, @uri.port) http.use_ssl = @uri.port == 443 || @uri.instance_of?(URI::HTTPS) if http.use_ssl? http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @options.disable_ssl_verification? http.ca_path = @options.ca_path end http.start do |c| response = c.get "#{@uri.path}?#{@uri.query}", VALIDATION_REQUEST_HEADERS.dup result = response.body end result end end end end end omniauth-cas3-1.1.3/lib/omniauth/cas3.rb0000644000175600017570000000010212645767255017014 0ustar pravipravirequire 'omniauth/cas3/version' require 'omniauth/strategies/cas3'omniauth-cas3-1.1.3/lib/omniauth/cas3/0000755000175600017570000000000012645767255016476 5ustar pravipraviomniauth-cas3-1.1.3/lib/omniauth/cas3/version.rb0000644000175600017570000000007612645767255020513 0ustar pravipravimodule Omniauth module Cas3 VERSION = '1.1.3' end end omniauth-cas3-1.1.3/lib/omniauth-cas3.rb0000644000175600017570000000003012645767255017012 0ustar pravipravirequire 'omniauth/cas3' omniauth-cas3-1.1.3/Rakefile0000644000175600017570000000051612645767255014722 0ustar pravipravi#!/usr/bin/env rake require 'bundler/gem_tasks' require 'rspec/core/rake_task' desc 'Default: run specs.' task default: :spec desc 'Run specs' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = '--require spec_helper --color --order rand' end task :test do fail %q{This application uses RSpec. Try running "rake spec"} end omniauth-cas3-1.1.3/.gitignore0000644000175600017570000000026412645767255015245 0ustar pravipravi*~ *.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp vendor # RSpec .rspec