From dc864970c97b980bd35738981cb399973ed33caa Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 10:56:47 +0100 Subject: [PATCH 001/350] Initial commit to CASinoCore. --- .document | 5 ++++ .gitignore | 49 +++++++++++++++++++++++++++++++++++++ Gemfile | 14 +++++++++++ LICENSE.txt | 20 ++++++++++++++++ README.rdoc | 19 +++++++++++++++ Rakefile | 53 +++++++++++++++++++++++++++++++++++++++++ lib/CASinoCore.rb | 0 test/helper.rb | 18 ++++++++++++++ test/test_CASinoCore.rb | 7 ++++++ 9 files changed, 185 insertions(+) create mode 100644 .document create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 LICENSE.txt create mode 100644 README.rdoc create mode 100644 Rakefile create mode 100644 lib/CASinoCore.rb create mode 100644 test/helper.rb create mode 100644 test/test_CASinoCore.rb diff --git a/.document b/.document new file mode 100644 index 00000000..3d618dd8 --- /dev/null +++ b/.document @@ -0,0 +1,5 @@ +lib/**/*.rb +bin/* +- +features/**/*.feature +LICENSE.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a9c888fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# rcov generated +coverage +coverage.data + +# rdoc generated +rdoc + +# yard generated +doc +.yardoc + +# bundler +.bundle + +# jeweler generated +pkg + +# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: +# +# * Create a file at ~/.gitignore +# * Include files you want ignored +# * Run: git config --global core.excludesfile ~/.gitignore +# +# After doing this, these files will be ignored in all your git projects, +# saving you from having to 'pollute' every project you touch with them +# +# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) +# +# For MacOS: +# +#.DS_Store + +# For TextMate +#*.tmproj +#tmtags + +# For emacs: +#*~ +#\#* +#.\#* + +# For vim: +#*.swp + +# For redcar: +#.redcar + +# For rubinius: +#*.rbc diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..d55f8056 --- /dev/null +++ b/Gemfile @@ -0,0 +1,14 @@ +source "http://rubygems.org" +# Add dependencies required to use your gem here. +# Example: +# gem "activesupport", ">= 2.3.5" + +# Add dependencies to develop your gem here. +# Include everything needed to run rake, tests, features, etc. +group :development do + gem "shoulda", ">= 0" + gem "rdoc", "~> 3.12" + gem "bundler", "~> 1.0.0" + gem "jeweler", "~> 1.8.4" + gem "rcov", ">= 0" +end diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..13e11e93 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2012 Nils Caspar + +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. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 00000000..40ec31d3 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,19 @@ += CASinoCore + +Description goes here. + +== Contributing to CASinoCore + +* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. +* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. +* Fork the project. +* Start a feature/bugfix branch. +* Commit and push until you are happy with your contribution. +* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. +* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. + +== Copyright + +Copyright (c) 2012 Nils Caspar. See LICENSE.txt for +further details. + diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..123073c7 --- /dev/null +++ b/Rakefile @@ -0,0 +1,53 @@ +# encoding: utf-8 + +require 'rubygems' +require 'bundler' +begin + Bundler.setup(:default, :development) +rescue Bundler::BundlerError => e + $stderr.puts e.message + $stderr.puts "Run `bundle install` to install missing gems" + exit e.status_code +end +require 'rake' + +require 'jeweler' +Jeweler::Tasks.new do |gem| + # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options + gem.name = "CASinoCore" + gem.homepage = "http://github.com/pencil/CASinoCore" + gem.license = "MIT" + gem.summary = %Q{TODO: one-line summary of your gem} + gem.description = %Q{TODO: longer description of your gem} + gem.email = "ncaspar@me.com" + gem.authors = ["Nils Caspar"] + # dependencies defined in Gemfile +end +Jeweler::RubygemsDotOrgTasks.new + +require 'rake/testtask' +Rake::TestTask.new(:test) do |test| + test.libs << 'lib' << 'test' + test.pattern = 'test/**/test_*.rb' + test.verbose = true +end + +require 'rcov/rcovtask' +Rcov::RcovTask.new do |test| + test.libs << 'test' + test.pattern = 'test/**/test_*.rb' + test.verbose = true + test.rcov_opts << '--exclude "gems/*"' +end + +task :default => :test + +require 'rdoc/task' +Rake::RDocTask.new do |rdoc| + version = File.exist?('VERSION') ? File.read('VERSION') : "" + + rdoc.rdoc_dir = 'rdoc' + rdoc.title = "CASinoCore #{version}" + rdoc.rdoc_files.include('README*') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/lib/CASinoCore.rb b/lib/CASinoCore.rb new file mode 100644 index 00000000..e69de29b diff --git a/test/helper.rb b/test/helper.rb new file mode 100644 index 00000000..30bab50a --- /dev/null +++ b/test/helper.rb @@ -0,0 +1,18 @@ +require 'rubygems' +require 'bundler' +begin + Bundler.setup(:default, :development) +rescue Bundler::BundlerError => e + $stderr.puts e.message + $stderr.puts "Run `bundle install` to install missing gems" + exit e.status_code +end +require 'test/unit' +require 'shoulda' + +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +$LOAD_PATH.unshift(File.dirname(__FILE__)) +require 'CASinoCore' + +class Test::Unit::TestCase +end diff --git a/test/test_CASinoCore.rb b/test/test_CASinoCore.rb new file mode 100644 index 00000000..fe0b0bb8 --- /dev/null +++ b/test/test_CASinoCore.rb @@ -0,0 +1,7 @@ +require 'helper' + +class TestCasinocore < Test::Unit::TestCase + should "probably rename this file and start testing for real" do + flunk "hey buddy, you should probably rename this file and start testing for real" + end +end From e920239ee7b8a073138081005ac5a01ad0ad215f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 10:57:13 +0100 Subject: [PATCH 002/350] use RVM --- .rvmrc | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .rvmrc diff --git a/.rvmrc b/.rvmrc new file mode 100644 index 00000000..139d2565 --- /dev/null +++ b/.rvmrc @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# This is an RVM Project .rvmrc file, used to automatically load the ruby +# development environment upon cd'ing into the directory + +# First we specify our desired [@], the @gemset name is optional, +# Only full ruby name is supported here, for short names use: +# echo "rvm use 1.9.3" > .rvmrc +environment_id="ruby-1.9.3-p194@casino_core" + +# Uncomment the following lines if you want to verify rvm version per project +# rvmrc_rvm_version="1.15.8 (stable)" # 1.10.1 seams as a safe start +# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || { +# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading." +# return 1 +# } + +# First we attempt to load the desired environment directly from the environment +# file. This is very fast and efficient compared to running through the entire +# CLI and selector. If you want feedback on which environment was used then +# insert the word 'use' after --create as this triggers verbose mode. +if [[ -d "${rvm_path:-$HOME/.rvm}/environments" + && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] +then + \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" + [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] && + \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true +else + # If the environment file has not yet been created, use the RVM CLI to select. + rvm --create "$environment_id" || { + echo "Failed to create RVM environment '${environment_id}'." + return 1 + } +fi + +# If you use bundler, this might be useful to you: +# if [[ -s Gemfile ]] && { +# ! builtin command -v bundle >/dev/null || +# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null +# } +# then +# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n" +# gem install bundler +# fi +# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null +# then +# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete' +# fi From 50558504b23499342f9d67d0800a3d2f193237e1 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 11:07:03 +0100 Subject: [PATCH 003/350] install some gems --- Gemfile | 19 +++++++---- Gemfile.lock | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 Gemfile.lock diff --git a/Gemfile b/Gemfile index d55f8056..976dac0b 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,19 @@ -source "http://rubygems.org" +source 'http://rubygems.org' # Add dependencies required to use your gem here. # Example: -# gem "activesupport", ">= 2.3.5" +# gem 'activesupport', '>= 2.3.5' # Add dependencies to develop your gem here. # Include everything needed to run rake, tests, features, etc. group :development do - gem "shoulda", ">= 0" - gem "rdoc", "~> 3.12" - gem "bundler", "~> 1.0.0" - gem "jeweler", "~> 1.8.4" - gem "rcov", ">= 0" + gem 'bundler', '~> 1.2.0' + gem 'jeweler', '~> 1.8.4' end + +group :development, :test do + gem 'rspec', '~> 2.12.0' + gem 'simplecov', '~> 0.7.1' +end + +gem 'standalone_migrations', '~> 2.0.2' +gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..5aa46a7e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,91 @@ +GEM + remote: http://rubygems.org/ + specs: + actionpack (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + builder (~> 3.0.0) + erubis (~> 2.7.0) + journey (~> 1.0.4) + rack (~> 1.4.0) + rack-cache (~> 1.2) + rack-test (~> 0.6.1) + sprockets (~> 2.2.1) + activemodel (3.2.9) + activesupport (= 3.2.9) + builder (~> 3.0.0) + activerecord (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + arel (~> 3.0.2) + tzinfo (~> 0.3.29) + activesupport (3.2.9) + i18n (~> 0.6) + multi_json (~> 1.0) + arel (3.0.2) + builder (3.0.4) + diff-lcs (1.1.3) + erubis (2.7.0) + git (1.2.5) + hike (1.2.1) + i18n (0.6.1) + jeweler (1.8.4) + bundler (~> 1.0) + git (>= 1.2.5) + rake + rdoc + journey (1.0.4) + json (1.7.5) + multi_json (1.4.0) + rack (1.4.1) + rack-cache (1.2) + rack (>= 0.4) + rack-ssl (1.3.2) + rack + rack-test (0.6.2) + rack (>= 1.0) + railties (3.2.9) + actionpack (= 3.2.9) + activesupport (= 3.2.9) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (>= 0.14.6, < 2.0) + rake (10.0.2) + rdoc (3.12) + json (~> 1.4) + rspec (2.12.0) + rspec-core (~> 2.12.0) + rspec-expectations (~> 2.12.0) + rspec-mocks (~> 2.12.0) + rspec-core (2.12.0) + rspec-expectations (2.12.0) + diff-lcs (~> 1.1.3) + rspec-mocks (2.12.0) + simplecov (0.7.1) + multi_json (~> 1.0) + simplecov-html (~> 0.7.1) + simplecov-html (0.7.1) + sprockets (2.2.2) + hike (~> 1.2) + multi_json (~> 1.0) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + standalone_migrations (2.0.2) + activerecord (~> 3.2.6) + railties (~> 3.2.6) + rake + thor (0.16.0) + tilt (1.3.3) + tzinfo (0.3.35) + +PLATFORMS + ruby + +DEPENDENCIES + activerecord (~> 3.2.9) + bundler (~> 1.2.0) + jeweler (~> 1.8.4) + rspec (~> 2.12.0) + simplecov (~> 0.7.1) + standalone_migrations (~> 2.0.2) From 25e5d6fef7e354f587811f9b421aa9ee91beef49 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 11:07:57 +0100 Subject: [PATCH 004/350] use RSpec instead of shoulda --- Rakefile | 29 +++++------------------------ test/helper.rb | 18 ------------------ test/test_CASinoCore.rb | 7 ------- 3 files changed, 5 insertions(+), 49 deletions(-) delete mode 100644 test/helper.rb delete mode 100644 test/test_CASinoCore.rb diff --git a/Rakefile b/Rakefile index 123073c7..c5c19c4c 100644 --- a/Rakefile +++ b/Rakefile @@ -25,29 +25,10 @@ Jeweler::Tasks.new do |gem| end Jeweler::RubygemsDotOrgTasks.new -require 'rake/testtask' -Rake::TestTask.new(:test) do |test| - test.libs << 'lib' << 'test' - test.pattern = 'test/**/test_*.rb' - test.verbose = true +require 'rspec/core' +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.pattern = FileList['spec/**/*_spec.rb'] end -require 'rcov/rcovtask' -Rcov::RcovTask.new do |test| - test.libs << 'test' - test.pattern = 'test/**/test_*.rb' - test.verbose = true - test.rcov_opts << '--exclude "gems/*"' -end - -task :default => :test - -require 'rdoc/task' -Rake::RDocTask.new do |rdoc| - version = File.exist?('VERSION') ? File.read('VERSION') : "" - - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "CASinoCore #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') -end +task :default => :spec diff --git a/test/helper.rb b/test/helper.rb deleted file mode 100644 index 30bab50a..00000000 --- a/test/helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rubygems' -require 'bundler' -begin - Bundler.setup(:default, :development) -rescue Bundler::BundlerError => e - $stderr.puts e.message - $stderr.puts "Run `bundle install` to install missing gems" - exit e.status_code -end -require 'test/unit' -require 'shoulda' - -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) -$LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'CASinoCore' - -class Test::Unit::TestCase -end diff --git a/test/test_CASinoCore.rb b/test/test_CASinoCore.rb deleted file mode 100644 index fe0b0bb8..00000000 --- a/test/test_CASinoCore.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'helper' - -class TestCasinocore < Test::Unit::TestCase - should "probably rename this file and start testing for real" do - flunk "hey buddy, you should probably rename this file and start testing for real" - end -end From 9d2d991230baaea918b1ee9d6b6958a7b543c761 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 11:39:44 +0100 Subject: [PATCH 005/350] static authenticator --- .rspec | 1 + lib/CASinoCore.rb | 0 lib/casino_core.rb | 3 ++ lib/casino_core/authenticator.rb | 16 ++++++++++ lib/casino_core/authenticator/static.rb | 18 +++++++++++ spec/authenticator/static_spec.rb | 42 +++++++++++++++++++++++++ spec/spec_helper.rb | 17 ++++++++++ 7 files changed, 97 insertions(+) create mode 100644 .rspec delete mode 100644 lib/CASinoCore.rb create mode 100644 lib/casino_core.rb create mode 100644 lib/casino_core/authenticator.rb create mode 100644 lib/casino_core/authenticator/static.rb create mode 100644 spec/authenticator/static_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..114486d9 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--color --format documentation diff --git a/lib/CASinoCore.rb b/lib/CASinoCore.rb deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/casino_core.rb b/lib/casino_core.rb new file mode 100644 index 00000000..b2563613 --- /dev/null +++ b/lib/casino_core.rb @@ -0,0 +1,3 @@ +module CASinoCore + autoload :Authenticator, 'casino_core/authenticator/static.rb' +end \ No newline at end of file diff --git a/lib/casino_core/authenticator.rb b/lib/casino_core/authenticator.rb new file mode 100644 index 00000000..2fa4ef6d --- /dev/null +++ b/lib/casino_core/authenticator.rb @@ -0,0 +1,16 @@ +class CASinoCore::Authenticator + autoload :Static, 'casino_core/authenticator/static.rb' + + def self.included(authenticator) + authenticator.extend(ClassMethods) + end + + class ClassMethods + def initialize(options) + end + + def validate(username, password) + raise NotImplementedError, "This method must be implemented by a class extending #{self.class}" + end + end +end diff --git a/lib/casino_core/authenticator/static.rb b/lib/casino_core/authenticator/static.rb new file mode 100644 index 00000000..8cd3d037 --- /dev/null +++ b/lib/casino_core/authenticator/static.rb @@ -0,0 +1,18 @@ +require 'casino_core/authenticator' + +class CASinoCore::Authenticator::Static < CASinoCore::Authenticator + def initialize(options) + @users = options['users'] || {} + end + + def validate(username, password) + if @users.include?(username) && @users[username]['password'] == password + { + username: username, + extra_attributes: @users[username].delete_if { |key, value| key == 'password' } + } + else + false + end + end +end diff --git a/spec/authenticator/static_spec.rb b/spec/authenticator/static_spec.rb new file mode 100644 index 00000000..a8139595 --- /dev/null +++ b/spec/authenticator/static_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe CASinoCore::Authenticator::Static do + subject { + CASinoCore::Authenticator::Static.new({ + 'users' => { + 'user' => { + 'password' => 'testing123', + 'fullname' => 'Example User' + } + } + }) + } + + context '#validate' do + context 'with invalid credentials' do + it 'returns false for an unknown username' do + subject.validate('foobar', 'test').should == false + end + + it 'returns false for a known username with wron password' do + subject.validate('user', 'test').should == false + end + end + + context 'with valid credentials' do + let(:result) { subject.validate('user', 'testing123') } + + it 'should not return false' do + result.should_not == false + end + + it 'should return the username' do + result[:username].should == 'user' + end + + it 'should return extra attributes' do + result[:extra_attributes]['fullname'].should == 'Example User' + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..995fed0c --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,17 @@ +require 'casino_core' + +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# Require this file using `require "spec_helper"` to ensure that it is only +# loaded once. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = 'random' +end From 7dcc9d4ec935f38a250f6323dea740389cb39f53 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 11:41:31 +0100 Subject: [PATCH 006/350] some documentation --- README.rdoc => README.md | 8 ++++---- Rakefile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename README.rdoc => README.md (77%) diff --git a/README.rdoc b/README.md similarity index 77% rename from README.rdoc rename to README.md index 40ec31d3..adaa6620 100644 --- a/README.rdoc +++ b/README.md @@ -1,8 +1,8 @@ -= CASinoCore +# CASinoCore [![Build Status](https://secure.travis-ci.org/pencil/CASinoCore.png?branch=master)](https://travis-ci.org/pencil/CASinoCore) -Description goes here. +A CAS server core library. -== Contributing to CASinoCore +## Contributing to CASinoCore * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. @@ -12,7 +12,7 @@ Description goes here. * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. -== Copyright +## Copyright Copyright (c) 2012 Nils Caspar. See LICENSE.txt for further details. diff --git a/Rakefile b/Rakefile index c5c19c4c..2e083334 100644 --- a/Rakefile +++ b/Rakefile @@ -17,8 +17,8 @@ Jeweler::Tasks.new do |gem| gem.name = "CASinoCore" gem.homepage = "http://github.com/pencil/CASinoCore" gem.license = "MIT" - gem.summary = %Q{TODO: one-line summary of your gem} - gem.description = %Q{TODO: longer description of your gem} + gem.summary = "A CAS server core library." + gem.description = gem.summary gem.email = "ncaspar@me.com" gem.authors = ["Nils Caspar"] # dependencies defined in Gemfile From 3e2264882c5f648116b84634c422e6170be40b0a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 11:43:16 +0100 Subject: [PATCH 007/350] added newline at end of file --- lib/casino_core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index b2563613..92e58f3b 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,3 +1,3 @@ module CASinoCore autoload :Authenticator, 'casino_core/authenticator/static.rb' -end \ No newline at end of file +end From 1502fe7a4988eb96173974f1a9e6c1009dc8074a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 12:04:55 +0100 Subject: [PATCH 008/350] use YARD for doumentation --- Gemfile | 2 ++ Gemfile.lock | 4 ++++ Rakefile | 5 +++++ lib/casino_core/authenticator/static.rb | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/Gemfile b/Gemfile index 976dac0b..6e6899bf 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,8 @@ source 'http://rubygems.org' group :development do gem 'bundler', '~> 1.2.0' gem 'jeweler', '~> 1.8.4' + gem 'redcarpet' + gem 'yard', '~> 0.8.3', require: 'redcarpet' end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 5aa46a7e..7396557c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,7 @@ GEM rake (10.0.2) rdoc (3.12) json (~> 1.4) + redcarpet (2.2.2) rspec (2.12.0) rspec-core (~> 2.12.0) rspec-expectations (~> 2.12.0) @@ -78,6 +79,7 @@ GEM thor (0.16.0) tilt (1.3.3) tzinfo (0.3.35) + yard (0.8.3) PLATFORMS ruby @@ -86,6 +88,8 @@ DEPENDENCIES activerecord (~> 3.2.9) bundler (~> 1.2.0) jeweler (~> 1.8.4) + redcarpet rspec (~> 2.12.0) simplecov (~> 0.7.1) standalone_migrations (~> 2.0.2) + yard (~> 0.8.3) diff --git a/Rakefile b/Rakefile index 2e083334..aabb2d5a 100644 --- a/Rakefile +++ b/Rakefile @@ -25,6 +25,11 @@ Jeweler::Tasks.new do |gem| end Jeweler::RubygemsDotOrgTasks.new +require 'yard' +YARD::Rake::YardocTask.new do |t| + t.files = FileList['lib/**/*.rb'].exclude('lib/jeweler/templates/**/*.rb') +end + require 'rspec/core' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |spec| diff --git a/lib/casino_core/authenticator/static.rb b/lib/casino_core/authenticator/static.rb index 8cd3d037..2c5436bc 100644 --- a/lib/casino_core/authenticator/static.rb +++ b/lib/casino_core/authenticator/static.rb @@ -1,6 +1,10 @@ require 'casino_core/authenticator' +# The static authenticator is just a simple example. +# Never ever us this authenticator in a productive environment! class CASinoCore::Authenticator::Static < CASinoCore::Authenticator + + # @param [Hash] options def initialize(options) @users = options['users'] || {} end From 6aa90777524a6307b9ae7c6e62d0556800af6887 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 13:56:00 +0100 Subject: [PATCH 009/350] Version bump to 0.0.1 --- VERSION | 1 + 1 file changed, 1 insertion(+) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..8a9ecc2e --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file From 9dc8b23255fc71debd869b5214ddd91b6d09577e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:17:32 +0100 Subject: [PATCH 010/350] standalone_migrations is not what we want --- Gemfile | 1 - Gemfile.lock | 39 --------------------------------------- 2 files changed, 40 deletions(-) diff --git a/Gemfile b/Gemfile index 6e6899bf..86471c5b 100644 --- a/Gemfile +++ b/Gemfile @@ -17,5 +17,4 @@ group :development, :test do gem 'simplecov', '~> 0.7.1' end -gem 'standalone_migrations', '~> 2.0.2' gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index 7396557c..fb289d8a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,16 +1,6 @@ GEM remote: http://rubygems.org/ specs: - actionpack (3.2.9) - activemodel (= 3.2.9) - activesupport (= 3.2.9) - builder (~> 3.0.0) - erubis (~> 2.7.0) - journey (~> 1.0.4) - rack (~> 1.4.0) - rack-cache (~> 1.2) - rack-test (~> 0.6.1) - sprockets (~> 2.2.1) activemodel (3.2.9) activesupport (= 3.2.9) builder (~> 3.0.0) @@ -25,32 +15,15 @@ GEM arel (3.0.2) builder (3.0.4) diff-lcs (1.1.3) - erubis (2.7.0) git (1.2.5) - hike (1.2.1) i18n (0.6.1) jeweler (1.8.4) bundler (~> 1.0) git (>= 1.2.5) rake rdoc - journey (1.0.4) json (1.7.5) multi_json (1.4.0) - rack (1.4.1) - rack-cache (1.2) - rack (>= 0.4) - rack-ssl (1.3.2) - rack - rack-test (0.6.2) - rack (>= 1.0) - railties (3.2.9) - actionpack (= 3.2.9) - activesupport (= 3.2.9) - rack-ssl (~> 1.3.2) - rake (>= 0.8.7) - rdoc (~> 3.4) - thor (>= 0.14.6, < 2.0) rake (10.0.2) rdoc (3.12) json (~> 1.4) @@ -67,17 +40,6 @@ GEM multi_json (~> 1.0) simplecov-html (~> 0.7.1) simplecov-html (0.7.1) - sprockets (2.2.2) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - standalone_migrations (2.0.2) - activerecord (~> 3.2.6) - railties (~> 3.2.6) - rake - thor (0.16.0) - tilt (1.3.3) tzinfo (0.3.35) yard (0.8.3) @@ -91,5 +53,4 @@ DEPENDENCIES redcarpet rspec (~> 2.12.0) simplecov (~> 0.7.1) - standalone_migrations (~> 2.0.2) yard (~> 0.8.3) From 048c54ef3075c2e23ee3f7ee4d004255995ea39e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:19:13 +0100 Subject: [PATCH 011/350] let's call the gem casino_core --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index aabb2d5a..b4d64c2e 100644 --- a/Rakefile +++ b/Rakefile @@ -14,7 +14,7 @@ require 'rake' require 'jeweler' Jeweler::Tasks.new do |gem| # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options - gem.name = "CASinoCore" + gem.name = "casino_core" gem.homepage = "http://github.com/pencil/CASinoCore" gem.license = "MIT" gem.summary = "A CAS server core library." From f2ca24febdd8292a737fa43a2f5d10baa6c59d47 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:19:25 +0100 Subject: [PATCH 012/350] gemspec --- casino_core.gemspec | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 casino_core.gemspec diff --git a/casino_core.gemspec b/casino_core.gemspec new file mode 100644 index 00000000..22c3703a --- /dev/null +++ b/casino_core.gemspec @@ -0,0 +1,74 @@ +# Generated by jeweler +# DO NOT EDIT THIS FILE DIRECTLY +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = "casino_core" + s.version = "0.0.1" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Nils Caspar"] + s.date = "2012-12-01" + s.description = "A CAS server core library." + s.email = "ncaspar@me.com" + s.extra_rdoc_files = [ + "LICENSE.txt", + "README.md" + ] + s.files = [ + ".document", + ".rspec", + ".rvmrc", + "Gemfile", + "Gemfile.lock", + "LICENSE.txt", + "README.md", + "Rakefile", + "VERSION", + "lib/casino_core.rb", + "lib/casino_core/authenticator.rb", + "lib/casino_core/authenticator/static.rb", + "spec/authenticator/static_spec.rb", + "spec/spec_helper.rb" + ] + s.homepage = "http://github.com/pencil/CASinoCore" + s.licenses = ["MIT"] + s.require_paths = ["lib"] + s.rubygems_version = "1.8.24" + s.summary = "A CAS server core library." + + if s.respond_to? :specification_version then + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, ["~> 2.0.2"]) + s.add_runtime_dependency(%q, ["~> 3.2.9"]) + s.add_development_dependency(%q, ["~> 1.2.0"]) + s.add_development_dependency(%q, ["~> 1.8.4"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 0.8.3"]) + s.add_development_dependency(%q, ["~> 2.12.0"]) + s.add_development_dependency(%q, ["~> 0.7.1"]) + else + s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.2.9"]) + s.add_dependency(%q, ["~> 1.2.0"]) + s.add_dependency(%q, ["~> 1.8.4"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.8.3"]) + s.add_dependency(%q, ["~> 2.12.0"]) + s.add_dependency(%q, ["~> 0.7.1"]) + end + else + s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.2.9"]) + s.add_dependency(%q, ["~> 1.2.0"]) + s.add_dependency(%q, ["~> 1.8.4"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.8.3"]) + s.add_dependency(%q, ["~> 2.12.0"]) + s.add_dependency(%q, ["~> 0.7.1"]) + end +end + From a36e3b6f64a7b8d2eed7392bde2a375815724fa6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:19:47 +0100 Subject: [PATCH 013/350] support for rake tasks and railtie for rails --- lib/casino_core.rb | 5 ++++- lib/casino_core/railtie.rb | 11 +++++++++++ lib/casino_core/rake_tasks.rb | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 lib/casino_core/railtie.rb create mode 100644 lib/casino_core/rake_tasks.rb diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 92e58f3b..01c9918e 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,3 +1,6 @@ module CASinoCore - autoload :Authenticator, 'casino_core/authenticator/static.rb' + autoload :Authenticator, 'casino_core/authenticator.rb' + autoload :RakeTasks, 'casino_core/rake_tasks.rb' + + require 'casino_core/railtie' if defined?(Rails) end diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb new file mode 100644 index 00000000..b9efbeda --- /dev/null +++ b/lib/casino_core/railtie.rb @@ -0,0 +1,11 @@ +require 'casino_core' +require 'rails' + +module CASinoCore + class Railtie < Rails::Railtie + rake_tasks do + puts 'foo' + CASinoCore::RakeTasks.load_tasks + end + end +end diff --git a/lib/casino_core/rake_tasks.rb b/lib/casino_core/rake_tasks.rb new file mode 100644 index 00000000..eee918a7 --- /dev/null +++ b/lib/casino_core/rake_tasks.rb @@ -0,0 +1,12 @@ +module CASinoCore + class RakeTasks + class << self + def load_tasks + %w( + ).each do + |task| load "casino_core/tasks/#{task}.rake" + end + end + end + end +end From 35a893f43785113bfe7bbcf66595fca15444d240 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:26:50 +0100 Subject: [PATCH 014/350] example rake task --- lib/casino_core/railtie.rb | 1 - lib/casino_core/rake_tasks.rb | 5 +++-- lib/casino_core/tasks/datebase.rake | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 lib/casino_core/tasks/datebase.rake diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index b9efbeda..1eaf3c36 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -4,7 +4,6 @@ module CASinoCore class Railtie < Rails::Railtie rake_tasks do - puts 'foo' CASinoCore::RakeTasks.load_tasks end end diff --git a/lib/casino_core/rake_tasks.rb b/lib/casino_core/rake_tasks.rb index eee918a7..f65e3747 100644 --- a/lib/casino_core/rake_tasks.rb +++ b/lib/casino_core/rake_tasks.rb @@ -3,8 +3,9 @@ class RakeTasks class << self def load_tasks %w( - ).each do - |task| load "casino_core/tasks/#{task}.rake" + datebase + ).each do |task| + load "casino_core/tasks/#{task}.rake" end end end diff --git a/lib/casino_core/tasks/datebase.rake b/lib/casino_core/tasks/datebase.rake new file mode 100644 index 00000000..5a35d509 --- /dev/null +++ b/lib/casino_core/tasks/datebase.rake @@ -0,0 +1,7 @@ +namespace :casino_core do + namespace :db do + task :migrate do + puts 'Migrate...' + end + end +end From 078d6dc9ae14101ba0bd8fc7ec78fddbd4a42dd4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:29:41 +0100 Subject: [PATCH 015/350] description for rake task --- lib/casino_core/tasks/datebase.rake | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/tasks/datebase.rake b/lib/casino_core/tasks/datebase.rake index 5a35d509..924ecee0 100644 --- a/lib/casino_core/tasks/datebase.rake +++ b/lib/casino_core/tasks/datebase.rake @@ -1,5 +1,6 @@ namespace :casino_core do namespace :db do + desc 'Migrate the database (options: VERSION=x, VERBOSE=false)' task :migrate do puts 'Migrate...' end From 2190a32d7f2189c805ec6617bd22b193346095ba Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:35:27 +0100 Subject: [PATCH 016/350] some environment and configuation helpers --- lib/casino_core/tasks/datebase.rake | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/tasks/datebase.rake b/lib/casino_core/tasks/datebase.rake index 924ecee0..ae81d238 100644 --- a/lib/casino_core/tasks/datebase.rake +++ b/lib/casino_core/tasks/datebase.rake @@ -1,8 +1,17 @@ namespace :casino_core do namespace :db do + task :environment do + DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development' + MIGRATIONS_DIR = File.join(Gem.loaded_specs['casino_core'].full_gem_path, 'db', 'migrate') + end + + task :configuration => :environment do + @config = YAML.load_file('config/database.yml')[DATABASE_ENV] + end + desc 'Migrate the database (options: VERSION=x, VERBOSE=false)' - task :migrate do - puts 'Migrate...' + task :migrate => :configuration do + puts File.expand_path MIGRATIONS_DIR end end end From 50a36ffd8ffaa369be957b95d1b21119eb7e1800 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 14:45:42 +0100 Subject: [PATCH 017/350] fixed typo --- lib/casino_core/rake_tasks.rb | 2 +- lib/casino_core/tasks/{datebase.rake => database.rake} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/casino_core/tasks/{datebase.rake => database.rake} (100%) diff --git a/lib/casino_core/rake_tasks.rb b/lib/casino_core/rake_tasks.rb index f65e3747..7b91b84d 100644 --- a/lib/casino_core/rake_tasks.rb +++ b/lib/casino_core/rake_tasks.rb @@ -3,7 +3,7 @@ class RakeTasks class << self def load_tasks %w( - datebase + database ).each do |task| load "casino_core/tasks/#{task}.rake" end diff --git a/lib/casino_core/tasks/datebase.rake b/lib/casino_core/tasks/database.rake similarity index 100% rename from lib/casino_core/tasks/datebase.rake rename to lib/casino_core/tasks/database.rake From 260c490bcc3a64de8feccd64419efc3fb7109f80 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 15:14:21 +0100 Subject: [PATCH 018/350] some more db rake tasks --- Rakefile | 6 +++++- lib/casino_core/tasks/database.rake | 29 ++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index b4d64c2e..715da720 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,5 @@ # encoding: utf-8 +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) require 'rubygems' require 'bundler' @@ -11,6 +12,9 @@ rescue Bundler::BundlerError => e end require 'rake' +require 'casino_core' +CASinoCore::RakeTasks.load_tasks + require 'jeweler' Jeweler::Tasks.new do |gem| # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options @@ -27,7 +31,7 @@ Jeweler::RubygemsDotOrgTasks.new require 'yard' YARD::Rake::YardocTask.new do |t| - t.files = FileList['lib/**/*.rb'].exclude('lib/jeweler/templates/**/*.rb') + t.files = FileList['lib/**/*.rb'] end require 'rspec/core' diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index ae81d238..ab5b8b55 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -1,17 +1,40 @@ +require 'yaml' +require 'logger' +require 'active_record' + namespace :casino_core do namespace :db do task :environment do + BASE_DIR = if Gem.loaded_specs['casino_core'].nil? + '' + else + Gem.loaded_specs['casino_core'].full_gem_path + end DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development' - MIGRATIONS_DIR = File.join(Gem.loaded_specs['casino_core'].full_gem_path, 'db', 'migrate') + MIGRATIONS_DIR = File.join(BASE_DIR, 'db', 'migrate') end task :configuration => :environment do @config = YAML.load_file('config/database.yml')[DATABASE_ENV] end + task :configure_connection => :configuration do + ActiveRecord::Base.establish_connection @config + ActiveRecord::Base.logger = Logger.new STDOUT if @config['logger'] + end + desc 'Migrate the database (options: VERSION=x, VERBOSE=false)' - task :migrate => :configuration do - puts File.expand_path MIGRATIONS_DIR + task :migrate => :configure_connection do + ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true + ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration| + ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) + end + end + + desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' + task :rollback => [:environment, :load_config] do + step = ENV['STEP'] ? ENV['STEP'].to_i : 1 + ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) end end end From a5cfe716b8ca37f2b5045ac434e34e9eca3ef92b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 15:33:40 +0100 Subject: [PATCH 019/350] base schema --- Gemfile | 1 + Gemfile.lock | 2 ++ lib/casino_core/tasks/database.rake | 22 +++++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 86471c5b..f64e7087 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,7 @@ end group :development, :test do gem 'rspec', '~> 2.12.0' gem 'simplecov', '~> 0.7.1' + gem 'sqlite3' end gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index fb289d8a..ccfbd82d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,6 +40,7 @@ GEM multi_json (~> 1.0) simplecov-html (~> 0.7.1) simplecov-html (0.7.1) + sqlite3 (1.3.6) tzinfo (0.3.35) yard (0.8.3) @@ -53,4 +54,5 @@ DEPENDENCIES redcarpet rspec (~> 2.12.0) simplecov (~> 0.7.1) + sqlite3 yard (~> 0.8.3) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index ab5b8b55..696d5387 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -6,12 +6,13 @@ namespace :casino_core do namespace :db do task :environment do BASE_DIR = if Gem.loaded_specs['casino_core'].nil? - '' + Dir.pwd else Gem.loaded_specs['casino_core'].full_gem_path end DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development' MIGRATIONS_DIR = File.join(BASE_DIR, 'db', 'migrate') + SCHEMA_PATH = ENV['SCHEMA'] || File.join(BASE_DIR, 'db', 'schema.rb') end task :configuration => :environment do @@ -36,5 +37,24 @@ namespace :casino_core do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) end + + namespace :schema do + desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR' + task :dump => :configure_connection do + require 'active_record/schema_dumper' + File.open(SCHEMA_PATH, "w:utf-8") do |file| + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) + end + end + + desc 'Load a schema.rb file into the database' + task :load => :configure_connection do + if File.exists?(SCHEMA_PATH) + load(SCHEMA_PATH) + else + abort %{#{SCHEMA_PATH} doesn't exist yet.} + end + end + end end end From 08ba48cec842f3dfdf133f8841a2a71bf47c0bbf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 15:33:50 +0100 Subject: [PATCH 020/350] base schema --- config/database.yml | 18 ++++++ db/development.sqlite3 | Bin 0 -> 20480 bytes ...12154930_create_ticket_granting_tickets.rb | 11 ++++ .../20121112160009_create_login_tickets.rb | 9 +++ ...0121112165804_ticket_should_not_be_null.rb | 5 ++ ...d_user_agent_to_ticket_granting_tickets.rb | 5 ++ ...for_username_to_ticket_granting_tickets.rb | 5 ++ .../20121124183542_create_service_tickets.rb | 13 ++++ .../20121124183732_add_ticket_indexes.rb | 6 ++ ...4195013_add_consumed_to_service_tickets.rb | 5 ++ ...ued_from_credentials_to_service_tickets.rb | 5 ++ ...125185415_create_proxy_granting_tickets.rb | 14 ++++ ...20121125190013_tickets_should_be_unique.rb | 8 +++ db/schema.rb | 61 ++++++++++++++++++ 14 files changed, 165 insertions(+) create mode 100644 config/database.yml create mode 100644 db/development.sqlite3 create mode 100644 db/migrate/20121112154930_create_ticket_granting_tickets.rb create mode 100644 db/migrate/20121112160009_create_login_tickets.rb create mode 100644 db/migrate/20121112165804_ticket_should_not_be_null.rb create mode 100644 db/migrate/20121122180310_add_user_agent_to_ticket_granting_tickets.rb create mode 100644 db/migrate/20121124170004_add_index_for_username_to_ticket_granting_tickets.rb create mode 100644 db/migrate/20121124183542_create_service_tickets.rb create mode 100644 db/migrate/20121124183732_add_ticket_indexes.rb create mode 100644 db/migrate/20121124195013_add_consumed_to_service_tickets.rb create mode 100644 db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb create mode 100644 db/migrate/20121125185415_create_proxy_granting_tickets.rb create mode 100644 db/migrate/20121125190013_tickets_should_be_unique.rb create mode 100644 db/schema.rb diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 00000000..fd7aa2af --- /dev/null +++ b/config/database.yml @@ -0,0 +1,18 @@ +# this configuration is only needed to setup the database for the tests + +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 diff --git a/db/development.sqlite3 b/db/development.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..6b284796af42fb4113f7c152f3e734fdbe60cad8 GIT binary patch literal 20480 zcmeHPZ%^As7`Jl?CIqPIDp{5G!pfw91r^S}K}M&Iwi!YVp@C!@n^emLlUgGQI1Xac zq{*y%Jx$st*o%FGN&5)XzQQI=+6Sn#H+wT_+G8gswmAo~7PSpeM1uXh=gxP(yXP+a zo;$Y7McvfI{g%;CO)Ry9e5#(K`7;sT*q3)SGq1)N7w>W}A-2?r#-V zD+RH#daqazlXRn`IG5D-lHz))Qdlc&iI29{H&(ZH#D|3)akX06TrWW>8--FuEN#NO zS}ZPz$?jv5;<0Mfo~XuLI+v5Y>3XXZp4iPCY=dG~LT{Q{T{FBz)eKEFwLL{Olj0s+ znR-L>rgsi)G$dWJ#tA2^&9c^_-rUnpoDb!oRo9y?bxO;<^uf^{ch%B*xmo~r9uz(i zlLPZ7#m$nK^p#?h+v7@l6d4od=h+>n8{0<*FvOL%cGS_DH7#&Ud>t1^oNKBLZ2@vh zR+Z(1*;)2fcgplV*>@B1m3O|1FTDSU>@;w%=}GvA$KivEP1)||^=kVbU&sJ5a4i@> z|GyU9jEzJFs0>WlD6aohZV*HUMx6m%|3|%tv3R z_I>ud$eZNTD{1k_$ftfy{3?XzS4Tz1aL99oL*9yU!giju);!@5dR)kki$EBMo;Wrj zIzrFUE3O+Op_@37C&9#8Q`GqXn)lpjxg8#+m+OraTaP(T_$tqy&J0mqN;IAR zq={Y&d;x8`X+JVfDgB~}JUqxRQMyR4X>L5e4L#<$P549Cfz&J6ywH2bE3ttA_FB|h z&331u!S0CNR_j1ho8p7QhpW|MMV#HA_3nhw+wBf)gxEJ)4Fv-5dm3yJQ4iXK^GOyT z=Y)szthMABZULJ6BO#D5IlvzRru{OyLnn~z^#EM|hnexv78w{V22lSWt^UN;AOpiN zfb0J-Y@sbOFj@?t|BqIGVr!6rVHmK#{}c8u!`_XsWP_9#!O49z%?XQ(tkpdT)2=70<^ z1Y(|cVj3!(WikzYw_|D`&Y1w&D&O-QbAtT#qZPFdnxFsSk|j@a!e=Y2^|5CNhmg`A zdLah<0KG3HOTWzS*bOoB>VNq6|1h&9+9Cs^#Q=W)AFckx)*u7JFo6C)3|nZ642%{7 z_V@n;d6yy2$nWG=@-z97d{0ivA$d%S0OJc8KnAD`@QH|&mQ(3eDxFI$=d!6BKha0z zd@7&G^6@hyo66_pRE8h-Aj_GhOq%C>NG_Y^$2`cAEX!Fwc7~)=%W@_q^HCRp8oZNR z1{>l9l0q6>-G~KsOR@_ALKaxcmM*==X{Xk`ePob=liif 20121125190013) do + + create_table "login_tickets", :force => true do |t| + t.string "ticket", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "login_tickets", ["ticket"], :name => "index_login_tickets_on_ticket", :unique => true + + create_table "proxy_granting_tickets", :force => true do |t| + t.string "ticket", :null => false + t.string "iou", :null => false + t.integer "ticket_granting_ticket_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true + add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true + add_index "proxy_granting_tickets", ["ticket_granting_ticket_id"], :name => "index_proxy_granting_tickets_on_ticket_granting_ticket_id" + + create_table "service_tickets", :force => true do |t| + t.string "ticket", :null => false + t.string "service", :null => false + t.integer "ticket_granting_ticket_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "consumed", :default => false, :null => false + t.boolean "issued_from_credentials", :default => false, :null => false + end + + add_index "service_tickets", ["ticket"], :name => "index_service_tickets_on_ticket", :unique => true + add_index "service_tickets", ["ticket_granting_ticket_id"], :name => "index_service_tickets_on_ticket_granting_ticket_id" + + create_table "ticket_granting_tickets", :force => true do |t| + t.string "ticket", :null => false + t.string "username", :null => false + t.text "extra_attributes" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "user_agent" + end + + add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true + add_index "ticket_granting_tickets", ["username"], :name => "index_ticket_granting_tickets_on_username" + +end From e4529ea89684c743ddbb1dfeb16c486b2a272adc Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:24:25 +0100 Subject: [PATCH 021/350] DatabaseCleaner --- Gemfile | 1 + Gemfile.lock | 2 ++ spec/spec_helper.rb | 14 ++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/Gemfile b/Gemfile index f64e7087..979dec28 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ group :development, :test do gem 'rspec', '~> 2.12.0' gem 'simplecov', '~> 0.7.1' gem 'sqlite3' + gem 'database_cleaner' end gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index ccfbd82d..1ee5edd2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,6 +14,7 @@ GEM multi_json (~> 1.0) arel (3.0.2) builder (3.0.4) + database_cleaner (0.9.1) diff-lcs (1.1.3) git (1.2.5) i18n (0.6.1) @@ -50,6 +51,7 @@ PLATFORMS DEPENDENCIES activerecord (~> 3.2.9) bundler (~> 1.2.0) + database_cleaner jeweler (~> 1.8.4) redcarpet rspec (~> 2.12.0) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 995fed0c..290c6e8c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'casino_core' +require 'database_cleaner' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. @@ -14,4 +15,17 @@ # the seed, which is printed after each run. # --seed 1234 config.order = 'random' + + config.before(:suite) do + DatabaseCleaner.strategy = :transaction + DatabaseCleaner.clean_with(:truncation) + end + + config.before(:each) do + DatabaseCleaner.start + end + + config.after(:each) do + DatabaseCleaner.clean + end end From bd954f00964a8d56eac707865c8610fb10567731 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:25:07 +0100 Subject: [PATCH 022/350] settings --- lib/casino_core.rb | 22 ++++++++++++++++++++++ lib/casino_core/settings.rb | 18 ++++++++++++++++++ spec/spec_helper.rb | 2 ++ 3 files changed, 42 insertions(+) create mode 100644 lib/casino_core/settings.rb diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 01c9918e..0d761b99 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,6 +1,28 @@ module CASinoCore autoload :Authenticator, 'casino_core/authenticator.rb' autoload :RakeTasks, 'casino_core/rake_tasks.rb' + autoload :Model, 'casino_core/model.rb' + autoload :Settings, 'casino_core/settings.rb' require 'casino_core/railtie' if defined?(Rails) + + class << self + def setup(environment = nil, options = {}) + @environment = environment || 'development' + require 'active_record' + require 'yaml' + YAML::ENGINE.yamler = 'syck' + ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')[@environment] + + config = YAML.load_file('config/cas.yml')[@environment].symbolize_keys + recursive_symbolize_keys!(config) + CASinoCore::Settings.init config + end + + private + def recursive_symbolize_keys! hash + hash.symbolize_keys! + hash.values.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)} + end + end end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb new file mode 100644 index 00000000..5bb89c0f --- /dev/null +++ b/lib/casino_core/settings.rb @@ -0,0 +1,18 @@ +module CASinoCore + class Settings + class << self + def init(config = {}) + config.each do |key,value| + puts "#{key} = #{value}" + define_singleton_method key do + value + end + end + end + + def method_missing(method_id, *args) + raise "#{method_id} is not defined in #{self.to_s}" + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 290c6e8c..19f127f9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,8 @@ # --seed 1234 config.order = 'random' + CASinoCore.setup 'test' + config.before(:suite) do DatabaseCleaner.strategy = :transaction DatabaseCleaner.clean_with(:truncation) From b2669e2f426f6f591a6839b4ec7af8d8e11d2335 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:25:35 +0100 Subject: [PATCH 023/350] use symbols --- lib/casino_core/authenticator/static.rb | 9 +++++---- spec/authenticator/static_spec.rb | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/casino_core/authenticator/static.rb b/lib/casino_core/authenticator/static.rb index 2c5436bc..e02b3689 100644 --- a/lib/casino_core/authenticator/static.rb +++ b/lib/casino_core/authenticator/static.rb @@ -6,14 +6,15 @@ class CASinoCore::Authenticator::Static < CASinoCore::Authenticator # @param [Hash] options def initialize(options) - @users = options['users'] || {} + @users = options[:users] || {} end def validate(username, password) - if @users.include?(username) && @users[username]['password'] == password + username = :"#{username}" + if @users.include?(username) && @users[username][:password] == password { - username: username, - extra_attributes: @users[username].delete_if { |key, value| key == 'password' } + username: "#{username}", + extra_attributes: @users[username].delete_if { |key, value| "#{key}" == 'password' } } else false diff --git a/spec/authenticator/static_spec.rb b/spec/authenticator/static_spec.rb index a8139595..bea29a72 100644 --- a/spec/authenticator/static_spec.rb +++ b/spec/authenticator/static_spec.rb @@ -3,10 +3,10 @@ describe CASinoCore::Authenticator::Static do subject { CASinoCore::Authenticator::Static.new({ - 'users' => { - 'user' => { - 'password' => 'testing123', - 'fullname' => 'Example User' + users: { + user: { + password: 'testing123', + fullname: 'Example User' } } }) @@ -35,7 +35,7 @@ end it 'should return extra attributes' do - result[:extra_attributes]['fullname'].should == 'Example User' + result[:extra_attributes][:fullname].should == 'Example User' end end end From 7f34c4cda76b5805f1140af1b35953eaa5ac52bb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:26:12 +0100 Subject: [PATCH 024/350] settings for testing --- config/cas.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 config/cas.yml diff --git a/config/cas.yml b/config/cas.yml new file mode 100644 index 00000000..02db7b66 --- /dev/null +++ b/config/cas.yml @@ -0,0 +1,26 @@ +defaults: &defaults + login_ticket: + lifetime: 600 + service_ticket: + lifetime_unconsumed: 300 + lifetime_consumed: 86400 + +development: + <<: *defaults + authenticators: + - + class: "CASino::Authenticator::Static" + options: + users: + testuser: + password: foobar123 + +test: + <<: *defaults + authenticators: + - + class: "CASino::Authenticator::Static" + options: + users: + testuser: + password: foobar123 From 1669f380873c9fefeef746ed3a07755a664a62a7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:26:38 +0100 Subject: [PATCH 025/350] ignore the default SQLite database --- .gitignore | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index a9c888fa..60c4ff92 100644 --- a/.gitignore +++ b/.gitignore @@ -15,35 +15,5 @@ doc # jeweler generated pkg -# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: -# -# * Create a file at ~/.gitignore -# * Include files you want ignored -# * Run: git config --global core.excludesfile ~/.gitignore -# -# After doing this, these files will be ignored in all your git projects, -# saving you from having to 'pollute' every project you touch with them -# -# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) -# -# For MacOS: -# -#.DS_Store - -# For TextMate -#*.tmproj -#tmtags - -# For emacs: -#*~ -#\#* -#.\#* - -# For vim: -#*.swp - -# For redcar: -#.redcar - -# For rubinius: -#*.rbc +# Ignore the default SQLite database. +/db/*.sqlite3 From 6c2dac706608f229b2f8d4434c3bdf2927bb3fac Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:26:46 +0100 Subject: [PATCH 026/350] some models --- lib/casino_core/model.rb | 7 +++++++ lib/casino_core/model/login_ticket.rb | 11 +++++++++++ spec/models/login_ticket_spec.rb | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 lib/casino_core/model.rb create mode 100644 lib/casino_core/model/login_ticket.rb create mode 100644 spec/models/login_ticket_spec.rb diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb new file mode 100644 index 00000000..3f8c1ed4 --- /dev/null +++ b/lib/casino_core/model.rb @@ -0,0 +1,7 @@ +require 'active_record' + +module CASinoCore + module Model + autoload :LoginTicket, 'casino_core/model/login_ticket.rb' + end +end diff --git a/lib/casino_core/model/login_ticket.rb b/lib/casino_core/model/login_ticket.rb new file mode 100644 index 00000000..fd5f463b --- /dev/null +++ b/lib/casino_core/model/login_ticket.rb @@ -0,0 +1,11 @@ +require 'casino_core/model' +require 'casino_core/settings' + +class CASinoCore::Model::LoginTicket < ActiveRecord::Base + attr_accessible :ticket + validates :ticket, uniqueness: true + + def self.cleanup + self.delete_all(['created_at < ?', CASinoCore::Settings.login_ticket[:lifetime].seconds.ago]) + end +end diff --git a/spec/models/login_ticket_spec.rb b/spec/models/login_ticket_spec.rb new file mode 100644 index 00000000..d9ba3190 --- /dev/null +++ b/spec/models/login_ticket_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe CASinoCore::Model::LoginTicket do + describe '.cleanup' do + it 'should delete expired login tickets' do + ticket = described_class.new ticket: 'LT-12345' + ticket.save! + ticket.created_at = 10.hours.ago + ticket.save! + lambda do + described_class.cleanup + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('LT-12345').should be_false + end + end +end From 365076c28895e19208b0d8023bc0a6f7f26a24d1 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:27:57 +0100 Subject: [PATCH 027/350] load default scheme before starting the tests --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..21d68955 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: ruby +before_script: + - RAILS_ENV=test rake casino_core:db:schema:load From 905354fa2196f2b19ddf314c84898df3c4ad44c5 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 1 Dec 2012 16:30:11 +0100 Subject: [PATCH 028/350] use DATABASE_ENV, not RAILS_ENV... --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 21d68955..a1828110 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: ruby before_script: - - RAILS_ENV=test rake casino_core:db:schema:load + - DATABASE_ENV=test rake casino_core:db:schema:load From 3d671edad9f120df8e33e848ec2cca562e9ded72 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 2 Dec 2012 13:13:15 +0100 Subject: [PATCH 029/350] removed debug output --- lib/casino_core/settings.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 5bb89c0f..56e59ff7 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,7 +3,6 @@ class Settings class << self def init(config = {}) config.each do |key,value| - puts "#{key} = #{value}" define_singleton_method key do value end From 96f5073aa3378eefdee70ed5f599bac276104cbd Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 2 Dec 2012 13:13:24 +0100 Subject: [PATCH 030/350] mode models --- lib/casino_core/model.rb | 3 ++ .../model/proxy_granting_ticket.rb | 8 +++ lib/casino_core/model/service_ticket.rb | 25 +++++++++ .../single_sign_out_notifier.rb | 53 +++++++++++++++++++ .../model/ticket_granting_ticket.rb | 9 ++++ spec/{models => model}/login_ticket_spec.rb | 0 spec/model/service_ticket_spec.rb | 45 ++++++++++++++++ 7 files changed, 143 insertions(+) create mode 100644 lib/casino_core/model/proxy_granting_ticket.rb create mode 100644 lib/casino_core/model/service_ticket.rb create mode 100644 lib/casino_core/model/service_ticket/single_sign_out_notifier.rb create mode 100644 lib/casino_core/model/ticket_granting_ticket.rb rename spec/{models => model}/login_ticket_spec.rb (100%) create mode 100644 spec/model/service_ticket_spec.rb diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index 3f8c1ed4..e063681b 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -3,5 +3,8 @@ module CASinoCore module Model autoload :LoginTicket, 'casino_core/model/login_ticket.rb' + autoload :ServiceTicket, 'casino_core/model/service_ticket.rb' + autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb' + autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb' end end diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb new file mode 100644 index 00000000..3f32ac5d --- /dev/null +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -0,0 +1,8 @@ +require 'casino_core/model' + +class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base + attr_accessible :iou, :ticket, :ticket_granting_ticket_id + validates :ticket, uniqueness: true + validates :iou, uniqueness: true + belongs_to :ticket_granting_ticket +end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb new file mode 100644 index 00000000..ee981eac --- /dev/null +++ b/lib/casino_core/model/service_ticket.rb @@ -0,0 +1,25 @@ +require 'casino_core/model' +require 'casino_core/settings' + +class CASinoCore::Model::ServiceTicket < ActiveRecord::Base + autoload :SingleSignOutNotifier, 'casino_core/model/service_ticket/single_sign_out_notifier.rb' + + attr_accessible :ticket, :service + validates :ticket, uniqueness: true + belongs_to :ticket_granting_ticket + before_destroy :send_single_sing_out_notification + + def self.cleanup_unconsumed + self.delete_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) + end + + def self.cleanup_consumed + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) + end + + private + def send_single_sing_out_notification + notifier = SingleSignOutNotifier.new(self) + notifier.notify + end +end diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb new file mode 100644 index 00000000..f420215b --- /dev/null +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -0,0 +1,53 @@ +require 'builder' +require 'net/https' +require 'casino_core/model/service_ticket' + +class CASinoCore::Model::ServiceTicket::SingleSignOutNotifier + def initialize(service_ticket) + @service_ticket = service_ticket + end + + def notify + xml = build_xml + uri = URI.parse(@service_ticket.service) + request = build_request(uri, xml) + send_notification(uri, request) + end + + private + def build_xml + xml = Builder::XmlMarkup.new(indent: 2) + xml.samlp :LogoutRequest, ID: SecureRandom.uuid, Version: '2.0', IsseInstant: Time.now do |logout_request| + logout_request.samlp :NameID, '@NOT_USED@' + logout_request.samlp :SessionIndex, @service_ticket.ticket + end + xml.target! + end + + def build_request(uri, xml) + request = Net::HTTP::Post.new(uri.path || '/') + request.set_form_data(logoutRequest: xml) + return request + end + + def send_notification(uri, request) + begin + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true if uri.scheme =='https' + + http.start do |conn| + response = conn.request(request) + if response.kind_of? Net::HTTPSuccess + Rails.logger.info "Logout notification successfully posted to #{uri}." + return true + else + puts "Service #{uri} responed to logout notification with code '#{response.code}'!" + return false + end + end + rescue Exception => e + puts "Failed to send logout notification to service #{uri} due to #{e}" + return false + end + end +end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb new file mode 100644 index 00000000..50dbdc63 --- /dev/null +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -0,0 +1,9 @@ +require 'casino_core/model' + +class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base + attr_accessible :ticket, :username, :user_agent, :extra_attributes + serialize :extra_attributes, Hash + validates :ticket, uniqueness: true + has_many :service_tickets + has_many :proxy_granting_tickets +end diff --git a/spec/models/login_ticket_spec.rb b/spec/model/login_ticket_spec.rb similarity index 100% rename from spec/models/login_ticket_spec.rb rename to spec/model/login_ticket_spec.rb diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb new file mode 100644 index 00000000..8e7eb4ef --- /dev/null +++ b/spec/model/service_ticket_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe CASinoCore::Model::ServiceTicket do + let(:ticket) { + ticket = described_class.new ticket: 'ST-12345', service: 'https://example.com/cas-service' + ticket.ticket_granting_ticket_id = 1 + ticket + } + + describe '.cleanup_unconsumed' do + it 'should delete expired unconsumed service tickets' do + ticket.created_at = 10.hours.ago + ticket.save! + lambda do + described_class.cleanup_unconsumed + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('ST-12345').should be_false + end + end + + describe '.cleanup_consumed' do + before(:each) do + described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(true) + end + + it 'should delete expired consumed service tickets' do + ticket.consumed = true + ticket.created_at = 10.days.ago + ticket.save! + lambda do + described_class.cleanup_consumed + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('ST-12345').should be_false + end + end + + describe '.destroy' do + it 'should send out a single sign out notification' do + described_class::SingleSignOutNotifier.any_instance.should_receive(:notify).and_return(true) + ticket.consumed = true + ticket.save! + ticket.destroy + end + end +end From e84a4b160b6faead479024e43cea7acdf53a88d1 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 2 Dec 2012 19:31:25 +0100 Subject: [PATCH 031/350] fixed methods --- lib/casino_core/authenticator.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/casino_core/authenticator.rb b/lib/casino_core/authenticator.rb index 2fa4ef6d..dd625777 100644 --- a/lib/casino_core/authenticator.rb +++ b/lib/casino_core/authenticator.rb @@ -1,13 +1,6 @@ -class CASinoCore::Authenticator - autoload :Static, 'casino_core/authenticator/static.rb' - - def self.included(authenticator) - authenticator.extend(ClassMethods) - end - - class ClassMethods - def initialize(options) - end +module CASinoCore + class Authenticator + autoload :Static, 'casino_core/authenticator/static.rb' def validate(username, password) raise NotImplementedError, "This method must be implemented by a class extending #{self.class}" From 837ccc60ed50e78481e86257a45e92d1c89e9b27 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 2 Dec 2012 19:31:37 +0100 Subject: [PATCH 032/350] LoginCredentialRequestor --- lib/casino_core.rb | 4 +++- lib/casino_core/helper.rb | 8 ++++++++ lib/casino_core/processor.rb | 11 +++++++++++ .../processor/login_credential_requestor.rb | 19 +++++++++++++++++++ .../login_credential_requestor_spec.rb | 14 ++++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lib/casino_core/helper.rb create mode 100644 lib/casino_core/processor.rb create mode 100644 lib/casino_core/processor/login_credential_requestor.rb create mode 100644 spec/processor/login_credential_requestor_spec.rb diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 0d761b99..f5a294a0 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,7 +1,9 @@ module CASinoCore autoload :Authenticator, 'casino_core/authenticator.rb' - autoload :RakeTasks, 'casino_core/rake_tasks.rb' + autoload :Helper, 'casino_core/helper.rb' autoload :Model, 'casino_core/model.rb' + autoload :Processor, 'casino_core/processor.rb' + autoload :RakeTasks, 'casino_core/rake_tasks.rb' autoload :Settings, 'casino_core/settings.rb' require 'casino_core/railtie' if defined?(Rails) diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb new file mode 100644 index 00000000..e76b170f --- /dev/null +++ b/lib/casino_core/helper.rb @@ -0,0 +1,8 @@ +module CASinoCore + module Helper + def random_ticket_string(prefix, length = 40) + random_string = rand(36**length).to_s(36) + "#{prefix}-#{Time.now.to_i}-#{random_string}" + end + end +end diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb new file mode 100644 index 00000000..1f49e6ee --- /dev/null +++ b/lib/casino_core/processor.rb @@ -0,0 +1,11 @@ +require 'active_record' + +module CASinoCore + class Processor + autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' + + def initialize(listener) + @listener = listener + end + end +end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb new file mode 100644 index 00000000..7093578b --- /dev/null +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -0,0 +1,19 @@ +require 'casino_core/processor' +require 'casino_core/helper' + +class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor + include CASinoCore::Helper + + def process(params = nil) + params ||= [] + login_ticket = acquire_login_ticket + @listener.render_login_page(login_ticket) + end + + private + def acquire_login_ticket + ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT') + #logger.debug "Created login ticket '#{ticket.ticket}'" + ticket + end +end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb new file mode 100644 index 00000000..a56ebe1f --- /dev/null +++ b/spec/processor/login_credential_requestor_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe CASinoCore::Processor::LoginCredentialRequestor do + describe '#process' do + context 'when logged out' do + it 'should call the render_login_page method on the listener' do + object = Object.new + object.should_receive(:render_login_page).with(kind_of(CASinoCore::Model::LoginTicket)) + processor = described_class.new(object) + processor.process + end + end + end +end From 786b787549365c4f05f0566e2970ccbc70769a59 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 2 Dec 2012 19:39:11 +0100 Subject: [PATCH 033/350] (silent) logger --- lib/casino_core/helper.rb | 9 +++++++++ lib/casino_core/processor/login_credential_requestor.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index e76b170f..17777bc5 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -1,8 +1,17 @@ +require 'logger' + module CASinoCore module Helper def random_ticket_string(prefix, length = 40) random_string = rand(36**length).to_s(36) "#{prefix}-#{Time.now.to_i}-#{random_string}" end + + def logger + # TODO this is just a "silent logger", make logger a setting! + logger = ::Logger.new(STDOUT) + logger.level = ::Logger::Severity::UNKNOWN + logger + end end end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 7093578b..e7890421 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -13,7 +13,7 @@ def process(params = nil) private def acquire_login_ticket ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT') - #logger.debug "Created login ticket '#{ticket.ticket}'" + logger.debug "Created login ticket '#{ticket.ticket}'" ticket end end From 530d207daf1f8c04a0f8f9e655449b2e79d430ce Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 3 Dec 2012 18:43:43 +0100 Subject: [PATCH 034/350] params should be a Hash --- lib/casino_core/processor/login_credential_requestor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 7093578b..0870797c 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -5,7 +5,7 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper def process(params = nil) - params ||= [] + params ||= {} login_ticket = acquire_login_ticket @listener.render_login_page(login_ticket) end From 0ab654c138ffff10215c16877f5f4645dda1f3a0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 3 Dec 2012 18:46:03 +0100 Subject: [PATCH 035/350] remove sqlite database --- db/development.sqlite3 | Bin 20480 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/development.sqlite3 diff --git a/db/development.sqlite3 b/db/development.sqlite3 deleted file mode 100644 index 6b284796af42fb4113f7c152f3e734fdbe60cad8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeHPZ%^As7`Jl?CIqPIDp{5G!pfw91r^S}K}M&Iwi!YVp@C!@n^emLlUgGQI1Xac zq{*y%Jx$st*o%FGN&5)XzQQI=+6Sn#H+wT_+G8gswmAo~7PSpeM1uXh=gxP(yXP+a zo;$Y7McvfI{g%;CO)Ry9e5#(K`7;sT*q3)SGq1)N7w>W}A-2?r#-V zD+RH#daqazlXRn`IG5D-lHz))Qdlc&iI29{H&(ZH#D|3)akX06TrWW>8--FuEN#NO zS}ZPz$?jv5;<0Mfo~XuLI+v5Y>3XXZp4iPCY=dG~LT{Q{T{FBz)eKEFwLL{Olj0s+ znR-L>rgsi)G$dWJ#tA2^&9c^_-rUnpoDb!oRo9y?bxO;<^uf^{ch%B*xmo~r9uz(i zlLPZ7#m$nK^p#?h+v7@l6d4od=h+>n8{0<*FvOL%cGS_DH7#&Ud>t1^oNKBLZ2@vh zR+Z(1*;)2fcgplV*>@B1m3O|1FTDSU>@;w%=}GvA$KivEP1)||^=kVbU&sJ5a4i@> z|GyU9jEzJFs0>WlD6aohZV*HUMx6m%|3|%tv3R z_I>ud$eZNTD{1k_$ftfy{3?XzS4Tz1aL99oL*9yU!giju);!@5dR)kki$EBMo;Wrj zIzrFUE3O+Op_@37C&9#8Q`GqXn)lpjxg8#+m+OraTaP(T_$tqy&J0mqN;IAR zq={Y&d;x8`X+JVfDgB~}JUqxRQMyR4X>L5e4L#<$P549Cfz&J6ywH2bE3ttA_FB|h z&331u!S0CNR_j1ho8p7QhpW|MMV#HA_3nhw+wBf)gxEJ)4Fv-5dm3yJQ4iXK^GOyT z=Y)szthMABZULJ6BO#D5IlvzRru{OyLnn~z^#EM|hnexv78w{V22lSWt^UN;AOpiN zfb0J-Y@sbOFj@?t|BqIGVr!6rVHmK#{}c8u!`_XsWP_9#!O49z%?XQ(tkpdT)2=70<^ z1Y(|cVj3!(WikzYw_|D`&Y1w&D&O-QbAtT#qZPFdnxFsSk|j@a!e=Y2^|5CNhmg`A zdLah<0KG3HOTWzS*bOoB>VNq6|1h&9+9Cs^#Q=W)AFckx)*u7JFo6C)3|nZ642%{7 z_V@n;d6yy2$nWG=@-z97d{0ivA$d%S0OJc8KnAD`@QH|&mQ(3eDxFI$=d!6BKha0z zd@7&G^6@hyo66_pRE8h-Aj_GhOq%C>NG_Y^$2`cAEX!Fwc7~)=%W@_q^HCRp8oZNR z1{>l9l0q6>-G~KsOR@_ALKaxcmM*==X{Xk`ePob=liif Date: Mon, 3 Dec 2012 18:46:16 +0100 Subject: [PATCH 036/350] credential acceptor --- lib/casino_core/processor.rb | 1 + .../processor/login_credential_acceptor.rb | 35 +++++++++++++++++++ .../login_credential_acceptor_spec.rb | 14 ++++++++ 3 files changed, 50 insertions(+) create mode 100644 lib/casino_core/processor/login_credential_acceptor.rb create mode 100644 spec/processor/login_credential_acceptor_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 1f49e6ee..c101c6f5 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -3,6 +3,7 @@ module CASinoCore class Processor autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' + autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' def initialize(listener) @listener = listener diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb new file mode 100644 index 00000000..4938865e --- /dev/null +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -0,0 +1,35 @@ +require 'casino_core/processor' +require 'casino_core/helper' + +class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor + include CASinoCore::Helper + + def process(params = nil) + params ||= {} + if login_ticket_valid?(params[:lt]) + if params[:service].blank? + @listener.user_logged_in_without_service + else + @listener.redirect_to(params[:service]) + end + else + @listener.invalid_login_ticket + end + end + + private + def login_ticket_valid?(lt) + ticket = CASinoCore::Model::LoginTicket.find_by_ticket lt + if ticket.nil? + logger.info "Login ticket '#{lt}' not found" + false + elsif ticket.created_at < CASinoCore::Settings.login_ticket[:lifetime].seconds.ago + logger.info "Login ticket '#{ticket.ticket}' expired" + false + else + logger.debug "Login ticket '#{ticket.ticket}' successfully validated" + ticket.delete + true + end + end +end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb new file mode 100644 index 00000000..6aa3ca92 --- /dev/null +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe CASinoCore::Processor::LoginCredentialAcceptor do + describe '#process' do + context 'without a valid login ticket' do + it 'should call the #invalid_login_ticket method on the listener' do + object = Object.new + object.should_receive(:invalid_login_ticket).with() + processor = described_class.new(object) + processor.process + end + end + end +end From 95e1bd0b75528d38b6fa53ff355aa76259ffc865 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 3 Dec 2012 20:20:29 +0100 Subject: [PATCH 037/350] validate login credentials --- config/cas.yml | 4 +-- lib/casino_core.rb | 3 ++ lib/casino_core/authenticator/static.rb | 2 +- .../processor/login_credential_acceptor.rb | 28 +++++++++++++-- .../login_credential_acceptor_spec.rb | 35 +++++++++++++++++-- 5 files changed, 63 insertions(+), 9 deletions(-) diff --git a/config/cas.yml b/config/cas.yml index 02db7b66..5dd82781 100644 --- a/config/cas.yml +++ b/config/cas.yml @@ -9,7 +9,7 @@ development: <<: *defaults authenticators: - - class: "CASino::Authenticator::Static" + class: "CASinoCore::Authenticator::Static" options: users: testuser: @@ -19,7 +19,7 @@ test: <<: *defaults authenticators: - - class: "CASino::Authenticator::Static" + class: "CASinoCore::Authenticator::Static" options: users: testuser: diff --git a/lib/casino_core.rb b/lib/casino_core.rb index f5a294a0..bf8d2f07 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -23,8 +23,11 @@ def setup(environment = nil, options = {}) private def recursive_symbolize_keys! hash + # ugly, ugly, ugly + # TODO refactor! hash.symbolize_keys! hash.values.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)} + hash.values.select{|v| v.is_a? Array}.each{|a| a.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)}} end end end diff --git a/lib/casino_core/authenticator/static.rb b/lib/casino_core/authenticator/static.rb index e02b3689..391a2717 100644 --- a/lib/casino_core/authenticator/static.rb +++ b/lib/casino_core/authenticator/static.rb @@ -14,7 +14,7 @@ def validate(username, password) if @users.include?(username) && @users[username][:password] == password { username: "#{username}", - extra_attributes: @users[username].delete_if { |key, value| "#{key}" == 'password' } + extra_attributes: @users[username].except(:password) } else false diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 4938865e..f5374033 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -7,10 +7,15 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor def process(params = nil) params ||= {} if login_ticket_valid?(params[:lt]) - if params[:service].blank? - @listener.user_logged_in_without_service + user_data = validate_login_credentials(params[:username], params[:password]) + if !user_data.nil? + if params[:service].blank? + @listener.user_logged_in_without_service + else + @listener.redirect_to(params[:service]) + end else - @listener.redirect_to(params[:service]) + @listener.invalid_login_credentials end else @listener.invalid_login_ticket @@ -32,4 +37,21 @@ def login_ticket_valid?(lt) true end end + + def validate_login_credentials(username, password) + user_data = nil + CASinoCore::Settings.authenticators.each do |authenticator| + instance = authenticator[:class].constantize.new(authenticator[:options]) + data = instance.validate(username, password) + if data + if data[:username].nil? + data[:username] = username + end + user_data = data + logger.info("Credentials for username '#{data[:username]}' successfully validated using #{authenticator['class']}") + break + end + end + user_data + end end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 6aa3ca92..40af0aa3 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -2,13 +2,42 @@ describe CASinoCore::Processor::LoginCredentialAcceptor do describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + context 'without a valid login ticket' do it 'should call the #invalid_login_ticket method on the listener' do - object = Object.new - object.should_receive(:invalid_login_ticket).with() - processor = described_class.new(object) + listener.should_receive(:invalid_login_ticket).with(no_args) processor.process end end + + context 'with a valid login ticket' do + let(:login_ticket) { CASinoCore::Model::LoginTicket.create! ticket: "LT-#{Random.rand(10000000)}" } + + context 'with invalid credentials' do + it 'should call the #invalid_login_credentials method on the listener' do + listener.should_receive(:invalid_login_credentials).with(no_args) + processor.process(lt: login_ticket.ticket) + end + end + + context 'with valid credentials' do + context 'without a service' do + it 'should call the #user_logged_in_without_service method on the listener' do + listener.should_receive(:user_logged_in_without_service).with(no_args) + processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123') + end + end + + context 'with a service' do + it 'should call the #redirect_to method on the listener' do + service = 'https://www.example.com/' + listener.should_receive(:redirect_to).with(service) + processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service) + end + end + end + end end end From c52dec01b750c8fba57db6c0dd22ff73b3662fc7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Thu, 6 Dec 2012 22:31:25 +0100 Subject: [PATCH 038/350] simplify API --- lib/casino_core/processor/login_credential_acceptor.rb | 6 +----- spec/processor/login_credential_acceptor_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index f5374033..d42faba9 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -9,11 +9,7 @@ def process(params = nil) if login_ticket_valid?(params[:lt]) user_data = validate_login_credentials(params[:username], params[:password]) if !user_data.nil? - if params[:service].blank? - @listener.user_logged_in_without_service - else - @listener.redirect_to(params[:service]) - end + @listener.user_logged_in(params[:service]) else @listener.invalid_login_credentials end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 40af0aa3..d2ee6d93 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -24,16 +24,16 @@ context 'with valid credentials' do context 'without a service' do - it 'should call the #user_logged_in_without_service method on the listener' do - listener.should_receive(:user_logged_in_without_service).with(no_args) + it 'should call the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with(nil) processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123') end end context 'with a service' do - it 'should call the #redirect_to method on the listener' do + it 'should call the #user_logged_in method on the listener' do service = 'https://www.example.com/' - listener.should_receive(:redirect_to).with(service) + listener.should_receive(:user_logged_in).with(service) processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service) end end From 767e95b030976dd48cfe8fe98971edc4564235db Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Thu, 6 Dec 2012 22:34:55 +0100 Subject: [PATCH 039/350] handle and test more cases --- .../processor/login_credential_requestor.rb | 18 ++++++++++--- .../login_credential_requestor_spec.rb | 25 ++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 913b28c7..669e6fe4 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -4,10 +4,22 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper - def process(params = nil) + def process(params = nil, cookies = nil) params ||= {} - login_ticket = acquire_login_ticket - @listener.render_login_page(login_ticket) + cookies ||= {} + if cookies[:tgt] + # TODO validate ticket + # TODO create new service ticket and url + if params[:service] + service_url_w_ticket = params[:service] + '?ticket=foo' + else + service_url_w_ticket = nil + end + @listener.user_logged_in(service_url_w_ticket) + else + login_ticket = acquire_login_ticket + @listener.user_not_logged_in(login_ticket) + end end private diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index a56ebe1f..aa635848 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -2,13 +2,30 @@ describe CASinoCore::Processor::LoginCredentialRequestor do describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + context 'when logged out' do - it 'should call the render_login_page method on the listener' do - object = Object.new - object.should_receive(:render_login_page).with(kind_of(CASinoCore::Model::LoginTicket)) - processor = described_class.new(object) + it 'should call the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) processor.process end end + + context 'when logged in' do + context 'with a service' do + it 'should call the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') + processor.process({ service: 'http://example.com/' }, { tgt: 'bla' }) + end + end + + context 'without a service' do + it 'should call the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with(nil) + processor.process(nil, { tgt: 'bla' }) + end + end + end end end From dfcd030256104461043584916ed90cadf3912d9f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 8 Dec 2012 21:49:50 +0100 Subject: [PATCH 040/350] initialize CASinoCore --- Rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Rakefile b/Rakefile index 715da720..febd2d8c 100644 --- a/Rakefile +++ b/Rakefile @@ -13,6 +13,7 @@ end require 'rake' require 'casino_core' +CASinoCore.setup 'development' CASinoCore::RakeTasks.load_tasks require 'jeweler' From d305920fad18dbb23f924cedc76e1149475d23e2 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 8 Dec 2012 21:50:39 +0100 Subject: [PATCH 041/350] return number of tickets deleted --- lib/casino_core/model/service_ticket.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index ee981eac..8f30f5f2 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -14,7 +14,7 @@ def self.cleanup_unconsumed end def self.cleanup_consumed - self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]).length end private From d4a6c81de5fadaf2b1ae41faa7f75c154bcc5ce6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 8 Dec 2012 21:50:53 +0100 Subject: [PATCH 042/350] some cleanup tasks --- lib/casino_core/rake_tasks.rb | 1 + lib/casino_core/tasks/cleanup.rake | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 lib/casino_core/tasks/cleanup.rake diff --git a/lib/casino_core/rake_tasks.rb b/lib/casino_core/rake_tasks.rb index 7b91b84d..04668c3a 100644 --- a/lib/casino_core/rake_tasks.rb +++ b/lib/casino_core/rake_tasks.rb @@ -4,6 +4,7 @@ class << self def load_tasks %w( database + cleanup ).each do |task| load "casino_core/tasks/#{task}.rake" end diff --git a/lib/casino_core/tasks/cleanup.rake b/lib/casino_core/tasks/cleanup.rake new file mode 100644 index 00000000..192a9316 --- /dev/null +++ b/lib/casino_core/tasks/cleanup.rake @@ -0,0 +1,26 @@ +require 'yaml' +require 'logger' +require 'active_record' +require 'casino_core/model' + +namespace :casino_core do + namespace :cleanup do + desc 'Remove expired service tickets.' + task service_tickets: 'casino_core:db:configure_connection' do + [:consumed, :unconsumed].each do |type| + rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}") + puts "Deleted #{rows_affected} #{type} service tickets." + end + end + + desc 'Remove expired login tickets.' + task login_tickets: 'casino_core:db:configure_connection' do + rows_affected = CASinoCore::Model::LoginTicket.cleanup + puts "Deleted #{rows_affected} login tickets." + end + + desc 'Perform all cleanup tasks.' + task all: [:service_tickets, :login_tickets] do + end + end +end From 5908fa8bc5e9bd4ad8da04c605e5f596b266013f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 8 Dec 2012 22:31:09 +0100 Subject: [PATCH 043/350] generate ticket-granting ticket and service ticket --- lib/casino_core/helper.rb | 2 ++ lib/casino_core/helper/service_tickets.rb | 29 +++++++++++++++ lib/casino_core/model/service_ticket.rb | 14 ++++++++ .../processor/login_credential_acceptor.rb | 21 +++++++++-- .../login_credential_acceptor_spec.rb | 35 ++++++++++++++++--- 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 lib/casino_core/helper/service_tickets.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index 17777bc5..dcf97e96 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -2,6 +2,8 @@ module CASinoCore module Helper + autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' + def random_ticket_string(prefix, length = 40) random_string = rand(36**length).to_s(36) "#{prefix}-#{Time.now.to_i}-#{random_string}" diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb new file mode 100644 index 00000000..6dc49a01 --- /dev/null +++ b/lib/casino_core/helper/service_tickets.rb @@ -0,0 +1,29 @@ +module CASinoCore + module Helper + module ServiceTickets + include CASinoCore::Helper + + def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied = nil) + ticket_granting_ticket.service_tickets.create!({ + ticket: random_ticket_string('ST'), + service: clean_service_url(service), + issued_from_credentials: !!credentials_supplied + }) + end + + def clean_service_url(dirty_service) + return dirty_service if dirty_service.blank? + clean_service = dirty_service.dup + ['service', 'ticket', 'gateway', 'renew'].each do |p| + clean_service.sub!(Regexp.new("&?#{p}=[^&]*"), '') + end + + clean_service = clean_service.gsub(/[\/\?&]$/, '').gsub('?&', '?').gsub(' ', '+') + + logger.debug("Cleaned dirty service URL '#{dirty_service}' to '#{clean_service}'") if dirty_service != clean_service + + clean_service + end + end + end +end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 8f30f5f2..31389c8f 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -17,6 +17,20 @@ def self.cleanup_consumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]).length end + def service_with_ticket_url + service_uri = URI.parse(service) + if service.include? '?' + if service_uri.query.empty? + query_separator = '' + else + query_separator = '&' + end + else + query_separator = '?' + end + self.service + query_separator + 'ticket=' + self.ticket + end + private def send_single_sing_out_notification notifier = SingleSignOutNotifier.new(self) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index d42faba9..53955fae 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -3,13 +3,21 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper + include CASinoCore::Helper::ServiceTickets - def process(params = nil) + def process(params = nil, cookies = nil, request_env = nil) params ||= {} + cookies ||= {} if login_ticket_valid?(params[:lt]) user_data = validate_login_credentials(params[:username], params[:password]) if !user_data.nil? - @listener.user_logged_in(params[:service]) + ticket_granting_ticket = acquire_ticket_granting_ticket(user_data[:username], user_data[:extra_attributes], request_env) + url = if params[:service].nil? + nil + else + acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(url, ticket_granting_ticket.ticket) else @listener.invalid_login_credentials end @@ -50,4 +58,13 @@ def validate_login_credentials(username, password) end user_data end + + def acquire_ticket_granting_ticket(username, extra_attributes = nil, request_env = nil) + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: random_ticket_string('TGC'), + username: username, + extra_attributes: extra_attributes, + user_agent: (request_env.nil? ? nil : request_env['HTTP_USER_AGENT']) + }) + end end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index d2ee6d93..6e9121b9 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -23,18 +23,45 @@ end context 'with valid credentials' do + let(:login_data) { { lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service } } + + before(:each) do + listener.stub(:user_logged_in) + end + context 'without a service' do + let(:service) { nil } + it 'should call the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with(nil) + listener.should_receive(:user_logged_in).with(nil, /^TGC\-/) processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123') end + + it 'should have generated a ticket-granting ticket' do + lambda do + processor.process(login_data) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) + end end context 'with a service' do + let(:service) { 'https://www.example.com' } + it 'should call the #user_logged_in method on the listener' do - service = 'https://www.example.com/' - listener.should_receive(:user_logged_in).with(service) - processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service) + listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) + processor.process(login_data) + end + + it 'should have generated a service ticket' do + lambda do + processor.process(login_data) + end.should change(CASinoCore::Model::ServiceTicket, :count).by(1) + end + + it 'should have generated a ticket-granting ticket' do + lambda do + processor.process(login_data) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) end end end From 070349982f6e806dafb917f154879e33ec0923fb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 14:01:22 +0100 Subject: [PATCH 044/350] clearer code --- lib/casino_core/processor/login_credential_acceptor.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 53955fae..f97deee6 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -11,7 +11,7 @@ def process(params = nil, cookies = nil, request_env = nil) if login_ticket_valid?(params[:lt]) user_data = validate_login_credentials(params[:username], params[:password]) if !user_data.nil? - ticket_granting_ticket = acquire_ticket_granting_ticket(user_data[:username], user_data[:extra_attributes], request_env) + ticket_granting_ticket = acquire_ticket_granting_ticket(user_data, request_env) url = if params[:service].nil? nil else @@ -59,11 +59,11 @@ def validate_login_credentials(username, password) user_data end - def acquire_ticket_granting_ticket(username, extra_attributes = nil, request_env = nil) + def acquire_ticket_granting_ticket(user_data, request_env = nil) CASinoCore::Model::TicketGrantingTicket.create!({ ticket: random_ticket_string('TGC'), - username: username, - extra_attributes: extra_attributes, + username: user_data[:username], + extra_attributes: user_data[:extra_attributes], user_agent: (request_env.nil? ? nil : request_env['HTTP_USER_AGENT']) }) end From b9fd422927270ae19707a0a64476b6427038091e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 14:01:51 +0100 Subject: [PATCH 045/350] use Addressable gem to handle URI stuff --- Gemfile | 1 + Gemfile.lock | 2 ++ lib/casino_core/helper/service_tickets.rb | 11 ++++++----- lib/casino_core/model/service_ticket.rb | 15 ++++----------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Gemfile b/Gemfile index 979dec28..de792245 100644 --- a/Gemfile +++ b/Gemfile @@ -20,3 +20,4 @@ group :development, :test do end gem 'activerecord', '~> 3.2.9' +gem 'addressable', '~> 2.3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 1ee5edd2..7d59b8ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,6 +12,7 @@ GEM activesupport (3.2.9) i18n (~> 0.6) multi_json (~> 1.0) + addressable (2.3.2) arel (3.0.2) builder (3.0.4) database_cleaner (0.9.1) @@ -50,6 +51,7 @@ PLATFORMS DEPENDENCIES activerecord (~> 3.2.9) + addressable (~> 2.3.2) bundler (~> 1.2.0) database_cleaner jeweler (~> 1.8.4) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index 6dc49a01..b2388fbc 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -1,3 +1,5 @@ +require 'addressable/uri' + module CASinoCore module Helper module ServiceTickets @@ -13,12 +15,11 @@ def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied def clean_service_url(dirty_service) return dirty_service if dirty_service.blank? - clean_service = dirty_service.dup - ['service', 'ticket', 'gateway', 'renew'].each do |p| - clean_service.sub!(Regexp.new("&?#{p}=[^&]*"), '') + service_uri = Addressable::URI.parse dirty_service + unless service_uri.query_values.nil? + service_uri.query_values = service_uri.query_values.except('service', 'ticket', 'gateway', 'renew') end - - clean_service = clean_service.gsub(/[\/\?&]$/, '').gsub('?&', '?').gsub(' ', '+') + clean_service = service_uri.to_s logger.debug("Cleaned dirty service URL '#{dirty_service}' to '#{clean_service}'") if dirty_service != clean_service diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 31389c8f..8689ce9c 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -1,5 +1,6 @@ require 'casino_core/model' require 'casino_core/settings' +require 'addressable/uri' class CASinoCore::Model::ServiceTicket < ActiveRecord::Base autoload :SingleSignOutNotifier, 'casino_core/model/service_ticket/single_sign_out_notifier.rb' @@ -18,17 +19,9 @@ def self.cleanup_consumed end def service_with_ticket_url - service_uri = URI.parse(service) - if service.include? '?' - if service_uri.query.empty? - query_separator = '' - else - query_separator = '&' - end - else - query_separator = '?' - end - self.service + query_separator + 'ticket=' + self.ticket + service_uri = Addressable::URI.parse(self.service) + service_uri.query_values = (service_uri.query_values || {}).merge(ticket: self.ticket) + service_uri.to_s end private From f35b46c36c0261c546f8f7575a6c905b42b43f27 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 14:08:41 +0100 Subject: [PATCH 046/350] changed wording --- spec/authenticator/static_spec.rb | 6 +++--- spec/model/login_ticket_spec.rb | 2 +- spec/model/service_ticket_spec.rb | 6 +++--- spec/processor/login_credential_acceptor_spec.rb | 14 +++++++------- spec/processor/login_credential_requestor_spec.rb | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/authenticator/static_spec.rb b/spec/authenticator/static_spec.rb index bea29a72..212dbb25 100644 --- a/spec/authenticator/static_spec.rb +++ b/spec/authenticator/static_spec.rb @@ -26,15 +26,15 @@ context 'with valid credentials' do let(:result) { subject.validate('user', 'testing123') } - it 'should not return false' do + it 'does not return false' do result.should_not == false end - it 'should return the username' do + it 'returns the username' do result[:username].should == 'user' end - it 'should return extra attributes' do + it 'returns extra attributes' do result[:extra_attributes][:fullname].should == 'Example User' end end diff --git a/spec/model/login_ticket_spec.rb b/spec/model/login_ticket_spec.rb index d9ba3190..0c0f78f9 100644 --- a/spec/model/login_ticket_spec.rb +++ b/spec/model/login_ticket_spec.rb @@ -2,7 +2,7 @@ describe CASinoCore::Model::LoginTicket do describe '.cleanup' do - it 'should delete expired login tickets' do + it 'deletes expired login tickets' do ticket = described_class.new ticket: 'LT-12345' ticket.save! ticket.created_at = 10.hours.ago diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index 8e7eb4ef..e0c96486 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -8,7 +8,7 @@ } describe '.cleanup_unconsumed' do - it 'should delete expired unconsumed service tickets' do + it 'deletes expired unconsumed service tickets' do ticket.created_at = 10.hours.ago ticket.save! lambda do @@ -23,7 +23,7 @@ described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(true) end - it 'should delete expired consumed service tickets' do + it 'deletes expired consumed service tickets' do ticket.consumed = true ticket.created_at = 10.days.ago ticket.save! @@ -35,7 +35,7 @@ end describe '.destroy' do - it 'should send out a single sign out notification' do + it 'sends out a single sign out notification' do described_class::SingleSignOutNotifier.any_instance.should_receive(:notify).and_return(true) ticket.consumed = true ticket.save! diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 6e9121b9..cb040107 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -6,7 +6,7 @@ let(:processor) { described_class.new(listener) } context 'without a valid login ticket' do - it 'should call the #invalid_login_ticket method on the listener' do + it 'calls the #invalid_login_ticket method on the listener' do listener.should_receive(:invalid_login_ticket).with(no_args) processor.process end @@ -16,7 +16,7 @@ let(:login_ticket) { CASinoCore::Model::LoginTicket.create! ticket: "LT-#{Random.rand(10000000)}" } context 'with invalid credentials' do - it 'should call the #invalid_login_credentials method on the listener' do + it 'calls the #invalid_login_credentials method on the listener' do listener.should_receive(:invalid_login_credentials).with(no_args) processor.process(lt: login_ticket.ticket) end @@ -32,12 +32,12 @@ context 'without a service' do let(:service) { nil } - it 'should call the #user_logged_in method on the listener' do + it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil, /^TGC\-/) processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123') end - it 'should have generated a ticket-granting ticket' do + it 'generates a ticket-granting ticket' do lambda do processor.process(login_data) end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) @@ -47,18 +47,18 @@ context 'with a service' do let(:service) { 'https://www.example.com' } - it 'should call the #user_logged_in method on the listener' do + it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) processor.process(login_data) end - it 'should have generated a service ticket' do + it 'generates a service ticket' do lambda do processor.process(login_data) end.should change(CASinoCore::Model::ServiceTicket, :count).by(1) end - it 'should have generated a ticket-granting ticket' do + it 'generates a ticket-granting ticket' do lambda do processor.process(login_data) end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index aa635848..583b1d3e 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -6,7 +6,7 @@ let(:processor) { described_class.new(listener) } context 'when logged out' do - it 'should call the #user_not_logged_in method on the listener' do + it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) processor.process end @@ -14,14 +14,14 @@ context 'when logged in' do context 'with a service' do - it 'should call the #user_logged_in method on the listener' do + it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') processor.process({ service: 'http://example.com/' }, { tgt: 'bla' }) end end context 'without a service' do - it 'should call the #user_logged_in method on the listener' do + it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil) processor.process(nil, { tgt: 'bla' }) end From 71fd4b5e3d1975bd1227d50850b0a53a9873a51e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 14:11:00 +0100 Subject: [PATCH 047/350] clearer settings handler --- lib/casino_core/settings.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 56e59ff7..991fa81e 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -1,17 +1,14 @@ module CASinoCore class Settings class << self + attr_accessor :login_ticket, :service_ticket, :authenticators def init(config = {}) config.each do |key,value| - define_singleton_method key do - value + if respond_to?("#{key}=") + send("#{key}=", value) end end end - - def method_missing(method_id, *args) - raise "#{method_id} is not defined in #{self.to_s}" - end end end end From 0a5770d385736e36467b5a292d08612aa345f058 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 14:14:36 +0100 Subject: [PATCH 048/350] instantiate authenticators only once --- .../processor/login_credential_acceptor.rb | 5 ++--- lib/casino_core/settings.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index f97deee6..39937dd3 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -45,14 +45,13 @@ def login_ticket_valid?(lt) def validate_login_credentials(username, password) user_data = nil CASinoCore::Settings.authenticators.each do |authenticator| - instance = authenticator[:class].constantize.new(authenticator[:options]) - data = instance.validate(username, password) + data = authenticator.validate(username, password) if data if data[:username].nil? data[:username] = username end user_data = data - logger.info("Credentials for username '#{data[:username]}' successfully validated using #{authenticator['class']}") + logger.info("Credentials for username '#{data[:username]}' successfully validated using #{authenticator.class}") break end end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 991fa81e..54b8f9e8 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -1,3 +1,5 @@ +require 'casino_core/authenticator' + module CASinoCore class Settings class << self @@ -9,6 +11,16 @@ def init(config = {}) end end end + + def authenticators=(authenticators) + @authenticators = [] + authenticators.each do |authenticator| + unless authenticator.is_a?(CASinoCore::Authenticator) + authenticator = authenticator[:class].constantize.new(authenticator[:options]) + end + @authenticators << authenticator + end + end end end end From 70e73f2615af9aaa0579f71596d7f242f101ce20 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 15:57:13 +0100 Subject: [PATCH 049/350] pass user_agent --- .../processor/login_credential_acceptor.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 39937dd3..fd5f2eaf 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -5,16 +5,14 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper include CASinoCore::Helper::ServiceTickets - def process(params = nil, cookies = nil, request_env = nil) + def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} if login_ticket_valid?(params[:lt]) user_data = validate_login_credentials(params[:username], params[:password]) if !user_data.nil? - ticket_granting_ticket = acquire_ticket_granting_ticket(user_data, request_env) - url = if params[:service].nil? - nil - else + ticket_granting_ticket = acquire_ticket_granting_ticket(user_data, user_agent) + url = unless params[:service].nil? acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url end @listener.user_logged_in(url, ticket_granting_ticket.ticket) @@ -58,12 +56,12 @@ def validate_login_credentials(username, password) user_data end - def acquire_ticket_granting_ticket(user_data, request_env = nil) + def acquire_ticket_granting_ticket(user_data, user_agent = nil) CASinoCore::Model::TicketGrantingTicket.create!({ ticket: random_ticket_string('TGC'), username: user_data[:username], extra_attributes: user_data[:extra_attributes], - user_agent: (request_env.nil? ? nil : request_env['HTTP_USER_AGENT']) + user_agent: user_agent }) end end From 7d9e221917d722d193634bbcb8ddba69a644b300 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 15:57:31 +0100 Subject: [PATCH 050/350] validate ticket-granting ticket --- Gemfile | 1 + Gemfile.lock | 2 ++ lib/casino_core/helper.rb | 2 ++ lib/casino_core/helper/browser.rb | 16 +++++++++ .../processor/login_credential_requestor.rb | 29 +++++++++++----- .../login_credential_requestor_spec.rb | 34 ++++++++++++++----- 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 lib/casino_core/helper/browser.rb diff --git a/Gemfile b/Gemfile index de792245..bfddaee7 100644 --- a/Gemfile +++ b/Gemfile @@ -21,3 +21,4 @@ end gem 'activerecord', '~> 3.2.9' gem 'addressable', '~> 2.3.2' +gem 'useragent', '~> 0.4.13' diff --git a/Gemfile.lock b/Gemfile.lock index 7d59b8ae..f890f9c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,6 +44,7 @@ GEM simplecov-html (0.7.1) sqlite3 (1.3.6) tzinfo (0.3.35) + useragent (0.4.15) yard (0.8.3) PLATFORMS @@ -59,4 +60,5 @@ DEPENDENCIES rspec (~> 2.12.0) simplecov (~> 0.7.1) sqlite3 + useragent (~> 0.4.13) yard (~> 0.8.3) diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index dcf97e96..e349c7f9 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -1,8 +1,10 @@ require 'logger' +require 'useragent' module CASinoCore module Helper autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' + autoload :Browser, 'casino_core/helper/browser.rb' def random_ticket_string(prefix, length = 40) random_string = rand(36**length).to_s(36) diff --git a/lib/casino_core/helper/browser.rb b/lib/casino_core/helper/browser.rb new file mode 100644 index 00000000..52feb951 --- /dev/null +++ b/lib/casino_core/helper/browser.rb @@ -0,0 +1,16 @@ +require 'addressable/uri' + +module CASinoCore + module Helper + module Browser + def browser_info(user_agent) + user_agent = UserAgent.parse(user_agent) + "#{user_agent.browser} (#{user_agent.platform})" + end + + def same_browser?(user_agent, other_user_agent) + user_agent == other_user_agent || browser_info(user_agent) == browser_info(other_user_agent) + end + end + end +end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 669e6fe4..bdc04f4a 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -3,19 +3,18 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper + include CASinoCore::Helper::Browser - def process(params = nil, cookies = nil) + def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} - if cookies[:tgt] - # TODO validate ticket + request_env ||= {} + if !params[:renew] && (ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)) # TODO create new service ticket and url - if params[:service] - service_url_w_ticket = params[:service] + '?ticket=foo' - else - service_url_w_ticket = nil + service_url_with_ticket = if params[:service] + params[:service] + '?ticket=foo' end - @listener.user_logged_in(service_url_w_ticket) + @listener.user_logged_in(service_url_with_ticket) else login_ticket = acquire_login_ticket @listener.user_not_logged_in(login_ticket) @@ -28,4 +27,18 @@ def acquire_login_ticket logger.debug "Created login ticket '#{ticket.ticket}'" ticket end + + def find_valid_ticket_granting_ticket(tgt, user_agent) + ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first + unless ticket_granting_ticket.nil? + if same_browser?(ticket_granting_ticket.user_agent, user_agent) + ticket_granting_ticket.user_agent = user_agent + ticket_granting_ticket.save! + ticket_granting_ticket + else + logger.info 'User-Agent changed: ticket-granting ticket not valid for this browser' + nil + end + end + end end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 583b1d3e..c03f209f 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -13,17 +13,35 @@ end context 'when logged in' do - context 'with a service' do - it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') - processor.process({ service: 'http://example.com/' }, { tgt: 'bla' }) + let(:user_agent) { 'TestBrowser 1.0' } + let(:ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-9H6Vx4850i2Ksp3R8hTCwO', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + context 'with the right browser' do + context 'with a service' do + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') + processor.process({ service: 'http://example.com/' }, { tgt: ticket.ticket }, user_agent) + end + end + + context 'without a service' do + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with(nil) + processor.process(nil, { tgt: ticket.ticket }, user_agent) + end end end - context 'without a service' do - it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with(nil) - processor.process(nil, { tgt: 'bla' }) + context 'with a changed browser' do + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(nil, { tgt: ticket.ticket }) end end end From fd91ed2296ad7ffa2056c8037942e3adb9da7af8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 16:00:50 +0100 Subject: [PATCH 051/350] test renew --- .../login_credential_requestor_spec.rb | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index c03f209f..0b3ca0fb 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -22,26 +22,31 @@ user_agent: user_agent }) } - context 'with the right browser' do - context 'with a service' do - it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') - processor.process({ service: 'http://example.com/' }, { tgt: ticket.ticket }, user_agent) - end + context 'with a service' do + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') + processor.process({ service: 'http://example.com/' }, { tgt: ticket.ticket }, user_agent) end - context 'without a service' do - it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with(nil) - processor.process(nil, { tgt: ticket.ticket }, user_agent) + context 'with renew parameter' do + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process({ renew: 'true', service: 'http://example.com/' }, { tgt: ticket.ticket }) end end end - context 'with a changed browser' do - it 'calls the #user_not_logged_in method on the listener' do - listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) - processor.process(nil, { tgt: ticket.ticket }) + context 'without a service' do + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with(nil) + processor.process(nil, { tgt: ticket.ticket }, user_agent) + end + + context 'with a changed browser' do + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(nil, { tgt: ticket.ticket }) + end end end end From 64cc2fda10174f5c34b63530d172915eaa4e10bd Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 16:04:41 +0100 Subject: [PATCH 052/350] less duplication --- spec/processor/login_credential_requestor_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 0b3ca0fb..6b170334 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -22,16 +22,18 @@ user_agent: user_agent }) } + let(:cookies) { { tgt: ticket.ticket } } + context 'with a service' do it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') - processor.process({ service: 'http://example.com/' }, { tgt: ticket.ticket }, user_agent) + processor.process({ service: 'http://example.com/' }, cookies, user_agent) end context 'with renew parameter' do it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) - processor.process({ renew: 'true', service: 'http://example.com/' }, { tgt: ticket.ticket }) + processor.process({ renew: 'true', service: 'http://example.com/' }, cookies) end end end @@ -39,13 +41,13 @@ context 'without a service' do it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil) - processor.process(nil, { tgt: ticket.ticket }, user_agent) + processor.process(nil, cookies, user_agent) end context 'with a changed browser' do it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) - processor.process(nil, { tgt: ticket.ticket }) + processor.process(nil, cookies) end end end From 9a624c5134e5b26eb3b7aea0b26951038cddc357 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 16:10:36 +0100 Subject: [PATCH 053/350] generate service ticket --- .../processor/login_credential_requestor.rb | 5 ++-- .../login_credential_requestor_spec.rb | 25 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index bdc04f4a..ea1ee416 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -3,6 +3,7 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper + include CASinoCore::Helper::ServiceTickets include CASinoCore::Helper::Browser def process(params = nil, cookies = nil, user_agent = nil) @@ -11,8 +12,8 @@ def process(params = nil, cookies = nil, user_agent = nil) request_env ||= {} if !params[:renew] && (ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)) # TODO create new service ticket and url - service_url_with_ticket = if params[:service] - params[:service] + '?ticket=foo' + service_url_with_ticket = unless params[:service].nil? + acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url end @listener.user_logged_in(service_url_with_ticket) else diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 6b170334..d9066b53 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -24,16 +24,29 @@ } let(:cookies) { { tgt: ticket.ticket } } + before(:each) do + listener.stub(:user_logged_in) + end + context 'with a service' do + let(:service) { 'http://example.com/' } + let(:params) { { service: service } } + it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with('http://example.com/?ticket=foo') - processor.process({ service: 'http://example.com/' }, cookies, user_agent) + listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/) + processor.process(params, cookies, user_agent) + end + + it 'generates a service ticket' do + lambda do + processor.process(params, cookies, user_agent) + end.should change(CASinoCore::Model::ServiceTicket, :count).by(1) end context 'with renew parameter' do it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) - processor.process({ renew: 'true', service: 'http://example.com/' }, cookies) + processor.process(params.merge({ renew: 'true' }), cookies) end end end @@ -44,6 +57,12 @@ processor.process(nil, cookies, user_agent) end + it 'does not generate a service ticket' do + lambda do + processor.process(nil, cookies, user_agent) + end.should change(CASinoCore::Model::ServiceTicket, :count).by(0) + end + context 'with a changed browser' do it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) From be4cb851db7d9e0bcd7d5a35979344f2cc2dd8df Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 17:03:34 +0100 Subject: [PATCH 054/350] renamed attribute --- spec/processor/login_credential_requestor_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index d9066b53..3f816485 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -14,7 +14,7 @@ context 'when logged in' do let(:user_agent) { 'TestBrowser 1.0' } - let(:ticket) { + let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-9H6Vx4850i2Ksp3R8hTCwO', username: 'test', @@ -22,7 +22,7 @@ user_agent: user_agent }) } - let(:cookies) { { tgt: ticket.ticket } } + let(:cookies) { { tgt: ticket_granting_ticket.ticket } } before(:each) do listener.stub(:user_logged_in) From 149c1762f24b1cc2c318b2c8e2b84bce23ceb6c2 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 17:05:31 +0100 Subject: [PATCH 055/350] processor for /validate --- lib/casino_core/processor.rb | 1 + lib/casino_core/processor/legacy_validator.rb | 49 +++++++++++ spec/processor/legacy_validator_spec.rb | 87 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 lib/casino_core/processor/legacy_validator.rb create mode 100644 spec/processor/legacy_validator_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index c101c6f5..4bee20c7 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -4,6 +4,7 @@ module CASinoCore class Processor autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' + autoload :LegacyValidator, 'casino_core/processor/legacy_validator.rb' def initialize(listener) @listener = listener diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb new file mode 100644 index 00000000..dd7ec4ca --- /dev/null +++ b/lib/casino_core/processor/legacy_validator.rb @@ -0,0 +1,49 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor + include CASinoCore::Helper + include CASinoCore::Helper::ServiceTickets + + def process(params = nil) + params ||= {} + ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first + if ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) + @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n") + else + @listener.validation_failed("no\n\n") + end + end + + private + def ticket_valid_for_service?(ticket, service, renew = false) + ticket_valid = if service.nil? or ticket.nil? + logger.warn 'Invalid validate request: no valid ticket or no valid service given' + false + else + if ticket.consumed? + logger.warn "Service ticket '#{ticket.ticket}' already consumed" + false + elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] + logger.warn "Service ticket '#{ticket.ticket}' has expired" + false + elsif clean_service_url(service) != ticket.service + logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'" + false + elsif renew && !ticket.issued_from_credentials? + logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" + false + else + logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated" + true + end + end + unless ticket.nil? + logger.debug "Consumed ticket '#{ticket.ticket}'" + ticket.consumed = true + ticket.save! + end + ticket_valid + end +end diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb new file mode 100644 index 00000000..d927e862 --- /dev/null +++ b/spec/processor/legacy_validator_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe CASinoCore::Processor::LegacyValidator do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:user_agent) { 'TestBrowser 1.0' } + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:service) { 'https://example.com/cas-service' } + let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } + let(:parameters) { { service: service, ticket: service_ticket.ticket }} + + before(:each) do + listener.stub(:validation_failed) + listener.stub(:validation_succeeded) + end + + context 'with an unconsumed service ticket' do + context 'without renew flag' do + it 'consumes the service ticket' do + processor.process(parameters) + service_ticket.reload + service_ticket.consumed.should == true + end + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with("yes\ntest\n") + processor.process(parameters) + end + end + + context 'with renew flag' do + let(:parameters) { { service: service, ticket: service_ticket.ticket, renew: 'true' }} + + context 'with a service ticket without issued_from_credentials flag' do + it 'consumes the service ticket' do + processor.process(parameters) + service_ticket.reload + service_ticket.consumed.should == true + end + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed).with("no\n\n") + processor.process(parameters) + end + end + + context 'with a service ticket with issued_from_credentials flag' do + before(:each) do + service_ticket.issued_from_credentials = true + service_ticket.save! + end + + it 'consumes the service ticket' do + processor.process(parameters) + service_ticket.reload + service_ticket.consumed.should == true + end + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with("yes\ntest\n") + processor.process(parameters) + end + end + end + end + + context 'with a consumed service ticket' do + before(:each) do + service_ticket.consumed = true + service_ticket.save! + end + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed).with("no\n\n") + processor.process(parameters) + end + end + end +end From c8ac58509864d986eae8ee0931a0bcf898aaf0a6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 21:42:16 +0100 Subject: [PATCH 056/350] fixed logging --- .../model/service_ticket/single_sign_out_notifier.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index f420215b..a96f94f7 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -38,15 +38,15 @@ def send_notification(uri, request) http.start do |conn| response = conn.request(request) if response.kind_of? Net::HTTPSuccess - Rails.logger.info "Logout notification successfully posted to #{uri}." + logger.info "Logout notification successfully posted to #{uri}." return true else - puts "Service #{uri} responed to logout notification with code '#{response.code}'!" + logger.warn "Service #{uri} responed to logout notification with code '#{response.code}'!" return false end end rescue Exception => e - puts "Failed to send logout notification to service #{uri} due to #{e}" + logger.warn "Failed to send logout notification to service #{uri} due to #{e}" return false end end From 71e67c36ef91602a8f22e21dc60d0eab5a857803 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 21:42:34 +0100 Subject: [PATCH 057/350] SessionDestroyer --- lib/casino_core/processor.rb | 5 +- .../processor/session_destroyer.rb | 17 +++++ spec/processor/session_destroyer_spec.rb | 68 +++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 lib/casino_core/processor/session_destroyer.rb create mode 100644 spec/processor/session_destroyer_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 4bee20c7..d77bd82a 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -2,9 +2,10 @@ module CASinoCore class Processor - autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' - autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' autoload :LegacyValidator, 'casino_core/processor/legacy_validator.rb' + autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' + autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' + autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' def initialize(listener) @listener = listener diff --git a/lib/casino_core/processor/session_destroyer.rb b/lib/casino_core/processor/session_destroyer.rb new file mode 100644 index 00000000..34809e12 --- /dev/null +++ b/lib/casino_core/processor/session_destroyer.rb @@ -0,0 +1,17 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor + include CASinoCore::Helper + + def process(tgt) + ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first + if ticket.nil? + @listener.ticket_not_found + else + ticket.destroy + @listener.ticket_deleted + end + end +end diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb new file mode 100644 index 00000000..9386533a --- /dev/null +++ b/spec/processor/session_destroyer_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe CASinoCore::Processor::SessionDestroyer do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:user_agent) { 'TestBrowser 1.0' } + let(:other_ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-ocCudGzZjJtrvOXJ485mt3', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + + before(:each) do + listener.stub(:ticket_deleted) + listener.stub(:ticket_not_found) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:tgt) { ticket_granting_ticket.ticket } + + it 'deletes only one ticket-granting ticket' do + ticket_granting_ticket + other_ticket_granting_ticket + lambda do + processor.process(tgt) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(-1) + end + + it 'deletes the ticket-granting ticket' do + processor.process(tgt) + CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).length.should == 0 + end + + it 'calls the #ticket_deleted method on the listener' do + listener.should_receive(:ticket_deleted).with(no_args) + processor.process(tgt) + end + end + + context 'with an invlaid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + + it 'does not delete a ticket-granting ticket' do + other_ticket_granting_ticket + lambda do + processor.process(tgt) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(0) + end + + it 'calls the #ticket_not_found method on the listener' do + listener.should_receive(:ticket_not_found).with(no_args) + processor.process(tgt) + end + end + end +end \ No newline at end of file From e121e10c577ef53e3b0d6eb222dfe92988d75bbf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 9 Dec 2012 21:51:05 +0100 Subject: [PATCH 058/350] fixed wording --- spec/authenticator/static_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/authenticator/static_spec.rb b/spec/authenticator/static_spec.rb index 212dbb25..7b08f335 100644 --- a/spec/authenticator/static_spec.rb +++ b/spec/authenticator/static_spec.rb @@ -18,7 +18,7 @@ subject.validate('foobar', 'test').should == false end - it 'returns false for a known username with wron password' do + it 'returns false for a known username with wrong password' do subject.validate('user', 'test').should == false end end From d5d3f963fc8110de95a3ec114dd0c7e6c903b69f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 10 Dec 2012 18:10:40 +0100 Subject: [PATCH 059/350] handle /logout --- lib/casino_core/processor.rb | 1 + lib/casino_core/processor/logout.rb | 23 ++++++++++++ spec/processor/logout_spec.rb | 55 +++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 lib/casino_core/processor/logout.rb create mode 100644 spec/processor/logout_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index d77bd82a..13b5914c 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -5,6 +5,7 @@ class Processor autoload :LegacyValidator, 'casino_core/processor/legacy_validator.rb' autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' + autoload :Logout, 'casino_core/processor/logout.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' def initialize(listener) diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb new file mode 100644 index 00000000..3167ba0e --- /dev/null +++ b/lib/casino_core/processor/logout.rb @@ -0,0 +1,23 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +class CASinoCore::Processor::Logout < CASinoCore::Processor + include CASinoCore::Helper + + def process(params = nil, cookies = nil) + params = params || {} + cookies ||= {} + session_destroyer = CASinoCore::Processor::SessionDestroyer.new(DummyListener.new) + session_destroyer.process(cookies[:tgt]) + @listener.user_logged_out(params[:url]) + end + + class DummyListener + def ticket_deleted(*args) + end + + def ticket_not_found(*args) + end + end +end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb new file mode 100644 index 00000000..c0a11f5b --- /dev/null +++ b/spec/processor/logout_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe CASinoCore::Processor::Logout do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + let(:url) { nil } + let(:params) { { :url => url } unless url.nil? } + + before(:each) do + listener.stub(:user_logged_out) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: nil, + user_agent: 'TestBrowser 1.0' + }) + } + let(:tgt) { ticket_granting_ticket.ticket } + + it 'calls the #process method of SessionDestroyer' do + CASinoCore::Processor::SessionDestroyer.any_instance.should_receive(:process).with(tgt) + processor.process(params, cookies) + end + + it 'calls the #user_logged_out method on the listener' do + listener.should_receive(:user_logged_out).with(nil) + processor.process(params, cookies) + end + + context 'with an URL' do + let(:url) { 'http://www.example.com' } + + it 'calls the #user_logged_out method on the listener and passes the URL' do + listener.should_receive(:user_logged_out).with(url) + processor.process(params, cookies) + end + end + end + + context 'with an invlaid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + + it 'calls the #user_logged_out method on the listener' do + listener.should_receive(:user_logged_out).with(nil) + processor.process(params, cookies) + end + end + end +end \ No newline at end of file From fce35e0e2280feb9d52cc9cd35833810bc1b57fa Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 16 Dec 2012 16:32:21 +0100 Subject: [PATCH 060/350] Regenerate gemspec for version 0.0.1 --- casino_core.gemspec | 60 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 22c3703a..1c3cb5d1 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-01" + s.date = "2012-12-16" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -20,16 +20,59 @@ Gem::Specification.new do |s| ".document", ".rspec", ".rvmrc", + ".travis.yml", "Gemfile", "Gemfile.lock", "LICENSE.txt", "README.md", "Rakefile", "VERSION", + "casino_core.gemspec", + "config/cas.yml", + "config/database.yml", + "db/migrate/20121112154930_create_ticket_granting_tickets.rb", + "db/migrate/20121112160009_create_login_tickets.rb", + "db/migrate/20121112165804_ticket_should_not_be_null.rb", + "db/migrate/20121122180310_add_user_agent_to_ticket_granting_tickets.rb", + "db/migrate/20121124170004_add_index_for_username_to_ticket_granting_tickets.rb", + "db/migrate/20121124183542_create_service_tickets.rb", + "db/migrate/20121124183732_add_ticket_indexes.rb", + "db/migrate/20121124195013_add_consumed_to_service_tickets.rb", + "db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb", + "db/migrate/20121125185415_create_proxy_granting_tickets.rb", + "db/migrate/20121125190013_tickets_should_be_unique.rb", + "db/schema.rb", "lib/casino_core.rb", "lib/casino_core/authenticator.rb", "lib/casino_core/authenticator/static.rb", + "lib/casino_core/helper.rb", + "lib/casino_core/helper/browser.rb", + "lib/casino_core/helper/service_tickets.rb", + "lib/casino_core/model.rb", + "lib/casino_core/model/login_ticket.rb", + "lib/casino_core/model/proxy_granting_ticket.rb", + "lib/casino_core/model/service_ticket.rb", + "lib/casino_core/model/service_ticket/single_sign_out_notifier.rb", + "lib/casino_core/model/ticket_granting_ticket.rb", + "lib/casino_core/processor.rb", + "lib/casino_core/processor/legacy_validator.rb", + "lib/casino_core/processor/login_credential_acceptor.rb", + "lib/casino_core/processor/login_credential_requestor.rb", + "lib/casino_core/processor/logout.rb", + "lib/casino_core/processor/session_destroyer.rb", + "lib/casino_core/railtie.rb", + "lib/casino_core/rake_tasks.rb", + "lib/casino_core/settings.rb", + "lib/casino_core/tasks/cleanup.rake", + "lib/casino_core/tasks/database.rake", "spec/authenticator/static_spec.rb", + "spec/model/login_ticket_spec.rb", + "spec/model/service_ticket_spec.rb", + "spec/processor/legacy_validator_spec.rb", + "spec/processor/login_credential_acceptor_spec.rb", + "spec/processor/login_credential_requestor_spec.rb", + "spec/processor/logout_spec.rb", + "spec/processor/session_destroyer_spec.rb", "spec/spec_helper.rb" ] s.homepage = "http://github.com/pencil/CASinoCore" @@ -42,33 +85,42 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["~> 2.0.2"]) s.add_runtime_dependency(%q, ["~> 3.2.9"]) + s.add_runtime_dependency(%q, ["~> 2.3.2"]) + s.add_runtime_dependency(%q, ["~> 0.4.13"]) s.add_development_dependency(%q, ["~> 1.2.0"]) s.add_development_dependency(%q, ["~> 1.8.4"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, ["~> 0.8.3"]) s.add_development_dependency(%q, ["~> 2.12.0"]) s.add_development_dependency(%q, ["~> 0.7.1"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) else - s.add_dependency(%q, ["~> 2.0.2"]) s.add_dependency(%q, ["~> 3.2.9"]) + s.add_dependency(%q, ["~> 2.3.2"]) + s.add_dependency(%q, ["~> 0.4.13"]) s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, ["~> 1.8.4"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 0.8.3"]) s.add_dependency(%q, ["~> 2.12.0"]) s.add_dependency(%q, ["~> 0.7.1"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end else - s.add_dependency(%q, ["~> 2.0.2"]) s.add_dependency(%q, ["~> 3.2.9"]) + s.add_dependency(%q, ["~> 2.3.2"]) + s.add_dependency(%q, ["~> 0.4.13"]) s.add_dependency(%q, ["~> 1.2.0"]) s.add_dependency(%q, ["~> 1.8.4"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 0.8.3"]) s.add_dependency(%q, ["~> 2.12.0"]) s.add_dependency(%q, ["~> 0.7.1"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end end From 4a8d392f6735f854855acd0da3cc6ed5ee383078 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 16 Dec 2012 18:27:11 +0100 Subject: [PATCH 061/350] automagically setup CASinoCore in Railtie --- lib/casino_core/railtie.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index 1eaf3c36..8e5e585f 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -6,5 +6,9 @@ class Railtie < Rails::Railtie rake_tasks do CASinoCore::RakeTasks.load_tasks end + + initializer "casino_core.load_configuration" do + CASinoCore.setup Rails.env + end end end From a91089706012dd0d1bc81827c412b7059a7b9896 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 16 Dec 2012 22:52:08 +0100 Subject: [PATCH 062/350] Removed obsolete TODO --- lib/casino_core/processor/login_credential_requestor.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index ea1ee416..21ea0184 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -11,7 +11,6 @@ def process(params = nil, cookies = nil, user_agent = nil) cookies ||= {} request_env ||= {} if !params[:renew] && (ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)) - # TODO create new service ticket and url service_url_with_ticket = unless params[:service].nil? acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url end From 01c72066721731b34dfdf56451108908cafc4535 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 10:34:53 +0100 Subject: [PATCH 063/350] Offer a fresh login ticket After an invalid login attempt, the listener will have to rerender the login page with a fresh login ticket --- lib/casino_core/helper.rb | 3 ++- lib/casino_core/helper/login_tickets.rb | 14 ++++++++++++++ .../processor/login_credential_acceptor.rb | 5 +++-- .../processor/login_credential_requestor.rb | 9 ++------- spec/processor/login_credential_acceptor_spec.rb | 4 ++-- 5 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 lib/casino_core/helper/login_tickets.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index e349c7f9..8288d364 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -3,8 +3,9 @@ module CASinoCore module Helper - autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' autoload :Browser, 'casino_core/helper/browser.rb' + autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' + autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' def random_ticket_string(prefix, length = 40) random_string = rand(36**length).to_s(36) diff --git a/lib/casino_core/helper/login_tickets.rb b/lib/casino_core/helper/login_tickets.rb new file mode 100644 index 00000000..c222786a --- /dev/null +++ b/lib/casino_core/helper/login_tickets.rb @@ -0,0 +1,14 @@ +require 'addressable/uri' + +module CASinoCore + module Helper + module LoginTickets + include CASinoCore::Helper + def acquire_login_ticket + ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT') + logger.debug "Created login ticket '#{ticket.ticket}'" + ticket + end + end + end +end diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index fd5f2eaf..a2af9cc3 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -3,6 +3,7 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper + include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets def process(params = nil, cookies = nil, user_agent = nil) @@ -17,10 +18,10 @@ def process(params = nil, cookies = nil, user_agent = nil) end @listener.user_logged_in(url, ticket_granting_ticket.ticket) else - @listener.invalid_login_credentials + @listener.invalid_login_credentials(acquire_login_ticket) end else - @listener.invalid_login_ticket + @listener.invalid_login_ticket(acquire_login_ticket) end end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 21ea0184..0661ed13 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -3,8 +3,9 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper - include CASinoCore::Helper::ServiceTickets include CASinoCore::Helper::Browser + include CASinoCore::Helper::LoginTickets + include CASinoCore::Helper::ServiceTickets def process(params = nil, cookies = nil, user_agent = nil) params ||= {} @@ -22,12 +23,6 @@ def process(params = nil, cookies = nil, user_agent = nil) end private - def acquire_login_ticket - ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT') - logger.debug "Created login ticket '#{ticket.ticket}'" - ticket - end - def find_valid_ticket_granting_ticket(tgt, user_agent) ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first unless ticket_granting_ticket.nil? diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index cb040107..8af8e20f 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -7,7 +7,7 @@ context 'without a valid login ticket' do it 'calls the #invalid_login_ticket method on the listener' do - listener.should_receive(:invalid_login_ticket).with(no_args) + listener.should_receive(:invalid_login_ticket).with(kind_of(CASinoCore::Model::LoginTicket)) processor.process end end @@ -17,7 +17,7 @@ context 'with invalid credentials' do it 'calls the #invalid_login_credentials method on the listener' do - listener.should_receive(:invalid_login_credentials).with(no_args) + listener.should_receive(:invalid_login_credentials).with(kind_of(CASinoCore::Model::LoginTicket)) processor.process(lt: login_ticket.ticket) end end From b8bf34a7dcea2c23e8e103bdd1b1ea0d9aa20dcb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 10:39:55 +0100 Subject: [PATCH 064/350] Extracted Logger and Tickets helper --- lib/casino_core/helper.rb | 14 ++------------ lib/casino_core/helper/browser.rb | 2 -- lib/casino_core/helper/logger.rb | 12 ++++++++++++ lib/casino_core/helper/login_tickets.rb | 6 +++--- lib/casino_core/helper/service_tickets.rb | 3 ++- lib/casino_core/helper/tickets.rb | 10 ++++++++++ lib/casino_core/processor/legacy_validator.rb | 2 +- .../processor/login_credential_acceptor.rb | 2 +- .../processor/login_credential_requestor.rb | 2 +- 9 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 lib/casino_core/helper/logger.rb create mode 100644 lib/casino_core/helper/tickets.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index 8288d364..a46c90d2 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -4,19 +4,9 @@ module CASinoCore module Helper autoload :Browser, 'casino_core/helper/browser.rb' + autoload :Logger, 'casino_core/helper/logger.rb' autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' - - def random_ticket_string(prefix, length = 40) - random_string = rand(36**length).to_s(36) - "#{prefix}-#{Time.now.to_i}-#{random_string}" - end - - def logger - # TODO this is just a "silent logger", make logger a setting! - logger = ::Logger.new(STDOUT) - logger.level = ::Logger::Severity::UNKNOWN - logger - end + autoload :Tickets, 'casino_core/helper/tickets.rb' end end diff --git a/lib/casino_core/helper/browser.rb b/lib/casino_core/helper/browser.rb index 52feb951..f7854c5a 100644 --- a/lib/casino_core/helper/browser.rb +++ b/lib/casino_core/helper/browser.rb @@ -1,5 +1,3 @@ -require 'addressable/uri' - module CASinoCore module Helper module Browser diff --git a/lib/casino_core/helper/logger.rb b/lib/casino_core/helper/logger.rb new file mode 100644 index 00000000..f7ed37dc --- /dev/null +++ b/lib/casino_core/helper/logger.rb @@ -0,0 +1,12 @@ +module CASinoCore + module Helper + module Logger + def logger + # TODO this is just a "silent logger", make logger a setting! + logger = ::Logger.new(STDOUT) + logger.level = ::Logger::Severity::UNKNOWN + logger + end + end + end +end diff --git a/lib/casino_core/helper/login_tickets.rb b/lib/casino_core/helper/login_tickets.rb index c222786a..e65385cc 100644 --- a/lib/casino_core/helper/login_tickets.rb +++ b/lib/casino_core/helper/login_tickets.rb @@ -1,9 +1,9 @@ -require 'addressable/uri' - module CASinoCore module Helper module LoginTickets - include CASinoCore::Helper + include CASinoCore::Helper::Logger + include CASinoCore::Helper::Tickets + def acquire_login_ticket ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT') logger.debug "Created login ticket '#{ticket.ticket}'" diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index b2388fbc..e47cc29b 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -3,7 +3,8 @@ module CASinoCore module Helper module ServiceTickets - include CASinoCore::Helper + include CASinoCore::Helper::Logger + include CASinoCore::Helper::Tickets def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied = nil) ticket_granting_ticket.service_tickets.create!({ diff --git a/lib/casino_core/helper/tickets.rb b/lib/casino_core/helper/tickets.rb new file mode 100644 index 00000000..c049e5bf --- /dev/null +++ b/lib/casino_core/helper/tickets.rb @@ -0,0 +1,10 @@ +module CASinoCore + module Helper + module Tickets + def random_ticket_string(prefix, length = 40) + random_string = rand(36**length).to_s(36) + "#{prefix}-#{Time.now.to_i}-#{random_string}" + end + end + end +end diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index dd7ec4ca..fcfb549a 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -3,7 +3,7 @@ require 'casino_core/model' class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor - include CASinoCore::Helper + include CASinoCore::Helper::Logger include CASinoCore::Helper::ServiceTickets def process(params = nil) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index a2af9cc3..2bd04dd5 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -2,7 +2,7 @@ require 'casino_core/helper' class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor - include CASinoCore::Helper + include CASinoCore::Helper::Logger include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 0661ed13..11d2a843 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -2,8 +2,8 @@ require 'casino_core/helper' class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor - include CASinoCore::Helper include CASinoCore::Helper::Browser + include CASinoCore::Helper::Logger include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets From eb3d016d287524ae65589a8bbe1b97398933ec3e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 10:40:12 +0100 Subject: [PATCH 065/350] Version bump to 0.0.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8a9ecc2e..7bcd0e36 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1 \ No newline at end of file +0.0.2 \ No newline at end of file From 4ea7e718d24efd1903f9fac1bd28123d8faafb3d Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 11:07:22 +0100 Subject: [PATCH 066/350] Regenerate gemspec for version 0.0.2 --- casino_core.gemspec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 1c3cb5d1..7badf105 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.1" + s.version = "0.0.2" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-16" + s.date = "2012-12-17" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -47,7 +47,10 @@ Gem::Specification.new do |s| "lib/casino_core/authenticator/static.rb", "lib/casino_core/helper.rb", "lib/casino_core/helper/browser.rb", + "lib/casino_core/helper/logger.rb", + "lib/casino_core/helper/login_tickets.rb", "lib/casino_core/helper/service_tickets.rb", + "lib/casino_core/helper/tickets.rb", "lib/casino_core/model.rb", "lib/casino_core/model/login_ticket.rb", "lib/casino_core/model/proxy_granting_ticket.rb", From 18b35c09684eeb0d9668674a69113636adf8be3a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 13:28:42 +0100 Subject: [PATCH 067/350] Extracted find_valid_ticket_granting_ticket --- lib/casino_core/helper.rb | 1 + .../helper/ticket_granting_tickets.rb | 24 +++++++++++++++++++ .../processor/login_credential_requestor.rb | 16 +------------ 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 lib/casino_core/helper/ticket_granting_tickets.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index a46c90d2..552d377a 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -8,5 +8,6 @@ module Helper autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' autoload :Tickets, 'casino_core/helper/tickets.rb' + autoload :TicketGrantingTickets, 'casino_core/helper/ticket_granting_tickets.rb' end end diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb new file mode 100644 index 00000000..b0b5eb44 --- /dev/null +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -0,0 +1,24 @@ +require 'addressable/uri' + +module CASinoCore + module Helper + module TicketGrantingTickets + include CASinoCore::Helper::Browser + include CASinoCore::Helper::Logger + + def find_valid_ticket_granting_ticket(tgt, user_agent) + ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first + unless ticket_granting_ticket.nil? + if same_browser?(ticket_granting_ticket.user_agent, user_agent) + ticket_granting_ticket.user_agent = user_agent + ticket_granting_ticket.save! + ticket_granting_ticket + else + logger.info 'User-Agent changed: ticket-granting ticket not valid for this browser' + nil + end + end + end + end + end +end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 11d2a843..ef8dd658 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -6,6 +6,7 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper::Logger include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::TicketGrantingTickets def process(params = nil, cookies = nil, user_agent = nil) params ||= {} @@ -21,19 +22,4 @@ def process(params = nil, cookies = nil, user_agent = nil) @listener.user_not_logged_in(login_ticket) end end - - private - def find_valid_ticket_granting_ticket(tgt, user_agent) - ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first - unless ticket_granting_ticket.nil? - if same_browser?(ticket_granting_ticket.user_agent, user_agent) - ticket_granting_ticket.user_agent = user_agent - ticket_granting_ticket.save! - ticket_granting_ticket - else - logger.info 'User-Agent changed: ticket-granting ticket not valid for this browser' - nil - end - end - end end From e84a9786d04001ea61151ba2676c7c9c76466510 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 14:00:19 +0100 Subject: [PATCH 068/350] SessionOverview processor --- lib/casino_core/processor.rb | 1 + lib/casino_core/processor/session_overview.rb | 25 ++++++++++ spec/processor/session_overview_spec.rb | 50 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 lib/casino_core/processor/session_overview.rb create mode 100644 spec/processor/session_overview_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 13b5914c..5cda3646 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -7,6 +7,7 @@ class Processor autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :Logout, 'casino_core/processor/logout.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' + autoload :SessionOverview, 'casino_core/processor/session_overview.rb' def initialize(listener) @listener = listener diff --git a/lib/casino_core/processor/session_overview.rb b/lib/casino_core/processor/session_overview.rb new file mode 100644 index 00000000..1f39b5ef --- /dev/null +++ b/lib/casino_core/processor/session_overview.rb @@ -0,0 +1,25 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The SessionOverview processor to list all open session for the currently signed in user. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::SessionOverview < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. + # @param [Hash] cookies cookies delivered by the client + # @param [String] user_agent user-agent delivered by the client + def process(cookies = nil, user_agent = nil) + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + if tgt.nil? + @listener.user_not_logged_in + else + ticket_granting_tickets = CASinoCore::Model::TicketGrantingTicket.where(username: tgt.username).order('updated_at DESC') + @listener.ticket_granting_tickets_found(ticket_granting_tickets) + end + end +end diff --git a/spec/processor/session_overview_spec.rb b/spec/processor/session_overview_spec.rb new file mode 100644 index 00000000..47887b05 --- /dev/null +++ b/spec/processor/session_overview_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe CASinoCore::Processor::SessionOverview do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:user_agent) { 'TestBrowser 1.0' } + let(:other_ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-ocCudGzZjJtrvOXJ485mt3', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:ticket_granting_tickets_found) + other_ticket_granting_ticket + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:tgt) { ticket_granting_ticket.ticket } + it 'calls the #ticket_granting_tickets_found method on the listener' do + listener.should_receive(:ticket_granting_tickets_found) do |tickets| + tickets.length.should == 2 + end + processor.process(cookies, user_agent) + end + end + + context 'with an invalid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process(cookies, user_agent) + end + end + end +end \ No newline at end of file From 903d0563a02b36abcaaa99b75ebbdb7e85077318 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 14:18:09 +0100 Subject: [PATCH 069/350] Added method to extract some human-readable browser info --- lib/casino_core/model/ticket_granting_ticket.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 50dbdc63..4196e69a 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -6,4 +6,9 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base validates :ticket, uniqueness: true has_many :service_tickets has_many :proxy_granting_tickets + + def browser_info + user_agent = UserAgent.parse(self.user_agent) + "#{user_agent.browser} (#{user_agent.platform})" + end end From 1769d0fe50315bb97446ac219ae44b57ba5fc9c8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 14:18:30 +0100 Subject: [PATCH 070/350] Version bump to 0.0.3 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7bcd0e36..6812f812 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.2 \ No newline at end of file +0.0.3 \ No newline at end of file From 004be9085ef1036c923917acb2a7c20281d8fd7c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 14:18:35 +0100 Subject: [PATCH 071/350] Regenerate gemspec for version 0.0.3 --- casino_core.gemspec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 7badf105..57454105 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.2" + s.version = "0.0.3" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] @@ -50,6 +50,7 @@ Gem::Specification.new do |s| "lib/casino_core/helper/logger.rb", "lib/casino_core/helper/login_tickets.rb", "lib/casino_core/helper/service_tickets.rb", + "lib/casino_core/helper/ticket_granting_tickets.rb", "lib/casino_core/helper/tickets.rb", "lib/casino_core/model.rb", "lib/casino_core/model/login_ticket.rb", @@ -63,6 +64,7 @@ Gem::Specification.new do |s| "lib/casino_core/processor/login_credential_requestor.rb", "lib/casino_core/processor/logout.rb", "lib/casino_core/processor/session_destroyer.rb", + "lib/casino_core/processor/session_overview.rb", "lib/casino_core/railtie.rb", "lib/casino_core/rake_tasks.rb", "lib/casino_core/settings.rb", @@ -76,6 +78,7 @@ Gem::Specification.new do |s| "spec/processor/login_credential_requestor_spec.rb", "spec/processor/logout_spec.rb", "spec/processor/session_destroyer_spec.rb", + "spec/processor/session_overview_spec.rb", "spec/spec_helper.rb" ] s.homepage = "http://github.com/pencil/CASinoCore" From fc1e8b45cb13ce0be8d1c413f9f2dd54bccadb60 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 17 Dec 2012 21:13:30 +0100 Subject: [PATCH 072/350] Documentation of SessionDestroyer --- lib/casino_core/processor/session_destroyer.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/processor/session_destroyer.rb b/lib/casino_core/processor/session_destroyer.rb index 34809e12..fcd88fa9 100644 --- a/lib/casino_core/processor/session_destroyer.rb +++ b/lib/casino_core/processor/session_destroyer.rb @@ -2,9 +2,15 @@ require 'casino_core/helper' require 'casino_core/model' +# The SessionDestroyer processor is used to destroy a ticket-granting ticket. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. It is especially useful in +# combination with the {CASinoCore::Processor::SessionOverview} processor. class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor - include CASinoCore::Helper + # This method will call `#ticket_not_found` or `#ticket_deleted` on the listener. + # @param [String] tgt ticket-granting ticket def process(tgt) ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first if ticket.nil? From dfd4ccf60191b1e519b73470efa630bae7a28a5f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 13:50:13 +0100 Subject: [PATCH 073/350] Check ownership of ticket-granting ticket before logout --- lib/casino_core/processor/logout.rb | 20 +++++++------------- spec/processor/logout_spec.rb | 13 +++++++------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb index 3167ba0e..9632afa2 100644 --- a/lib/casino_core/processor/logout.rb +++ b/lib/casino_core/processor/logout.rb @@ -3,21 +3,15 @@ require 'casino_core/model' class CASinoCore::Processor::Logout < CASinoCore::Processor - include CASinoCore::Helper + include CASinoCore::Helper::TicketGrantingTickets - def process(params = nil, cookies = nil) - params = params || {} + def process(params = nil, cookies = nil, user_agent = nil) + params ||= {} cookies ||= {} - session_destroyer = CASinoCore::Processor::SessionDestroyer.new(DummyListener.new) - session_destroyer.process(cookies[:tgt]) - @listener.user_logged_out(params[:url]) - end - - class DummyListener - def ticket_deleted(*args) - end - - def ticket_not_found(*args) + ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + unless ticket_granting_ticket.nil? + ticket_granting_ticket.destroy end + @listener.user_logged_out(params[:url]) end end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index c0a11f5b..aa5cee99 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -7,6 +7,7 @@ let(:cookies) { { tgt: tgt } } let(:url) { nil } let(:params) { { :url => url } unless url.nil? } + let(:user_agent) { 'TestBrowser 1.0' } before(:each) do listener.stub(:user_logged_out) @@ -18,19 +19,19 @@ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', username: 'test', extra_attributes: nil, - user_agent: 'TestBrowser 1.0' + user_agent: user_agent }) } let(:tgt) { ticket_granting_ticket.ticket } - it 'calls the #process method of SessionDestroyer' do - CASinoCore::Processor::SessionDestroyer.any_instance.should_receive(:process).with(tgt) - processor.process(params, cookies) + it 'deletes the ticket-granting ticket' do + processor.process(params, cookies, user_agent) + CASinoCore::Model::TicketGrantingTicket.where(id: ticket_granting_ticket.id).first.should == nil end it 'calls the #user_logged_out method on the listener' do listener.should_receive(:user_logged_out).with(nil) - processor.process(params, cookies) + processor.process(params, cookies, user_agent) end context 'with an URL' do @@ -38,7 +39,7 @@ it 'calls the #user_logged_out method on the listener and passes the URL' do listener.should_receive(:user_logged_out).with(url) - processor.process(params, cookies) + processor.process(params, cookies, user_agent) end end end From e1ab98855248b670f08441064252cc63222bc9e4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 13:50:30 +0100 Subject: [PATCH 074/350] Improved security of SessionDestroyer --- .../processor/session_destroyer.rb | 13 +++-- spec/processor/session_destroyer_spec.rb | 47 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/lib/casino_core/processor/session_destroyer.rb b/lib/casino_core/processor/session_destroyer.rb index fcd88fa9..54f3d30b 100644 --- a/lib/casino_core/processor/session_destroyer.rb +++ b/lib/casino_core/processor/session_destroyer.rb @@ -10,10 +10,15 @@ class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor # This method will call `#ticket_not_found` or `#ticket_deleted` on the listener. - # @param [String] tgt ticket-granting ticket - def process(tgt) - ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first - if ticket.nil? + # @param [Hash] params parameters supplied by user (ID of ticket-granting ticket to delete should by in params[:id]) + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client + def process(params = nil, cookies = nil, user_agent = nil) + params ||= {} + cookies ||= {} + ticket = CASinoCore::Model::TicketGrantingTicket.where(id: params[:id]).first + owner_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: cookies[:tgt]).first + if ticket.nil? || owner_ticket.nil? || ticket.username != owner_ticket.username @listener.ticket_not_found else ticket.destroy diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index 9386533a..5b659e15 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -5,7 +5,7 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } let(:user_agent) { 'TestBrowser 1.0' } - let(:other_ticket_granting_ticket) { + let(:owner_ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-ocCudGzZjJtrvOXJ485mt3', username: 'test', @@ -13,6 +13,7 @@ user_agent: user_agent }) } + let(:cookies) { { tgt: owner_ticket_granting_ticket.ticket } } before(:each) do listener.stub(:ticket_deleted) @@ -28,40 +29,64 @@ user_agent: user_agent }) } - let(:tgt) { ticket_granting_ticket.ticket } + let(:params) { { id: ticket_granting_ticket.id } } it 'deletes only one ticket-granting ticket' do ticket_granting_ticket - other_ticket_granting_ticket + owner_ticket_granting_ticket lambda do - processor.process(tgt) + processor.process(params, cookies, user_agent) end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(-1) end it 'deletes the ticket-granting ticket' do - processor.process(tgt) - CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).length.should == 0 + processor.process(params, cookies, user_agent) + CASinoCore::Model::TicketGrantingTicket.where(id: params[:id]).length.should == 0 end it 'calls the #ticket_deleted method on the listener' do listener.should_receive(:ticket_deleted).with(no_args) - processor.process(tgt) + processor.process(params, cookies, user_agent) end end context 'with an invlaid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } + let(:params) { { id: 99999 } } + it 'does not delete a ticket-granting ticket' do + owner_ticket_granting_ticket + lambda do + processor.process(params, cookies, user_agent) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(0) + end + + it 'calls the #ticket_not_found method on the listener' do + listener.should_receive(:ticket_not_found).with(no_args) + processor.process(params, cookies, user_agent) + end + end + + context 'when trying to delete ticket-granting ticket of another user' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'this_is_another_user', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:params) { { id: ticket_granting_ticket.id } } it 'does not delete a ticket-granting ticket' do - other_ticket_granting_ticket + owner_ticket_granting_ticket + ticket_granting_ticket lambda do - processor.process(tgt) + processor.process(params, cookies, user_agent) end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(0) end it 'calls the #ticket_not_found method on the listener' do listener.should_receive(:ticket_not_found).with(no_args) - processor.process(tgt) + processor.process(params, cookies, user_agent) end end end From c8a7e039b03bc9364629d6d5c022a78dc17d2b62 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 13:53:59 +0100 Subject: [PATCH 075/350] Extracted ownership check --- lib/casino_core/model/ticket_granting_ticket.rb | 8 ++++++++ lib/casino_core/processor/session_destroyer.rb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 4196e69a..7fc18b59 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -11,4 +11,12 @@ def browser_info user_agent = UserAgent.parse(self.user_agent) "#{user_agent.browser} (#{user_agent.platform})" end + + def same_user?(other_ticket) + if other_ticket.nil? + false + else + other_ticket.username == self.username + end + end end diff --git a/lib/casino_core/processor/session_destroyer.rb b/lib/casino_core/processor/session_destroyer.rb index 54f3d30b..28d1cf2d 100644 --- a/lib/casino_core/processor/session_destroyer.rb +++ b/lib/casino_core/processor/session_destroyer.rb @@ -18,7 +18,7 @@ def process(params = nil, cookies = nil, user_agent = nil) cookies ||= {} ticket = CASinoCore::Model::TicketGrantingTicket.where(id: params[:id]).first owner_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: cookies[:tgt]).first - if ticket.nil? || owner_ticket.nil? || ticket.username != owner_ticket.username + if ticket.nil? || !ticket.same_user?(owner_ticket) @listener.ticket_not_found else ticket.destroy From eb0a90085ad2c6105e419195a69171b6de79949e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 13:55:15 +0100 Subject: [PATCH 076/350] Version bump to 0.0.4 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 6812f812..05b19b1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.3 \ No newline at end of file +0.0.4 \ No newline at end of file From d6b1df90a2dc6c99dd37788818ad38feca6efa04 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 13:56:02 +0100 Subject: [PATCH 077/350] Regenerate gemspec for version 0.0.4 --- casino_core.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 57454105..723305ca 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.3" + s.version = "0.0.4" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-17" + s.date = "2012-12-19" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ From 121fb4de4fba9bff66738d197f5ba4992e682151 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:19:20 +0100 Subject: [PATCH 078/350] Documented the Logout processor --- lib/casino_core/processor/logout.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb index 9632afa2..64d017f8 100644 --- a/lib/casino_core/processor/logout.rb +++ b/lib/casino_core/processor/logout.rb @@ -2,9 +2,18 @@ require 'casino_core/helper' require 'casino_core/model' +# The Logout processor should be used to process GET requests to /logout. class CASinoCore::Processor::Logout < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets + # This method will call `#user_logged_out` and may supply an URL that should be presented to the user. + # As per specification, the URL specified by "url" SHOULD be on the logout page with descriptive text. + # For example, "The application you just logged out of has provided a link it would like you to follow. + # Please click here to access http://www.go-back.edu/." + # + # @param [Hash] params parameters supplied by user + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} From c9dc6f4adc5428cd8469b6b82d2682d7dc8baf32 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:21:46 +0100 Subject: [PATCH 079/350] Documented the LegacyValidator processor --- lib/casino_core/processor/legacy_validator.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index fcfb549a..9a1958d4 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -2,10 +2,16 @@ require 'casino_core/helper' require 'casino_core/model' +# The LegacyValidator processor should be used for GET requests to /validate class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor include CASinoCore::Helper::Logger include CASinoCore::Helper::ServiceTickets + # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies + # a string as argument. The webapplication should present that string (and nothing else) to the + # requestor. + # + # @param [Hash] params parameters supplied by requestor (a service) def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first From 134e59033626d0ad9ddfca05b7cfcd2df194c8b7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:36:33 +0100 Subject: [PATCH 080/350] Use ticket in #to_s --- lib/casino_core/model/login_ticket.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/casino_core/model/login_ticket.rb b/lib/casino_core/model/login_ticket.rb index fd5f463b..7f6824ec 100644 --- a/lib/casino_core/model/login_ticket.rb +++ b/lib/casino_core/model/login_ticket.rb @@ -8,4 +8,8 @@ class CASinoCore::Model::LoginTicket < ActiveRecord::Base def self.cleanup self.delete_all(['created_at < ?', CASinoCore::Settings.login_ticket[:lifetime].seconds.ago]) end + + def to_s + self.ticket + end end From 4eee6f0c0a2442051006e91256d4ebefd718d7f8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:36:44 +0100 Subject: [PATCH 081/350] More documentation --- .../processor/login_credential_acceptor.rb | 13 +++++++++++++ .../processor/login_credential_requestor.rb | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 2bd04dd5..643e2456 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -1,11 +1,24 @@ require 'casino_core/processor' require 'casino_core/helper' +# This processor should be used for POST requests to /login class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper::Logger include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets + # Use this method to process the request. It expects the username in the parameter "username" and the password + # in "password". + # + # The method will call one of the following methods on the listener: + # * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to. + # The second argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". + # * `#invalid_login_ticket` and `#invalid_login_credentials`: The first argument is a LoginTicket. + # See {CASinoCore::Processor::LoginCredentialRequestor} for details. + # + # @param [Hash] params parameters supplied by user + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index ef8dd658..526c6c09 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -1,6 +1,7 @@ require 'casino_core/processor' require 'casino_core/helper' +# This processor should be used for GET requests to /login class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper::Browser include CASinoCore::Helper::Logger @@ -8,6 +9,15 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor include CASinoCore::Helper::ServiceTickets include CASinoCore::Helper::TicketGrantingTickets + # Use this method to process the request. + # + # The method will call one of the following methods on the listener: + # * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to. + # * `#user_not_logged_in`: The first argument is a LoginTicket. It should be stored in a hidden field with name "lt". + # + # @param [Hash] params parameters supplied by user + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} From 57ab2f38872bc95e57980ba3ec02158d985ccdb5 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:37:07 +0100 Subject: [PATCH 082/350] Version bump to 0.0.5 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 05b19b1f..fa3de586 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.4 \ No newline at end of file +0.0.5 \ No newline at end of file From b3d0279ad1c5d23e91370780a48f412e601e2789 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:37:14 +0100 Subject: [PATCH 083/350] Regenerate gemspec for version 0.0.5 --- casino_core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 723305ca..3becfa98 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.4" + s.version = "0.0.5" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] From 71aa081378565bec9d6be238635c718b38571dfb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 14:41:17 +0100 Subject: [PATCH 084/350] Updated gems --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f890f9c1..b81a1744 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,8 +25,8 @@ GEM rake rdoc json (1.7.5) - multi_json (1.4.0) - rake (10.0.2) + multi_json (1.5.0) + rake (10.0.3) rdoc (3.12) json (~> 1.4) redcarpet (2.2.2) @@ -34,8 +34,8 @@ GEM rspec-core (~> 2.12.0) rspec-expectations (~> 2.12.0) rspec-mocks (~> 2.12.0) - rspec-core (2.12.0) - rspec-expectations (2.12.0) + rspec-core (2.12.2) + rspec-expectations (2.12.1) diff-lcs (~> 1.1.3) rspec-mocks (2.12.0) simplecov (0.7.1) From 96b1e4f50888feed2062348043ba3928e84131e0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 19:17:53 +0100 Subject: [PATCH 085/350] Extracted service ticket validation --- lib/casino_core/helper/service_tickets.rb | 34 ++++++++++++++++++ lib/casino_core/processor/legacy_validator.rb | 35 ++----------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index e47cc29b..ba52b0d2 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -26,6 +26,40 @@ def clean_service_url(dirty_service) clean_service end + + def validate_service_ticket_for_service(ticket, service, renew = false) + result = if service.nil? or ticket.nil? + logger.warn 'Invalid validate request: no valid ticket or no valid service given' + 'INVALID_REQUEST' + else + if ticket.consumed? + logger.warn "Service ticket '#{ticket.ticket}' already consumed" + 'INVALID_TICKET' + elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] + logger.warn "Service ticket '#{ticket.ticket}' has expired" + 'INVALID_TICKET' + elsif clean_service_url(service) != ticket.service + logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'" + 'INVALID_SERVICE' + elsif renew && !ticket.issued_from_credentials? + logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" + 'INVALID_TICKET' + else + logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated" + true + end + end + unless ticket.nil? + logger.debug "Consumed ticket '#{ticket.ticket}'" + ticket.consumed = true + ticket.save! + end + result + end + + def service_ticket_valid_for_service?(ticket, service, renew = false) + validate_service_ticket_for_service(ticket, service, renew) == true + end end end end diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index 9a1958d4..370b3064 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -8,48 +8,17 @@ class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor include CASinoCore::Helper::ServiceTickets # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies - # a string as argument. The webapplication should present that string (and nothing else) to the + # a string as argument. The web application should present that string (and nothing else) to the # requestor. # # @param [Hash] params parameters supplied by requestor (a service) def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - if ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) + if service_ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n") else @listener.validation_failed("no\n\n") end end - - private - def ticket_valid_for_service?(ticket, service, renew = false) - ticket_valid = if service.nil? or ticket.nil? - logger.warn 'Invalid validate request: no valid ticket or no valid service given' - false - else - if ticket.consumed? - logger.warn "Service ticket '#{ticket.ticket}' already consumed" - false - elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] - logger.warn "Service ticket '#{ticket.ticket}' has expired" - false - elsif clean_service_url(service) != ticket.service - logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'" - false - elsif renew && !ticket.issued_from_credentials? - logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" - false - else - logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated" - true - end - end - unless ticket.nil? - logger.debug "Consumed ticket '#{ticket.ticket}'" - ticket.consumed = true - ticket.save! - end - ticket_valid - end end From 22ff5c66197aeb75b1fead497bee600a6f728ab1 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 19:18:20 +0100 Subject: [PATCH 086/350] Don't duplicate parameters --- spec/processor/legacy_validator_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb index d927e862..26a5a384 100644 --- a/spec/processor/legacy_validator_spec.rb +++ b/spec/processor/legacy_validator_spec.rb @@ -37,18 +37,18 @@ end context 'with renew flag' do - let(:parameters) { { service: service, ticket: service_ticket.ticket, renew: 'true' }} + let(:parameters_with_renew) { parameters.merge renew: 'true' } context 'with a service ticket without issued_from_credentials flag' do it 'consumes the service ticket' do - processor.process(parameters) + processor.process(parameters_with_renew) service_ticket.reload service_ticket.consumed.should == true end it 'calls the #validation_failed method on the listener' do listener.should_receive(:validation_failed).with("no\n\n") - processor.process(parameters) + processor.process(parameters_with_renew) end end @@ -59,14 +59,14 @@ end it 'consumes the service ticket' do - processor.process(parameters) + processor.process(parameters_with_renew) service_ticket.reload service_ticket.consumed.should == true end it 'calls the #validation_succeeded method on the listener' do listener.should_receive(:validation_succeeded).with("yes\ntest\n") - processor.process(parameters) + processor.process(parameters_with_renew) end end end From 6c793749de22bfd6b400843f787cd1ff2132a970 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 20:52:56 +0100 Subject: [PATCH 087/350] Logger is a setting now --- lib/casino_core/helper/logger.rb | 7 +++---- lib/casino_core/settings.rb | 6 +++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/casino_core/helper/logger.rb b/lib/casino_core/helper/logger.rb index f7ed37dc..4453959b 100644 --- a/lib/casino_core/helper/logger.rb +++ b/lib/casino_core/helper/logger.rb @@ -1,11 +1,10 @@ +require 'casino_core/settings' + module CASinoCore module Helper module Logger def logger - # TODO this is just a "silent logger", make logger a setting! - logger = ::Logger.new(STDOUT) - logger.level = ::Logger::Severity::UNKNOWN - logger + CASinoCore::Settings.logger end end end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 54b8f9e8..59e5eaa9 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,7 +3,7 @@ module CASinoCore class Settings class << self - attr_accessor :login_ticket, :service_ticket, :authenticators + attr_accessor :login_ticket, :service_ticket, :authenticators, :logger def init(config = {}) config.each do |key,value| if respond_to?("#{key}=") @@ -12,6 +12,10 @@ def init(config = {}) end end + def logger + @logger || ::Logger.new(STDOUT) + end + def authenticators=(authenticators) @authenticators = [] authenticators.each do |authenticator| From e94008f7b623d7695a0a7ac781906b9b36bd67fc Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 21:00:31 +0100 Subject: [PATCH 088/350] Use Rails.logger as logger --- lib/casino_core/railtie.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index 8e5e585f..68684190 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -7,8 +7,11 @@ class Railtie < Rails::Railtie CASinoCore::RakeTasks.load_tasks end - initializer "casino_core.load_configuration" do + initializer 'casino_core.load_configuration' do CASinoCore.setup Rails.env end + + initializer 'casino_core.setup_logger' do + CASinoCore::Settings.logger = Rails.logger end end From 872f6bf4ac010ea77dab11f2f78f6f62d0d2038b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 21:02:48 +0100 Subject: [PATCH 089/350] Setup default logger only once --- lib/casino_core/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 59e5eaa9..8dd5c7cd 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -13,7 +13,7 @@ def init(config = {}) end def logger - @logger || ::Logger.new(STDOUT) + @logger ||= ::Logger.new(STDOUT) end def authenticators=(authenticators) From 16d9be9bfe0f0b28471e9e2ef928266fd97b8c96 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 21:03:00 +0100 Subject: [PATCH 090/350] Don't log while running tests --- spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 19f127f9..eb0cbca0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'casino_core' require 'database_cleaner' +require 'logger' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. @@ -17,6 +18,7 @@ config.order = 'random' CASinoCore.setup 'test' + CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN config.before(:suite) do DatabaseCleaner.strategy = :transaction From 319078201081fef0f97b21570771995889efd3e9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 19 Dec 2012 21:09:11 +0100 Subject: [PATCH 091/350] Initial version of serviceValidate (WIP) --- lib/casino_core/helper.rb | 1 + .../helper/proxy_granting_tickets.rb | 45 +++++++++ lib/casino_core/processor.rb | 1 + .../processor/service_ticket_validator.rb | 46 +++++++++ .../service_ticket_validator_spec.rb | 99 +++++++++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 lib/casino_core/helper/proxy_granting_tickets.rb create mode 100644 lib/casino_core/processor/service_ticket_validator.rb create mode 100644 spec/processor/service_ticket_validator_spec.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index 552d377a..3d41a220 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -6,6 +6,7 @@ module Helper autoload :Browser, 'casino_core/helper/browser.rb' autoload :Logger, 'casino_core/helper/logger.rb' autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' + autoload :ProxyGrantingTickets, 'casino_core/helper/proxy_granting_tickets.rb' autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' autoload :Tickets, 'casino_core/helper/tickets.rb' autoload :TicketGrantingTickets, 'casino_core/helper/ticket_granting_tickets.rb' diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb new file mode 100644 index 00000000..6e4c1a8e --- /dev/null +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -0,0 +1,45 @@ +require 'addressable/uri' +require 'net/https' + +require 'casino_core/helper/logger' +require 'casino_core/helper/tickets' + +module CASinoCore + module Helper + module ProxyGrantingTickets + include CASinoCore::Helper::Logger + include CASinoCore::Helper::Tickets + + def acquire_proxy_granting_ticket(pgt_url, service_ticket) + begin + uri = URI.parse(pgt_url) + https = Net::HTTP.new(uri.host, uri.port) + https.use_ssl = true + + https.start do |conn| + pgt = service_ticket.proxy_granting_ticket.new({ + ticket: random_ticket_string('PGT'), + iou: random_ticket_string('PGTIOU') + }) + + uri.query_values = (uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) + + response = conn.request_get(uri.request_uri) + # TODO: follow redirects... 2.5.4 says that redirects MAY be followed + if response.code == 200 + # 3.4 (proxy-granting ticket IOU) + pgt.save! + logger.debug "Proxy-granting ticket generated for pgt_url '#{pgt_url}': #{pgt.inspect}" + return pgt + else + logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored." + end + end + rescue Exception => e + logger.warn "Exception while communication with proxy-granting ticket callback server: #{e.message}" + end + nil + end + end + end +end diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 5cda3646..c980016a 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -6,6 +6,7 @@ class Processor autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :Logout, 'casino_core/processor/logout.rb' + autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb new file mode 100644 index 00000000..df5ad5b4 --- /dev/null +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -0,0 +1,46 @@ +require 'builder' +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate +class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor + include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::ProxyGrantingTickets + + # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies + # a string as argument. The web application should present that string (and nothing else) to the + # requestor. The Content-Type should be set to 'text/xml; charset=utf-8' + # + # @param [Hash] params parameters delivered by the client + def process(params = nil) + params ||= {} + ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first + validation_result = validate_service_ticket_for_service(ticket, params[:service], !!params[:renew]) + if validation_result == true + options = { service_ticket: ticket } + unless params[:pgtUrl].nil? + options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) + end + @listener.validation_succeeded(build_xml(true, options)) + else + @listener.validation_failed(build_xml(false, error_code: validation_result, error_message: 'Validation failed')) + end + end + + private + def build_xml(success, options = {}) + xml = Builder::XmlMarkup.new(indent: 2) + xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response| + if success + ticket_granting_ticket = options[:service_ticket].ticket_granting_ticket + service_response.cas :authenticationSuccess do |authentication_success| + authentication_success.cas :user, ticket_granting_ticket.username + end + else + service_response.cas :authenticationFailure, options[:error_message], code: options[:error_code] + end + end + xml.target! + end +end diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb new file mode 100644 index 00000000..2b0d8c7f --- /dev/null +++ b/spec/processor/service_ticket_validator_spec.rb @@ -0,0 +1,99 @@ +require 'spec_helper' + +describe CASinoCore::Processor::ServiceTicketValidator do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:user_agent) { 'TestBrowser 1.0' } + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:service) { 'https://example.com/cas-service' } + let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } + let(:parameters) { { service: service, ticket: service_ticket.ticket }} + + let(:regex_failure) { /\A\ Date: Wed, 19 Dec 2012 22:40:43 +0100 Subject: [PATCH 092/350] Bugfixing --- lib/casino_core/railtie.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index 68684190..f756d5d8 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -13,5 +13,6 @@ class Railtie < Rails::Railtie initializer 'casino_core.setup_logger' do CASinoCore::Settings.logger = Rails.logger + end end end From 15b66bef4953bf1f60c21888c723bafc4d74f3d6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:02:11 +0100 Subject: [PATCH 093/350] Include PGTIOU in response --- lib/casino_core/processor/service_ticket_validator.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index df5ad5b4..b92e5299 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -36,6 +36,10 @@ def build_xml(success, options = {}) ticket_granting_ticket = options[:service_ticket].ticket_granting_ticket service_response.cas :authenticationSuccess do |authentication_success| authentication_success.cas :user, ticket_granting_ticket.username + if options[:proxy_granting_ticket] + proxy_granting_ticket = options[:proxy_granting_ticket] + authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou + end end else service_response.cas :authenticationFailure, options[:error_message], code: options[:error_code] From ea8da3c2a0242af9746e62cea9be64217149a35a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:02:21 +0100 Subject: [PATCH 094/350] Note about tests --- spec/processor/service_ticket_validator_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb index 2b0d8c7f..fb1353f5 100644 --- a/spec/processor/service_ticket_validator_spec.rb +++ b/spec/processor/service_ticket_validator_spec.rb @@ -81,6 +81,8 @@ listener.should_receive(:validation_succeeded).with(regex_success) processor.process(parameters_with_pgt_url) end + + # TODO use https://github.com/bblimke/webmock to test the HTTP calls end end From f0aa0eb41b2fc00a7ab27dea067df263e2ba3d23 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:17:31 +0100 Subject: [PATCH 095/350] Include extra attributes --- .../processor/service_ticket_validator.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index b92e5299..cc814f42 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -36,6 +36,13 @@ def build_xml(success, options = {}) ticket_granting_ticket = options[:service_ticket].ticket_granting_ticket service_response.cas :authenticationSuccess do |authentication_success| authentication_success.cas :user, ticket_granting_ticket.username + unless ticket_granting_ticket.extra_attributes.blank? + authentication_success.cas :attributes do |attributes| + ticket_granting_ticket.extra_attributes.each do |key, value| + serialize_extra_attribute(attributes, key, value) + end + end + end if options[:proxy_granting_ticket] proxy_granting_ticket = options[:proxy_granting_ticket] authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou @@ -47,4 +54,16 @@ def build_xml(success, options = {}) end xml.target! end + + def serialize_extra_attribute(builder, key, value) + if value.kind_of?(String) || value.kind_of?(Numeric) || value.kind_of?(Symbol) + builder.cas key, "#{value}" + elsif value.kind_of?(Numeric) + builder.cas key, value.to_s + else + builder.cas key do + builder.cdata! value.to_yaml + end + end + end end From f5891ec2d012fa6a88fd07bdfdc966ff2d86a87a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:18:39 +0100 Subject: [PATCH 096/350] Include an extra attribute --- config/cas.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/cas.yml b/config/cas.yml index 5dd82781..de056081 100644 --- a/config/cas.yml +++ b/config/cas.yml @@ -13,7 +13,8 @@ development: options: users: testuser: - password: foobar123 + password: "foobar123" + name: "Test User" test: <<: *defaults @@ -23,4 +24,5 @@ test: options: users: testuser: - password: foobar123 + password: "foobar123" + name: "Test User" From 5a30cabdf51c16a69e72b472268029c48cfb3e2b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:39:45 +0100 Subject: [PATCH 097/350] Gem 'webmock' --- Gemfile | 1 + Gemfile.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index bfddaee7..73fa0cb8 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ group :development, :test do gem 'simplecov', '~> 0.7.1' gem 'sqlite3' gem 'database_cleaner' + gem 'webmock' end gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index b81a1744..88a5c8f5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,6 +15,7 @@ GEM addressable (2.3.2) arel (3.0.2) builder (3.0.4) + crack (0.3.1) database_cleaner (0.9.1) diff-lcs (1.1.3) git (1.2.5) @@ -45,6 +46,9 @@ GEM sqlite3 (1.3.6) tzinfo (0.3.35) useragent (0.4.15) + webmock (1.9.0) + addressable (>= 2.2.7) + crack (>= 0.1.7) yard (0.8.3) PLATFORMS @@ -61,4 +65,5 @@ DEPENDENCIES simplecov (~> 0.7.1) sqlite3 useragent (~> 0.4.13) + webmock yard (~> 0.8.3) From 8fd763ee1b99919fc60d7ba5e32f303a28ba5608 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 13:40:13 +0100 Subject: [PATCH 098/350] Updated Gems --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 88a5c8f5..af3e3f15 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,7 +38,7 @@ GEM rspec-core (2.12.2) rspec-expectations (2.12.1) diff-lcs (~> 1.1.3) - rspec-mocks (2.12.0) + rspec-mocks (2.12.1) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) From 2a65aff4c37956a77a8a25cf441aa1d75ccd9018 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 14:18:26 +0100 Subject: [PATCH 099/350] Proxy-granting tickets belong to service tickets --- ...y_granting_tickets_belongs_to_service_ticket.rb | 14 ++++++++++++++ db/schema.rb | 14 +++++++------- lib/casino_core/model/proxy_granting_ticket.rb | 4 ++-- lib/casino_core/model/service_ticket.rb | 1 + lib/casino_core/model/ticket_granting_ticket.rb | 1 - 5 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb diff --git a/db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb b/db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb new file mode 100644 index 00000000..f510a214 --- /dev/null +++ b/db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb @@ -0,0 +1,14 @@ +require 'casino_core/model' + +class ProxyGrantingTicketsBelongsToServiceTicket < ActiveRecord::Migration + def change + CASinoCore::Model::ProxyGrantingTicket.delete_all + + remove_index :proxy_granting_tickets, :ticket_granting_ticket_id + remove_column :proxy_granting_tickets, :ticket_granting_ticket_id + + add_column :proxy_granting_tickets, :service_ticket_id, :integer + change_column :proxy_granting_tickets, :service_ticket_id, :integer, null: false + add_index :proxy_granting_tickets, :service_ticket_id + end +end diff --git a/db/schema.rb b/db/schema.rb index f781b742..29aec7ad 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121125190013) do +ActiveRecord::Schema.define(:version => 20121223135227) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -22,16 +22,16 @@ add_index "login_tickets", ["ticket"], :name => "index_login_tickets_on_ticket", :unique => true create_table "proxy_granting_tickets", :force => true do |t| - t.string "ticket", :null => false - t.string "iou", :null => false - t.integer "ticket_granting_ticket_id", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.string "ticket", :null => false + t.string "iou", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "service_ticket_id", :null => false end add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true + add_index "proxy_granting_tickets", ["service_ticket_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id" add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true - add_index "proxy_granting_tickets", ["ticket_granting_ticket_id"], :name => "index_proxy_granting_tickets_on_ticket_granting_ticket_id" create_table "service_tickets", :force => true do |t| t.string "ticket", :null => false diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb index 3f32ac5d..c5a789b7 100644 --- a/lib/casino_core/model/proxy_granting_ticket.rb +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -1,8 +1,8 @@ require 'casino_core/model' class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base - attr_accessible :iou, :ticket, :ticket_granting_ticket_id + attr_accessible :iou, :ticket, :service_ticket_id validates :ticket, uniqueness: true validates :iou, uniqueness: true - belongs_to :ticket_granting_ticket + belongs_to :service_ticket end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 8689ce9c..e2b297c8 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -9,6 +9,7 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base validates :ticket, uniqueness: true belongs_to :ticket_granting_ticket before_destroy :send_single_sing_out_notification + has_many :proxy_granting_tickets def self.cleanup_unconsumed self.delete_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 7fc18b59..b97e5d04 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -5,7 +5,6 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base serialize :extra_attributes, Hash validates :ticket, uniqueness: true has_many :service_tickets - has_many :proxy_granting_tickets def browser_info user_agent = UserAgent.parse(self.user_agent) From 7d3ddc26052a48e7c2d78157f0f3b2680c6657e4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 14:19:03 +0100 Subject: [PATCH 100/350] Bugfixing --- lib/casino_core/helper/proxy_granting_tickets.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index 6e4c1a8e..c759daf9 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -12,12 +12,12 @@ module ProxyGrantingTickets def acquire_proxy_granting_ticket(pgt_url, service_ticket) begin - uri = URI.parse(pgt_url) - https = Net::HTTP.new(uri.host, uri.port) + uri = Addressable::URI.parse(pgt_url) + https = Net::HTTP.new(uri.host, uri.port || 443) https.use_ssl = true https.start do |conn| - pgt = service_ticket.proxy_granting_ticket.new({ + pgt = service_ticket.proxy_granting_tickets.new({ ticket: random_ticket_string('PGT'), iou: random_ticket_string('PGTIOU') }) @@ -26,7 +26,7 @@ def acquire_proxy_granting_ticket(pgt_url, service_ticket) response = conn.request_get(uri.request_uri) # TODO: follow redirects... 2.5.4 says that redirects MAY be followed - if response.code == 200 + if "#{response.code}" == "200" # 3.4 (proxy-granting ticket IOU) pgt.save! logger.debug "Proxy-granting ticket generated for pgt_url '#{pgt_url}': #{pgt.inspect}" @@ -35,7 +35,7 @@ def acquire_proxy_granting_ticket(pgt_url, service_ticket) logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored." end end - rescue Exception => e + rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError logger.warn "Exception while communication with proxy-granting ticket callback server: #{e.message}" end nil From d6119991fe9722ce7902cf593ca435b4930f8941 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 14:19:22 +0100 Subject: [PATCH 101/350] Test communication with callback server --- .../service_ticket_validator_spec.rb | 24 ++++++++++++++++++- spec/spec_helper.rb | 3 ++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb index fb1353f5..6a9a6718 100644 --- a/spec/processor/service_ticket_validator_spec.rb +++ b/spec/processor/service_ticket_validator_spec.rb @@ -77,12 +77,34 @@ context 'with proxy-granting ticket callback server' do let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.com/' } + before(:each) do + stub_request(:get, /https:\/\/www\.example\.com\/\?pgtId=[^&]+&pgtIou=[^&]+/) + end + it 'calls the #validation_succeeded method on the listener' do listener.should_receive(:validation_succeeded).with(regex_success) processor.process(parameters_with_pgt_url) end - # TODO use https://github.com/bblimke/webmock to test the HTTP calls + it 'includes the PGTIOU in the response' do + listener.should_receive(:validation_succeeded).with(/\\n?\s*PGTIOU-.+/) + processor.process(parameters_with_pgt_url) + end + + it 'creates a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should change(service_ticket.proxy_granting_tickets, :count).by(1) + end + + it 'contacts the callback server' do + processor.process(parameters_with_pgt_url) + proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.last + WebMock.should have_requested(:get, 'https://www.example.com').with(query: { + pgtId: proxy_granting_ticket.ticket, + pgtIou: proxy_granting_ticket.iou + }) + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eb0cbca0..64754d6c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ -require 'casino_core' require 'database_cleaner' require 'logger' +require 'webmock/rspec' +require 'casino_core' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. From 505d5a27212ffd3a4f50bea7819672c579a09be4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 16:58:04 +0100 Subject: [PATCH 102/350] Test for #to_s --- spec/model/login_ticket_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/model/login_ticket_spec.rb b/spec/model/login_ticket_spec.rb index 0c0f78f9..dc0e6253 100644 --- a/spec/model/login_ticket_spec.rb +++ b/spec/model/login_ticket_spec.rb @@ -13,4 +13,11 @@ described_class.find_by_ticket('LT-12345').should be_false end end + + describe '#to_s' do + it 'returns the ticket identifier' do + ticket = described_class.new ticket: 'LT-12345' + "#{ticket}".should == ticket.ticket + end + end end From a7b6cf3adce87ef0fea4feef31a30a33f27da81e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 23 Dec 2012 18:56:06 +0100 Subject: [PATCH 103/350] Generate coverage report --- spec/spec_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 64754d6c..f6312827 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ require 'logger' require 'webmock/rspec' require 'casino_core' +require 'simplecov' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. @@ -18,6 +19,8 @@ # --seed 1234 config.order = 'random' + SimpleCov.start + CASinoCore.setup 'test' CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN From 407d86deeda0c37edc82d2dd9592409cbd47190e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 24 Dec 2012 11:02:50 +0100 Subject: [PATCH 104/350] Version bump to 0.0.6 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index fa3de586..99d85ecd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.5 \ No newline at end of file +0.0.6 \ No newline at end of file From d911cb67e6d11a123f92a5f3c7c310528be6d110 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 24 Dec 2012 11:02:54 +0100 Subject: [PATCH 105/350] Regenerate gemspec for version 0.0.6 --- casino_core.gemspec | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 3becfa98..34a270cf 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.5" + s.version = "0.0.6" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-19" + s.date = "2012-12-24" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -41,6 +41,7 @@ Gem::Specification.new do |s| "db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb", "db/migrate/20121125185415_create_proxy_granting_tickets.rb", "db/migrate/20121125190013_tickets_should_be_unique.rb", + "db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb", "db/schema.rb", "lib/casino_core.rb", "lib/casino_core/authenticator.rb", @@ -49,6 +50,7 @@ Gem::Specification.new do |s| "lib/casino_core/helper/browser.rb", "lib/casino_core/helper/logger.rb", "lib/casino_core/helper/login_tickets.rb", + "lib/casino_core/helper/proxy_granting_tickets.rb", "lib/casino_core/helper/service_tickets.rb", "lib/casino_core/helper/ticket_granting_tickets.rb", "lib/casino_core/helper/tickets.rb", @@ -63,6 +65,7 @@ Gem::Specification.new do |s| "lib/casino_core/processor/login_credential_acceptor.rb", "lib/casino_core/processor/login_credential_requestor.rb", "lib/casino_core/processor/logout.rb", + "lib/casino_core/processor/service_ticket_validator.rb", "lib/casino_core/processor/session_destroyer.rb", "lib/casino_core/processor/session_overview.rb", "lib/casino_core/railtie.rb", @@ -77,6 +80,7 @@ Gem::Specification.new do |s| "spec/processor/login_credential_acceptor_spec.rb", "spec/processor/login_credential_requestor_spec.rb", "spec/processor/logout_spec.rb", + "spec/processor/service_ticket_validator_spec.rb", "spec/processor/session_destroyer_spec.rb", "spec/processor/session_overview_spec.rb", "spec/spec_helper.rb" @@ -102,6 +106,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, ["~> 0.7.1"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) @@ -114,6 +119,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 0.7.1"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, ["~> 3.2.9"]) @@ -127,6 +133,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 0.7.1"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end end From 5a472e7be417b115e47ee48e5abb4b6f4a31ac47 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 24 Dec 2012 11:43:59 +0100 Subject: [PATCH 106/350] Added proxy tickets model and table --- db/migrate/20121224113737_create_proxy_tickets.rb | 15 +++++++++++++++ db/schema.rb | 14 +++++++++++++- lib/casino_core/model/proxy_ticket.rb | 10 ++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20121224113737_create_proxy_tickets.rb create mode 100644 lib/casino_core/model/proxy_ticket.rb diff --git a/db/migrate/20121224113737_create_proxy_tickets.rb b/db/migrate/20121224113737_create_proxy_tickets.rb new file mode 100644 index 00000000..6cdc43cc --- /dev/null +++ b/db/migrate/20121224113737_create_proxy_tickets.rb @@ -0,0 +1,15 @@ +class CreateProxyTickets < ActiveRecord::Migration + def change + create_table :proxy_tickets do |t| + t.string :ticket, null: false + t.string :service, null: false + t.boolean :consumed, null: false, default: false + t.integer :proxy_granting_ticket_id, null: false + + t.timestamps + end + + add_index :proxy_tickets, :ticket, unique: true + add_index :proxy_tickets, :proxy_granting_ticket_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 29aec7ad..a5eca11e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121223135227) do +ActiveRecord::Schema.define(:version => 20121224113737) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -33,6 +33,18 @@ add_index "proxy_granting_tickets", ["service_ticket_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id" add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true + create_table "proxy_tickets", :force => true do |t| + t.string "ticket", :null => false + t.string "service", :null => false + t.boolean "consumed", :default => false, :null => false + t.integer "proxy_granting_ticket_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "proxy_tickets", ["proxy_granting_ticket_id"], :name => "index_proxy_tickets_on_proxy_granting_ticket_id" + add_index "proxy_tickets", ["ticket"], :name => "index_proxy_tickets_on_ticket", :unique => true + create_table "service_tickets", :force => true do |t| t.string "ticket", :null => false t.string "service", :null => false diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb new file mode 100644 index 00000000..c4da42e4 --- /dev/null +++ b/lib/casino_core/model/proxy_ticket.rb @@ -0,0 +1,10 @@ +require 'casino_core/model' +require 'casino_core/settings' +require 'addressable/uri' + +class CASinoCore::Model::Proxy < ActiveRecord::Base + attr_accessible :ticket, :service + validates :ticket, uniqueness: true + belongs_to :proxy_granting_ticket + has_many :proxy_granting_tickets +end From 3b663940ee044425764f1c4fd76d5a452cd98411 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 24 Dec 2012 11:52:03 +0100 Subject: [PATCH 107/350] Always use #destroy but only send SSOut notification for consumed --- lib/casino_core/model/service_ticket.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index e2b297c8..baabdec7 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -8,11 +8,11 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base attr_accessible :ticket, :service validates :ticket, uniqueness: true belongs_to :ticket_granting_ticket - before_destroy :send_single_sing_out_notification + before_destroy :send_single_sing_out_notification, if: :consumed? has_many :proxy_granting_tickets def self.cleanup_unconsumed - self.delete_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) end def self.cleanup_consumed From db5c64dd22b12565178ad980f7dfeba1c311af00 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 24 Dec 2012 13:16:08 +0100 Subject: [PATCH 108/350] Validate host of callback URL (pgtUrl) As per CAS specification: > This is a callback URL of the service [...]. This URL MUST be HTTPS, > and CAS MUST verify [...] that its name matches that of the service --- .../helper/proxy_granting_tickets.rb | 58 +++++++++++-------- .../service_ticket_validator_spec.rb | 19 +++++- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index c759daf9..4640803c 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -12,34 +12,46 @@ module ProxyGrantingTickets def acquire_proxy_granting_ticket(pgt_url, service_ticket) begin - uri = Addressable::URI.parse(pgt_url) - https = Net::HTTP.new(uri.host, uri.port || 443) - https.use_ssl = true - - https.start do |conn| - pgt = service_ticket.proxy_granting_tickets.new({ - ticket: random_ticket_string('PGT'), - iou: random_ticket_string('PGTIOU') - }) - - uri.query_values = (uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) - - response = conn.request_get(uri.request_uri) - # TODO: follow redirects... 2.5.4 says that redirects MAY be followed - if "#{response.code}" == "200" - # 3.4 (proxy-granting ticket IOU) - pgt.save! - logger.debug "Proxy-granting ticket generated for pgt_url '#{pgt_url}': #{pgt.inspect}" - return pgt - else - logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored." - end + callback_uri = Addressable::URI.parse(pgt_url) + service_uri = Addressable::URI.parse(service_ticket.service) + + if service_uri.host != callback_uri.host + logger.warn "Proxy-granting ticket not created: Host of callback url '#{pgt_url}' does not match service '#{service_uri}'" + else + return contact_callback_server(callback_uri, service_ticket) end rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError - logger.warn "Exception while communication with proxy-granting ticket callback server: #{e.message}" + logger.warn "Exception while communicating with proxy-granting ticket callback server: #{e.message}" end nil end + + private + def contact_callback_server(callback_uri, service_ticket) + https = Net::HTTP.new(callback_uri.host, callback_uri.port || 443) + https.use_ssl = true + + https.start do |conn| + pgt = service_ticket.proxy_granting_tickets.new({ + ticket: random_ticket_string('PGT'), + iou: random_ticket_string('PGTIOU') + }) + + callback_uri.query_values = (callback_uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) + + response = conn.request_get(callback_uri.request_uri) + # TODO: follow redirects... 2.5.4 says that redirects MAY be followed + if "#{response.code}" == "200" + # 3.4 (proxy-granting ticket IOU) + pgt.save! + logger.debug "Proxy-granting ticket generated for service '#{service_ticket.service}': #{pgt.inspect}" + pgt + else + logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored." + nil + end + end + end end end end diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb index 6a9a6718..8bf63cce 100644 --- a/spec/processor/service_ticket_validator_spec.rb +++ b/spec/processor/service_ticket_validator_spec.rb @@ -13,7 +13,7 @@ user_agent: user_agent }) } - let(:service) { 'https://example.com/cas-service' } + let(:service) { 'https://www.example.com/cas-service' } let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } let(:parameters) { { service: service, ticket: service_ticket.ticket }} @@ -75,7 +75,7 @@ end context 'with proxy-granting ticket callback server' do - let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.com/' } + let(:parameters_with_pgt_url) { parameters.merge pgtUrl: "https://www.example.com" } before(:each) do stub_request(:get, /https:\/\/www\.example\.com\/\?pgtId=[^&]+&pgtIou=[^&]+/) @@ -106,6 +106,21 @@ }) end end + + context 'with proxy-granting ticket callback server not matching the service' do + let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.org/' } + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters_with_pgt_url) + end + + it 'does not create a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should_not change(service_ticket.proxy_granting_tickets, :count).by(1) + end + end end context 'with a consumed service ticket' do From fe77cf8f36015d3b6779a845b93a9905a577b987 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 11:54:50 +0100 Subject: [PATCH 109/350] Processor for /proxy --- lib/casino_core/helper.rb | 1 + lib/casino_core/helper/proxy_tickets.rb | 15 ++++ lib/casino_core/model.rb | 1 + .../model/proxy_granting_ticket.rb | 1 + lib/casino_core/model/proxy_ticket.rb | 2 +- lib/casino_core/processor.rb | 1 + .../processor/proxy_ticket_provider.rb | 44 +++++++++++ spec/processor/proxy_ticket_provider_spec.rb | 75 +++++++++++++++++++ .../service_ticket_validator_spec.rb | 2 +- 9 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 lib/casino_core/helper/proxy_tickets.rb create mode 100644 lib/casino_core/processor/proxy_ticket_provider.rb create mode 100644 spec/processor/proxy_ticket_provider_spec.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index 3d41a220..8614b56f 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -7,6 +7,7 @@ module Helper autoload :Logger, 'casino_core/helper/logger.rb' autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' autoload :ProxyGrantingTickets, 'casino_core/helper/proxy_granting_tickets.rb' + autoload :ProxyTickets, 'casino_core/helper/proxy_tickets.rb' autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' autoload :Tickets, 'casino_core/helper/tickets.rb' autoload :TicketGrantingTickets, 'casino_core/helper/ticket_granting_tickets.rb' diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb new file mode 100644 index 00000000..d47479d5 --- /dev/null +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -0,0 +1,15 @@ +module CASinoCore + module Helper + module ProxyTickets + include CASinoCore::Helper::Logger + include CASinoCore::Helper::Tickets + + def acquire_proxy_ticket(proxy_granting_ticket, service) + proxy_granting_ticket.proxy_tickets.create!({ + ticket: random_ticket_string('ST'), + service: service, + }) + end + end + end +end diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index e063681b..532e726f 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -5,6 +5,7 @@ module Model autoload :LoginTicket, 'casino_core/model/login_ticket.rb' autoload :ServiceTicket, 'casino_core/model/service_ticket.rb' autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb' + autoload :ProxyTicket, 'casino_core/model/proxy_ticket.rb' autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb' end end diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb index c5a789b7..6f8bd320 100644 --- a/lib/casino_core/model/proxy_granting_ticket.rb +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -5,4 +5,5 @@ class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base validates :ticket, uniqueness: true validates :iou, uniqueness: true belongs_to :service_ticket + has_many :proxy_tickets end diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index c4da42e4..fc423b23 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -2,7 +2,7 @@ require 'casino_core/settings' require 'addressable/uri' -class CASinoCore::Model::Proxy < ActiveRecord::Base +class CASinoCore::Model::ProxyTicket < ActiveRecord::Base attr_accessible :ticket, :service validates :ticket, uniqueness: true belongs_to :proxy_granting_ticket diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index c980016a..cefb41d6 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -6,6 +6,7 @@ class Processor autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :Logout, 'casino_core/processor/logout.rb' + autoload :ProxyTicketProvider, 'casino_core/processor/proxy_ticket_provider.rb' autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' diff --git a/lib/casino_core/processor/proxy_ticket_provider.rb b/lib/casino_core/processor/proxy_ticket_provider.rb new file mode 100644 index 00000000..30dd943d --- /dev/null +++ b/lib/casino_core/processor/proxy_ticket_provider.rb @@ -0,0 +1,44 @@ +require 'builder' +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The ProxyTicketProvider processor should be used to handle GET requests to /proxy +class CASinoCore::Processor::ProxyTicketProvider < CASinoCore::Processor + include CASinoCore::Helper::ProxyGrantingTickets + include CASinoCore::Helper::ProxyTickets + + # This method will call `#request_succeeded` or `#request_failed`. In both cases, it supplies + # a string as argument. The web application should present that string (and nothing else) to the + # requestor. The Content-Type should be set to 'text/xml; charset=utf-8' + # + # @param [Hash] params parameters delivered by the client + def process(params = nil) + if params[:pgt].nil? || params[:targetService].nil? + @listener.request_failed build_xml false, error_code: 'INVALID_REQUEST', error_message: '"pgt" and "targetService" parameters are both required' + else + proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.where(ticket: params[:pgt]).first + if proxy_granting_ticket.nil? + @listener.request_failed build_xml false, error_code: 'BAD_PGT', error_message: 'PGT not found' + else + proxy_ticket = acquire_proxy_ticket(proxy_granting_ticket, params[:targetService]) + @listener.request_succeeded build_xml true, proxy_ticket: proxy_ticket + end + end + end + + private + def build_xml(success, options = {}) + xml = Builder::XmlMarkup.new(indent: 2) + xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response| + if success + service_response.cas :proxySuccess do |proxy_success| + proxy_success.cas :proxyTicket, options[:proxy_ticket].ticket + end + else + service_response.cas :proxyFailure, options[:error_message], code: options[:error_code] + end + end + xml.target! + end +end diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb new file mode 100644 index 00000000..9345f8fd --- /dev/null +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe CASinoCore::Processor::ProxyTicketProvider do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:params) { { targetService: 'this_does_not_have_to_be_a_url' } } + + before(:each) do + listener.stub(:request_failed) + listener.stub(:request_succeeded) + end + + context 'without proxy-granting ticket' do + it 'calls the #request_failed method on the listener' do + listener.should_receive(:request_failed) + processor.process(params) + end + + it 'does not create a proxy ticket' do + lambda do + processor.process(params) + end.should_not change(CASinoCore::Model::ProxyTicket, :count) + end + end + + context 'with a not-existing proxy-granting ticket' do + let(:params_with_deleted_pgt) { params.merge(pgt: 'PGT-123453789') } + + it 'calls the #request_failed method on the listener' do + listener.should_receive(:request_failed) + processor.process(params_with_deleted_pgt) + end + + it 'does not create a proxy ticket' do + lambda do + processor.process(params_with_deleted_pgt) + end.should_not change(CASinoCore::Model::ProxyTicket, :count) + end + end + + context 'with a proxy-granting ticket' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-Qu6B5IVQ7RmLc972TruM9u', + username: 'test' + }) + } + let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: 'http://www.example.com/' } + let(:proxy_granting_ticket) { + service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg' + } + let(:params_with_valid_pgt) { params.merge(pgt: proxy_granting_ticket.ticket) } + + it 'calls the #request_succeeded method on the listener' do + listener.should_receive(:request_succeeded) + processor.process(params_with_valid_pgt) + end + + it 'does not create a proxy ticket' do + lambda do + processor.process(params_with_valid_pgt) + end.should change(proxy_granting_ticket.proxy_tickets, :count).by(1) + end + + it 'includes the proxy ticket in the response' do + listener.should_receive(:request_succeeded) do |response| + proxy_ticket = CASinoCore::Model::ProxyTicket.last + response.should =~ /#{proxy_ticket.ticket}<\/cas:proxyTicket>/ + end + processor.process(params_with_valid_pgt) + end + end + end +end diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb index 8bf63cce..c5dbab72 100644 --- a/spec/processor/service_ticket_validator_spec.rb +++ b/spec/processor/service_ticket_validator_spec.rb @@ -118,7 +118,7 @@ it 'does not create a proxy-granting ticket' do lambda do processor.process(parameters_with_pgt_url) - end.should_not change(service_ticket.proxy_granting_tickets, :count).by(1) + end.should_not change(service_ticket.proxy_granting_tickets, :count) end end end From 324a16d4bd5787f10f0a07221b8423e93c2f8b98 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 12:02:54 +0100 Subject: [PATCH 110/350] Cleanup methods for proxy tickets --- config/cas.yml | 3 +++ lib/casino_core/model/proxy_ticket.rb | 8 +++++++ lib/casino_core/model/service_ticket.rb | 2 +- lib/casino_core/settings.rb | 2 +- lib/casino_core/tasks/cleanup.rake | 12 ++++++++-- spec/model/proxy_ticket_spec.rb | 32 +++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 spec/model/proxy_ticket_spec.rb diff --git a/config/cas.yml b/config/cas.yml index de056081..3a1fa581 100644 --- a/config/cas.yml +++ b/config/cas.yml @@ -4,6 +4,9 @@ defaults: &defaults service_ticket: lifetime_unconsumed: 300 lifetime_consumed: 86400 + proxy_ticket: + lifetime_unconsumed: 300 + lifetime_consumed: 86400 development: <<: *defaults diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index fc423b23..2366f588 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -7,4 +7,12 @@ class CASinoCore::Model::ProxyTicket < ActiveRecord::Base validates :ticket, uniqueness: true belongs_to :proxy_granting_ticket has_many :proxy_granting_tickets + + def self.cleanup_unconsumed + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed].seconds.ago, false]) + end + + def self.cleanup_consumed + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_consumed].seconds.ago, true]) + end end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index baabdec7..4d80930d 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -16,7 +16,7 @@ def self.cleanup_unconsumed end def self.cleanup_consumed - self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]).length + self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) end def service_with_ticket_url diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 8dd5c7cd..35c73608 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,7 +3,7 @@ module CASinoCore class Settings class << self - attr_accessor :login_ticket, :service_ticket, :authenticators, :logger + attr_accessor :login_ticket, :service_ticket, :proxy_ticket, :authenticators, :logger def init(config = {}) config.each do |key,value| if respond_to?("#{key}=") diff --git a/lib/casino_core/tasks/cleanup.rake b/lib/casino_core/tasks/cleanup.rake index 192a9316..e556b38d 100644 --- a/lib/casino_core/tasks/cleanup.rake +++ b/lib/casino_core/tasks/cleanup.rake @@ -8,11 +8,19 @@ namespace :casino_core do desc 'Remove expired service tickets.' task service_tickets: 'casino_core:db:configure_connection' do [:consumed, :unconsumed].each do |type| - rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}") + rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}").length puts "Deleted #{rows_affected} #{type} service tickets." end end + desc 'Remove expired proxy tickets.' + task proxy_tickets: 'casino_core:db:configure_connection' do + [:consumed, :unconsumed].each do |type| + rows_affected = CASinoCore::Model::ProxyTicket.send("cleanup_#{type}").length + puts "Deleted #{rows_affected} #{type} proxy tickets." + end + end + desc 'Remove expired login tickets.' task login_tickets: 'casino_core:db:configure_connection' do rows_affected = CASinoCore::Model::LoginTicket.cleanup @@ -20,7 +28,7 @@ namespace :casino_core do end desc 'Perform all cleanup tasks.' - task all: [:service_tickets, :login_tickets] do + task all: [:service_tickets, :proxy_tickets, :login_tickets] do end end end diff --git a/spec/model/proxy_ticket_spec.rb b/spec/model/proxy_ticket_spec.rb new file mode 100644 index 00000000..d2825db0 --- /dev/null +++ b/spec/model/proxy_ticket_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe CASinoCore::Model::ProxyTicket do + let(:ticket) { + ticket = described_class.new ticket: 'PT-12345', service: 'any string is valid' + ticket.proxy_granting_ticket_id = 1 + ticket + } + + describe '.cleanup_unconsumed' do + it 'deletes expired unconsumed service tickets' do + ticket.created_at = 10.hours.ago + ticket.save! + lambda do + described_class.cleanup_unconsumed + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('ST-12345').should be_false + end + end + + describe '.cleanup_consumed' do + it 'deletes expired consumed service tickets' do + ticket.consumed = true + ticket.created_at = 10.days.ago + ticket.save! + lambda do + described_class.cleanup_consumed + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('ST-12345').should be_false + end + end +end From a5f43ef2284a01a726ae80a99f6f86ca67a94b98 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 15:57:23 +0100 Subject: [PATCH 111/350] Make rollback work --- lib/casino_core/tasks/database.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index 696d5387..27795db7 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -33,7 +33,7 @@ namespace :casino_core do end desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' - task :rollback => [:environment, :load_config] do + task :rollback => :configure_connection do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) end From d40af31897181bc5c1126ff7941f58738a59d783 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 15:02:13 +0100 Subject: [PATCH 112/350] Test authenticator base class --- spec/authenticator/base_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 spec/authenticator/base_spec.rb diff --git a/spec/authenticator/base_spec.rb b/spec/authenticator/base_spec.rb new file mode 100644 index 00000000..85b65ea5 --- /dev/null +++ b/spec/authenticator/base_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe CASinoCore::Authenticator do + subject { + CASinoCore::Authenticator.new + } + + context '#validate' do + it 'should raise an error' do + expect { subject.validate }.to raise_error + end + end +end From 52c764aac94ac00e2706ba8169feae5ca81a4d5e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 10:58:29 +0100 Subject: [PATCH 113/350] Allow reuse of service ticket validation methods --- lib/casino_core/helper/proxy_tickets.rb | 33 +++++++++++++++++ lib/casino_core/helper/service_tickets.rb | 35 +------------------ lib/casino_core/model/service_ticket.rb | 4 +++ lib/casino_core/processor/legacy_validator.rb | 2 +- .../processor/service_ticket_validator.rb | 2 +- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index d47479d5..b429ba97 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -9,6 +9,39 @@ def acquire_proxy_ticket(proxy_granting_ticket, service) ticket: random_ticket_string('ST'), service: service, }) + + def validate_ticket_for_service(ticket, service, renew = false) + result = if service.nil? or ticket.nil? + logger.warn 'Invalid validate request: no valid ticket or no valid service given' + 'INVALID_REQUEST' + else + if ticket.consumed? + logger.warn "Ticket '#{ticket.ticket}' already consumed" + 'INVALID_TICKET' + elsif ticket.expired? + logger.warn "Ticket '#{ticket.ticket}' has expired" + 'INVALID_TICKET' + elsif clean_service_url(service) != ticket.service + logger.warn "Ticket '#{ticket.ticket}' is not valid for service '#{service}'" + 'INVALID_SERVICE' + elsif renew && !ticket.issued_from_credentials? + logger.info "Ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" + 'INVALID_TICKET' + else + logger.info "Ticket '#{ticket.ticket}' for service '#{service}' successfully validated" + true + end + end + unless ticket.nil? + logger.debug "Consumed ticket '#{ticket.ticket}'" + ticket.consumed = true + ticket.save! + end + result + end + + def ticket_valid_for_service?(ticket, service, renew = false) + validate_ticket_for_service(ticket, service, renew) == true end end end diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index ba52b0d2..96c8b452 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -5,6 +5,7 @@ module Helper module ServiceTickets include CASinoCore::Helper::Logger include CASinoCore::Helper::Tickets + include CASinoCore::Helper::ProxyTickets def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied = nil) ticket_granting_ticket.service_tickets.create!({ @@ -26,40 +27,6 @@ def clean_service_url(dirty_service) clean_service end - - def validate_service_ticket_for_service(ticket, service, renew = false) - result = if service.nil? or ticket.nil? - logger.warn 'Invalid validate request: no valid ticket or no valid service given' - 'INVALID_REQUEST' - else - if ticket.consumed? - logger.warn "Service ticket '#{ticket.ticket}' already consumed" - 'INVALID_TICKET' - elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] - logger.warn "Service ticket '#{ticket.ticket}' has expired" - 'INVALID_TICKET' - elsif clean_service_url(service) != ticket.service - logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'" - 'INVALID_SERVICE' - elsif renew && !ticket.issued_from_credentials? - logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" - 'INVALID_TICKET' - else - logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated" - true - end - end - unless ticket.nil? - logger.debug "Consumed ticket '#{ticket.ticket}'" - ticket.consumed = true - ticket.save! - end - result - end - - def service_ticket_valid_for_service?(ticket, service, renew = false) - validate_service_ticket_for_service(ticket, service, renew) == true - end end end end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 4d80930d..1fce0793 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -25,6 +25,10 @@ def service_with_ticket_url service_uri.to_s end + def expired? + Time.now - self.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] + end + private def send_single_sing_out_notification notifier = SingleSignOutNotifier.new(self) diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index 370b3064..549ab50e 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -15,7 +15,7 @@ class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - if service_ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) + if ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n") else @listener.validation_failed("no\n\n") diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index cc814f42..c5e99800 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -16,7 +16,7 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - validation_result = validate_service_ticket_for_service(ticket, params[:service], !!params[:renew]) + validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) if validation_result == true options = { service_ticket: ticket } unless params[:pgtUrl].nil? From 0889efa6e52df41372bb393d0b53612c6b0f6db0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 11:17:58 +0100 Subject: [PATCH 114/350] Extracted builder for ServiceResponse --- lib/casino_core/builder.rb | 7 +++ lib/casino_core/builder/service_response.rb | 48 +++++++++++++++++++ lib/casino_core/processor.rb | 1 + .../processor/proxy_ticket_validator.rb | 41 ++++++++++++++++ .../processor/service_ticket_validator.rb | 47 +++--------------- .../service_ticket_validator_spec.rb | 2 +- 6 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 lib/casino_core/builder.rb create mode 100644 lib/casino_core/builder/service_response.rb create mode 100644 lib/casino_core/processor/proxy_ticket_validator.rb diff --git a/lib/casino_core/builder.rb b/lib/casino_core/builder.rb new file mode 100644 index 00000000..ee90a109 --- /dev/null +++ b/lib/casino_core/builder.rb @@ -0,0 +1,7 @@ +require 'active_record' + +module CASinoCore + class Builder + autoload :ServiceResponse, 'casino_core/builder/service_response.rb' + end +end diff --git a/lib/casino_core/builder/service_response.rb b/lib/casino_core/builder/service_response.rb new file mode 100644 index 00000000..6ef90515 --- /dev/null +++ b/lib/casino_core/builder/service_response.rb @@ -0,0 +1,48 @@ +require 'builder' +require 'casino_core/builder' + +class CASinoCore::Builder::ServiceResponse < CASinoCore::Builder + def initialize(success, options) + @success = success + @options = options + end + + def build + xml = Builder::XmlMarkup.new(indent: 2) + xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response| + if @success + ticket_granting_ticket = @options[:ticket].ticket_granting_ticket + service_response.cas :authenticationSuccess do |authentication_success| + authentication_success.cas :user, ticket_granting_ticket.username + unless ticket_granting_ticket.extra_attributes.blank? + authentication_success.cas :attributes do |attributes| + ticket_granting_ticket.extra_attributes.each do |key, value| + serialize_extra_attribute(attributes, key, value) + end + end + end + if @options[:proxy_granting_ticket] + proxy_granting_ticket = @options[:proxy_granting_ticket] + authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou + end + end + else + service_response.cas :authenticationFailure, @options[:error_message], code: @options[:error_code] + end + end + xml.target! + end + + private + def serialize_extra_attribute(builder, key, value) + if value.kind_of?(String) || value.kind_of?(Numeric) || value.kind_of?(Symbol) + builder.cas key, "#{value}" + elsif value.kind_of?(Numeric) + builder.cas key, value.to_s + else + builder.cas key do |container| + container.cdata! value.to_yaml + end + end + end +end diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index cefb41d6..cfef69d7 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -7,6 +7,7 @@ class Processor autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :Logout, 'casino_core/processor/logout.rb' autoload :ProxyTicketProvider, 'casino_core/processor/proxy_ticket_provider.rb' + autoload :ProxyTicketValidator, 'casino_core/processor/proxy_ticket_validator.rb' autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' diff --git a/lib/casino_core/processor/proxy_ticket_validator.rb b/lib/casino_core/processor/proxy_ticket_validator.rb new file mode 100644 index 00000000..ba2edbff --- /dev/null +++ b/lib/casino_core/processor/proxy_ticket_validator.rb @@ -0,0 +1,41 @@ +require 'builder' +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate +class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor + include CASinoCore::Helper::ProxyTickets + include CASinoCore::Helper::ProxyGrantingTickets + + # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies + # a string as argument. The web application should present that string (and nothing else) to the + # requestor. The Content-Type should be set to 'text/xml; charset=utf-8' + # + # @param [Hash] params parameters delivered by the client + def process(params = nil) + params ||= {} + ticket = extract_ticket_from_params(params) + validation_result = validate_service_ticket_for_service(ticket, params[:service], !!params[:renew]) + if validation_result == true + options = { service_ticket: ticket } + unless params[:pgtUrl].nil? + options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) + end + @listener.validation_succeeded(build_xml(true, options)) + else + @listener.validation_failed(build_xml(false, error_code: validation_result, error_message: 'Validation failed')) + end + end + + private + def extract_ticket_from_params(params) + if params[:ticket].nil? + nil + elsif params[:ticket].starts_with?('PT-') + CASinoCore::Model::ProxyTicket.where(ticket: params[:ticket]).first + else + CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first + end + end +end diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index c5e99800..3f821eae 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -1,7 +1,7 @@ -require 'builder' require 'casino_core/processor' require 'casino_core/helper' require 'casino_core/model' +require 'casino_core/builder' # The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor @@ -18,52 +18,19 @@ def process(params = nil) ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) if validation_result == true - options = { service_ticket: ticket } + options = { ticket: ticket } unless params[:pgtUrl].nil? options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) end - @listener.validation_succeeded(build_xml(true, options)) + @listener.validation_succeeded(build_service_response(true, options)) else - @listener.validation_failed(build_xml(false, error_code: validation_result, error_message: 'Validation failed')) + @listener.validation_failed(build_service_response(false, error_code: validation_result, error_message: 'Validation failed')) end end private - def build_xml(success, options = {}) - xml = Builder::XmlMarkup.new(indent: 2) - xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response| - if success - ticket_granting_ticket = options[:service_ticket].ticket_granting_ticket - service_response.cas :authenticationSuccess do |authentication_success| - authentication_success.cas :user, ticket_granting_ticket.username - unless ticket_granting_ticket.extra_attributes.blank? - authentication_success.cas :attributes do |attributes| - ticket_granting_ticket.extra_attributes.each do |key, value| - serialize_extra_attribute(attributes, key, value) - end - end - end - if options[:proxy_granting_ticket] - proxy_granting_ticket = options[:proxy_granting_ticket] - authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou - end - end - else - service_response.cas :authenticationFailure, options[:error_message], code: options[:error_code] - end - end - xml.target! - end - - def serialize_extra_attribute(builder, key, value) - if value.kind_of?(String) || value.kind_of?(Numeric) || value.kind_of?(Symbol) - builder.cas key, "#{value}" - elsif value.kind_of?(Numeric) - builder.cas key, value.to_s - else - builder.cas key do - builder.cdata! value.to_yaml - end - end + def build_service_response(success, options = {}) + builder = CASinoCore::Builder::ServiceResponse.new(success, options) + builder.build end end diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb index c5dbab72..768b309a 100644 --- a/spec/processor/service_ticket_validator_spec.rb +++ b/spec/processor/service_ticket_validator_spec.rb @@ -9,7 +9,7 @@ CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', username: 'test', - extra_attributes: nil, + extra_attributes: { name: "Example User", roles: ['User', 'Admin'] }, user_agent: user_agent }) } From 425e5a3d32777c7c3984376027a12f881dad8cc4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 15:35:14 +0100 Subject: [PATCH 115/350] Renamed to match it's use --- lib/casino_core/builder.rb | 2 +- .../{service_response.rb => ticket_validation_response.rb} | 2 +- lib/casino_core/processor/service_ticket_validator.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename lib/casino_core/builder/{service_response.rb => ticket_validation_response.rb} (95%) diff --git a/lib/casino_core/builder.rb b/lib/casino_core/builder.rb index ee90a109..276b74af 100644 --- a/lib/casino_core/builder.rb +++ b/lib/casino_core/builder.rb @@ -2,6 +2,6 @@ module CASinoCore class Builder - autoload :ServiceResponse, 'casino_core/builder/service_response.rb' + autoload :TicketValidationResponse, 'casino_core/builder/ticket_validation_response.rb' end end diff --git a/lib/casino_core/builder/service_response.rb b/lib/casino_core/builder/ticket_validation_response.rb similarity index 95% rename from lib/casino_core/builder/service_response.rb rename to lib/casino_core/builder/ticket_validation_response.rb index 6ef90515..098cc73c 100644 --- a/lib/casino_core/builder/service_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -1,7 +1,7 @@ require 'builder' require 'casino_core/builder' -class CASinoCore::Builder::ServiceResponse < CASinoCore::Builder +class CASinoCore::Builder::TicketValidationResponse < CASinoCore::Builder def initialize(success, options) @success = success @options = options diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index 3f821eae..dd52b7e9 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -30,7 +30,7 @@ def process(params = nil) private def build_service_response(success, options = {}) - builder = CASinoCore::Builder::ServiceResponse.new(success, options) + builder = CASinoCore::Builder::TicketValidationResponse.new(success, options) builder.build end end From d14cd747a09074cafdbaa2552493a3b0045a4728 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 15:41:15 +0100 Subject: [PATCH 116/350] Add pgt_url to proxy_granting_tickets --- ...225153637_add_pgt_url_to_proxy_granting_tickets.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb diff --git a/db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb b/db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb new file mode 100644 index 00000000..ab524a5f --- /dev/null +++ b/db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb @@ -0,0 +1,11 @@ +class AddPgtUrlToProxyGrantingTickets < ActiveRecord::Migration + def up + add_column :proxy_granting_tickets, :pgt_url, :string, null: true + CASinoCore::Model::ProxyGrantingTicket.delete_all + change_column :proxy_granting_tickets, :pgt_url, :string, null: false + end + + def down + remove_column :proxy_granting_tickets, :pgt_url + end +end From 7d39e483d58492900ee7ed91f015430b53e34ae0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 15:57:08 +0100 Subject: [PATCH 117/350] Return ValidationResult --- lib/casino_core/helper/proxy_tickets.rb | 55 +++++++++++-------- .../processor/proxy_ticket_validator.rb | 19 +++---- .../processor/service_ticket_validator.rb | 22 +++++--- 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index b429ba97..6c28493b 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -1,6 +1,13 @@ module CASinoCore module Helper module ProxyTickets + + class ValidationResult < Struct.new(:error_code, :error_message, :error_severity) + def success? + self.error_code.nil? + end + end + include CASinoCore::Helper::Logger include CASinoCore::Helper::Tickets @@ -11,37 +18,39 @@ def acquire_proxy_ticket(proxy_granting_ticket, service) }) def validate_ticket_for_service(ticket, service, renew = false) - result = if service.nil? or ticket.nil? - logger.warn 'Invalid validate request: no valid ticket or no valid service given' - 'INVALID_REQUEST' + if ticket.nil? + result = ValidationResult.new 'INVALID_TICKET', 'Invalid validate request: Ticket does not exist', :warn else - if ticket.consumed? - logger.warn "Ticket '#{ticket.ticket}' already consumed" - 'INVALID_TICKET' - elsif ticket.expired? - logger.warn "Ticket '#{ticket.ticket}' has expired" - 'INVALID_TICKET' - elsif clean_service_url(service) != ticket.service - logger.warn "Ticket '#{ticket.ticket}' is not valid for service '#{service}'" - 'INVALID_SERVICE' - elsif renew && !ticket.issued_from_credentials? - logger.info "Ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket" - 'INVALID_TICKET' - else - logger.info "Ticket '#{ticket.ticket}' for service '#{service}' successfully validated" - true - end - end - unless ticket.nil? - logger.debug "Consumed ticket '#{ticket.ticket}'" + result = validate_existing_ticket_for_service(ticket, service, renew) ticket.consumed = true ticket.save! + logger.debug "Consumed ticket '#{ticket.ticket}'" + end + if result.success? + logger.info "Ticket '#{ticket.ticket}' for service '#{service}' successfully validated" + else + logger.send(result.error_severity, result.error_message) end result end def ticket_valid_for_service?(ticket, service, renew = false) - validate_ticket_for_service(ticket, service, renew) == true + validate_ticket_for_service(ticket, service, renew).success? + end + + private + def validate_existing_ticket_for_service(ticket, service, renew = false) + if ticket.consumed? + ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' already consumed", :warn + elsif ticket.expired? + ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' has expired", :warn + elsif clean_service_url(service) != ticket.service + ValidationResult.new 'INVALID_SERVICE', "Ticket '#{ticket.ticket}' is not valid for service '#{service}'", :warn + elsif renew && !ticket.issued_from_credentials? + ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket", :info + else + ValidationResult.new + end end end end diff --git a/lib/casino_core/processor/proxy_ticket_validator.rb b/lib/casino_core/processor/proxy_ticket_validator.rb index ba2edbff..2998e14a 100644 --- a/lib/casino_core/processor/proxy_ticket_validator.rb +++ b/lib/casino_core/processor/proxy_ticket_validator.rb @@ -15,10 +15,10 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor # @param [Hash] params parameters delivered by the client def process(params = nil) params ||= {} - ticket = extract_ticket_from_params(params) - validation_result = validate_service_ticket_for_service(ticket, params[:service], !!params[:renew]) + ticket = fetch_ticket(params[:ticket]) + validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) if validation_result == true - options = { service_ticket: ticket } + options = { ticket: ticket } unless params[:pgtUrl].nil? options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) end @@ -29,13 +29,12 @@ def process(params = nil) end private + def build_service_response(success, options = {}) + builder = CASinoCore::Builder::TicketValidationResponse.new(success, options) + builder.build + end + def extract_ticket_from_params(params) - if params[:ticket].nil? - nil - elsif params[:ticket].starts_with?('PT-') - CASinoCore::Model::ProxyTicket.where(ticket: params[:ticket]).first - else - CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - end + if() end end diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index dd52b7e9..d8c91fab 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -15,16 +15,20 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor # @param [Hash] params parameters delivered by the client def process(params = nil) params ||= {} - ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) - if validation_result == true - options = { ticket: ticket } - unless params[:pgtUrl].nil? - options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) - end - @listener.validation_succeeded(build_service_response(true, options)) + if params[:ticket].nil? || params[:service].nil? + @listener.validation_failed build_service_response(false, error_code: 'INVALID_REQUEST', error_message: '"ticket" and "service" parameters are both required') else - @listener.validation_failed(build_service_response(false, error_code: validation_result, error_message: 'Validation failed')) + ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first + validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) + if validation_result.success? + options = { ticket: ticket } + unless params[:pgtUrl].nil? + options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) + end + @listener.validation_succeeded(build_service_response(true, options)) + else + @listener.validation_failed(build_service_response(false, error_code: validation_result.error_code, error_message: validation_result.error_message)) + end end end From 35eb5d8c110879c07917d54c6ea133921bdce3f6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 16:06:16 +0100 Subject: [PATCH 118/350] ProxyTicketValidator is a ServiceTicketValidator --- lib/casino_core/processor/legacy_validator.rb | 2 +- .../processor/proxy_ticket_validator.rb | 33 +++++----------- .../processor/service_ticket_validator.rb | 39 ++++++++++++------- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index 549ab50e..dec216f2 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -15,7 +15,7 @@ class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - if ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) + if !params[:service].nil? && ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n") else @listener.validation_failed("no\n\n") diff --git a/lib/casino_core/processor/proxy_ticket_validator.rb b/lib/casino_core/processor/proxy_ticket_validator.rb index 2998e14a..5565aea9 100644 --- a/lib/casino_core/processor/proxy_ticket_validator.rb +++ b/lib/casino_core/processor/proxy_ticket_validator.rb @@ -3,10 +3,8 @@ require 'casino_core/helper' require 'casino_core/model' -# The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate -class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor - include CASinoCore::Helper::ProxyTickets - include CASinoCore::Helper::ProxyGrantingTickets +# The ProxyTicketValidator processor should be used to handle GET requests to /proxyValidate +class CASinoCore::Processor::ProxyTicketValidator < CASinoCore::Processor::ServiceTicketValidator # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies # a string as argument. The web application should present that string (and nothing else) to the @@ -15,26 +13,15 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor # @param [Hash] params parameters delivered by the client def process(params = nil) params ||= {} - ticket = fetch_ticket(params[:ticket]) - validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) - if validation_result == true - options = { ticket: ticket } - unless params[:pgtUrl].nil? - options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) + if request_valid?(params) + ticket = if params[:ticket].start_with?('PT-') + CASinoCore::Model::ProxyTicket.where(ticket: params[:ticket]).first + elsif params[:ticket].start_with?('ST-') + CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first + else + nil end - @listener.validation_succeeded(build_xml(true, options)) - else - @listener.validation_failed(build_xml(false, error_code: validation_result, error_message: 'Validation failed')) + validate_ticket!(ticket, params) end end - - private - def build_service_response(success, options = {}) - builder = CASinoCore::Builder::TicketValidationResponse.new(success, options) - builder.build - end - - def extract_ticket_from_params(params) - if() - end end diff --git a/lib/casino_core/processor/service_ticket_validator.rb b/lib/casino_core/processor/service_ticket_validator.rb index d8c91fab..ff3a8206 100644 --- a/lib/casino_core/processor/service_ticket_validator.rb +++ b/lib/casino_core/processor/service_ticket_validator.rb @@ -15,26 +15,37 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor # @param [Hash] params parameters delivered by the client def process(params = nil) params ||= {} - if params[:ticket].nil? || params[:service].nil? - @listener.validation_failed build_service_response(false, error_code: 'INVALID_REQUEST', error_message: '"ticket" and "service" parameters are both required') - else + if request_valid?(params) ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first - validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) - if validation_result.success? - options = { ticket: ticket } - unless params[:pgtUrl].nil? - options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) - end - @listener.validation_succeeded(build_service_response(true, options)) - else - @listener.validation_failed(build_service_response(false, error_code: validation_result.error_code, error_message: validation_result.error_message)) - end + validate_ticket!(ticket, params) end end - private + protected def build_service_response(success, options = {}) builder = CASinoCore::Builder::TicketValidationResponse.new(success, options) builder.build end + + def request_valid?(params) + if params[:ticket].nil? || params[:service].nil? + @listener.validation_failed build_service_response(false, error_code: 'INVALID_REQUEST', error_message: '"ticket" and "service" parameters are both required') + false + else + true + end + end + + def validate_ticket!(ticket, params) + validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew]) + if validation_result.success? + options = { ticket: ticket } + unless params[:pgtUrl].nil? + options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket) + end + @listener.validation_succeeded(build_service_response(true, options)) + else + @listener.validation_failed(build_service_response(false, error_code: validation_result.error_code, error_message: validation_result.error_message)) + end + end end From feb9f0fc77ee21c29a02b4e26348a90cdb164739 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 23:02:41 +0100 Subject: [PATCH 119/350] Store pgt_url in proxy-granting ticket We have to include the pgt_url in /proxyValidate requests in the "proxies" section. --- db/schema.rb | 3 ++- lib/casino_core/helper/proxy_granting_tickets.rb | 3 ++- lib/casino_core/model/proxy_granting_ticket.rb | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index a5eca11e..21e6f6e0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121224113737) do +ActiveRecord::Schema.define(:version => 20121225153637) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -27,6 +27,7 @@ t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.integer "service_ticket_id", :null => false + t.string "pgt_url", :null => false end add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index 4640803c..cd255f67 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -34,7 +34,8 @@ def contact_callback_server(callback_uri, service_ticket) https.start do |conn| pgt = service_ticket.proxy_granting_tickets.new({ ticket: random_ticket_string('PGT'), - iou: random_ticket_string('PGTIOU') + iou: random_ticket_string('PGTIOU'), + pgt_url: "#{callback_uri}" }) callback_uri.query_values = (callback_uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb index 6f8bd320..633d476d 100644 --- a/lib/casino_core/model/proxy_granting_ticket.rb +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -1,7 +1,7 @@ require 'casino_core/model' class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base - attr_accessible :iou, :ticket, :service_ticket_id + attr_accessible :iou, :ticket, :pgt_url validates :ticket, uniqueness: true validates :iou, uniqueness: true belongs_to :service_ticket From 94a065d9104a5dae812b365adb59850eb5f8fc58 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 25 Dec 2012 23:24:12 +0100 Subject: [PATCH 120/350] Use polymorphic association for granter of PGT --- ...ing_ticket_can_be_granted_by_proxy_ticket.rb | 6 ++++++ .../20121225231713_no_default_granter_type.rb | 5 +++++ db/schema.rb | 17 +++++++++-------- lib/casino_core/model/proxy_granting_ticket.rb | 2 +- lib/casino_core/model/proxy_ticket.rb | 2 +- lib/casino_core/model/service_ticket.rb | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb create mode 100644 db/migrate/20121225231713_no_default_granter_type.rb diff --git a/db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb b/db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb new file mode 100644 index 00000000..84ec5778 --- /dev/null +++ b/db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb @@ -0,0 +1,6 @@ +class ProxyGrantingTicketCanBeGrantedByProxyTicket < ActiveRecord::Migration + def up + add_column :proxy_granting_tickets, :granter_type, :string, null: false, default: 'ServiceTicket' + rename_column :proxy_granting_tickets, :service_ticket_id, :granter_id + end +end diff --git a/db/migrate/20121225231713_no_default_granter_type.rb b/db/migrate/20121225231713_no_default_granter_type.rb new file mode 100644 index 00000000..796b2258 --- /dev/null +++ b/db/migrate/20121225231713_no_default_granter_type.rb @@ -0,0 +1,5 @@ +class NoDefaultGranterType < ActiveRecord::Migration + def up + change_column_default :proxy_granting_tickets, :granter_type, nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 21e6f6e0..0a1a635d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121225153637) do +ActiveRecord::Schema.define(:version => 20121225231713) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -22,16 +22,17 @@ add_index "login_tickets", ["ticket"], :name => "index_login_tickets_on_ticket", :unique => true create_table "proxy_granting_tickets", :force => true do |t| - t.string "ticket", :null => false - t.string "iou", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.integer "service_ticket_id", :null => false - t.string "pgt_url", :null => false + t.string "ticket", :null => false + t.string "iou", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "granter_id", :null => false + t.string "pgt_url", :null => false + t.string "granter_type", :null => false end + add_index "proxy_granting_tickets", ["granter_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id" add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true - add_index "proxy_granting_tickets", ["service_ticket_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id" add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true create_table "proxy_tickets", :force => true do |t| diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb index 633d476d..c316f778 100644 --- a/lib/casino_core/model/proxy_granting_ticket.rb +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -4,6 +4,6 @@ class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base attr_accessible :iou, :ticket, :pgt_url validates :ticket, uniqueness: true validates :iou, uniqueness: true - belongs_to :service_ticket + belongs_to :granter, polymorphic: true has_many :proxy_tickets end diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index 2366f588..e5e77956 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -6,7 +6,7 @@ class CASinoCore::Model::ProxyTicket < ActiveRecord::Base attr_accessible :ticket, :service validates :ticket, uniqueness: true belongs_to :proxy_granting_ticket - has_many :proxy_granting_tickets + has_many :proxy_granting_tickets, as: :granter def self.cleanup_unconsumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed].seconds.ago, false]) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 1fce0793..38f9a984 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -9,7 +9,7 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base validates :ticket, uniqueness: true belongs_to :ticket_granting_ticket before_destroy :send_single_sing_out_notification, if: :consumed? - has_many :proxy_granting_tickets + has_many :proxy_granting_tickets, as: :granter def self.cleanup_unconsumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) From 57147450e61190181e6ab5a19002f5ce3eabce7b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 14:58:47 +0100 Subject: [PATCH 121/350] Reuse ST specs for PT --- .../service_ticket_validator_spec.rb | 138 ----------------- spec/processor/ticket_validator_spec.rb | 140 ++++++++++++++++++ 2 files changed, 140 insertions(+), 138 deletions(-) delete mode 100644 spec/processor/service_ticket_validator_spec.rb create mode 100644 spec/processor/ticket_validator_spec.rb diff --git a/spec/processor/service_ticket_validator_spec.rb b/spec/processor/service_ticket_validator_spec.rb deleted file mode 100644 index 768b309a..00000000 --- a/spec/processor/service_ticket_validator_spec.rb +++ /dev/null @@ -1,138 +0,0 @@ -require 'spec_helper' - -describe CASinoCore::Processor::ServiceTicketValidator do - describe '#process' do - let(:listener) { Object.new } - let(:processor) { described_class.new(listener) } - let(:user_agent) { 'TestBrowser 1.0' } - let(:ticket_granting_ticket) { - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', - username: 'test', - extra_attributes: { name: "Example User", roles: ['User', 'Admin'] }, - user_agent: user_agent - }) - } - let(:service) { 'https://www.example.com/cas-service' } - let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } - let(:parameters) { { service: service, ticket: service_ticket.ticket }} - - let(:regex_failure) { /\A\\n?\s*PGTIOU-.+/) - processor.process(parameters_with_pgt_url) - end - - it 'creates a proxy-granting ticket' do - lambda do - processor.process(parameters_with_pgt_url) - end.should change(service_ticket.proxy_granting_tickets, :count).by(1) - end - - it 'contacts the callback server' do - processor.process(parameters_with_pgt_url) - proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.last - WebMock.should have_requested(:get, 'https://www.example.com').with(query: { - pgtId: proxy_granting_ticket.ticket, - pgtIou: proxy_granting_ticket.iou - }) - end - end - - context 'with proxy-granting ticket callback server not matching the service' do - let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.org/' } - - it 'calls the #validation_succeeded method on the listener' do - listener.should_receive(:validation_succeeded).with(regex_success) - processor.process(parameters_with_pgt_url) - end - - it 'does not create a proxy-granting ticket' do - lambda do - processor.process(parameters_with_pgt_url) - end.should_not change(service_ticket.proxy_granting_tickets, :count) - end - end - end - - context 'with a consumed service ticket' do - before(:each) do - service_ticket.consumed = true - service_ticket.save! - end - - it 'calls the #validation_failed method on the listener' do - listener.should_receive(:validation_failed).with(regex_failure) - processor.process(parameters) - end - end - end -end diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb new file mode 100644 index 00000000..1a428e8b --- /dev/null +++ b/spec/processor/ticket_validator_spec.rb @@ -0,0 +1,140 @@ +require 'spec_helper' + +[CASinoCore::Processor::ServiceTicketValidator, CASinoCore::Processor::ProxyTicketValidator].each do |class_under_test| + describe class_under_test do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:user_agent) { 'TestBrowser 1.0' } + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + username: 'test', + extra_attributes: { name: "Example User", roles: ['User', 'Admin'] }, + user_agent: user_agent + }) + } + let(:service) { 'https://www.example.com/cas-service' } + let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } + let(:parameters) { { service: service, ticket: service_ticket.ticket }} + + let(:regex_failure) { /\A\\n?\s*PGTIOU-.+/) + processor.process(parameters_with_pgt_url) + end + + it 'creates a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should change(service_ticket.proxy_granting_tickets, :count).by(1) + end + + it 'contacts the callback server' do + processor.process(parameters_with_pgt_url) + proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.last + WebMock.should have_requested(:get, 'https://www.example.com').with(query: { + pgtId: proxy_granting_ticket.ticket, + pgtIou: proxy_granting_ticket.iou + }) + end + end + + context 'with proxy-granting ticket callback server not matching the service' do + let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.org/' } + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters_with_pgt_url) + end + + it 'does not create a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should_not change(service_ticket.proxy_granting_tickets, :count) + end + end + end + + context 'with a consumed service ticket' do + before(:each) do + service_ticket.consumed = true + service_ticket.save! + end + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed).with(regex_failure) + processor.process(parameters) + end + end + end + end +end From 074aeef97faa5fde9726b2b5cce94665f93dd5a7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 18:55:32 +0100 Subject: [PATCH 122/350] Handle consumed/unconsumed --- lib/casino_core/helper/proxy_tickets.rb | 1 + lib/casino_core/model/proxy_ticket.rb | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index 6c28493b..67eeba43 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -16,6 +16,7 @@ def acquire_proxy_ticket(proxy_granting_ticket, service) ticket: random_ticket_string('ST'), service: service, }) + end def validate_ticket_for_service(ticket, service, renew = false) if ticket.nil? diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index e5e77956..8dcd6e6f 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -15,4 +15,13 @@ def self.cleanup_unconsumed def self.cleanup_consumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_consumed].seconds.ago, true]) end + + def expired? + lifetime = if consumed? + CASinoCore::Settings.proxy_ticket[:lifetime_consumed] + else + CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed] + end + Time.now - self.created_at > lifetime + end end From cefdb9e1ef7fe82109fa17bb3811d40131e1e51a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 18:56:35 +0100 Subject: [PATCH 123/350] Include list of proxies in validation response --- .../builder/ticket_validation_response.rb | 21 ++++++++++- lib/casino_core/model/service_ticket.rb | 7 +++- spec/processor/proxy_ticket_provider_spec.rb | 2 +- spec/processor/proxy_ticket_validator_spec.rb | 37 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 spec/processor/proxy_ticket_validator_spec.rb diff --git a/lib/casino_core/builder/ticket_validation_response.rb b/lib/casino_core/builder/ticket_validation_response.rb index 098cc73c..4f083c9f 100644 --- a/lib/casino_core/builder/ticket_validation_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -11,7 +11,19 @@ def build xml = Builder::XmlMarkup.new(indent: 2) xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response| if @success - ticket_granting_ticket = @options[:ticket].ticket_granting_ticket + ticket = @options[:ticket] + if ticket.is_a?(CASinoCore::Model::ProxyTicket) + proxies = [] + _ticket = ticket + while _ticket.is_a?(CASinoCore::Model::ProxyTicket) + proxy_granting_ticket = ticket.proxy_granting_ticket + proxies << proxy_granting_ticket.pgt_url + _ticket = proxy_granting_ticket.granter + end + ticket_granting_ticket = _ticket.ticket_granting_ticket + else + ticket_granting_ticket = ticket.ticket_granting_ticket + end service_response.cas :authenticationSuccess do |authentication_success| authentication_success.cas :user, ticket_granting_ticket.username unless ticket_granting_ticket.extra_attributes.blank? @@ -25,6 +37,13 @@ def build proxy_granting_ticket = @options[:proxy_granting_ticket] authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou end + if ticket.is_a?(CASinoCore::Model::ProxyTicket) + authentication_success.cas :proxies do |proxies_container| + proxies.each do |proxy| + proxies_container.cas :proxy, proxy + end + end + end end else service_response.cas :authenticationFailure, @options[:error_message], code: @options[:error_code] diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 38f9a984..ddc12692 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -26,7 +26,12 @@ def service_with_ticket_url end def expired? - Time.now - self.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed] + lifetime = if consumed? + CASinoCore::Settings.service_ticket[:lifetime_consumed] + else + CASinoCore::Settings.service_ticket[:lifetime_unconsumed] + end + Time.now - self.created_at > lifetime end private diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb index 9345f8fd..c9d315f9 100644 --- a/spec/processor/proxy_ticket_provider_spec.rb +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -48,7 +48,7 @@ } let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: 'http://www.example.com/' } let(:proxy_granting_ticket) { - service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg' + service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg', pgt_url: 'https://www.example.com/pgtUrl' } let(:params_with_valid_pgt) { params.merge(pgt: proxy_granting_ticket.ticket) } diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb new file mode 100644 index 00000000..982472e7 --- /dev/null +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe CASinoCore::Processor::ProxyTicketValidator do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + + describe '#process' do + let(:regex_success) { /\A\s*https:\/\/www.example.com\/pgtUrl<\/cas:proxy>\s*<\/cas:proxies[>]/ } + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters) + end + + it 'includes the proxy in the response' do + listener.should_receive(:validation_succeeded).with(regex_proxy) + processor.process(parameters) + end + end + end +end From 17e7898b14baaee9e254da045de2bb6d4012e844 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 18:59:05 +0100 Subject: [PATCH 124/350] Refactoring --- .../builder/ticket_validation_response.rb | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/casino_core/builder/ticket_validation_response.rb b/lib/casino_core/builder/ticket_validation_response.rb index 4f083c9f..21d4d1da 100644 --- a/lib/casino_core/builder/ticket_validation_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -24,29 +24,10 @@ def build else ticket_granting_ticket = ticket.ticket_granting_ticket end - service_response.cas :authenticationSuccess do |authentication_success| - authentication_success.cas :user, ticket_granting_ticket.username - unless ticket_granting_ticket.extra_attributes.blank? - authentication_success.cas :attributes do |attributes| - ticket_granting_ticket.extra_attributes.each do |key, value| - serialize_extra_attribute(attributes, key, value) - end - end - end - if @options[:proxy_granting_ticket] - proxy_granting_ticket = @options[:proxy_granting_ticket] - authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou - end - if ticket.is_a?(CASinoCore::Model::ProxyTicket) - authentication_success.cas :proxies do |proxies_container| - proxies.each do |proxy| - proxies_container.cas :proxy, proxy - end - end - end - end + + build_success_xml(service_response, ticket, ticket_granting_ticket, proxies) else - service_response.cas :authenticationFailure, @options[:error_message], code: @options[:error_code] + build_failure_xml(service_response) end end xml.target! @@ -64,4 +45,32 @@ def serialize_extra_attribute(builder, key, value) end end end + + def build_success_xml(service_response, ticket, ticket_granting_ticket, proxies) + service_response.cas :authenticationSuccess do |authentication_success| + authentication_success.cas :user, ticket_granting_ticket.username + unless ticket_granting_ticket.extra_attributes.blank? + authentication_success.cas :attributes do |attributes| + ticket_granting_ticket.extra_attributes.each do |key, value| + serialize_extra_attribute(attributes, key, value) + end + end + end + if @options[:proxy_granting_ticket] + proxy_granting_ticket = @options[:proxy_granting_ticket] + authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou + end + if ticket.is_a?(CASinoCore::Model::ProxyTicket) + authentication_success.cas :proxies do |proxies_container| + proxies.each do |proxy| + proxies_container.cas :proxy, proxy + end + end + end + end + end + + def build_failure_xml(service_response) + service_response.cas :authenticationFailure, @options[:error_message], code: @options[:error_code] + end end From ea6d8c6f3d5b77f5a4d19004e120e36b44ec1e40 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 19:03:49 +0100 Subject: [PATCH 125/350] Fixed test for base authenticator --- spec/authenticator/base_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/authenticator/base_spec.rb b/spec/authenticator/base_spec.rb index 85b65ea5..4120bff1 100644 --- a/spec/authenticator/base_spec.rb +++ b/spec/authenticator/base_spec.rb @@ -7,7 +7,7 @@ context '#validate' do it 'should raise an error' do - expect { subject.validate }.to raise_error + expect { subject.validate(nil, nil) }.to raise_error(NotImplementedError) end end end From c83b95d34514f3a33adf245e0dec5ffdeda0b9ab Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 19:16:37 +0100 Subject: [PATCH 126/350] More tests --- lib/casino_core/helper/proxy_tickets.rb | 5 ++- spec/processor/proxy_ticket_validator_spec.rb | 33 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index 67eeba43..2bbdc560 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -41,11 +41,14 @@ def ticket_valid_for_service?(ticket, service, renew = false) private def validate_existing_ticket_for_service(ticket, service, renew = false) + if ticket.is_a?(CASinoCore::Model::ServiceTicket) + service = clean_service_url(service) + end if ticket.consumed? ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' already consumed", :warn elsif ticket.expired? ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' has expired", :warn - elsif clean_service_url(service) != ticket.service + elsif service != ticket.service ValidationResult.new 'INVALID_SERVICE', "Ticket '#{ticket.ticket}' is not valid for service '#{service}'", :warn elsif renew && !ticket.issued_from_credentials? ValidationResult.new 'INVALID_TICKET', "Ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket", :info diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index 982472e7..1f06b092 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -7,7 +7,7 @@ describe '#process' do let(:regex_success) { /\A\s*https:\/\/www.example.com\/pgtUrl<\/cas:proxy>\s*<\/cas:proxies[>]/ } @@ -32,6 +32,35 @@ listener.should_receive(:validation_succeeded).with(regex_proxy) processor.process(parameters) end + + context 'with an expired proxy ticket' do + before(:each) do + CASinoCore::Model::ProxyTicket.any_instance.stub(:expired?).and_return(true) + end + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed) + processor.process(parameters) + end + end + + context 'with an other service' do + let(:parameters_with_other_service) { parameters.merge(service: 'this_is_another_service') } + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed) + processor.process(parameters_with_other_service) + end + end + + context 'without an existing ticket' do + let(:parameters_without_existing_ticket) { { ticket: 'PT-1234', service: 'https://www.example.com/' } } + + it 'calls the #validation_failed method on the listener' do + listener.should_receive(:validation_failed) + processor.process(parameters_without_existing_ticket) + end + end end end end From f9bacac92fa49ef485685fabccadd8a8359e0ad9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 19:20:36 +0100 Subject: [PATCH 127/350] Wording --- spec/authenticator/base_spec.rb | 2 +- spec/model/service_ticket_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/authenticator/base_spec.rb b/spec/authenticator/base_spec.rb index 4120bff1..866f4dcb 100644 --- a/spec/authenticator/base_spec.rb +++ b/spec/authenticator/base_spec.rb @@ -6,7 +6,7 @@ } context '#validate' do - it 'should raise an error' do + it 'raises an error' do expect { subject.validate(nil, nil) }.to raise_error(NotImplementedError) end end diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index e0c96486..e0838ea2 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -34,7 +34,7 @@ end end - describe '.destroy' do + describe '#destroy' do it 'sends out a single sign out notification' do described_class::SingleSignOutNotifier.any_instance.should_receive(:notify).and_return(true) ticket.consumed = true From 5f5a9ad95b20548164b83f013a2fd1f809096ffe Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 19:26:10 +0100 Subject: [PATCH 128/350] Fixed index for granter on proxy_granting_tickets --- ...192211_fix_index_for_granter_on_proxy_granting_ticket.rb | 6 ++++++ db/schema.rb | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb diff --git a/db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb b/db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb new file mode 100644 index 00000000..f54ef42a --- /dev/null +++ b/db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb @@ -0,0 +1,6 @@ +class FixIndexForGranterOnProxyGrantingTicket < ActiveRecord::Migration + def change + remove_index :proxy_granting_tickets, :service_ticket_id + add_index :proxy_granting_tickets, [:granter_type, :granter_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 0a1a635d..c05796b0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121225231713) do +ActiveRecord::Schema.define(:version => 20121226192211) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -31,7 +31,7 @@ t.string "granter_type", :null => false end - add_index "proxy_granting_tickets", ["granter_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id" + add_index "proxy_granting_tickets", ["granter_type", "granter_id"], :name => "index_proxy_granting_tickets_on_granter_type_and_granter_id", :unique => true add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true From cafad8ff3fd9aae507bb2357a75de59a89c7f582 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 20:05:13 +0100 Subject: [PATCH 129/350] Don't need to check host of service matches pgtUrl Looked at other implementations. Doesn't seem like this needs to be checked. --- .../helper/proxy_granting_tickets.rb | 14 ++++--------- spec/processor/ticket_validator_spec.rb | 21 +++---------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index cd255f67..e66ec0e1 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -12,14 +12,7 @@ module ProxyGrantingTickets def acquire_proxy_granting_ticket(pgt_url, service_ticket) begin - callback_uri = Addressable::URI.parse(pgt_url) - service_uri = Addressable::URI.parse(service_ticket.service) - - if service_uri.host != callback_uri.host - logger.warn "Proxy-granting ticket not created: Host of callback url '#{pgt_url}' does not match service '#{service_uri}'" - else - return contact_callback_server(callback_uri, service_ticket) - end + return contact_callback_server(pgt_url, service_ticket) rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError logger.warn "Exception while communicating with proxy-granting ticket callback server: #{e.message}" end @@ -27,7 +20,8 @@ def acquire_proxy_granting_ticket(pgt_url, service_ticket) end private - def contact_callback_server(callback_uri, service_ticket) + def contact_callback_server(pgt_url, service_ticket) + callback_uri = Addressable::URI.parse(pgt_url) https = Net::HTTP.new(callback_uri.host, callback_uri.port || 443) https.use_ssl = true @@ -35,7 +29,7 @@ def contact_callback_server(callback_uri, service_ticket) pgt = service_ticket.proxy_granting_tickets.new({ ticket: random_ticket_string('PGT'), iou: random_ticket_string('PGTIOU'), - pgt_url: "#{callback_uri}" + pgt_url: pgt_url }) callback_uri.query_values = (callback_uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 1a428e8b..9596d55d 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -76,10 +76,10 @@ end context 'with proxy-granting ticket callback server' do - let(:parameters_with_pgt_url) { parameters.merge pgtUrl: "https://www.example.com" } + let(:parameters_with_pgt_url) { parameters.merge pgtUrl: "https://www.example.org" } before(:each) do - stub_request(:get, /https:\/\/www\.example\.com\/\?pgtId=[^&]+&pgtIou=[^&]+/) + stub_request(:get, /https:\/\/www\.example\.org\/\?pgtId=[^&]+&pgtIou=[^&]+/) end it 'calls the #validation_succeeded method on the listener' do @@ -101,27 +101,12 @@ it 'contacts the callback server' do processor.process(parameters_with_pgt_url) proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.last - WebMock.should have_requested(:get, 'https://www.example.com').with(query: { + WebMock.should have_requested(:get, 'https://www.example.org').with(query: { pgtId: proxy_granting_ticket.ticket, pgtIou: proxy_granting_ticket.iou }) end end - - context 'with proxy-granting ticket callback server not matching the service' do - let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.org/' } - - it 'calls the #validation_succeeded method on the listener' do - listener.should_receive(:validation_succeeded).with(regex_success) - processor.process(parameters_with_pgt_url) - end - - it 'does not create a proxy-granting ticket' do - lambda do - processor.process(parameters_with_pgt_url) - end.should_not change(service_ticket.proxy_granting_tickets, :count) - end - end end context 'with a consumed service ticket' do From 2abbd6db76d85f9313ff025c8a87233a4944f59e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 20:08:42 +0100 Subject: [PATCH 130/350] Fixed gemspec --- casino_core.gemspec | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 34a270cf..c12f4e78 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-24" + s.date = "2012-12-26" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -42,21 +42,30 @@ Gem::Specification.new do |s| "db/migrate/20121125185415_create_proxy_granting_tickets.rb", "db/migrate/20121125190013_tickets_should_be_unique.rb", "db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb", + "db/migrate/20121224113737_create_proxy_tickets.rb", + "db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb", + "db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb", + "db/migrate/20121225231713_no_default_granter_type.rb", + "db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb", "db/schema.rb", "lib/casino_core.rb", "lib/casino_core/authenticator.rb", "lib/casino_core/authenticator/static.rb", + "lib/casino_core/builder.rb", + "lib/casino_core/builder/ticket_validation_response.rb", "lib/casino_core/helper.rb", "lib/casino_core/helper/browser.rb", "lib/casino_core/helper/logger.rb", "lib/casino_core/helper/login_tickets.rb", "lib/casino_core/helper/proxy_granting_tickets.rb", + "lib/casino_core/helper/proxy_tickets.rb", "lib/casino_core/helper/service_tickets.rb", "lib/casino_core/helper/ticket_granting_tickets.rb", "lib/casino_core/helper/tickets.rb", "lib/casino_core/model.rb", "lib/casino_core/model/login_ticket.rb", "lib/casino_core/model/proxy_granting_ticket.rb", + "lib/casino_core/model/proxy_ticket.rb", "lib/casino_core/model/service_ticket.rb", "lib/casino_core/model/service_ticket/single_sign_out_notifier.rb", "lib/casino_core/model/ticket_granting_ticket.rb", @@ -65,6 +74,8 @@ Gem::Specification.new do |s| "lib/casino_core/processor/login_credential_acceptor.rb", "lib/casino_core/processor/login_credential_requestor.rb", "lib/casino_core/processor/logout.rb", + "lib/casino_core/processor/proxy_ticket_provider.rb", + "lib/casino_core/processor/proxy_ticket_validator.rb", "lib/casino_core/processor/service_ticket_validator.rb", "lib/casino_core/processor/session_destroyer.rb", "lib/casino_core/processor/session_overview.rb", @@ -73,16 +84,20 @@ Gem::Specification.new do |s| "lib/casino_core/settings.rb", "lib/casino_core/tasks/cleanup.rake", "lib/casino_core/tasks/database.rake", + "spec/authenticator/base_spec.rb", "spec/authenticator/static_spec.rb", "spec/model/login_ticket_spec.rb", + "spec/model/proxy_ticket_spec.rb", "spec/model/service_ticket_spec.rb", "spec/processor/legacy_validator_spec.rb", "spec/processor/login_credential_acceptor_spec.rb", "spec/processor/login_credential_requestor_spec.rb", "spec/processor/logout_spec.rb", - "spec/processor/service_ticket_validator_spec.rb", + "spec/processor/proxy_ticket_provider_spec.rb", + "spec/processor/proxy_ticket_validator_spec.rb", "spec/processor/session_destroyer_spec.rb", "spec/processor/session_overview_spec.rb", + "spec/processor/ticket_validator_spec.rb", "spec/spec_helper.rb" ] s.homepage = "http://github.com/pencil/CASinoCore" From 1c37b923f2db494f55bd7217a53b093fa94896f7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 20:43:38 +0100 Subject: [PATCH 131/350] Use Adressable::URI --- .../model/service_ticket/single_sign_out_notifier.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index a96f94f7..e7e00942 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -1,15 +1,18 @@ require 'builder' require 'net/https' require 'casino_core/model/service_ticket' +require 'addressable/uri' class CASinoCore::Model::ServiceTicket::SingleSignOutNotifier + include CASinoCore::Helper::Logger + def initialize(service_ticket) @service_ticket = service_ticket end def notify xml = build_xml - uri = URI.parse(@service_ticket.service) + uri = Addressable::URI.parse(@service_ticket.service) request = build_request(uri, xml) send_notification(uri, request) end @@ -25,12 +28,13 @@ def build_xml end def build_request(uri, xml) - request = Net::HTTP::Post.new(uri.path || '/') + request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data(logoutRequest: xml) return request end def send_notification(uri, request) + logger.info "Sending Single Sign Out notification for ticket '#{@service_ticket.ticket}'" begin http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true if uri.scheme =='https' From 192ba8730796fc2462ec4302a8a63de6ad7078a6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 20:53:19 +0100 Subject: [PATCH 132/350] Logging --- lib/casino_core/processor/session_destroyer.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/casino_core/processor/session_destroyer.rb b/lib/casino_core/processor/session_destroyer.rb index 28d1cf2d..ad2d9bb0 100644 --- a/lib/casino_core/processor/session_destroyer.rb +++ b/lib/casino_core/processor/session_destroyer.rb @@ -9,6 +9,8 @@ # combination with the {CASinoCore::Processor::SessionOverview} processor. class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor + include CASinoCore::Helper::Logger + # This method will call `#ticket_not_found` or `#ticket_deleted` on the listener. # @param [Hash] params parameters supplied by user (ID of ticket-granting ticket to delete should by in params[:id]) # @param [Hash] cookies cookies supplied by user @@ -21,6 +23,7 @@ def process(params = nil, cookies = nil, user_agent = nil) if ticket.nil? || !ticket.same_user?(owner_ticket) @listener.ticket_not_found else + logger.info "Destroying ticket-granting ticket '#{ticket.ticket}'" ticket.destroy @listener.ticket_deleted end From 66419a8627da03b75a1f78d5447902cc63187b39 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 21:02:04 +0100 Subject: [PATCH 133/350] Destroy dependents --- lib/casino_core/model/proxy_granting_ticket.rb | 2 +- lib/casino_core/model/proxy_ticket.rb | 2 +- lib/casino_core/model/service_ticket.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/model/proxy_granting_ticket.rb b/lib/casino_core/model/proxy_granting_ticket.rb index c316f778..6a3d9a4e 100644 --- a/lib/casino_core/model/proxy_granting_ticket.rb +++ b/lib/casino_core/model/proxy_granting_ticket.rb @@ -5,5 +5,5 @@ class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base validates :ticket, uniqueness: true validates :iou, uniqueness: true belongs_to :granter, polymorphic: true - has_many :proxy_tickets + has_many :proxy_tickets, dependent: :destroy end diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index 8dcd6e6f..62c8bde2 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -6,7 +6,7 @@ class CASinoCore::Model::ProxyTicket < ActiveRecord::Base attr_accessible :ticket, :service validates :ticket, uniqueness: true belongs_to :proxy_granting_ticket - has_many :proxy_granting_tickets, as: :granter + has_many :proxy_granting_tickets, as: :granter, dependent: :destroy def self.cleanup_unconsumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed].seconds.ago, false]) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index ddc12692..7ad60dc0 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -9,7 +9,7 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base validates :ticket, uniqueness: true belongs_to :ticket_granting_ticket before_destroy :send_single_sing_out_notification, if: :consumed? - has_many :proxy_granting_tickets, as: :granter + has_many :proxy_granting_tickets, as: :granter, dependent: :destroy def self.cleanup_unconsumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false]) From ec1a47bcc27a625bd55d2ae65b260d3b93c2568c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 21:24:29 +0100 Subject: [PATCH 134/350] Destroy service tickets when TGT is deleted --- ..._tickets_without_ticket_granting_ticket.rb | 5 ++++ lib/casino_core/model/service_ticket.rb | 4 +++ .../model/ticket_granting_ticket.rb | 11 ++++++++ lib/casino_core/tasks/cleanup.rake | 2 ++ spec/processor/session_destroyer_spec.rb | 26 +++++++++++++++++-- 5 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb diff --git a/db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb b/db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb new file mode 100644 index 00000000..869e0993 --- /dev/null +++ b/db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb @@ -0,0 +1,5 @@ +class AllowServiceTicketsWithoutTicketGrantingTicket < ActiveRecord::Migration + def change + change_column :service_tickets, :ticket_granting_ticket_id, :integer, null: true + end +end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 7ad60dc0..fa1dd1fd 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -19,6 +19,10 @@ def self.cleanup_consumed self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) end + def self.cleanup_consumed_hard + self.delete_all(['created_at < ? AND consumed = ?', (CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds * 2).ago, true]) + end + def service_with_ticket_url service_uri = Addressable::URI.parse(self.service) service_uri.query_values = (service_uri.query_values || {}).merge(ticket: self.ticket) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index b97e5d04..1c0ac667 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -6,6 +6,8 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base validates :ticket, uniqueness: true has_many :service_tickets + before_destroy :destroy_service_tickets + def browser_info user_agent = UserAgent.parse(self.user_agent) "#{user_agent.browser} (#{user_agent.platform})" @@ -18,4 +20,13 @@ def same_user?(other_ticket) other_ticket.username == self.username end end + + def destroy_service_tickets + self.service_tickets.each do |service_ticket| + unless service_ticket.destroy + service_ticket.ticket_granting_ticket_id = nil + service_ticket.save + end + end + end end diff --git a/lib/casino_core/tasks/cleanup.rake b/lib/casino_core/tasks/cleanup.rake index e556b38d..0057b225 100644 --- a/lib/casino_core/tasks/cleanup.rake +++ b/lib/casino_core/tasks/cleanup.rake @@ -11,6 +11,8 @@ namespace :casino_core do rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}").length puts "Deleted #{rows_affected} #{type} service tickets." end + rows_affected = CASinoCore::Model::ServiceTicket.cleanup_consumed_hard + puts "Force deleted #{rows_affected} consumed service tickets." end desc 'Remove expired proxy tickets.' diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index 5b659e15..d379fa79 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -29,6 +29,15 @@ user_agent: user_agent }) } + let(:service_ticket) { + ticket_granting_ticket.service_tickets.create! ticket: 'ST-6NBRr5DAg2NW181H5chaHh', service: 'http://www.example.com' + } + let(:consumed_service_ticket) { + st = ticket_granting_ticket.service_tickets.create! ticket: 'ST-6NBRr5DAg2NW181H5chaHh', service: 'http://www.example.com' + st.consumed = true + st.save! + st + } let(:params) { { id: ticket_granting_ticket.id } } it 'deletes only one ticket-granting ticket' do @@ -48,15 +57,28 @@ listener.should_receive(:ticket_deleted).with(no_args) processor.process(params, cookies, user_agent) end + + it 'deletes the dependent service ticket' do + service_ticket.ticket # creates the service ticket + lambda { + processor.process(params, cookies, user_agent) + }.should change(CASinoCore::Model::ServiceTicket, :count).by(-1) + end + + it 'nullifies the dependent service ticket if destroying fails' do + lambda { + processor.process(params, cookies, user_agent) + }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.to(nil) + end end - context 'with an invlaid ticket-granting ticket' do + context 'with an invalid ticket-granting ticket' do let(:params) { { id: 99999 } } it 'does not delete a ticket-granting ticket' do owner_ticket_granting_ticket lambda do processor.process(params, cookies, user_agent) - end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(0) + end.should_not change(CASinoCore::Model::TicketGrantingTicket, :count) end it 'calls the #ticket_not_found method on the listener' do From 340d6ef3e50b59c51993c8693d09bf91719d167b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 21:46:54 +0100 Subject: [PATCH 135/350] Delete PGTs event when ST can not be destroyed --- .../model/ticket_granting_ticket.rb | 10 ++++++ spec/model/service_ticket_spec.rb | 29 +++++++++++---- spec/model/ticket_granting_ticket_spec.rb | 35 +++++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 spec/model/ticket_granting_ticket_spec.rb diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 1c0ac667..8e248906 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -7,6 +7,7 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base has_many :service_tickets before_destroy :destroy_service_tickets + after_destroy :destroy_proxy_granting_tickets def browser_info user_agent = UserAgent.parse(self.user_agent) @@ -21,6 +22,7 @@ def same_user?(other_ticket) end end + private def destroy_service_tickets self.service_tickets.each do |service_ticket| unless service_ticket.destroy @@ -29,4 +31,12 @@ def destroy_service_tickets end end end + + # Deletes proxy-granting tickets of service tickets that + # could not be deleted (see #destroy_service_tickets) + def destroy_proxy_granting_tickets + self.service_tickets.each do |service_ticket| + service_ticket.proxy_granting_tickets.destroy_all + end + end end diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index e0838ea2..d57dff8a 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -6,6 +6,13 @@ ticket.ticket_granting_ticket_id = 1 ticket } + let(:consumed_ticket) { + ticket = described_class.new ticket: 'ST-54321', service: 'https://example.com/cas-service' + ticket.ticket_granting_ticket_id = 1 + ticket.consumed = true + ticket.save! + ticket + } describe '.cleanup_unconsumed' do it 'deletes expired unconsumed service tickets' do @@ -24,9 +31,8 @@ end it 'deletes expired consumed service tickets' do - ticket.consumed = true - ticket.created_at = 10.days.ago - ticket.save! + consumed_ticket.created_at = 10.days.ago + consumed_ticket.save! lambda do described_class.cleanup_consumed end.should change(described_class, :count).by(-1) @@ -37,9 +43,20 @@ describe '#destroy' do it 'sends out a single sign out notification' do described_class::SingleSignOutNotifier.any_instance.should_receive(:notify).and_return(true) - ticket.consumed = true - ticket.save! - ticket.destroy + consumed_ticket.destroy + end + + context 'when notification fails' do + before(:each) do + described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(false) + end + + it 'does not delete the service ticket' do + consumed_ticket + lambda { + consumed_ticket.destroy + }.should_not change(CASinoCore::Model::ServiceTicket, :count) + end end end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb new file mode 100644 index 00000000..e7b600a1 --- /dev/null +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe CASinoCore::Model::TicketGrantingTicket do + let(:subject) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user' } + let(:service_ticket) { + subject.service_tickets.create! ticket: 'ST-12345', service: 'https://example.com/cas-service' + } + let(:consumed_service_ticket) { + service_ticket = subject.service_tickets.create! ticket: 'ST-n9oZvDtYkVFHj5M3s59Ws5', service: 'https://example.com/cas-service' + service_ticket.consumed = true + service_ticket.save! + service_ticket + } + + describe '#destroy' do + context 'when notification for a service ticket fails' do + before(:each) do + CASinoCore::Model::ServiceTicket::SingleSignOutNotifier.any_instance.stub(:notify).and_return(false) + end + + it 'deletes depending proxy-granting tickets' do + consumed_service_ticket.proxy_granting_tickets.create! ticket: 'PGT-12345', iou: 'PGTIOU-12345', pgt_url: 'bla' + lambda { + subject.destroy + }.should change(CASinoCore::Model::ProxyGrantingTicket, :count).by(-1) + end + + it 'nullifies depending service tickets' do + lambda { + subject.destroy + }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.from(subject.id).to(nil) + end + end + end +end From 70264c75497fa521fc3439f05034b193484e12c6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 21:54:14 +0100 Subject: [PATCH 136/350] Fresh schema dump --- db/schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index c05796b0..5489d571 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121226192211) do +ActiveRecord::Schema.define(:version => 20121226211511) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -50,7 +50,7 @@ create_table "service_tickets", :force => true do |t| t.string "ticket", :null => false t.string "service", :null => false - t.integer "ticket_granting_ticket_id", :null => false + t.integer "ticket_granting_ticket_id" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.boolean "consumed", :default => false, :null => false From aa9ed2f10231d9cd7d8e2ebb3e6b5ec562a04c27 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:00:48 +0100 Subject: [PATCH 137/350] Set migrations_path --- lib/casino_core/tasks/database.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index 27795db7..92283d13 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -11,7 +11,7 @@ namespace :casino_core do Gem.loaded_specs['casino_core'].full_gem_path end DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development' - MIGRATIONS_DIR = File.join(BASE_DIR, 'db', 'migrate') + ActiveRecord::Migrator.migrations_paths = File.join(BASE_DIR, 'db', 'migrate') SCHEMA_PATH = ENV['SCHEMA'] || File.join(BASE_DIR, 'db', 'schema.rb') end From f2757b33cf8422b69bcb50cc270d9ad578ffa39c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:08:44 +0100 Subject: [PATCH 138/350] Setup CASinoCore --- lib/casino_core/tasks/database.rake | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index 92283d13..6a41fd46 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -17,6 +17,7 @@ namespace :casino_core do task :configuration => :environment do @config = YAML.load_file('config/database.yml')[DATABASE_ENV] + CASinoCore.setup DATABASE_ENV end task :configure_connection => :configuration do From 20ad8c9deeeb043772f542656a09b5e9621e5215 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:13:30 +0100 Subject: [PATCH 139/350] Version bump to 1.0.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 99d85ecd..afaf360d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.6 \ No newline at end of file +1.0.0 \ No newline at end of file From 603a1b511e4c83ffaed1b47c2dd6c09a7c056951 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:13:33 +0100 Subject: [PATCH 140/350] Regenerate gemspec for version 1.0.0 --- casino_core.gemspec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index c12f4e78..6be7da83 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "0.0.6" + s.version = "1.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] @@ -47,6 +47,7 @@ Gem::Specification.new do |s| "db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb", "db/migrate/20121225231713_no_default_granter_type.rb", "db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb", + "db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb", "db/schema.rb", "lib/casino_core.rb", "lib/casino_core/authenticator.rb", @@ -89,6 +90,7 @@ Gem::Specification.new do |s| "spec/model/login_ticket_spec.rb", "spec/model/proxy_ticket_spec.rb", "spec/model/service_ticket_spec.rb", + "spec/model/ticket_granting_ticket_spec.rb", "spec/processor/legacy_validator_spec.rb", "spec/processor/login_credential_acceptor_spec.rb", "spec/processor/login_credential_requestor_spec.rb", From f96757d8ed35262325261bdabc25db125a6adba8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:50:34 +0100 Subject: [PATCH 141/350] Proxy tickets should be prefixed with 'PT-' --- lib/casino_core/helper/proxy_tickets.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index 2bbdc560..7fe4dc96 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -13,7 +13,7 @@ def success? def acquire_proxy_ticket(proxy_granting_ticket, service) proxy_granting_ticket.proxy_tickets.create!({ - ticket: random_ticket_string('ST'), + ticket: random_ticket_string('PT'), service: service, }) end From a13fb77088dc2df2c08111c9ca7185065941dd11 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:50:52 +0100 Subject: [PATCH 142/350] Version bump to 1.0.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index afaf360d..7f207341 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.0.1 \ No newline at end of file From 5637c801b6010d640b64e4d8d562b366d66234c5 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 26 Dec 2012 22:50:55 +0100 Subject: [PATCH 143/350] Regenerate gemspec for version 1.0.1 --- casino_core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 6be7da83..15d5fdd1 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.0" + s.version = "1.0.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] From b4815d8e1b95ed7f5c1302b1268ed30fe5276361 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 12:33:47 +0100 Subject: [PATCH 144/350] Better handling of empty query values in service URL --- lib/casino_core/helper/service_tickets.rb | 6 ++++++ spec/processor/ticket_validator_spec.rb | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index 96c8b452..bf2eca1e 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -21,6 +21,12 @@ def clean_service_url(dirty_service) unless service_uri.query_values.nil? service_uri.query_values = service_uri.query_values.except('service', 'ticket', 'gateway', 'renew') end + if service_uri.query_values.blank? + service_uri.query_values = nil + end + if "#{service_uri.path}".length > 1 + service_uri.path = service_uri.path.gsub(/\/\z/, '') + end clean_service = service_uri.to_s logger.debug("Cleaned dirty service URL '#{dirty_service}' to '#{clean_service}'") if dirty_service != clean_service diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 9596d55d..6d7e9893 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -40,6 +40,13 @@ end end + context 'with empty query values' do + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters.merge(service: "#{service}/?")) + end + end + context 'with renew flag' do let(:parameters_with_renew) { parameters.merge renew: 'true' } From 64578b8a07ff62c028aea4a2b729f2df45278729 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 12:37:16 +0100 Subject: [PATCH 145/350] Version bump to 1.0.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7f207341..e6d5cb83 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1 \ No newline at end of file +1.0.2 \ No newline at end of file From e66e4ddbf57b6a4753db81d67246f69266da2ddf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 12:37:22 +0100 Subject: [PATCH 146/350] Regenerate gemspec for version 1.0.2 --- casino_core.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 15d5fdd1..0eaea368 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.1" + s.version = "1.0.2" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-26" + s.date = "2012-12-29" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ From 3e926427fa455807ba937962ea2e5a407f51ac8d Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 13:16:01 +0100 Subject: [PATCH 147/350] Destroy service tickets without ticket_granting_ticket --- lib/casino_core/model/service_ticket.rb | 2 +- spec/model/service_ticket_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index fa1dd1fd..f4ac3213 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -16,7 +16,7 @@ def self.cleanup_unconsumed end def self.cleanup_consumed - self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) + self.destroy_all(['(ticket_granting_ticket_id IS NULL OR created_at < ?) AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]) end def self.cleanup_consumed_hard diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index d57dff8a..d2f8268b 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -38,6 +38,22 @@ end.should change(described_class, :count).by(-1) described_class.find_by_ticket('ST-12345').should be_false end + + it 'deletes consumed service tickets without ticket_granting_ticket' do + consumed_ticket.ticket_granting_ticket_id = nil + consumed_ticket.save! + lambda do + described_class.cleanup_consumed + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket('ST-12345').should be_false + end + + it 'does not delete unexpired service tickets' do + consumed_ticket # create the ticket + lambda do + described_class.cleanup_consumed + end.should_not change(described_class, :count) + end end describe '#destroy' do From c89d439165319a1282a252710f1fc373e784c1da Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 18:55:58 +0100 Subject: [PATCH 148/350] Match specification --- .../model/service_ticket/single_sign_out_notifier.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index e7e00942..12da7605 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -20,8 +20,8 @@ def notify private def build_xml xml = Builder::XmlMarkup.new(indent: 2) - xml.samlp :LogoutRequest, ID: SecureRandom.uuid, Version: '2.0', IsseInstant: Time.now do |logout_request| - logout_request.samlp :NameID, '@NOT_USED@' + xml.samlp :LogoutRequest, ID: SecureRandom.uuid, Version: '2.0', IssueInstant: Time.now do |logout_request| + logout_request.saml :NameID, '@NOT_USED@' logout_request.samlp :SessionIndex, @service_ticket.ticket end xml.target! From 107d6c1c56a20e785c97786e0ebb965340a16545 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:24:35 +0100 Subject: [PATCH 149/350] Bugfixing and specs for SingleSignOutNotifier --- Gemfile | 3 +- Gemfile.lock | 2 + .../single_sign_out_notifier.rb | 7 ++- .../single_sign_out_notifier_spec.rb | 45 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 spec/model/service_ticket/single_sign_out_notifier_spec.rb diff --git a/Gemfile b/Gemfile index 73fa0cb8..afe97501 100644 --- a/Gemfile +++ b/Gemfile @@ -12,12 +12,13 @@ group :development do gem 'yard', '~> 0.8.3', require: 'redcarpet' end -group :development, :test do +group :test do gem 'rspec', '~> 2.12.0' gem 'simplecov', '~> 0.7.1' gem 'sqlite3' gem 'database_cleaner' gem 'webmock' + gem 'nokogiri' end gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index af3e3f15..d31b0ef3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,6 +27,7 @@ GEM rdoc json (1.7.5) multi_json (1.5.0) + nokogiri (1.5.6) rake (10.0.3) rdoc (3.12) json (~> 1.4) @@ -60,6 +61,7 @@ DEPENDENCIES bundler (~> 1.2.0) database_cleaner jeweler (~> 1.8.4) + nokogiri redcarpet rspec (~> 2.12.0) simplecov (~> 0.7.1) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index 12da7605..2ef2ea0d 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -20,7 +20,12 @@ def notify private def build_xml xml = Builder::XmlMarkup.new(indent: 2) - xml.samlp :LogoutRequest, ID: SecureRandom.uuid, Version: '2.0', IssueInstant: Time.now do |logout_request| + xml.samlp :LogoutRequest, + 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol', + 'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion', + ID: SecureRandom.uuid, + Version: '2.0', + IssueInstant: Time.now do |logout_request| logout_request.saml :NameID, '@NOT_USED@' logout_request.samlp :SessionIndex, @service_ticket.ticket end diff --git a/spec/model/service_ticket/single_sign_out_notifier_spec.rb b/spec/model/service_ticket/single_sign_out_notifier_spec.rb new file mode 100644 index 00000000..a8356726 --- /dev/null +++ b/spec/model/service_ticket/single_sign_out_notifier_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require 'nokogiri' + +describe CASinoCore::Model::ServiceTicket::SingleSignOutNotifier do + let(:ticket) { 'ST-123456' } + let(:service) { 'http://www.example.org/' } + let(:service_ticket) { CASinoCore::Model::ServiceTicket.create ticket: ticket, service: service } + let(:notifier) { described_class.new service_ticket } + + describe '#notify' do + before(:each) do + stub_request(:post, service) + end + + it 'sends a valid Single Sign Out XML to the service URL' do + notifier.notify + WebMock.should have_requested(:post, service).with { |request| + post_params = CGI.parse(request.body) + post_params.should_not be_nil + xml = Nokogiri::XML post_params['logoutRequest'].first + xml.at_xpath('/samlp:LogoutRequest/samlp:SessionIndex').text.strip.should == service_ticket.ticket + } + end + + context 'when it is a success' do + it 'returns true' do + notifier.notify.should == true + end + end + + context 'with server error' do + [404, 500].each do |status_code| + context "#{status_code}" do + before(:each) do + stub_request(:post, service).to_return status: status_code + end + + it 'returns false' do + notifier.notify.should == false + end + end + end + end + end +end \ No newline at end of file From b6a569ec9057754ed10eabebc3c19e8803b398f9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:27:17 +0100 Subject: [PATCH 150/350] Don't measure coverage of specs --- spec/spec_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f6312827..b3619363 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,7 +19,9 @@ # --seed 1234 config.order = 'random' - SimpleCov.start + SimpleCov.start do + add_filter '/spec' + end CASinoCore.setup 'test' CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN From 57c64a1292c5ae3011e4d049785182da69f2b2d9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:33:23 +0100 Subject: [PATCH 151/350] Bugfixing in error handling and more specs --- .../helper/proxy_granting_tickets.rb | 2 +- spec/processor/ticket_validator_spec.rb | 39 ++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index e66ec0e1..865ead9a 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -13,7 +13,7 @@ module ProxyGrantingTickets def acquire_proxy_granting_ticket(pgt_url, service_ticket) begin return contact_callback_server(pgt_url, service_ticket) - rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError + rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e logger.warn "Exception while communicating with proxy-granting ticket callback server: #{e.message}" end nil diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 6d7e9893..5426839d 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -83,10 +83,11 @@ end context 'with proxy-granting ticket callback server' do - let(:parameters_with_pgt_url) { parameters.merge pgtUrl: "https://www.example.org" } + let(:pgt_url) { 'https://www.example.org' } + let(:parameters_with_pgt_url) { parameters.merge pgtUrl: pgt_url } before(:each) do - stub_request(:get, /https:\/\/www\.example\.org\/\?pgtId=[^&]+&pgtIou=[^&]+/) + stub_request(:get, /#{pgt_url}\/\?pgtId=[^&]+&pgtIou=[^&]+/) end it 'calls the #validation_succeeded method on the listener' do @@ -113,6 +114,40 @@ pgtIou: proxy_granting_ticket.iou }) end + + context 'when callback server gives an error' do + before(:each) do + stub_request(:get, /#{pgt_url}.*/).to_return status: 404 + end + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters_with_pgt_url) + end + + it 'does not create a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should_not change(service_ticket.proxy_granting_tickets, :count) + end + end + + context 'when callback server is unreachable' do + before(:each) do + stub_request(:get, /#{pgt_url}.*/).to_raise(Timeout::Error) + end + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters_with_pgt_url) + end + + it 'does not create a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should_not change(service_ticket.proxy_granting_tickets, :count) + end + end end end From b82cece6111e68d5aecbcb81de6ae5d09135cc4f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:47:08 +0100 Subject: [PATCH 152/350] Only test what not has been tested --- spec/processor/session_destroyer_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index d379fa79..319aa581 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -57,19 +57,6 @@ listener.should_receive(:ticket_deleted).with(no_args) processor.process(params, cookies, user_agent) end - - it 'deletes the dependent service ticket' do - service_ticket.ticket # creates the service ticket - lambda { - processor.process(params, cookies, user_agent) - }.should change(CASinoCore::Model::ServiceTicket, :count).by(-1) - end - - it 'nullifies the dependent service ticket if destroying fails' do - lambda { - processor.process(params, cookies, user_agent) - }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.to(nil) - end end context 'with an invalid ticket-granting ticket' do From 349a3904e24e9f97c2b390d5862bba77fbbbd5ba Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:47:23 +0100 Subject: [PATCH 153/350] Only rescue Net::HTTP's errors --- .../model/service_ticket/single_sign_out_notifier.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index 2ef2ea0d..1d5f926e 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -54,7 +54,7 @@ def send_notification(uri, request) return false end end - rescue Exception => e + rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e logger.warn "Failed to send logout notification to service #{uri} due to #{e}" return false end From 1ce9e68737f322414fb4f26cf72cf15b8f8af3fe Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:50:58 +0100 Subject: [PATCH 154/350] Fix strange bug --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index afe97501..70e0b5f1 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ group :development do gem 'yard', '~> 0.8.3', require: 'redcarpet' end -group :test do +group :development, :test do gem 'rspec', '~> 2.12.0' gem 'simplecov', '~> 0.7.1' gem 'sqlite3' From 97d5960cd38797cb1b471a5de4ecab259b7d434f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 19:53:19 +0100 Subject: [PATCH 155/350] Test exception handling --- .../service_ticket/single_sign_out_notifier_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/model/service_ticket/single_sign_out_notifier_spec.rb b/spec/model/service_ticket/single_sign_out_notifier_spec.rb index a8356726..52750a82 100644 --- a/spec/model/service_ticket/single_sign_out_notifier_spec.rb +++ b/spec/model/service_ticket/single_sign_out_notifier_spec.rb @@ -40,6 +40,16 @@ end end end + + context 'connection timeout' do + before(:each) do + stub_request(:post, service).to_raise Timeout::Error + end + + it 'returns false' do + notifier.notify.should == false + end + end end end end \ No newline at end of file From eb1efedab381c711162fec7ac02cc25197a4644b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 20:04:36 +0100 Subject: [PATCH 156/350] Version bump to 1.0.3 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e6d5cb83..e4c0d46e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.2 \ No newline at end of file +1.0.3 \ No newline at end of file From f9514f9a38e7f1f0f9485094effcffdd02aaf179 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 29 Dec 2012 20:04:41 +0100 Subject: [PATCH 157/350] Regenerate gemspec for version 1.0.3 --- casino_core.gemspec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 0eaea368..9299ad3e 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.2" + s.version = "1.0.3" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] @@ -89,6 +89,7 @@ Gem::Specification.new do |s| "spec/authenticator/static_spec.rb", "spec/model/login_ticket_spec.rb", "spec/model/proxy_ticket_spec.rb", + "spec/model/service_ticket/single_sign_out_notifier_spec.rb", "spec/model/service_ticket_spec.rb", "spec/model/ticket_granting_ticket_spec.rb", "spec/processor/legacy_validator_spec.rb", @@ -124,6 +125,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) @@ -137,6 +139,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, ["~> 3.2.9"]) @@ -151,6 +154,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) end end From 600d9b066b5fea032c2b344a33595ba531339334 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:04:37 +0100 Subject: [PATCH 158/350] Store authenticator name for disambiguation --- config/cas.yml | 21 ++++++++--------- ...uthenticator_to_ticket_granting_tickets.rb | 15 ++++++++++++ db/schema.rb | 5 ++-- .../model/ticket_granting_ticket.rb | 4 ++-- .../processor/login_credential_acceptor.rb | 23 +++++++++---------- lib/casino_core/processor/session_overview.rb | 5 +++- lib/casino_core/settings.rb | 6 ++--- spec/model/ticket_granting_ticket_spec.rb | 2 +- spec/processor/legacy_validator_spec.rb | 1 + .../login_credential_requestor_spec.rb | 1 + spec/processor/logout_spec.rb | 1 + spec/processor/proxy_ticket_provider_spec.rb | 1 + spec/processor/proxy_ticket_validator_spec.rb | 1 + spec/processor/session_destroyer_spec.rb | 3 +++ spec/processor/session_overview_spec.rb | 22 ++++++++++++++++++ spec/processor/ticket_validator_spec.rb | 1 + 16 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb diff --git a/config/cas.yml b/config/cas.yml index 3a1fa581..217b8e7f 100644 --- a/config/cas.yml +++ b/config/cas.yml @@ -7,25 +7,24 @@ defaults: &defaults proxy_ticket: lifetime_unconsumed: 300 lifetime_consumed: 86400 - -development: - <<: *defaults authenticators: - - + static_1: class: "CASinoCore::Authenticator::Static" options: users: testuser: password: "foobar123" name: "Test User" - -test: - <<: *defaults - authenticators: - - + static_2: class: "CASinoCore::Authenticator::Static" options: users: - testuser: - password: "foobar123" + example: + password: "dito123" name: "Test User" + +development: + <<: *defaults + +test: + <<: *defaults diff --git a/db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb b/db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb new file mode 100644 index 00000000..42aa12c0 --- /dev/null +++ b/db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb @@ -0,0 +1,15 @@ +class AddAuthenticatorToTicketGrantingTickets < ActiveRecord::Migration + def up + add_column :ticket_granting_tickets, :authenticator, :string, null: true + CASinoCore::Model::TicketGrantingTicket.delete_all + change_column :ticket_granting_tickets, :authenticator, :string, null: false + add_index :ticket_granting_tickets, [:authenticator, :username] + remove_index :ticket_granting_tickets, :username + end + + def down + remove_index :ticket_granting_tickets, [:authenticator, :username] + remove_column :ticket_granting_tickets, :authenticator + add_index :ticket_granting_tickets, :username + end +end diff --git a/db/schema.rb b/db/schema.rb index 5489d571..4bcec369 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121226211511) do +ActiveRecord::Schema.define(:version => 20121231114141) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -67,9 +67,10 @@ t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.string "user_agent" + t.string "authenticator", :null => false end + add_index "ticket_granting_tickets", ["authenticator", "username"], :name => "index_ticket_granting_tickets_on_authenticator_and_username" add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true - add_index "ticket_granting_tickets", ["username"], :name => "index_ticket_granting_tickets_on_username" end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 8e248906..4b234779 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -1,7 +1,7 @@ require 'casino_core/model' class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base - attr_accessible :ticket, :username, :user_agent, :extra_attributes + attr_accessible :ticket, :authenticator, :username, :user_agent, :extra_attributes serialize :extra_attributes, Hash validates :ticket, uniqueness: true has_many :service_tickets @@ -18,7 +18,7 @@ def same_user?(other_ticket) if other_ticket.nil? false else - other_ticket.username == self.username + other_ticket.username == self.username && other_ticket.authenticator == self.authenticator end end diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 643e2456..6701684d 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -23,9 +23,9 @@ def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} if login_ticket_valid?(params[:lt]) - user_data = validate_login_credentials(params[:username], params[:password]) - if !user_data.nil? - ticket_granting_ticket = acquire_ticket_granting_ticket(user_data, user_agent) + authentication_result = validate_login_credentials(params[:username], params[:password]) + if !authentication_result.nil? + ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, user_agent) url = unless params[:service].nil? acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url end @@ -55,24 +55,23 @@ def login_ticket_valid?(lt) end def validate_login_credentials(username, password) - user_data = nil - CASinoCore::Settings.authenticators.each do |authenticator| + authentication_result = nil + CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| data = authenticator.validate(username, password) if data - if data[:username].nil? - data[:username] = username - end - user_data = data - logger.info("Credentials for username '#{data[:username]}' successfully validated using #{authenticator.class}") + authentication_result = { authenticator: authenticator_name, user_data: data } + logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") break end end - user_data + authentication_result end - def acquire_ticket_granting_ticket(user_data, user_agent = nil) + def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) + user_data = authentication_result[:user_data] CASinoCore::Model::TicketGrantingTicket.create!({ ticket: random_ticket_string('TGC'), + authenticator: authentication_result[:authenticator], username: user_data[:username], extra_attributes: user_data[:extra_attributes], user_agent: user_agent diff --git a/lib/casino_core/processor/session_overview.rb b/lib/casino_core/processor/session_overview.rb index 1f39b5ef..c19eeb15 100644 --- a/lib/casino_core/processor/session_overview.rb +++ b/lib/casino_core/processor/session_overview.rb @@ -18,7 +18,10 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else - ticket_granting_tickets = CASinoCore::Model::TicketGrantingTicket.where(username: tgt.username).order('updated_at DESC') + ticket_granting_tickets = CASinoCore::Model::TicketGrantingTicket.where( + username: tgt.username, + authenticator: tgt.authenticator + ).order('updated_at DESC') @listener.ticket_granting_tickets_found(ticket_granting_tickets) end end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 35c73608..3c568ee1 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -17,12 +17,12 @@ def logger end def authenticators=(authenticators) - @authenticators = [] - authenticators.each do |authenticator| + @authenticators = {} + authenticators.each do |index, authenticator| unless authenticator.is_a?(CASinoCore::Authenticator) authenticator = authenticator[:class].constantize.new(authenticator[:options]) end - @authenticators << authenticator + @authenticators[index] = authenticator end end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index e7b600a1..98fdbe8a 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CASinoCore::Model::TicketGrantingTicket do - let(:subject) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user' } + let(:subject) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user', authenticator: 'test' } let(:service_ticket) { subject.service_tickets.create! ticket: 'ST-12345', service: 'https://example.com/cas-service' } diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb index 26a5a384..bf3877d0 100644 --- a/spec/processor/legacy_validator_spec.rb +++ b/spec/processor/legacy_validator_spec.rb @@ -8,6 +8,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 3f816485..19ca2ee0 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -17,6 +17,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-9H6Vx4850i2Ksp3R8hTCwO', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index aa5cee99..7ff59f18 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -17,6 +17,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb index c9d315f9..36534bef 100644 --- a/spec/processor/proxy_ticket_provider_spec.rb +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -43,6 +43,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-Qu6B5IVQ7RmLc972TruM9u', + authenticator: 'test', username: 'test' }) } diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index 1f06b092..616cfa17 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -11,6 +11,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-Qu6B5IVQ7RmLc972TruM9u', + authenticator: 'test', username: 'test' }) } diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index 319aa581..5b4b5f65 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -8,6 +8,7 @@ let(:owner_ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-ocCudGzZjJtrvOXJ485mt3', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent @@ -24,6 +25,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent @@ -78,6 +80,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'test', username: 'this_is_another_user', extra_attributes: nil, user_agent: user_agent diff --git a/spec/processor/session_overview_spec.rb b/spec/processor/session_overview_spec.rb index 47887b05..6e57007a 100644 --- a/spec/processor/session_overview_spec.rb +++ b/spec/processor/session_overview_spec.rb @@ -8,6 +8,7 @@ let(:other_ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-ocCudGzZjJtrvOXJ485mt3', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent @@ -25,6 +26,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'test', username: 'test', extra_attributes: nil, user_agent: user_agent @@ -39,6 +41,26 @@ end end + context 'with a ticket-granting ticket with same username but different authenticator' do + let(:ticket_granting_ticket) { + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: 'TGC-Xz9iea8ro7O5eR99e54vWN', + authenticator: 'lala', + username: 'test', + extra_attributes: nil, + user_agent: user_agent + }) + } + let(:tgt) { ticket_granting_ticket.ticket } + + it 'calls the #ticket_granting_tickets_found method on the listener' do + listener.should_receive(:ticket_granting_tickets_found) do |tickets| + tickets.length.should == 1 + end + processor.process(cookies, user_agent) + end + end + context 'with an invalid ticket-granting ticket' do let(:tgt) { 'TGT-lalala' } it 'calls the #user_not_logged_in method on the listener' do diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 5426839d..8520ea71 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -9,6 +9,7 @@ let(:ticket_granting_ticket) { CASinoCore::Model::TicketGrantingTicket.create!({ ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', + authenticator: 'foo', username: 'test', extra_attributes: { name: "Example User", roles: ['User', 'Admin'] }, user_agent: user_agent From fbd2408eb0b49407fbb0ffb7586423757d984454 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:29:11 +0100 Subject: [PATCH 159/350] Add factory_girl gem --- Gemfile | 1 + Gemfile.lock | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 70e0b5f1..04eb9df2 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ group :development, :test do gem 'database_cleaner' gem 'webmock' gem 'nokogiri' + gem 'factory_girl', '~> 4.1.0' end gem 'activerecord', '~> 3.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index d31b0ef3..c063329e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,6 +18,8 @@ GEM crack (0.3.1) database_cleaner (0.9.1) diff-lcs (1.1.3) + factory_girl (4.1.0) + activesupport (>= 3.0.0) git (1.2.5) i18n (0.6.1) jeweler (1.8.4) @@ -60,6 +62,7 @@ DEPENDENCIES addressable (~> 2.3.2) bundler (~> 1.2.0) database_cleaner + factory_girl (~> 4.1.0) jeweler (~> 1.8.4) nokogiri redcarpet From e79d4118f18ad461de9f23267e42145639b2f30b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:29:24 +0100 Subject: [PATCH 160/350] Factory for TicketGrantingTicket --- spec/support/factories/ticket_granting_ticket.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 spec/support/factories/ticket_granting_ticket.rb diff --git a/spec/support/factories/ticket_granting_ticket.rb b/spec/support/factories/ticket_granting_ticket.rb new file mode 100644 index 00000000..01316969 --- /dev/null +++ b/spec/support/factories/ticket_granting_ticket.rb @@ -0,0 +1,13 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :ticket_granting_ticket, class: CASinoCore::Model::TicketGrantingTicket do + sequence :ticket do |n| + "TGC-ticket#{n}" + end + authenticator 'test' + username 'test' + extra_attributes nil + user_agent 'TestBrowser 1.0' + end +end From 7e2ac464acec211c6e2cc37c50d94c9627b14aca Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:29:37 +0100 Subject: [PATCH 161/350] Use factory for TicketGrantingTicket --- spec/processor/legacy_validator_spec.rb | 12 ++----- .../login_credential_requestor_spec.rb | 16 +++------- spec/processor/logout_spec.rb | 12 ++----- spec/processor/proxy_ticket_provider_spec.rb | 8 +---- spec/processor/proxy_ticket_validator_spec.rb | 8 +---- spec/processor/session_destroyer_spec.rb | 32 +++---------------- spec/processor/session_overview_spec.rb | 32 +++---------------- spec/processor/ticket_validator_spec.rb | 12 ++----- spec/spec_helper.rb | 5 +++ 9 files changed, 26 insertions(+), 111 deletions(-) diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb index bf3877d0..98e57dbc 100644 --- a/spec/processor/legacy_validator_spec.rb +++ b/spec/processor/legacy_validator_spec.rb @@ -4,16 +4,8 @@ describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:user_agent) { 'TestBrowser 1.0' } - let(:ticket_granting_ticket) { - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', - authenticator: 'test', - username: 'test', - extra_attributes: nil, - user_agent: user_agent - }) - } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } let(:service) { 'https://example.com/cas-service' } let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } let(:parameters) { { service: service, ticket: service_ticket.ticket }} diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 19ca2ee0..63a96b95 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -13,16 +13,8 @@ end context 'when logged in' do - let(:user_agent) { 'TestBrowser 1.0' } - let(:ticket_granting_ticket) { - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: 'TGC-9H6Vx4850i2Ksp3R8hTCwO', - authenticator: 'test', - username: 'test', - extra_attributes: nil, - user_agent: user_agent - }) - } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } let(:cookies) { { tgt: ticket_granting_ticket.ticket } } before(:each) do @@ -65,9 +57,11 @@ end context 'with a changed browser' do + let(:user_agent) { 'FooBar 1.0' } + it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) - processor.process(nil, cookies) + processor.process(nil, cookies, user_agent) end end end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index 7ff59f18..f8f79041 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -7,23 +7,15 @@ let(:cookies) { { tgt: tgt } } let(:url) { nil } let(:params) { { :url => url } unless url.nil? } - let(:user_agent) { 'TestBrowser 1.0' } before(:each) do listener.stub(:user_logged_out) end context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7', - authenticator: 'test', - username: 'test', - extra_attributes: nil, - user_agent: user_agent - }) - } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } it 'deletes the ticket-granting ticket' do processor.process(params, cookies, user_agent) diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb index 36534bef..496d18b9 100644 --- a/spec/processor/proxy_ticket_provider_spec.rb +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -40,13 +40,7 @@ end context 'with a proxy-granting ticket' do - let(:ticket_granting_ticket) { - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: 'TGC-Qu6B5IVQ7RmLc972TruM9u', - authenticator: 'test', - username: 'test' - }) - } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: 'http://www.example.com/' } let(:proxy_granting_ticket) { service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg', pgt_url: 'https://www.example.com/pgtUrl' diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index 616cfa17..e80cb29a 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -8,13 +8,7 @@ let(:regex_success) { /\A Date: Sun, 30 Dec 2012 12:34:49 +0100 Subject: [PATCH 162/350] Remove dead code --- lib/casino_core/builder/ticket_validation_response.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/casino_core/builder/ticket_validation_response.rb b/lib/casino_core/builder/ticket_validation_response.rb index 21d4d1da..edb41f4b 100644 --- a/lib/casino_core/builder/ticket_validation_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -37,8 +37,6 @@ def build def serialize_extra_attribute(builder, key, value) if value.kind_of?(String) || value.kind_of?(Numeric) || value.kind_of?(Symbol) builder.cas key, "#{value}" - elsif value.kind_of?(Numeric) - builder.cas key, value.to_s else builder.cas key do |container| container.cdata! value.to_yaml From dd1e9a20f7e06cb9bd539f635862233498048316 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:34:59 +0100 Subject: [PATCH 163/350] Add some extra attributes --- ...ket_granting_ticket.rb => ticket_granting_ticket_factory.rb} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename spec/support/factories/{ticket_granting_ticket.rb => ticket_granting_ticket_factory.rb} (79%) diff --git a/spec/support/factories/ticket_granting_ticket.rb b/spec/support/factories/ticket_granting_ticket_factory.rb similarity index 79% rename from spec/support/factories/ticket_granting_ticket.rb rename to spec/support/factories/ticket_granting_ticket_factory.rb index 01316969..d916012a 100644 --- a/spec/support/factories/ticket_granting_ticket.rb +++ b/spec/support/factories/ticket_granting_ticket_factory.rb @@ -7,7 +7,7 @@ end authenticator 'test' username 'test' - extra_attributes nil + extra_attributes({ fullname: "Test User", age: 15, roles: [:user] }) user_agent 'TestBrowser 1.0' end end From 93e1f58342181fe77a4a82df5e0983ca5cc215a8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 12:53:18 +0100 Subject: [PATCH 164/350] Add groups to SimpleCov --- spec/spec_helper.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 085881b4..b8eb47ca 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,6 +26,12 @@ SimpleCov.start do add_filter '/spec' + base_path = "#{File.dirname(__FILE__)}/../" + Dir["#{base_path}lib/casino_core/*.rb"].each do |f| + f.gsub!(/\A#{base_path}(.+)\.rb\z/, '\1') + name = File.basename(f).humanize.pluralize + add_group name, f + end end CASinoCore.setup 'test' From 74de4f52a597db02005a316b420a1d130daae39f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 14:04:09 +0100 Subject: [PATCH 165/350] Factory for ServiceTicket --- .../single_sign_out_notifier_spec.rb | 7 +++---- spec/model/ticket_granting_ticket_spec.rb | 19 ++++++------------- spec/processor/legacy_validator_spec.rb | 7 ++----- spec/processor/proxy_ticket_provider_spec.rb | 3 +-- spec/processor/proxy_ticket_validator_spec.rb | 3 +-- spec/processor/session_destroyer_spec.rb | 11 ++--------- spec/processor/ticket_validator_spec.rb | 10 +++------- .../factories/service_ticket_factory.rb | 17 +++++++++++++++++ 8 files changed, 35 insertions(+), 42 deletions(-) create mode 100644 spec/support/factories/service_ticket_factory.rb diff --git a/spec/model/service_ticket/single_sign_out_notifier_spec.rb b/spec/model/service_ticket/single_sign_out_notifier_spec.rb index 52750a82..d702df13 100644 --- a/spec/model/service_ticket/single_sign_out_notifier_spec.rb +++ b/spec/model/service_ticket/single_sign_out_notifier_spec.rb @@ -2,9 +2,8 @@ require 'nokogiri' describe CASinoCore::Model::ServiceTicket::SingleSignOutNotifier do - let(:ticket) { 'ST-123456' } - let(:service) { 'http://www.example.org/' } - let(:service_ticket) { CASinoCore::Model::ServiceTicket.create ticket: ticket, service: service } + let(:service_ticket) { FactoryGirl.create :service_ticket } + let(:service) { service_ticket.service } let(:notifier) { described_class.new service_ticket } describe '#notify' do @@ -18,7 +17,7 @@ post_params = CGI.parse(request.body) post_params.should_not be_nil xml = Nokogiri::XML post_params['logoutRequest'].first - xml.at_xpath('/samlp:LogoutRequest/samlp:SessionIndex').text.strip.should == service_ticket.ticket + xml.at_xpath('/samlp:LogoutRequest/samlp:SessionIndex').text.should == service_ticket.ticket } end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 98fdbe8a..1b733865 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -1,16 +1,9 @@ require 'spec_helper' describe CASinoCore::Model::TicketGrantingTicket do - let(:subject) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user', authenticator: 'test' } - let(:service_ticket) { - subject.service_tickets.create! ticket: 'ST-12345', service: 'https://example.com/cas-service' - } - let(:consumed_service_ticket) { - service_ticket = subject.service_tickets.create! ticket: 'ST-n9oZvDtYkVFHj5M3s59Ws5', service: 'https://example.com/cas-service' - service_ticket.consumed = true - service_ticket.save! - service_ticket - } + let(:ticket_granting_ticket) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user', authenticator: 'test' } + let(:service_ticket) { FactoryGirl.create :service_ticket, ticket_granting_ticket: ticket_granting_ticket } + let(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket } describe '#destroy' do context 'when notification for a service ticket fails' do @@ -21,14 +14,14 @@ it 'deletes depending proxy-granting tickets' do consumed_service_ticket.proxy_granting_tickets.create! ticket: 'PGT-12345', iou: 'PGTIOU-12345', pgt_url: 'bla' lambda { - subject.destroy + ticket_granting_ticket.destroy }.should change(CASinoCore::Model::ProxyGrantingTicket, :count).by(-1) end it 'nullifies depending service tickets' do lambda { - subject.destroy - }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.from(subject.id).to(nil) + ticket_granting_ticket.destroy + }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.from(ticket_granting_ticket.id).to(nil) end end end diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb index 98e57dbc..04e3b154 100644 --- a/spec/processor/legacy_validator_spec.rb +++ b/spec/processor/legacy_validator_spec.rb @@ -4,11 +4,8 @@ describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } - let(:user_agent) { ticket_granting_ticket.user_agent } - let(:service) { 'https://example.com/cas-service' } - let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service } - let(:parameters) { { service: service, ticket: service_ticket.ticket }} + let(:service_ticket) { FactoryGirl.create :service_ticket } + let(:parameters) { { service: service_ticket.service, ticket: service_ticket.ticket }} before(:each) do listener.stub(:validation_failed) diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb index 496d18b9..4687cc10 100644 --- a/spec/processor/proxy_ticket_provider_spec.rb +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -40,8 +40,7 @@ end context 'with a proxy-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } - let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: 'http://www.example.com/' } + let(:service_ticket) { FactoryGirl.create :service_ticket } let(:proxy_granting_ticket) { service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg', pgt_url: 'https://www.example.com/pgtUrl' } diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index e80cb29a..e4575f52 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -8,8 +8,7 @@ let(:regex_success) { /\A Date: Sun, 30 Dec 2012 14:06:28 +0100 Subject: [PATCH 166/350] Factory for LoginTicket --- spec/processor/login_credential_acceptor_spec.rb | 2 +- spec/support/factories/login_ticket_factory.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 spec/support/factories/login_ticket_factory.rb diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 8af8e20f..3fba3a76 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -13,7 +13,7 @@ end context 'with a valid login ticket' do - let(:login_ticket) { CASinoCore::Model::LoginTicket.create! ticket: "LT-#{Random.rand(10000000)}" } + let(:login_ticket) { FactoryGirl.create :login_ticket } context 'with invalid credentials' do it 'calls the #invalid_login_credentials method on the listener' do diff --git a/spec/support/factories/login_ticket_factory.rb b/spec/support/factories/login_ticket_factory.rb new file mode 100644 index 00000000..bff2a584 --- /dev/null +++ b/spec/support/factories/login_ticket_factory.rb @@ -0,0 +1,13 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :login_ticket, class: CASinoCore::Model::LoginTicket do + sequence :ticket do |n| + "LT-ticket#{n}" + end + + trait :consumed do + consumed true + end + end +end From 850f3f5ad0e726c3484286ada1157d101bc6f2dc Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 14:14:09 +0100 Subject: [PATCH 167/350] Factory for ProxyGrantingTicket --- spec/processor/proxy_ticket_provider_spec.rb | 5 +---- spec/processor/proxy_ticket_validator_spec.rb | 7 ++----- .../factories/proxy_granting_ticket_factory.rb | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 spec/support/factories/proxy_granting_ticket_factory.rb diff --git a/spec/processor/proxy_ticket_provider_spec.rb b/spec/processor/proxy_ticket_provider_spec.rb index 4687cc10..523ca1e7 100644 --- a/spec/processor/proxy_ticket_provider_spec.rb +++ b/spec/processor/proxy_ticket_provider_spec.rb @@ -40,10 +40,7 @@ end context 'with a proxy-granting ticket' do - let(:service_ticket) { FactoryGirl.create :service_ticket } - let(:proxy_granting_ticket) { - service_ticket.proxy_granting_tickets.create! ticket: 'PGT-OIE42ZadV3B9VcaG2xMjAf', iou: 'PGTIOU-PYg4CCPQHNyyS9s6bJF6Rg', pgt_url: 'https://www.example.com/pgtUrl' - } + let(:proxy_granting_ticket) { FactoryGirl.create :proxy_granting_ticket } let(:params_with_valid_pgt) { params.merge(pgt: proxy_granting_ticket.ticket) } it 'calls the #request_succeeded method on the listener' do diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index e4575f52..8c295ded 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -8,14 +8,11 @@ let(:regex_success) { /\A\s*https:\/\/www.example.com\/pgtUrl<\/cas:proxy>\s*<\/cas:proxies[>]/ } + let(:regex_proxy) { /\s*#{proxy_granting_ticket.pgt_url}<\/cas:proxy>\s*<\/cas:proxies>/ } it 'calls the #validation_succeeded method on the listener' do listener.should_receive(:validation_succeeded).with(regex_success) diff --git a/spec/support/factories/proxy_granting_ticket_factory.rb b/spec/support/factories/proxy_granting_ticket_factory.rb new file mode 100644 index 00000000..36044695 --- /dev/null +++ b/spec/support/factories/proxy_granting_ticket_factory.rb @@ -0,0 +1,16 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :proxy_granting_ticket, class: CASinoCore::Model::ProxyGrantingTicket do + association :granter, factory: :service_ticket + sequence :ticket do |n| + "PGT-ticket#{n}" + end + sequence :iou do |n| + "PGTIOU-ticket#{n}" + end + sequence :pgt_url do |n| + "https://www#{n}.example.org/pgtUrl" + end + end +end From 64562900d5e6b2266d5bd310d14e3badd916130e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 14:17:11 +0100 Subject: [PATCH 168/350] Factory for ProxyTicket --- spec/processor/proxy_ticket_validator_spec.rb | 8 +++----- spec/support/factories/proxy_ticket_factory.rb | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 spec/support/factories/proxy_ticket_factory.rb diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index 8c295ded..2f8128dc 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -8,11 +8,9 @@ let(:regex_success) { /\A\s*#{proxy_granting_ticket.pgt_url}<\/cas:proxy>\s*<\/cas:proxies>/ } + let(:proxy_ticket) { FactoryGirl.create :proxy_ticket } + let(:parameters) { { ticket: proxy_ticket.ticket, service: proxy_ticket.service } } + let(:regex_proxy) { /\s*#{proxy_ticket.proxy_granting_ticket.pgt_url}<\/cas:proxy>\s*<\/cas:proxies>/ } it 'calls the #validation_succeeded method on the listener' do listener.should_receive(:validation_succeeded).with(regex_success) diff --git a/spec/support/factories/proxy_ticket_factory.rb b/spec/support/factories/proxy_ticket_factory.rb new file mode 100644 index 00000000..6d3d8e74 --- /dev/null +++ b/spec/support/factories/proxy_ticket_factory.rb @@ -0,0 +1,17 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :proxy_ticket, class: CASinoCore::Model::ProxyTicket do + proxy_granting_ticket + sequence :ticket do |n| + "PT-ticket#{n}" + end + sequence :service do |n| + "imaps://mail#{n}.example.org/" + end + + trait :consumed do + consumed true + end + end +end From 763d8985e3d8163156b74a737521101d5d454cba Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 14:31:46 +0100 Subject: [PATCH 169/350] Support for gateway parameter --- .../processor/login_credential_requestor.rb | 9 ++++++-- .../login_credential_requestor_spec.rb | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 526c6c09..44f97921 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -28,8 +28,13 @@ def process(params = nil, cookies = nil, user_agent = nil) end @listener.user_logged_in(service_url_with_ticket) else - login_ticket = acquire_login_ticket - @listener.user_not_logged_in(login_ticket) + if params[:gateway] == 'true' && params[:service] + # we actually lie to the listener to simplify things + @listener.user_logged_in(params[:service]) + else + login_ticket = acquire_login_ticket + @listener.user_not_logged_in(login_ticket) + end end end end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 63a96b95..08ecdbc1 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -10,6 +10,27 @@ listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) processor.process end + + context 'with gateway parameter' do + context 'with a service' do + let(:service) { 'http://example.com/' } + let(:params) { { service: service, gateway: 'true' } } + + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in).with(service) + processor.process(params) + end + end + + context 'without a service' do + let(:params) { { gateway: 'true' } } + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process + end + end + end end context 'when logged in' do From 476d75d8d232831befed1eaf9bb9cd02193452cf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 14:39:18 +0100 Subject: [PATCH 170/350] =?UTF-8?q?Yay,=20100%=20coverage=20=F0=9F=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/processor/login_credential_acceptor_spec.rb | 9 +++++++++ spec/processor/proxy_ticket_validator_spec.rb | 10 ++++++++++ spec/processor/ticket_validator_spec.rb | 13 +++++++++++++ spec/support/factories/login_ticket_factory.rb | 3 +++ 4 files changed, 35 insertions(+) diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 3fba3a76..68d21ae0 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -12,6 +12,15 @@ end end + context 'with an expired login ticket' do + let(:expired_login_ticket) { FactoryGirl.create :login_ticket, :expired } + + it 'calls the #invalid_login_ticket method on the listener' do + listener.should_receive(:invalid_login_ticket).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(lt: expired_login_ticket.ticket) + end + end + context 'with a valid login ticket' do let(:login_ticket) { FactoryGirl.create :login_ticket } diff --git a/spec/processor/proxy_ticket_validator_spec.rb b/spec/processor/proxy_ticket_validator_spec.rb index 2f8128dc..12c4c088 100644 --- a/spec/processor/proxy_ticket_validator_spec.rb +++ b/spec/processor/proxy_ticket_validator_spec.rb @@ -7,6 +7,16 @@ describe '#process' do let(:regex_success) { /\A Date: Sun, 30 Dec 2012 16:36:34 +0100 Subject: [PATCH 171/350] Version bump to 1.0.4 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e4c0d46e..a6a3a43c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.3 \ No newline at end of file +1.0.4 \ No newline at end of file From b17969d1fe77427e5510f06216dc438462f3c8a4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 16:36:37 +0100 Subject: [PATCH 172/350] Regenerate gemspec for version 1.0.4 --- casino_core.gemspec | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 9299ad3e..fc247e79 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.3" + s.version = "1.0.4" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-29" + s.date = "2012-12-30" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -48,6 +48,7 @@ Gem::Specification.new do |s| "db/migrate/20121225231713_no_default_granter_type.rb", "db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb", "db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb", + "db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb", "db/schema.rb", "lib/casino_core.rb", "lib/casino_core/authenticator.rb", @@ -101,7 +102,12 @@ Gem::Specification.new do |s| "spec/processor/session_destroyer_spec.rb", "spec/processor/session_overview_spec.rb", "spec/processor/ticket_validator_spec.rb", - "spec/spec_helper.rb" + "spec/spec_helper.rb", + "spec/support/factories/login_ticket_factory.rb", + "spec/support/factories/proxy_granting_ticket_factory.rb", + "spec/support/factories/proxy_ticket_factory.rb", + "spec/support/factories/service_ticket_factory.rb", + "spec/support/factories/ticket_granting_ticket_factory.rb" ] s.homepage = "http://github.com/pencil/CASinoCore" s.licenses = ["MIT"] @@ -126,6 +132,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 4.1.0"]) else s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) @@ -140,6 +147,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 4.1.0"]) end else s.add_dependency(%q, ["~> 3.2.9"]) @@ -155,6 +163,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 4.1.0"]) end end From 3ece66d9f00c7b4a9bf575470a49812b2dd26a08 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:19:44 +0100 Subject: [PATCH 173/350] add test and code for the first api processor --- lib/casino_core/processor.rb | 2 + lib/casino_core/processor/api.rb | 7 ++ .../api/login_credential_acceptor.rb | 72 +++++++++++++++++++ .../api/login_credential_acceptor_spec.rb | 37 ++++++++++ 4 files changed, 118 insertions(+) create mode 100644 lib/casino_core/processor/api.rb create mode 100644 lib/casino_core/processor/api/login_credential_acceptor.rb create mode 100644 spec/processor/api/login_credential_acceptor_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index cfef69d7..199c8ea4 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -12,6 +12,8 @@ class Processor autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' + autoload :API, 'casino_core/processor/api.rb' + def initialize(listener) @listener = listener end diff --git a/lib/casino_core/processor/api.rb b/lib/casino_core/processor/api.rb new file mode 100644 index 00000000..5a532f88 --- /dev/null +++ b/lib/casino_core/processor/api.rb @@ -0,0 +1,7 @@ +module CASinoCore + class Processor + module API + autoload :LoginCredentialAcceptor, 'casino_core/processor/api/login_credential_acceptor.rb' + end + end +end diff --git a/lib/casino_core/processor/api/login_credential_acceptor.rb b/lib/casino_core/processor/api/login_credential_acceptor.rb new file mode 100644 index 00000000..f3a7e8e6 --- /dev/null +++ b/lib/casino_core/processor/api/login_credential_acceptor.rb @@ -0,0 +1,72 @@ +require 'casino_core/processor/api' +require 'casino_core/helper' + +# This processor should be used for API calls: POST /cas/v1/tickets +class CASinoCore::Processor::API::LoginCredentialAcceptor < CASinoCore::Processor + include CASinoCore::Helper::Logger + include CASinoCore::Helper::ServiceTickets + + # Use this method to process the request. It expects the username in the parameter "username" and the password + # in "password". + # + # The method will call one of the following methods on the listener: + # * `#api_user_logged_in`: + # * `#api_invalid_login_credentials`: + # + # @param [Hash] params parameters supplied by user + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client + def process(login_data) + @login_data = login_data + + validate_login_data + + unless @authentication_result.nil? + generate_ticket_granting_ticket + callback_user_logged_in + else + callback_invalid_login_credentials + end + end + + private + def validate_login_data + @authentication_result = validate_login_credentials(@login_data[:username], @login_data[:password]) + end + + def callback_user_logged_in + @listener.user_logged_in_via_api @ticket_granting_ticket.ticket + end + + def generate_ticket_granting_ticket + @ticket_granting_ticket = acquire_ticket_granting_ticket(@authentication_result) + end + + def callback_invalid_login_credentials + @listener.invalid_login_credentials_via_api + end + + def validate_login_credentials(username, password) + authentication_result = nil + CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| + data = authenticator.validate(username, password) + if data + authentication_result = { authenticator: authenticator_name, user_data: data } + logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") + break + end + end + authentication_result + end + + def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) + user_data = authentication_result[:user_data] + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: random_ticket_string('TGC'), + authenticator: authentication_result[:authenticator], + username: user_data[:username], + extra_attributes: user_data[:extra_attributes], + user_agent: user_agent + }) + end +end diff --git a/spec/processor/api/login_credential_acceptor_spec.rb b/spec/processor/api/login_credential_acceptor_spec.rb new file mode 100644 index 00000000..88cfd049 --- /dev/null +++ b/spec/processor/api/login_credential_acceptor_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe CASinoCore::Processor::API::LoginCredentialAcceptor do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + + context 'with invalid credentials' do + let(:login_data) { {username: 'testuser', password: 'wrong'} } + + it 'calls the #invalid_login_credentials method on the listener' do + listener.should_receive(:invalid_login_credentials_via_api) + processor.process(login_data).should be_false + end + end + + context 'with valid credentials' do + let(:login_data) { {username: 'testuser', password: 'foobar123'} } + + before(:each) do + listener.stub(:user_logged_in) + end + + it 'calls the #user_logged_in method on the listener' do + listener.should_receive(:user_logged_in_via_api).with(/^TGC\-/) + processor.process(login_data) + end + + it 'generates a ticket-granting ticket' do + listener.should_receive(:user_logged_in_via_api).with(/^TGC\-/) + expect { + processor.process(login_data) + }.to change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) + end + end + end +end From 0d7dd01e4ce2221c36494b132e969f669fcc9986 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:29:32 +0100 Subject: [PATCH 174/350] move the authentiction method and the tgt create method into a helper --- lib/casino_core/helper/authentication.rb | 20 +++++++++++++++++++ .../helper/ticket_granting_tickets.rb | 13 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 lib/casino_core/helper/authentication.rb diff --git a/lib/casino_core/helper/authentication.rb b/lib/casino_core/helper/authentication.rb new file mode 100644 index 00000000..95233b72 --- /dev/null +++ b/lib/casino_core/helper/authentication.rb @@ -0,0 +1,20 @@ +module CASinoCore + module Helper + module Authentication + + def validate_login_credentials(username, password) + authentication_result = nil + CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| + data = authenticator.validate(username, password) + if data + authentication_result = { authenticator: authenticator_name, user_data: data } + logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") + break + end + end + authentication_result + end + + end + end +end diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index b0b5eb44..acfd020d 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -3,6 +3,7 @@ module CASinoCore module Helper module TicketGrantingTickets + include CASinoCore::Helper::Browser include CASinoCore::Helper::Logger @@ -19,6 +20,18 @@ def find_valid_ticket_granting_ticket(tgt, user_agent) end end end + + def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) + user_data = authentication_result[:user_data] + CASinoCore::Model::TicketGrantingTicket.create!({ + ticket: random_ticket_string('TGC'), + authenticator: authentication_result[:authenticator], + username: user_data[:username], + extra_attributes: user_data[:extra_attributes], + user_agent: user_agent + }) + end + end end end From 1f6eb7ca34f4f51c5f98c56d815e0ee7de224ce2 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:30:07 +0100 Subject: [PATCH 175/350] configure autoload --- lib/casino_core/helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index 8614b56f..b07719bc 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -3,6 +3,7 @@ module CASinoCore module Helper + autoload :Authentication, 'casino_core/helper/authentication.rb' autoload :Browser, 'casino_core/helper/browser.rb' autoload :Logger, 'casino_core/helper/logger.rb' autoload :LoginTickets, 'casino_core/helper/login_tickets.rb' From 5d7e3825dd39c0c622af924bce87ea83a66a5e76 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:30:15 +0100 Subject: [PATCH 176/350] use the "new" helper methods --- .../api/login_credential_acceptor.rb | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/lib/casino_core/processor/api/login_credential_acceptor.rb b/lib/casino_core/processor/api/login_credential_acceptor.rb index f3a7e8e6..3f82a3db 100644 --- a/lib/casino_core/processor/api/login_credential_acceptor.rb +++ b/lib/casino_core/processor/api/login_credential_acceptor.rb @@ -5,6 +5,8 @@ class CASinoCore::Processor::API::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper::Logger include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::Authentication + include CASinoCore::Helper::TicketGrantingTickets # Use this method to process the request. It expects the username in the parameter "username" and the password # in "password". @@ -46,27 +48,4 @@ def callback_invalid_login_credentials @listener.invalid_login_credentials_via_api end - def validate_login_credentials(username, password) - authentication_result = nil - CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| - data = authenticator.validate(username, password) - if data - authentication_result = { authenticator: authenticator_name, user_data: data } - logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") - break - end - end - authentication_result - end - - def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) - user_data = authentication_result[:user_data] - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: random_ticket_string('TGC'), - authenticator: authentication_result[:authenticator], - username: user_data[:username], - extra_attributes: user_data[:extra_attributes], - user_agent: user_agent - }) - end end From a1f019133557859f61d3228b04a15df2bc07e36c Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:32:10 +0100 Subject: [PATCH 177/350] use the new helper methods also for normal logins --- .../processor/login_credential_acceptor.rb | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 6701684d..2db243ef 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -6,6 +6,8 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor include CASinoCore::Helper::Logger include CASinoCore::Helper::LoginTickets include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::Authentication + include CASinoCore::Helper::TicketGrantingTickets # Use this method to process the request. It expects the username in the parameter "username" and the password # in "password". @@ -54,27 +56,4 @@ def login_ticket_valid?(lt) end end - def validate_login_credentials(username, password) - authentication_result = nil - CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| - data = authenticator.validate(username, password) - if data - authentication_result = { authenticator: authenticator_name, user_data: data } - logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") - break - end - end - authentication_result - end - - def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) - user_data = authentication_result[:user_data] - CASinoCore::Model::TicketGrantingTicket.create!({ - ticket: random_ticket_string('TGC'), - authenticator: authentication_result[:authenticator], - username: user_data[:username], - extra_attributes: user_data[:extra_attributes], - user_agent: user_agent - }) - end end From e94bd23c7c1cf2a4c2e86950908c93dff54caab7 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 17:38:33 +0100 Subject: [PATCH 178/350] document all the things! --- .../processor/api/login_credential_acceptor.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/casino_core/processor/api/login_credential_acceptor.rb b/lib/casino_core/processor/api/login_credential_acceptor.rb index 3f82a3db..daf486be 100644 --- a/lib/casino_core/processor/api/login_credential_acceptor.rb +++ b/lib/casino_core/processor/api/login_credential_acceptor.rb @@ -12,12 +12,10 @@ class CASinoCore::Processor::API::LoginCredentialAcceptor < CASinoCore::Processo # in "password". # # The method will call one of the following methods on the listener: - # * `#api_user_logged_in`: - # * `#api_invalid_login_credentials`: + # * `#user_logged_in_via_api`: First and only argument is a String with the TGT-id + # * `#invalid_login_credentials_via_api`: No argument # - # @param [Hash] params parameters supplied by user - # @param [Hash] cookies cookies supplied by user - # @param [String] user_agent user-agent delivered by the client + # @param [Hash] login_data parameters supplied by user (username and password) def process(login_data) @login_data = login_data From 9a8a53f571b5955f5da3c084ec028bef1c52a9a2 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 19:27:07 +0100 Subject: [PATCH 179/350] add a service ticket provider for the API --- lib/casino_core/processor/api.rb | 1 + .../processor/api/service_ticket_provider.rb | 38 +++++++++++++++++++ .../api/service_ticket_provider_spec.rb | 36 ++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 lib/casino_core/processor/api/service_ticket_provider.rb create mode 100644 spec/processor/api/service_ticket_provider_spec.rb diff --git a/lib/casino_core/processor/api.rb b/lib/casino_core/processor/api.rb index 5a532f88..0692ef50 100644 --- a/lib/casino_core/processor/api.rb +++ b/lib/casino_core/processor/api.rb @@ -2,6 +2,7 @@ module CASinoCore class Processor module API autoload :LoginCredentialAcceptor, 'casino_core/processor/api/login_credential_acceptor.rb' + autoload :ServiceTicketProvider, 'casino_core/processor/api/service_ticket_provider.rb' end end end diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb new file mode 100644 index 00000000..513e54bb --- /dev/null +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -0,0 +1,38 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' +require 'casino_core/builder' + +# The ServiceTicketProvider processor should be used to handle API calls: POST requests to /cas/v1/tickets/{TGT id} +class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor + include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::TicketGrantingTickets + + + def process(ticket_granting_ticket, service_url) + @client_ticket_granting_ticket = ticket_granting_ticket + @service_url = service_url + + fetch_valid_ticket_granting_ticket + handle_ticket_granting_ticket + end + + private + def fetch_valid_ticket_granting_ticket + @ticket_granting_ticket = find_valid_ticket_granting_ticket(@client_ticket_granting_ticket, nil) + end + + def handle_ticket_granting_ticket + if @ticket_granting_ticket + create_service_ticket + @listener.granted_service_ticket_via_api @service_ticket.ticket + else + @listener.invalid_tgt_via_api + end + end + + def create_service_ticket + @service_ticket = acquire_service_ticket(@ticket_granting_ticket, @service_url) + end + +end diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb new file mode 100644 index 00000000..68781d72 --- /dev/null +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe CASinoCore::Processor::API::ServiceTicketProvider do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + + let(:service) { 'http://example.org/' } + + context 'with an invalid tgt' do + let(:tgt) { 'TGT-INVALID' } + + it 'calls the #invalid_tgt_via_api method on the listener' do + listener.should_receive(:invalid_tgt_via_api) + processor.process(tgt, service).should be_false + end + end + + context 'with a valid tgt' do + let(:tgt) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil) } + + it 'calls the #granted_service_ticket_via_api method on the listener' do + listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) + processor.process(tgt.ticket, service) + end + + it 'generates a ticket-granting ticket' do + listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) + expect { + processor.process(tgt.ticket, service) + }.to change(CASinoCore::Model::ServiceTicket, :count).by(1) + end + end + end +end + From 83ad044b311d17a1f799557649ac70f1aa4be26b Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 19:51:18 +0100 Subject: [PATCH 180/350] add callback for an undefined service --- .../processor/api/service_ticket_provider.rb | 29 ++++++++++++++----- .../api/service_ticket_provider_spec.rb | 21 ++++++++++---- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 513e54bb..42cca100 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -9,9 +9,9 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets - def process(ticket_granting_ticket, service_url) - @client_ticket_granting_ticket = ticket_granting_ticket - @service_url = service_url + def process(parameters) + @client_ticket_granting_ticket = parameters[:ticket_granting_ticket] + @service_url = parameters[:service] fetch_valid_ticket_granting_ticket handle_ticket_granting_ticket @@ -23,11 +23,14 @@ def fetch_valid_ticket_granting_ticket end def handle_ticket_granting_ticket - if @ticket_granting_ticket + case + when (@service_url and @ticket_granting_ticket) create_service_ticket - @listener.granted_service_ticket_via_api @service_ticket.ticket - else - @listener.invalid_tgt_via_api + callback_granted_service_ticket + when (@service_url and not @ticket_granting_ticket) + callback_invalid_tgt + when (not @service_url and @ticket_granting_ticket) + callback_empty_service end end @@ -35,4 +38,16 @@ def create_service_ticket @service_ticket = acquire_service_ticket(@ticket_granting_ticket, @service_url) end + def callback_granted_service_ticket + @listener.granted_service_ticket_via_api @service_ticket.ticket + end + + def callback_invalid_tgt + @listener.invalid_tgt_via_api + end + + def callback_empty_service + @listener.no_service_provided_via_api + end + end diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index 68781d72..d72515ba 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -5,31 +5,42 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:service) { 'http://example.org/' } + let(:default_parameters) { {service: 'http://example.org/'} } context 'with an invalid tgt' do - let(:tgt) { 'TGT-INVALID' } + let(:parameters) { default_parameters.merge(ticket_granting_ticket: 'TGT-INVALID') } it 'calls the #invalid_tgt_via_api method on the listener' do listener.should_receive(:invalid_tgt_via_api) - processor.process(tgt, service).should be_false + processor.process(parameters).should be_false end end context 'with a valid tgt' do let(:tgt) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil) } + let(:parameters) { default_parameters.merge(ticket_granting_ticket: tgt.ticket) } it 'calls the #granted_service_ticket_via_api method on the listener' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) - processor.process(tgt.ticket, service) + processor.process(parameters) end it 'generates a ticket-granting ticket' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) expect { - processor.process(tgt.ticket, service) + processor.process(parameters) }.to change(CASinoCore::Model::ServiceTicket, :count).by(1) end + + context "without a service" do + let(:parameters) { {ticket_granting_ticket: tgt.ticket} } + + it 'calls the #no_service_provided_via_api method on the listener' do + listener.should_receive(:no_service_provided_via_api) + processor.process(parameters) + end + end + end end end From 31ebd8a55146b57867678362b1bb275460318801 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 19:51:56 +0100 Subject: [PATCH 181/350] add some documentation --- lib/casino_core/processor/api/service_ticket_provider.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 42cca100..65f1bb55 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -8,7 +8,14 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor include CASinoCore::Helper::ServiceTickets include CASinoCore::Helper::TicketGrantingTickets - + # Use this method to process the request. + # + # The method will call one of the following methods on the listener: + # * `#granted_service_ticket_via_api`: First and only argument is a String with the TS-id + # * `#invalid_tgt_via_api`: No argument + # * `#no_service_provided_via_api`: No argument + # + # @param [Hash] parameters parameters supplied by user (ticket granting ticket and service url) def process(parameters) @client_ticket_granting_ticket = parameters[:ticket_granting_ticket] @service_url = parameters[:service] From 2c2c40bf852975a1b0068a9bd08787c8fa2b9f9f Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 20:00:06 +0100 Subject: [PATCH 182/350] do not shorten this stupid name, @pencil says... --- lib/casino_core/processor/api/service_ticket_provider.rb | 4 ++-- spec/processor/api/service_ticket_provider_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 65f1bb55..df6465df 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -12,7 +12,7 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor # # The method will call one of the following methods on the listener: # * `#granted_service_ticket_via_api`: First and only argument is a String with the TS-id - # * `#invalid_tgt_via_api`: No argument + # * `#invalid_ticket_granting_ticket_via_api`: No argument # * `#no_service_provided_via_api`: No argument # # @param [Hash] parameters parameters supplied by user (ticket granting ticket and service url) @@ -50,7 +50,7 @@ def callback_granted_service_ticket end def callback_invalid_tgt - @listener.invalid_tgt_via_api + @listener.invalid_ticket_granting_ticket_via_api end def callback_empty_service diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index d72515ba..635c28da 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -11,7 +11,7 @@ let(:parameters) { default_parameters.merge(ticket_granting_ticket: 'TGT-INVALID') } it 'calls the #invalid_tgt_via_api method on the listener' do - listener.should_receive(:invalid_tgt_via_api) + listener.should_receive(:invalid_ticket_graning_ticket_via_api) processor.process(parameters).should be_false end end From 2f745c28017b26ca92870d25f0ac2132fc8373eb Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 20:01:57 +0100 Subject: [PATCH 183/350] fu typo --- spec/processor/api/service_ticket_provider_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index 635c28da..f84e0145 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -11,7 +11,7 @@ let(:parameters) { default_parameters.merge(ticket_granting_ticket: 'TGT-INVALID') } it 'calls the #invalid_tgt_via_api method on the listener' do - listener.should_receive(:invalid_ticket_graning_ticket_via_api) + listener.should_receive(:invalid_ticket_granting_ticket_via_api) processor.process(parameters).should be_false end end From 9b6e29410922a4f1cec72009b208d0ebf36e5f4f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 20:29:01 +0100 Subject: [PATCH 184/350] Extracted ticket_granting_ticket out of parameters --- .../processor/api/service_ticket_provider.rb | 9 ++++---- .../api/service_ticket_provider_spec.rb | 23 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index df6465df..45a6a6ee 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -3,7 +3,7 @@ require 'casino_core/model' require 'casino_core/builder' -# The ServiceTicketProvider processor should be used to handle API calls: POST requests to /cas/v1/tickets/{TGT id} +# The ServiceTicketProvider processor should be used to handle API calls: POST requests to /cas/v1/tickets/ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor include CASinoCore::Helper::ServiceTickets include CASinoCore::Helper::TicketGrantingTickets @@ -11,13 +11,14 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor # Use this method to process the request. # # The method will call one of the following methods on the listener: - # * `#granted_service_ticket_via_api`: First and only argument is a String with the TS-id + # * `#granted_service_ticket_via_api`: First and only argument is a String with the ST-id # * `#invalid_ticket_granting_ticket_via_api`: No argument # * `#no_service_provided_via_api`: No argument # + # @param [String] ticket_granting_ticket ticket_granting_ticket supplied by the user in the URL # @param [Hash] parameters parameters supplied by user (ticket granting ticket and service url) - def process(parameters) - @client_ticket_granting_ticket = parameters[:ticket_granting_ticket] + def process(ticket_granting_ticket, parameters) + @client_ticket_granting_ticket = ticket_granting_ticket @service_url = parameters[:service] fetch_valid_ticket_granting_ticket diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index f84e0145..21889ad5 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -5,39 +5,38 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:default_parameters) { {service: 'http://example.org/'} } + let(:parameters) { { service: 'http://example.org/' } } - context 'with an invalid tgt' do - let(:parameters) { default_parameters.merge(ticket_granting_ticket: 'TGT-INVALID') } + context 'with an invalid ticket-granting ticket' do + let(:ticket_granting_ticket) { 'TGT-INVALID' } it 'calls the #invalid_tgt_via_api method on the listener' do listener.should_receive(:invalid_ticket_granting_ticket_via_api) - processor.process(parameters).should be_false + processor.process(ticket_granting_ticket, parameters).should be_false end end - context 'with a valid tgt' do - let(:tgt) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil) } - let(:parameters) { default_parameters.merge(ticket_granting_ticket: tgt.ticket) } + context 'with a valid ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil).ticket } it 'calls the #granted_service_ticket_via_api method on the listener' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) - processor.process(parameters) + processor.process(ticket_granting_ticket, parameters) end it 'generates a ticket-granting ticket' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) expect { - processor.process(parameters) + processor.process(ticket_granting_ticket, parameters) }.to change(CASinoCore::Model::ServiceTicket, :count).by(1) end - context "without a service" do - let(:parameters) { {ticket_granting_ticket: tgt.ticket} } + context 'without a service' do + let(:parameters) { { } } it 'calls the #no_service_provided_via_api method on the listener' do listener.should_receive(:no_service_provided_via_api) - processor.process(parameters) + processor.process(ticket_granting_ticket, parameters) end end From d56549753105b700b79e9a4d57aec0d4ec5ef84c Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 20:56:31 +0100 Subject: [PATCH 185/350] move ticket removal into a helper --- lib/casino_core/helper/ticket_granting_tickets.rb | 4 ++++ lib/casino_core/processor/logout.rb | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index acfd020d..c878f05c 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -32,6 +32,10 @@ def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) }) end + def remove_ticket_granting_ticket(ticket_granting_ticket) + ticket_granting_ticket.destroy + end + end end end diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb index 64d017f8..09496a4e 100644 --- a/lib/casino_core/processor/logout.rb +++ b/lib/casino_core/processor/logout.rb @@ -18,9 +18,7 @@ def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - unless ticket_granting_ticket.nil? - ticket_granting_ticket.destroy - end + remove_ticket_granting_ticket(ticket_granting_ticket) unless ticket_granting_ticket.nil? @listener.user_logged_out(params[:url]) end end From af84a07edae4c2c53f62c556e2d5f71b8fbcd4ce Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 20:56:44 +0100 Subject: [PATCH 186/350] add an api logout processor and tests --- lib/casino_core/processor/api.rb | 1 + lib/casino_core/processor/api/logout.rb | 32 ++++++++++++++++++++++++ spec/processor/api/logout_spec.rb | 33 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 lib/casino_core/processor/api/logout.rb create mode 100644 spec/processor/api/logout_spec.rb diff --git a/lib/casino_core/processor/api.rb b/lib/casino_core/processor/api.rb index 0692ef50..6765bd64 100644 --- a/lib/casino_core/processor/api.rb +++ b/lib/casino_core/processor/api.rb @@ -3,6 +3,7 @@ class Processor module API autoload :LoginCredentialAcceptor, 'casino_core/processor/api/login_credential_acceptor.rb' autoload :ServiceTicketProvider, 'casino_core/processor/api/service_ticket_provider.rb' + autoload :Logout, 'casino_core/processor/api/logout.rb' end end end diff --git a/lib/casino_core/processor/api/logout.rb b/lib/casino_core/processor/api/logout.rb new file mode 100644 index 00000000..074a04fb --- /dev/null +++ b/lib/casino_core/processor/api/logout.rb @@ -0,0 +1,32 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The Logout processor should be used to process API DELET requests to /cas/v1/tickets/TGT-fdsjfsdfjkalfewrihfdhfaie +class CASinoCore::Processor::API::Logout < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#user_logged_out_via_api` + # + # @param [String] ticket_granting_ticket Ticket granting ticket to logout + def process(ticket_granting_ticket) + @user_ticket_granting_ticket = ticket_granting_ticket + + fetch_ticket_granting_ticket + handle_ticket_granting_ticket + callback_user_logged_out + end + + def fetch_ticket_granting_ticket + @ticket_granting_ticket = find_valid_ticket_granting_ticket(@user_ticket_granting_ticket, nil) + end + + def handle_ticket_granting_ticket + remove_ticket_granting_ticket(@ticket_granting_ticket) if @ticket_granting_ticket + end + + def callback_user_logged_out + @listener.user_logged_out_via_api + end + +end diff --git a/spec/processor/api/logout_spec.rb b/spec/processor/api/logout_spec.rb new file mode 100644 index 00000000..e170af3a --- /dev/null +++ b/spec/processor/api/logout_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe CASinoCore::Processor::API::Logout do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil) } + + it 'deletes the ticket-granting ticket' do + listener.should_receive(:user_logged_out_via_api) + processor.process(ticket_granting_ticket.ticket) + CASinoCore::Model::TicketGrantingTicket.where(id: ticket_granting_ticket.id).first.should == nil + end + + it 'calls the #user_logged_out_via_api method on the listener' do + listener.should_receive(:user_logged_out_via_api) + processor.process(ticket_granting_ticket) + end + + end + + context 'with an invlaid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + + it 'calls the #user_logged_out method on the listener' do + listener.should_receive(:user_logged_out_via_api) + processor.process(tgt) + end + end + end +end From 8a36c1fc10296d5f3156b9e7d50c0cd970f1d0d1 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 21:11:35 +0100 Subject: [PATCH 187/350] move the whole ticket fetching and removing into the helper --- lib/casino_core/helper/ticket_granting_tickets.rb | 5 +++-- lib/casino_core/processor/api/logout.rb | 12 +----------- lib/casino_core/processor/logout.rb | 3 +-- spec/processor/logout_spec.rb | 2 +- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index c878f05c..abfa3a0d 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -32,8 +32,9 @@ def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) }) end - def remove_ticket_granting_ticket(ticket_granting_ticket) - ticket_granting_ticket.destroy + def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) + tgt = find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) + tgt.destroy rescue false end end diff --git a/lib/casino_core/processor/api/logout.rb b/lib/casino_core/processor/api/logout.rb index 074a04fb..05b9c299 100644 --- a/lib/casino_core/processor/api/logout.rb +++ b/lib/casino_core/processor/api/logout.rb @@ -10,21 +10,11 @@ class CASinoCore::Processor::API::Logout < CASinoCore::Processor # # @param [String] ticket_granting_ticket Ticket granting ticket to logout def process(ticket_granting_ticket) - @user_ticket_granting_ticket = ticket_granting_ticket - fetch_ticket_granting_ticket - handle_ticket_granting_ticket + remove_ticket_granting_ticket(ticket_granting_ticket) callback_user_logged_out end - def fetch_ticket_granting_ticket - @ticket_granting_ticket = find_valid_ticket_granting_ticket(@user_ticket_granting_ticket, nil) - end - - def handle_ticket_granting_ticket - remove_ticket_granting_ticket(@ticket_granting_ticket) if @ticket_granting_ticket - end - def callback_user_logged_out @listener.user_logged_out_via_api end diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb index 09496a4e..fd09f738 100644 --- a/lib/casino_core/processor/logout.rb +++ b/lib/casino_core/processor/logout.rb @@ -17,8 +17,7 @@ class CASinoCore::Processor::Logout < CASinoCore::Processor def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} - ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - remove_ticket_granting_ticket(ticket_granting_ticket) unless ticket_granting_ticket.nil? + remove_ticket_granting_ticket(cookies[:tgt], user_agent) @listener.user_logged_out(params[:url]) end end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index f8f79041..4ed81476 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -46,4 +46,4 @@ end end end -end \ No newline at end of file +end From 5b22f2556c1e016eeb9fff62cee882975873b6a0 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Sun, 30 Dec 2012 21:15:14 +0100 Subject: [PATCH 188/350] next try... for @pencil --- lib/casino_core/helper/ticket_granting_tickets.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index abfa3a0d..de8d9297 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -34,7 +34,8 @@ def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) tgt = find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) - tgt.destroy rescue false + tgt.destroy if tgt + tgt || false end end From 72fab73632b09193a172308df8851aa338957d74 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 21:18:56 +0100 Subject: [PATCH 189/350] Refactoring (kasper around with nil) --- lib/casino_core/helper/ticket_granting_tickets.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index de8d9297..e298644a 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -34,8 +34,9 @@ def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) tgt = find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) - tgt.destroy if tgt - tgt || false + unless tgt.nil? + tgt.destroy + end end end From 16dd3428967d136cc2268c5d5dac2b7eef801b98 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 21:19:23 +0100 Subject: [PATCH 190/350] Version bump to 1.0.5 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a6a3a43c..1464c521 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.4 \ No newline at end of file +1.0.5 \ No newline at end of file From 62ce5eea7dfa1f5d3588eef098b4deb67459ef76 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 30 Dec 2012 21:19:28 +0100 Subject: [PATCH 191/350] Regenerate gemspec for version 1.0.5 --- casino_core.gemspec | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index fc247e79..97850a5e 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.4" + s.version = "1.0.5" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] @@ -56,6 +56,7 @@ Gem::Specification.new do |s| "lib/casino_core/builder.rb", "lib/casino_core/builder/ticket_validation_response.rb", "lib/casino_core/helper.rb", + "lib/casino_core/helper/authentication.rb", "lib/casino_core/helper/browser.rb", "lib/casino_core/helper/logger.rb", "lib/casino_core/helper/login_tickets.rb", @@ -72,6 +73,10 @@ Gem::Specification.new do |s| "lib/casino_core/model/service_ticket/single_sign_out_notifier.rb", "lib/casino_core/model/ticket_granting_ticket.rb", "lib/casino_core/processor.rb", + "lib/casino_core/processor/api.rb", + "lib/casino_core/processor/api/login_credential_acceptor.rb", + "lib/casino_core/processor/api/logout.rb", + "lib/casino_core/processor/api/service_ticket_provider.rb", "lib/casino_core/processor/legacy_validator.rb", "lib/casino_core/processor/login_credential_acceptor.rb", "lib/casino_core/processor/login_credential_requestor.rb", @@ -93,6 +98,9 @@ Gem::Specification.new do |s| "spec/model/service_ticket/single_sign_out_notifier_spec.rb", "spec/model/service_ticket_spec.rb", "spec/model/ticket_granting_ticket_spec.rb", + "spec/processor/api/login_credential_acceptor_spec.rb", + "spec/processor/api/logout_spec.rb", + "spec/processor/api/service_ticket_provider_spec.rb", "spec/processor/legacy_validator_spec.rb", "spec/processor/login_credential_acceptor_spec.rb", "spec/processor/login_credential_requestor_spec.rb", From de68e86d1c3b388f0014f255bf4a38ad6b3c66d7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 11:00:29 +0100 Subject: [PATCH 192/350] Store user-agent in the ticket-granting ticket --- .../api/login_credential_acceptor.rb | 5 +-- .../api/login_credential_acceptor_spec.rb | 33 ++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/casino_core/processor/api/login_credential_acceptor.rb b/lib/casino_core/processor/api/login_credential_acceptor.rb index daf486be..6b717a74 100644 --- a/lib/casino_core/processor/api/login_credential_acceptor.rb +++ b/lib/casino_core/processor/api/login_credential_acceptor.rb @@ -16,8 +16,9 @@ class CASinoCore::Processor::API::LoginCredentialAcceptor < CASinoCore::Processo # * `#invalid_login_credentials_via_api`: No argument # # @param [Hash] login_data parameters supplied by user (username and password) - def process(login_data) + def process(login_data, user_agent = nil) @login_data = login_data + @user_agent = user_agent validate_login_data @@ -39,7 +40,7 @@ def callback_user_logged_in end def generate_ticket_granting_ticket - @ticket_granting_ticket = acquire_ticket_granting_ticket(@authentication_result) + @ticket_granting_ticket = acquire_ticket_granting_ticket(@authentication_result, @user_agent) end def callback_invalid_login_credentials diff --git a/spec/processor/api/login_credential_acceptor_spec.rb b/spec/processor/api/login_credential_acceptor_spec.rb index 88cfd049..e3952283 100644 --- a/spec/processor/api/login_credential_acceptor_spec.rb +++ b/spec/processor/api/login_credential_acceptor_spec.rb @@ -4,34 +4,49 @@ describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } + let(:user_agent) { 'ThisIsATestBrwoser 1.0' } context 'with invalid credentials' do - let(:login_data) { {username: 'testuser', password: 'wrong'} } + let(:login_data) { { username: 'testuser', password: 'wrong' } } - it 'calls the #invalid_login_credentials method on the listener' do + before(:each) do + listener.stub(:invalid_login_credentials_via_api) + end + + it 'calls the #invalid_login_credentials_via_api method on the listener' do listener.should_receive(:invalid_login_credentials_via_api) - processor.process(login_data).should be_false + processor.process(login_data, user_agent).should be_false + end + + it 'does not generate a ticket-granting ticket' do + expect { + processor.process(login_data, user_agent) + }.to_not change(CASinoCore::Model::TicketGrantingTicket, :count) end end context 'with valid credentials' do - let(:login_data) { {username: 'testuser', password: 'foobar123'} } + let(:login_data) { { username: 'testuser', password: 'foobar123' } } before(:each) do - listener.stub(:user_logged_in) + listener.stub(:user_logged_in_via_api) end - it 'calls the #user_logged_in method on the listener' do + it 'calls the #user_logged_in_via_api method on the listener' do listener.should_receive(:user_logged_in_via_api).with(/^TGC\-/) - processor.process(login_data) + processor.process(login_data, user_agent) end it 'generates a ticket-granting ticket' do - listener.should_receive(:user_logged_in_via_api).with(/^TGC\-/) expect { - processor.process(login_data) + processor.process(login_data, user_agent) }.to change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) end + + it 'sets the user-agent in the ticket-granting ticket' do + processor.process(login_data, user_agent) + CASinoCore::Model::TicketGrantingTicket.last.user_agent.should == user_agent + end end end end From 444a712ee39682e46049e1d34bd611c9a0a2ab74 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 11:07:23 +0100 Subject: [PATCH 193/350] Check user-agent when logging out --- lib/casino_core/processor/api/logout.rb | 11 +++++------ spec/processor/api/logout_spec.rb | 9 +++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/casino_core/processor/api/logout.rb b/lib/casino_core/processor/api/logout.rb index 05b9c299..a23181c2 100644 --- a/lib/casino_core/processor/api/logout.rb +++ b/lib/casino_core/processor/api/logout.rb @@ -2,16 +2,15 @@ require 'casino_core/helper' require 'casino_core/model' -# The Logout processor should be used to process API DELET requests to /cas/v1/tickets/TGT-fdsjfsdfjkalfewrihfdhfaie +# The Logout processor should be used to process API DELETE requests to /cas/v1/tickets/ class CASinoCore::Processor::API::Logout < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets - # This method will call `#user_logged_out_via_api` + # This method will call `#user_logged_out_via_api` on the listener. # - # @param [String] ticket_granting_ticket Ticket granting ticket to logout - def process(ticket_granting_ticket) - - remove_ticket_granting_ticket(ticket_granting_ticket) + # @param [String] ticket_granting_ticket Ticket-granting ticket to logout + def process(ticket_granting_ticket, user_agent = nil) + remove_ticket_granting_ticket(ticket_granting_ticket, user_agent) callback_user_logged_out end diff --git a/spec/processor/api/logout_spec.rb b/spec/processor/api/logout_spec.rb index e170af3a..91d36b32 100644 --- a/spec/processor/api/logout_spec.rb +++ b/spec/processor/api/logout_spec.rb @@ -6,22 +6,23 @@ let(:processor) { described_class.new(listener) } context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil) } + let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket) } + let(:user_agent) { ticket_granting_ticket.user_agent } it 'deletes the ticket-granting ticket' do listener.should_receive(:user_logged_out_via_api) - processor.process(ticket_granting_ticket.ticket) + processor.process(ticket_granting_ticket.ticket, user_agent) CASinoCore::Model::TicketGrantingTicket.where(id: ticket_granting_ticket.id).first.should == nil end it 'calls the #user_logged_out_via_api method on the listener' do listener.should_receive(:user_logged_out_via_api) - processor.process(ticket_granting_ticket) + processor.process(ticket_granting_ticket, user_agent) end end - context 'with an invlaid ticket-granting ticket' do + context 'with an invalid ticket-granting ticket' do let(:tgt) { 'TGT-lalala' } it 'calls the #user_logged_out method on the listener' do From 6f22ed1ca295c2b1df3211d9c006a73acdcca12d Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 11:19:05 +0100 Subject: [PATCH 194/350] Check user-agent before providing service tickets --- .../processor/api/service_ticket_provider.rb | 16 ++++++++++------ .../api/service_ticket_provider_spec.rb | 10 ++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 45a6a6ee..120d2a02 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -11,15 +11,19 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor # Use this method to process the request. # # The method will call one of the following methods on the listener: - # * `#granted_service_ticket_via_api`: First and only argument is a String with the ST-id - # * `#invalid_ticket_granting_ticket_via_api`: No argument - # * `#no_service_provided_via_api`: No argument + # * `#granted_service_ticket_via_api`: First and only argument is a String with the service ticket. + # The service ticket (and nothing else) should be displayed. + # * `#invalid_ticket_granting_ticket_via_api`: No argument. The application should respond with status "400 Bad Request" + # * `#no_service_provided_via_api`: No argument. The application should respond with status "400 Bad Request" # # @param [String] ticket_granting_ticket ticket_granting_ticket supplied by the user in the URL - # @param [Hash] parameters parameters supplied by user (ticket granting ticket and service url) - def process(ticket_granting_ticket, parameters) + # @param [Hash] parameters parameters supplied by user (`service` in particular) + # @param [String] user_agent user-agent delivered by the client + def process(ticket_granting_ticket, parameters = nil, user_agent = nil) + parameters ||= {} @client_ticket_granting_ticket = ticket_granting_ticket @service_url = parameters[:service] + @user_agent = user_agent fetch_valid_ticket_granting_ticket handle_ticket_granting_ticket @@ -27,7 +31,7 @@ def process(ticket_granting_ticket, parameters) private def fetch_valid_ticket_granting_ticket - @ticket_granting_ticket = find_valid_ticket_granting_ticket(@client_ticket_granting_ticket, nil) + @ticket_granting_ticket = find_valid_ticket_granting_ticket(@client_ticket_granting_ticket, @user_agent) end def handle_ticket_granting_ticket diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index 21889ad5..f702a50c 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -17,17 +17,19 @@ end context 'with a valid ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket, user_agent: nil).ticket } + let(:ticket_granting_ticket) { FactoryGirl.create(:ticket_granting_ticket) } + let(:ticket) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } it 'calls the #granted_service_ticket_via_api method on the listener' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) - processor.process(ticket_granting_ticket, parameters) + processor.process(ticket, parameters, user_agent) end it 'generates a ticket-granting ticket' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) expect { - processor.process(ticket_granting_ticket, parameters) + processor.process(ticket, parameters, user_agent) }.to change(CASinoCore::Model::ServiceTicket, :count).by(1) end @@ -36,7 +38,7 @@ it 'calls the #no_service_provided_via_api method on the listener' do listener.should_receive(:no_service_provided_via_api) - processor.process(ticket_granting_ticket, parameters) + processor.process(ticket, parameters, user_agent) end end From 2fc5e1abe823e0928ca320ba144d4eff5bc76acc Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 11:21:34 +0100 Subject: [PATCH 195/350] Version bump to 1.0.6 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1464c521..ece61c60 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.5 \ No newline at end of file +1.0.6 \ No newline at end of file From aab66eea92925f3d7b17323f77746f6aba86dcf7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 11:22:12 +0100 Subject: [PATCH 196/350] Regenerate gemspec for version 1.0.6 --- casino_core.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 97850a5e..89902f90 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.5" + s.version = "1.0.6" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-30" + s.date = "2012-12-31" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ From 23119d6be173cd151ab67a311d18104c32f3bb31 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 14:50:04 +0100 Subject: [PATCH 197/350] rbCAS --- README.md | 2 +- Rakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index adaa6620..586da9e7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CASinoCore [![Build Status](https://secure.travis-ci.org/pencil/CASinoCore.png?branch=master)](https://travis-ci.org/pencil/CASinoCore) +# CASinoCore [![Build Status](https://secure.travis-ci.org/rbCAS/CASinoCore.png?branch=master)](https://travis-ci.org/pencil/CASinoCore) A CAS server core library. diff --git a/Rakefile b/Rakefile index febd2d8c..1f709922 100644 --- a/Rakefile +++ b/Rakefile @@ -20,7 +20,7 @@ require 'jeweler' Jeweler::Tasks.new do |gem| # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options gem.name = "casino_core" - gem.homepage = "http://github.com/pencil/CASinoCore" + gem.homepage = "http://rbcas.org/" gem.license = "MIT" gem.summary = "A CAS server core library." gem.description = gem.summary From 2c1a467b729680d178172fcf77f5e10b1aa77cf1 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 31 Dec 2012 14:51:53 +0100 Subject: [PATCH 198/350] Fixed link to Travis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 586da9e7..bb7ad17f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CASinoCore [![Build Status](https://secure.travis-ci.org/rbCAS/CASinoCore.png?branch=master)](https://travis-ci.org/pencil/CASinoCore) +# CASinoCore [![Build Status](https://secure.travis-ci.org/rbCAS/CASinoCore.png?branch=master)](https://travis-ci.org/rbCAS/CASinoCore) A CAS server core library. From 8d2f087373f48a00d9f049c35aef18c4bd7624bf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 11:50:42 +0100 Subject: [PATCH 199/350] Regsiter CAS and CASino as acronyoms --- lib/casino_core.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index bf8d2f07..b85c432f 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -19,6 +19,11 @@ def setup(environment = nil, options = {}) config = YAML.load_file('config/cas.yml')[@environment].symbolize_keys recursive_symbolize_keys!(config) CASinoCore::Settings.init config + + ActiveSupport::Inflector.inflections do |inflect| + inflect.acronym 'CAS' + inflect.acronym 'CASino' + end end private From 3769309bdd2d687045509ddfd31bb2d2edb270bb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 11:51:55 +0100 Subject: [PATCH 200/350] Better handling of user-agent --- lib/casino_core/model/ticket_granting_ticket.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 4b234779..2538d169 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -10,8 +10,14 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base after_destroy :destroy_proxy_granting_tickets def browser_info - user_agent = UserAgent.parse(self.user_agent) - "#{user_agent.browser} (#{user_agent.platform})" + unless self.user_agent.blank? + user_agent = UserAgent.parse(self.user_agent) + if user_agent.platform.nil? + "#{user_agent.browser}" + else + "#{user_agent.browser} (#{user_agent.platform})" + end + end end def same_user?(other_ticket) From fd2f9e140d4d400d79dfef7423ee2fa4cce543c3 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 13:31:08 +0100 Subject: [PATCH 201/350] Error for Authenticators --- lib/casino_core/authenticator.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/casino_core/authenticator.rb b/lib/casino_core/authenticator.rb index dd625777..efddcc44 100644 --- a/lib/casino_core/authenticator.rb +++ b/lib/casino_core/authenticator.rb @@ -2,6 +2,8 @@ module CASinoCore class Authenticator autoload :Static, 'casino_core/authenticator/static.rb' + class AuthenticatorError < StandardError; end + def validate(username, password) raise NotImplementedError, "This method must be implemented by a class extending #{self.class}" end From ee9cab79afb1b6627aaf68dd9ebd386f6bfe1d96 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 14:12:46 +0100 Subject: [PATCH 202/350] Handle exceptions from authenticators --- lib/casino_core/helper/authentication.rb | 6 +++++- spec/processor/login_credential_acceptor_spec.rb | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/helper/authentication.rb b/lib/casino_core/helper/authentication.rb index 95233b72..66e0cf3d 100644 --- a/lib/casino_core/helper/authentication.rb +++ b/lib/casino_core/helper/authentication.rb @@ -5,7 +5,11 @@ module Authentication def validate_login_credentials(username, password) authentication_result = nil CASinoCore::Settings.authenticators.each do |authenticator_name, authenticator| - data = authenticator.validate(username, password) + begin + data = authenticator.validate(username, password) + rescue CASinoCore::Authenticator::AuthenticatorError => e + logger.error "Authenticator '#{authenticator_name}' (#{authenticator.class}) raised an error: #{e}" + end if data authentication_result = { authenticator: authenticator_name, user_data: data } logger.info("Credentials for username '#{data[:username]}' successfully validated using authenticator '#{authenticator_name}' (#{authenticator.class})") diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 68d21ae0..ba75e611 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -32,18 +32,32 @@ end context 'with valid credentials' do + let(:service) { 'https://www.example.org' } let(:login_data) { { lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service } } before(:each) do listener.stub(:user_logged_in) end + context 'when all authenticators raise an error' do + before(:each) do + CASinoCore::Authenticator::Static.any_instance.stub(:validate) do + raise CASinoCore::Authenticator::AuthenticatorError, 'error123' + end + end + + it 'calls the #invalid_login_credentials method on the listener' do + listener.should_receive(:invalid_login_credentials).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(login_data) + end + end + context 'without a service' do let(:service) { nil } it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil, /^TGC\-/) - processor.process(lt: login_ticket.ticket, username: 'testuser', password: 'foobar123') + processor.process(login_data) end it 'generates a ticket-granting ticket' do From a99995dc3d125677c43002463071d04fc1c64a3b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 14:37:43 +0100 Subject: [PATCH 203/350] More tests --- lib/casino_core/model/proxy_ticket.rb | 2 +- lib/casino_core/model/service_ticket.rb | 2 +- spec/model/proxy_ticket_spec.rb | 49 +++++++++++++---- spec/model/service_ticket_spec.rb | 45 ++++++++++++++-- spec/model/ticket_granting_ticket_spec.rb | 65 ++++++++++++++++++++++- spec/spec_helper.rb | 24 +++++---- 6 files changed, 161 insertions(+), 26 deletions(-) diff --git a/lib/casino_core/model/proxy_ticket.rb b/lib/casino_core/model/proxy_ticket.rb index 62c8bde2..e270ac82 100644 --- a/lib/casino_core/model/proxy_ticket.rb +++ b/lib/casino_core/model/proxy_ticket.rb @@ -22,6 +22,6 @@ def expired? else CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed] end - Time.now - self.created_at > lifetime + (Time.now - (self.created_at || Time.now)) > lifetime end end diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index f4ac3213..b7ae6bf3 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -35,7 +35,7 @@ def expired? else CASinoCore::Settings.service_ticket[:lifetime_unconsumed] end - Time.now - self.created_at > lifetime + (Time.now - (self.created_at || Time.now)) > lifetime end private diff --git a/spec/model/proxy_ticket_spec.rb b/spec/model/proxy_ticket_spec.rb index d2825db0..792951c4 100644 --- a/spec/model/proxy_ticket_spec.rb +++ b/spec/model/proxy_ticket_spec.rb @@ -1,32 +1,63 @@ require 'spec_helper' describe CASinoCore::Model::ProxyTicket do - let(:ticket) { - ticket = described_class.new ticket: 'PT-12345', service: 'any string is valid' + let(:unconsumed_ticket) { + ticket = described_class.new ticket: 'PT-12345', service: 'any_string_is_valid' ticket.proxy_granting_ticket_id = 1 ticket } + let(:consumed_ticket) { + ticket = described_class.new ticket: 'PT-54321', service: 'any_string_is_valid' + ticket.proxy_granting_ticket_id = 1 + ticket.consumed = true + ticket.save! + ticket + } + + describe '#expired?' do + [:unconsumed, :consumed].each do |state| + context "with an #{state} ticket" do + let(:ticket) { send("#{state}_ticket") } + + context 'with an expired ticket' do + before(:each) do + ticket.created_at = (CASinoCore::Settings.service_ticket[:"lifetime_#{state}"].seconds + 1).ago + ticket.save! + end + + it 'returns true' do + ticket.expired?.should == true + end + end + + context 'with an unexpired ticket' do + it 'returns false' do + ticket.expired?.should == false + end + end + end + end + end describe '.cleanup_unconsumed' do it 'deletes expired unconsumed service tickets' do - ticket.created_at = 10.hours.ago - ticket.save! + unconsumed_ticket.created_at = 10.hours.ago + unconsumed_ticket.save! lambda do described_class.cleanup_unconsumed end.should change(described_class, :count).by(-1) - described_class.find_by_ticket('ST-12345').should be_false + described_class.find_by_ticket('PT-12345').should be_false end end describe '.cleanup_consumed' do it 'deletes expired consumed service tickets' do - ticket.consumed = true - ticket.created_at = 10.days.ago - ticket.save! + consumed_ticket.created_at = 10.days.ago + consumed_ticket.save! lambda do described_class.cleanup_consumed end.should change(described_class, :count).by(-1) - described_class.find_by_ticket('ST-12345').should be_false + described_class.find_by_ticket('PT-12345').should be_false end end end diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index d2f8268b..7fb69257 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CASinoCore::Model::ServiceTicket do - let(:ticket) { + let(:unconsumed_ticket) { ticket = described_class.new ticket: 'ST-12345', service: 'https://example.com/cas-service' ticket.ticket_granting_ticket_id = 1 ticket @@ -14,10 +14,35 @@ ticket } + describe '#expired?' do + [:unconsumed, :consumed].each do |state| + context "with an #{state} ticket" do + let(:ticket) { send("#{state}_ticket") } + + context 'with an expired ticket' do + before(:each) do + ticket.created_at = (CASinoCore::Settings.service_ticket[:"lifetime_#{state}"].seconds + 1).ago + ticket.save! + end + + it 'returns true' do + ticket.expired?.should == true + end + end + + context 'with an unexpired ticket' do + it 'returns false' do + ticket.expired?.should == false + end + end + end + end + end + describe '.cleanup_unconsumed' do it 'deletes expired unconsumed service tickets' do - ticket.created_at = 10.hours.ago - ticket.save! + unconsumed_ticket.created_at = 10.hours.ago + unconsumed_ticket.save! lambda do described_class.cleanup_unconsumed end.should change(described_class, :count).by(-1) @@ -25,6 +50,20 @@ end end + describe '.cleanup_consumed_hard' do + before(:each) do + described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(false) + end + + it 'deletes consumed service tickets with an unreachable Single Sign Out callback server' do + consumed_ticket.created_at = 10.days.ago + consumed_ticket.save! + lambda do + described_class.cleanup_consumed_hard + end.should change(described_class, :count).by(-1) + end + end + describe '.cleanup_consumed' do before(:each) do described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(true) diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 1b733865..094b4b76 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' +require 'useragent' describe CASinoCore::Model::TicketGrantingTicket do - let(:ticket_granting_ticket) { described_class.create! ticket: 'TGT-3ep9awhy2ty5UhL8wM1xAZ', username: 'example-user', authenticator: 'test' } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:service_ticket) { FactoryGirl.create :service_ticket, ticket_granting_ticket: ticket_granting_ticket } let(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket } @@ -25,4 +26,66 @@ end end end + + describe '#browser_info' do + let(:user_agent) { Object.new } + before(:each) do + user_agent.stub(:browser).and_return('TestBrowser') + UserAgent.stub(:parse).and_return(user_agent) + end + + context 'without platform' do + before(:each) do + user_agent.stub(:platform).and_return(nil) + end + + it 'returns the browser name' do + ticket_granting_ticket.browser_info.should == 'TestBrowser' + end + end + + context 'with a platform' do + before(:each) do + user_agent.stub(:platform).and_return('Linux') + end + + it 'returns the browser name' do + ticket_granting_ticket.browser_info.should == 'TestBrowser (Linux)' + end + end + end + + describe '#same_user?' do + context 'with a nil value' do + let(:other_ticket_granting_ticket) { nil } + + it 'should return false' do + ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == false + end + end + + context 'with a ticket from another user' do + let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, username: 'bla' } + + it 'should return false' do + ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == false + end + end + + context 'with a ticket from another authenticator' do + let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, authenticator: 'bla' } + + it 'should return false' do + ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == false + end + end + + context 'with a ticket from the same user and authenticator' do + let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + + it 'should return true' do + ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == true + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b8eb47ca..9d880508 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,20 @@ +require 'active_support/core_ext' +require 'simplecov' + +SimpleCov.start do + add_filter '/spec' + base_path = "#{File.dirname(__FILE__)}/../" + Dir["#{base_path}lib/casino_core/*.rb"].each do |f| + f.gsub!(/\A#{base_path}(.+)\.rb\z/, '\1') + name = File.basename(f).humanize.pluralize + add_group name, f + end +end + require 'database_cleaner' require 'logger' require 'webmock/rspec' require 'casino_core' -require 'simplecov' # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. @@ -24,16 +36,6 @@ # --seed 1234 config.order = 'random' - SimpleCov.start do - add_filter '/spec' - base_path = "#{File.dirname(__FILE__)}/../" - Dir["#{base_path}lib/casino_core/*.rb"].each do |f| - f.gsub!(/\A#{base_path}(.+)\.rb\z/, '\1') - name = File.basename(f).humanize.pluralize - add_group name, f - end - end - CASinoCore.setup 'test' CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN From 7efc5f88c039d391134f8b9c38edde47320d3628 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 14:39:07 +0100 Subject: [PATCH 204/350] Version bump to 1.0.7 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ece61c60..f9cbc01a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.6 \ No newline at end of file +1.0.7 \ No newline at end of file From 6b8f44d4863819254b7d5650382616940dd6dcbe Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 14:39:07 +0100 Subject: [PATCH 205/350] Regenerate gemspec for version 1.0.7 --- casino_core.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 89902f90..5e0ae0dd 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.6" + s.version = "1.0.7" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] - s.date = "2012-12-31" + s.date = "2013-01-01" s.description = "A CAS server core library." s.email = "ncaspar@me.com" s.extra_rdoc_files = [ @@ -117,7 +117,7 @@ Gem::Specification.new do |s| "spec/support/factories/service_ticket_factory.rb", "spec/support/factories/ticket_granting_ticket_factory.rb" ] - s.homepage = "http://github.com/pencil/CASinoCore" + s.homepage = "http://rbcas.org/" s.licenses = ["MIT"] s.require_paths = ["lib"] s.rubygems_version = "1.8.24" From 7c4f9b8a0169d5d2715e67db29848e905cde7ae6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:22:30 +0100 Subject: [PATCH 206/350] Add gemspec to Gemfile --- Gemfile | 5 ++++- Gemfile.lock | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 04eb9df2..d37a2e49 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,7 @@ -source 'http://rubygems.org' +source :rubygems + +gemspec + # Add dependencies required to use your gem here. # Example: # gem 'activesupport', '>= 2.3.5' diff --git a/Gemfile.lock b/Gemfile.lock index c063329e..1b742e2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,11 @@ +PATH + remote: . + specs: + casino_core (1.0.7) + activerecord (~> 3.2.9) + addressable (~> 2.3.2) + useragent (~> 0.4.13) + GEM remote: http://rubygems.org/ specs: @@ -61,6 +69,7 @@ DEPENDENCIES activerecord (~> 3.2.9) addressable (~> 2.3.2) bundler (~> 1.2.0) + casino_core! database_cleaner factory_girl (~> 4.1.0) jeweler (~> 1.8.4) From 255dce29473f657f4d6d2f017cbfa0bb5419011f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:23:03 +0100 Subject: [PATCH 207/350] Allow dynamic loading of authenticators --- lib/casino_core/settings.rb | 18 ++++++++++++- spec/settings_spec.rb | 53 +++++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 6 ++--- 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 spec/settings_spec.rb diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 3c568ee1..488016c1 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -20,11 +20,27 @@ def authenticators=(authenticators) @authenticators = {} authenticators.each do |index, authenticator| unless authenticator.is_a?(CASinoCore::Authenticator) - authenticator = authenticator[:class].constantize.new(authenticator[:options]) + if authenticator[:class] + authenticator = authenticator[:class].constantize.new(authenticator[:options]) + else + authenticator = load_and_instantiate_authenticator(authenticator[:authenticator], authenticator[:options]) + end end @authenticators[index] = authenticator end end + + private + def load_and_instantiate_authenticator(name, options) + gemname = "casino_core-authenticator-#{name.underscore}" + classname = name.classify + begin + require gemname + CASinoCore::Authenticator.const_get(classname).new(options) + rescue LoadError + raise LoadError, "Authenticator '#{name}' not found. Please include \"gem '#{gemname}'\" in your Gemfile and try again." + end + end end end end diff --git a/spec/settings_spec.rb b/spec/settings_spec.rb new file mode 100644 index 00000000..3c9be5b3 --- /dev/null +++ b/spec/settings_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe CASinoCore::Settings do + describe '#authenticators=' do + context 'with an authenticator name' do + let(:authenticator_name) { 'testing' } + let(:gem_name) { "casino_core-authenticator-#{authenticator_name}" } + let(:options) { { } } + let(:authenticators) { + { + test_1: { + authenticator: authenticator_name, + options: options + } + } + } + + context 'when the authenticator exists' do + let(:class_name) { 'Testing' } + let(:authenticator) { CASinoCore::Authenticator::Static } + + before(:each) do + CASinoCore::Settings.stub(:require) + CASinoCore::Authenticator.stub(:const_get).and_return(authenticator) + end + + it 'loads the required file' do + CASinoCore::Settings.should_receive(:require).with(gem_name) + described_class.authenticators = authenticators + end + + it 'instantiates the authenticator' do + CASinoCore::Authenticator.should_receive(:const_get).with(class_name).and_return(authenticator) + described_class.authenticators = authenticators + end + end + + context 'when the authenticator does not exist' do + before(:each) do + CASinoCore::Settings.stub(:require) do + raise LoadError, 'cannot load such file' + end + end + + it 'raises an error' do + lambda { + described_class.authenticators = authenticators + }.should raise_error(LoadError) + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9d880508..89ef0515 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -36,15 +36,15 @@ # --seed 1234 config.order = 'random' - CASinoCore.setup 'test' - CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN config.before(:suite) do DatabaseCleaner.strategy = :transaction - DatabaseCleaner.clean_with(:truncation) end config.before(:each) do + CASinoCore.setup 'test' + CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN + DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.start end From d8c82efd6cdc3b738bd9c1cf175db4ebb46847b9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:24:18 +0100 Subject: [PATCH 208/350] Version bump to 1.0.8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f9cbc01a..337a6a8f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.7 \ No newline at end of file +1.0.8 \ No newline at end of file From ee4e87daa512b70c2582ff8e2b837ab6c68993ce Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:24:22 +0100 Subject: [PATCH 209/350] Regenerate gemspec for version 1.0.8 --- casino_core.gemspec | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 5e0ae0dd..36724123 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.7" + s.version = "1.0.8" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] @@ -110,6 +110,7 @@ Gem::Specification.new do |s| "spec/processor/session_destroyer_spec.rb", "spec/processor/session_overview_spec.rb", "spec/processor/ticket_validator_spec.rb", + "spec/settings_spec.rb", "spec/spec_helper.rb", "spec/support/factories/login_ticket_factory.rb", "spec/support/factories/proxy_granting_ticket_factory.rb", @@ -127,6 +128,7 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, ["~> 3.2.9"]) s.add_runtime_dependency(%q, ["~> 2.3.2"]) s.add_runtime_dependency(%q, ["~> 0.4.13"]) @@ -141,7 +143,19 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, ["~> 4.1.0"]) + s.add_development_dependency(%q, ["~> 1.2.0"]) + s.add_development_dependency(%q, ["~> 1.8.4"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 0.8.3"]) + s.add_development_dependency(%q, ["~> 2.12.0"]) + s.add_development_dependency(%q, ["~> 0.7.1"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 4.1.0"]) else + s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) s.add_dependency(%q, ["~> 0.4.13"]) @@ -156,8 +170,20 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 4.1.0"]) + s.add_dependency(%q, ["~> 1.2.0"]) + s.add_dependency(%q, ["~> 1.8.4"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.8.3"]) + s.add_dependency(%q, ["~> 2.12.0"]) + s.add_dependency(%q, ["~> 0.7.1"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 4.1.0"]) end else + s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) s.add_dependency(%q, ["~> 0.4.13"]) @@ -172,6 +198,17 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 4.1.0"]) + s.add_dependency(%q, ["~> 1.2.0"]) + s.add_dependency(%q, ["~> 1.8.4"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.8.3"]) + s.add_dependency(%q, ["~> 2.12.0"]) + s.add_dependency(%q, ["~> 0.7.1"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 4.1.0"]) end end From a0d0d3448bcfe142c898a7ea7afa5da64775a25b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:27:46 +0100 Subject: [PATCH 210/350] Grrrr... Fu gemspec --- Gemfile.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1b742e2a..ddc2136f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,10 @@ PATH remote: . specs: - casino_core (1.0.7) + casino_core (1.0.8) activerecord (~> 3.2.9) addressable (~> 2.3.2) + casino_core useragent (~> 0.4.13) GEM From 38b07b18105b9739251e2f2517ed25cfb0834e8e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:31:25 +0100 Subject: [PATCH 211/350] Cleanup --- Gemfile | 2 -- Gemfile.lock | 10 ---------- casino_core.gemspec | 36 ------------------------------------ 3 files changed, 48 deletions(-) diff --git a/Gemfile b/Gemfile index d37a2e49..25cb2858 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,5 @@ source :rubygems -gemspec - # Add dependencies required to use your gem here. # Example: # gem 'activesupport', '>= 2.3.5' diff --git a/Gemfile.lock b/Gemfile.lock index ddc2136f..c063329e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,12 +1,3 @@ -PATH - remote: . - specs: - casino_core (1.0.8) - activerecord (~> 3.2.9) - addressable (~> 2.3.2) - casino_core - useragent (~> 0.4.13) - GEM remote: http://rubygems.org/ specs: @@ -70,7 +61,6 @@ DEPENDENCIES activerecord (~> 3.2.9) addressable (~> 2.3.2) bundler (~> 1.2.0) - casino_core! database_cleaner factory_girl (~> 4.1.0) jeweler (~> 1.8.4) diff --git a/casino_core.gemspec b/casino_core.gemspec index 36724123..32b089ff 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -128,7 +128,6 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) s.add_runtime_dependency(%q, ["~> 3.2.9"]) s.add_runtime_dependency(%q, ["~> 2.3.2"]) s.add_runtime_dependency(%q, ["~> 0.4.13"]) @@ -143,19 +142,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, ["~> 4.1.0"]) - s.add_development_dependency(%q, ["~> 1.2.0"]) - s.add_development_dependency(%q, ["~> 1.8.4"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 0.8.3"]) - s.add_development_dependency(%q, ["~> 2.12.0"]) - s.add_development_dependency(%q, ["~> 0.7.1"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 4.1.0"]) else - s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) s.add_dependency(%q, ["~> 0.4.13"]) @@ -170,20 +157,8 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 4.1.0"]) - s.add_dependency(%q, ["~> 1.2.0"]) - s.add_dependency(%q, ["~> 1.8.4"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 0.8.3"]) - s.add_dependency(%q, ["~> 2.12.0"]) - s.add_dependency(%q, ["~> 0.7.1"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 4.1.0"]) end else - s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 3.2.9"]) s.add_dependency(%q, ["~> 2.3.2"]) s.add_dependency(%q, ["~> 0.4.13"]) @@ -198,17 +173,6 @@ Gem::Specification.new do |s| s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, ["~> 4.1.0"]) - s.add_dependency(%q, ["~> 1.2.0"]) - s.add_dependency(%q, ["~> 1.8.4"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 0.8.3"]) - s.add_dependency(%q, ["~> 2.12.0"]) - s.add_dependency(%q, ["~> 0.7.1"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 4.1.0"]) end end From eb6fccad783d3710a7a371041caa6be9ad9e18e4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:31:32 +0100 Subject: [PATCH 212/350] Version bump to 1.0.9 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 337a6a8f..e5a4a5e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.8 \ No newline at end of file +1.0.9 \ No newline at end of file From 8d21d42d6147488e4367a21b000f6df3bf4444d6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 16:31:35 +0100 Subject: [PATCH 213/350] Regenerate gemspec for version 1.0.9 --- casino_core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 32b089ff..cc129ec5 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = "casino_core" - s.version = "1.0.8" + s.version = "1.0.9" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nils Caspar"] From d3cec7366a2d14a41095a6e2a0a0775e5bacbf8f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 17:58:49 +0100 Subject: [PATCH 214/350] No more Jeweler --- Gemfile | 28 +----- Gemfile.lock | 42 ++++---- Rakefile | 38 ++----- casino_core.gemspec | 196 +++++-------------------------------- lib/casino_core/version.rb | 3 + 5 files changed, 55 insertions(+), 252 deletions(-) create mode 100644 lib/casino_core/version.rb diff --git a/Gemfile b/Gemfile index 25cb2858..e45e65f8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,28 +1,2 @@ source :rubygems - -# Add dependencies required to use your gem here. -# Example: -# gem 'activesupport', '>= 2.3.5' - -# Add dependencies to develop your gem here. -# Include everything needed to run rake, tests, features, etc. -group :development do - gem 'bundler', '~> 1.2.0' - gem 'jeweler', '~> 1.8.4' - gem 'redcarpet' - gem 'yard', '~> 0.8.3', require: 'redcarpet' -end - -group :development, :test do - gem 'rspec', '~> 2.12.0' - gem 'simplecov', '~> 0.7.1' - gem 'sqlite3' - gem 'database_cleaner' - gem 'webmock' - gem 'nokogiri' - gem 'factory_girl', '~> 4.1.0' -end - -gem 'activerecord', '~> 3.2.9' -gem 'addressable', '~> 2.3.2' -gem 'useragent', '~> 0.4.13' +gemspec diff --git a/Gemfile.lock b/Gemfile.lock index c063329e..daa70c9d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,11 @@ +PATH + remote: . + specs: + casino_core (1.0.9) + activerecord (~> 3.2.9) + addressable (~> 2.3) + useragent (~> 0.4) + GEM remote: http://rubygems.org/ specs: @@ -20,20 +28,10 @@ GEM diff-lcs (1.1.3) factory_girl (4.1.0) activesupport (>= 3.0.0) - git (1.2.5) i18n (0.6.1) - jeweler (1.8.4) - bundler (~> 1.0) - git (>= 1.2.5) - rake - rdoc - json (1.7.5) multi_json (1.5.0) nokogiri (1.5.6) rake (10.0.3) - rdoc (3.12) - json (~> 1.4) - redcarpet (2.2.2) rspec (2.12.0) rspec-core (~> 2.12.0) rspec-expectations (~> 2.12.0) @@ -58,17 +56,13 @@ PLATFORMS ruby DEPENDENCIES - activerecord (~> 3.2.9) - addressable (~> 2.3.2) - bundler (~> 1.2.0) - database_cleaner - factory_girl (~> 4.1.0) - jeweler (~> 1.8.4) - nokogiri - redcarpet - rspec (~> 2.12.0) - simplecov (~> 0.7.1) - sqlite3 - useragent (~> 0.4.13) - webmock - yard (~> 0.8.3) + casino_core! + database_cleaner (~> 0.9) + factory_girl (~> 4.1) + nokogiri (~> 1.5) + rake (~> 10.0) + rspec (~> 2.12) + simplecov (~> 0.7) + sqlite3 (~> 1.3) + webmock (~> 1.9) + yard (~> 0.8) diff --git a/Rakefile b/Rakefile index 1f709922..6c6daf54 100644 --- a/Rakefile +++ b/Rakefile @@ -1,44 +1,20 @@ -# encoding: utf-8 -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) - -require 'rubygems' require 'bundler' -begin - Bundler.setup(:default, :development) -rescue Bundler::BundlerError => e - $stderr.puts e.message - $stderr.puts "Run `bundle install` to install missing gems" - exit e.status_code -end require 'rake' +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' +require 'yard' require 'casino_core' -CASinoCore.setup 'development' -CASinoCore::RakeTasks.load_tasks -require 'jeweler' -Jeweler::Tasks.new do |gem| - # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options - gem.name = "casino_core" - gem.homepage = "http://rbcas.org/" - gem.license = "MIT" - gem.summary = "A CAS server core library." - gem.description = gem.summary - gem.email = "ncaspar@me.com" - gem.authors = ["Nils Caspar"] - # dependencies defined in Gemfile -end -Jeweler::RubygemsDotOrgTasks.new +task :default => :spec + +CASinoCore::RakeTasks.load_tasks -require 'yard' YARD::Rake::YardocTask.new do |t| t.files = FileList['lib/**/*.rb'] end -require 'rspec/core' -require 'rspec/core/rake_task' +desc 'Run all specs' RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = FileList['spec/**/*_spec.rb'] end - -task :default => :spec diff --git a/casino_core.gemspec b/casino_core.gemspec index cc129ec5..511af809 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -1,178 +1,34 @@ -# Generated by jeweler -# DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +$:.push File.expand_path('../lib', __FILE__) +require 'casino_core/version' Gem::Specification.new do |s| - s.name = "casino_core" - s.version = "1.0.9" + s.name = 'casino_core' + s.version = CASinoCore::VERSION + s.authors = ['Nils Caspar'] + s.email = ['ncaspar@me.com'] + s.homepage = 'http://rbcas.org/' + s.license = 'MIT' + s.summary = 'A CAS server core library.' + s.description = 'CASinoCore is a CAS server library. It can be used by other projects to build a fully functional CAS server.' - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Nils Caspar"] - s.date = "2013-01-01" - s.description = "A CAS server core library." - s.email = "ncaspar@me.com" - s.extra_rdoc_files = [ - "LICENSE.txt", - "README.md" - ] - s.files = [ - ".document", - ".rspec", - ".rvmrc", - ".travis.yml", - "Gemfile", - "Gemfile.lock", - "LICENSE.txt", - "README.md", - "Rakefile", - "VERSION", - "casino_core.gemspec", - "config/cas.yml", - "config/database.yml", - "db/migrate/20121112154930_create_ticket_granting_tickets.rb", - "db/migrate/20121112160009_create_login_tickets.rb", - "db/migrate/20121112165804_ticket_should_not_be_null.rb", - "db/migrate/20121122180310_add_user_agent_to_ticket_granting_tickets.rb", - "db/migrate/20121124170004_add_index_for_username_to_ticket_granting_tickets.rb", - "db/migrate/20121124183542_create_service_tickets.rb", - "db/migrate/20121124183732_add_ticket_indexes.rb", - "db/migrate/20121124195013_add_consumed_to_service_tickets.rb", - "db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb", - "db/migrate/20121125185415_create_proxy_granting_tickets.rb", - "db/migrate/20121125190013_tickets_should_be_unique.rb", - "db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb", - "db/migrate/20121224113737_create_proxy_tickets.rb", - "db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb", - "db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb", - "db/migrate/20121225231713_no_default_granter_type.rb", - "db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb", - "db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb", - "db/migrate/20121231114141_add_authenticator_to_ticket_granting_tickets.rb", - "db/schema.rb", - "lib/casino_core.rb", - "lib/casino_core/authenticator.rb", - "lib/casino_core/authenticator/static.rb", - "lib/casino_core/builder.rb", - "lib/casino_core/builder/ticket_validation_response.rb", - "lib/casino_core/helper.rb", - "lib/casino_core/helper/authentication.rb", - "lib/casino_core/helper/browser.rb", - "lib/casino_core/helper/logger.rb", - "lib/casino_core/helper/login_tickets.rb", - "lib/casino_core/helper/proxy_granting_tickets.rb", - "lib/casino_core/helper/proxy_tickets.rb", - "lib/casino_core/helper/service_tickets.rb", - "lib/casino_core/helper/ticket_granting_tickets.rb", - "lib/casino_core/helper/tickets.rb", - "lib/casino_core/model.rb", - "lib/casino_core/model/login_ticket.rb", - "lib/casino_core/model/proxy_granting_ticket.rb", - "lib/casino_core/model/proxy_ticket.rb", - "lib/casino_core/model/service_ticket.rb", - "lib/casino_core/model/service_ticket/single_sign_out_notifier.rb", - "lib/casino_core/model/ticket_granting_ticket.rb", - "lib/casino_core/processor.rb", - "lib/casino_core/processor/api.rb", - "lib/casino_core/processor/api/login_credential_acceptor.rb", - "lib/casino_core/processor/api/logout.rb", - "lib/casino_core/processor/api/service_ticket_provider.rb", - "lib/casino_core/processor/legacy_validator.rb", - "lib/casino_core/processor/login_credential_acceptor.rb", - "lib/casino_core/processor/login_credential_requestor.rb", - "lib/casino_core/processor/logout.rb", - "lib/casino_core/processor/proxy_ticket_provider.rb", - "lib/casino_core/processor/proxy_ticket_validator.rb", - "lib/casino_core/processor/service_ticket_validator.rb", - "lib/casino_core/processor/session_destroyer.rb", - "lib/casino_core/processor/session_overview.rb", - "lib/casino_core/railtie.rb", - "lib/casino_core/rake_tasks.rb", - "lib/casino_core/settings.rb", - "lib/casino_core/tasks/cleanup.rake", - "lib/casino_core/tasks/database.rake", - "spec/authenticator/base_spec.rb", - "spec/authenticator/static_spec.rb", - "spec/model/login_ticket_spec.rb", - "spec/model/proxy_ticket_spec.rb", - "spec/model/service_ticket/single_sign_out_notifier_spec.rb", - "spec/model/service_ticket_spec.rb", - "spec/model/ticket_granting_ticket_spec.rb", - "spec/processor/api/login_credential_acceptor_spec.rb", - "spec/processor/api/logout_spec.rb", - "spec/processor/api/service_ticket_provider_spec.rb", - "spec/processor/legacy_validator_spec.rb", - "spec/processor/login_credential_acceptor_spec.rb", - "spec/processor/login_credential_requestor_spec.rb", - "spec/processor/logout_spec.rb", - "spec/processor/proxy_ticket_provider_spec.rb", - "spec/processor/proxy_ticket_validator_spec.rb", - "spec/processor/session_destroyer_spec.rb", - "spec/processor/session_overview_spec.rb", - "spec/processor/ticket_validator_spec.rb", - "spec/settings_spec.rb", - "spec/spec_helper.rb", - "spec/support/factories/login_ticket_factory.rb", - "spec/support/factories/proxy_granting_ticket_factory.rb", - "spec/support/factories/proxy_ticket_factory.rb", - "spec/support/factories/service_ticket_factory.rb", - "spec/support/factories/ticket_granting_ticket_factory.rb" - ] - s.homepage = "http://rbcas.org/" - s.licenses = ["MIT"] - s.require_paths = ["lib"] - s.rubygems_version = "1.8.24" - s.summary = "A CAS server core library." + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + s.require_paths = ['lib'] - if s.respond_to? :specification_version then - s.specification_version = 3 + s.add_development_dependency 'rake', '~> 10.0' + s.add_development_dependency 'rspec', '~> 2.12' + s.add_development_dependency 'simplecov', '~> 0.7' + s.add_development_dependency 'sqlite3', '~> 1.3' + s.add_development_dependency 'database_cleaner', '~> 0.9' + s.add_development_dependency 'webmock', '~> 1.9' + s.add_development_dependency 'nokogiri', '~> 1.5' + s.add_development_dependency 'factory_girl', '~> 4.1' + s.add_development_dependency 'yard', '~> 0.8' - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["~> 3.2.9"]) - s.add_runtime_dependency(%q, ["~> 2.3.2"]) - s.add_runtime_dependency(%q, ["~> 0.4.13"]) - s.add_development_dependency(%q, ["~> 1.2.0"]) - s.add_development_dependency(%q, ["~> 1.8.4"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 0.8.3"]) - s.add_development_dependency(%q, ["~> 2.12.0"]) - s.add_development_dependency(%q, ["~> 0.7.1"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 4.1.0"]) - else - s.add_dependency(%q, ["~> 3.2.9"]) - s.add_dependency(%q, ["~> 2.3.2"]) - s.add_dependency(%q, ["~> 0.4.13"]) - s.add_dependency(%q, ["~> 1.2.0"]) - s.add_dependency(%q, ["~> 1.8.4"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 0.8.3"]) - s.add_dependency(%q, ["~> 2.12.0"]) - s.add_dependency(%q, ["~> 0.7.1"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 4.1.0"]) - end - else - s.add_dependency(%q, ["~> 3.2.9"]) - s.add_dependency(%q, ["~> 2.3.2"]) - s.add_dependency(%q, ["~> 0.4.13"]) - s.add_dependency(%q, ["~> 1.2.0"]) - s.add_dependency(%q, ["~> 1.8.4"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 0.8.3"]) - s.add_dependency(%q, ["~> 2.12.0"]) - s.add_dependency(%q, ["~> 0.7.1"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 4.1.0"]) - end + s.add_runtime_dependency 'activerecord', '~> 3.2.9' + s.add_runtime_dependency 'addressable', '~> 2.3' + s.add_runtime_dependency 'useragent', '~> 0.4' end diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb new file mode 100644 index 00000000..abd3dc2c --- /dev/null +++ b/lib/casino_core/version.rb @@ -0,0 +1,3 @@ +module CASinoCore + VERSION = '1.0.9' +end From 8d5d730151704f066456c3a08bc3bff5e6297c93 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 18:05:08 +0100 Subject: [PATCH 215/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index daa70c9d..283cf834 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.0.9) + casino_core (1.0.10) activerecord (~> 3.2.9) addressable (~> 2.3) useragent (~> 0.4) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index abd3dc2c..df8854c7 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.0.9' + VERSION = '1.0.10' end From 79ba73b55edab7a9ca9ab4178f04169b19091825 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 19:44:56 +0100 Subject: [PATCH 216/350] Load inflections earlier --- lib/casino_core.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index b85c432f..7115988e 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,3 +1,5 @@ +require 'active_support/inflector' + module CASinoCore autoload :Authenticator, 'casino_core/authenticator.rb' autoload :Helper, 'casino_core/helper.rb' @@ -19,11 +21,6 @@ def setup(environment = nil, options = {}) config = YAML.load_file('config/cas.yml')[@environment].symbolize_keys recursive_symbolize_keys!(config) CASinoCore::Settings.init config - - ActiveSupport::Inflector.inflections do |inflect| - inflect.acronym 'CAS' - inflect.acronym 'CASino' - end end private @@ -36,3 +33,8 @@ def recursive_symbolize_keys! hash end end end + +ActiveSupport::Inflector.inflections do |inflect| + inflect.acronym 'CAS' + inflect.acronym 'CASino' +end From 6940c42e39aa155716156d1c8bd7f757e3fe50d2 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 21:28:26 +0100 Subject: [PATCH 217/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 283cf834..952c6fe2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.0.10) + casino_core (1.0.11) activerecord (~> 3.2.9) addressable (~> 2.3) useragent (~> 0.4) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index df8854c7..f0b337fb 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.0.10' + VERSION = '1.0.11' end From 8acde1edb6f4a2b09a11969e56438b30e5c79c1e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 1 Jan 2013 22:07:57 +0100 Subject: [PATCH 218/350] Load from actual application root --- lib/casino_core.rb | 5 +++-- lib/casino_core/railtie.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 7115988e..fd2f8b3f 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -13,12 +13,13 @@ module CASinoCore class << self def setup(environment = nil, options = {}) @environment = environment || 'development' + root_path = options[:application_root] || '.' require 'active_record' require 'yaml' YAML::ENGINE.yamler = 'syck' - ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')[@environment] + ActiveRecord::Base.establish_connection YAML.load_file(File.join(root_path, 'config/database.yml'))[@environment] - config = YAML.load_file('config/cas.yml')[@environment].symbolize_keys + config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys recursive_symbolize_keys!(config) CASinoCore::Settings.init config end diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index f756d5d8..eac1aecc 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -8,7 +8,7 @@ class Railtie < Rails::Railtie end initializer 'casino_core.load_configuration' do - CASinoCore.setup Rails.env + CASinoCore.setup Rails.env, application_root: Rails.root end initializer 'casino_core.setup_logger' do From 5adbc7529f034c4747db653b971285af35efc940 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 2 Jan 2013 08:59:08 +0100 Subject: [PATCH 219/350] Don't automatically setup CASinoCore --- lib/casino_core/railtie.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/casino_core/railtie.rb b/lib/casino_core/railtie.rb index eac1aecc..d2d5cef6 100644 --- a/lib/casino_core/railtie.rb +++ b/lib/casino_core/railtie.rb @@ -7,10 +7,6 @@ class Railtie < Rails::Railtie CASinoCore::RakeTasks.load_tasks end - initializer 'casino_core.load_configuration' do - CASinoCore.setup Rails.env, application_root: Rails.root - end - initializer 'casino_core.setup_logger' do CASinoCore::Settings.logger = Rails.logger end From 4f11b7c7a66c75789fa28c78745904aa47a1f8f4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 2 Jan 2013 09:46:05 +0100 Subject: [PATCH 220/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 952c6fe2..f5bcfac1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.0.11) + casino_core (1.0.12) activerecord (~> 3.2.9) addressable (~> 2.3) useragent (~> 0.4) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index f0b337fb..3a1caebb 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.0.11' + VERSION = '1.0.12' end From fbc662f41f1fbd457ff4b7f2b10254330b1edd68 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 5 Jan 2013 15:48:39 +0100 Subject: [PATCH 221/350] Added table and model for service whitelist --- db/migrate/20130105152327_create_service_rules.rb | 15 +++++++++++++++ db/schema.rb | 14 +++++++++++++- lib/casino_core/model.rb | 1 + lib/casino_core/model/service_rule.rb | 5 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20130105152327_create_service_rules.rb create mode 100644 lib/casino_core/model/service_rule.rb diff --git a/db/migrate/20130105152327_create_service_rules.rb b/db/migrate/20130105152327_create_service_rules.rb new file mode 100644 index 00000000..97978f1e --- /dev/null +++ b/db/migrate/20130105152327_create_service_rules.rb @@ -0,0 +1,15 @@ +class CreateServiceRules < ActiveRecord::Migration + def change + create_table :service_rules do |t| + t.boolean :enabled, null: false, default: true + t.integer :order, null: false, default: 10 + t.string :name, null: false + t.string :url, null: false + t.boolean :regex, null: false, default: false + + t.timestamps + end + + add_index :service_rules, :url, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 4bcec369..3f2ea9af 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121231114141) do +ActiveRecord::Schema.define(:version => 20130105152327) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -47,6 +47,18 @@ add_index "proxy_tickets", ["proxy_granting_ticket_id"], :name => "index_proxy_tickets_on_proxy_granting_ticket_id" add_index "proxy_tickets", ["ticket"], :name => "index_proxy_tickets_on_ticket", :unique => true + create_table "service_rules", :force => true do |t| + t.boolean "enabled", :default => true, :null => false + t.integer "order", :default => 10, :null => false + t.string "name", :null => false + t.string "url", :null => false + t.boolean "regex", :default => false, :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "service_rules", ["url"], :name => "index_service_rules_on_url", :unique => true + create_table "service_tickets", :force => true do |t| t.string "ticket", :null => false t.string "service", :null => false diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index 532e726f..067b8364 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -3,6 +3,7 @@ module CASinoCore module Model autoload :LoginTicket, 'casino_core/model/login_ticket.rb' + autoload :ServiceRule, 'casino_core/model/service_rule.rb' autoload :ServiceTicket, 'casino_core/model/service_ticket.rb' autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb' autoload :ProxyTicket, 'casino_core/model/proxy_ticket.rb' diff --git a/lib/casino_core/model/service_rule.rb b/lib/casino_core/model/service_rule.rb new file mode 100644 index 00000000..182888df --- /dev/null +++ b/lib/casino_core/model/service_rule.rb @@ -0,0 +1,5 @@ +require 'casino_core/model' + +class CASinoCore::Model::ServiceRule < ActiveRecord::Base + attr_accessible :enabled, :order, :name, :url, :regex +end From e64c2120f6ac94008cea76fdd12382f7a340353f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 5 Jan 2013 16:33:17 +0100 Subject: [PATCH 222/350] Basic functionality if ServiceRule --- lib/casino_core/model/service_rule.rb | 21 +++++++ spec/model/service_rule_spec.rb | 63 +++++++++++++++++++ .../support/factories/service_rule_factory.rb | 16 +++++ 3 files changed, 100 insertions(+) create mode 100644 spec/model/service_rule_spec.rb create mode 100644 spec/support/factories/service_rule_factory.rb diff --git a/lib/casino_core/model/service_rule.rb b/lib/casino_core/model/service_rule.rb index 182888df..ee54a10f 100644 --- a/lib/casino_core/model/service_rule.rb +++ b/lib/casino_core/model/service_rule.rb @@ -2,4 +2,25 @@ class CASinoCore::Model::ServiceRule < ActiveRecord::Base attr_accessible :enabled, :order, :name, :url, :regex + + def self.is_allowed?(service_url) + rules = self.where(enabled: true) + if rules.empty? + true + else + rules.any? { |rule| rule.allows?(service_url) } + end + end + + def allows?(service_url) + if self.regex? + regex = Regexp.new self.url, true + if regex =~ service_url + return true + end + elsif self.url == service_url + return true + end + false + end end diff --git a/spec/model/service_rule_spec.rb b/spec/model/service_rule_spec.rb new file mode 100644 index 00000000..d0fb92c4 --- /dev/null +++ b/spec/model/service_rule_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe CASinoCore::Model::ServiceRule do + context 'with an empty table' do + ['https://www.example.org/', 'http://www.google.com/'].each do |service_url| + it "allows access to #{service_url}" do + described_class.is_allowed?(service_url).should == true + end + end + end + + context 'with a regex rule' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*$' + end + + ['https://www.example.org/', 'https://www.google.com/'].each do |service_url| + it "allows access to #{service_url}" do + described_class.is_allowed?(service_url).should == true + end + end + + ['http://www.example.org/', 'http://www.google.com/'].each do |service_url| + it "does not allow access to #{service_url}" do + described_class.is_allowed?(service_url).should == false + end + end + end + + context 'with many regex rules' do + before(:each) do + 100.times do |counter| + FactoryGirl.create :service_rule, :regex, url: "^https://www#{counter}.example.com.*$" + end + end + + let(:service_url) { 'https://www111.example.com/bla' } + + it 'does not take too long to check a denied service' do + start = Time.now + described_class.is_allowed?(service_url).should == false + (Time.now - start).should < 0.1 + end + end + + context 'with a non-regex rule' do + before(:each) do + FactoryGirl.create :service_rule, url: 'https://www.google.com/foo' + end + + ['https://www.google.com/foo'].each do |service_url| + it "allows access to #{service_url}" do + described_class.is_allowed?(service_url).should == true + end + end + + ['https://www.example.org/', 'http://www.example.org/', 'https://www.google.com/test'].each do |service_url| + it "does not allow access to #{service_url}" do + described_class.is_allowed?(service_url).should == false + end + end + end +end diff --git a/spec/support/factories/service_rule_factory.rb b/spec/support/factories/service_rule_factory.rb new file mode 100644 index 00000000..37cf6be2 --- /dev/null +++ b/spec/support/factories/service_rule_factory.rb @@ -0,0 +1,16 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :service_rule, class: CASinoCore::Model::ServiceRule do + sequence :order do |n| + n + end + sequence :name do |n| + "Rule #{n}" + end + + trait :regex do + regex true + end + end +end From 2ab5d9ed16f7e1c3265d0d95954fd1c8beed10fa Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 5 Jan 2013 16:58:58 +0100 Subject: [PATCH 223/350] Simplified regex --- spec/model/service_rule_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/model/service_rule_spec.rb b/spec/model/service_rule_spec.rb index d0fb92c4..5d25e283 100644 --- a/spec/model/service_rule_spec.rb +++ b/spec/model/service_rule_spec.rb @@ -11,7 +11,7 @@ context 'with a regex rule' do before(:each) do - FactoryGirl.create :service_rule, :regex, url: '^https://.*$' + FactoryGirl.create :service_rule, :regex, url: '^https://.*' end ['https://www.example.org/', 'https://www.google.com/'].each do |service_url| @@ -30,7 +30,7 @@ context 'with many regex rules' do before(:each) do 100.times do |counter| - FactoryGirl.create :service_rule, :regex, url: "^https://www#{counter}.example.com.*$" + FactoryGirl.create :service_rule, :regex, url: "^https://www#{counter}.example.com" end end From a746379b5af63c8424b6f38dfcaf70c6e08c8555 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 5 Jan 2013 20:35:46 +0100 Subject: [PATCH 224/350] allowed? instead of is_allowed? --- lib/casino_core/model/service_rule.rb | 2 +- spec/model/service_rule_spec.rb | 80 ++++++++++++++------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/lib/casino_core/model/service_rule.rb b/lib/casino_core/model/service_rule.rb index ee54a10f..2630181d 100644 --- a/lib/casino_core/model/service_rule.rb +++ b/lib/casino_core/model/service_rule.rb @@ -3,7 +3,7 @@ class CASinoCore::Model::ServiceRule < ActiveRecord::Base attr_accessible :enabled, :order, :name, :url, :regex - def self.is_allowed?(service_url) + def self.allowed?(service_url) rules = self.where(enabled: true) if rules.empty? true diff --git a/spec/model/service_rule_spec.rb b/spec/model/service_rule_spec.rb index 5d25e283..bf4a24d2 100644 --- a/spec/model/service_rule_spec.rb +++ b/spec/model/service_rule_spec.rb @@ -1,62 +1,64 @@ require 'spec_helper' describe CASinoCore::Model::ServiceRule do - context 'with an empty table' do - ['https://www.example.org/', 'http://www.google.com/'].each do |service_url| - it "allows access to #{service_url}" do - described_class.is_allowed?(service_url).should == true + describe '.allowed?' do + context 'with an empty table' do + ['https://www.example.org/', 'http://www.google.com/'].each do |service_url| + it "allows access to #{service_url}" do + described_class.allowed?(service_url).should == true + end end end - end - context 'with a regex rule' do - before(:each) do - FactoryGirl.create :service_rule, :regex, url: '^https://.*' - end + context 'with a regex rule' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end - ['https://www.example.org/', 'https://www.google.com/'].each do |service_url| - it "allows access to #{service_url}" do - described_class.is_allowed?(service_url).should == true + ['https://www.example.org/', 'https://www.google.com/'].each do |service_url| + it "allows access to #{service_url}" do + described_class.allowed?(service_url).should == true + end end - end - ['http://www.example.org/', 'http://www.google.com/'].each do |service_url| - it "does not allow access to #{service_url}" do - described_class.is_allowed?(service_url).should == false + ['http://www.example.org/', 'http://www.google.com/'].each do |service_url| + it "does not allow access to #{service_url}" do + described_class.allowed?(service_url).should == false + end end end - end - context 'with many regex rules' do - before(:each) do - 100.times do |counter| - FactoryGirl.create :service_rule, :regex, url: "^https://www#{counter}.example.com" + context 'with many regex rules' do + before(:each) do + 100.times do |counter| + FactoryGirl.create :service_rule, :regex, url: "^https://www#{counter}.example.com" + end end - end - let(:service_url) { 'https://www111.example.com/bla' } + let(:service_url) { 'https://www111.example.com/bla' } - it 'does not take too long to check a denied service' do - start = Time.now - described_class.is_allowed?(service_url).should == false - (Time.now - start).should < 0.1 + it 'does not take too long to check a denied service' do + start = Time.now + described_class.allowed?(service_url).should == false + (Time.now - start).should < 0.1 + end end - end - context 'with a non-regex rule' do - before(:each) do - FactoryGirl.create :service_rule, url: 'https://www.google.com/foo' - end + context 'with a non-regex rule' do + before(:each) do + FactoryGirl.create :service_rule, url: 'https://www.google.com/foo' + end - ['https://www.google.com/foo'].each do |service_url| - it "allows access to #{service_url}" do - described_class.is_allowed?(service_url).should == true + ['https://www.google.com/foo'].each do |service_url| + it "allows access to #{service_url}" do + described_class.allowed?(service_url).should == true + end end - end - ['https://www.example.org/', 'http://www.example.org/', 'https://www.google.com/test'].each do |service_url| - it "does not allow access to #{service_url}" do - described_class.is_allowed?(service_url).should == false + ['https://www.example.org/', 'http://www.example.org/', 'https://www.google.com/test'].each do |service_url| + it "does not allow access to #{service_url}" do + described_class.allowed?(service_url).should == false + end end end end From 59c3f8d1bdf4223f943e0c8a2a03931ff6b415d0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 5 Jan 2013 21:44:15 +0100 Subject: [PATCH 225/350] Check if service is allowed --- lib/casino_core/helper/service_tickets.rb | 10 +++- .../processor/login_credential_requestor.rb | 60 +++++++++++++++---- .../login_credential_requestor_spec.rb | 13 ++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index bf2eca1e..3670fccf 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -7,10 +7,18 @@ module ServiceTickets include CASinoCore::Helper::Tickets include CASinoCore::Helper::ProxyTickets + class ServiceNotAllowedError < StandardError; end + def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied = nil) + service_url = clean_service_url(service) + unless CASinoCore::Model::ServiceRule.allowed?(service_url) + message = "#{service_url} is not in the list of allowed URLs" + logger.error message + raise ServiceNotAllowedError, message + end ticket_granting_ticket.service_tickets.create!({ ticket: random_ticket_string('ST'), - service: clean_service_url(service), + service: service_url, issued_from_credentials: !!credentials_supplied }) end diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index 44f97921..b87ff4ee 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -14,27 +14,61 @@ class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor # The method will call one of the following methods on the listener: # * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to. # * `#user_not_logged_in`: The first argument is a LoginTicket. It should be stored in a hidden field with name "lt". + # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. # # @param [Hash] params parameters supplied by user # @param [Hash] cookies cookies supplied by user # @param [String] user_agent user-agent delivered by the client def process(params = nil, cookies = nil, user_agent = nil) - params ||= {} - cookies ||= {} - request_env ||= {} - if !params[:renew] && (ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)) - service_url_with_ticket = unless params[:service].nil? - acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url - end - @listener.user_logged_in(service_url_with_ticket) + @params = params || {} + @cookies = cookies || {} + @user_agent = user_agent || {} + if check_service_allowed + handle_allowed_service + end + end + + private + def handle_allowed_service + if !@params[:renew] && (@ticket_granting_ticket = find_valid_ticket_granting_ticket(@cookies[:tgt], @user_agent)) + handle_logged_in else - if params[:gateway] == 'true' && params[:service] - # we actually lie to the listener to simplify things - @listener.user_logged_in(params[:service]) + handle_not_logged_in + end + end + + def handle_logged_in + service_url_with_ticket = unless @params[:service].nil? + acquire_service_ticket(@ticket_granting_ticket, @params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(service_url_with_ticket) + end + + def handle_not_logged_in + if gateway_request? + # we actually lie to the listener to simplify things + @listener.user_logged_in(@params[:service]) + else + login_ticket = acquire_login_ticket + @listener.user_not_logged_in(login_ticket) + end + end + + def check_service_allowed + if @params[:service].nil? + true + else + service_url = clean_service_url(@params[:service]) + if CASinoCore::Model::ServiceRule.allowed?(service_url) + true else - login_ticket = acquire_login_ticket - @listener.user_not_logged_in(login_ticket) + @listener.service_not_allowed(service_url) + false end end end + + def gateway_request? + @params[:gateway] == 'true' && @params[:service] + end end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 08ecdbc1..834318e2 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -5,6 +5,19 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } + context 'with a not allowed service' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end + let(:service) { 'http://www.example.org/' } + let(:params) { { service: service } } + + it 'calls the #service_not_allowed method on the listener' do + listener.should_receive(:service_not_allowed).with(service) + processor.process(params) + end + end + context 'when logged out' do it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) From da7a951d4654e699b31d0d0202938c0cb65399a3 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 09:56:17 +0100 Subject: [PATCH 226/350] Refactoring --- .../processor/login_credential_requestor.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/casino_core/processor/login_credential_requestor.rb b/lib/casino_core/processor/login_credential_requestor.rb index b87ff4ee..a53d1906 100644 --- a/lib/casino_core/processor/login_credential_requestor.rb +++ b/lib/casino_core/processor/login_credential_requestor.rb @@ -55,16 +55,12 @@ def handle_not_logged_in end def check_service_allowed - if @params[:service].nil? + service_url = clean_service_url(@params[:service]) unless @params[:service].nil? + if service_url.nil? || CASinoCore::Model::ServiceRule.allowed?(service_url) true else - service_url = clean_service_url(@params[:service]) - if CASinoCore::Model::ServiceRule.allowed?(service_url) - true - else - @listener.service_not_allowed(service_url) - false - end + @listener.service_not_allowed(service_url) + false end end From 1ca58b0f6f3e511fd7114baebaeac00be141ef6d Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 09:57:16 +0100 Subject: [PATCH 227/350] Handle not allowed service --- .../processor/login_credential_acceptor.rb | 38 ++++++++++++------- .../login_credential_acceptor_spec.rb | 12 ++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 2db243ef..ee89db2e 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -22,19 +22,11 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # @param [Hash] cookies cookies supplied by user # @param [String] user_agent user-agent delivered by the client def process(params = nil, cookies = nil, user_agent = nil) - params ||= {} - cookies ||= {} - if login_ticket_valid?(params[:lt]) - authentication_result = validate_login_credentials(params[:username], params[:password]) - if !authentication_result.nil? - ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, user_agent) - url = unless params[:service].nil? - acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url - end - @listener.user_logged_in(url, ticket_granting_ticket.ticket) - else - @listener.invalid_login_credentials(acquire_login_ticket) - end + @params = params || {} + @cookies = cookies || {} + @user_agent = user_agent + if login_ticket_valid?(@params[:lt]) + authenticate_user else @listener.invalid_login_ticket(acquire_login_ticket) end @@ -56,4 +48,24 @@ def login_ticket_valid?(lt) end end + def authenticate_user + authentication_result = validate_login_credentials(@params[:username], @params[:password]) + if !authentication_result.nil? + user_logged_in(authentication_result) + else + @listener.invalid_login_credentials(acquire_login_ticket) + end + end + + def user_logged_in(authentication_result) + begin + ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, @user_agent) + url = unless @params[:service].nil? + acquire_service_ticket(ticket_granting_ticket, @params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(url, ticket_granting_ticket.ticket) + rescue ServiceNotAllowedError => e + @listener.service_not_allowed(clean_service_url @params[:service]) + end + end end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index ba75e611..44c6bd32 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -39,6 +39,18 @@ listener.stub(:user_logged_in) end + context 'with a not allowed service' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end + let(:service) { 'http://www.example.org/' } + + it 'calls the #service_not_allowed method on the listener' do + listener.should_receive(:service_not_allowed).with(service) + processor.process(login_data) + end + end + context 'when all authenticators raise an error' do before(:each) do CASinoCore::Authenticator::Static.any_instance.stub(:validate) do From 2557d6e057667af86edd0dd6cd75af8aa8564992 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 09:57:51 +0100 Subject: [PATCH 228/350] Refactoring --- lib/casino_core/helper/login_tickets.rb | 15 +++++++++++++++ .../processor/login_credential_acceptor.rb | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/casino_core/helper/login_tickets.rb b/lib/casino_core/helper/login_tickets.rb index e65385cc..44a37a21 100644 --- a/lib/casino_core/helper/login_tickets.rb +++ b/lib/casino_core/helper/login_tickets.rb @@ -9,6 +9,21 @@ def acquire_login_ticket logger.debug "Created login ticket '#{ticket.ticket}'" ticket end + + def login_ticket_valid?(lt) + ticket = CASinoCore::Model::LoginTicket.find_by_ticket lt + if ticket.nil? + logger.info "Login ticket '#{lt}' not found" + false + elsif ticket.created_at < CASinoCore::Settings.login_ticket[:lifetime].seconds.ago + logger.info "Login ticket '#{ticket.ticket}' expired" + false + else + logger.debug "Login ticket '#{ticket.ticket}' successfully validated" + ticket.delete + true + end + end end end end diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index ee89db2e..e515c5a0 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -33,21 +33,6 @@ def process(params = nil, cookies = nil, user_agent = nil) end private - def login_ticket_valid?(lt) - ticket = CASinoCore::Model::LoginTicket.find_by_ticket lt - if ticket.nil? - logger.info "Login ticket '#{lt}' not found" - false - elsif ticket.created_at < CASinoCore::Settings.login_ticket[:lifetime].seconds.ago - logger.info "Login ticket '#{ticket.ticket}' expired" - false - else - logger.debug "Login ticket '#{ticket.ticket}' successfully validated" - ticket.delete - true - end - end - def authenticate_user authentication_result = validate_login_credentials(@params[:username], @params[:password]) if !authentication_result.nil? From e3c4c0de83932156d93d69a88e4095055e74187f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 10:02:57 +0100 Subject: [PATCH 229/350] Documentation --- lib/casino_core/processor/login_credential_acceptor.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index e515c5a0..4e607671 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -17,6 +17,7 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # The second argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". # * `#invalid_login_ticket` and `#invalid_login_credentials`: The first argument is a LoginTicket. # See {CASinoCore::Processor::LoginCredentialRequestor} for details. + # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. # # @param [Hash] params parameters supplied by user # @param [Hash] cookies cookies supplied by user From d01667491b3ce599884fb784d32e26a6796f19ad Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 10:03:12 +0100 Subject: [PATCH 230/350] No need for cookies here --- lib/casino_core/processor/login_credential_acceptor.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 4e607671..3f0df8bb 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -20,11 +20,9 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. # # @param [Hash] params parameters supplied by user - # @param [Hash] cookies cookies supplied by user # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user_agent = nil) @params = params || {} - @cookies = cookies || {} @user_agent = user_agent if login_ticket_valid?(@params[:lt]) authenticate_user From 596b5f942adf5454f017accc8d6fd84287af9d37 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 6 Jan 2013 18:02:01 +0100 Subject: [PATCH 231/350] Handle ServiceNotAllowedError --- .../processor/api/service_ticket_provider.rb | 13 +++++++++++-- .../processor/api/service_ticket_provider_spec.rb | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 120d2a02..76299fe9 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -15,6 +15,7 @@ class CASinoCore::Processor::API::ServiceTicketProvider < CASinoCore::Processor # The service ticket (and nothing else) should be displayed. # * `#invalid_ticket_granting_ticket_via_api`: No argument. The application should respond with status "400 Bad Request" # * `#no_service_provided_via_api`: No argument. The application should respond with status "400 Bad Request" + # * `#service_not_allowed_via_api`: The user tried to access a service that this CAS server is not allowed to serve. # # @param [String] ticket_granting_ticket ticket_granting_ticket supplied by the user in the URL # @param [Hash] parameters parameters supplied by user (`service` in particular) @@ -37,8 +38,12 @@ def fetch_valid_ticket_granting_ticket def handle_ticket_granting_ticket case when (@service_url and @ticket_granting_ticket) - create_service_ticket - callback_granted_service_ticket + begin + create_service_ticket + callback_granted_service_ticket + rescue ServiceNotAllowedError => e + callback_service_not_allowed + end when (@service_url and not @ticket_granting_ticket) callback_invalid_tgt when (not @service_url and @ticket_granting_ticket) @@ -62,4 +67,8 @@ def callback_empty_service @listener.no_service_provided_via_api end + def callback_service_not_allowed + @listener.service_not_allowed_via_api(clean_service_url @service_url) + end + end diff --git a/spec/processor/api/service_ticket_provider_spec.rb b/spec/processor/api/service_ticket_provider_spec.rb index f702a50c..3180ff32 100644 --- a/spec/processor/api/service_ticket_provider_spec.rb +++ b/spec/processor/api/service_ticket_provider_spec.rb @@ -5,7 +5,8 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:parameters) { { service: 'http://example.org/' } } + let(:service) { 'http://example.org/' } + let(:parameters) { { service: service } } context 'with an invalid ticket-granting ticket' do let(:ticket_granting_ticket) { 'TGT-INVALID' } @@ -21,6 +22,18 @@ let(:ticket) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } + context 'with a not allowed service' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end + let(:service) { 'http://www.example.org/' } + + it 'calls the #service_not_allowed method on the listener' do + listener.should_receive(:service_not_allowed_via_api).with(service) + processor.process(ticket, parameters, user_agent) + end + end + it 'calls the #granted_service_ticket_via_api method on the listener' do listener.should_receive(:granted_service_ticket_via_api).with(/^ST\-/) processor.process(ticket, parameters, user_agent) From b183561e815c7dcb0223295afc0c3a66c81bca98 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 11 Jan 2013 14:59:09 +0100 Subject: [PATCH 232/350] Rake tasks for service rule management --- casino_core.gemspec | 1 + lib/casino_core/helper/service_tickets.rb | 2 +- lib/casino_core/rake_tasks.rb | 1 + lib/casino_core/tasks/service_rule.rake | 46 +++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 lib/casino_core/tasks/service_rule.rake diff --git a/casino_core.gemspec b/casino_core.gemspec index 511af809..c8eda1c4 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'activerecord', '~> 3.2.9' s.add_runtime_dependency 'addressable', '~> 2.3' + s.add_runtime_dependency 'terminal-table', '~> 1.4' s.add_runtime_dependency 'useragent', '~> 0.4' end diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index 3670fccf..c670476e 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -33,7 +33,7 @@ def clean_service_url(dirty_service) service_uri.query_values = nil end if "#{service_uri.path}".length > 1 - service_uri.path = service_uri.path.gsub(/\/\z/, '') + service_uri.path = service_uri.path.gsub(/\/+\z/, '') end clean_service = service_uri.to_s diff --git a/lib/casino_core/rake_tasks.rb b/lib/casino_core/rake_tasks.rb index 04668c3a..e95e2be6 100644 --- a/lib/casino_core/rake_tasks.rb +++ b/lib/casino_core/rake_tasks.rb @@ -5,6 +5,7 @@ def load_tasks %w( database cleanup + service_rule ).each do |task| load "casino_core/tasks/#{task}.rake" end diff --git a/lib/casino_core/tasks/service_rule.rake b/lib/casino_core/tasks/service_rule.rake new file mode 100644 index 00000000..c84e2352 --- /dev/null +++ b/lib/casino_core/tasks/service_rule.rake @@ -0,0 +1,46 @@ +require 'terminal-table' +require 'casino_core/model' +require 'casino_core/helper/service_tickets' + +namespace :casino_core do + namespace :service_rule do + include CASinoCore::Helper::ServiceTickets + + desc 'Add a service rule (prefix the url parameter with "regex:" to add a regular expression)' + task :add, [:name, :url] => 'casino_core:db:configure_connection' do |task, args| + match = /^regex:(.*)/.match(args[:url]) + if match.nil? + url = clean_service_url(args[:url]) + regex = false + else + url = match[1] + regex = true + end + CASinoCore::Model::ServiceRule.create! name: args[:name], url: url, regex: regex + end + + desc 'Remove a servcice rule.' + task :delete, [:id] => 'casino_core:db:configure_connection' do |task, args| + CASinoCore::Model::ServiceRule.find(args[:id]).destroy + end + + desc 'Delete all servcice rules.' + task :flush => 'casino_core:db:configure_connection' do |task, args| + CASinoCore::Model::ServiceRule.delete_all + end + + desc 'List all service rules.' + task list: 'casino_core:db:configure_connection' do + table = Terminal::Table.new :headings => ['ID', 'Name', 'URL'] do |t| + CASinoCore::Model::ServiceRule.all.each do |service_rule| + url = service_rule.url + if service_rule.regex? + url += " (Regex)" + end + t.add_row [service_rule.id, service_rule.name, url] + end + end + puts table + end + end +end From 90a2b24bc24d633a2602be28eed9e465975a6b71 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 11 Jan 2013 14:59:19 +0100 Subject: [PATCH 233/350] Updated gems --- Gemfile.lock | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f5bcfac1..a781267b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,26 +4,27 @@ PATH casino_core (1.0.12) activerecord (~> 3.2.9) addressable (~> 2.3) + terminal-table (~> 1.4) useragent (~> 0.4) GEM remote: http://rubygems.org/ specs: - activemodel (3.2.9) - activesupport (= 3.2.9) + activemodel (3.2.11) + activesupport (= 3.2.11) builder (~> 3.0.0) - activerecord (3.2.9) - activemodel (= 3.2.9) - activesupport (= 3.2.9) + activerecord (3.2.11) + activemodel (= 3.2.11) + activesupport (= 3.2.11) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activesupport (3.2.9) + activesupport (3.2.11) i18n (~> 0.6) multi_json (~> 1.0) addressable (2.3.2) arel (3.0.2) builder (3.0.4) - crack (0.3.1) + crack (0.3.2) database_cleaner (0.9.1) diff-lcs (1.1.3) factory_girl (4.1.0) @@ -45,8 +46,9 @@ GEM simplecov-html (~> 0.7.1) simplecov-html (0.7.1) sqlite3 (1.3.6) + terminal-table (1.4.5) tzinfo (0.3.35) - useragent (0.4.15) + useragent (0.4.16) webmock (1.9.0) addressable (>= 2.2.7) crack (>= 0.1.7) From 9a55623042f3530ccc146b4e094e6017e61ac709 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 11 Jan 2013 23:17:43 +0100 Subject: [PATCH 234/350] Don't strip last / in uri path --- lib/casino_core/helper/service_tickets.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index c670476e..3e5a51a0 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -32,9 +32,10 @@ def clean_service_url(dirty_service) if service_uri.query_values.blank? service_uri.query_values = nil end - if "#{service_uri.path}".length > 1 - service_uri.path = service_uri.path.gsub(/\/+\z/, '') - end + + service_uri.path = (service_uri.path || '').gsub(/\/+\z/, '') + service_uri.path = '/' if service_uri.path.blank? + clean_service = service_uri.to_s logger.debug("Cleaned dirty service URL '#{dirty_service}' to '#{clean_service}'") if dirty_service != clean_service From 5b9a319b91c95dcd514fb4f0436b28152c22aa84 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 11 Jan 2013 23:17:58 +0100 Subject: [PATCH 235/350] Some validation --- lib/casino_core/model/service_rule.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/casino_core/model/service_rule.rb b/lib/casino_core/model/service_rule.rb index 2630181d..646d88e7 100644 --- a/lib/casino_core/model/service_rule.rb +++ b/lib/casino_core/model/service_rule.rb @@ -2,6 +2,8 @@ class CASinoCore::Model::ServiceRule < ActiveRecord::Base attr_accessible :enabled, :order, :name, :url, :regex + validates :name, presence: true + validates :url, uniqueness: true, presence: true def self.allowed?(service_url) rules = self.where(enabled: true) From d0f9cc1b31d7696e0c99ac4a6104cb49b622f328 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 11 Jan 2013 23:18:19 +0100 Subject: [PATCH 236/350] More output --- lib/casino_core/tasks/service_rule.rake | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/casino_core/tasks/service_rule.rake b/lib/casino_core/tasks/service_rule.rake index c84e2352..2cf0c4ef 100644 --- a/lib/casino_core/tasks/service_rule.rake +++ b/lib/casino_core/tasks/service_rule.rake @@ -8,36 +8,42 @@ namespace :casino_core do desc 'Add a service rule (prefix the url parameter with "regex:" to add a regular expression)' task :add, [:name, :url] => 'casino_core:db:configure_connection' do |task, args| + service_rule = CASinoCore::Model::ServiceRule.new name: args[:name] match = /^regex:(.*)/.match(args[:url]) if match.nil? - url = clean_service_url(args[:url]) - regex = false + service_rule.url = clean_service_url(args[:url]) else - url = match[1] - regex = true + service_rule.url = match[1] + service_rule.regex = true + end + if !service_rule.save + fail service_rule.errors.full_messages.join("\n") + elsif service_rule.regex && service_rule.url[0] != '^' + puts 'Warning: Potentially unsafe regex! Use ^ to match the beginning of the URL. Example: ^https://' end - CASinoCore::Model::ServiceRule.create! name: args[:name], url: url, regex: regex end desc 'Remove a servcice rule.' task :delete, [:id] => 'casino_core:db:configure_connection' do |task, args| - CASinoCore::Model::ServiceRule.find(args[:id]).destroy + CASinoCore::Model::ServiceRule.find(args[:id]).delete + puts "Successfully deleted service rule ##{args[:id]}." end desc 'Delete all servcice rules.' task :flush => 'casino_core:db:configure_connection' do |task, args| CASinoCore::Model::ServiceRule.delete_all + puts 'Successfully deleted all service rules.' end desc 'List all service rules.' task list: 'casino_core:db:configure_connection' do - table = Terminal::Table.new :headings => ['ID', 'Name', 'URL'] do |t| + table = Terminal::Table.new :headings => ['Enabled', 'ID', 'Name', 'URL'] do |t| CASinoCore::Model::ServiceRule.all.each do |service_rule| url = service_rule.url if service_rule.regex? url += " (Regex)" end - t.add_row [service_rule.id, service_rule.name, url] + t.add_row [service_rule.enabled, service_rule.id, service_rule.name, url] end end puts table From e83a73fc1e1258ca0471a51abafcf052980a0adb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 12 Jan 2013 12:01:31 +0100 Subject: [PATCH 237/350] Fixed Specs --- spec/processor/login_credential_acceptor_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 44c6bd32..cb73ec88 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -83,7 +83,7 @@ let(:service) { 'https://www.example.com' } it 'calls the #user_logged_in method on the listener' do - listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) + listener.should_receive(:user_logged_in).with(/^#{service}\/\?ticket=ST\-/, /^TGC\-/) processor.process(login_data) end From cdb2fb1e4f54e5e3815e3d374e5e53ec2d6ab898 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 14:40:33 +0100 Subject: [PATCH 238/350] Upgrade information --- UPGRADE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 UPGRADE.md diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 00000000..ff157ca7 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,10 @@ +# Upgrade CASinoCore + +Here is a list of backward-incompatible changes that were introduced. + +## 1.1.0 + +New callbacks: + +* `login_credential_requestor` calls `#service_not_allowed` on the listener, when a service is not in the service whitelist. +* `api/service_ticket_provider` calls `#service_not_allowed_via_api` on the listener, when a service is not in the service whitelist. From 13c1d67117a0e360cefd9f0f484b800ab8bf9e1e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 15:07:33 +0100 Subject: [PATCH 239/350] More Upgrade hints --- UPGRADE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index ff157ca7..bcd02c90 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -4,6 +4,10 @@ Here is a list of backward-incompatible changes that were introduced. ## 1.1.0 +API changes: + +* `login_credential_acceptor`: The parameters of `#process` changed from `params, cookies, user_agent` to just `params, user_agent` + New callbacks: * `login_credential_requestor` calls `#service_not_allowed` on the listener, when a service is not in the service whitelist. From 212d0773caa8153909eb0f4c6eec8200b1831ba6 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 15:08:12 +0100 Subject: [PATCH 240/350] Removed unused variable --- lib/casino_core/processor/api/service_ticket_provider.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/processor/api/service_ticket_provider.rb b/lib/casino_core/processor/api/service_ticket_provider.rb index 76299fe9..29432de9 100644 --- a/lib/casino_core/processor/api/service_ticket_provider.rb +++ b/lib/casino_core/processor/api/service_ticket_provider.rb @@ -41,7 +41,7 @@ def handle_ticket_granting_ticket begin create_service_ticket callback_granted_service_ticket - rescue ServiceNotAllowedError => e + rescue ServiceNotAllowedError callback_service_not_allowed end when (@service_url and not @ticket_granting_ticket) From 7308f494edf896a28f98bd4160019f6e9b83432d Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 15:19:07 +0100 Subject: [PATCH 241/350] Updated gems --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a781267b..632258f3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,7 +27,7 @@ GEM crack (0.3.2) database_cleaner (0.9.1) diff-lcs (1.1.3) - factory_girl (4.1.0) + factory_girl (4.2.0) activesupport (>= 3.0.0) i18n (0.6.1) multi_json (1.5.0) @@ -40,12 +40,12 @@ GEM rspec-core (2.12.2) rspec-expectations (2.12.1) diff-lcs (~> 1.1.3) - rspec-mocks (2.12.1) + rspec-mocks (2.12.2) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) simplecov-html (0.7.1) - sqlite3 (1.3.6) + sqlite3 (1.3.7) terminal-table (1.4.5) tzinfo (0.3.35) useragent (0.4.16) From 7b29613a2451340fc0d697ae3ede7ffcc5e15ac4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 15:19:51 +0100 Subject: [PATCH 242/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 632258f3..7f5d60ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.0.12) + casino_core (1.1.0) activerecord (~> 3.2.9) addressable (~> 2.3) terminal-table (~> 1.4) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index 3a1caebb..f1c00e10 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.0.12' + VERSION = '1.1.0' end From 633e8b93eff4c2ec2fe489060069fe05e1bb00e5 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 15:50:20 +0100 Subject: [PATCH 243/350] Fixed upgrade hints --- UPGRADE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE.md b/UPGRADE.md index bcd02c90..cbd99788 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -10,5 +10,5 @@ API changes: New callbacks: -* `login_credential_requestor` calls `#service_not_allowed` on the listener, when a service is not in the service whitelist. +* `login_credential_requestor` and `login_credential_acceptor` call `#service_not_allowed` on the listener, when a service is not in the service whitelist. * `api/service_ticket_provider` calls `#service_not_allowed_via_api` on the listener, when a service is not in the service whitelist. From 8911223b34ece95dff80171e5ac1d7a5ebcf89ac Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 21:25:36 +0100 Subject: [PATCH 244/350] Extracted user data --- db/migrate/20130202210100_create_users.rb | 35 +++++++++++++++++++ db/schema.rb | 19 ++++++---- lib/casino_core/model.rb | 1 + .../model/ticket_granting_ticket.rb | 5 +-- lib/casino_core/model/user.rb | 8 +++++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20130202210100_create_users.rb create mode 100644 lib/casino_core/model/user.rb diff --git a/db/migrate/20130202210100_create_users.rb b/db/migrate/20130202210100_create_users.rb new file mode 100644 index 00000000..db33c580 --- /dev/null +++ b/db/migrate/20130202210100_create_users.rb @@ -0,0 +1,35 @@ +class CreateUsers < ActiveRecord::Migration + def up + tgt = CASinoCore::Model::TicketGrantingTicket.new + tgt.authenticator = 'foo' + tgt.username = 'bar' + tgt.ticket = 'TGT-bla' + tgt.save! + + create_table :users do |t| + t.string :authenticator, null: false + t.string :username, null: false + t.text :extra_attributes + + t.timestamps + end + + add_index :users, [:authenticator, :username], unique: true + + remove_index :ticket_granting_tickets, [:authenticator, :username] + add_column :ticket_granting_tickets, :user_id, :integer, null: true + CASinoCore::Model::TicketGrantingTicket.reset_column_information + CASinoCore::Model::TicketGrantingTicket.all.each do |ticket| + user = CASinoCore::Model::User.where( + authenticator: ticket.authenticator, + username: ticket.username).first_or_initialize + user.extra_attributes = ticket.extra_attributes + user.save! + ticket.user_id = user.id + ticket.save! + end + p CASinoCore::Model::TicketGrantingTicket.all + change_column :ticket_granting_tickets, :user_id, :integer, null: false + remove_columns :ticket_granting_tickets, :authenticator, :username, :extra_attributes + end +end diff --git a/db/schema.rb b/db/schema.rb index 3f2ea9af..bf24f870 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130105152327) do +ActiveRecord::Schema.define(:version => 20130202210100) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -73,16 +73,23 @@ add_index "service_tickets", ["ticket_granting_ticket_id"], :name => "index_service_tickets_on_ticket_granting_ticket_id" create_table "ticket_granting_tickets", :force => true do |t| - t.string "ticket", :null => false + t.string "ticket", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "user_agent" + t.integer "user_id", :null => false + end + + add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true + + create_table "users", :force => true do |t| + t.string "authenticator", :null => false t.string "username", :null => false t.text "extra_attributes" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false - t.string "user_agent" - t.string "authenticator", :null => false end - add_index "ticket_granting_tickets", ["authenticator", "username"], :name => "index_ticket_granting_tickets_on_authenticator_and_username" - add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true + add_index "users", ["authenticator", "username"], :name => "index_users_on_authenticator_and_username", :unique => true end diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index 067b8364..da7b866d 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -8,5 +8,6 @@ module Model autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb' autoload :ProxyTicket, 'casino_core/model/proxy_ticket.rb' autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb' + autoload :User, 'casino_core/model/user.rb' end end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 2538d169..d5ad8904 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -1,9 +1,10 @@ require 'casino_core/model' class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base - attr_accessible :ticket, :authenticator, :username, :user_agent, :extra_attributes - serialize :extra_attributes, Hash + attr_accessible :ticket, :user_agent validates :ticket, uniqueness: true + + belongs_to :user has_many :service_tickets before_destroy :destroy_service_tickets diff --git a/lib/casino_core/model/user.rb b/lib/casino_core/model/user.rb new file mode 100644 index 00000000..db8ba257 --- /dev/null +++ b/lib/casino_core/model/user.rb @@ -0,0 +1,8 @@ +require 'casino_core/model' + +class CASinoCore::Model::User < ActiveRecord::Base + attr_accessible :authenticator, :username, :extra_attributes + serialize :extra_attributes, Hash + + has_many :ticket_granting_tickets +end From 3a8db11306ef538f0a507f4dff8e69ea021821b2 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 21:50:05 +0100 Subject: [PATCH 245/350] Use new user model --- .../builder/ticket_validation_response.rb | 7 ++++--- lib/casino_core/helper/ticket_granting_tickets.rb | 15 +++++++++++---- lib/casino_core/model/ticket_granting_ticket.rb | 2 +- lib/casino_core/processor/legacy_validator.rb | 2 +- lib/casino_core/processor/session_overview.rb | 5 +---- spec/model/ticket_granting_ticket_spec.rb | 14 +++----------- spec/processor/legacy_validator_spec.rb | 5 +++-- spec/processor/session_destroyer_spec.rb | 7 ++++--- spec/processor/session_overview_spec.rb | 5 +++-- .../factories/ticket_granting_ticket_factory.rb | 4 +--- spec/support/factories/user_factory.rb | 11 +++++++++++ 11 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 spec/support/factories/user_factory.rb diff --git a/lib/casino_core/builder/ticket_validation_response.rb b/lib/casino_core/builder/ticket_validation_response.rb index edb41f4b..9c2547d3 100644 --- a/lib/casino_core/builder/ticket_validation_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -45,11 +45,12 @@ def serialize_extra_attribute(builder, key, value) end def build_success_xml(service_response, ticket, ticket_granting_ticket, proxies) + user = ticket_granting_ticket.user service_response.cas :authenticationSuccess do |authentication_success| - authentication_success.cas :user, ticket_granting_ticket.username - unless ticket_granting_ticket.extra_attributes.blank? + authentication_success.cas :user, user.username + unless user.extra_attributes.blank? authentication_success.cas :attributes do |attributes| - ticket_granting_ticket.extra_attributes.each do |key, value| + user.extra_attributes.each do |key, value| serialize_extra_attribute(attributes, key, value) end end diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index e298644a..9ecc1f32 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -23,15 +23,22 @@ def find_valid_ticket_granting_ticket(tgt, user_agent) def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) user_data = authentication_result[:user_data] - CASinoCore::Model::TicketGrantingTicket.create!({ + user = load_or_initialize_user(authentication_result[:authenticator], user_data[:username], user_data[:extra_attributes]) + user.ticket_granting_tickets.create!({ ticket: random_ticket_string('TGC'), - authenticator: authentication_result[:authenticator], - username: user_data[:username], - extra_attributes: user_data[:extra_attributes], user_agent: user_agent }) end + def load_or_initialize_user(authenticator, username, extra_attributes) + user = CASinoCore::Model::User.where( + authenticator: authenticator, + username: username).first_or_initialize + user.extra_attributes = extra_attributes + user.save! + return user + end + def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) tgt = find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) unless tgt.nil? diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index d5ad8904..9abe7d87 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -25,7 +25,7 @@ def same_user?(other_ticket) if other_ticket.nil? false else - other_ticket.username == self.username && other_ticket.authenticator == self.authenticator + other_ticket.user_id == self.user_id end end diff --git a/lib/casino_core/processor/legacy_validator.rb b/lib/casino_core/processor/legacy_validator.rb index dec216f2..7d118d96 100644 --- a/lib/casino_core/processor/legacy_validator.rb +++ b/lib/casino_core/processor/legacy_validator.rb @@ -16,7 +16,7 @@ def process(params = nil) params ||= {} ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first if !params[:service].nil? && ticket_valid_for_service?(ticket, params[:service], !!params[:renew]) - @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n") + @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.user.username}\n") else @listener.validation_failed("no\n\n") end diff --git a/lib/casino_core/processor/session_overview.rb b/lib/casino_core/processor/session_overview.rb index c19eeb15..eb2ce085 100644 --- a/lib/casino_core/processor/session_overview.rb +++ b/lib/casino_core/processor/session_overview.rb @@ -18,10 +18,7 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else - ticket_granting_tickets = CASinoCore::Model::TicketGrantingTicket.where( - username: tgt.username, - authenticator: tgt.authenticator - ).order('updated_at DESC') + ticket_granting_tickets = tgt.user.ticket_granting_tickets @listener.ticket_granting_tickets_found(ticket_granting_tickets) end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 094b4b76..fb451808 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -65,23 +65,15 @@ end context 'with a ticket from another user' do - let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, username: 'bla' } + let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } it 'should return false' do ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == false end end - context 'with a ticket from another authenticator' do - let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, authenticator: 'bla' } - - it 'should return false' do - ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == false - end - end - - context 'with a ticket from the same user and authenticator' do - let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + context 'with a ticket from the same user' do + let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: ticket_granting_ticket.user } it 'should return true' do ticket_granting_ticket.same_user?(other_ticket_granting_ticket).should == true diff --git a/spec/processor/legacy_validator_spec.rb b/spec/processor/legacy_validator_spec.rb index 04e3b154..84fbb0fb 100644 --- a/spec/processor/legacy_validator_spec.rb +++ b/spec/processor/legacy_validator_spec.rb @@ -6,6 +6,7 @@ let(:processor) { described_class.new(listener) } let(:service_ticket) { FactoryGirl.create :service_ticket } let(:parameters) { { service: service_ticket.service, ticket: service_ticket.ticket }} + let(:username) { service_ticket.ticket_granting_ticket.user.username } before(:each) do listener.stub(:validation_failed) @@ -21,7 +22,7 @@ end it 'calls the #validation_succeeded method on the listener' do - listener.should_receive(:validation_succeeded).with("yes\ntest\n") + listener.should_receive(:validation_succeeded).with("yes\n#{username}\n") processor.process(parameters) end end @@ -55,7 +56,7 @@ end it 'calls the #validation_succeeded method on the listener' do - listener.should_receive(:validation_succeeded).with("yes\ntest\n") + listener.should_receive(:validation_succeeded).with("yes\n#{username}\n") processor.process(parameters_with_renew) end end diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index 18707c9c..a448a66a 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -5,6 +5,7 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } let(:owner_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { owner_ticket_granting_ticket.user } let(:user_agent) { owner_ticket_granting_ticket.user_agent } let(:cookies) { { tgt: owner_ticket_granting_ticket.ticket } } @@ -14,12 +15,12 @@ end context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } let(:service_ticket) { FactoryGirl.create :service_ticket, ticket_granting_ticket: ticket_granting_ticket } let(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket } let(:params) { { id: ticket_granting_ticket.id } } - it 'deletes only one ticket-granting ticket' do + it 'deletes exactly one ticket-granting ticket' do ticket_granting_ticket owner_ticket_granting_ticket lambda do @@ -54,7 +55,7 @@ end context 'when trying to delete ticket-granting ticket of another user' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, username: 'other_user' } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:params) { { id: ticket_granting_ticket.id } } it 'does not delete a ticket-granting ticket' do diff --git a/spec/processor/session_overview_spec.rb b/spec/processor/session_overview_spec.rb index be31a112..4b5ab3f6 100644 --- a/spec/processor/session_overview_spec.rb +++ b/spec/processor/session_overview_spec.rb @@ -5,6 +5,7 @@ let(:listener) { Object.new } let(:processor) { described_class.new(listener) } let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { other_ticket_granting_ticket.user } let(:user_agent) { other_ticket_granting_ticket.user_agent } let(:cookies) { { tgt: tgt } } @@ -15,7 +16,7 @@ end context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } let(:tgt) { ticket_granting_ticket.ticket } it 'calls the #ticket_granting_tickets_found method on the listener' do listener.should_receive(:ticket_granting_tickets_found) do |tickets| @@ -26,7 +27,7 @@ end context 'with a ticket-granting ticket with same username but different authenticator' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, authenticator: 'other' } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:tgt) { ticket_granting_ticket.ticket } it 'calls the #ticket_granting_tickets_found method on the listener' do diff --git a/spec/support/factories/ticket_granting_ticket_factory.rb b/spec/support/factories/ticket_granting_ticket_factory.rb index d916012a..6b4b2960 100644 --- a/spec/support/factories/ticket_granting_ticket_factory.rb +++ b/spec/support/factories/ticket_granting_ticket_factory.rb @@ -2,12 +2,10 @@ FactoryGirl.define do factory :ticket_granting_ticket, class: CASinoCore::Model::TicketGrantingTicket do + user sequence :ticket do |n| "TGC-ticket#{n}" end - authenticator 'test' - username 'test' - extra_attributes({ fullname: "Test User", age: 15, roles: [:user] }) user_agent 'TestBrowser 1.0' end end diff --git a/spec/support/factories/user_factory.rb b/spec/support/factories/user_factory.rb new file mode 100644 index 00000000..b3003e62 --- /dev/null +++ b/spec/support/factories/user_factory.rb @@ -0,0 +1,11 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :user, class: CASinoCore::Model::User do + authenticator 'test' + sequence(:username) do |n| + "test#{n}" + end + extra_attributes({ fullname: "Test User", age: 15, roles: [:user] }) + end +end From 16680ef489facbd94b54767e68fed8a5e59f74b7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 22:08:24 +0100 Subject: [PATCH 246/350] More tests --- .../login_credential_acceptor_spec.rb | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index cb73ec88..bdceb8f4 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -33,7 +33,9 @@ context 'with valid credentials' do let(:service) { 'https://www.example.org' } - let(:login_data) { { lt: login_ticket.ticket, username: 'testuser', password: 'foobar123', service: service } } + let(:username) { 'testuser' } + let(:authenticator) { 'static_1' } + let(:login_data) { { lt: login_ticket.ticket, username: username, password: 'foobar123', service: service } } before(:each) do listener.stub(:user_logged_in) @@ -77,6 +79,38 @@ processor.process(login_data) end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(1) end + + context 'when the user does not exist yet' do + it 'generates exactly one user' do + lambda do + processor.process(login_data) + end.should change(CASinoCore::Model::User, :count).by(1) + end + + it 'sets the users attributes' do + processor.process(login_data) + user = CASinoCore::Model::User.last + user.username.should == username + user.authenticator.should == 'static_1' + end + end + + context 'when the user already exists' do + it 'does not regenerate the user' do + CASinoCore::Model::User.create! username: username, authenticator: authenticator + lambda do + processor.process(login_data) + end.should_not change(CASinoCore::Model::User, :count) + end + + it 'updates the extra attributes' do + user = CASinoCore::Model::User.create! username: username, authenticator: authenticator + lambda do + processor.process(login_data) + user.reload + end.should change(user, :extra_attributes) + end + end end context 'with a service' do From 0103a1b8d6497f998ffb58e047921f461c76f282 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 22:12:28 +0100 Subject: [PATCH 247/350] Removed debug output --- db/migrate/20130202210100_create_users.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/db/migrate/20130202210100_create_users.rb b/db/migrate/20130202210100_create_users.rb index db33c580..ab2384be 100644 --- a/db/migrate/20130202210100_create_users.rb +++ b/db/migrate/20130202210100_create_users.rb @@ -28,7 +28,6 @@ def up ticket.user_id = user.id ticket.save! end - p CASinoCore::Model::TicketGrantingTicket.all change_column :ticket_granting_tickets, :user_id, :integer, null: false remove_columns :ticket_granting_tickets, :authenticator, :username, :extra_attributes end From 9cff5b0a73df2e4650088c09d6a8af383c52f764 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 22:30:32 +0100 Subject: [PATCH 248/350] Add upgrade hint --- UPGRADE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index cbd99788..0cc9f7b9 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,12 @@ Here is a list of backward-incompatible changes that were introduced. +## 1.x.y + +API changes: + +* We extracted user data into an entity. Because of this, attributes such as `username` are no longer accessible directly on a `ticket_granting_ticket`. Use `ticket_granting_ticket.user.username` instead. + ## 1.1.0 API changes: From 24371df5a57d0b44af2c97fad770a0e0992c9010 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 22:31:12 +0100 Subject: [PATCH 249/350] Touch ticket-granting ticket to update time --- lib/casino_core/helper/ticket_granting_tickets.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index e298644a..06465434 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -12,6 +12,7 @@ def find_valid_ticket_granting_ticket(tgt, user_agent) unless ticket_granting_ticket.nil? if same_browser?(ticket_granting_ticket.user_agent, user_agent) ticket_granting_ticket.user_agent = user_agent + ticket_granting_ticket.touch ticket_granting_ticket.save! ticket_granting_ticket else From fee7b1b733a588e7fe9f1d05995241d68c003dd4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 23:31:25 +0100 Subject: [PATCH 250/350] Sign gem --- casino-public_cert.pem | 19 +++++++++++++++++++ casino_core.gemspec | 3 +++ 2 files changed, 22 insertions(+) create mode 100644 casino-public_cert.pem diff --git a/casino-public_cert.pem b/casino-public_cert.pem new file mode 100644 index 00000000..8c57fb93 --- /dev/null +++ b/casino-public_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhKgAwIBAgIBADANBgkqhkiG9w0BAQUFADA7MQ0wCwYDVQQDDARpbmZv +MRUwEwYKCZImiZPyLGQBGRYFcmJjYXMxEzARBgoJkiaJk/IsZAEZFgNjb20wHhcN +MTMwMjAyMjIyNjI2WhcNMTQwMjAyMjIyNjI2WjA7MQ0wCwYDVQQDDARpbmZvMRUw +EwYKCZImiZPyLGQBGRYFcmJjYXMxEzARBgoJkiaJk/IsZAEZFgNjb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbdmNy4heNReG8LXB2nakrpArkqWvw +jVnxXMLS6T5qebfLWal1PRoPHzbhRGmA3uCYYYuVuXv6V1VmCtnM0mj3YgN6h61D +D+Wnh1KT8sUYhRB36MNtmierS1EcMyvRujXRLk6x06Abz9bJadyEW7DS4VkpCz9n +f9MEnHqIlyQBPP3zHsDyMrTrIBuvDWPr+aAMKqIXLjqWep1Ebd//ppNcSiVF87s+ +e0JaFe7/1alxIPGOak/cGEvom42TLGdPKyu0Xjk2n/cWTAlBshFPOQS3hks6Rh8s +Vzwj0LQvU0rhXJWHNb6WujKjiwsvzSTlGydNwIENprJIAQJsaIX3RQInAgMBAAGj +OTA3MAkGA1UdEwQCMAAwHQYDVR0OBBYEFKyL/UzGU8IZneOjr73XPCLZJ7QuMAsG +A1UdDwQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAUK+fuki/gUhIlJqM24NCs/y3 +SoqCGP0z+c5g+BMu3sc3xIN/mH+HYlPaEa6Wj4bwmSVgThaZ54OsmRyZIK1VoAym +T4zOqCwt0twT2az1P6XThVMEebLjZDbuQ/oQzU/fA6DYqjnfmYNtgp5qYX6CKNJz +w3YRKrK2X6qYYHcHI/KL5wc1DOn+UNU4efmP0VVd5UNfR40IBLNtxX96X9YTXOHE +wQsLi+Ljnz+UaORk1dxZlcXYGc34wFg1oURvu0G8/YyHUAmIU/WKrjr1bgcf1VRv +R4KD1MnUV/v502piMlXmjxOWdbK8yvQEHksu/zjbCjSu+M2kwFmWGsx5nx+Vdw== +-----END CERTIFICATE----- diff --git a/casino_core.gemspec b/casino_core.gemspec index c8eda1c4..9992233d 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -17,6 +17,9 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ['lib'] + s.signing_key = '~/.gem/casino-private_key.pem' + s.cert_chain = ['casino-public_cert.pem'] + s.add_development_dependency 'rake', '~> 10.0' s.add_development_dependency 'rspec', '~> 2.12' s.add_development_dependency 'simplecov', '~> 0.7' From bbe352f0f34d2017b2fc46f3341c260bdbdbe6d9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 23:31:55 +0100 Subject: [PATCH 251/350] Bumped version --- Gemfile.lock | 2 +- UPGRADE.md | 2 +- lib/casino_core/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7f5d60ed..1d82710c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.1.0) + casino_core (1.2.0) activerecord (~> 3.2.9) addressable (~> 2.3) terminal-table (~> 1.4) diff --git a/UPGRADE.md b/UPGRADE.md index 0cc9f7b9..c65c8eec 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,7 +2,7 @@ Here is a list of backward-incompatible changes that were introduced. -## 1.x.y +## 1.2.0 API changes: diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index f1c00e10..e313398a 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.1.0' + VERSION = '1.2.0' end From a5b15fbd35811f662e22b807ba7c737bbc1a1dcb Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 23:33:38 +0100 Subject: [PATCH 252/350] Expand path to private key --- casino_core.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 9992233d..fc528bc5 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ['lib'] - s.signing_key = '~/.gem/casino-private_key.pem' + s.signing_key = File.expand_path '~/.gem/casino-private_key.pem' s.cert_chain = ['casino-public_cert.pem'] s.add_development_dependency 'rake', '~> 10.0' From d4fbddce31ae8eac341d7e11358f03fa199d51ca Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 00:08:07 +0100 Subject: [PATCH 253/350] Removed debug code --- db/migrate/20130202210100_create_users.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/db/migrate/20130202210100_create_users.rb b/db/migrate/20130202210100_create_users.rb index ab2384be..65b1422d 100644 --- a/db/migrate/20130202210100_create_users.rb +++ b/db/migrate/20130202210100_create_users.rb @@ -1,11 +1,5 @@ class CreateUsers < ActiveRecord::Migration def up - tgt = CASinoCore::Model::TicketGrantingTicket.new - tgt.authenticator = 'foo' - tgt.username = 'bar' - tgt.ticket = 'TGT-bla' - tgt.save! - create_table :users do |t| t.string :authenticator, null: false t.string :username, null: false From fcc27492a300d3efc8efe5156ed9be6d7ebda5c9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Thu, 7 Feb 2013 21:28:38 +0100 Subject: [PATCH 254/350] Newest tickets should be on top --- lib/casino_core/processor/session_overview.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/processor/session_overview.rb b/lib/casino_core/processor/session_overview.rb index eb2ce085..da114869 100644 --- a/lib/casino_core/processor/session_overview.rb +++ b/lib/casino_core/processor/session_overview.rb @@ -18,7 +18,7 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else - ticket_granting_tickets = tgt.user.ticket_granting_tickets + ticket_granting_tickets = tgt.user.ticket_granting_tickets.order('updated_at DESC') @listener.ticket_granting_tickets_found(ticket_granting_tickets) end end From c6fe0d8d3ba1eaee640173aaeae0564e4be9489f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 15:55:51 +0100 Subject: [PATCH 255/350] Added faraday gem --- Gemfile.lock | 4 ++++ casino_core.gemspec | 1 + 2 files changed, 5 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 1d82710c..88620449 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ PATH casino_core (1.2.0) activerecord (~> 3.2.9) addressable (~> 2.3) + faraday (~> 0.8) terminal-table (~> 1.4) useragent (~> 0.4) @@ -29,8 +30,11 @@ GEM diff-lcs (1.1.3) factory_girl (4.2.0) activesupport (>= 3.0.0) + faraday (0.8.5) + multipart-post (~> 1.1) i18n (0.6.1) multi_json (1.5.0) + multipart-post (1.1.5) nokogiri (1.5.6) rake (10.0.3) rspec (2.12.0) diff --git a/casino_core.gemspec b/casino_core.gemspec index fc528bc5..470dd6e5 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -34,5 +34,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'addressable', '~> 2.3' s.add_runtime_dependency 'terminal-table', '~> 1.4' s.add_runtime_dependency 'useragent', '~> 0.4' + s.add_runtime_dependency 'faraday', '~> 0.8' end From 53bc4988c09463bf20db9431423f432edd86333f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 16:16:24 +0100 Subject: [PATCH 256/350] Faraday instead of Net::HTTP for Single Sign Out Notifications --- .../single_sign_out_notifier.rb | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index 1d5f926e..25faf19c 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -1,7 +1,6 @@ require 'builder' -require 'net/https' +require 'faraday' require 'casino_core/model/service_ticket' -require 'addressable/uri' class CASinoCore::Model::ServiceTicket::SingleSignOutNotifier include CASinoCore::Helper::Logger @@ -11,10 +10,7 @@ def initialize(service_ticket) end def notify - xml = build_xml - uri = Addressable::URI.parse(@service_ticket.service) - request = build_request(uri, xml) - send_notification(uri, request) + send_notification @service_ticket.service, build_xml end private @@ -32,31 +28,18 @@ def build_xml xml.target! end - def build_request(uri, xml) - request = Net::HTTP::Post.new(uri.request_uri) - request.set_form_data(logoutRequest: xml) - return request - end - - def send_notification(uri, request) + def send_notification(url, xml) logger.info "Sending Single Sign Out notification for ticket '#{@service_ticket.ticket}'" - begin - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true if uri.scheme =='https' - - http.start do |conn| - response = conn.request(request) - if response.kind_of? Net::HTTPSuccess - logger.info "Logout notification successfully posted to #{uri}." - return true - else - logger.warn "Service #{uri} responed to logout notification with code '#{response.code}'!" - return false - end - end - rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e - logger.warn "Failed to send logout notification to service #{uri} due to #{e}" - return false + result = Faraday.post(url, logoutRequest: xml) + if result.status == 200 + logger.info "Logout notification successfully posted to #{url}." + true + else + logger.warn "Service #{url} responed to logout notification with code '#{result.status}'!" + false end + rescue Faraday::Error::ClientError => error + logger.warn "Failed to send logout notification to service #{url} due to #{error}" + false end end From 4b7e289a515f174b43c37465a62f9af2d22b1ce5 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 20:02:49 +0100 Subject: [PATCH 257/350] Use faraday also for PGT callback servers --- .../helper/proxy_granting_tickets.rb | 57 +++++++++---------- spec/processor/ticket_validator_spec.rb | 15 +++++ 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/lib/casino_core/helper/proxy_granting_tickets.rb b/lib/casino_core/helper/proxy_granting_tickets.rb index 865ead9a..351683ca 100644 --- a/lib/casino_core/helper/proxy_granting_tickets.rb +++ b/lib/casino_core/helper/proxy_granting_tickets.rb @@ -1,5 +1,5 @@ require 'addressable/uri' -require 'net/https' +require 'faraday' require 'casino_core/helper/logger' require 'casino_core/helper/tickets' @@ -11,41 +11,36 @@ module ProxyGrantingTickets include CASinoCore::Helper::Tickets def acquire_proxy_granting_ticket(pgt_url, service_ticket) - begin - return contact_callback_server(pgt_url, service_ticket) - rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e - logger.warn "Exception while communicating with proxy-granting ticket callback server: #{e.message}" + callback_uri = Addressable::URI.parse(pgt_url) + if callback_uri.scheme != 'https' + logger.warn "Proxy tickets can only be granted to callback servers using HTTPS." + nil + else + contact_callback_server(callback_uri, service_ticket) end - nil end private - def contact_callback_server(pgt_url, service_ticket) - callback_uri = Addressable::URI.parse(pgt_url) - https = Net::HTTP.new(callback_uri.host, callback_uri.port || 443) - https.use_ssl = true - - https.start do |conn| - pgt = service_ticket.proxy_granting_tickets.new({ - ticket: random_ticket_string('PGT'), - iou: random_ticket_string('PGTIOU'), - pgt_url: pgt_url - }) - - callback_uri.query_values = (callback_uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) - - response = conn.request_get(callback_uri.request_uri) - # TODO: follow redirects... 2.5.4 says that redirects MAY be followed - if "#{response.code}" == "200" - # 3.4 (proxy-granting ticket IOU) - pgt.save! - logger.debug "Proxy-granting ticket generated for service '#{service_ticket.service}': #{pgt.inspect}" - pgt - else - logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored." - nil - end + def contact_callback_server(callback_uri, service_ticket) + pgt = service_ticket.proxy_granting_tickets.new({ + ticket: random_ticket_string('PGT'), + iou: random_ticket_string('PGTIOU'), + pgt_url: "#{callback_uri}" + }) + callback_uri.query_values = (callback_uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou) + response = Faraday.get "#{callback_uri}" + # TODO: does this follow redirects? CAS specification says that redirects MAY be followed (2.5.4) + if response.success? + pgt.save! + logger.debug "Proxy-granting ticket generated for service '#{service_ticket.service}': #{pgt.inspect}" + pgt + else + logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.status}'. PGT will not be stored." + nil end + rescue Faraday::Error::ClientError => error + logger.warn "Exception while communicating with proxy-granting ticket callback server: #{error.message}" + nil end end end diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 24d71c95..083b65d5 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -92,6 +92,21 @@ stub_request(:get, /#{pgt_url}\/\?pgtId=[^&]+&pgtIou=[^&]+/) end + context 'not using https' do + let(:pgt_url) { 'http://www.example.org' } + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with(regex_success) + processor.process(parameters_with_pgt_url) + end + + it 'does not create a proxy-granting ticket' do + lambda do + processor.process(parameters_with_pgt_url) + end.should_not change(service_ticket.proxy_granting_tickets, :count) + end + end + it 'calls the #validation_succeeded method on the listener' do listener.should_receive(:validation_succeeded).with(regex_success) processor.process(parameters_with_pgt_url) From 216d873e663f637a3546b519ebd3a841e9d1c259 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 20:03:14 +0100 Subject: [PATCH 258/350] Make it clearer --- .../model/service_ticket/single_sign_out_notifier.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index 25faf19c..aae25b9f 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -31,7 +31,7 @@ def build_xml def send_notification(url, xml) logger.info "Sending Single Sign Out notification for ticket '#{@service_ticket.ticket}'" result = Faraday.post(url, logoutRequest: xml) - if result.status == 200 + if result.success? logger.info "Logout notification successfully posted to #{url}." true else From ce347a31a80ce9a6d2d575a2e67a436b24f09539 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 20:02:57 +0100 Subject: [PATCH 259/350] Add rotp gem https://github.com/mdp/rotp --- Gemfile.lock | 2 ++ casino_core.gemspec | 1 + 2 files changed, 3 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 88620449..19c14b83 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,6 +5,7 @@ PATH activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) + rotp (~> 1.4) terminal-table (~> 1.4) useragent (~> 0.4) @@ -37,6 +38,7 @@ GEM multipart-post (1.1.5) nokogiri (1.5.6) rake (10.0.3) + rotp (1.4.1) rspec (2.12.0) rspec-core (~> 2.12.0) rspec-expectations (~> 2.12.0) diff --git a/casino_core.gemspec b/casino_core.gemspec index 470dd6e5..504c5718 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -35,5 +35,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'terminal-table', '~> 1.4' s.add_runtime_dependency 'useragent', '~> 0.4' s.add_runtime_dependency 'faraday', '~> 0.8' + s.add_runtime_dependency 'rotp', '~> 1.4' end From e86a314c86ef9662c3174fb34458e1215bea604e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 21:00:23 +0100 Subject: [PATCH 260/350] Added model for two-factor authenticators --- ...0130202201500_create_two_factor_authenticators.rb | 12 ++++++++++++ lib/casino_core/model.rb | 1 + lib/casino_core/model/two_factor_authenticator.rb | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 db/migrate/20130202201500_create_two_factor_authenticators.rb create mode 100644 lib/casino_core/model/two_factor_authenticator.rb diff --git a/db/migrate/20130202201500_create_two_factor_authenticators.rb b/db/migrate/20130202201500_create_two_factor_authenticators.rb new file mode 100644 index 00000000..feb4ec41 --- /dev/null +++ b/db/migrate/20130202201500_create_two_factor_authenticators.rb @@ -0,0 +1,12 @@ +class CreateTwoFactorAuthenticators < ActiveRecord::Migration + def change + create_table :two_factor_authenticators do |t| + t.integer :user_id, null: false + t.string :secret, null: false + + t.timestamps + end + + add_index :two_factor_authenticators, :user_id + end +end diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index da7b866d..ff61a735 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -8,6 +8,7 @@ module Model autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb' autoload :ProxyTicket, 'casino_core/model/proxy_ticket.rb' autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb' + autoload :TwoFactorAuthenticator, 'casino_core/model/two_factor_authenticator.rb' autoload :User, 'casino_core/model/user.rb' end end diff --git a/lib/casino_core/model/two_factor_authenticator.rb b/lib/casino_core/model/two_factor_authenticator.rb new file mode 100644 index 00000000..6b15fde4 --- /dev/null +++ b/lib/casino_core/model/two_factor_authenticator.rb @@ -0,0 +1,7 @@ +require 'casino_core/model' + +class CASinoCore::Model::TwoFactorAuthenticator < ActiveRecord::Base + attr_accessible :secret + + belongs_to :user +end From 7b02b3b2583b0de550a70b4bd487af37b4722ddf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 2 Feb 2013 21:00:42 +0100 Subject: [PATCH 261/350] Processor for two-factor authenticator overview --- lib/casino_core/processor.rb | 1 + .../two_factor_authenticator_overview.rb | 23 ++++++++++ .../two_factor_authenticator_overview_spec.rb | 45 +++++++++++++++++++ .../two_factor_authenticator_factory.rb | 10 +++++ 4 files changed, 79 insertions(+) create mode 100644 lib/casino_core/processor/two_factor_authenticator_overview.rb create mode 100644 spec/processor/two_factor_authenticator_overview_spec.rb create mode 100644 spec/support/factories/two_factor_authenticator_factory.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 199c8ea4..a4ede1be 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -11,6 +11,7 @@ class Processor autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' + autoload :TwoFactorAuthenticatorOverview, 'casino_core/processor/two_factor_authenticator_overview.rb' autoload :API, 'casino_core/processor/api.rb' diff --git a/lib/casino_core/processor/two_factor_authenticator_overview.rb b/lib/casino_core/processor/two_factor_authenticator_overview.rb new file mode 100644 index 00000000..de1a5812 --- /dev/null +++ b/lib/casino_core/processor/two_factor_authenticator_overview.rb @@ -0,0 +1,23 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The TwoFactorOverview processor lists registered two factor devices for the currently signed in user. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::TwoFactorAuthenticatorOverview < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. + # @param [Hash] cookies cookies delivered by the client + # @param [String] user_agent user-agent delivered by the client + def process(cookies = nil, user_agent = nil) + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + if tgt.nil? + @listener.user_not_logged_in + else + end + end +end diff --git a/spec/processor/two_factor_authenticator_overview_spec.rb b/spec/processor/two_factor_authenticator_overview_spec.rb new file mode 100644 index 00000000..ea6d9940 --- /dev/null +++ b/spec/processor/two_factor_authenticator_overview_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe CASinoCore::Processor::TwoFactorAuthenticatorOverview do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:ticket_granting_tickets_found) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + + context 'without a two-factor authenticator registered' do + + end + + context 'with a two-factor authenticator registered' do + let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator } + before(:each) do + two_factor_authenticator + end + + it 'calls the #two_factor_authenticators_found method on the listener' do + listener.should_receive(:two_factor_authenticators_found).with([two_factor_authenticator]) + processor.process(cookies, user_agent) + end + end + end + + context 'with an invalid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process(cookies, user_agent) + end + end + end +end \ No newline at end of file diff --git a/spec/support/factories/two_factor_authenticator_factory.rb b/spec/support/factories/two_factor_authenticator_factory.rb new file mode 100644 index 00000000..f2d5e097 --- /dev/null +++ b/spec/support/factories/two_factor_authenticator_factory.rb @@ -0,0 +1,10 @@ +require 'factory_girl' + +FactoryGirl.define do + factory :two_factor_authenticator, class: CASinoCore::Model::TwoFactorAuthenticator do + ticket_granting_ticket + secret do |a| + ROTP::Base32.random_base32 + end + end +end From 4fe04e11498bd071ac96ded1865dd08133959462 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:01:01 +0100 Subject: [PATCH 262/350] Fixed migration --- ...0130203100015_create_two_factor_authenticators.rb} | 0 db/schema.rb | 11 ++++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) rename db/migrate/{20130202201500_create_two_factor_authenticators.rb => 20130203100015_create_two_factor_authenticators.rb} (100%) diff --git a/db/migrate/20130202201500_create_two_factor_authenticators.rb b/db/migrate/20130203100015_create_two_factor_authenticators.rb similarity index 100% rename from db/migrate/20130202201500_create_two_factor_authenticators.rb rename to db/migrate/20130203100015_create_two_factor_authenticators.rb diff --git a/db/schema.rb b/db/schema.rb index bf24f870..fac6f0ea 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130202210100) do +ActiveRecord::Schema.define(:version => 20130203100015) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -82,6 +82,15 @@ add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true + create_table "two_factor_authenticators", :force => true do |t| + t.integer "user_id", :null => false + t.string "secret", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "two_factor_authenticators", ["user_id"], :name => "index_two_factor_authenticators_on_user_id" + create_table "users", :force => true do |t| t.string "authenticator", :null => false t.string "username", :null => false From 0da259753f669de17dc25e2f3e58e9610dba0487 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:08:33 +0100 Subject: [PATCH 263/350] Two-factor authenticator overview --- lib/casino_core/model/user.rb | 1 + .../processor/two_factor_authenticator_overview.rb | 3 ++- .../two_factor_authenticator_overview_spec.rb | 12 +++++++----- .../factories/two_factor_authenticator_factory.rb | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/casino_core/model/user.rb b/lib/casino_core/model/user.rb index db8ba257..4842d6aa 100644 --- a/lib/casino_core/model/user.rb +++ b/lib/casino_core/model/user.rb @@ -5,4 +5,5 @@ class CASinoCore::Model::User < ActiveRecord::Base serialize :extra_attributes, Hash has_many :ticket_granting_tickets + has_many :two_factor_authenticators end diff --git a/lib/casino_core/processor/two_factor_authenticator_overview.rb b/lib/casino_core/processor/two_factor_authenticator_overview.rb index de1a5812..1e0c12a5 100644 --- a/lib/casino_core/processor/two_factor_authenticator_overview.rb +++ b/lib/casino_core/processor/two_factor_authenticator_overview.rb @@ -2,7 +2,7 @@ require 'casino_core/helper' require 'casino_core/model' -# The TwoFactorOverview processor lists registered two factor devices for the currently signed in user. +# The TwoFactorAuthenticatorOverview processor lists registered two factor devices for the currently signed in user. # # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. @@ -18,6 +18,7 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else + @listener.two_factor_authenticators_found(tgt.user.two_factor_authenticators) end end end diff --git a/spec/processor/two_factor_authenticator_overview_spec.rb b/spec/processor/two_factor_authenticator_overview_spec.rb index ea6d9940..896a8202 100644 --- a/spec/processor/two_factor_authenticator_overview_spec.rb +++ b/spec/processor/two_factor_authenticator_overview_spec.rb @@ -13,18 +13,20 @@ context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { ticket_granting_ticket.user } let(:tgt) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } context 'without a two-factor authenticator registered' do - + it 'calls the #two_factor_authenticators_found method on the listener' do + listener.should_receive(:two_factor_authenticators_found).with([]) + processor.process(cookies, user_agent) + end end context 'with a two-factor authenticator registered' do - let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator } - before(:each) do - two_factor_authenticator - end + let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } + let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator } it 'calls the #two_factor_authenticators_found method on the listener' do listener.should_receive(:two_factor_authenticators_found).with([two_factor_authenticator]) diff --git a/spec/support/factories/two_factor_authenticator_factory.rb b/spec/support/factories/two_factor_authenticator_factory.rb index f2d5e097..5383a1c1 100644 --- a/spec/support/factories/two_factor_authenticator_factory.rb +++ b/spec/support/factories/two_factor_authenticator_factory.rb @@ -1,8 +1,9 @@ require 'factory_girl' +require 'rotp' FactoryGirl.define do factory :two_factor_authenticator, class: CASinoCore::Model::TwoFactorAuthenticator do - ticket_granting_ticket + user secret do |a| ROTP::Base32.random_base32 end From 321f890afbef7b212460eed9fa40f6023822d883 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:19:10 +0100 Subject: [PATCH 264/350] Only include active two-factor authenticators --- ...3101351_add_active_to_two_factor_authenticators.rb | 5 +++++ db/schema.rb | 11 ++++++----- .../processor/two_factor_authenticator_overview.rb | 2 +- .../two_factor_authenticator_overview_spec.rb | 11 ++++++++++- .../factories/two_factor_authenticator_factory.rb | 5 +++++ 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20130203101351_add_active_to_two_factor_authenticators.rb diff --git a/db/migrate/20130203101351_add_active_to_two_factor_authenticators.rb b/db/migrate/20130203101351_add_active_to_two_factor_authenticators.rb new file mode 100644 index 00000000..fbb3e277 --- /dev/null +++ b/db/migrate/20130203101351_add_active_to_two_factor_authenticators.rb @@ -0,0 +1,5 @@ +class AddActiveToTwoFactorAuthenticators < ActiveRecord::Migration + def change + add_column :two_factor_authenticators, :active, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index fac6f0ea..484c4864 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130203100015) do +ActiveRecord::Schema.define(:version => 20130203101351) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -83,10 +83,11 @@ add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true create_table "two_factor_authenticators", :force => true do |t| - t.integer "user_id", :null => false - t.string "secret", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.integer "user_id", :null => false + t.string "secret", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "active", :default => false, :null => false end add_index "two_factor_authenticators", ["user_id"], :name => "index_two_factor_authenticators_on_user_id" diff --git a/lib/casino_core/processor/two_factor_authenticator_overview.rb b/lib/casino_core/processor/two_factor_authenticator_overview.rb index 1e0c12a5..bc6adc49 100644 --- a/lib/casino_core/processor/two_factor_authenticator_overview.rb +++ b/lib/casino_core/processor/two_factor_authenticator_overview.rb @@ -18,7 +18,7 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else - @listener.two_factor_authenticators_found(tgt.user.two_factor_authenticators) + @listener.two_factor_authenticators_found(tgt.user.two_factor_authenticators.where(active: true)) end end end diff --git a/spec/processor/two_factor_authenticator_overview_spec.rb b/spec/processor/two_factor_authenticator_overview_spec.rb index 896a8202..6c05ace7 100644 --- a/spec/processor/two_factor_authenticator_overview_spec.rb +++ b/spec/processor/two_factor_authenticator_overview_spec.rb @@ -8,7 +8,7 @@ before(:each) do listener.stub(:user_not_logged_in) - listener.stub(:ticket_granting_tickets_found) + listener.stub(:two_factor_authenticators_found) end context 'with an existing ticket-granting ticket' do @@ -24,6 +24,15 @@ end end + context 'with an inactive two-factor authenticator' do + let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive, user: user } + + it 'does not include the inactive authenticator' do + listener.should_receive(:two_factor_authenticators_found).with([]) + processor.process(cookies, user_agent) + end + end + context 'with a two-factor authenticator registered' do let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator } diff --git a/spec/support/factories/two_factor_authenticator_factory.rb b/spec/support/factories/two_factor_authenticator_factory.rb index 5383a1c1..e3bb9169 100644 --- a/spec/support/factories/two_factor_authenticator_factory.rb +++ b/spec/support/factories/two_factor_authenticator_factory.rb @@ -7,5 +7,10 @@ secret do |a| ROTP::Base32.random_base32 end + active true + + trait :inactive do + active false + end end end From e1b79dcf7266343fc6d1e66b02f5120341bb603c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:30:04 +0100 Subject: [PATCH 265/350] TwoFactorAuthenticatorProvider --- lib/casino_core/processor.rb | 1 + .../two_factor_authenticator_provider.rb | 25 ++++++++++ .../two_factor_authenticator_provider_spec.rb | 48 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 lib/casino_core/processor/two_factor_authenticator_provider.rb create mode 100644 spec/processor/two_factor_authenticator_provider_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index a4ede1be..4a82c6de 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -12,6 +12,7 @@ class Processor autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' autoload :TwoFactorAuthenticatorOverview, 'casino_core/processor/two_factor_authenticator_overview.rb' + autoload :TwoFactorAuthenticatorProvider, 'casino_core/processor/two_factor_authenticator_provider.rb' autoload :API, 'casino_core/processor/api.rb' diff --git a/lib/casino_core/processor/two_factor_authenticator_provider.rb b/lib/casino_core/processor/two_factor_authenticator_provider.rb new file mode 100644 index 00000000..1cb62ef0 --- /dev/null +++ b/lib/casino_core/processor/two_factor_authenticator_provider.rb @@ -0,0 +1,25 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The TwoFactorAuthenticatorProvider processor can be used as the first step to register a new two-factor authenticator. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::TwoFactorAuthenticatorProvider < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. + # @param [Hash] cookies cookies delivered by the client + # @param [String] user_agent user-agent delivered by the client + def process(cookies = nil, user_agent = nil) + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + if tgt.nil? + @listener.user_not_logged_in + else + two_factor_authenticator = tgt.user.two_factor_authenticators.create! secret: ROTP::Base32.random_base32 + @listener.two_factor_authenticator_created(two_factor_authenticator) + end + end +end diff --git a/spec/processor/two_factor_authenticator_provider_spec.rb b/spec/processor/two_factor_authenticator_provider_spec.rb new file mode 100644 index 00000000..0612224b --- /dev/null +++ b/spec/processor/two_factor_authenticator_provider_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe CASinoCore::Processor::TwoFactorAuthenticatorProvider do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:two_factor_authenticator_created) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { ticket_granting_ticket.user } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + + it 'creates exactly one authenticator' do + lambda do + processor.process(cookies, user_agent) + end.should change(CASinoCore::Model::TwoFactorAuthenticator, :count).by(1) + end + + it 'calls #two_factor_authenticator_created on the listener' do + listener.should_receive(:two_factor_authenticator_created) do |authenticator| + authenticator.should == CASinoCore::Model::TwoFactorAuthenticator.last + end + processor.process(cookies, user_agent) + end + + it 'creates an inactive two-factor authenticator' do + processor.process(cookies, user_agent) + CASinoCore::Model::TwoFactorAuthenticator.last.should_not be_active + end + end + + context 'with an invalid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process(cookies, user_agent) + end + end + end +end \ No newline at end of file From cd1b77ae9c71c176fecb08856acc60146a680919 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:45:47 +0100 Subject: [PATCH 266/350] Default settings --- config/cas.yml | 8 +------- lib/casino_core/settings.rb | 21 +++++++++++++++++++-- spec/settings_spec.rb | 9 +++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/config/cas.yml b/config/cas.yml index 217b8e7f..f18ca4b2 100644 --- a/config/cas.yml +++ b/config/cas.yml @@ -1,12 +1,6 @@ defaults: &defaults - login_ticket: - lifetime: 600 service_ticket: - lifetime_unconsumed: 300 - lifetime_consumed: 86400 - proxy_ticket: - lifetime_unconsumed: 300 - lifetime_consumed: 86400 + lifetime_unconsumed: 299 authenticators: static_1: class: "CASinoCore::Authenticator::Static" diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 488016c1..4bbcdbef 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,9 +3,26 @@ module CASinoCore class Settings class << self - attr_accessor :login_ticket, :service_ticket, :proxy_ticket, :authenticators, :logger + attr_accessor :login_ticket, :service_ticket, :proxy_ticket, :two_factor_authenticator, :authenticators, :logger + DEFAULT_SETTINGS = { + login_ticket: { + lifetime: 600 + }, + service_ticket: { + lifetime_unconsumed: 300, + lifetime_consumed: 86400 + }, + proxy_ticket: { + lifetime_unconsumed: 300, + lifetime_consumed: 86400 + }, + two_factor_authenticator: { + lifetime_inactive: 300 + } + } + def init(config = {}) - config.each do |key,value| + DEFAULT_SETTINGS.deep_merge(config).each do |key,value| if respond_to?("#{key}=") send("#{key}=", value) end diff --git a/spec/settings_spec.rb b/spec/settings_spec.rb index 3c9be5b3..d5cd0fda 100644 --- a/spec/settings_spec.rb +++ b/spec/settings_spec.rb @@ -1,6 +1,15 @@ require 'spec_helper' describe CASinoCore::Settings do + describe 'initializer' do + it 'loads default settings' do + described_class.service_ticket[:lifetime_consumed].should == 86400 + end + it 'overwrites specific settings' do + described_class.service_ticket[:lifetime_unconsumed].should == 299 + end + end + describe '#authenticators=' do context 'with an authenticator name' do let(:authenticator_name) { 'testing' } From 084711c2fdc79f895f9daaa26b8f3de037691031 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 10:53:58 +0100 Subject: [PATCH 267/350] Cleanup method for two-factor authenticators --- .../model/two_factor_authenticator.rb | 4 +++ lib/casino_core/tasks/cleanup.rake | 8 ++++- spec/model/two_factor_authenticator_spec.rb | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 spec/model/two_factor_authenticator_spec.rb diff --git a/lib/casino_core/model/two_factor_authenticator.rb b/lib/casino_core/model/two_factor_authenticator.rb index 6b15fde4..601b97d6 100644 --- a/lib/casino_core/model/two_factor_authenticator.rb +++ b/lib/casino_core/model/two_factor_authenticator.rb @@ -4,4 +4,8 @@ class CASinoCore::Model::TwoFactorAuthenticator < ActiveRecord::Base attr_accessible :secret belongs_to :user + + def self.cleanup + self.delete_all(['(created_at < ?) AND active = ?', CASinoCore::Settings.two_factor_authenticator[:lifetime_inactive].seconds.ago, false]) + end end diff --git a/lib/casino_core/tasks/cleanup.rake b/lib/casino_core/tasks/cleanup.rake index 0057b225..16576e3f 100644 --- a/lib/casino_core/tasks/cleanup.rake +++ b/lib/casino_core/tasks/cleanup.rake @@ -29,8 +29,14 @@ namespace :casino_core do puts "Deleted #{rows_affected} login tickets." end + desc 'Remove expired inactive two-factor authenticators.' + task two_factor_authenticators: 'casino_core:db:configure_connection' do + rows_affected = CASinoCore::Model::TwoFactorAuthenticator.cleanup + puts "Deleted #{rows_affected} inactive two-factor authenticators." + end + desc 'Perform all cleanup tasks.' - task all: [:service_tickets, :proxy_tickets, :login_tickets] do + task all: [:service_tickets, :proxy_tickets, :login_tickets, :two_factor_authenticators] do end end end diff --git a/spec/model/two_factor_authenticator_spec.rb b/spec/model/two_factor_authenticator_spec.rb new file mode 100644 index 00000000..3ccb045e --- /dev/null +++ b/spec/model/two_factor_authenticator_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe CASinoCore::Model::TwoFactorAuthenticator do + describe '.cleanup' do + it 'deletes expired inactive two-factor authenticators' do + authenticator = FactoryGirl.create :two_factor_authenticator, :inactive + authenticator.created_at = 10.hours.ago + authenticator.save! + lambda do + described_class.cleanup + end.should change(described_class, :count).by(-1) + end + + it 'does not delete not expired inactive two-factor authenticators' do + authenticator = FactoryGirl.create :two_factor_authenticator, :inactive + authenticator.created_at = (CASinoCore::Settings.two_factor_authenticator[:lifetime_inactive].seconds - 5).ago + lambda do + described_class.cleanup + end.should_not change(described_class, :count) + end + + it 'does not delete active two-factor authenticators' do + authenticator = FactoryGirl.create :two_factor_authenticator + authenticator.created_at = 10.hours.ago + authenticator.save! + lambda do + described_class.cleanup + end.should_not change(described_class, :count) + end + end +end From 5c1105423a4dbf403888132bb8dc6c2815d61e0b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 11:05:57 +0100 Subject: [PATCH 268/350] Fixed documentation --- lib/casino_core/processor/two_factor_authenticator_overview.rb | 2 +- lib/casino_core/processor/two_factor_authenticator_provider.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/processor/two_factor_authenticator_overview.rb b/lib/casino_core/processor/two_factor_authenticator_overview.rb index bc6adc49..03361295 100644 --- a/lib/casino_core/processor/two_factor_authenticator_overview.rb +++ b/lib/casino_core/processor/two_factor_authenticator_overview.rb @@ -9,7 +9,7 @@ class CASinoCore::Processor::TwoFactorAuthenticatorOverview < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets - # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. + # This method will call `#user_not_logged_in` or `#two_factor_authenticators_found(Enumerable)` on the listener. # @param [Hash] cookies cookies delivered by the client # @param [String] user_agent user-agent delivered by the client def process(cookies = nil, user_agent = nil) diff --git a/lib/casino_core/processor/two_factor_authenticator_provider.rb b/lib/casino_core/processor/two_factor_authenticator_provider.rb index 1cb62ef0..d82612e2 100644 --- a/lib/casino_core/processor/two_factor_authenticator_provider.rb +++ b/lib/casino_core/processor/two_factor_authenticator_provider.rb @@ -9,7 +9,7 @@ class CASinoCore::Processor::TwoFactorAuthenticatorProvider < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets - # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. + # This method will call `#user_not_logged_in` or `#two_factor_authenticator_created(two_factor_authenticator)` on the listener. # @param [Hash] cookies cookies delivered by the client # @param [String] user_agent user-agent delivered by the client def process(cookies = nil, user_agent = nil) From b0dba8285ef1956bdaac20d3e8210dec8755e059 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 12:47:41 +0100 Subject: [PATCH 269/350] Skeleton for TwoFactorAuthenticatorActivator --- lib/casino_core/processor.rb | 1 + .../two_factor_authenticator_activator.rb | 25 +++++++++++++++ ...two_factor_authenticator_activator_spec.rb | 31 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 lib/casino_core/processor/two_factor_authenticator_activator.rb create mode 100644 spec/processor/two_factor_authenticator_activator_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 4a82c6de..5a1ee1c5 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -11,6 +11,7 @@ class Processor autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' + autoload :TwoFactorAuthenticatorActivator, 'casino_core/processor/two_factor_authenticator_activator.rb' autoload :TwoFactorAuthenticatorOverview, 'casino_core/processor/two_factor_authenticator_overview.rb' autoload :TwoFactorAuthenticatorProvider, 'casino_core/processor/two_factor_authenticator_provider.rb' diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb new file mode 100644 index 00000000..8e94588f --- /dev/null +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -0,0 +1,25 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The TwoFactorAuthenticatorActivator processor can be used to activate a previously generated two-factor authenticator. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::TwoFactorAuthenticatorActivator < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#user_not_logged_in` on the listener. + # @param [Hash] cookies cookies delivered by the client + # @param [String] user_agent user-agent delivered by the client + # @param [String] id id of the two-factor authenticator + # @param [String] otp one time password given by the user + def process(cookies = nil, user_agent = nil, id = nil, otp = nil) + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + if tgt.nil? + @listener.user_not_logged_in + else + end + end +end diff --git a/spec/processor/two_factor_authenticator_activator_spec.rb b/spec/processor/two_factor_authenticator_activator_spec.rb new file mode 100644 index 00000000..6552bb1f --- /dev/null +++ b/spec/processor/two_factor_authenticator_activator_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe CASinoCore::Processor::TwoFactorAuthenticatorActivator do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:two_factor_authenticator_activated) + listener.stub(:invalid_two_factor_authenticator) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { ticket_granting_ticket.user } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + end + + context 'with an invalid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process(cookies, user_agent) + end + end + end +end \ No newline at end of file From 4a400c5d81d0ba3ab87b3c1489faa7a306112f0a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 12:51:03 +0100 Subject: [PATCH 270/350] Minor wording changes --- lib/casino_core/processor.rb | 2 +- ...ovider.rb => two_factor_authenticator_registrator.rb} | 9 +++++---- ...c.rb => two_factor_authenticator_registrator_spec.rb} | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) rename lib/casino_core/processor/{two_factor_authenticator_provider.rb => two_factor_authenticator_registrator.rb} (64%) rename spec/processor/{two_factor_authenticator_provider_spec.rb => two_factor_authenticator_registrator_spec.rb} (87%) diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 5a1ee1c5..4278b317 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -13,7 +13,7 @@ class Processor autoload :SessionOverview, 'casino_core/processor/session_overview.rb' autoload :TwoFactorAuthenticatorActivator, 'casino_core/processor/two_factor_authenticator_activator.rb' autoload :TwoFactorAuthenticatorOverview, 'casino_core/processor/two_factor_authenticator_overview.rb' - autoload :TwoFactorAuthenticatorProvider, 'casino_core/processor/two_factor_authenticator_provider.rb' + autoload :TwoFactorAuthenticatorRegistrator, 'casino_core/processor/two_factor_authenticator_registrator.rb' autoload :API, 'casino_core/processor/api.rb' diff --git a/lib/casino_core/processor/two_factor_authenticator_provider.rb b/lib/casino_core/processor/two_factor_authenticator_registrator.rb similarity index 64% rename from lib/casino_core/processor/two_factor_authenticator_provider.rb rename to lib/casino_core/processor/two_factor_authenticator_registrator.rb index d82612e2..d3855b71 100644 --- a/lib/casino_core/processor/two_factor_authenticator_provider.rb +++ b/lib/casino_core/processor/two_factor_authenticator_registrator.rb @@ -2,14 +2,15 @@ require 'casino_core/helper' require 'casino_core/model' -# The TwoFactorAuthenticatorProvider processor can be used as the first step to register a new two-factor authenticator. +# The TwoFactorAuthenticatorRegistrator processor can be used as the first step to register a new two-factor authenticator. +# It is inactive until activated using TwoFactorAuthenticatorActivator. # # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. -class CASinoCore::Processor::TwoFactorAuthenticatorProvider < CASinoCore::Processor +class CASinoCore::Processor::TwoFactorAuthenticatorRegistrator < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets - # This method will call `#user_not_logged_in` or `#two_factor_authenticator_created(two_factor_authenticator)` on the listener. + # This method will call `#user_not_logged_in` or `#two_factor_authenticator_registered(two_factor_authenticator)` on the listener. # @param [Hash] cookies cookies delivered by the client # @param [String] user_agent user-agent delivered by the client def process(cookies = nil, user_agent = nil) @@ -19,7 +20,7 @@ def process(cookies = nil, user_agent = nil) @listener.user_not_logged_in else two_factor_authenticator = tgt.user.two_factor_authenticators.create! secret: ROTP::Base32.random_base32 - @listener.two_factor_authenticator_created(two_factor_authenticator) + @listener.two_factor_authenticator_registered(two_factor_authenticator) end end end diff --git a/spec/processor/two_factor_authenticator_provider_spec.rb b/spec/processor/two_factor_authenticator_registrator_spec.rb similarity index 87% rename from spec/processor/two_factor_authenticator_provider_spec.rb rename to spec/processor/two_factor_authenticator_registrator_spec.rb index 0612224b..6b0f4582 100644 --- a/spec/processor/two_factor_authenticator_provider_spec.rb +++ b/spec/processor/two_factor_authenticator_registrator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CASinoCore::Processor::TwoFactorAuthenticatorProvider do +describe CASinoCore::Processor::TwoFactorAuthenticatorRegistrator do describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } @@ -8,7 +8,7 @@ before(:each) do listener.stub(:user_not_logged_in) - listener.stub(:two_factor_authenticator_created) + listener.stub(:two_factor_authenticator_registered) end context 'with an existing ticket-granting ticket' do @@ -24,7 +24,7 @@ end it 'calls #two_factor_authenticator_created on the listener' do - listener.should_receive(:two_factor_authenticator_created) do |authenticator| + listener.should_receive(:two_factor_authenticator_registered) do |authenticator| authenticator.should == CASinoCore::Model::TwoFactorAuthenticator.last end processor.process(cookies, user_agent) From de8b4b5e2662886aab11c2ff9f5963a40666485f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 13:02:13 +0100 Subject: [PATCH 271/350] Extracted ValidationResult --- lib/casino_core/helper/proxy_tickets.rb | 6 +----- lib/casino_core/model.rb | 1 + lib/casino_core/model/validation_result.rb | 7 +++++++ 3 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 lib/casino_core/model/validation_result.rb diff --git a/lib/casino_core/helper/proxy_tickets.rb b/lib/casino_core/helper/proxy_tickets.rb index 7fe4dc96..e0f2a1b6 100644 --- a/lib/casino_core/helper/proxy_tickets.rb +++ b/lib/casino_core/helper/proxy_tickets.rb @@ -2,11 +2,7 @@ module CASinoCore module Helper module ProxyTickets - class ValidationResult < Struct.new(:error_code, :error_message, :error_severity) - def success? - self.error_code.nil? - end - end + class ValidationResult < CASinoCore::Model::ValidationResult; end include CASinoCore::Helper::Logger include CASinoCore::Helper::Tickets diff --git a/lib/casino_core/model.rb b/lib/casino_core/model.rb index ff61a735..5fc10c2f 100644 --- a/lib/casino_core/model.rb +++ b/lib/casino_core/model.rb @@ -10,5 +10,6 @@ module Model autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb' autoload :TwoFactorAuthenticator, 'casino_core/model/two_factor_authenticator.rb' autoload :User, 'casino_core/model/user.rb' + autoload :ValidationResult, 'casino_core/model/validation_result.rb' end end diff --git a/lib/casino_core/model/validation_result.rb b/lib/casino_core/model/validation_result.rb new file mode 100644 index 00000000..478631ce --- /dev/null +++ b/lib/casino_core/model/validation_result.rb @@ -0,0 +1,7 @@ +require 'casino_core/model' + +class CASinoCore::Model::ValidationResult < Struct.new(:error_code, :error_message, :error_severity) + def success? + self.error_code.nil? + end +end From 590e3413d3bbe476d95eb0555ccbc5501018756b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 13:38:37 +0100 Subject: [PATCH 272/350] Allow activation of tow-factor authenticators --- lib/casino_core/helper.rb | 1 + .../helper/two_factor_authenticators.rb | 22 +++++ .../model/two_factor_authenticator.rb | 10 ++- .../two_factor_authenticator_activator.rb | 15 ++++ lib/casino_core/settings.rb | 3 +- ...two_factor_authenticator_activator_spec.rb | 90 +++++++++++++++++++ 6 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 lib/casino_core/helper/two_factor_authenticators.rb diff --git a/lib/casino_core/helper.rb b/lib/casino_core/helper.rb index b07719bc..30d5495f 100644 --- a/lib/casino_core/helper.rb +++ b/lib/casino_core/helper.rb @@ -12,5 +12,6 @@ module Helper autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb' autoload :Tickets, 'casino_core/helper/tickets.rb' autoload :TicketGrantingTickets, 'casino_core/helper/ticket_granting_tickets.rb' + autoload :TwoFactorAuthenticators, 'casino_core/helper/two_factor_authenticators.rb' end end diff --git a/lib/casino_core/helper/two_factor_authenticators.rb b/lib/casino_core/helper/two_factor_authenticators.rb new file mode 100644 index 00000000..63ba9e9e --- /dev/null +++ b/lib/casino_core/helper/two_factor_authenticators.rb @@ -0,0 +1,22 @@ +require 'addressable/uri' + +module CASinoCore + module Helper + module TwoFactorAuthenticators + class ValidationResult < CASinoCore::Model::ValidationResult; end + + def validate_one_time_password(user, otp, authenticator = nil) + if authenticator.nil? || authenticator.expired? || authenticator.user_id != user.id + ValidationResult.new 'INVALID_AUTHENTICATOR', 'Authenticator does not exist or expired', :warn + else + totp = ROTP::TOTP.new(authenticator.secret) + if totp.verify_with_drift(otp, CASinoCore::Settings.two_factor_authenticator[:drift]) + ValidationResult.new + else + ValidationResult.new 'INVALID_OTP', 'One-time password not valid', :warn + end + end + end + end + end +end diff --git a/lib/casino_core/model/two_factor_authenticator.rb b/lib/casino_core/model/two_factor_authenticator.rb index 601b97d6..89225d4b 100644 --- a/lib/casino_core/model/two_factor_authenticator.rb +++ b/lib/casino_core/model/two_factor_authenticator.rb @@ -6,6 +6,14 @@ class CASinoCore::Model::TwoFactorAuthenticator < ActiveRecord::Base belongs_to :user def self.cleanup - self.delete_all(['(created_at < ?) AND active = ?', CASinoCore::Settings.two_factor_authenticator[:lifetime_inactive].seconds.ago, false]) + self.delete_all(['(created_at < ?) AND active = ?', self.lifetime.ago, false]) + end + + def self.lifetime + CASinoCore::Settings.two_factor_authenticator[:lifetime_inactive].seconds + end + + def expired? + !self.active? && (Time.now - (self.created_at || Time.now)) > self.class.lifetime end end diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb index 8e94588f..58501645 100644 --- a/lib/casino_core/processor/two_factor_authenticator_activator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -8,6 +8,7 @@ # to implement this on the web application side. class CASinoCore::Processor::TwoFactorAuthenticatorActivator < CASinoCore::Processor include CASinoCore::Helper::TicketGrantingTickets + include CASinoCore::Helper::TwoFactorAuthenticators # This method will call `#user_not_logged_in` on the listener. # @param [Hash] cookies cookies delivered by the client @@ -20,6 +21,20 @@ def process(cookies = nil, user_agent = nil, id = nil, otp = nil) if tgt.nil? @listener.user_not_logged_in else + authenticator = tgt.user.two_factor_authenticators.where(id: id).first + validation_result = validate_one_time_password(tgt.user, otp, authenticator) + if validation_result.success? + tgt.user.two_factor_authenticators.where(active: true).delete_all + authenticator.active = true + authenticator.save! + @listener.two_factor_authenticator_activated + else + if validation_result.error_code == 'INVALID_OTP' + @listener.invalid_one_time_password + else + @listener.invalid_two_factor_authenticator + end + end end end end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 4bbcdbef..ce460283 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -17,7 +17,8 @@ class << self lifetime_consumed: 86400 }, two_factor_authenticator: { - lifetime_inactive: 300 + lifetime_inactive: 300, + drift: 30 } } diff --git a/spec/processor/two_factor_authenticator_activator_spec.rb b/spec/processor/two_factor_authenticator_activator_spec.rb index 6552bb1f..6f079620 100644 --- a/spec/processor/two_factor_authenticator_activator_spec.rb +++ b/spec/processor/two_factor_authenticator_activator_spec.rb @@ -10,6 +10,8 @@ listener.stub(:user_not_logged_in) listener.stub(:two_factor_authenticator_activated) listener.stub(:invalid_two_factor_authenticator) + listener.stub(:invalid_one_time_password) + listener.stub(:two_factor_authenticator_activated) end context 'with an existing ticket-granting ticket' do @@ -17,6 +19,94 @@ let(:user) { ticket_granting_ticket.user } let(:tgt) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } + let(:id) { two_factor_authenticator.id } + let(:otp) { '123456' } + + context 'with an invalid authenticator' do + context 'with an expired authenticator' do + let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive, user: user } + + before(:each) do + two_factor_authenticator.created_at = 10.hours.ago + two_factor_authenticator.save! + end + + it 'calls the `#invalid_two_factor_authenticator` method an the listener' do + listener.should_receive(:invalid_two_factor_authenticator).with(no_args) + processor.process(cookies, user_agent, id, otp) + end + end + + context 'with a authenticator of another user' do + let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive } + + before(:each) do + two_factor_authenticator.created_at = 10.hours.ago + two_factor_authenticator.save! + end + + it 'calls the `#invalid_two_factor_authenticator` method an the listener' do + listener.should_receive(:invalid_two_factor_authenticator).with(no_args) + processor.process(cookies, user_agent, id, otp) + end + end + end + + context 'with a valid authenticator' do + let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive, user: user } + + context 'with a valid OTP' do + before(:each) do + ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(true) + end + + it 'calls the `#two_factor_authenticator_activated` method an the listener' do + listener.should_receive(:two_factor_authenticator_activated).with(no_args) + processor.process(cookies, user_agent, id, otp) + end + + it 'does activate the authenticator' do + processor.process(cookies, user_agent, id, otp) + two_factor_authenticator.reload + two_factor_authenticator.should be_active + end + + context 'when another two-factor authenticator was active' do + let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } + + it 'does activate the authenticator' do + processor.process(cookies, user_agent, id, otp) + two_factor_authenticator.reload + two_factor_authenticator.should be_active + end + + it 'does delete the other authenticator' do + processor.process(cookies, user_agent, id, otp) + lambda do + other_two_factor_authenticator.reload + end.should raise_error(ActiveRecord::RecordNotFound) + end + end + + end + + context 'with an invalid OTP' do + before(:each) do + ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(false) + end + + it 'calls the `#invalid_one_time_password` method an the listener' do + listener.should_receive(:invalid_one_time_password).with(no_args) + processor.process(cookies, user_agent, id, otp) + end + + it 'does not activate the authenticator' do + processor.process(cookies, user_agent, id, otp) + two_factor_authenticator.reload + two_factor_authenticator.should_not be_active + end + end + end end context 'with an invalid ticket-granting ticket' do From 42e4764e6d28fa581ffee540f6a12ec27ef037bf Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 16:21:40 +0100 Subject: [PATCH 273/350] Add flag for pendig authentication --- ...uthentication_to_ticket_granting_tickets.rb | 5 +++++ db/schema.rb | 11 ++++++----- .../helper/ticket_granting_tickets.rb | 5 +++-- .../model/ticket_granting_ticket.rb | 2 +- lib/casino_core/model/user.rb | 4 ++++ .../processor/login_credential_acceptor.rb | 18 +++++++++++------- .../login_credential_acceptor_spec.rb | 10 ++++++++++ .../login_credential_requestor_spec.rb | 9 +++++++++ .../ticket_granting_ticket_factory.rb | 4 ++++ 9 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20130203155008_add_awaiting_two_factor_authentication_to_ticket_granting_tickets.rb diff --git a/db/migrate/20130203155008_add_awaiting_two_factor_authentication_to_ticket_granting_tickets.rb b/db/migrate/20130203155008_add_awaiting_two_factor_authentication_to_ticket_granting_tickets.rb new file mode 100644 index 00000000..ebec4a55 --- /dev/null +++ b/db/migrate/20130203155008_add_awaiting_two_factor_authentication_to_ticket_granting_tickets.rb @@ -0,0 +1,5 @@ +class AddAwaitingTwoFactorAuthenticationToTicketGrantingTickets < ActiveRecord::Migration + def change + add_column :ticket_granting_tickets, :awaiting_two_factor_authentication, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 484c4864..f16f8a58 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130203101351) do +ActiveRecord::Schema.define(:version => 20130203155008) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -73,11 +73,12 @@ add_index "service_tickets", ["ticket_granting_ticket_id"], :name => "index_service_tickets_on_ticket_granting_ticket_id" create_table "ticket_granting_tickets", :force => true do |t| - t.string "ticket", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.string "ticket", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "user_agent" - t.integer "user_id", :null => false + t.integer "user_id", :null => false + t.boolean "awaiting_two_factor_authentication", :default => false, :null => false end add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index a8b5c280..3c8d370b 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -7,9 +7,9 @@ module TicketGrantingTickets include CASinoCore::Helper::Browser include CASinoCore::Helper::Logger - def find_valid_ticket_granting_ticket(tgt, user_agent) + def find_valid_ticket_granting_ticket(tgt, user_agent, ignore_two_factor = false) ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first - unless ticket_granting_ticket.nil? + unless ticket_granting_ticket.nil? || (!ignore_two_factor && ticket_granting_ticket.awaiting_two_factor_authentication?) if same_browser?(ticket_granting_ticket.user_agent, user_agent) ticket_granting_ticket.user_agent = user_agent ticket_granting_ticket.touch @@ -27,6 +27,7 @@ def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) user = load_or_initialize_user(authentication_result[:authenticator], user_data[:username], user_data[:extra_attributes]) user.ticket_granting_tickets.create!({ ticket: random_ticket_string('TGC'), + awaiting_two_factor_authentication: !user.active_two_factor_authenticator.nil?, user_agent: user_agent }) end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 9abe7d87..aef752e7 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -1,7 +1,7 @@ require 'casino_core/model' class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base - attr_accessible :ticket, :user_agent + attr_accessible :ticket, :user_agent, :awaiting_two_factor_authentication validates :ticket, uniqueness: true belongs_to :user diff --git a/lib/casino_core/model/user.rb b/lib/casino_core/model/user.rb index 4842d6aa..d71049ef 100644 --- a/lib/casino_core/model/user.rb +++ b/lib/casino_core/model/user.rb @@ -6,4 +6,8 @@ class CASinoCore::Model::User < ActiveRecord::Base has_many :ticket_granting_tickets has_many :two_factor_authenticators + + def active_two_factor_authenticator + self.two_factor_authenticators.where(active: true).first + end end diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 3f0df8bb..4fc19ae9 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -42,14 +42,18 @@ def authenticate_user end def user_logged_in(authentication_result) - begin - ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, @user_agent) - url = unless @params[:service].nil? - acquire_service_ticket(ticket_granting_ticket, @params[:service], true).service_with_ticket_url + ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, @user_agent) + if ticket_granting_ticket.awaiting_two_factor_authentication? + @listener.two_factor_authentication_pending(ticket_granting_ticket.ticket) + else + begin + url = unless @params[:service].nil? + acquire_service_ticket(ticket_granting_ticket, @params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(url, ticket_granting_ticket.ticket) + rescue ServiceNotAllowedError => e + @listener.service_not_allowed(clean_service_url @params[:service]) end - @listener.user_logged_in(url, ticket_granting_ticket.ticket) - rescue ServiceNotAllowedError => e - @listener.service_not_allowed(clean_service_url @params[:service]) end end end diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index bdceb8f4..28dc920c 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -41,6 +41,16 @@ listener.stub(:user_logged_in) end + context 'with two-factor authentication enabled' do + let(:user) { CASinoCore::Model::User.create! username: username, authenticator: authenticator } + let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } + + it 'calls the `#two_factor_authentication_pending` method on the listener' do + listener.should_receive(:two_factor_authentication_pending).with(/^TGC\-/) + processor.process(login_data) + end + end + context 'with a not allowed service' do before(:each) do FactoryGirl.create :service_rule, :regex, url: '^https://.*' diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 834318e2..aa2ef6e0 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -55,6 +55,15 @@ listener.stub(:user_logged_in) end + context 'when two-factor authentication is pending' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication } + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(nil, cookies, user_agent) + end + end + context 'with a service' do let(:service) { 'http://example.com/' } let(:params) { { service: service } } diff --git a/spec/support/factories/ticket_granting_ticket_factory.rb b/spec/support/factories/ticket_granting_ticket_factory.rb index 6b4b2960..5270b5ec 100644 --- a/spec/support/factories/ticket_granting_ticket_factory.rb +++ b/spec/support/factories/ticket_granting_ticket_factory.rb @@ -7,5 +7,9 @@ "TGC-ticket#{n}" end user_agent 'TestBrowser 1.0' + + trait :awaiting_two_factor_authentication do + awaiting_two_factor_authentication true + end end end From b2459ef772ae233678eef39b6aa44a3edc3a7973 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 17:10:47 +0100 Subject: [PATCH 274/350] Allow activating ticket-granting tickets with pending two-factor --- .../helper/two_factor_authenticators.rb | 4 +- lib/casino_core/processor.rb | 1 + .../processor/login_credential_acceptor.rb | 1 + .../second_factor_authentication_acceptor.rb | 42 +++++++++++ .../two_factor_authenticator_activator.rb | 2 +- ...cond_factor_authenticaton_acceptor_spec.rb | 72 +++++++++++++++++++ 6 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 lib/casino_core/processor/second_factor_authentication_acceptor.rb create mode 100644 spec/processor/second_factor_authenticaton_acceptor_spec.rb diff --git a/lib/casino_core/helper/two_factor_authenticators.rb b/lib/casino_core/helper/two_factor_authenticators.rb index 63ba9e9e..8874f12d 100644 --- a/lib/casino_core/helper/two_factor_authenticators.rb +++ b/lib/casino_core/helper/two_factor_authenticators.rb @@ -5,8 +5,8 @@ module Helper module TwoFactorAuthenticators class ValidationResult < CASinoCore::Model::ValidationResult; end - def validate_one_time_password(user, otp, authenticator = nil) - if authenticator.nil? || authenticator.expired? || authenticator.user_id != user.id + def validate_one_time_password(otp, authenticator) + if authenticator.nil? || authenticator.expired? ValidationResult.new 'INVALID_AUTHENTICATOR', 'Authenticator does not exist or expired', :warn else totp = ROTP::TOTP.new(authenticator.secret) diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 4278b317..8db9f6a4 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -8,6 +8,7 @@ class Processor autoload :Logout, 'casino_core/processor/logout.rb' autoload :ProxyTicketProvider, 'casino_core/processor/proxy_ticket_provider.rb' autoload :ProxyTicketValidator, 'casino_core/processor/proxy_ticket_validator.rb' + autoload :SecondFactorAuthenticationAcceptor, 'casino_core/processor/second_factor_authentication_acceptor.rb' autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb' autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index 4fc19ae9..cf60193b 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -18,6 +18,7 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # * `#invalid_login_ticket` and `#invalid_login_credentials`: The first argument is a LoginTicket. # See {CASinoCore::Processor::LoginCredentialRequestor} for details. # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. + # * `#two_factor_authentication_pending`: The user should be asked to enter his OTP. The first argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". The ticket-granting ticket is not active yet. Use SecondFactorAuthenticatonAcceptor to activate it. # # @param [Hash] params parameters supplied by user # @param [String] user_agent user-agent delivered by the client diff --git a/lib/casino_core/processor/second_factor_authentication_acceptor.rb b/lib/casino_core/processor/second_factor_authentication_acceptor.rb new file mode 100644 index 00000000..cccee4a3 --- /dev/null +++ b/lib/casino_core/processor/second_factor_authentication_acceptor.rb @@ -0,0 +1,42 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The SecondFactorAuthenticationAcceptor processor can be used to activate a previously generated ticket-granting ticket with pending two-factor authentication. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::SecondFactorAuthenticationAcceptor < CASinoCore::Processor + include CASinoCore::Helper::ServiceTickets + include CASinoCore::Helper::TicketGrantingTickets + include CASinoCore::Helper::TwoFactorAuthenticators + + # The method will call one of the following methods on the listener: + # * `#user_not_logged_in`: The user should be redirected to /login. + # * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to. + # The second argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". + # * `#invalid_one_time_password`: The user should be asked for a new OTP. + # + # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :service. + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client + def process(params = nil, cookies = nil, user_agent = nil) + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent, true) + if tgt.nil? + @listener.user_not_logged_in + else + validation_result = validate_one_time_password(params[:otp], tgt.user.active_two_factor_authenticator) + if validation_result.success? + tgt.awaiting_two_factor_authentication = false + tgt.save! + url = unless params[:service].nil? + acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(url, tgt.ticket) + else + @listener.invalid_one_time_password + end + end + end +end diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb index 58501645..3e0a17b4 100644 --- a/lib/casino_core/processor/two_factor_authenticator_activator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -22,7 +22,7 @@ def process(cookies = nil, user_agent = nil, id = nil, otp = nil) @listener.user_not_logged_in else authenticator = tgt.user.two_factor_authenticators.where(id: id).first - validation_result = validate_one_time_password(tgt.user, otp, authenticator) + validation_result = validate_one_time_password(otp, authenticator) if validation_result.success? tgt.user.two_factor_authenticators.where(active: true).delete_all authenticator.active = true diff --git a/spec/processor/second_factor_authenticaton_acceptor_spec.rb b/spec/processor/second_factor_authenticaton_acceptor_spec.rb new file mode 100644 index 00000000..346ac0d0 --- /dev/null +++ b/spec/processor/second_factor_authenticaton_acceptor_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe CASinoCore::Processor::SecondFactorAuthenticationAcceptor do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:invalid_one_time_password) + listener.stub(:user_logged_in) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication } + let(:user) { ticket_granting_ticket.user } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + let(:otp) { '123456' } + let(:service) { 'http://www.example.com/testing' } + let(:params) { { otp: otp, service: service }} + + context 'with an active authenticator' do + let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } + + context 'with a valid OTP' do + before(:each) do + ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(true) + end + + it 'calls the `#user_logged_in` method an the listener' do + listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) + processor.process(params, cookies, user_agent) + end + + it 'does activate the ticket-granting ticket' do + processor.process(params, cookies, user_agent) + ticket_granting_ticket.reload + ticket_granting_ticket.should_not be_awaiting_two_factor_authentication + end + end + + context 'with an invalid OTP' do + before(:each) do + ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(false) + end + + it 'calls the `#invalid_one_time_password` method an the listener' do + listener.should_receive(:invalid_one_time_password).with(no_args) + processor.process(params, cookies, user_agent) + end + + it 'does not activate the ticket-granting ticket' do + processor.process(params, cookies, user_agent) + ticket_granting_ticket.reload + ticket_granting_ticket.should be_awaiting_two_factor_authentication + end + end + end + end + + context 'with an invalid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process({}, cookies, user_agent) + end + end + end +end \ No newline at end of file From 2364ad7aa2e4d3b6baa91a1307f05dc0dfa1315b Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 17:13:51 +0100 Subject: [PATCH 275/350] Refactoring --- .../two_factor_authenticator_activator.rb | 10 +++++----- ...two_factor_authenticator_activator_spec.rb | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb index 3e0a17b4..70dce09b 100644 --- a/lib/casino_core/processor/two_factor_authenticator_activator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -11,18 +11,18 @@ class CASinoCore::Processor::TwoFactorAuthenticatorActivator < CASinoCore::Proce include CASinoCore::Helper::TwoFactorAuthenticators # This method will call `#user_not_logged_in` on the listener. + # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :id. # @param [Hash] cookies cookies delivered by the client # @param [String] user_agent user-agent delivered by the client - # @param [String] id id of the two-factor authenticator - # @param [String] otp one time password given by the user - def process(cookies = nil, user_agent = nil, id = nil, otp = nil) + def process(params = nil, cookies = nil, user_agent = nil) cookies ||= {} + params ||= {} tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) if tgt.nil? @listener.user_not_logged_in else - authenticator = tgt.user.two_factor_authenticators.where(id: id).first - validation_result = validate_one_time_password(otp, authenticator) + authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first + validation_result = validate_one_time_password(params[:otp], authenticator) if validation_result.success? tgt.user.two_factor_authenticators.where(active: true).delete_all authenticator.active = true diff --git a/spec/processor/two_factor_authenticator_activator_spec.rb b/spec/processor/two_factor_authenticator_activator_spec.rb index 6f079620..753fc483 100644 --- a/spec/processor/two_factor_authenticator_activator_spec.rb +++ b/spec/processor/two_factor_authenticator_activator_spec.rb @@ -21,6 +21,7 @@ let(:user_agent) { ticket_granting_ticket.user_agent } let(:id) { two_factor_authenticator.id } let(:otp) { '123456' } + let(:params) { { otp: otp, id: id } } context 'with an invalid authenticator' do context 'with an expired authenticator' do @@ -33,7 +34,7 @@ it 'calls the `#invalid_two_factor_authenticator` method an the listener' do listener.should_receive(:invalid_two_factor_authenticator).with(no_args) - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) end end @@ -47,7 +48,7 @@ it 'calls the `#invalid_two_factor_authenticator` method an the listener' do listener.should_receive(:invalid_two_factor_authenticator).with(no_args) - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) end end end @@ -62,11 +63,11 @@ it 'calls the `#two_factor_authenticator_activated` method an the listener' do listener.should_receive(:two_factor_authenticator_activated).with(no_args) - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) end it 'does activate the authenticator' do - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) two_factor_authenticator.reload two_factor_authenticator.should be_active end @@ -75,13 +76,13 @@ let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } it 'does activate the authenticator' do - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) two_factor_authenticator.reload two_factor_authenticator.should be_active end it 'does delete the other authenticator' do - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) lambda do other_two_factor_authenticator.reload end.should raise_error(ActiveRecord::RecordNotFound) @@ -97,11 +98,11 @@ it 'calls the `#invalid_one_time_password` method an the listener' do listener.should_receive(:invalid_one_time_password).with(no_args) - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) end it 'does not activate the authenticator' do - processor.process(cookies, user_agent, id, otp) + processor.process(params, cookies, user_agent) two_factor_authenticator.reload two_factor_authenticator.should_not be_active end @@ -114,7 +115,7 @@ let(:user_agent) { 'TestBrowser 1.0' } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(cookies, user_agent) + processor.process(nil, cookies, user_agent) end end end From 03e63e9f48ca5ce396020a4abdb74f2a4f606fd8 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 17:18:39 +0100 Subject: [PATCH 276/350] Removed unused parameter --- spec/support/factories/two_factor_authenticator_factory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/factories/two_factor_authenticator_factory.rb b/spec/support/factories/two_factor_authenticator_factory.rb index e3bb9169..e874a191 100644 --- a/spec/support/factories/two_factor_authenticator_factory.rb +++ b/spec/support/factories/two_factor_authenticator_factory.rb @@ -4,7 +4,7 @@ FactoryGirl.define do factory :two_factor_authenticator, class: CASinoCore::Model::TwoFactorAuthenticator do user - secret do |a| + secret do ROTP::Base32.random_base32 end active true From c08bfc123820c09725b1b73af764079d5afd9426 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Feb 2013 17:39:04 +0100 Subject: [PATCH 277/350] Documentation --- UPGRADE.md | 18 +++++++++++++++--- .../two_factor_authenticator_activator.rb | 7 ++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index c65c8eec..ca6398d5 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,18 @@ Here is a list of backward-incompatible changes that were introduced. +## 1.x.y + +This release adds support for two-factor authentication using a [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) (time-based one-time password) which can be generated with applications like [Google Authenticator](http://support.google.com/a/bin/answer.py?hl=en&answer=1037451) (iPhone, Android, BlackBerry) or gadgets such as the [YubiKey](http://www.yubico.com/products/yubikey-hardware/yubikey/). + +If you would like to support two-factor authentication in your web application, please have a look at the corresponding processors: `SecondFactorAuthenticationAcceptor`, `TwoFactorAuthenticatorActivator`, `TwoFactorAuthenticatorOverview`, `TwoFactorAuthenticatorRegistrator` + +New callbacks: + +* `LoginCredentialAcceptor`: calls `#two_factor_authentication_pending` on the listener, when two-factor authentication is enabled for this user. + +If you don't want to support two-factor authentication, nothing has to be changed. + ## 1.2.0 API changes: @@ -12,9 +24,9 @@ API changes: API changes: -* `login_credential_acceptor`: The parameters of `#process` changed from `params, cookies, user_agent` to just `params, user_agent` +* `LoginCredentialAcceptor`: The parameters of `#process` changed from `params, cookies, user_agent` to just `params, user_agent` New callbacks: -* `login_credential_requestor` and `login_credential_acceptor` call `#service_not_allowed` on the listener, when a service is not in the service whitelist. -* `api/service_ticket_provider` calls `#service_not_allowed_via_api` on the listener, when a service is not in the service whitelist. +* `LoginCredentialRequestor` and `LoginCredentialAcceptor` call `#service_not_allowed` on the listener, when a service is not in the service whitelist. +* `API::ServiceTicketProvider` calls `#service_not_allowed_via_api` on the listener, when a service is not in the service whitelist. diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb index 70dce09b..f2834d2e 100644 --- a/lib/casino_core/processor/two_factor_authenticator_activator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -10,7 +10,12 @@ class CASinoCore::Processor::TwoFactorAuthenticatorActivator < CASinoCore::Proce include CASinoCore::Helper::TicketGrantingTickets include CASinoCore::Helper::TwoFactorAuthenticators - # This method will call `#user_not_logged_in` on the listener. + # The method will call one of the following methods on the listener: + # * `#user_not_logged_in`: The user is not logged in and should be redirected to /login. + # * `#two_factor_authenticator_activated`: The two-factor authenticator was successfully activated. + # * `#invalid_two_factor_authenticator`: The two-factor authenticator is not valid. + # * `#invalid_one_time_password`: The user should be asked for a new OTP. + # # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :id. # @param [Hash] cookies cookies delivered by the client # @param [String] user_agent user-agent delivered by the client From 9b02098e2f37d0b1288b6ae643c1267a8aa927d9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 4 Feb 2013 00:14:40 +0100 Subject: [PATCH 278/350] Don't store pending tgt in a cookie --- .../processor/login_credential_acceptor.rb | 2 +- .../second_factor_authentication_acceptor.rb | 5 ++--- .../second_factor_authenticaton_acceptor_spec.rb | 13 ++++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index cf60193b..df9da7b0 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -18,7 +18,7 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # * `#invalid_login_ticket` and `#invalid_login_credentials`: The first argument is a LoginTicket. # See {CASinoCore::Processor::LoginCredentialRequestor} for details. # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. - # * `#two_factor_authentication_pending`: The user should be asked to enter his OTP. The first argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". The ticket-granting ticket is not active yet. Use SecondFactorAuthenticatonAcceptor to activate it. + # * `#two_factor_authentication_pending`: The user should be asked to enter his OTP. The first argument (String) is the ticket-granting ticket. The ticket-granting ticket is not active yet. Use SecondFactorAuthenticatonAcceptor to activate it. # # @param [Hash] params parameters supplied by user # @param [String] user_agent user-agent delivered by the client diff --git a/lib/casino_core/processor/second_factor_authentication_acceptor.rb b/lib/casino_core/processor/second_factor_authentication_acceptor.rb index cccee4a3..84597d47 100644 --- a/lib/casino_core/processor/second_factor_authentication_acceptor.rb +++ b/lib/casino_core/processor/second_factor_authentication_acceptor.rb @@ -18,11 +18,10 @@ class CASinoCore::Processor::SecondFactorAuthenticationAcceptor < CASinoCore::Pr # * `#invalid_one_time_password`: The user should be asked for a new OTP. # # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :service. - # @param [Hash] cookies cookies supplied by user # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user_agent = nil) cookies ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent, true) + tgt = find_valid_ticket_granting_ticket(params[:tgt], user_agent, true) if tgt.nil? @listener.user_not_logged_in else diff --git a/spec/processor/second_factor_authenticaton_acceptor_spec.rb b/spec/processor/second_factor_authenticaton_acceptor_spec.rb index 346ac0d0..867f3413 100644 --- a/spec/processor/second_factor_authenticaton_acceptor_spec.rb +++ b/spec/processor/second_factor_authenticaton_acceptor_spec.rb @@ -4,7 +4,6 @@ describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } before(:each) do listener.stub(:user_not_logged_in) @@ -19,7 +18,7 @@ let(:user_agent) { ticket_granting_ticket.user_agent } let(:otp) { '123456' } let(:service) { 'http://www.example.com/testing' } - let(:params) { { otp: otp, service: service }} + let(:params) { { tgt: tgt, otp: otp, service: service }} context 'with an active authenticator' do let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } @@ -31,11 +30,11 @@ it 'calls the `#user_logged_in` method an the listener' do listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) - processor.process(params, cookies, user_agent) + processor.process(params, user_agent) end it 'does activate the ticket-granting ticket' do - processor.process(params, cookies, user_agent) + processor.process(params, user_agent) ticket_granting_ticket.reload ticket_granting_ticket.should_not be_awaiting_two_factor_authentication end @@ -48,11 +47,11 @@ it 'calls the `#invalid_one_time_password` method an the listener' do listener.should_receive(:invalid_one_time_password).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user_agent) end it 'does not activate the ticket-granting ticket' do - processor.process(params, cookies, user_agent) + processor.process(params, user_agent) ticket_granting_ticket.reload ticket_granting_ticket.should be_awaiting_two_factor_authentication end @@ -65,7 +64,7 @@ let(:user_agent) { 'TestBrowser 1.0' } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process({}, cookies, user_agent) + processor.process({tgt: tgt}, user_agent) end end end From 8453bb5cb1422aba79243ac3d36ca0f574d0fab0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 21:51:52 +0100 Subject: [PATCH 279/350] Processor to disable two-factor authentication --- lib/casino_core/processor.rb | 1 + .../two_factor_authenticator_destroyer.rb | 38 ++++++++++ ...two_factor_authenticator_destroyer_spec.rb | 71 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 lib/casino_core/processor/two_factor_authenticator_destroyer.rb create mode 100644 spec/processor/two_factor_authenticator_destroyer_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index 8db9f6a4..d87d2a03 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -13,6 +13,7 @@ class Processor autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb' autoload :SessionOverview, 'casino_core/processor/session_overview.rb' autoload :TwoFactorAuthenticatorActivator, 'casino_core/processor/two_factor_authenticator_activator.rb' + autoload :TwoFactorAuthenticatorDestroyer, 'casino_core/processor/two_factor_authenticator_destroyer.rb' autoload :TwoFactorAuthenticatorOverview, 'casino_core/processor/two_factor_authenticator_overview.rb' autoload :TwoFactorAuthenticatorRegistrator, 'casino_core/processor/two_factor_authenticator_registrator.rb' diff --git a/lib/casino_core/processor/two_factor_authenticator_destroyer.rb b/lib/casino_core/processor/two_factor_authenticator_destroyer.rb new file mode 100644 index 00000000..d45ee8c7 --- /dev/null +++ b/lib/casino_core/processor/two_factor_authenticator_destroyer.rb @@ -0,0 +1,38 @@ +require 'rotp' +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The TwoFactorAuthenticatorActivator processor can be used to deactivate a previously activated two-factor authenticator. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::TwoFactorAuthenticatorDestroyer < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + include CASinoCore::Helper::TwoFactorAuthenticators + + # The method will call one of the following methods on the listener: + # * `#user_not_logged_in`: The user is not logged in and should be redirected to /login. + # * `#two_factor_authenticator_destroyed`: The two-factor authenticator was successfully destroyed. + # * `#invalid_two_factor_authenticator`: The two-factor authenticator is not valid. + # + # @param [Hash] params parameters supplied by user. The processor will look for key :id. + # @param [Hash] cookies cookies delivered by the client + # @param [String] user_agent user-agent delivered by the client + def process(params = nil, cookies = nil, user_agent = nil) + cookies ||= {} + params ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + if tgt.nil? + @listener.user_not_logged_in + else + authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first + if authenticator + authenticator.destroy + @listener.two_factor_authenticator_destroyed + else + @listener.invalid_two_factor_authenticator + end + end + end +end diff --git a/spec/processor/two_factor_authenticator_destroyer_spec.rb b/spec/processor/two_factor_authenticator_destroyer_spec.rb new file mode 100644 index 00000000..c9e3a15f --- /dev/null +++ b/spec/processor/two_factor_authenticator_destroyer_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe CASinoCore::Processor::TwoFactorAuthenticatorDestroyer do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + + before(:each) do + listener.stub(:user_not_logged_in) + listener.stub(:two_factor_authenticator_destroyed) + listener.stub(:invalid_two_factor_authenticator) + end + + context 'with an existing ticket-granting ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { ticket_granting_ticket.user } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + let(:params) { { id: two_factor_authenticator.id } } + + context 'with a valid two-factor authenticator' do + let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } + + it 'calls the #two_factor_authenticator_destroyed method on the listener' do + listener.should_receive(:two_factor_authenticator_destroyed).with(no_args) + processor.process(params, cookies, user_agent) + end + + it 'deletes the two-factor authenticator' do + processor.process(params, cookies, user_agent) + lambda do + two_factor_authenticator.reload + end.should raise_error(ActiveRecord::RecordNotFound) + end + + it 'does not delete other two-factor authenticators' do + other = FactoryGirl.create :two_factor_authenticator + lambda do + processor.process(params, cookies, user_agent) + end.should change(CASinoCore::Model::TwoFactorAuthenticator, :count).by(-1) + end + end + + context 'with a two-factor authenticator of another user' do + let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator } + + it 'calls the #invalid_two_factor_authenticator method on the listener' do + listener.should_receive(:invalid_two_factor_authenticator).with(no_args) + processor.process(params, cookies, user_agent) + end + + it 'does not delete two-factor authenticators' do + lambda do + processor.process(params, cookies, user_agent) + end.should_not change(CASinoCore::Model::TwoFactorAuthenticator, :count) + end + end + end + + context 'with an invalid ticket-granting ticket' do + let(:params) { {} } + let(:tgt) { 'TGT-lalala' } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(no_args) + processor.process(params, cookies, user_agent) + end + end + end +end \ No newline at end of file From 7956f542bed91942eb1c05db64d8e264d3536f14 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 21:53:11 +0100 Subject: [PATCH 280/350] Check service whitelist --- .../second_factor_authentication_acceptor.rb | 10 +++++++--- .../second_factor_authenticaton_acceptor_spec.rb | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/casino_core/processor/second_factor_authentication_acceptor.rb b/lib/casino_core/processor/second_factor_authentication_acceptor.rb index 84597d47..8c894946 100644 --- a/lib/casino_core/processor/second_factor_authentication_acceptor.rb +++ b/lib/casino_core/processor/second_factor_authentication_acceptor.rb @@ -29,10 +29,14 @@ def process(params = nil, user_agent = nil) if validation_result.success? tgt.awaiting_two_factor_authentication = false tgt.save! - url = unless params[:service].nil? - acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url + begin + url = unless params[:service].blank? + acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url + end + @listener.user_logged_in(url, tgt.ticket) + rescue ServiceNotAllowedError => e + @listener.service_not_allowed(clean_service_url params[:service]) end - @listener.user_logged_in(url, tgt.ticket) else @listener.invalid_one_time_password end diff --git a/spec/processor/second_factor_authenticaton_acceptor_spec.rb b/spec/processor/second_factor_authenticaton_acceptor_spec.rb index 867f3413..ea0fd6f7 100644 --- a/spec/processor/second_factor_authenticaton_acceptor_spec.rb +++ b/spec/processor/second_factor_authenticaton_acceptor_spec.rb @@ -38,6 +38,18 @@ ticket_granting_ticket.reload ticket_granting_ticket.should_not be_awaiting_two_factor_authentication end + + context 'with a not allowed service' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end + let(:service) { 'http://www.example.org/' } + + it 'calls the #service_not_allowed method on the listener' do + listener.should_receive(:service_not_allowed).with(service) + processor.process(params.merge(service: service), user_agent) + end + end end context 'with an invalid OTP' do From fda205de50f1092d2c9210bb12906c028836f1b4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 21:53:36 +0100 Subject: [PATCH 281/350] Don't include pending tgts in session overview --- lib/casino_core/processor/session_overview.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/processor/session_overview.rb b/lib/casino_core/processor/session_overview.rb index da114869..bc2da531 100644 --- a/lib/casino_core/processor/session_overview.rb +++ b/lib/casino_core/processor/session_overview.rb @@ -18,7 +18,7 @@ def process(cookies = nil, user_agent = nil) if tgt.nil? @listener.user_not_logged_in else - ticket_granting_tickets = tgt.user.ticket_granting_tickets.order('updated_at DESC') + ticket_granting_tickets = tgt.user.ticket_granting_tickets.where(awaiting_two_factor_authentication: false).order('updated_at DESC') @listener.ticket_granting_tickets_found(ticket_granting_tickets) end end From 0b6e6c457d20bdb5b23702bd5acc61efacc79319 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 21:54:07 +0100 Subject: [PATCH 282/350] Include authenticator in callback --- .../processor/two_factor_authenticator_activator.rb | 3 ++- spec/processor/two_factor_authenticator_activator_spec.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/processor/two_factor_authenticator_activator.rb b/lib/casino_core/processor/two_factor_authenticator_activator.rb index f2834d2e..17c3df76 100644 --- a/lib/casino_core/processor/two_factor_authenticator_activator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_activator.rb @@ -1,3 +1,4 @@ +require 'rotp' require 'casino_core/processor' require 'casino_core/helper' require 'casino_core/model' @@ -35,7 +36,7 @@ def process(params = nil, cookies = nil, user_agent = nil) @listener.two_factor_authenticator_activated else if validation_result.error_code == 'INVALID_OTP' - @listener.invalid_one_time_password + @listener.invalid_one_time_password(authenticator) else @listener.invalid_two_factor_authenticator end diff --git a/spec/processor/two_factor_authenticator_activator_spec.rb b/spec/processor/two_factor_authenticator_activator_spec.rb index 753fc483..95225507 100644 --- a/spec/processor/two_factor_authenticator_activator_spec.rb +++ b/spec/processor/two_factor_authenticator_activator_spec.rb @@ -97,7 +97,7 @@ end it 'calls the `#invalid_one_time_password` method an the listener' do - listener.should_receive(:invalid_one_time_password).with(no_args) + listener.should_receive(:invalid_one_time_password).with(two_factor_authenticator) processor.process(params, cookies, user_agent) end From c8c5b185cadf7cee1a0922e89be14f80582e0401 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 21:54:18 +0100 Subject: [PATCH 283/350] Bugfixing --- lib/casino_core/processor/login_credential_acceptor.rb | 2 +- .../processor/second_factor_authentication_acceptor.rb | 1 + .../processor/two_factor_authenticator_registrator.rb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index df9da7b0..a84d1e94 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -48,7 +48,7 @@ def user_logged_in(authentication_result) @listener.two_factor_authentication_pending(ticket_granting_ticket.ticket) else begin - url = unless @params[:service].nil? + url = unless @params[:service].blank? acquire_service_ticket(ticket_granting_ticket, @params[:service], true).service_with_ticket_url end @listener.user_logged_in(url, ticket_granting_ticket.ticket) diff --git a/lib/casino_core/processor/second_factor_authentication_acceptor.rb b/lib/casino_core/processor/second_factor_authentication_acceptor.rb index 8c894946..fc22fd7a 100644 --- a/lib/casino_core/processor/second_factor_authentication_acceptor.rb +++ b/lib/casino_core/processor/second_factor_authentication_acceptor.rb @@ -1,3 +1,4 @@ +require 'rotp' require 'casino_core/processor' require 'casino_core/helper' require 'casino_core/model' diff --git a/lib/casino_core/processor/two_factor_authenticator_registrator.rb b/lib/casino_core/processor/two_factor_authenticator_registrator.rb index d3855b71..ee2514db 100644 --- a/lib/casino_core/processor/two_factor_authenticator_registrator.rb +++ b/lib/casino_core/processor/two_factor_authenticator_registrator.rb @@ -1,3 +1,4 @@ +require 'rotp' require 'casino_core/processor' require 'casino_core/helper' require 'casino_core/model' From 94f38982baae293052ccfe13606e239c603aab44 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 22:01:14 +0100 Subject: [PATCH 284/350] Fixed documentation --- UPGRADE.md | 2 +- lib/casino_core/processor/two_factor_authenticator_destroyer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index ca6398d5..a1d37755 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -6,7 +6,7 @@ Here is a list of backward-incompatible changes that were introduced. This release adds support for two-factor authentication using a [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) (time-based one-time password) which can be generated with applications like [Google Authenticator](http://support.google.com/a/bin/answer.py?hl=en&answer=1037451) (iPhone, Android, BlackBerry) or gadgets such as the [YubiKey](http://www.yubico.com/products/yubikey-hardware/yubikey/). -If you would like to support two-factor authentication in your web application, please have a look at the corresponding processors: `SecondFactorAuthenticationAcceptor`, `TwoFactorAuthenticatorActivator`, `TwoFactorAuthenticatorOverview`, `TwoFactorAuthenticatorRegistrator` +If you would like to support two-factor authentication in your web application, please have a look at the corresponding processors: `SecondFactorAuthenticationAcceptor`, `TwoFactorAuthenticatorActivator`, `TwoFactorAuthenticatorDestroyer`, `TwoFactorAuthenticatorOverview`, `TwoFactorAuthenticatorRegistrator` New callbacks: diff --git a/lib/casino_core/processor/two_factor_authenticator_destroyer.rb b/lib/casino_core/processor/two_factor_authenticator_destroyer.rb index d45ee8c7..40421933 100644 --- a/lib/casino_core/processor/two_factor_authenticator_destroyer.rb +++ b/lib/casino_core/processor/two_factor_authenticator_destroyer.rb @@ -3,7 +3,7 @@ require 'casino_core/helper' require 'casino_core/model' -# The TwoFactorAuthenticatorActivator processor can be used to deactivate a previously activated two-factor authenticator. +# The TwoFactorAuthenticatorDestroyer processor can be used to deactivate a previously activated two-factor authenticator. # # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. From 2537fe103f23454f3812bdf3cdbd135695f6b9b4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 8 Feb 2013 22:12:36 +0100 Subject: [PATCH 285/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 19c14b83..8e09918f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.2.0) + casino_core (1.3.0) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index e313398a..f54e4ffb 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.2.0' + VERSION = '1.3.0' end From 2382e6dd6b2d9c925bc7547842ca6451597dd9b9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 10 Feb 2013 16:06:15 +0100 Subject: [PATCH 286/350] Add lifetime and cleanup for ticket-granting tickets --- lib/casino_core/model/ticket_granting_ticket.rb | 4 ++++ lib/casino_core/settings.rb | 5 ++++- lib/casino_core/tasks/cleanup.rake | 8 +++++++- spec/model/ticket_granting_ticket_spec.rb | 12 ++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index aef752e7..363b19fb 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -10,6 +10,10 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base before_destroy :destroy_service_tickets after_destroy :destroy_proxy_granting_tickets + def self.cleanup + self.destroy_all(['created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago]) + end + def browser_info unless self.user_agent.blank? user_agent = UserAgent.parse(self.user_agent) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index ce460283..5719d0f6 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,11 +3,14 @@ module CASinoCore class Settings class << self - attr_accessor :login_ticket, :service_ticket, :proxy_ticket, :two_factor_authenticator, :authenticators, :logger + attr_accessor :login_ticket, :ticket_granting_ticket, :service_ticket, :proxy_ticket, :two_factor_authenticator, :authenticators, :logger DEFAULT_SETTINGS = { login_ticket: { lifetime: 600 }, + ticket_granting_ticket: { + lifetime: 86400 + }, service_ticket: { lifetime_unconsumed: 300, lifetime_consumed: 86400 diff --git a/lib/casino_core/tasks/cleanup.rake b/lib/casino_core/tasks/cleanup.rake index 16576e3f..bbd12ea5 100644 --- a/lib/casino_core/tasks/cleanup.rake +++ b/lib/casino_core/tasks/cleanup.rake @@ -35,8 +35,14 @@ namespace :casino_core do puts "Deleted #{rows_affected} inactive two-factor authenticators." end + desc 'Remove expired ticket-granting tickets.' + task ticket_granting_tickets: 'casino_core:db:configure_connection' do + rows_affected = CASinoCore::Model::TicketGrantingTicket.cleanup.length + puts "Deleted #{rows_affected} ticket-granting tickets." + end + desc 'Perform all cleanup tasks.' - task all: [:service_tickets, :proxy_tickets, :login_tickets, :two_factor_authenticators] do + task all: [:ticket_granting_tickets, :service_tickets, :proxy_tickets, :login_tickets, :two_factor_authenticators] do end end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index fb451808..87cd10b8 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -80,4 +80,16 @@ end end end + describe '.cleanup' do + let!(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + + it 'deletes expired ticket-granting tickets' do + ticket_granting_ticket.created_at = 25.hours.ago + ticket_granting_ticket.save! + lambda do + described_class.cleanup + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket(ticket_granting_ticket.ticket).should be_false + end + end end From ecfa47c5104dc6a9d407a0dc98429014a13b27ee Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 10 Feb 2013 16:11:24 +0100 Subject: [PATCH 287/350] Check if ticket-granting ticket expired --- .../helper/ticket_granting_tickets.rb | 11 ++++++++-- .../model/ticket_granting_ticket.rb | 5 +++++ spec/model/ticket_granting_ticket_spec.rb | 20 +++++++++++++++++++ .../login_credential_requestor_spec.rb | 12 +++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index 3c8d370b..0632ca8b 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -9,8 +9,15 @@ module TicketGrantingTickets def find_valid_ticket_granting_ticket(tgt, user_agent, ignore_two_factor = false) ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first - unless ticket_granting_ticket.nil? || (!ignore_two_factor && ticket_granting_ticket.awaiting_two_factor_authentication?) - if same_browser?(ticket_granting_ticket.user_agent, user_agent) + unless ticket_granting_ticket.nil? + if ticket_granting_ticket.expired? + logger.info "Ticket-granting ticket expired (Created: #{ticket_granting_ticket.created_at})" + ticket_granting_ticket.destroy + nil + elsif !ignore_two_factor && ticket_granting_ticket.awaiting_two_factor_authentication? + logger.info 'Ticket-granting ticket is valid, but two-factor authentication is pending' + nil + elsif same_browser?(ticket_granting_ticket.user_agent, user_agent) ticket_granting_ticket.user_agent = user_agent ticket_granting_ticket.touch ticket_granting_ticket.save! diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 363b19fb..696310fd 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -33,6 +33,11 @@ def same_user?(other_ticket) end end + def expired? + lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime] + (Time.now - (self.created_at || Time.now)) > lifetime + end + private def destroy_service_tickets self.service_tickets.each do |service_ticket| diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 87cd10b8..06682db4 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -80,6 +80,26 @@ end end end + + describe '#expired?' do + context 'with an expired ticket' do + before(:each) do + ticket_granting_ticket.created_at = 25.hours.ago + ticket_granting_ticket.save! + end + + it 'returns true' do + ticket_granting_ticket.expired?.should == true + end + end + + context 'with an unexpired ticket' do + it 'returns false' do + ticket_granting_ticket.expired?.should == false + end + end + end + describe '.cleanup' do let!(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index aa2ef6e0..2efc1d2e 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -64,6 +64,18 @@ end end + context 'when ticket-granting ticket expired' do + before(:each) do + ticket_granting_ticket.created_at = 25.hours.ago + ticket_granting_ticket.save! + end + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in).with(kind_of(CASinoCore::Model::LoginTicket)) + processor.process(nil, cookies, user_agent) + end + end + context 'with a service' do let(:service) { 'http://example.com/' } let(:params) { { service: service } } From 877a8f5499d4c08a745b0261ef026463f4890e3f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 17 Feb 2013 12:24:45 +0100 Subject: [PATCH 288/350] Updated gems --- Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8e09918f..03846d80 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,15 +12,15 @@ PATH GEM remote: http://rubygems.org/ specs: - activemodel (3.2.11) - activesupport (= 3.2.11) + activemodel (3.2.12) + activesupport (= 3.2.12) builder (~> 3.0.0) - activerecord (3.2.11) - activemodel (= 3.2.11) - activesupport (= 3.2.11) + activerecord (3.2.12) + activemodel (= 3.2.12) + activesupport (= 3.2.12) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activesupport (3.2.11) + activesupport (3.2.12) i18n (~> 0.6) multi_json (~> 1.0) addressable (2.3.2) @@ -34,7 +34,7 @@ GEM faraday (0.8.5) multipart-post (~> 1.1) i18n (0.6.1) - multi_json (1.5.0) + multi_json (1.6.1) multipart-post (1.1.5) nokogiri (1.5.6) rake (10.0.3) @@ -58,7 +58,7 @@ GEM webmock (1.9.0) addressable (>= 2.2.7) crack (>= 0.1.7) - yard (0.8.3) + yard (0.8.4.1) PLATFORMS ruby From 0a4b32cd8f55b44092f3727bd7f8ae6321443e36 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 17 Feb 2013 12:25:51 +0100 Subject: [PATCH 289/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 03846d80..eb51faaf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.0) + casino_core (1.3.1) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index f54e4ffb..493d4340 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.0' + VERSION = '1.3.1' end From 344b80fdf0af227cc7d1bb2e514498127f817754 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 17 Feb 2013 13:01:47 +0100 Subject: [PATCH 290/350] Configurable timeout for Single Sign Out notifications --- .../model/service_ticket/single_sign_out_notifier.rb | 4 +++- lib/casino_core/settings.rb | 5 ++++- spec/model/service_ticket/single_sign_out_notifier_spec.rb | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb index aae25b9f..c9463c71 100644 --- a/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +++ b/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb @@ -30,7 +30,9 @@ def build_xml def send_notification(url, xml) logger.info "Sending Single Sign Out notification for ticket '#{@service_ticket.ticket}'" - result = Faraday.post(url, logoutRequest: xml) + result = Faraday.post(url, logoutRequest: xml) do |request| + request.options[:timeout] = CASinoCore::Settings.service_ticket[:single_sign_out_notification][:timeout] + end if result.success? logger.info "Logout notification successfully posted to #{url}." true diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 5719d0f6..15a7d827 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -13,7 +13,10 @@ class << self }, service_ticket: { lifetime_unconsumed: 300, - lifetime_consumed: 86400 + lifetime_consumed: 86400, + single_sign_out_notification: { + timeout: 10 + } }, proxy_ticket: { lifetime_unconsumed: 300, diff --git a/spec/model/service_ticket/single_sign_out_notifier_spec.rb b/spec/model/service_ticket/single_sign_out_notifier_spec.rb index d702df13..334e860f 100644 --- a/spec/model/service_ticket/single_sign_out_notifier_spec.rb +++ b/spec/model/service_ticket/single_sign_out_notifier_spec.rb @@ -21,6 +21,13 @@ } end + it 'sets the timeout values' do + [:read_timeout=, :open_timeout=].each do |timeout| + Net::HTTP.any_instance.should_receive(timeout).with(10) + end + notifier.notify + end + context 'when it is a success' do it 'returns true' do notifier.notify.should == true From 022fa49d0ae9c74b9efb6d0c1af2d2bab4a4e064 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 17 Feb 2013 20:03:01 +0100 Subject: [PATCH 291/350] Allow frontends to define overwritable settings --- lib/casino_core/settings.rb | 6 +++++- spec/settings_spec.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 15a7d827..10f7e324 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -3,7 +3,7 @@ module CASinoCore class Settings class << self - attr_accessor :login_ticket, :ticket_granting_ticket, :service_ticket, :proxy_ticket, :two_factor_authenticator, :authenticators, :logger + attr_accessor :login_ticket, :ticket_granting_ticket, :service_ticket, :proxy_ticket, :two_factor_authenticator, :authenticators, :logger, :frontend DEFAULT_SETTINGS = { login_ticket: { lifetime: 600 @@ -54,6 +54,10 @@ def authenticators=(authenticators) end end + def add_defaults(name, config = {}) + DEFAULT_SETTINGS[name] = config + end + private def load_and_instantiate_authenticator(name, options) gemname = "casino_core-authenticator-#{name.underscore}" diff --git a/spec/settings_spec.rb b/spec/settings_spec.rb index d5cd0fda..516a7fe1 100644 --- a/spec/settings_spec.rb +++ b/spec/settings_spec.rb @@ -10,6 +10,14 @@ end end + describe '.add_defaults' do + it 'allows to set a overwritable default' do + CASinoCore::Settings.add_defaults :frontend, { foo: 'bar', example: 'test' } + CASinoCore::Settings.init frontend: { foo: 'test', test: 'example' } + CASinoCore::Settings.frontend.should == { foo: 'test', example: 'test', test: 'example' } + end + end + describe '#authenticators=' do context 'with an authenticator name' do let(:authenticator_name) { 'testing' } From dd10824ce0c03b3b7f2caac4128b0069b4104925 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 17 Feb 2013 20:03:23 +0100 Subject: [PATCH 292/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index eb51faaf..4fd0a5fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.1) + casino_core (1.3.2) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index 493d4340..3c523c46 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.1' + VERSION = '1.3.2' end From 5e70d959f96a8a263d8389dc51cc2adcecc543e6 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Fri, 22 Feb 2013 17:06:59 +0100 Subject: [PATCH 293/350] Use camelize instead of classify --- lib/casino_core/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 10f7e324..fd1e16b4 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -61,7 +61,7 @@ def add_defaults(name, config = {}) private def load_and_instantiate_authenticator(name, options) gemname = "casino_core-authenticator-#{name.underscore}" - classname = name.classify + classname = name.camelize begin require gemname CASinoCore::Authenticator.const_get(classname).new(options) From b5a422ba50888e58bab23d4c5279c66232018cab Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 22 Feb 2013 17:09:53 +0100 Subject: [PATCH 294/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4fd0a5fd..721a00bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.2) + casino_core (1.3.3) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index 3c523c46..a1683657 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.2' + VERSION = '1.3.3' end From 6f98be4b46182cb43dfbdb346bceea783afdbc19 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Fri, 22 Feb 2013 17:39:17 +0100 Subject: [PATCH 295/350] Fix error message about authenticator loading --- lib/casino_core/settings.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index fd1e16b4..40628d0c 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -65,8 +65,11 @@ def load_and_instantiate_authenticator(name, options) begin require gemname CASinoCore::Authenticator.const_get(classname).new(options) - rescue LoadError - raise LoadError, "Authenticator '#{name}' not found. Please include \"gem '#{gemname}'\" in your Gemfile and try again." + rescue LoadError => error + puts "Failed to load authenticator '#{name}'. Maybe you have to include \"gem '#{gemname}'\" in your Gemfile?" + puts " Error: #{error.message}" + puts '' + raise error end end end From c3e36cdd57e06b9c169a3ada3013156c66ff512a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 22 Feb 2013 17:44:49 +0100 Subject: [PATCH 296/350] Don't want error messages while running tests --- spec/settings_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/settings_spec.rb b/spec/settings_spec.rb index 516a7fe1..c236cfb7 100644 --- a/spec/settings_spec.rb +++ b/spec/settings_spec.rb @@ -57,6 +57,7 @@ CASinoCore::Settings.stub(:require) do raise LoadError, 'cannot load such file' end + CASinoCore::Settings.stub(:puts) end it 'raises an error' do From e43442622faa4a2988184fc46cefabb76fea2d82 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 22 Feb 2013 17:52:35 +0100 Subject: [PATCH 297/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 721a00bc..35594daf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.3) + casino_core (1.3.4) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index a1683657..5c06679a 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.3' + VERSION = '1.3.4' end From ae3bc060a810666fc8bc3d280f633df3879a831c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:05:48 +0100 Subject: [PATCH 298/350] Travis should test with Ruby 1.9.3 and 2.0.0 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index a1828110..36338adb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ language: ruby before_script: - DATABASE_ENV=test rake casino_core:db:schema:load +rvm: + - 1.9.3 + - 2.0.0 From c7b2c0f818d7461c6bf7b96eb02e7d82c2c5697e Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:14:54 +0100 Subject: [PATCH 299/350] Maybe this fixes ruby 2.0.0 build? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 36338adb..2c6bbfea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: ruby before_script: - DATABASE_ENV=test rake casino_core:db:schema:load + - "mkdir -p /home/travis/.gem && touch /home/travis/.gem/casino-private_key.pem" rvm: - 1.9.3 - 2.0.0 From 8abf1abb58bb1a6e32dec29938dd9464cb887700 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:16:38 +0100 Subject: [PATCH 300/350] Let's try Ruby 2.0.0-p0 --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c6bbfea..017497a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: ruby before_script: - DATABASE_ENV=test rake casino_core:db:schema:load - - "mkdir -p /home/travis/.gem && touch /home/travis/.gem/casino-private_key.pem" rvm: - 1.9.3 - - 2.0.0 + - 2.0.0-p0 From 88773b8a303cac8bcccc7ee498b9567661e21338 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:18:34 +0100 Subject: [PATCH 301/350] Only sign, when key exists --- .travis.yml | 2 +- casino_core.gemspec | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 017497a3..36338adb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,4 @@ before_script: - DATABASE_ENV=test rake casino_core:db:schema:load rvm: - 1.9.3 - - 2.0.0-p0 + - 2.0.0 diff --git a/casino_core.gemspec b/casino_core.gemspec index 504c5718..1cf61926 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -17,8 +17,11 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ['lib'] - s.signing_key = File.expand_path '~/.gem/casino-private_key.pem' - s.cert_chain = ['casino-public_cert.pem'] + sign_file = File.expand_path '~/.gem/casino-private_key.pem' + if File.exist?(sign_file) + s.signing_key = sign_file + s.cert_chain = ['casino-public_cert.pem'] + end s.add_development_dependency 'rake', '~> 10.0' s.add_development_dependency 'rspec', '~> 2.12' From bab9a396d01819f1a1b7e0b2ae7a00a0a7f00ada Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:48:08 +0100 Subject: [PATCH 302/350] Use SSL to access rubygems --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index e45e65f8..851fabc2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ -source :rubygems +source 'https://rubygems.org' gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 35594daf..32546623 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,7 +10,7 @@ PATH useragent (~> 0.4) GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: activemodel (3.2.12) activesupport (= 3.2.12) From 90bc77cbcc2d433e9a371320d614756c5d51ce4a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Feb 2013 11:49:15 +0100 Subject: [PATCH 303/350] Don't change default YAML engine --- lib/casino_core.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index fd2f8b3f..c164778d 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -16,7 +16,6 @@ def setup(environment = nil, options = {}) root_path = options[:application_root] || '.' require 'active_record' require 'yaml' - YAML::ENGINE.yamler = 'syck' ActiveRecord::Base.establish_connection YAML.load_file(File.join(root_path, 'config/database.yml'))[@environment] config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys From e7d92dc98bbe70b53fe7e811207f9a6a1d1ca8d7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 3 Mar 2013 09:25:06 +0100 Subject: [PATCH 304/350] Destroy expired ticket-granting tickets when logging in --- lib/casino_core/helper/ticket_granting_tickets.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index 0632ca8b..9b64563a 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -32,6 +32,7 @@ def find_valid_ticket_granting_ticket(tgt, user_agent, ignore_two_factor = false def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) user_data = authentication_result[:user_data] user = load_or_initialize_user(authentication_result[:authenticator], user_data[:username], user_data[:extra_attributes]) + cleanup_expired_ticket_granting_tickets(user) user.ticket_granting_tickets.create!({ ticket: random_ticket_string('TGC'), awaiting_two_factor_authentication: !user.active_two_factor_authenticator.nil?, @@ -55,6 +56,12 @@ def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) end end + def cleanup_expired_ticket_granting_tickets(user) + user.ticket_granting_tickets.where(['created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago]).destroy_all.tap do |destroyed| + logger.info "Destroyed #{destroyed.length} expired ticket-granting tickets" + end + end + end end end From 6b3e74640c3877f70d313e2c54ba60e5c7a2b8ad Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Tue, 12 Mar 2013 21:09:36 +0100 Subject: [PATCH 305/350] add coveralls --- Gemfile.lock | 12 ++++++++++++ casino_core.gemspec | 1 + 2 files changed, 13 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 32546623..fc51aad7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,6 +26,13 @@ GEM addressable (2.3.2) arel (3.0.2) builder (3.0.4) + colorize (0.5.8) + coveralls (0.6.2) + colorize + multi_json (~> 1.3) + rest-client + simplecov (>= 0.7) + thor crack (0.3.2) database_cleaner (0.9.1) diff-lcs (1.1.3) @@ -34,10 +41,13 @@ GEM faraday (0.8.5) multipart-post (~> 1.1) i18n (0.6.1) + mime-types (1.21) multi_json (1.6.1) multipart-post (1.1.5) nokogiri (1.5.6) rake (10.0.3) + rest-client (1.6.7) + mime-types (>= 1.16) rotp (1.4.1) rspec (2.12.0) rspec-core (~> 2.12.0) @@ -53,6 +63,7 @@ GEM simplecov-html (0.7.1) sqlite3 (1.3.7) terminal-table (1.4.5) + thor (0.17.0) tzinfo (0.3.35) useragent (0.4.16) webmock (1.9.0) @@ -65,6 +76,7 @@ PLATFORMS DEPENDENCIES casino_core! + coveralls (> 0) database_cleaner (~> 0.9) factory_girl (~> 4.1) nokogiri (~> 1.5) diff --git a/casino_core.gemspec b/casino_core.gemspec index 1cf61926..fc9ca80a 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -32,6 +32,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'nokogiri', '~> 1.5' s.add_development_dependency 'factory_girl', '~> 4.1' s.add_development_dependency 'yard', '~> 0.8' + s.add_development_dependency 'coveralls', '> 0' s.add_runtime_dependency 'activerecord', '~> 3.2.9' s.add_runtime_dependency 'addressable', '~> 2.3' From 0eac68c6285d1ea034def829479a9c6727fec058 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 12 Mar 2013 21:11:51 +0100 Subject: [PATCH 306/350] Coveralls --- Gemfile.lock | 12 ++++++++++++ casino_core.gemspec | 1 + spec/spec_helper.rb | 3 +++ 3 files changed, 16 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 32546623..8b006da0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,6 +26,13 @@ GEM addressable (2.3.2) arel (3.0.2) builder (3.0.4) + colorize (0.5.8) + coveralls (0.6.2) + colorize + multi_json (~> 1.3) + rest-client + simplecov (>= 0.7) + thor crack (0.3.2) database_cleaner (0.9.1) diff-lcs (1.1.3) @@ -34,10 +41,13 @@ GEM faraday (0.8.5) multipart-post (~> 1.1) i18n (0.6.1) + mime-types (1.21) multi_json (1.6.1) multipart-post (1.1.5) nokogiri (1.5.6) rake (10.0.3) + rest-client (1.6.7) + mime-types (>= 1.16) rotp (1.4.1) rspec (2.12.0) rspec-core (~> 2.12.0) @@ -53,6 +63,7 @@ GEM simplecov-html (0.7.1) sqlite3 (1.3.7) terminal-table (1.4.5) + thor (0.17.0) tzinfo (0.3.35) useragent (0.4.16) webmock (1.9.0) @@ -65,6 +76,7 @@ PLATFORMS DEPENDENCIES casino_core! + coveralls database_cleaner (~> 0.9) factory_girl (~> 4.1) nokogiri (~> 1.5) diff --git a/casino_core.gemspec b/casino_core.gemspec index 1cf61926..024f58d8 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -32,6 +32,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'nokogiri', '~> 1.5' s.add_development_dependency 'factory_girl', '~> 4.1' s.add_development_dependency 'yard', '~> 0.8' + s.add_development_dependency 'coveralls' s.add_runtime_dependency 'activerecord', '~> 3.2.9' s.add_runtime_dependency 'addressable', '~> 2.3' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 89ef0515..771c1f7c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,6 +11,9 @@ end end +require 'coveralls' +Coveralls.wear! + require 'database_cleaner' require 'logger' require 'webmock/rspec' From 7ed7cdaf45d65ba0da4e2d7a89a6b1d869180e8e Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Tue, 12 Mar 2013 21:13:45 +0100 Subject: [PATCH 307/350] ignore coveralls config file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 60c4ff92..949a12fe 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ pkg # Ignore the default SQLite database. /db/*.sqlite3 + +.coveralls.yml From 2e30962a43e78b44e886cecc024bbb15d0997dd9 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Tue, 12 Mar 2013 21:14:07 +0100 Subject: [PATCH 308/350] add coverall simplecov formatter --- spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 89ef0515..8fb9e0f1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext' require 'simplecov' +require 'coveralls' +SimpleCov.formatter = Coveralls::SimpleCov::Formatter SimpleCov.start do add_filter '/spec' base_path = "#{File.dirname(__FILE__)}/../" From 3f11adb5c2ff28a7f20d2d3a65ced8e0e4c2ef31 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 12 Mar 2013 21:44:55 +0100 Subject: [PATCH 309/350] Don't need to reference coveralls twice --- casino_core.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/casino_core.gemspec b/casino_core.gemspec index 3935e15d..024f58d8 100644 --- a/casino_core.gemspec +++ b/casino_core.gemspec @@ -32,7 +32,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'nokogiri', '~> 1.5' s.add_development_dependency 'factory_girl', '~> 4.1' s.add_development_dependency 'yard', '~> 0.8' - s.add_development_dependency 'coveralls', '> 0' s.add_development_dependency 'coveralls' s.add_runtime_dependency 'activerecord', '~> 3.2.9' From 30e7465d6d13cccf05670ac5c0de042b7d552ee3 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 18 Mar 2013 18:07:05 +0100 Subject: [PATCH 310/350] Processor to destroy other sessions --- lib/casino_core/processor.rb | 1 + .../processor/other_sessions_destroyer.rb | 30 +++++++++++ spec/processor/logout_other_sessions_spec.rb | 53 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 lib/casino_core/processor/other_sessions_destroyer.rb create mode 100644 spec/processor/logout_other_sessions_spec.rb diff --git a/lib/casino_core/processor.rb b/lib/casino_core/processor.rb index d87d2a03..2754a31e 100644 --- a/lib/casino_core/processor.rb +++ b/lib/casino_core/processor.rb @@ -6,6 +6,7 @@ class Processor autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb' autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb' autoload :Logout, 'casino_core/processor/logout.rb' + autoload :OtherSessionsDestroyer, 'casino_core/processor/other_sessions_destroyer.rb' autoload :ProxyTicketProvider, 'casino_core/processor/proxy_ticket_provider.rb' autoload :ProxyTicketValidator, 'casino_core/processor/proxy_ticket_validator.rb' autoload :SecondFactorAuthenticationAcceptor, 'casino_core/processor/second_factor_authentication_acceptor.rb' diff --git a/lib/casino_core/processor/other_sessions_destroyer.rb b/lib/casino_core/processor/other_sessions_destroyer.rb new file mode 100644 index 00000000..5ca73661 --- /dev/null +++ b/lib/casino_core/processor/other_sessions_destroyer.rb @@ -0,0 +1,30 @@ +require 'casino_core/processor' +require 'casino_core/helper' +require 'casino_core/model' + +# The OtherSessionsDestroyer processor should be used to process GET requests to /destroy-other-sessions. +# +# It is usefule to redirect users to this action after a password change. +# +# This feature is not described in the CAS specification so it's completly optional +# to implement this on the web application side. +class CASinoCore::Processor::OtherSessionsDestroyer < CASinoCore::Processor + include CASinoCore::Helper::TicketGrantingTickets + + # This method will call `#other_sessions_destroyed` and may supply an URL that should be presented to the user. + # The user should be redirected to this URL immediately. + # + # @param [Hash] params parameters supplied by user + # @param [Hash] cookies cookies supplied by user + # @param [String] user_agent user-agent delivered by the client + def process(params = nil, cookies = nil, user_agent = nil) + params ||= {} + cookies ||= {} + tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) + unless tgt.nil? + other_ticket_granting_tickets = tgt.user.ticket_granting_tickets.where('id != ?', tgt.id) + other_ticket_granting_tickets.destroy_all + end + @listener.other_sessions_destroyed(params[:url]) + end +end diff --git a/spec/processor/logout_other_sessions_spec.rb b/spec/processor/logout_other_sessions_spec.rb new file mode 100644 index 00000000..aa7fe38b --- /dev/null +++ b/spec/processor/logout_other_sessions_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe CASinoCore::Processor::OtherSessionsDestroyer do + describe '#process' do + let(:listener) { Object.new } + let(:processor) { described_class.new(listener) } + let(:cookies) { { tgt: tgt } } + let(:url) { nil } + let(:params) { { :url => url } unless url.nil? } + + before(:each) do + listener.stub(:other_sessions_destroyed) + end + + context 'with an existing ticket-granting ticket' do + let(:user) { FactoryGirl.create :user } + let!(:other_users_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3 } + let!(:other_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3, user: user } + let!(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } + let(:tgt) { ticket_granting_ticket.ticket } + let(:user_agent) { ticket_granting_ticket.user_agent } + + it 'deletes all other ticket-granting tickets' do + lambda do + processor.process(params, cookies, user_agent) + end.should change(CASinoCore::Model::TicketGrantingTicket, :count).by(-3) + end + + it 'calls the #user_logged_out method on the listener' do + listener.should_receive(:other_sessions_destroyed).with(nil) + processor.process(params, cookies, user_agent) + end + + context 'with an URL' do + let(:url) { 'http://www.example.com' } + + it 'calls the #user_logged_out method on the listener and passes the URL' do + listener.should_receive(:other_sessions_destroyed).with(url) + processor.process(params, cookies, user_agent) + end + end + end + + context 'with an invlaid ticket-granting ticket' do + let(:tgt) { 'TGT-lalala' } + + it 'calls the #other_sessions_destroyed method on the listener' do + listener.should_receive(:other_sessions_destroyed).with(nil) + processor.process(params, cookies) + end + end + end +end From 3ddc4ef9b8663186ee927199c1fda1efe86c5d85 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 19 Mar 2013 15:15:24 +0100 Subject: [PATCH 311/350] Renamed url parameter to service --- lib/casino_core/processor/other_sessions_destroyer.rb | 2 +- spec/processor/logout_other_sessions_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/processor/other_sessions_destroyer.rb b/lib/casino_core/processor/other_sessions_destroyer.rb index 5ca73661..321f50dd 100644 --- a/lib/casino_core/processor/other_sessions_destroyer.rb +++ b/lib/casino_core/processor/other_sessions_destroyer.rb @@ -25,6 +25,6 @@ def process(params = nil, cookies = nil, user_agent = nil) other_ticket_granting_tickets = tgt.user.ticket_granting_tickets.where('id != ?', tgt.id) other_ticket_granting_tickets.destroy_all end - @listener.other_sessions_destroyed(params[:url]) + @listener.other_sessions_destroyed(params[:service]) end end diff --git a/spec/processor/logout_other_sessions_spec.rb b/spec/processor/logout_other_sessions_spec.rb index aa7fe38b..9ad195e6 100644 --- a/spec/processor/logout_other_sessions_spec.rb +++ b/spec/processor/logout_other_sessions_spec.rb @@ -6,7 +6,7 @@ let(:processor) { described_class.new(listener) } let(:cookies) { { tgt: tgt } } let(:url) { nil } - let(:params) { { :url => url } unless url.nil? } + let(:params) { { :service => url } unless url.nil? } before(:each) do listener.stub(:other_sessions_destroyed) From 18762066acde183b1665b7101c6d440549e881c2 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 19 Mar 2013 15:16:34 +0100 Subject: [PATCH 312/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8b006da0..5191ee91 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.4) + casino_core (1.3.5) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index 5c06679a..1bbea4f2 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.4' + VERSION = '1.3.5' end From e71a46c903bca3df99caca8fe012486b34d9c672 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 20 Mar 2013 18:00:35 +0100 Subject: [PATCH 313/350] Changed timeout to Jasig's recommendation --- lib/casino_core/settings.rb | 2 +- spec/model/service_ticket/single_sign_out_notifier_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 40628d0c..80925f92 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -15,7 +15,7 @@ class << self lifetime_unconsumed: 300, lifetime_consumed: 86400, single_sign_out_notification: { - timeout: 10 + timeout: 5 } }, proxy_ticket: { diff --git a/spec/model/service_ticket/single_sign_out_notifier_spec.rb b/spec/model/service_ticket/single_sign_out_notifier_spec.rb index 334e860f..24467301 100644 --- a/spec/model/service_ticket/single_sign_out_notifier_spec.rb +++ b/spec/model/service_ticket/single_sign_out_notifier_spec.rb @@ -23,7 +23,7 @@ it 'sets the timeout values' do [:read_timeout=, :open_timeout=].each do |timeout| - Net::HTTP.any_instance.should_receive(timeout).with(10) + Net::HTTP.any_instance.should_receive(timeout).with(CASinoCore::Settings.service_ticket[:single_sign_out_notification][:timeout]) end notifier.notify end From b073130217a9557c2b7b9e15db12565bf92347db Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Wed, 20 Mar 2013 18:12:49 +0100 Subject: [PATCH 314/350] Support service parameter on /logout --- lib/casino_core/processor/logout.rb | 6 +++++- spec/processor/logout_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/processor/logout.rb b/lib/casino_core/processor/logout.rb index fd09f738..ab8646c3 100644 --- a/lib/casino_core/processor/logout.rb +++ b/lib/casino_core/processor/logout.rb @@ -18,6 +18,10 @@ def process(params = nil, cookies = nil, user_agent = nil) params ||= {} cookies ||= {} remove_ticket_granting_ticket(cookies[:tgt], user_agent) - @listener.user_logged_out(params[:url]) + if params[:service] && CASinoCore::Model::ServiceRule.allowed?(params[:service]) + @listener.user_logged_out(params[:service], true) + else + @listener.user_logged_out(params[:url]) + end end end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index 4ed81476..94778857 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -35,6 +35,29 @@ processor.process(params, cookies, user_agent) end end + + context 'with a service' do + let(:params) { { :service => url } } + let(:url) { 'http://www.example.org' } + + context '(whitelisted)' do + it 'calls the #user_logged_out method on the listener and passes the URL and the redirect_immediate flag' do + listener.should_receive(:user_logged_out).with(url, true) + processor.process(params, cookies, user_agent) + end + end + + context '(not whitelisted)' do + before(:each) do + FactoryGirl.create :service_rule, :regex, url: '^https://.*' + end + + it 'calls the #user_logged_out method on the listener and passes no URL' do + listener.should_receive(:user_logged_out).with(nil) + processor.process(params, cookies, user_agent) + end + end + end end context 'with an invlaid ticket-granting ticket' do From ceb998706442ae2d096766c9c32aafe25a69627f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 23 Mar 2013 10:40:40 +0100 Subject: [PATCH 315/350] Single Logout should be "fire and forget" --- lib/casino_core/model/service_ticket.rb | 1 + .../model/ticket_granting_ticket.rb | 23 +------------------ spec/model/service_ticket_spec.rb | 4 ++-- spec/model/ticket_granting_ticket_spec.rb | 7 +++--- 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index b7ae6bf3..843e73ed 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -42,5 +42,6 @@ def expired? def send_single_sing_out_notification notifier = SingleSignOutNotifier.new(self) notifier.notify + true end end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 696310fd..06b7ad7b 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -5,10 +5,7 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base validates :ticket, uniqueness: true belongs_to :user - has_many :service_tickets - - before_destroy :destroy_service_tickets - after_destroy :destroy_proxy_granting_tickets + has_many :service_tickets, dependent: :destroy def self.cleanup self.destroy_all(['created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago]) @@ -37,22 +34,4 @@ def expired? lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime] (Time.now - (self.created_at || Time.now)) > lifetime end - - private - def destroy_service_tickets - self.service_tickets.each do |service_ticket| - unless service_ticket.destroy - service_ticket.ticket_granting_ticket_id = nil - service_ticket.save - end - end - end - - # Deletes proxy-granting tickets of service tickets that - # could not be deleted (see #destroy_service_tickets) - def destroy_proxy_granting_tickets - self.service_tickets.each do |service_ticket| - service_ticket.proxy_granting_tickets.destroy_all - end - end end diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index 7fb69257..ce8f1909 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -106,11 +106,11 @@ described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(false) end - it 'does not delete the service ticket' do + it 'does delete the service ticket anyway' do consumed_ticket lambda { consumed_ticket.destroy - }.should_not change(CASinoCore::Model::ServiceTicket, :count) + }.should change(CASinoCore::Model::ServiceTicket, :count).by(-1) end end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 06682db4..a78cdb5f 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -4,9 +4,10 @@ describe CASinoCore::Model::TicketGrantingTicket do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:service_ticket) { FactoryGirl.create :service_ticket, ticket_granting_ticket: ticket_granting_ticket } - let(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket } describe '#destroy' do + let!(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket } + context 'when notification for a service ticket fails' do before(:each) do CASinoCore::Model::ServiceTicket::SingleSignOutNotifier.any_instance.stub(:notify).and_return(false) @@ -19,10 +20,10 @@ }.should change(CASinoCore::Model::ProxyGrantingTicket, :count).by(-1) end - it 'nullifies depending service tickets' do + it 'deletes depending service tickets' do lambda { ticket_granting_ticket.destroy - }.should change { consumed_service_ticket.reload.ticket_granting_ticket_id }.from(ticket_granting_ticket.id).to(nil) + }.should change(CASinoCore::Model::ServiceTicket, :count).by(-1) end end end From 8c749ba2939e68fe393b9a98cbaab4448196181c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 23 Mar 2013 11:17:26 +0100 Subject: [PATCH 316/350] long_term support on login --- ...08_add_long_term_to_ticket_granting_tickets.rb | 5 +++++ lib/casino_core/helper/ticket_granting_tickets.rb | 5 +++-- lib/casino_core/model/ticket_granting_ticket.rb | 2 +- .../processor/login_credential_acceptor.rb | 11 +++++++++-- lib/casino_core/settings.rb | 3 ++- spec/processor/login_credential_acceptor_spec.rb | 15 +++++++++++++++ 6 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20130323111208_add_long_term_to_ticket_granting_tickets.rb diff --git a/db/migrate/20130323111208_add_long_term_to_ticket_granting_tickets.rb b/db/migrate/20130323111208_add_long_term_to_ticket_granting_tickets.rb new file mode 100644 index 00000000..b64e5a90 --- /dev/null +++ b/db/migrate/20130323111208_add_long_term_to_ticket_granting_tickets.rb @@ -0,0 +1,5 @@ +class AddLongTermToTicketGrantingTickets < ActiveRecord::Migration + def change + add_column :ticket_granting_tickets, :long_term, :boolean, null: false, default: false + end +end diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index 9b64563a..30b12202 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -29,14 +29,15 @@ def find_valid_ticket_granting_ticket(tgt, user_agent, ignore_two_factor = false end end - def acquire_ticket_granting_ticket(authentication_result, user_agent = nil) + def acquire_ticket_granting_ticket(authentication_result, user_agent = nil, long_term = nil) user_data = authentication_result[:user_data] user = load_or_initialize_user(authentication_result[:authenticator], user_data[:username], user_data[:extra_attributes]) cleanup_expired_ticket_granting_tickets(user) user.ticket_granting_tickets.create!({ ticket: random_ticket_string('TGC'), awaiting_two_factor_authentication: !user.active_two_factor_authenticator.nil?, - user_agent: user_agent + user_agent: user_agent, + long_term: !!long_term }) end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 06b7ad7b..2b7d6a06 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -1,7 +1,7 @@ require 'casino_core/model' class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base - attr_accessible :ticket, :user_agent, :awaiting_two_factor_authentication + attr_accessible :ticket, :user_agent, :awaiting_two_factor_authentication, :long_term validates :ticket, uniqueness: true belongs_to :user diff --git a/lib/casino_core/processor/login_credential_acceptor.rb b/lib/casino_core/processor/login_credential_acceptor.rb index a84d1e94..2af3724e 100644 --- a/lib/casino_core/processor/login_credential_acceptor.rb +++ b/lib/casino_core/processor/login_credential_acceptor.rb @@ -15,6 +15,8 @@ class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor # The method will call one of the following methods on the listener: # * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to. # The second argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt". + # The third argument (Time, optional, default = nil) is for "Remember Me" functionality. + # This is the cookies expiration date. If it is `nil`, the cookie should be a session cookie. # * `#invalid_login_ticket` and `#invalid_login_credentials`: The first argument is a LoginTicket. # See {CASinoCore::Processor::LoginCredentialRequestor} for details. # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. @@ -43,7 +45,8 @@ def authenticate_user end def user_logged_in(authentication_result) - ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, @user_agent) + long_term = @params[:rememberMe] + ticket_granting_ticket = acquire_ticket_granting_ticket(authentication_result, @user_agent, long_term) if ticket_granting_ticket.awaiting_two_factor_authentication? @listener.two_factor_authentication_pending(ticket_granting_ticket.ticket) else @@ -51,7 +54,11 @@ def user_logged_in(authentication_result) url = unless @params[:service].blank? acquire_service_ticket(ticket_granting_ticket, @params[:service], true).service_with_ticket_url end - @listener.user_logged_in(url, ticket_granting_ticket.ticket) + if long_term + @listener.user_logged_in(url, ticket_granting_ticket.ticket, CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term].seconds.from_now) + else + @listener.user_logged_in(url, ticket_granting_ticket.ticket) + end rescue ServiceNotAllowedError => e @listener.service_not_allowed(clean_service_url @params[:service]) end diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index 80925f92..c9c777bb 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -9,7 +9,8 @@ class << self lifetime: 600 }, ticket_granting_ticket: { - lifetime: 86400 + lifetime: 86400, + lifetime_long_term: 864000 }, service_ticket: { lifetime_unconsumed: 300, diff --git a/spec/processor/login_credential_acceptor_spec.rb b/spec/processor/login_credential_acceptor_spec.rb index 28dc920c..3310a652 100644 --- a/spec/processor/login_credential_acceptor_spec.rb +++ b/spec/processor/login_credential_acceptor_spec.rb @@ -41,6 +41,21 @@ listener.stub(:user_logged_in) end + context 'with rememberMe set' do + let(:login_data_with_remember_me) { login_data.merge(rememberMe: true) } + + it 'calls the #user_logged_in method on the listener with an expiration date set' do + listener.should_receive(:user_logged_in).with(/^#{service}\/\?ticket=ST\-/, /^TGC\-/, kind_of(Time)) + processor.process(login_data_with_remember_me) + end + + it 'creates a long-term ticket-granting ticket' do + processor.process(login_data_with_remember_me) + tgt = CASinoCore::Model::TicketGrantingTicket.last + tgt.long_term.should == true + end + end + context 'with two-factor authentication enabled' do let(:user) { CASinoCore::Model::User.create! username: username, authenticator: authenticator } let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } From cf262270725bf6a55b334253da477b150095904c Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 23 Mar 2013 11:30:11 +0100 Subject: [PATCH 317/350] long_term support in cleanup methods --- .../model/ticket_granting_ticket.rb | 13 +++++- spec/model/ticket_granting_ticket_spec.rb | 45 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 2b7d6a06..8a04a827 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -8,7 +8,12 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base has_many :service_tickets, dependent: :destroy def self.cleanup - self.destroy_all(['created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago]) + self.destroy_all([ + '(created_at < ? AND long_term = ?) OR created_at < ?', + CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago, + false, + CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term].seconds.ago + ]) end def browser_info @@ -31,7 +36,11 @@ def same_user?(other_ticket) end def expired? - lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime] + if long_term? + lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term] + else + lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime] + end (Time.now - (self.created_at || Time.now)) > lifetime end end diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index a78cdb5f..446ba711 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -83,6 +83,32 @@ end describe '#expired?' do + context 'with a long-term ticket' do + context 'when almost expired' do + before(:each) do + ticket_granting_ticket.created_at = 9.days.ago + ticket_granting_ticket.long_term = true + ticket_granting_ticket.save! + end + + it 'returns false' do + ticket_granting_ticket.expired?.should == false + end + end + + context 'when expired' do + before(:each) do + ticket_granting_ticket.created_at = 30.days.ago + ticket_granting_ticket.long_term = true + ticket_granting_ticket.save! + end + + it 'returns true' do + ticket_granting_ticket.expired?.should == true + end + end + end + context 'with an expired ticket' do before(:each) do ticket_granting_ticket.created_at = 25.hours.ago @@ -112,5 +138,24 @@ end.should change(described_class, :count).by(-1) described_class.find_by_ticket(ticket_granting_ticket.ticket).should be_false end + + it 'does not delete almost expired long-term ticket-granting tickets' do + ticket_granting_ticket.created_at = 9.days.ago + ticket_granting_ticket.long_term = true + ticket_granting_ticket.save! + lambda do + described_class.cleanup + end.should_not change(described_class, :count) + end + + it 'does delete expired long-term ticket-granting tickets' do + ticket_granting_ticket.created_at = 30.days.ago + ticket_granting_ticket.long_term = true + ticket_granting_ticket.save! + lambda do + described_class.cleanup + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket(ticket_granting_ticket.ticket).should be_false + end end end From 10c2f51da81fa4f379722b1fd0fd8cf621500fa4 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 23 Mar 2013 11:31:41 +0100 Subject: [PATCH 318/350] Update schema.rb --- db/schema.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index f16f8a58..3d55a6c3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130203155008) do +ActiveRecord::Schema.define(:version => 20130323111208) do create_table "login_tickets", :force => true do |t| t.string "ticket", :null => false @@ -79,6 +79,7 @@ t.string "user_agent" t.integer "user_id", :null => false t.boolean "awaiting_two_factor_authentication", :default => false, :null => false + t.boolean "long_term", :default => false, :null => false end add_index "ticket_granting_tickets", ["ticket"], :name => "index_ticket_granting_tickets_on_ticket", :unique => true From 4418d8033842ce9ca65fb3da3955fd552c5bc50a Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 23 Mar 2013 12:17:06 +0100 Subject: [PATCH 319/350] Add some extra attributes --- .../builder/ticket_validation_response.rb | 22 ++++++++++++++----- spec/processor/ticket_validator_spec.rb | 15 +++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/casino_core/builder/ticket_validation_response.rb b/lib/casino_core/builder/ticket_validation_response.rb index 9c2547d3..aaea30b0 100644 --- a/lib/casino_core/builder/ticket_validation_response.rb +++ b/lib/casino_core/builder/ticket_validation_response.rb @@ -14,18 +14,19 @@ def build ticket = @options[:ticket] if ticket.is_a?(CASinoCore::Model::ProxyTicket) proxies = [] - _ticket = ticket - while _ticket.is_a?(CASinoCore::Model::ProxyTicket) + service_ticket = ticket + while service_ticket.is_a?(CASinoCore::Model::ProxyTicket) proxy_granting_ticket = ticket.proxy_granting_ticket proxies << proxy_granting_ticket.pgt_url - _ticket = proxy_granting_ticket.granter + service_ticket = proxy_granting_ticket.granter end - ticket_granting_ticket = _ticket.ticket_granting_ticket + ticket_granting_ticket = service_ticket.ticket_granting_ticket else + service_ticket = ticket ticket_granting_ticket = ticket.ticket_granting_ticket end - build_success_xml(service_response, ticket, ticket_granting_ticket, proxies) + build_success_xml(service_response, ticket, service_ticket, ticket_granting_ticket, proxies) else build_failure_xml(service_response) end @@ -44,12 +45,21 @@ def serialize_extra_attribute(builder, key, value) end end - def build_success_xml(service_response, ticket, ticket_granting_ticket, proxies) + def build_success_xml(service_response, ticket, service_ticket, ticket_granting_ticket, proxies) user = ticket_granting_ticket.user service_response.cas :authenticationSuccess do |authentication_success| authentication_success.cas :user, user.username unless user.extra_attributes.blank? authentication_success.cas :attributes do |attributes| + attributes.cas :authenticationDate, ticket_granting_ticket.created_at.iso8601 + attributes.cas :longTermAuthenticationRequestTokenUsed, ticket_granting_ticket.long_term? + attributes.cas :isFromNewLogin, service_ticket.issued_from_credentials? + # This would probably be the correct way, but current clients do not support this: + # attributes.cas :userAttributes do |user_attributes| + # user.extra_attributes.each do |key, value| + # serialize_extra_attribute(user_attributes, key, value) + # end + # end user.extra_attributes.each do |key, value| serialize_extra_attribute(attributes, key, value) end diff --git a/spec/processor/ticket_validator_spec.rb b/spec/processor/ticket_validator_spec.rb index 083b65d5..a22c09bd 100644 --- a/spec/processor/ticket_validator_spec.rb +++ b/spec/processor/ticket_validator_spec.rb @@ -29,6 +29,21 @@ end context 'with an unconsumed service ticket' do + context 'issued from a long_term ticket-granting ticket' do + before(:each) do + tgt = service_ticket.ticket_granting_ticket + tgt.long_term = true + tgt.save! + end + + it 'calls the #validation_succeeded method on the listener' do + listener.should_receive(:validation_succeeded).with( + /true<\/cas\:longTermAuthenticationRequestTokenUsed>/ + ) + processor.process(parameters) + end + end + context 'without renew flag' do it 'consumes the service ticket' do processor.process(parameters) From 8704289574cfa2589d0ecdff1382558381dbc8c7 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Mar 2013 18:28:32 +0100 Subject: [PATCH 320/350] List of changes --- UPGRADE.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UPGRADE.md b/UPGRADE.md index a1d37755..2405991b 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,7 +2,16 @@ Here is a list of backward-incompatible changes that were introduced. -## 1.x.y +## 1.4.0 + +This release changed some database structure. Be sure to advise users to migrate the database using `bundle exec rake casino_core:db:migrate`. + +API changes: + +* `LoginCredentialAcceptor`: `user_logged_in` may receive a third argument (`Time`, optional, default = `nil`) which represents the expiry date of the cookie. If it is `nil`, the cookie should be a session cookie. +* `Logout`: `user_logged_out` may receive a second argument (`boolean`, optional, default = `false`). When it is `true`, the user should be redirected immediately. + +## 1.3.0 This release adds support for two-factor authentication using a [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) (time-based one-time password) which can be generated with applications like [Google Authenticator](http://support.google.com/a/bin/answer.py?hl=en&answer=1037451) (iPhone, Android, BlackBerry) or gadgets such as the [YubiKey](http://www.yubico.com/products/yubikey-hardware/yubikey/). From e8a7c4fbfb4272374a83647f6f80cb07a7f6b45f Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Mar 2013 20:04:52 +0100 Subject: [PATCH 321/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5191ee91..8684d390 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.3.5) + casino_core (1.4.0) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index 1bbea4f2..a52d1631 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.3.5' + VERSION = '1.4.0' end From 0e66300d17c99a048e03252ef496247013be41ef Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 24 Mar 2013 20:42:21 +0100 Subject: [PATCH 322/350] Ticket-granting ticket with pending two-factor should timeout earlier --- .../model/ticket_granting_ticket.rb | 4 +++- lib/casino_core/settings.rb | 1 + spec/model/ticket_granting_ticket_spec.rb | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index 8a04a827..dfb81408 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -36,7 +36,9 @@ def same_user?(other_ticket) end def expired? - if long_term? + if awaiting_two_factor_authentication? + lifetime = CASinoCore::Settings.two_factor_authenticator[:timeout] + elsif long_term? lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term] else lifetime = CASinoCore::Settings.ticket_granting_ticket[:lifetime] diff --git a/lib/casino_core/settings.rb b/lib/casino_core/settings.rb index c9c777bb..73d869c6 100644 --- a/lib/casino_core/settings.rb +++ b/lib/casino_core/settings.rb @@ -24,6 +24,7 @@ class << self lifetime_consumed: 86400 }, two_factor_authenticator: { + timeout: 180, lifetime_inactive: 300, drift: 30 } diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index 446ba711..d7923ee6 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -125,6 +125,30 @@ ticket_granting_ticket.expired?.should == false end end + + context 'with pending two-factor authentication' do + before(:each) do + ticket_granting_ticket.awaiting_two_factor_authentication = true + ticket_granting_ticket.save! + end + + context 'with an expired ticket' do + before(:each) do + ticket_granting_ticket.created_at = 10.minutes.ago + ticket_granting_ticket.save! + end + + it 'returns true' do + ticket_granting_ticket.expired?.should == true + end + end + + context 'with an unexpired ticket' do + it 'returns false' do + ticket_granting_ticket.expired?.should == false + end + end + end end describe '.cleanup' do From 17ab701047a97fa2313d8ba12de7058a386028ae Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Mon, 25 Mar 2013 15:46:00 +0100 Subject: [PATCH 323/350] added coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb7ad17f..a418c002 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CASinoCore [![Build Status](https://secure.travis-ci.org/rbCAS/CASinoCore.png?branch=master)](https://travis-ci.org/rbCAS/CASinoCore) +# CASinoCore [![Build Status](https://secure.travis-ci.org/rbCAS/CASinoCore.png?branch=master)](https://travis-ci.org/rbCAS/CASinoCore) [![Coverage Status](https://coveralls.io/repos/rbCAS/CASinoCore/badge.png?branch=master)](https://coveralls.io/r/rbCAS/CASinoCore) A CAS server core library. From 67e8c3ae48d23effb77697fb5117a8342888c451 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 26 Mar 2013 15:30:01 +0100 Subject: [PATCH 324/350] Fix cleanup of users's ticket-granting tickets --- lib/casino_core/helper/ticket_granting_tickets.rb | 4 +--- lib/casino_core/model/ticket_granting_ticket.rb | 11 ++++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/casino_core/helper/ticket_granting_tickets.rb b/lib/casino_core/helper/ticket_granting_tickets.rb index 30b12202..549f6513 100644 --- a/lib/casino_core/helper/ticket_granting_tickets.rb +++ b/lib/casino_core/helper/ticket_granting_tickets.rb @@ -58,9 +58,7 @@ def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) end def cleanup_expired_ticket_granting_tickets(user) - user.ticket_granting_tickets.where(['created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago]).destroy_all.tap do |destroyed| - logger.info "Destroyed #{destroyed.length} expired ticket-granting tickets" - end + CASinoCore::Model::TicketGrantingTicket.cleanup(user) end end diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index dfb81408..a2ba4e8c 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -7,13 +7,18 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base belongs_to :user has_many :service_tickets, dependent: :destroy - def self.cleanup - self.destroy_all([ + def self.cleanup(user = nil) + if user.nil? + base = self + else + base = user.ticket_granting_tickets + end + base.where([ '(created_at < ? AND long_term = ?) OR created_at < ?', CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago, false, CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term].seconds.ago - ]) + ]).destroy_all end def browser_info From 70b2b28230ef975aea5b0cfd5637b3c2d0d07a00 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 26 Mar 2013 15:30:38 +0100 Subject: [PATCH 325/350] Remove unused VERSION --- VERSION | 1 - 1 file changed, 1 deletion(-) delete mode 100644 VERSION diff --git a/VERSION b/VERSION deleted file mode 100644 index e5a4a5e7..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.9 \ No newline at end of file From fa0184be9d9a990da17efc0985a329fb6c4120be Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 26 Mar 2013 15:30:47 +0100 Subject: [PATCH 326/350] Bumped version --- lib/casino_core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index a52d1631..ffc0b5e5 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.4.0' + VERSION = '1.4.1' end From 8222f2c9888509f51d9f7e7d42a3a5b5c54af943 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 29 Mar 2013 00:00:35 +0100 Subject: [PATCH 327/350] Fix handling of nested query values --- Gemfile.lock | 2 +- lib/casino_core/helper/service_tickets.rb | 4 +++- lib/casino_core/model/service_ticket.rb | 2 +- spec/processor/login_credential_requestor_spec.rb | 10 ++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8684d390..e682c675 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.4.0) + casino_core (1.4.1) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/helper/service_tickets.rb b/lib/casino_core/helper/service_tickets.rb index 3e5a51a0..2779e9b4 100644 --- a/lib/casino_core/helper/service_tickets.rb +++ b/lib/casino_core/helper/service_tickets.rb @@ -9,6 +9,8 @@ module ServiceTickets class ServiceNotAllowedError < StandardError; end + RESERVED_CAS_PARAMETER_KEYS = ['service', 'ticket', 'gateway', 'renew'] + def acquire_service_ticket(ticket_granting_ticket, service, credentials_supplied = nil) service_url = clean_service_url(service) unless CASinoCore::Model::ServiceRule.allowed?(service_url) @@ -27,7 +29,7 @@ def clean_service_url(dirty_service) return dirty_service if dirty_service.blank? service_uri = Addressable::URI.parse dirty_service unless service_uri.query_values.nil? - service_uri.query_values = service_uri.query_values.except('service', 'ticket', 'gateway', 'renew') + service_uri.query_values = service_uri.query_values(Array).select { |k,v| !RESERVED_CAS_PARAMETER_KEYS.include?(k) } end if service_uri.query_values.blank? service_uri.query_values = nil diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 843e73ed..08460336 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -25,7 +25,7 @@ def self.cleanup_consumed_hard def service_with_ticket_url service_uri = Addressable::URI.parse(self.service) - service_uri.query_values = (service_uri.query_values || {}).merge(ticket: self.ticket) + service_uri.query_values = (service_uri.query_values(Array) || []) << ['ticket', self.ticket] service_uri.to_s end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index 2efc1d2e..5a155532 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -99,6 +99,16 @@ end end + context 'with a service with nested attributes' do + let(:service) { 'http://example.com/?a%5B%5D=test&a%5B%5D=example' } + let(:params) { { service: service } } + + it 'does not remove the attributes' do + listener.should_receive(:user_logged_in).with(/\?a%5B%5D=test&a%5B%5D=example&ticket=ST\-[^&]+$/) + processor.process(params, cookies, user_agent) + end + end + context 'without a service' do it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil) From d8a8927ead8c52ea7539bbcfa9b6a5a326db3901 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 29 Mar 2013 12:30:17 +0100 Subject: [PATCH 328/350] Bumped version --- Gemfile.lock | 2 +- lib/casino_core/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e682c675..09cbb9e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.4.1) + casino_core (1.4.2) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index ffc0b5e5..de4b6d16 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.4.1' + VERSION = '1.4.2' end From 9095b53cfe7c177f6994494228e0840db6ee6f86 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Fri, 29 Mar 2013 12:50:17 +0100 Subject: [PATCH 329/350] Handle long-term tickets with two-factor authentication enabled --- .../second_factor_authentication_acceptor.rb | 6 +++++- ...second_factor_authenticaton_acceptor_spec.rb | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/casino_core/processor/second_factor_authentication_acceptor.rb b/lib/casino_core/processor/second_factor_authentication_acceptor.rb index fc22fd7a..e088d4ce 100644 --- a/lib/casino_core/processor/second_factor_authentication_acceptor.rb +++ b/lib/casino_core/processor/second_factor_authentication_acceptor.rb @@ -34,7 +34,11 @@ def process(params = nil, user_agent = nil) url = unless params[:service].blank? acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url end - @listener.user_logged_in(url, tgt.ticket) + if tgt.long_term? + @listener.user_logged_in(url, tgt.ticket, CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term].seconds.from_now) + else + @listener.user_logged_in(url, tgt.ticket) + end rescue ServiceNotAllowedError => e @listener.service_not_allowed(clean_service_url params[:service]) end diff --git a/spec/processor/second_factor_authenticaton_acceptor_spec.rb b/spec/processor/second_factor_authenticaton_acceptor_spec.rb index ea0fd6f7..38248411 100644 --- a/spec/processor/second_factor_authenticaton_acceptor_spec.rb +++ b/spec/processor/second_factor_authenticaton_acceptor_spec.rb @@ -17,7 +17,7 @@ let(:tgt) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } let(:otp) { '123456' } - let(:service) { 'http://www.example.com/testing' } + let(:service) { 'http://www.example.com/testing' } let(:params) { { tgt: tgt, otp: otp, service: service }} context 'with an active authenticator' do @@ -39,6 +39,17 @@ ticket_granting_ticket.should_not be_awaiting_two_factor_authentication end + context 'with a long-term ticket-granting ticket' do + before(:each) do + ticket_granting_ticket.update_attributes! long_term: true + end + + it 'calls the #user_logged_in method on the listener with an expiration date set' do + listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/, kind_of(Time)) + processor.process(params, user_agent) + end + end + context 'with a not allowed service' do before(:each) do FactoryGirl.create :service_rule, :regex, url: '^https://.*' @@ -56,12 +67,12 @@ before(:each) do ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(false) end - + it 'calls the `#invalid_one_time_password` method an the listener' do listener.should_receive(:invalid_one_time_password).with(no_args) processor.process(params, user_agent) end - + it 'does not activate the ticket-granting ticket' do processor.process(params, user_agent) ticket_granting_ticket.reload From cb8361c5d8724bd5ce3f0c3e30dc701a575c3722 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 30 Mar 2013 10:10:29 +0100 Subject: [PATCH 330/350] Cleanup ticket-granting tickets with pending two-factor authentication --- .../model/ticket_granting_ticket.rb | 4 +++- spec/model/ticket_granting_ticket_spec.rb | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/model/ticket_granting_ticket.rb b/lib/casino_core/model/ticket_granting_ticket.rb index a2ba4e8c..485828aa 100644 --- a/lib/casino_core/model/ticket_granting_ticket.rb +++ b/lib/casino_core/model/ticket_granting_ticket.rb @@ -14,7 +14,9 @@ def self.cleanup(user = nil) base = user.ticket_granting_tickets end base.where([ - '(created_at < ? AND long_term = ?) OR created_at < ?', + '(created_at < ? AND awaiting_two_factor_authentication = ?) OR (created_at < ? AND long_term = ?) OR created_at < ?', + CASinoCore::Settings.two_factor_authenticator[:timeout].seconds.ago, + true, CASinoCore::Settings.ticket_granting_ticket[:lifetime].seconds.ago, false, CASinoCore::Settings.ticket_granting_ticket[:lifetime_long_term].seconds.ago diff --git a/spec/model/ticket_granting_ticket_spec.rb b/spec/model/ticket_granting_ticket_spec.rb index d7923ee6..aec699fc 100644 --- a/spec/model/ticket_granting_ticket_spec.rb +++ b/spec/model/ticket_granting_ticket_spec.rb @@ -181,5 +181,24 @@ end.should change(described_class, :count).by(-1) described_class.find_by_ticket(ticket_granting_ticket.ticket).should be_false end + + it 'does not delete almost expired ticket-granting tickets with pending two-factor authentication' do + ticket_granting_ticket.created_at = 2.minutes.ago + ticket_granting_ticket.awaiting_two_factor_authentication = true + ticket_granting_ticket.save! + lambda do + described_class.cleanup + end.should_not change(described_class, :count) + end + + it 'does delete expired ticket-granting tickets with pending two-factor authentication' do + ticket_granting_ticket.created_at = 20.minutes.ago + ticket_granting_ticket.awaiting_two_factor_authentication = true + ticket_granting_ticket.save! + lambda do + described_class.cleanup + end.should change(described_class, :count).by(-1) + described_class.find_by_ticket(ticket_granting_ticket.ticket).should be_false + end end end From 7ba21fc050f3caab81b4d0308676cb5901db5cdd Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sat, 30 Mar 2013 15:24:40 +0100 Subject: [PATCH 331/350] Use SecureRandom to generate ticket strings --- lib/casino_core/helper/tickets.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/casino_core/helper/tickets.rb b/lib/casino_core/helper/tickets.rb index c049e5bf..596a9fdd 100644 --- a/lib/casino_core/helper/tickets.rb +++ b/lib/casino_core/helper/tickets.rb @@ -1,8 +1,15 @@ +require 'securerandom' + module CASinoCore module Helper module Tickets + + ALLOWED_TICKET_STRING_CHARACTERS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + def random_ticket_string(prefix, length = 40) - random_string = rand(36**length).to_s(36) + random_string = SecureRandom.random_bytes(length).each_char.map do |char| + ALLOWED_TICKET_STRING_CHARACTERS[(char.ord % ALLOWED_TICKET_STRING_CHARACTERS.length)] + end.join "#{prefix}-#{Time.now.to_i}-#{random_string}" end end From c4b2a5988dcbabb329c9a8a9f7038fe5254ce029 Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Fri, 12 Apr 2013 11:13:48 +0200 Subject: [PATCH 332/350] update rvm configuration --- .ruby-gemset | 1 + .ruby-version | 1 + .rvmrc | 48 ------------------------------------------------ 3 files changed, 2 insertions(+), 48 deletions(-) create mode 100644 .ruby-gemset create mode 100644 .ruby-version delete mode 100644 .rvmrc diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 00000000..606eb0c4 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +casino_core diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..f62fc9ad --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-1.9.3-p194 diff --git a/.rvmrc b/.rvmrc deleted file mode 100644 index 139d2565..00000000 --- a/.rvmrc +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# This is an RVM Project .rvmrc file, used to automatically load the ruby -# development environment upon cd'ing into the directory - -# First we specify our desired [@], the @gemset name is optional, -# Only full ruby name is supported here, for short names use: -# echo "rvm use 1.9.3" > .rvmrc -environment_id="ruby-1.9.3-p194@casino_core" - -# Uncomment the following lines if you want to verify rvm version per project -# rvmrc_rvm_version="1.15.8 (stable)" # 1.10.1 seams as a safe start -# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || { -# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading." -# return 1 -# } - -# First we attempt to load the desired environment directly from the environment -# file. This is very fast and efficient compared to running through the entire -# CLI and selector. If you want feedback on which environment was used then -# insert the word 'use' after --create as this triggers verbose mode. -if [[ -d "${rvm_path:-$HOME/.rvm}/environments" - && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] -then - \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" - [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] && - \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true -else - # If the environment file has not yet been created, use the RVM CLI to select. - rvm --create "$environment_id" || { - echo "Failed to create RVM environment '${environment_id}'." - return 1 - } -fi - -# If you use bundler, this might be useful to you: -# if [[ -s Gemfile ]] && { -# ! builtin command -v bundle >/dev/null || -# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null -# } -# then -# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n" -# gem install bundler -# fi -# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null -# then -# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete' -# fi From ecdeda66e4c3a2ad504234907bc242c9b3f831bd Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 21 Apr 2013 17:02:26 +0200 Subject: [PATCH 333/350] Prevent collisions --- lib/casino_core/helper/tickets.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/helper/tickets.rb b/lib/casino_core/helper/tickets.rb index 596a9fdd..6944baff 100644 --- a/lib/casino_core/helper/tickets.rb +++ b/lib/casino_core/helper/tickets.rb @@ -10,7 +10,7 @@ def random_ticket_string(prefix, length = 40) random_string = SecureRandom.random_bytes(length).each_char.map do |char| ALLOWED_TICKET_STRING_CHARACTERS[(char.ord % ALLOWED_TICKET_STRING_CHARACTERS.length)] end.join - "#{prefix}-#{Time.now.to_i}-#{random_string}" + "#{prefix}-#{'%d' % (Time.now.to_f * 10000)}-#{random_string}" end end end From af0ed2598b0a522a04d1331fe4341ab352031531 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 21 Apr 2013 17:02:42 +0200 Subject: [PATCH 334/350] issued_from_credentials can be mass-assigned --- lib/casino_core/model/service_ticket.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/model/service_ticket.rb b/lib/casino_core/model/service_ticket.rb index 08460336..95fdc400 100644 --- a/lib/casino_core/model/service_ticket.rb +++ b/lib/casino_core/model/service_ticket.rb @@ -5,7 +5,7 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base autoload :SingleSignOutNotifier, 'casino_core/model/service_ticket/single_sign_out_notifier.rb' - attr_accessible :ticket, :service + attr_accessible :ticket, :service, :issued_from_credentials validates :ticket, uniqueness: true belongs_to :ticket_granting_ticket before_destroy :send_single_sing_out_notification, if: :consumed? From 61de81a5b63e8f30176f7b7ce4bec1da6c8e8900 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 21 Apr 2013 17:03:52 +0200 Subject: [PATCH 335/350] Bumped version --- lib/casino_core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index de4b6d16..de9099b2 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.4.2' + VERSION = '1.4.3' end From 56cc8b6d30d108a6ad3b4e44fec1d20bac8bd057 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Sun, 21 Apr 2013 17:04:27 +0200 Subject: [PATCH 336/350] bundle install --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 09cbb9e1..6b656b4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - casino_core (1.4.2) + casino_core (1.4.3) activerecord (~> 3.2.9) addressable (~> 2.3) faraday (~> 0.8) From a048fe949274b10f2b8c8039849e70962f5a59e0 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Tue, 2 Jul 2013 08:23:08 -0700 Subject: [PATCH 337/350] Adds support for database.yml files that contain ERB --- lib/casino_core.rb | 2 +- lib/casino_core/tasks/database.rake | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index c164778d..987bf857 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -16,7 +16,7 @@ def setup(environment = nil, options = {}) root_path = options[:application_root] || '.' require 'active_record' require 'yaml' - ActiveRecord::Base.establish_connection YAML.load_file(File.join(root_path, 'config/database.yml'))[@environment] + ActiveRecord::Base.establish_connection YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[@environment] config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys recursive_symbolize_keys!(config) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index 6a41fd46..de73986c 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -1,3 +1,4 @@ +require 'erb' require 'yaml' require 'logger' require 'active_record' @@ -16,7 +17,7 @@ namespace :casino_core do end task :configuration => :environment do - @config = YAML.load_file('config/database.yml')[DATABASE_ENV] + @config = YAML::load(ERB.new(IO.read('config/database.yml')).result)[DATABASE_ENV] CASinoCore.setup DATABASE_ENV end From 4af75aedf17280b219faced43e573d9b1bc595e7 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Tue, 9 Jul 2013 13:28:21 -0400 Subject: [PATCH 338/350] Removes duplicate ActiveRecord connection establishment calls --- lib/casino_core.rb | 3 ++- lib/casino_core/tasks/database.rake | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 987bf857..75683a90 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -16,7 +16,8 @@ def setup(environment = nil, options = {}) root_path = options[:application_root] || '.' require 'active_record' require 'yaml' - ActiveRecord::Base.establish_connection YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[@environment] + db_cfg = YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[@environment] + ActiveRecord::Base.establish_connection db_cfg config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys recursive_symbolize_keys!(config) diff --git a/lib/casino_core/tasks/database.rake b/lib/casino_core/tasks/database.rake index de73986c..b5ca4c4b 100644 --- a/lib/casino_core/tasks/database.rake +++ b/lib/casino_core/tasks/database.rake @@ -17,17 +17,12 @@ namespace :casino_core do end task :configuration => :environment do - @config = YAML::load(ERB.new(IO.read('config/database.yml')).result)[DATABASE_ENV] CASinoCore.setup DATABASE_ENV - end - - task :configure_connection => :configuration do - ActiveRecord::Base.establish_connection @config - ActiveRecord::Base.logger = Logger.new STDOUT if @config['logger'] + ActiveRecord::Base.logger = CASinoCore::Settings.logger end desc 'Migrate the database (options: VERSION=x, VERBOSE=false)' - task :migrate => :configure_connection do + task :migrate => :configuration do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration| ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) @@ -35,14 +30,14 @@ namespace :casino_core do end desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' - task :rollback => :configure_connection do + task :rollback => :configuration do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) end namespace :schema do desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR' - task :dump => :configure_connection do + task :dump => :configuration do require 'active_record/schema_dumper' File.open(SCHEMA_PATH, "w:utf-8") do |file| ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) @@ -50,7 +45,7 @@ namespace :casino_core do end desc 'Load a schema.rb file into the database' - task :load => :configure_connection do + task :load => :configuration do if File.exists?(SCHEMA_PATH) load(SCHEMA_PATH) else From e022fcec473404c762175a38579404f042b8c8f0 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Tue, 9 Jul 2013 20:20:47 +0200 Subject: [PATCH 339/350] Remove ruby- prefix --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index f62fc9ad..f3a9c9a8 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-1.9.3-p194 +1.9.3-p194 From 10397b6db38169e7767a3e8e7de2a101209358e4 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 16:46:04 -0400 Subject: [PATCH 340/350] Removes the Gemfile.lock from source control See also: * http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ --- .gitignore | 1 + Gemfile.lock | 88 ---------------------------------------------------- 2 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index 949a12fe..82d2746a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ doc # bundler .bundle +Gemfile.lock # jeweler generated pkg diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 6b656b4c..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,88 +0,0 @@ -PATH - remote: . - specs: - casino_core (1.4.3) - activerecord (~> 3.2.9) - addressable (~> 2.3) - faraday (~> 0.8) - rotp (~> 1.4) - terminal-table (~> 1.4) - useragent (~> 0.4) - -GEM - remote: https://rubygems.org/ - specs: - activemodel (3.2.12) - activesupport (= 3.2.12) - builder (~> 3.0.0) - activerecord (3.2.12) - activemodel (= 3.2.12) - activesupport (= 3.2.12) - arel (~> 3.0.2) - tzinfo (~> 0.3.29) - activesupport (3.2.12) - i18n (~> 0.6) - multi_json (~> 1.0) - addressable (2.3.2) - arel (3.0.2) - builder (3.0.4) - colorize (0.5.8) - coveralls (0.6.2) - colorize - multi_json (~> 1.3) - rest-client - simplecov (>= 0.7) - thor - crack (0.3.2) - database_cleaner (0.9.1) - diff-lcs (1.1.3) - factory_girl (4.2.0) - activesupport (>= 3.0.0) - faraday (0.8.5) - multipart-post (~> 1.1) - i18n (0.6.1) - mime-types (1.21) - multi_json (1.6.1) - multipart-post (1.1.5) - nokogiri (1.5.6) - rake (10.0.3) - rest-client (1.6.7) - mime-types (>= 1.16) - rotp (1.4.1) - rspec (2.12.0) - rspec-core (~> 2.12.0) - rspec-expectations (~> 2.12.0) - rspec-mocks (~> 2.12.0) - rspec-core (2.12.2) - rspec-expectations (2.12.1) - diff-lcs (~> 1.1.3) - rspec-mocks (2.12.2) - simplecov (0.7.1) - multi_json (~> 1.0) - simplecov-html (~> 0.7.1) - simplecov-html (0.7.1) - sqlite3 (1.3.7) - terminal-table (1.4.5) - thor (0.17.0) - tzinfo (0.3.35) - useragent (0.4.16) - webmock (1.9.0) - addressable (>= 2.2.7) - crack (>= 0.1.7) - yard (0.8.4.1) - -PLATFORMS - ruby - -DEPENDENCIES - casino_core! - coveralls - database_cleaner (~> 0.9) - factory_girl (~> 4.1) - nokogiri (~> 1.5) - rake (~> 10.0) - rspec (~> 2.12) - simplecov (~> 0.7) - sqlite3 (~> 1.3) - webmock (~> 1.9) - yard (~> 0.8) From 772a3a51b93db929705d6ab8be6004be515f1241 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 11:16:41 -0400 Subject: [PATCH 341/350] Ignores vendored gems installed by Bundler This covers the basic rbenv + Bundler workflow --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 82d2746a..91de5a53 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ doc # bundler .bundle +vendor Gemfile.lock # jeweler generated From 62fb82360b56db08b031b04c71d7a6925bcfced7 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 11:22:02 -0400 Subject: [PATCH 342/350] Uses the RAIL_ENV environment to setup CASinoCore during spec runs Always using the ENV variable ensures consistency across the code base. --- spec/spec_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d9e9f318..9991b620 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +ENV['RAILS_ENV'] ||= ENV['DATABASE_ENV'] || 'test' + require 'active_support/core_ext' require 'simplecov' require 'coveralls' @@ -47,7 +49,7 @@ end config.before(:each) do - CASinoCore.setup 'test' + CASinoCore.setup ENV['RAILS_ENV'] CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.start From 8da83b2f05dbf7f4aee32d699f5c46c38a9700d7 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 11:27:42 -0400 Subject: [PATCH 343/350] Organizes various spec run configurations into separate support files Each gem that requires configuration for the spec run gets its own spec/support file. This keeps things short, succinct, and on-topic --- spec/spec_helper.rb | 26 -------------------------- spec/support/casino_core.rb | 6 ++++++ spec/support/database_cleaner.rb | 14 ++++++++++++++ spec/support/rspec.rb | 4 ++++ 4 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 spec/support/casino_core.rb create mode 100644 spec/support/database_cleaner.rb create mode 100644 spec/support/rspec.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9991b620..9177b09b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,29 +33,3 @@ # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} - -RSpec.configure do |config| - config.treat_symbols_as_metadata_keys_with_true_values = true - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = 'random' - - - config.before(:suite) do - DatabaseCleaner.strategy = :transaction - end - - config.before(:each) do - CASinoCore.setup ENV['RAILS_ENV'] - CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN - DatabaseCleaner.clean_with(:truncation) - DatabaseCleaner.start - end - - config.after(:each) do - DatabaseCleaner.clean - end -end diff --git a/spec/support/casino_core.rb b/spec/support/casino_core.rb new file mode 100644 index 00000000..df72acd0 --- /dev/null +++ b/spec/support/casino_core.rb @@ -0,0 +1,6 @@ +RSpec.configure do |config| + config.before(:each) do + CASinoCore.setup ENV['RAILS_ENV'] + CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN + end +end \ No newline at end of file diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb new file mode 100644 index 00000000..65a2f4c4 --- /dev/null +++ b/spec/support/database_cleaner.rb @@ -0,0 +1,14 @@ +RSpec.configure do |config| + config.before(:suite) do + DatabaseCleaner.strategy = :transaction + end + + config.before(:each) do + DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.start + end + + config.after(:each) do + DatabaseCleaner.clean + end +end \ No newline at end of file diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb new file mode 100644 index 00000000..97777d52 --- /dev/null +++ b/spec/support/rspec.rb @@ -0,0 +1,4 @@ +RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + config.order = 'random' +end \ No newline at end of file From 007b7993e56b9b989408ed3c0ab3803b53b5ef1d Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 11:30:02 -0400 Subject: [PATCH 344/350] Adds some RSpec fu for focused spec runs Just tag a spec with `:focus` to only run that particular spec or group of specs --- spec/support/rspec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index 97777d52..eb84224e 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -1,4 +1,6 @@ RSpec.configure do |config| config.treat_symbols_as_metadata_keys_with_true_values = true + config.run_all_when_everything_filtered = true + config.filter_run focus: true config.order = 'random' end \ No newline at end of file From d4a6369c319c3f267ec21edc7a9f1a0aa47123f3 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Wed, 10 Jul 2013 11:36:23 -0400 Subject: [PATCH 345/350] Uses in-memory SQLite server Not only should this improve the speed of spec runs, but it allows all specs to pass right out of the box. This should make it easier for contributors to get the project up and running. A simple `bundle install && rspec` should be all that is necessary now. --- config/database.yml | 3 ++- lib/casino_core.rb | 20 ++++++++++++++++---- spec/support/sqlite3.rb | 5 +++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 spec/support/sqlite3.rb diff --git a/config/database.yml b/config/database.yml index fd7aa2af..97207281 100644 --- a/config/database.yml +++ b/config/database.yml @@ -13,6 +13,7 @@ development: test: adapter: sqlite3 - database: db/test.sqlite3 + database: ':memory:' pool: 5 timeout: 5000 + verbosity: quiet \ No newline at end of file diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 75683a90..5d455135 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -14,10 +14,8 @@ class << self def setup(environment = nil, options = {}) @environment = environment || 'development' root_path = options[:application_root] || '.' - require 'active_record' - require 'yaml' - db_cfg = YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[@environment] - ActiveRecord::Base.establish_connection db_cfg + + establish_connection(@environment, root_path) unless active_record_connected? config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys recursive_symbolize_keys!(config) @@ -32,6 +30,20 @@ def recursive_symbolize_keys! hash hash.values.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)} hash.values.select{|v| v.is_a? Array}.each{|a| a.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)}} end + + def active_record_connected? + ActiveRecord::Base.connection + rescue ActiveRecord::ConnectionNotEstablished + false + end + + def establish_connection(env, root_path) + require 'active_record' + require 'yaml' + + db_cfg = YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[env] + ActiveRecord::Base.establish_connection db_cfg + end end end diff --git a/spec/support/sqlite3.rb b/spec/support/sqlite3.rb new file mode 100644 index 00000000..4cd36c9c --- /dev/null +++ b/spec/support/sqlite3.rb @@ -0,0 +1,5 @@ +root_path = File.join(File.dirname(__FILE__),'..','..') +schema_path = File.join(root_path, 'db') + +CASinoCore.send(:establish_connection, ENV['RAILS_ENV'], root_path) +load File.join(schema_path, 'schema.rb') \ No newline at end of file From d41dadf14046a19937ee2d1c11a4878fa9990fb9 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 15 Jul 2013 10:04:14 +0200 Subject: [PATCH 346/350] Add newlines --- config/database.yml | 2 +- spec/support/casino_core.rb | 2 +- spec/support/database_cleaner.rb | 2 +- spec/support/rspec.rb | 2 +- spec/support/sqlite3.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/database.yml b/config/database.yml index 97207281..6f94a7ba 100644 --- a/config/database.yml +++ b/config/database.yml @@ -16,4 +16,4 @@ test: database: ':memory:' pool: 5 timeout: 5000 - verbosity: quiet \ No newline at end of file + verbosity: quiet diff --git a/spec/support/casino_core.rb b/spec/support/casino_core.rb index df72acd0..dbddf75d 100644 --- a/spec/support/casino_core.rb +++ b/spec/support/casino_core.rb @@ -3,4 +3,4 @@ CASinoCore.setup ENV['RAILS_ENV'] CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN end -end \ No newline at end of file +end diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 65a2f4c4..78343974 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -11,4 +11,4 @@ config.after(:each) do DatabaseCleaner.clean end -end \ No newline at end of file +end diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index eb84224e..d917df55 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -3,4 +3,4 @@ config.run_all_when_everything_filtered = true config.filter_run focus: true config.order = 'random' -end \ No newline at end of file +end diff --git a/spec/support/sqlite3.rb b/spec/support/sqlite3.rb index 4cd36c9c..4a049aaf 100644 --- a/spec/support/sqlite3.rb +++ b/spec/support/sqlite3.rb @@ -2,4 +2,4 @@ schema_path = File.join(root_path, 'db') CASinoCore.send(:establish_connection, ENV['RAILS_ENV'], root_path) -load File.join(schema_path, 'schema.rb') \ No newline at end of file +load File.join(schema_path, 'schema.rb') From 7904ffb6aa9688593752d47f27894726e5a8d8b3 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 15 Jul 2013 10:05:47 +0200 Subject: [PATCH 347/350] Fix loading of ActiveRecord --- lib/casino_core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 5d455135..926234d7 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -15,6 +15,7 @@ def setup(environment = nil, options = {}) @environment = environment || 'development' root_path = options[:application_root] || '.' + require 'active_record' establish_connection(@environment, root_path) unless active_record_connected? config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys @@ -38,7 +39,6 @@ def active_record_connected? end def establish_connection(env, root_path) - require 'active_record' require 'yaml' db_cfg = YAML::load(ERB.new(IO.read(File.join(root_path, 'config/database.yml'))).result)[env] From fede8270de8a7a9e5193920bc0af28b38e215cab Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 15 Jul 2013 11:31:46 +0200 Subject: [PATCH 348/350] Fix strange ActiveRecord loading error? --- lib/casino_core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core.rb b/lib/casino_core.rb index 926234d7..b566906c 100644 --- a/lib/casino_core.rb +++ b/lib/casino_core.rb @@ -1,4 +1,5 @@ require 'active_support/inflector' +require 'active_record' module CASinoCore autoload :Authenticator, 'casino_core/authenticator.rb' @@ -15,7 +16,6 @@ def setup(environment = nil, options = {}) @environment = environment || 'development' root_path = options[:application_root] || '.' - require 'active_record' establish_connection(@environment, root_path) unless active_record_connected? config = YAML.load_file(File.join(root_path, 'config/cas.yml'))[@environment].symbolize_keys From 1c9c8202522069e05908addc4752b93ce428059a Mon Sep 17 00:00:00 2001 From: Raffael Schmid Date: Mon, 15 Jul 2013 13:59:16 +0200 Subject: [PATCH 349/350] add a test for the service_url_with_ticket --- spec/model/service_ticket_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/model/service_ticket_spec.rb b/spec/model/service_ticket_spec.rb index ce8f1909..6222eb4d 100644 --- a/spec/model/service_ticket_spec.rb +++ b/spec/model/service_ticket_spec.rb @@ -114,4 +114,11 @@ end end end + + describe '#service_with_ticket_url' do + it 'does not escape the url from the database' do + unconsumed_ticket.service = 'https://host.example.org/test.php?t=other&other=testing' + unconsumed_ticket.service_with_ticket_url.should eq('https://host.example.org/test.php?t=other&other=testing&ticket=ST-12345') + end + end end From 4444fe388d8535bacb1011784d8748c2d5b18898 Mon Sep 17 00:00:00 2001 From: Nils Caspar Date: Mon, 15 Jul 2013 19:27:27 +0200 Subject: [PATCH 350/350] Bump version --- lib/casino_core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/casino_core/version.rb b/lib/casino_core/version.rb index de9099b2..69242d68 100644 --- a/lib/casino_core/version.rb +++ b/lib/casino_core/version.rb @@ -1,3 +1,3 @@ module CASinoCore - VERSION = '1.4.3' + VERSION = '1.4.4' end