casino_core 0.0.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/VERSION +1 -1
  2. data/casino_core.gemspec +20 -3
  3. data/config/cas.yml +3 -0
  4. data/db/migrate/20121224113737_create_proxy_tickets.rb +15 -0
  5. data/db/migrate/20121225153637_add_pgt_url_to_proxy_granting_tickets.rb +11 -0
  6. data/db/migrate/20121225231301_proxy_granting_ticket_can_be_granted_by_proxy_ticket.rb +6 -0
  7. data/db/migrate/20121225231713_no_default_granter_type.rb +5 -0
  8. data/db/migrate/20121226192211_fix_index_for_granter_on_proxy_granting_ticket.rb +6 -0
  9. data/db/migrate/20121226211511_allow_service_tickets_without_ticket_granting_ticket.rb +5 -0
  10. data/db/schema.rb +22 -8
  11. data/lib/casino_core/builder.rb +7 -0
  12. data/lib/casino_core/builder/ticket_validation_response.rb +76 -0
  13. data/lib/casino_core/helper.rb +1 -0
  14. data/lib/casino_core/helper/proxy_granting_tickets.rb +29 -22
  15. data/lib/casino_core/helper/proxy_tickets.rb +61 -0
  16. data/lib/casino_core/helper/service_tickets.rb +1 -34
  17. data/lib/casino_core/model.rb +1 -0
  18. data/lib/casino_core/model/proxy_granting_ticket.rb +3 -2
  19. data/lib/casino_core/model/proxy_ticket.rb +27 -0
  20. data/lib/casino_core/model/service_ticket.rb +17 -4
  21. data/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +6 -2
  22. data/lib/casino_core/model/ticket_granting_ticket.rb +21 -0
  23. data/lib/casino_core/processor.rb +2 -0
  24. data/lib/casino_core/processor/legacy_validator.rb +1 -1
  25. data/lib/casino_core/processor/proxy_ticket_provider.rb +44 -0
  26. data/lib/casino_core/processor/proxy_ticket_validator.rb +27 -0
  27. data/lib/casino_core/processor/service_ticket_validator.rb +25 -43
  28. data/lib/casino_core/processor/session_destroyer.rb +3 -0
  29. data/lib/casino_core/settings.rb +1 -1
  30. data/lib/casino_core/tasks/cleanup.rake +12 -2
  31. data/lib/casino_core/tasks/database.rake +3 -2
  32. data/spec/authenticator/base_spec.rb +13 -0
  33. data/spec/model/proxy_ticket_spec.rb +32 -0
  34. data/spec/model/service_ticket_spec.rb +24 -7
  35. data/spec/model/ticket_granting_ticket_spec.rb +35 -0
  36. data/spec/processor/proxy_ticket_provider_spec.rb +75 -0
  37. data/spec/processor/proxy_ticket_validator_spec.rb +66 -0
  38. data/spec/processor/session_destroyer_spec.rb +24 -2
  39. data/spec/processor/ticket_validator_spec.rb +125 -0
  40. metadata +21 -4
  41. data/spec/processor/service_ticket_validator_spec.rb +0 -123
@@ -5,6 +5,7 @@ module CASinoCore
5
5
  autoload :LoginTicket, 'casino_core/model/login_ticket.rb'
6
6
  autoload :ServiceTicket, 'casino_core/model/service_ticket.rb'
7
7
  autoload :ProxyGrantingTicket, 'casino_core/model/proxy_granting_ticket.rb'
8
+ autoload :ProxyTicket, 'casino_core/model/proxy_ticket.rb'
8
9
  autoload :TicketGrantingTicket, 'casino_core/model/ticket_granting_ticket.rb'
9
10
  end
10
11
  end
@@ -1,8 +1,9 @@
1
1
  require 'casino_core/model'
2
2
 
3
3
  class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base
4
- attr_accessible :iou, :ticket, :service_ticket_id
4
+ attr_accessible :iou, :ticket, :pgt_url
5
5
  validates :ticket, uniqueness: true
6
6
  validates :iou, uniqueness: true
7
- belongs_to :service_ticket
7
+ belongs_to :granter, polymorphic: true
8
+ has_many :proxy_tickets, dependent: :destroy
8
9
  end
