rubycas-server 0.1.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.
- data/CHANGELOG.txt +3 -0
- data/LICENSE.txt +339 -0
- data/Manifest.txt +32 -0
- data/README.txt +27 -0
- data/Rakefile +59 -0
- data/bin/rubycas-server +8 -0
- data/config.example.yml +197 -0
- data/lib/casserver.rb +152 -0
- data/lib/casserver/authenticators/active_directory_ldap.rb +8 -0
- data/lib/casserver/authenticators/base.rb +22 -0
- data/lib/casserver/authenticators/ldap.rb +40 -0
- data/lib/casserver/authenticators/sql.rb +37 -0
- data/lib/casserver/authenticators/test.rb +8 -0
- data/lib/casserver/cas.rb +224 -0
- data/lib/casserver/conf.rb +78 -0
- data/lib/casserver/controllers.rb +253 -0
- data/lib/casserver/models.rb +109 -0
- data/lib/casserver/utils.rb +33 -0
- data/lib/casserver/version.rb +9 -0
- data/lib/casserver/views.rb +185 -0
- data/lib/themes/cas.css +112 -0
- data/lib/themes/ok.png +0 -0
- data/lib/themes/simple/bg.png +0 -0
- data/lib/themes/simple/login_box_bg.png +0 -0
- data/lib/themes/simple/logo.png +0 -0
- data/lib/themes/simple/theme.css +28 -0
- data/lib/themes/urbacon/bg.png +0 -0
- data/lib/themes/urbacon/login_box_bg.png +0 -0
- data/lib/themes/urbacon/logo.png +0 -0
- data/lib/themes/urbacon/theme.css +33 -0
- data/lib/themes/warning.png +0 -0
- data/setup.rb +1585 -0
- data/test/test_casserver.rb +150 -0
- metadata +103 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'camping/db'
|
2
|
+
|
3
|
+
module CASServer::Models
|
4
|
+
|
5
|
+
module Consumable
|
6
|
+
def consume!
|
7
|
+
self.consumed = Time.now
|
8
|
+
self.save!
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Ticket < Base
|
13
|
+
self.abstract_class = true
|
14
|
+
def to_s
|
15
|
+
ticket
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.cleanup_expired(expiry_time)
|
19
|
+
transaction do
|
20
|
+
expired_tickets = find(:all,
|
21
|
+
:conditions => ["created_on < ?", Time.now - expiry_time])
|
22
|
+
|
23
|
+
$LOG.debug("Destroying #{expired_tickets.size} expired #{self}"+
|
24
|
+
"#{'s' if expired_tickets.size > 1}.") if expired_tickets.size > 0
|
25
|
+
|
26
|
+
expired_tickets.each do |t|
|
27
|
+
t.destroy
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class LoginTicket < Ticket
|
34
|
+
include Consumable
|
35
|
+
end
|
36
|
+
|
37
|
+
class ServiceTicket < Ticket
|
38
|
+
include Consumable
|
39
|
+
end
|
40
|
+
|
41
|
+
class ProxyTicket < ServiceTicket
|
42
|
+
belongs_to :proxy_granting_ticket
|
43
|
+
end
|
44
|
+
|
45
|
+
class TicketGrantingTicket < Ticket
|
46
|
+
end
|
47
|
+
|
48
|
+
class ProxyGrantingTicket < Ticket
|
49
|
+
belongs_to :service_ticket
|
50
|
+
has_many :proxy_tickets, :dependent => :destroy
|
51
|
+
end
|
52
|
+
|
53
|
+
class Error
|
54
|
+
attr_reader :code, :message
|
55
|
+
|
56
|
+
def initialize(code, message)
|
57
|
+
@code = code
|
58
|
+
@message = message
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
message
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class CreateCASServer < V 0.1
|
67
|
+
def self.up
|
68
|
+
$LOG.info("Migrating database")
|
69
|
+
|
70
|
+
create_table :casserver_login_tickets, :force => true do |t|
|
71
|
+
t.column :ticket, :string, :null => false
|
72
|
+
t.column :created_on, :timestamp, :null => false
|
73
|
+
t.column :consumed, :datetime, :null => true
|
74
|
+
t.column :client_hostname, :string, :null => false
|
75
|
+
end
|
76
|
+
|
77
|
+
create_table :casserver_service_tickets, :force => true do |t|
|
78
|
+
t.column :ticket, :string, :null => false
|
79
|
+
t.column :service, :string, :null => false
|
80
|
+
t.column :created_on, :timestamp, :null => false
|
81
|
+
t.column :consumed, :datetime, :null => true
|
82
|
+
t.column :client_hostname, :string, :null => false
|
83
|
+
t.column :username, :string, :null => false
|
84
|
+
t.column :type, :string, :null => false
|
85
|
+
t.column :proxy_granting_ticket_id, :integer, :null => true
|
86
|
+
end
|
87
|
+
|
88
|
+
create_table :casserver_ticket_granting_tickets, :force => true do |t|
|
89
|
+
t.column :ticket, :string, :null => false
|
90
|
+
t.column :created_on, :timestamp, :null => false
|
91
|
+
t.column :client_hostname, :string, :null => false
|
92
|
+
t.column :username, :string, :null => false
|
93
|
+
end
|
94
|
+
|
95
|
+
create_table :casserver_proxy_granting_tickets, :force => true do |t|
|
96
|
+
t.column :ticket, :string, :null => false
|
97
|
+
t.column :created_on, :timestamp, :null => false
|
98
|
+
t.column :client_hostname, :string, :null => false
|
99
|
+
t.column :iou, :string, :null => false
|
100
|
+
t.column :service_ticket_id, :integer, :null => false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.down
|
105
|
+
drop_table :casserver_service_tickets
|
106
|
+
drop_table :casserver_login_tickets
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Misc utility function used throughout by the RubyCAS-server.
|
2
|
+
module CASServer
|
3
|
+
module Utils
|
4
|
+
def random_string
|
5
|
+
"#{Time.now.to_i}r%X" % rand(10**32)
|
6
|
+
end
|
7
|
+
module_function :random_string
|
8
|
+
|
9
|
+
class Logger < ::Logger
|
10
|
+
def initialize(logdev, shift_age = 0, shift_size = 1048576)
|
11
|
+
begin
|
12
|
+
super
|
13
|
+
rescue Exception
|
14
|
+
puts "WARNING: Couldn't create Logger with output '#{logdev}'. Logger output will be redirected to STDOUT."
|
15
|
+
super(STDOUT, shift_age, shift_size)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def format_message(severity, datetime, progrname, msg)
|
20
|
+
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class LogFormatter < ::Logger::Formatter
|
26
|
+
Format = "[%s#%d] %5s -- %s: %s\n"
|
27
|
+
|
28
|
+
def call(severity, time, progname, msg)
|
29
|
+
Format % [format_datetime(time), $$, severity, progname,
|
30
|
+
msg2str(msg)]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# The #.#.# comments (e.g. "2.1.3") refer to section numbers in the CAS protocol spec
|
2
|
+
# under http://www.ja-sig.org/products/cas/overview/protocol/index.html
|
3
|
+
|
4
|
+
module CASServer::Views
|
5
|
+
|
6
|
+
# need to turn off autovalidation to render CAS xml responses
|
7
|
+
#
|
8
|
+
|
9
|
+
def layout
|
10
|
+
# wrap as XHTML only when auto_validation is on, otherwise pass right through
|
11
|
+
if @auto_validation
|
12
|
+
xhtml_strict do
|
13
|
+
head do
|
14
|
+
title { "#{organization} Central Login" }
|
15
|
+
link(:rel => "stylesheet", :type => "text/css", :href => "/themes/cas.css")
|
16
|
+
link(:rel => "stylesheet", :type => "text/css", :href => "/themes/#{current_theme}/theme.css")
|
17
|
+
end
|
18
|
+
body(:onload => "if (document.getElementById('username')) document.getElementById('username').focus()") do
|
19
|
+
self << yield
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
self << yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# 2.1.3
|
29
|
+
def login
|
30
|
+
@logo =
|
31
|
+
|
32
|
+
table(:id => "login-box") do
|
33
|
+
tr do
|
34
|
+
td(:colspan => 2) do
|
35
|
+
div(:id => "headline-container") do
|
36
|
+
strong organization
|
37
|
+
text "Central Login"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
if @message
|
42
|
+
tr do
|
43
|
+
td(:colspan => 2, :id => "messagebox-container") do
|
44
|
+
div(:class => "messagebox #{@message[:type]}") { @message[:message] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
tr do
|
49
|
+
td(:id => "logo-container") do
|
50
|
+
img(:id => "logo", :src => "/themes/#{current_theme}/logo.png")
|
51
|
+
end
|
52
|
+
td(:id => "login-form-container") do
|
53
|
+
form(:method => "post", :action => "/login", :id => "login-form",
|
54
|
+
:onsubmit => "submit = document.getElementById('login-submit'); submit.value='Please wait...'; submit.disabled=true; return true;") do
|
55
|
+
table(:id => "form-layout") do
|
56
|
+
tr do
|
57
|
+
td(:id => "username-label-container") do
|
58
|
+
label(:id => "username-label", :for => "username") { "Username" }
|
59
|
+
end
|
60
|
+
td(:id => "username-container") do
|
61
|
+
input(:type => "text", :id => "username", :name => "username", :size => "32", :tabindex => "1", :accesskey => "n")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
tr do
|
65
|
+
td(:id => "password-label-container") do
|
66
|
+
label(:id => "password-label", :for => "password") { "Password" }
|
67
|
+
end
|
68
|
+
td(:id => "password-container") do
|
69
|
+
input(:type => "password", :id => "password", :name => "password", :size => "32", :tabindex => "2", :accesskey => "p")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
tr do
|
73
|
+
td{}
|
74
|
+
td(:id => "submit-container") do
|
75
|
+
input(:type => "hidden", :id => "lt", :name => "lt", :value => @lt)
|
76
|
+
input(:type => "hidden", :id => "service", :name => "service", :value => @service)
|
77
|
+
input(:type => "hidden", :id => "warn", :name => "warn", :value => @warn)
|
78
|
+
input(:type => "submit", :class => "button", :accesskey => "l", :value => "LOGIN", :tabindex => "4", :id => "login-submit")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
tr do
|
82
|
+
td(:colspan => 2, :id => "infoline") { infoline }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# 2.4.2
|
92
|
+
def validate
|
93
|
+
@auto_validation = false
|
94
|
+
if @success
|
95
|
+
text "yes\n#{@username}\n"
|
96
|
+
else
|
97
|
+
text "no\n\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# 2.5.2
|
102
|
+
def service_validate
|
103
|
+
@auto_validation = false
|
104
|
+
if @success
|
105
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
106
|
+
tag!("cas:authenticationSuccess") do
|
107
|
+
tag!("cas:user") {@username}
|
108
|
+
if @pgtiou
|
109
|
+
tag!("cas:proxyGrantingTicket") {@pgtiou}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
115
|
+
tag!("cas:authenticationFailure", :code => @error.code) {@error}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# 2.6.2
|
121
|
+
def proxy_validate
|
122
|
+
@auto_validation = false
|
123
|
+
if @success
|
124
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
125
|
+
tag!("cas:authenticationSuccess") do
|
126
|
+
tag!("cas:user") {@username}
|
127
|
+
if @pgtiou
|
128
|
+
tag!("cas:proxyGrantingTicket") {@pgtiou}
|
129
|
+
end
|
130
|
+
if @proxies && !@proxies.empty?
|
131
|
+
tag!("cas:proxies") do
|
132
|
+
@proxies.each do |proxy_url|
|
133
|
+
tag!("cas:proxy") {proxy_url}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
else
|
140
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
141
|
+
tag!("cas:authenticationFailure", :code => @error.code) {@error}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# 2.7.2
|
147
|
+
def proxy
|
148
|
+
@auto_validation = false
|
149
|
+
if @success
|
150
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
151
|
+
tag!("cas:proxySuccess") do
|
152
|
+
tag!("cas:proxyTicket") {@pt}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
else
|
156
|
+
tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
|
157
|
+
tag!("cas:proxyFailure", :code => @error.code) {@error}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def configure
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
def themes_dir
|
167
|
+
File.dirname(File.expand_path(__FILE__))+'../themes'
|
168
|
+
end
|
169
|
+
module_function :themes_dir
|
170
|
+
|
171
|
+
def current_theme
|
172
|
+
CASServer::Conf.theme || "simple"
|
173
|
+
end
|
174
|
+
module_function :current_theme
|
175
|
+
|
176
|
+
def organization
|
177
|
+
CASServer::Conf.organization || ""
|
178
|
+
end
|
179
|
+
module_function :organization
|
180
|
+
|
181
|
+
def infoline
|
182
|
+
CASServer::Conf.infoline || ""
|
183
|
+
end
|
184
|
+
module_function :infoline
|
185
|
+
end
|
data/lib/themes/cas.css
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
* {
|
2
|
+
font-family: Verdana, sans-serif;
|
3
|
+
}
|
4
|
+
|
5
|
+
body {
|
6
|
+
}
|
7
|
+
|
8
|
+
label {
|
9
|
+
font-weight: bold;
|
10
|
+
font-size: 9px;
|
11
|
+
}
|
12
|
+
|
13
|
+
input {
|
14
|
+
font-weight: normal;
|
15
|
+
font-size: 12px;
|
16
|
+
}
|
17
|
+
|
18
|
+
input.button {
|
19
|
+
/*font-weight: bold;*/
|
20
|
+
font-size: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
#login-box {
|
24
|
+
margin: 0 auto;
|
25
|
+
width: 350px;
|
26
|
+
top: 130px;
|
27
|
+
position: relative;
|
28
|
+
}
|
29
|
+
|
30
|
+
#headline-container {
|
31
|
+
text-align: right;
|
32
|
+
border-bottom: 1px solid #899989;
|
33
|
+
font-family: Tahoma, Verdana, sans-serif;
|
34
|
+
font-size: 22px;
|
35
|
+
margin-right: 0px;
|
36
|
+
padding-right: 7px;
|
37
|
+
margin-left: 10px;
|
38
|
+
letter-spacing: -0.25px;
|
39
|
+
}
|
40
|
+
|
41
|
+
#logo-container {
|
42
|
+
vertical-align: top;
|
43
|
+
}
|
44
|
+
|
45
|
+
#logo {
|
46
|
+
}
|
47
|
+
|
48
|
+
#login-form-container {
|
49
|
+
vertical-align: top;
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
#username,
|
54
|
+
#password {
|
55
|
+
width: 10em;
|
56
|
+
}
|
57
|
+
|
58
|
+
#login-form {
|
59
|
+
padding: 20px;
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
#form-layout {
|
64
|
+
position: relative;
|
65
|
+
top: 6px;
|
66
|
+
width: 100%;
|
67
|
+
}
|
68
|
+
|
69
|
+
#form-layout td {
|
70
|
+
text-align: center;
|
71
|
+
padding-bottom: 8px;
|
72
|
+
}
|
73
|
+
|
74
|
+
#form-layout td#submit-container {
|
75
|
+
text-align: right;
|
76
|
+
padding-right: 10px;
|
77
|
+
}
|
78
|
+
|
79
|
+
#infoline {
|
80
|
+
font-size: 9px;
|
81
|
+
}
|
82
|
+
|
83
|
+
#messagebox-container {
|
84
|
+
padding-left: 11px;
|
85
|
+
padding-right: 16px;
|
86
|
+
}
|
87
|
+
|
88
|
+
div.messagebox {
|
89
|
+
font-size: 12px;
|
90
|
+
padding: 5px;
|
91
|
+
padding-left: 55px;
|
92
|
+
text-align: center;
|
93
|
+
width: 70%;
|
94
|
+
min-height: 34px;
|
95
|
+
vertical-align: middle;
|
96
|
+
}
|
97
|
+
|
98
|
+
div.mistake {
|
99
|
+
color: #d00;
|
100
|
+
background-image: url(warning.png);
|
101
|
+
background-repeat: no-repeat;
|
102
|
+
background-position: 10px 5px;
|
103
|
+
font-weight: bold;
|
104
|
+
}
|
105
|
+
|
106
|
+
div.confirmation {
|
107
|
+
color: #280;
|
108
|
+
background-image: url(ok.png);
|
109
|
+
background-repeat: no-repeat;
|
110
|
+
background-position: 10px 5px;
|
111
|
+
font-weight: bold;
|
112
|
+
}
|