casino_core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +48 -0
- data/.travis.yml +3 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +64 -0
- data/LICENSE.txt +20 -0
- data/README.md +19 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/casino_core.gemspec +126 -0
- data/config/cas.yml +26 -0
- data/config/database.yml +18 -0
- data/db/migrate/20121112154930_create_ticket_granting_tickets.rb +11 -0
- data/db/migrate/20121112160009_create_login_tickets.rb +9 -0
- data/db/migrate/20121112165804_ticket_should_not_be_null.rb +5 -0
- data/db/migrate/20121122180310_add_user_agent_to_ticket_granting_tickets.rb +5 -0
- data/db/migrate/20121124170004_add_index_for_username_to_ticket_granting_tickets.rb +5 -0
- data/db/migrate/20121124183542_create_service_tickets.rb +13 -0
- data/db/migrate/20121124183732_add_ticket_indexes.rb +6 -0
- data/db/migrate/20121124195013_add_consumed_to_service_tickets.rb +5 -0
- data/db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb +5 -0
- data/db/migrate/20121125185415_create_proxy_granting_tickets.rb +14 -0
- data/db/migrate/20121125190013_tickets_should_be_unique.rb +8 -0
- data/db/schema.rb +61 -0
- data/lib/casino_core.rb +33 -0
- data/lib/casino_core/authenticator.rb +9 -0
- data/lib/casino_core/authenticator/static.rb +23 -0
- data/lib/casino_core/helper.rb +21 -0
- data/lib/casino_core/helper/browser.rb +16 -0
- data/lib/casino_core/helper/service_tickets.rb +30 -0
- data/lib/casino_core/model.rb +10 -0
- data/lib/casino_core/model/login_ticket.rb +11 -0
- data/lib/casino_core/model/proxy_granting_ticket.rb +8 -0
- data/lib/casino_core/model/service_ticket.rb +32 -0
- data/lib/casino_core/model/service_ticket/single_sign_out_notifier.rb +53 -0
- data/lib/casino_core/model/ticket_granting_ticket.rb +9 -0
- data/lib/casino_core/processor.rb +15 -0
- data/lib/casino_core/processor/legacy_validator.rb +49 -0
- data/lib/casino_core/processor/login_credential_acceptor.rb +67 -0
- data/lib/casino_core/processor/login_credential_requestor.rb +45 -0
- data/lib/casino_core/processor/logout.rb +23 -0
- data/lib/casino_core/processor/session_destroyer.rb +17 -0
- data/lib/casino_core/railtie.rb +10 -0
- data/lib/casino_core/rake_tasks.rb +14 -0
- data/lib/casino_core/settings.rb +26 -0
- data/lib/casino_core/tasks/cleanup.rake +26 -0
- data/lib/casino_core/tasks/database.rake +60 -0
- data/spec/authenticator/static_spec.rb +42 -0
- data/spec/model/login_ticket_spec.rb +16 -0
- data/spec/model/service_ticket_spec.rb +45 -0
- data/spec/processor/legacy_validator_spec.rb +87 -0
- data/spec/processor/login_credential_acceptor_spec.rb +70 -0
- data/spec/processor/login_credential_requestor_spec.rb +75 -0
- data/spec/processor/logout_spec.rb +55 -0
- data/spec/processor/session_destroyer_spec.rb +68 -0
- data/spec/spec_helper.rb +33 -0
- metadata +234 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'casino_core/processor'
|
2
|
+
require 'casino_core/helper'
|
3
|
+
|
4
|
+
class CASinoCore::Processor::LoginCredentialAcceptor < CASinoCore::Processor
|
5
|
+
include CASinoCore::Helper
|
6
|
+
include CASinoCore::Helper::ServiceTickets
|
7
|
+
|
8
|
+
def process(params = nil, cookies = nil, user_agent = nil)
|
9
|
+
params ||= {}
|
10
|
+
cookies ||= {}
|
11
|
+
if login_ticket_valid?(params[:lt])
|
12
|
+
user_data = validate_login_credentials(params[:username], params[:password])
|
13
|
+
if !user_data.nil?
|
14
|
+
ticket_granting_ticket = acquire_ticket_granting_ticket(user_data, user_agent)
|
15
|
+
url = unless params[:service].nil?
|
16
|
+
acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url
|
17
|
+
end
|
18
|
+
@listener.user_logged_in(url, ticket_granting_ticket.ticket)
|
19
|
+
else
|
20
|
+
@listener.invalid_login_credentials
|
21
|
+
end
|
22
|
+
else
|
23
|
+
@listener.invalid_login_ticket
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def login_ticket_valid?(lt)
|
29
|
+
ticket = CASinoCore::Model::LoginTicket.find_by_ticket lt
|
30
|
+
if ticket.nil?
|
31
|
+
logger.info "Login ticket '#{lt}' not found"
|
32
|
+
false
|
33
|
+
elsif ticket.created_at < CASinoCore::Settings.login_ticket[:lifetime].seconds.ago
|
34
|
+
logger.info "Login ticket '#{ticket.ticket}' expired"
|
35
|
+
false
|
36
|
+
else
|
37
|
+
logger.debug "Login ticket '#{ticket.ticket}' successfully validated"
|
38
|
+
ticket.delete
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_login_credentials(username, password)
|
44
|
+
user_data = nil
|
45
|
+
CASinoCore::Settings.authenticators.each do |authenticator|
|
46
|
+
data = authenticator.validate(username, password)
|
47
|
+
if data
|
48
|
+
if data[:username].nil?
|
49
|
+
data[:username] = username
|
50
|
+
end
|
51
|
+
user_data = data
|
52
|
+
logger.info("Credentials for username '#{data[:username]}' successfully validated using #{authenticator.class}")
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
user_data
|
57
|
+
end
|
58
|
+
|
59
|
+
def acquire_ticket_granting_ticket(user_data, user_agent = nil)
|
60
|
+
CASinoCore::Model::TicketGrantingTicket.create!({
|
61
|
+
ticket: random_ticket_string('TGC'),
|
62
|
+
username: user_data[:username],
|
63
|
+
extra_attributes: user_data[:extra_attributes],
|
64
|
+
user_agent: user_agent
|
65
|
+
})
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'casino_core/processor'
|
2
|
+
require 'casino_core/helper'
|
3
|
+
|
4
|
+
class CASinoCore::Processor::LoginCredentialRequestor < CASinoCore::Processor
|
5
|
+
include CASinoCore::Helper
|
6
|
+
include CASinoCore::Helper::ServiceTickets
|
7
|
+
include CASinoCore::Helper::Browser
|
8
|
+
|
9
|
+
def process(params = nil, cookies = nil, user_agent = nil)
|
10
|
+
params ||= {}
|
11
|
+
cookies ||= {}
|
12
|
+
request_env ||= {}
|
13
|
+
if !params[:renew] && (ticket_granting_ticket = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent))
|
14
|
+
# TODO create new service ticket and url
|
15
|
+
service_url_with_ticket = unless params[:service].nil?
|
16
|
+
acquire_service_ticket(ticket_granting_ticket, params[:service], true).service_with_ticket_url
|
17
|
+
end
|
18
|
+
@listener.user_logged_in(service_url_with_ticket)
|
19
|
+
else
|
20
|
+
login_ticket = acquire_login_ticket
|
21
|
+
@listener.user_not_logged_in(login_ticket)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def acquire_login_ticket
|
27
|
+
ticket = CASinoCore::Model::LoginTicket.create ticket: random_ticket_string('LT')
|
28
|
+
logger.debug "Created login ticket '#{ticket.ticket}'"
|
29
|
+
ticket
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_valid_ticket_granting_ticket(tgt, user_agent)
|
33
|
+
ticket_granting_ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first
|
34
|
+
unless ticket_granting_ticket.nil?
|
35
|
+
if same_browser?(ticket_granting_ticket.user_agent, user_agent)
|
36
|
+
ticket_granting_ticket.user_agent = user_agent
|
37
|
+
ticket_granting_ticket.save!
|
38
|
+
ticket_granting_ticket
|
39
|
+
else
|
40
|
+
logger.info 'User-Agent changed: ticket-granting ticket not valid for this browser'
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'casino_core/processor'
|
2
|
+
require 'casino_core/helper'
|
3
|
+
require 'casino_core/model'
|
4
|
+
|
5
|
+
class CASinoCore::Processor::Logout < CASinoCore::Processor
|
6
|
+
include CASinoCore::Helper
|
7
|
+
|
8
|
+
def process(params = nil, cookies = nil)
|
9
|
+
params = params || {}
|
10
|
+
cookies ||= {}
|
11
|
+
session_destroyer = CASinoCore::Processor::SessionDestroyer.new(DummyListener.new)
|
12
|
+
session_destroyer.process(cookies[:tgt])
|
13
|
+
@listener.user_logged_out(params[:url])
|
14
|
+
end
|
15
|
+
|
16
|
+
class DummyListener
|
17
|
+
def ticket_deleted(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ticket_not_found(*args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'casino_core/processor'
|
2
|
+
require 'casino_core/helper'
|
3
|
+
require 'casino_core/model'
|
4
|
+
|
5
|
+
class CASinoCore::Processor::SessionDestroyer < CASinoCore::Processor
|
6
|
+
include CASinoCore::Helper
|
7
|
+
|
8
|
+
def process(tgt)
|
9
|
+
ticket = CASinoCore::Model::TicketGrantingTicket.where(ticket: tgt).first
|
10
|
+
if ticket.nil?
|
11
|
+
@listener.ticket_not_found
|
12
|
+
else
|
13
|
+
ticket.destroy
|
14
|
+
@listener.ticket_deleted
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'casino_core/authenticator'
|
2
|
+
|
3
|
+
module CASinoCore
|
4
|
+
class Settings
|
5
|
+
class << self
|
6
|
+
attr_accessor :login_ticket, :service_ticket, :authenticators
|
7
|
+
def init(config = {})
|
8
|
+
config.each do |key,value|
|
9
|
+
if respond_to?("#{key}=")
|
10
|
+
send("#{key}=", value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def authenticators=(authenticators)
|
16
|
+
@authenticators = []
|
17
|
+
authenticators.each do |authenticator|
|
18
|
+
unless authenticator.is_a?(CASinoCore::Authenticator)
|
19
|
+
authenticator = authenticator[:class].constantize.new(authenticator[:options])
|
20
|
+
end
|
21
|
+
@authenticators << authenticator
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'logger'
|
3
|
+
require 'active_record'
|
4
|
+
require 'casino_core/model'
|
5
|
+
|
6
|
+
namespace :casino_core do
|
7
|
+
namespace :cleanup do
|
8
|
+
desc 'Remove expired service tickets.'
|
9
|
+
task service_tickets: 'casino_core:db:configure_connection' do
|
10
|
+
[:consumed, :unconsumed].each do |type|
|
11
|
+
rows_affected = CASinoCore::Model::ServiceTicket.send("cleanup_#{type}")
|
12
|
+
puts "Deleted #{rows_affected} #{type} service tickets."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Remove expired login tickets.'
|
17
|
+
task login_tickets: 'casino_core:db:configure_connection' do
|
18
|
+
rows_affected = CASinoCore::Model::LoginTicket.cleanup
|
19
|
+
puts "Deleted #{rows_affected} login tickets."
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Perform all cleanup tasks.'
|
23
|
+
task all: [:service_tickets, :login_tickets] do
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'logger'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
namespace :casino_core do
|
6
|
+
namespace :db do
|
7
|
+
task :environment do
|
8
|
+
BASE_DIR = if Gem.loaded_specs['casino_core'].nil?
|
9
|
+
Dir.pwd
|
10
|
+
else
|
11
|
+
Gem.loaded_specs['casino_core'].full_gem_path
|
12
|
+
end
|
13
|
+
DATABASE_ENV = ENV['DATABASE_ENV'] || ENV['RAILS_ENV'] || 'development'
|
14
|
+
MIGRATIONS_DIR = File.join(BASE_DIR, 'db', 'migrate')
|
15
|
+
SCHEMA_PATH = ENV['SCHEMA'] || File.join(BASE_DIR, 'db', 'schema.rb')
|
16
|
+
end
|
17
|
+
|
18
|
+
task :configuration => :environment do
|
19
|
+
@config = YAML.load_file('config/database.yml')[DATABASE_ENV]
|
20
|
+
end
|
21
|
+
|
22
|
+
task :configure_connection => :configuration do
|
23
|
+
ActiveRecord::Base.establish_connection @config
|
24
|
+
ActiveRecord::Base.logger = Logger.new STDOUT if @config['logger']
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Migrate the database (options: VERSION=x, VERBOSE=false)'
|
28
|
+
task :migrate => :configure_connection do
|
29
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
30
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
31
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
36
|
+
task :rollback => [:environment, :load_config] do
|
37
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
38
|
+
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :schema do
|
42
|
+
desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
|
43
|
+
task :dump => :configure_connection do
|
44
|
+
require 'active_record/schema_dumper'
|
45
|
+
File.open(SCHEMA_PATH, "w:utf-8") do |file|
|
46
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'Load a schema.rb file into the database'
|
51
|
+
task :load => :configure_connection do
|
52
|
+
if File.exists?(SCHEMA_PATH)
|
53
|
+
load(SCHEMA_PATH)
|
54
|
+
else
|
55
|
+
abort %{#{SCHEMA_PATH} doesn't exist yet.}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASinoCore::Authenticator::Static do
|
4
|
+
subject {
|
5
|
+
CASinoCore::Authenticator::Static.new({
|
6
|
+
users: {
|
7
|
+
user: {
|
8
|
+
password: 'testing123',
|
9
|
+
fullname: 'Example User'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
})
|
13
|
+
}
|
14
|
+
|
15
|
+
context '#validate' do
|
16
|
+
context 'with invalid credentials' do
|
17
|
+
it 'returns false for an unknown username' do
|
18
|
+
subject.validate('foobar', 'test').should == false
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns false for a known username with wrong password' do
|
22
|
+
subject.validate('user', 'test').should == false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with valid credentials' do
|
27
|
+
let(:result) { subject.validate('user', 'testing123') }
|
28
|
+
|
29
|
+
it 'does not return false' do
|
30
|
+
result.should_not == false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns the username' do
|
34
|
+
result[:username].should == 'user'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns extra attributes' do
|
38
|
+
result[:extra_attributes][:fullname].should == 'Example User'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASinoCore::Model::LoginTicket do
|
4
|
+
describe '.cleanup' do
|
5
|
+
it 'deletes expired login tickets' do
|
6
|
+
ticket = described_class.new ticket: 'LT-12345'
|
7
|
+
ticket.save!
|
8
|
+
ticket.created_at = 10.hours.ago
|
9
|
+
ticket.save!
|
10
|
+
lambda do
|
11
|
+
described_class.cleanup
|
12
|
+
end.should change(described_class, :count).by(-1)
|
13
|
+
described_class.find_by_ticket('LT-12345').should be_false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASinoCore::Model::ServiceTicket do
|
4
|
+
let(:ticket) {
|
5
|
+
ticket = described_class.new ticket: 'ST-12345', service: 'https://example.com/cas-service'
|
6
|
+
ticket.ticket_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
|
+
before(:each) do
|
23
|
+
described_class::SingleSignOutNotifier.any_instance.stub(:notify).and_return(true)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'deletes expired consumed service tickets' do
|
27
|
+
ticket.consumed = true
|
28
|
+
ticket.created_at = 10.days.ago
|
29
|
+
ticket.save!
|
30
|
+
lambda do
|
31
|
+
described_class.cleanup_consumed
|
32
|
+
end.should change(described_class, :count).by(-1)
|
33
|
+
described_class.find_by_ticket('ST-12345').should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.destroy' do
|
38
|
+
it 'sends out a single sign out notification' do
|
39
|
+
described_class::SingleSignOutNotifier.any_instance.should_receive(:notify).and_return(true)
|
40
|
+
ticket.consumed = true
|
41
|
+
ticket.save!
|
42
|
+
ticket.destroy
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASinoCore::Processor::LegacyValidator do
|
4
|
+
describe '#process' do
|
5
|
+
let(:listener) { Object.new }
|
6
|
+
let(:processor) { described_class.new(listener) }
|
7
|
+
let(:user_agent) { 'TestBrowser 1.0' }
|
8
|
+
let(:ticket_granting_ticket) {
|
9
|
+
CASinoCore::Model::TicketGrantingTicket.create!({
|
10
|
+
ticket: 'TGC-HXdkW233TsRtiqYGq4b8U7',
|
11
|
+
username: 'test',
|
12
|
+
extra_attributes: nil,
|
13
|
+
user_agent: user_agent
|
14
|
+
})
|
15
|
+
}
|
16
|
+
let(:service) { 'https://example.com/cas-service' }
|
17
|
+
let(:service_ticket) { ticket_granting_ticket.service_tickets.create! ticket: 'ST-2nOcXx56dtPTsB069yYf0h', service: service }
|
18
|
+
let(:parameters) { { service: service, ticket: service_ticket.ticket }}
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
listener.stub(:validation_failed)
|
22
|
+
listener.stub(:validation_succeeded)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with an unconsumed service ticket' do
|
26
|
+
context 'without renew flag' do
|
27
|
+
it 'consumes the service ticket' do
|
28
|
+
processor.process(parameters)
|
29
|
+
service_ticket.reload
|
30
|
+
service_ticket.consumed.should == true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'calls the #validation_succeeded method on the listener' do
|
34
|
+
listener.should_receive(:validation_succeeded).with("yes\ntest\n")
|
35
|
+
processor.process(parameters)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with renew flag' do
|
40
|
+
let(:parameters) { { service: service, ticket: service_ticket.ticket, renew: 'true' }}
|
41
|
+
|
42
|
+
context 'with a service ticket without issued_from_credentials flag' do
|
43
|
+
it 'consumes the service ticket' do
|
44
|
+
processor.process(parameters)
|
45
|
+
service_ticket.reload
|
46
|
+
service_ticket.consumed.should == true
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'calls the #validation_failed method on the listener' do
|
50
|
+
listener.should_receive(:validation_failed).with("no\n\n")
|
51
|
+
processor.process(parameters)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with a service ticket with issued_from_credentials flag' do
|
56
|
+
before(:each) do
|
57
|
+
service_ticket.issued_from_credentials = true
|
58
|
+
service_ticket.save!
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'consumes the service ticket' do
|
62
|
+
processor.process(parameters)
|
63
|
+
service_ticket.reload
|
64
|
+
service_ticket.consumed.should == true
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'calls the #validation_succeeded method on the listener' do
|
68
|
+
listener.should_receive(:validation_succeeded).with("yes\ntest\n")
|
69
|
+
processor.process(parameters)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'with a consumed service ticket' do
|
76
|
+
before(:each) do
|
77
|
+
service_ticket.consumed = true
|
78
|
+
service_ticket.save!
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'calls the #validation_failed method on the listener' do
|
82
|
+
listener.should_receive(:validation_failed).with("no\n\n")
|
83
|
+
processor.process(parameters)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|