@@ -0,0 +1,27 @@
1
+ require 'casino_core/model'
2
+ require 'casino_core/settings'
3
+ require 'addressable/uri'
4
+
5
+ class CASinoCore::Model::ProxyTicket < ActiveRecord::Base
6
+ attr_accessible :ticket, :service
7
+ validates :ticket, uniqueness: true
8
+ belongs_to :proxy_granting_ticket
9
+ has_many :proxy_granting_tickets, as: :granter, dependent: :destroy
10
+
11
+ def self.cleanup_unconsumed
12
+ self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed].seconds.ago, false])
13
+ end
14
+
15
+ def self.cleanup_consumed
16
+ self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.proxy_ticket[:lifetime_consumed].seconds.ago, true])
17
+ end
18
+
19
+ def expired?
20
+ lifetime = if consumed?
21
+ CASinoCore::Settings.proxy_ticket[:lifetime_consumed]
22
+ else
23
+ CASinoCore::Settings.proxy_ticket[:lifetime_unconsumed]
24
+ end
25
+ Time.now - self.created_at > lifetime
26
+ end
27
+ end
@@ -8,15 +8,19 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base
8
8
  attr_accessible :ticket, :service
9
9
  validates :ticket, uniqueness: true
10
10
  belongs_to :ticket_granting_ticket
11
- before_destroy :send_single_sing_out_notification
12
- has_many :proxy_granting_tickets
11
+ before_destroy :send_single_sing_out_notification, if: :consumed?
12
+ has_many :proxy_granting_tickets, as: :granter, dependent: :destroy
13
13
 
14
14
  def self.cleanup_unconsumed
15
- self.delete_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false])
15
+ self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false])
16
16
  end
17
17
 
18
18
  def self.cleanup_consumed
19
- self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true]).length
19
+ self.destroy_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds.ago, true])
20
+ end
21
+
22
+ def self.cleanup_consumed_hard
23
+ self.delete_all(['created_at < ? AND consumed = ?', (CASinoCore::Settings.service_ticket[:lifetime_consumed].seconds * 2).ago, true])
20
24
  end
21
25
 
22
26
  def service_with_ticket_url
@@ -25,6 +29,15 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base
25
29
  service_uri.to_s
26
30
  end
27
31
 
32
+ def expired?
33
+ lifetime = if consumed?
34
+ CASinoCore::Settings.service_ticket[:lifetime_consumed]
35
+ else
36
+ CASinoCore::Settings.service_ticket[:lifetime_unconsumed]
37
+ end
38
+ Time.now - self.created_at > lifetime
39
+ end
40
+
28
41
  private
29
42
  def send_single_sing_out_notification
30
43
  notifier = SingleSignOutNotifier.new(self)
@@ -1,15 +1,18 @@
1
1
  require 'builder'
2
2
  require 'net/https'
3
3
  require 'casino_core/model/service_ticket'
4
+ require 'addressable/uri'
4
5
 
5
6
  class CASinoCore::Model::ServiceTicket::SingleSignOutNotifier
7
+ include CASinoCore::Helper::Logger
8
+
6
9
  def initialize(service_ticket)
7
10
  @service_ticket = service_ticket
8
11
  end
9
12
 
10
13
  def notify
11
14
  xml = build_xml
12
- uri = URI.parse(@service_ticket.service)
15
+ uri = Addressable::URI.parse(@service_ticket.service)
13
16
  request = build_request(uri, xml)
14
17
  send_notification(uri, request)
15
18
  end
@@ -25,12 +28,13 @@ class CASinoCore::Model::ServiceTicket::SingleSignOutNotifier
25
28
  end
26
29
 
27
30
  def build_request(uri, xml)
28
- request = Net::HTTP::Post.new(uri.path || '/')
31
+ request = Net::HTTP::Post.new(uri.request_uri)
29
32
  request.set_form_data(logoutRequest: xml)
30
33
  return request
31
34
  end
32
35
 
33
36
  def send_notification(uri, request)
37
+ logger.info "Sending Single Sign Out notification for ticket '#{@service_ticket.ticket}'"
34
38
  begin
35
39
  http = Net::HTTP.new(uri.host, uri.port)
36
40
  http.use_ssl = true if uri.scheme =='https'
@@ -6,6 +6,9 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base
6
6
  validates :ticket, uniqueness: true
7
7
  has_many :service_tickets
8
8
 
9
+ before_destroy :destroy_service_tickets
10
+ after_destroy :destroy_proxy_granting_tickets
11
+
9
12
  def browser_info
10
13
  user_agent = UserAgent.parse(self.user_agent)
11
14
  "#{user_agent.browser} (#{user_agent.platform})"
@@ -18,4 +21,22 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base
18
21
  other_ticket.username == self.username
19
22
  end
20
23
  end
