casino_core 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -5
- data/VERSION +1 -1
- data/casino_core.gemspec +9 -2
- data/config/cas.yml +4 -2
- data/db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb +14 -0
- data/db/schema.rb +7 -7
- data/lib/casino_core/helper/logger.rb +3 -4
- data/lib/casino_core/helper/proxy_granting_tickets.rb +45 -0
- data/lib/casino_core/helper/service_tickets.rb +34 -0
- data/lib/casino_core/helper.rb +1 -0
- data/lib/casino_core/model/proxy_granting_ticket.rb +2 -2
- data/lib/casino_core/model/service_ticket.rb +1 -0
- data/lib/casino_core/model/ticket_granting_ticket.rb +0 -1
- data/lib/casino_core/processor/legacy_validator.rb +2 -33
- data/lib/casino_core/processor/service_ticket_validator.rb +69 -0
- data/lib/casino_core/processor.rb +1 -0
- data/lib/casino_core/railtie.rb +5 -1
- data/lib/casino_core/settings.rb +5 -1
- data/spec/model/login_ticket_spec.rb +7 -0
- data/spec/processor/legacy_validator_spec.rb +5 -5
- data/spec/processor/service_ticket_validator_spec.rb +123 -0
- data/spec/spec_helper.rb +7 -1
- metadata +18 -3
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -15,6 +15,7 @@ GEM
|
|
15
15
|
addressable (2.3.2)
|
16
16
|
arel (3.0.2)
|
17
17
|
builder (3.0.4)
|
18
|
+
crack (0.3.1)
|
18
19
|
database_cleaner (0.9.1)
|
19
20
|
diff-lcs (1.1.3)
|
20
21
|
git (1.2.5)
|
@@ -25,8 +26,8 @@ GEM
|
|
25
26
|
rake
|
26
27
|
rdoc
|
27
28
|
json (1.7.5)
|
28
|
-
multi_json (1.
|
29
|
-
rake (10.0.
|
29
|
+
multi_json (1.5.0)
|
30
|
+
rake (10.0.3)
|
30
31
|
rdoc (3.12)
|
31
32
|
json (~> 1.4)
|
32
33
|
redcarpet (2.2.2)
|
@@ -34,10 +35,10 @@ GEM
|
|
34
35
|
rspec-core (~> 2.12.0)
|
35
36
|
rspec-expectations (~> 2.12.0)
|
36
37
|
rspec-mocks (~> 2.12.0)
|
37
|
-
rspec-core (2.12.
|
38
|
-
rspec-expectations (2.12.
|
38
|
+
rspec-core (2.12.2)
|
39
|
+
rspec-expectations (2.12.1)
|
39
40
|
diff-lcs (~> 1.1.3)
|
40
|
-
rspec-mocks (2.12.
|
41
|
+
rspec-mocks (2.12.1)
|
41
42
|
simplecov (0.7.1)
|
42
43
|
multi_json (~> 1.0)
|
43
44
|
simplecov-html (~> 0.7.1)
|
@@ -45,6 +46,9 @@ GEM
|
|
45
46
|
sqlite3 (1.3.6)
|
46
47
|
tzinfo (0.3.35)
|
47
48
|
useragent (0.4.15)
|
49
|
+
webmock (1.9.0)
|
50
|
+
addressable (>= 2.2.7)
|
51
|
+
crack (>= 0.1.7)
|
48
52
|
yard (0.8.3)
|
49
53
|
|
50
54
|
PLATFORMS
|
@@ -61,4 +65,5 @@ DEPENDENCIES
|
|
61
65
|
simplecov (~> 0.7.1)
|
62
66
|
sqlite3
|
63
67
|
useragent (~> 0.4.13)
|
68
|
+
webmock
|
64
69
|
yard (~> 0.8.3)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/casino_core.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "casino_core"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nils Caspar"]
|
12
|
-
s.date = "2012-12-
|
12
|
+
s.date = "2012-12-24"
|
13
13
|
s.description = "A CAS server core library."
|
14
14
|
s.email = "ncaspar@me.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
"db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb",
|
42
42
|
"db/migrate/20121125185415_create_proxy_granting_tickets.rb",
|
43
43
|
"db/migrate/20121125190013_tickets_should_be_unique.rb",
|
44
|
+
"db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb",
|
44
45
|
"db/schema.rb",
|
45
46
|
"lib/casino_core.rb",
|
46
47
|
"lib/casino_core/authenticator.rb",
|
@@ -49,6 +50,7 @@ Gem::Specification.new do |s|
|
|
49
50
|
"lib/casino_core/helper/browser.rb",
|
50
51
|
"lib/casino_core/helper/logger.rb",
|
51
52
|
"lib/casino_core/helper/login_tickets.rb",
|
53
|
+
"lib/casino_core/helper/proxy_granting_tickets.rb",
|
52
54
|
"lib/casino_core/helper/service_tickets.rb",
|
53
55
|
"lib/casino_core/helper/ticket_granting_tickets.rb",
|
54
56
|
"lib/casino_core/helper/tickets.rb",
|
@@ -63,6 +65,7 @@ Gem::Specification.new do |s|
|
|
63
65
|
"lib/casino_core/processor/login_credential_acceptor.rb",
|
64
66
|
"lib/casino_core/processor/login_credential_requestor.rb",
|
65
67
|
"lib/casino_core/processor/logout.rb",
|
68
|
+
"lib/casino_core/processor/service_ticket_validator.rb",
|
66
69
|
"lib/casino_core/processor/session_destroyer.rb",
|
67
70
|
"lib/casino_core/processor/session_overview.rb",
|
68
71
|
"lib/casino_core/railtie.rb",
|
@@ -77,6 +80,7 @@ Gem::Specification.new do |s|
|
|
77
80
|
"spec/processor/login_credential_acceptor_spec.rb",
|
78
81
|
"spec/processor/login_credential_requestor_spec.rb",
|
79
82
|
"spec/processor/logout_spec.rb",
|
83
|
+
"spec/processor/service_ticket_validator_spec.rb",
|
80
84
|
"spec/processor/session_destroyer_spec.rb",
|
81
85
|
"spec/processor/session_overview_spec.rb",
|
82
86
|
"spec/spec_helper.rb"
|
@@ -102,6 +106,7 @@ Gem::Specification.new do |s|
|
|
102
106
|
s.add_development_dependency(%q<simplecov>, ["~> 0.7.1"])
|
103
107
|
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
104
108
|
s.add_development_dependency(%q<database_cleaner>, [">= 0"])
|
109
|
+
s.add_development_dependency(%q<webmock>, [">= 0"])
|
105
110
|
else
|
106
111
|
s.add_dependency(%q<activerecord>, ["~> 3.2.9"])
|
107
112
|
s.add_dependency(%q<addressable>, ["~> 2.3.2"])
|
@@ -114,6 +119,7 @@ Gem::Specification.new do |s|
|
|
114
119
|
s.add_dependency(%q<simplecov>, ["~> 0.7.1"])
|
115
120
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
116
121
|
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
122
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
117
123
|
end
|
118
124
|
else
|
119
125
|
s.add_dependency(%q<activerecord>, ["~> 3.2.9"])
|
@@ -127,6 +133,7 @@ Gem::Specification.new do |s|
|
|
127
133
|
s.add_dependency(%q<simplecov>, ["~> 0.7.1"])
|
128
134
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
129
135
|
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
136
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
130
137
|
end
|
131
138
|
end
|
132
139
|
|
data/config/cas.yml
CHANGED
@@ -13,7 +13,8 @@ development:
|
|
13
13
|
options:
|
14
14
|
users:
|
15
15
|
testuser:
|
16
|
-
password: foobar123
|
16
|
+
password: "foobar123"
|
17
|
+
name: "Test User"
|
17
18
|
|
18
19
|
test:
|
19
20
|
<<: *defaults
|
@@ -23,4 +24,5 @@ test:
|
|
23
24
|
options:
|
24
25
|
users:
|
25
26
|
testuser:
|
26
|
-
password: foobar123
|
27
|
+
password: "foobar123"
|
28
|
+
name: "Test User"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'casino_core/model'
|
2
|
+
|
3
|
+
class ProxyGrantingTicketsBelongsToServiceTicket < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
CASinoCore::Model::ProxyGrantingTicket.delete_all
|
6
|
+
|
7
|
+
remove_index :proxy_granting_tickets, :ticket_granting_ticket_id
|
8
|
+
remove_column :proxy_granting_tickets, :ticket_granting_ticket_id
|
9
|
+
|
10
|
+
add_column :proxy_granting_tickets, :service_ticket_id, :integer
|
11
|
+
change_column :proxy_granting_tickets, :service_ticket_id, :integer, null: false
|
12
|
+
add_index :proxy_granting_tickets, :service_ticket_id
|
13
|
+
end
|
14
|
+
end
|
data/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20121223135227) do
|
15
15
|
|
16
16
|
create_table "login_tickets", :force => true do |t|
|
17
17
|
t.string "ticket", :null => false
|
@@ -22,16 +22,16 @@ ActiveRecord::Schema.define(:version => 20121125190013) do
|
|
22
22
|
add_index "login_tickets", ["ticket"], :name => "index_login_tickets_on_ticket", :unique => true
|
23
23
|
|
24
24
|
create_table "proxy_granting_tickets", :force => true do |t|
|
25
|
-
t.string "ticket",
|
26
|
-
t.string "iou",
|
27
|
-
t.
|
28
|
-
t.datetime "
|
29
|
-
t.
|
25
|
+
t.string "ticket", :null => false
|
26
|
+
t.string "iou", :null => false
|
27
|
+
t.datetime "created_at", :null => false
|
28
|
+
t.datetime "updated_at", :null => false
|
29
|
+
t.integer "service_ticket_id", :null => false
|
30
30
|
end
|
31
31
|
|
32
32
|
add_index "proxy_granting_tickets", ["iou"], :name => "index_proxy_granting_tickets_on_iou", :unique => true
|
33
|
+
add_index "proxy_granting_tickets", ["service_ticket_id"], :name => "index_proxy_granting_tickets_on_service_ticket_id"
|
33
34
|
add_index "proxy_granting_tickets", ["ticket"], :name => "index_proxy_granting_tickets_on_ticket", :unique => true
|
34
|
-
add_index "proxy_granting_tickets", ["ticket_granting_ticket_id"], :name => "index_proxy_granting_tickets_on_ticket_granting_ticket_id"
|
35
35
|
|
36
36
|
create_table "service_tickets", :force => true do |t|
|
37
37
|
t.string "ticket", :null => false
|
@@ -1,11 +1,10 @@
|
|
1
|
+
require 'casino_core/settings'
|
2
|
+
|
1
3
|
module CASinoCore
|
2
4
|
module Helper
|
3
5
|
module Logger
|
4
6
|
def logger
|
5
|
-
|
6
|
-
logger = ::Logger.new(STDOUT)
|
7
|
-
logger.level = ::Logger::Severity::UNKNOWN
|
8
|
-
logger
|
7
|
+
CASinoCore::Settings.logger
|
9
8
|
end
|
10
9
|
end
|
11
10
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
require 'casino_core/helper/logger'
|
5
|
+
require 'casino_core/helper/tickets'
|
6
|
+
|
7
|
+
module CASinoCore
|
8
|
+
module Helper
|
9
|
+
module ProxyGrantingTickets
|
10
|
+
include CASinoCore::Helper::Logger
|
11
|
+
include CASinoCore::Helper::Tickets
|
12
|
+
|
13
|
+
def acquire_proxy_granting_ticket(pgt_url, service_ticket)
|
14
|
+
begin
|
15
|
+
uri = Addressable::URI.parse(pgt_url)
|
16
|
+
https = Net::HTTP.new(uri.host, uri.port || 443)
|
17
|
+
https.use_ssl = true
|
18
|
+
|
19
|
+
https.start do |conn|
|
20
|
+
pgt = service_ticket.proxy_granting_tickets.new({
|
21
|
+
ticket: random_ticket_string('PGT'),
|
22
|
+
iou: random_ticket_string('PGTIOU')
|
23
|
+
})
|
24
|
+
|
25
|
+
uri.query_values = (uri.query_values || {}).merge(pgtId: pgt.ticket, pgtIou: pgt.iou)
|
26
|
+
|
27
|
+
response = conn.request_get(uri.request_uri)
|
28
|
+
# TODO: follow redirects... 2.5.4 says that redirects MAY be followed
|
29
|
+
if "#{response.code}" == "200"
|
30
|
+
# 3.4 (proxy-granting ticket IOU)
|
31
|
+
pgt.save!
|
32
|
+
logger.debug "Proxy-granting ticket generated for pgt_url '#{pgt_url}': #{pgt.inspect}"
|
33
|
+
return pgt
|
34
|
+
else
|
35
|
+
logger.warn "Proxy-granting ticket callback server responded with a bad result code '#{response.code}'. PGT will not be stored."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
|
39
|
+
logger.warn "Exception while communication with proxy-granting ticket callback server: #{e.message}"
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -26,6 +26,40 @@ module CASinoCore
|
|
26
26
|
|
27
27
|
clean_service
|
28
28
|
end
|
29
|
+
|
30
|
+
def validate_service_ticket_for_service(ticket, service, renew = false)
|
31
|
+
result = if service.nil? or ticket.nil?
|
32
|
+
logger.warn 'Invalid validate request: no valid ticket or no valid service given'
|
33
|
+
'INVALID_REQUEST'
|
34
|
+
else
|
35
|
+
if ticket.consumed?
|
36
|
+
logger.warn "Service ticket '#{ticket.ticket}' already consumed"
|
37
|
+
'INVALID_TICKET'
|
38
|
+
elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed]
|
39
|
+
logger.warn "Service ticket '#{ticket.ticket}' has expired"
|
40
|
+
'INVALID_TICKET'
|
41
|
+
elsif clean_service_url(service) != ticket.service
|
42
|
+
logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'"
|
43
|
+
'INVALID_SERVICE'
|
44
|
+
elsif renew && !ticket.issued_from_credentials?
|
45
|
+
logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket"
|
46
|
+
'INVALID_TICKET'
|
47
|
+
else
|
48
|
+
logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated"
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
unless ticket.nil?
|
53
|
+
logger.debug "Consumed ticket '#{ticket.ticket}'"
|
54
|
+
ticket.consumed = true
|
55
|
+
ticket.save!
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def service_ticket_valid_for_service?(ticket, service, renew = false)
|
61
|
+
validate_service_ticket_for_service(ticket, service, renew) == true
|
62
|
+
end
|
29
63
|
end
|
30
64
|
end
|
31
65
|
end
|
data/lib/casino_core/helper.rb
CHANGED
@@ -6,6 +6,7 @@ module CASinoCore
|
|
6
6
|
autoload :Browser, 'casino_core/helper/browser.rb'
|
7
7
|
autoload :Logger, 'casino_core/helper/logger.rb'
|
8
8
|
autoload :LoginTickets, 'casino_core/helper/login_tickets.rb'
|
9
|
+
autoload :ProxyGrantingTickets, 'casino_core/helper/proxy_granting_tickets.rb'
|
9
10
|
autoload :ServiceTickets, 'casino_core/helper/service_tickets.rb'
|
10
11
|
autoload :Tickets, 'casino_core/helper/tickets.rb'
|
11
12
|
autoload :TicketGrantingTickets, 'casino_core/helper/ticket_granting_tickets.rb'
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'casino_core/model'
|
2
2
|
|
3
3
|
class CASinoCore::Model::ProxyGrantingTicket < ActiveRecord::Base
|
4
|
-
attr_accessible :iou, :ticket, :
|
4
|
+
attr_accessible :iou, :ticket, :service_ticket_id
|
5
5
|
validates :ticket, uniqueness: true
|
6
6
|
validates :iou, uniqueness: true
|
7
|
-
belongs_to :
|
7
|
+
belongs_to :service_ticket
|
8
8
|
end
|
@@ -9,6 +9,7 @@ class CASinoCore::Model::ServiceTicket < ActiveRecord::Base
|
|
9
9
|
validates :ticket, uniqueness: true
|
10
10
|
belongs_to :ticket_granting_ticket
|
11
11
|
before_destroy :send_single_sing_out_notification
|
12
|
+
has_many :proxy_granting_tickets
|
12
13
|
|
13
14
|
def self.cleanup_unconsumed
|
14
15
|
self.delete_all(['created_at < ? AND consumed = ?', CASinoCore::Settings.service_ticket[:lifetime_unconsumed].seconds.ago, false])
|
@@ -5,7 +5,6 @@ class CASinoCore::Model::TicketGrantingTicket < ActiveRecord::Base
|
|
5
5
|
serialize :extra_attributes, Hash
|
6
6
|
validates :ticket, uniqueness: true
|
7
7
|
has_many :service_tickets
|
8
|
-
has_many :proxy_granting_tickets
|
9
8
|
|
10
9
|
def browser_info
|
11
10
|
user_agent = UserAgent.parse(self.user_agent)
|
@@ -8,48 +8,17 @@ class CASinoCore::Processor::LegacyValidator < CASinoCore::Processor
|
|
8
8
|
include CASinoCore::Helper::ServiceTickets
|
9
9
|
|
10
10
|
# This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies
|
11
|
-
# a string as argument. The
|
11
|
+
# a string as argument. The web application should present that string (and nothing else) to the
|
12
12
|
# requestor.
|
13
13
|
#
|
14
14
|
# @param [Hash] params parameters supplied by requestor (a service)
|
15
15
|
def process(params = nil)
|
16
16
|
params ||= {}
|
17
17
|
ticket = CASinoCore::Model::ServiceTicket.where(ticket: params[:ticket]).first
|
18
|
-
if
|
18
|
+
if service_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")
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def ticket_valid_for_service?(ticket, service, renew = false)
|
27
|
-
ticket_valid = if service.nil? or ticket.nil?
|
28
|
-
logger.warn 'Invalid validate request: no valid ticket or no valid service given'
|
29
|
-
false
|
30
|
-
else
|
31
|
-
if ticket.consumed?
|
32
|
-
logger.warn "Service ticket '#{ticket.ticket}' already consumed"
|
33
|
-
false
|
34
|
-
elsif Time.now - ticket.created_at > CASinoCore::Settings.service_ticket[:lifetime_unconsumed]
|
35
|
-
logger.warn "Service ticket '#{ticket.ticket}' has expired"
|
36
|
-
false
|
37
|
-
elsif clean_service_url(service) != ticket.service
|
38
|
-
logger.warn "Service ticket '#{ticket.ticket}' is not valid for service '#{service}'"
|
39
|
-
false
|
40
|
-
elsif renew && !ticket.issued_from_credentials?
|
41
|
-
logger.info "Service ticket '#{ticket.ticket}' was not issued from credentials but service '#{service}' will only accept a renewed ticket"
|
42
|
-
false
|
43
|
-
else
|
44
|
-
logger.info "Service ticket '#{ticket.ticket}' for service '#{service}' successfully validated"
|
45
|
-
true
|
46
|
-
end
|
47
|
-
end
|
48
|
-
unless ticket.nil?
|
49
|
-
logger.debug "Consumed ticket '#{ticket.ticket}'"
|
50
|
-
ticket.consumed = true
|
51
|
-
ticket.save!
|
52
|
-
end
|
53
|
-
ticket_valid
|
54
|
-
end
|
55
24
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'casino_core/processor'
|
3
|
+
require 'casino_core/helper'
|
4
|
+
require 'casino_core/model'
|
5
|
+
|
6
|
+
# The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate
|
7
|
+
class CASinoCore::Processor::ServiceTicketValidator < CASinoCore::Processor
|
8
|
+
include CASinoCore::Helper::ServiceTickets
|
9
|
+
include CASinoCore::Helper::ProxyGrantingTickets
|
10
|
+
|
11
|
+
# This method will call `#validation_succeeded` or `#validation_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
|
+
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'))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
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!
|
56
|
+
end
|
57
|
+
|
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
|
63
|
+
else
|
64
|
+
builder.cas key do
|
65
|
+
builder.cdata! value.to_yaml
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -6,6 +6,7 @@ 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 :ServiceTicketValidator, 'casino_core/processor/service_ticket_validator.rb'
|
9
10
|
autoload :SessionDestroyer, 'casino_core/processor/session_destroyer.rb'
|
10
11
|
autoload :SessionOverview, 'casino_core/processor/session_overview.rb'
|
11
12
|
|
data/lib/casino_core/railtie.rb
CHANGED
@@ -7,8 +7,12 @@ module CASinoCore
|
|
7
7
|
CASinoCore::RakeTasks.load_tasks
|
8
8
|
end
|
9
9
|
|
10
|
-
initializer
|
10
|
+
initializer 'casino_core.load_configuration' do
|
11
11
|
CASinoCore.setup Rails.env
|
12
12
|
end
|
13
|
+
|
14
|
+
initializer 'casino_core.setup_logger' do
|
15
|
+
CASinoCore::Settings.logger = Rails.logger
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
data/lib/casino_core/settings.rb
CHANGED
@@ -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
|
6
|
+
attr_accessor :login_ticket, :service_ticket, :authenticators, :logger
|
7
7
|
def init(config = {})
|
8
8
|
config.each do |key,value|
|
9
9
|
if respond_to?("#{key}=")
|
@@ -12,6 +12,10 @@ module CASinoCore
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def logger
|
16
|
+
@logger ||= ::Logger.new(STDOUT)
|
17
|
+
end
|
18
|
+
|
15
19
|
def authenticators=(authenticators)
|
16
20
|
@authenticators = []
|
17
21
|
authenticators.each do |authenticator|
|
@@ -13,4 +13,11 @@ describe CASinoCore::Model::LoginTicket do
|
|
13
13
|
described_class.find_by_ticket('LT-12345').should be_false
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
describe '#to_s' do
|
18
|
+
it 'returns the ticket identifier' do
|
19
|
+
ticket = described_class.new ticket: 'LT-12345'
|
20
|
+
"#{ticket}".should == ticket.ticket
|
21
|
+
end
|
22
|
+
end
|
16
23
|
end
|
@@ -37,18 +37,18 @@ describe CASinoCore::Processor::LegacyValidator do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context 'with renew flag' do
|
40
|
-
let(:
|
40
|
+
let(:parameters_with_renew) { parameters.merge renew: 'true' }
|
41
41
|
|
42
42
|
context 'with a service ticket without issued_from_credentials flag' do
|
43
43
|
it 'consumes the service ticket' do
|
44
|
-
processor.process(
|
44
|
+
processor.process(parameters_with_renew)
|
45
45
|
service_ticket.reload
|
46
46
|
service_ticket.consumed.should == true
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'calls the #validation_failed method on the listener' do
|
50
50
|
listener.should_receive(:validation_failed).with("no\n\n")
|
51
|
-
processor.process(
|
51
|
+
processor.process(parameters_with_renew)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -59,14 +59,14 @@ describe CASinoCore::Processor::LegacyValidator do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'consumes the service ticket' do
|
62
|
-
processor.process(
|
62
|
+
processor.process(parameters_with_renew)
|
63
63
|
service_ticket.reload
|
64
64
|
service_ticket.consumed.should == true
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'calls the #validation_succeeded method on the listener' do
|
68
68
|
listener.should_receive(:validation_succeeded).with("yes\ntest\n")
|
69
|
-
processor.process(
|
69
|
+
processor.process(parameters_with_renew)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CASinoCore::Processor::ServiceTicketValidator 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
|
+
let(:regex_failure) { /\A\<cas\:serviceResponse.*\n.*authenticationFailure/ }
|
21
|
+
let(:regex_success) { /\A\<cas\:serviceResponse.*\n.*authenticationSuccess/ }
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
listener.stub(:validation_failed)
|
25
|
+
listener.stub(:validation_succeeded)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with an unconsumed service ticket' do
|
29
|
+
context 'without renew flag' do
|
30
|
+
it 'consumes the service ticket' do
|
31
|
+
processor.process(parameters)
|
32
|
+
service_ticket.reload
|
33
|
+
service_ticket.consumed.should == true
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'calls the #validation_succeeded method on the listener' do
|
37
|
+
listener.should_receive(:validation_succeeded).with(regex_success)
|
38
|
+
processor.process(parameters)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with renew flag' do
|
43
|
+
let(:parameters_with_renew) { parameters.merge renew: 'true' }
|
44
|
+
|
45
|
+
context 'with a service ticket without issued_from_credentials flag' do
|
46
|
+
it 'consumes the service ticket' do
|
47
|
+
processor.process(parameters_with_renew)
|
48
|
+
service_ticket.reload
|
49
|
+
service_ticket.consumed.should == true
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'calls the #validation_failed method on the listener' do
|
53
|
+
listener.should_receive(:validation_failed).with(regex_failure)
|
54
|
+
processor.process(parameters_with_renew)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with a service ticket with issued_from_credentials flag' do
|
59
|
+
before(:each) do
|
60
|
+
service_ticket.issued_from_credentials = true
|
61
|
+
service_ticket.save!
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'consumes the service ticket' do
|
65
|
+
processor.process(parameters_with_renew)
|
66
|
+
service_ticket.reload
|
67
|
+
service_ticket.consumed.should == true
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'calls the #validation_succeeded method on the listener' do
|
71
|
+
listener.should_receive(:validation_succeeded).with(regex_success)
|
72
|
+
processor.process(parameters_with_renew)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with proxy-granting ticket callback server' do
|
78
|
+
let(:parameters_with_pgt_url) { parameters.merge pgtUrl: 'https://www.example.com/' }
|
79
|
+
|
80
|
+
before(:each) do
|
81
|
+
stub_request(:get, /https:\/\/www\.example\.com\/\?pgtId=[^&]+&pgtIou=[^&]+/)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'calls the #validation_succeeded method on the listener' do
|
85
|
+
listener.should_receive(:validation_succeeded).with(regex_success)
|
86
|
+
processor.process(parameters_with_pgt_url)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'includes the PGTIOU in the response' do
|
90
|
+
listener.should_receive(:validation_succeeded).with(/\<cas\:proxyGrantingTicket\>\n?\s*PGTIOU-.+/)
|
91
|
+
processor.process(parameters_with_pgt_url)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'creates a proxy-granting ticket' do
|
95
|
+
lambda do
|
96
|
+
processor.process(parameters_with_pgt_url)
|
97
|
+
end.should change(service_ticket.proxy_granting_tickets, :count).by(1)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'contacts the callback server' do
|
101
|
+
processor.process(parameters_with_pgt_url)
|
102
|
+
proxy_granting_ticket = CASinoCore::Model::ProxyGrantingTicket.last
|
103
|
+
WebMock.should have_requested(:get, 'https://www.example.com').with(query: {
|
104
|
+
pgtId: proxy_granting_ticket.ticket,
|
105
|
+
pgtIou: proxy_granting_ticket.iou
|
106
|
+
})
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'with a consumed service ticket' do
|
112
|
+
before(:each) do
|
113
|
+
service_ticket.consumed = true
|
114
|
+
service_ticket.save!
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'calls the #validation_failed method on the listener' do
|
118
|
+
listener.should_receive(:validation_failed).with(regex_failure)
|
119
|
+
processor.process(parameters)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'casino_core'
|
2
1
|
require 'database_cleaner'
|
2
|
+
require 'logger'
|
3
|
+
require 'webmock/rspec'
|
4
|
+
require 'casino_core'
|
5
|
+
require 'simplecov'
|
3
6
|
|
4
7
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
5
8
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
@@ -16,7 +19,10 @@ RSpec.configure do |config|
|
|
16
19
|
# --seed 1234
|
17
20
|
config.order = 'random'
|
18
21
|
|
22
|
+
SimpleCov.start
|
23
|
+
|
19
24
|
CASinoCore.setup 'test'
|
25
|
+
CASinoCore::Settings.logger.level = ::Logger::Severity::UNKNOWN
|
20
26
|
|
21
27
|
config.before(:suite) do
|
22
28
|
DatabaseCleaner.strategy = :transaction
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: casino_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nils Caspar
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-12-
|
13
|
+
date: 2012-12-24 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -133,6 +133,17 @@ dependencies:
|
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: *id011
|
136
|
+
- !ruby/object:Gem::Dependency
|
137
|
+
name: webmock
|
138
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: "0"
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: *id012
|
136
147
|
description: A CAS server core library.
|
137
148
|
email: ncaspar@me.com
|
138
149
|
executables: []
|
@@ -167,6 +178,7 @@ files:
|
|
167
178
|
- db/migrate/20121125091934_add_issued_from_credentials_to_service_tickets.rb
|
168
179
|
- db/migrate/20121125185415_create_proxy_granting_tickets.rb
|
169
180
|
- db/migrate/20121125190013_tickets_should_be_unique.rb
|
181
|
+
- db/migrate/20121223135227_proxy_granting_tickets_belongs_to_service_ticket.rb
|
170
182
|
- db/schema.rb
|
171
183
|
- lib/casino_core.rb
|
172
184
|
- lib/casino_core/authenticator.rb
|
@@ -175,6 +187,7 @@ files:
|
|
175
187
|
- lib/casino_core/helper/browser.rb
|
176
188
|
- lib/casino_core/helper/logger.rb
|
177
189
|
- lib/casino_core/helper/login_tickets.rb
|
190
|
+
- lib/casino_core/helper/proxy_granting_tickets.rb
|
178
191
|
- lib/casino_core/helper/service_tickets.rb
|
179
192
|
- lib/casino_core/helper/ticket_granting_tickets.rb
|
180
193
|
- lib/casino_core/helper/tickets.rb
|
@@ -189,6 +202,7 @@ files:
|
|
189
202
|
- lib/casino_core/processor/login_credential_acceptor.rb
|
190
203
|
- lib/casino_core/processor/login_credential_requestor.rb
|
191
204
|
- lib/casino_core/processor/logout.rb
|
205
|
+
- lib/casino_core/processor/service_ticket_validator.rb
|
192
206
|
- lib/casino_core/processor/session_destroyer.rb
|
193
207
|
- lib/casino_core/processor/session_overview.rb
|
194
208
|
- lib/casino_core/railtie.rb
|
@@ -203,6 +217,7 @@ files:
|
|
203
217
|
- spec/processor/login_credential_acceptor_spec.rb
|
204
218
|
- spec/processor/login_credential_requestor_spec.rb
|
205
219
|
- spec/processor/logout_spec.rb
|
220
|
+
- spec/processor/service_ticket_validator_spec.rb
|
206
221
|
- spec/processor/session_destroyer_spec.rb
|
207
222
|
- spec/processor/session_overview_spec.rb
|
208
223
|
- spec/spec_helper.rb
|
@@ -219,7 +234,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
219
234
|
requirements:
|
220
235
|
- - ">="
|
221
236
|
- !ruby/object:Gem::Version
|
222
|
-
hash:
|
237
|
+
hash: 209769528785287815
|
223
238
|
segments:
|
224
239
|
- 0
|
225
240
|
version: "0"
|