24
+
25
+ private
26
+ def destroy_service_tickets
27
+ self.service_tickets.each do |service_ticket|
28
+ unless service_ticket.destroy
29
+ service_ticket.ticket_granting_ticket_id = nil
30
+ service_ticket.save
31
+ end
32
+ end
33
+ end
34
+
35
+ # Deletes proxy-granting tickets of service tickets that
36
+ # could not be deleted (see #destroy_service_tickets)
37
+ def destroy_proxy_granting_tickets
38
+ self.service_tickets.each do |service_ticket|
39
+ service_ticket.proxy_granting_tickets.destroy_all
40
+ end
41
+ end
21
42
  end
@@ -6,6 +6,8 @@ module CASinoCore
6
6
  autoload :LoginCredentialAcceptor, 'casino_core/processor/login_credential_acceptor.rb'
7
7
  autoload :LoginCredentialRequestor, 'casino_core/processor/login_credential_requestor.rb'
8
8
  autoload :Logout, 'casino_core/processor/logout.rb'
9
+ autoload :ProxyTicketProvider, 'casino_core/processor/proxy_ticket_provider.rb'
10
+ autoload :ProxyTicketValidator, 'casino_core/processor/proxy_ticket_validator.rb'
9
11
  autoload :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb'
10
12
  autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb'
11
13
  autoload :SessionOverview, 'casino_core/processor/session_overview.rb'
@@ -15,7 +15,7 @@ class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor
15
15
  def process(params = nil)
16
16
  params ||= {}
17
17
  ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first
18
- if service_ticket_valid_for_service?(ticket, params[:service], !!params[:renew])
18
+ if !params[:service].nil? && ticket_valid_for_service?(ticket, params[:service], !!params[:renew])
19
19
  @listener.validation_succeeded("yes\n#{ticket.ticket_granting_ticket.username}\n")
20
20
  else
21
21
  @listener.validation_failed("no\n\n")
@@ -0,0 +1,44 @@
1
+ require 'builder'
2
+ require 'casino_core/processor'
3
+ require 'casino_core/helper'
4
+ require 'casino_core/model'
5
+
6
+ # The ProxyTicketProvider processor should be used to handle GET requests to /proxy
7
+ class CASinoCore::Processor::ProxyTicketProvider < CASinoCore::Processor
8
+ include CASinoCore::Helper::ProxyGrantingTickets
9
+ include CASinoCore::Helper::ProxyTickets
10
+
11
+ # This method will call `#request_succeeded` or `#request_failed`. In both cases, it supplies
12
+ # a string as argument. The web application should present that string (and nothing else) to the
13
+ # requestor. The Content-Type should be set to 'text/xml; charset=utf-8'
14
+ #
15
+ # @param [Hash] params parameters delivered by the client
16
+ def process(params = nil)
17
+ if params[:pgt].nil? || params[:targetService].nil?
18
+ @listener.request_failed build_xml false, error_code: 'INVALID_REQUEST', error_message: '"pgt" and "targetService" parameters are both required'
19
+ else
20
+ proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.where(ticket: params[:pgt]).first
21
+ if proxy_granting_ticket.nil?
22
+ @listener.request_failed build_xml false, error_code: 'BAD_PGT', error_message: 'PGT not found'
23
+ else
24
+ proxy_ticket = acquire_proxy_ticket(proxy_granting_ticket, params[:targetService])
25
+ @listener.request_succeeded build_xml true, proxy_ticket: proxy_ticket
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+ def build_xml(success, options = {})
32
+ xml = Builder::XmlMarkup.new(indent: 2)
33
+ xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response|
34
+ if success
35
+ service_response.cas :proxySuccess do |proxy_success|
36
+ proxy_success.cas :proxyTicket, options[:proxy_ticket].ticket
37
+ end
38
+ else
39
+ service_response.cas :proxyFailure, options[:error_message], code: options[:error_code]
40
+ end
41
+ end
42
+ xml.target!
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ require 'builder'
2
+ require 'casino_core/processor'
3
+ require 'casino_core/helper'
4
+ require 'casino_core/model'
5
+
6
+ # The ProxyTicketValidator processor should be used to handle GET requests to /proxyValidate
7
+ class CASinoCore::Processor::ProxyTicketValidator < CASinoCore::Processor::ServiceTicketValidator
8
+
9
+ # This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies
10
+ # a string as argument. The web application should present that string (and nothing else) to the
11
+ # requestor. The Content-Type should be set to 'text/xml; charset=utf-8'
12
+ #
13
+ # @param [Hash] params parameters delivered by the client
14
+ def process(params = nil)
15
+ params ||= {}
16
+ if request_valid?(params)
17
+ ticket = if params[:ticket].start_with?('PT-')
18
+ CASinoCore::Model::ProxyTicket.where(ticket: params[:ticket]).first
19
+ elsif params[:ticket].start_with?('ST-')
20
+ CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first
21
+ else
22
+ nil
23
+ end
24
+ validate_ticket!(ticket, params)
25
+ end
26
+ end
27
+ end
@@ -1,7 +1,7 @@
1
- require 'builder'
2
1
  require 'casino_core/processor'
3
2
  require 'casino_core/helper'
4
3
  require 'casino_core/model'
4
+ require 'casino_core/builder'
5
5
 
6
6
  # The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate
7
7
  class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor
@@ -15,55 +15,37 @@ class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor
15
15
  # @param [Hash] params parameters delivered by the client
16
16
  def process(params = nil)
17
17
  params ||= {}
18
- ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first
19
- validation_result = validate_service_ticket_for_service(ticket, params[:service], !!params[:renew])
20
- if validation_result == true
21
- options = { service_ticket: ticket }
22
- unless params[:pgtUrl].nil?
23
- options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket)
24
- end
25
- @listener.validation_succeeded(build_xml(true, options))
26
- else
27
- @listener.validation_failed(build_xml(false, error_code: validation_result, error_message: 'Validation failed'))
18
+ if request_valid?(params)
19
+ ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first
20
+ validate_ticket!(ticket, params)
28
21
  end
29
22
  end
30
23
 
31
- private
32
- def build_xml(success, options = {})
33
- xml = Builder::XmlMarkup.new(indent: 2)
34
- xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response|
35
- if success
36
- ticket_granting_ticket = options[:service_ticket].ticket_granting_ticket
37
- service_response.cas :authenticationSuccess do |authentication_success|
38
- authentication_success.cas :user, ticket_granting_ticket.username
39
- unless ticket_granting_ticket.extra_attributes.blank?
40
- authentication_success.cas :attributes do |attributes|
41
- ticket_granting_ticket.extra_attributes.each do |key, value|
42
- serialize_extra_attribute(attributes, key, value)
43
- end
44
- end
45
- end
46
- if options[:proxy_granting_ticket]
47
- proxy_granting_ticket = options[:proxy_granting_ticket]
48
- authentication_success.cas :proxyGrantingTicket, proxy_granting_ticket.iou
49
- end
50
- end
51
- else
52
- service_response.cas :authenticationFailure, options[:error_message], code: options[:error_code]
53
- end
54
- end
55
- xml.target!
24
+ protected
25
+ def build_service_response(success, options = {})
26
+ builder = CASinoCore::Builder::TicketValidationResponse.new(success, options)
27
+ builder.build
56
28
  end
57
29
 
58
- def serialize_extra_attribute(builder, key, value)
59
- if value.kind_of?(String) || value.kind_of?(Numeric) || value.kind_of?(Symbol)
60
- builder.cas key, "#{value}"
61
- elsif value.kind_of?(Numeric)
62
- builder.cas key, value.to_s
30
+ def request_valid?(params)
31
+ if params[:ticket].nil? || params[:service].nil?
32
+ @listener.validation_failed build_service_response(false, error_code: 'INVALID_REQUEST', error_message: '"ticket" and "service" parameters are both required')
33
+ false
63
34
  else
64
- builder.cas key do
65
- builder.cdata! value.to_yaml
35
+ true
36
+ end
37
+ end
38
+
39
+ def validate_ticket!(ticket, params)
40
+ validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew])
41
+ if validation_result.success?
42
+ options = { ticket: ticket }
43
+ unless params[:pgtUrl].nil?
44
+ options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket)
66
45
  end
46
+ @listener.validation_succeeded(build_service_response(true, options))
47
+ else
48
+ @listener.validation_failed(build_service_response(false, error_code: validation_result.error_code, error_message: validation_result.error_message))
67
49
  end
68
50
  end
69
51
  end
@@ -9,6 +9,8 @@ require 'casino_core/model'
9
9
  # combination with the {CASinoCore::Processor::SessionOverview} processor.
10
10
  class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor
11
11
 
12
+ include CASinoCore::Helper::Logger
13
+
12
14
  # This method will call `#ticket_not_found` or `#ticket_deleted` on the listener.
13
15
  # @param [Hash] params parameters supplied by user (ID of ticket-granting ticket to delete should by in params[:id])
14
16
  # @param [Hash] cookies cookies supplied by user
@@ -21,6 +23,7 @@ class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor
21
23
  if ticket.nil? || !ticket.same_user?(owner_ticket)
22
24
  @listener.ticket_not_found
23
25
  else
26
+ logger.info "Destroying ticket-granting ticket '#{ticket.ticket}'"
24
27
  ticket.destroy
25
28
  @listener.ticket_deleted
26
29
  end
@@ -3,7 +3,7 @@ require 'casino_core/authenticator'
3
3
  module CASinoCore
4
4
  class Settings
5
5
  class << self
6
- attr_accessor :login_ticket, :service_ticket, :authenticators, :logger
6
+ attr_accessor :login_ticket, :service_ticket, :proxy_ticket, :authenticators, :logger
7
7
  def init(config = {})
8
8
  config.each do |key,value|
9
9
  if respond_to?("#{key}=")
@@ -8,9 +8,19 @@ namespace :casino_core do
8
8
  desc 'Remove expired service tickets.'
9
9
  task service_tickets: 'casino_core:db:configure_connection' do
10
10
  [:consumed, :unconsumed].each do |type|
11
- rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}")
11
+ rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}").length
12
12
  puts "Deleted #{rows_affected} #{type} service tickets."
13
13
  end
14
+ rows_affected = CASinoCore::Model::ServiceTicket.cleanup_consumed_hard
15
+ puts "Force deleted #{rows_affected} consumed service tickets."
16
+ end
17
+
18
+ desc 'Remove expired proxy tickets.'
19
+ task proxy_tickets: 'casino_core:db:configure_connection' do
20
+ [:consumed, :unconsumed].each do |type|
21
+ rows_affected = CASinoCore::Model::ProxyTicket.send("cleanup_#{type}").length
22
+ puts "Deleted #{rows_affected} #{type} proxy tickets."
23
+ end
14
24
  end
15
25
 
16
26
  desc 'Remove expired login tickets.'
@@ -20,7 +30,7 @@ namespace :casino_core do
20
30
  end
21
31
 
22
32
  desc 'Perform all cleanup tasks.'
23
- task all: [:service_tickets, :login_tickets] do
33
+ task all: [:service_tickets, :proxy_tickets, :login_tickets] do
24
34
  end
25
35
  end
26
36
  end
@@ -11,12 +11,13 @@ namespace :casino_core do
11
11
  Gem.loaded_specs['casino_core'].full_gem_path
12
12
  end
13
13
  DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development'
14
- MIGRATIONS_DIR = File.join(BASE_DIR, 'db', 'migrate')
14
+ ActiveRecord::Migrator.migrations_paths = File.join(BASE_DIR, 'db', 'migrate')
15
15
  SCHEMA_PATH = ENV['SCHEMA'] || File.join(BASE_DIR, 'db', 'schema.rb')
16
16
  end
17
17
 
18
18
  task :configuration => :environment do
19
19
  @config = YAML.load_file('config/database.yml')[DATABASE_ENV]
20
+ CASinoCore.setup DATABASE_ENV
20
21
  end
21
22
 
22
23
  task :configure_connection => :configuration do
@@ -33,7 +34,7 @@ namespace :casino_core do
33
34
  end
34
35
 
35
36
  desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
36
- task :rollback => [:environment, :load_config] do
37
+ task :rollback => :configure_connection do
37
38
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
38
39
  ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
39
40
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe CASinoCore::Authenticator do
4
+ subject {
5
+ CASinoCore::Authenticator.new
6
+ }
7
+
8
+ context '#validate' do
9
+ it 'raises an error' do
10
+ expect { subject.validate(nil, nil) }.to raise_error(NotImplementedError)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe CASinoCore::Model::ProxyTicket do
4
+ let(:ticket) {
5
+ ticket = described_class.new ticket: 'PT-12345', service: 'any string is valid'
6
+ ticket.proxy_granting_ticket_id = 1
7
+ ticket
8
+ }
9
+
10
+ describe '.cleanup_unconsumed' do
11
+ it 'deletes expired unconsumed service tickets' do
12
+ ticket.created_at = 10.hours.ago
13
+ ticket.save!
14
+ lambda do
15
+ described_class.cleanup_unconsumed
16
+ end.should change(described_class, :count).by(-1)
17
+ described_class.find_by_ticket('ST-12345').should be_false
18
+ end
19
+ end
20
+
21
+ describe '.cleanup_consumed' do
22
+ it 'deletes expired consumed service tickets' do
23
+ ticket.consumed = true
24
+ ticket.created_at = 10.days.ago
25
+ ticket.save!
26
+ lambda do
27
+ described_class.cleanup_consumed
28
+ end.should change(described_class, :count).by(-1)
29
+ described_class.find_by_ticket('ST-12345').should be_false
30
+ end
31
+ end
32
+ end