rubycas-server 1.0.1 → 1.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 +20 -0
- data/LICENSE +1 -1
- data/README.md +17 -3
- data/Rakefile +2 -1
- data/config.ru +7 -1
- data/config/config.example.yml +1 -1
- data/lib/casserver/authenticators/sql_encrypted.rb +0 -1
- data/lib/casserver/authenticators/sql_rest_auth.rb +7 -7
- data/lib/casserver/cas.rb +23 -15
- data/lib/casserver/localization.rb +4 -82
- data/lib/casserver/server.rb +24 -17
- data/lib/casserver/views/_login_form.erb +4 -4
- data/lib/casserver/views/layout.erb +1 -1
- data/lib/casserver/views/login.erb +1 -1
- data/locales/de.yml +27 -0
- data/locales/en.yml +26 -0
- data/locales/es.yml +26 -0
- data/locales/es_ar.yml +26 -0
- data/locales/fr.yml +26 -0
- data/locales/jp.yml +26 -0
- data/locales/pl.yml +26 -0
- data/locales/pt.yml +26 -0
- data/locales/ru.yml +26 -0
- data/locales/zh.yml +26 -0
- data/locales/zh_tw.yml +26 -0
- data/public/themes/cas.css +6 -1
- data/rubycas-server.gemspec +7 -11
- data/spec/casserver_spec.rb +12 -5
- data/spec/spec_helper.rb +6 -5
- data/tasks/spec.rake +4 -4
- metadata +60 -48
- data/po/de_DE/rubycas-server.po +0 -127
- data/po/es_ES/rubycas-server.po +0 -123
- data/po/fr_FR/rubycas-server.po +0 -128
- data/po/ja_JP/rubycas-server.po +0 -126
- data/po/pl_PL/rubycas-server.po +0 -123
- data/po/pt_BR/rubycas-server.po +0 -123
- data/po/ru_RU/rubycas-server.po +0 -118
- data/po/rubycas-server.pot +0 -112
- data/po/zh_CN/rubycas-server.po +0 -113
- data/po/zh_TW/rubycas-server.po +0 -113
- data/tasks/localization.rake +0 -13
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 1.1.0 :: 2012-04-19
|
2
|
+
|
3
|
+
* NEW:
|
4
|
+
* Localization is now done using R18n instead of Gettext.
|
5
|
+
* Restored compatibility with Sinatra 1.2
|
6
|
+
* Now compatibile with Ruby 1.9.3
|
7
|
+
* Can now run without Bundler if all required dependencies are already installed.
|
8
|
+
* es_AR translations.
|
9
|
+
|
10
|
+
* CHANGED:
|
11
|
+
* It is no longer possible to select the locale by adding a 'lang=xx' attribute to the
|
12
|
+
request URL. The locale is selected using the 'Accept-Lanuage' header sent in the
|
13
|
+
request. However the old 'lang' functionality may be restored in a future version.
|
14
|
+
* Certain localized string keys have changed. If you are using your own custom views
|
15
|
+
you may need to modify them accordingly.
|
16
|
+
|
17
|
+
* FIXED:
|
18
|
+
* Removed unnecessary bcrypt requirement for encrypted sql authenticators.
|
19
|
+
* Single Sign Out requests should now work with SSL-enabled services.
|
20
|
+
|
1
21
|
=== 1.0.1 :: 2011-11-22
|
2
22
|
|
3
23
|
* NEW:
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# RubyCAS-Server 
|
2
2
|
|
3
|
-
|
3
|
+
## Copyright
|
4
4
|
|
5
|
-
|
5
|
+
Portions contributed by Matt Zukowski are copyright (c) 2011 Urbacon Ltd.
|
6
|
+
Other portions are copyright of their respective authors.
|
7
|
+
|
8
|
+
## Authors
|
9
|
+
|
10
|
+
See http://github.com/gunark/rubycas-server/commits/
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
See http://code.google.com/p/rubycas-server
|
15
|
+
|
16
|
+
## License
|
17
|
+
|
18
|
+
RubyCAS-Server is licensed for use under the terms of the MIT License.
|
19
|
+
See the LICENSE file bundled with the official RubyCAS-Server distribution for details.
|
data/Rakefile
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
1
|
+
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
2
|
+
task :default => :spec
|
data/config.ru
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
|
2
|
+
|
3
|
+
# Assume all necessary gems are in place if bundler is not installed.
|
4
|
+
begin
|
5
|
+
require 'bundler/setup'
|
6
|
+
rescue LoadError => e
|
7
|
+
raise e unless e.message =~ /no such file to load -- bundler/
|
8
|
+
end
|
3
9
|
|
4
10
|
$:.unshift "#{File.dirname(__FILE__)}/lib"
|
5
11
|
require "casserver"
|
data/config/config.example.yml
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# passenger -- served out by Apache via the mod_rails/mod_rack module
|
16
16
|
# (see http://www.modrails.com/)
|
17
17
|
#
|
18
|
-
# The following are
|
18
|
+
# The following are example configurations for each of these three methods:
|
19
19
|
#
|
20
20
|
|
21
21
|
|
@@ -38,7 +38,7 @@ class CASServer::Authenticators::SQLRestAuth < CASServer::Authenticators::SQLEnc
|
|
38
38
|
if results.size > 0
|
39
39
|
$LOG.warn("Multiple matches found for user '#{@username}'") if results.size > 1
|
40
40
|
user = results.first
|
41
|
-
if user.crypted_password == user.encrypt(@password)
|
41
|
+
if user.crypted_password == user.encrypt(@password,@options[:site_key],@options[:digest_streches])
|
42
42
|
unless @options[:extra_attributes].blank?
|
43
43
|
extract_extra(user)
|
44
44
|
log_extra
|
@@ -63,18 +63,18 @@ class CASServer::Authenticators::SQLRestAuth < CASServer::Authenticators::SQLEnc
|
|
63
63
|
raise "#{self} should be inclued in an ActiveRecord class!" unless mod.respond_to?(:before_save)
|
64
64
|
end
|
65
65
|
|
66
|
-
def encrypt(password)
|
67
|
-
password_digest(password, self.salt)
|
66
|
+
def encrypt(password,site_key,digest_streches)
|
67
|
+
password_digest(password, self.salt,site_key,digest_streches)
|
68
68
|
end
|
69
69
|
|
70
70
|
def secure_digest(*args)
|
71
71
|
Digest::SHA1.hexdigest(args.flatten.join('--'))
|
72
72
|
end
|
73
73
|
|
74
|
-
def password_digest(password, salt)
|
75
|
-
digest =
|
76
|
-
|
77
|
-
digest = secure_digest(digest, salt, password,
|
74
|
+
def password_digest(password, salt,site_key,digest_streches)
|
75
|
+
digest = site_key
|
76
|
+
digest_streches.times do
|
77
|
+
digest = secure_digest(digest, salt, password, site_key)
|
78
78
|
end
|
79
79
|
digest
|
80
80
|
end
|
data/lib/casserver/cas.rb
CHANGED
@@ -119,20 +119,20 @@ module CASServer::CAS
|
|
119
119
|
|
120
120
|
success = false
|
121
121
|
if ticket.nil?
|
122
|
-
error =
|
122
|
+
error = t.error.no_login_ticket
|
123
123
|
$LOG.warn "Missing login ticket."
|
124
124
|
elsif lt = LoginTicket.find_by_ticket(ticket)
|
125
125
|
if lt.consumed?
|
126
|
-
error =
|
126
|
+
error = t.error.login_ticket_already_used
|
127
127
|
$LOG.warn "Login ticket '#{ticket}' previously used up"
|
128
128
|
elsif Time.now - lt.created_on < settings.config[:maximum_unused_login_ticket_lifetime]
|
129
129
|
$LOG.info "Login ticket '#{ticket}' successfully validated"
|
130
130
|
else
|
131
|
-
error =
|
131
|
+
error = t.error.login_timeout
|
132
132
|
$LOG.warn "Expired login ticket '#{ticket}'"
|
133
133
|
end
|
134
134
|
else
|
135
|
-
error =
|
135
|
+
error = t.error.invalid_login_ticket
|
136
136
|
$LOG.warn "Invalid login ticket '#{ticket}'"
|
137
137
|
end
|
138
138
|
|
@@ -244,18 +244,26 @@ module CASServer::CAS
|
|
244
244
|
uri.path = '/' if uri.path.empty?
|
245
245
|
time = Time.now
|
246
246
|
rand = CASServer::Utils.random_string
|
247
|
-
|
247
|
+
path = uri.path
|
248
|
+
req = Net::HTTP::Post.new(path)
|
249
|
+
req.set_form_data('logoutRequest' => %{<samlp:LogoutRequest ID="#{rand}" Version="2.0" IssueInstant="#{time.rfc2822}">
|
250
|
+
<saml:NameID></saml:NameID>
|
251
|
+
<samlp:SessionIndex>#{st.ticket}</samlp:SessionIndex>
|
252
|
+
</samlp:LogoutRequest>})
|
253
|
+
|
248
254
|
begin
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
255
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
256
|
+
http.use_ssl = true if uri.scheme =='https'
|
257
|
+
|
258
|
+
http.start do |conn|
|
259
|
+
response = conn.request(req)
|
260
|
+
if response.kind_of? Net::HTTPSuccess
|
261
|
+
$LOG.info "Logout notification successfully posted to #{st.service.inspect}."
|
262
|
+
return true
|
263
|
+
else
|
264
|
+
$LOG.error "Service #{st.service.inspect} responed to logout notification with code '#{response.code}'!"
|
265
|
+
return false
|
266
|
+
end
|
259
267
|
end
|
260
268
|
rescue Exception => e
|
261
269
|
$LOG.error "Failed to send logout notification to service #{st.service.inspect} due to #{e}"
|
@@ -1,91 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require "gettext/cgi"
|
3
|
-
require 'active_support'
|
1
|
+
require 'sinatra/r18n'
|
4
2
|
|
5
3
|
module CASServer
|
6
4
|
module Localization
|
7
5
|
def self.included(mod)
|
8
6
|
mod.module_eval do
|
9
|
-
|
7
|
+
register Sinatra::R18n
|
8
|
+
set :default_locale, 'en'
|
9
|
+
set :translations, './locales'
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
|
-
include GetText
|
14
|
-
bindtextdomain("rubycas-server", :path => File.join(File.dirname(File.expand_path(__FILE__)), "../../locale"))
|
15
|
-
|
16
|
-
def determine_locale(request)
|
17
|
-
source = nil
|
18
|
-
lang = case
|
19
|
-
when !request.params['lang'].blank?
|
20
|
-
source = "'lang' request variable"
|
21
|
-
request.cookies['lang'] = request.params['lang']
|
22
|
-
request.params['lang']
|
23
|
-
when !request.cookies['lang'].blank?
|
24
|
-
source = "'lang' cookie"
|
25
|
-
request.cookies['lang']
|
26
|
-
when !request.env['HTTP_ACCEPT_LANGUAGE'].blank?
|
27
|
-
source = "'HTTP_ACCEPT_LANGUAGE' header"
|
28
|
-
lang = request.env['HTTP_ACCEPT_LANGUAGE']
|
29
|
-
when !request.env['HTTP_USER_AGENT'].blank? && request.env['HTTP_USER_AGENT'] =~ /[^a-z]([a-z]{2}(-[a-z]{2})?)[^a-z]/i
|
30
|
-
source = "'HTTP_USER_AGENT' header"
|
31
|
-
$~[1]
|
32
|
-
# when !$CONF['default_locale'].blank?
|
33
|
-
# source = "'default_locale' config option"
|
34
|
-
# $CONF[:default_locale]
|
35
|
-
else
|
36
|
-
source = "default"
|
37
|
-
"en"
|
38
|
-
end
|
39
|
-
|
40
|
-
$LOG.debug "Detected locale is #{lang.inspect} (from #{source})"
|
41
|
-
|
42
|
-
lang.gsub!('_','-')
|
43
|
-
|
44
|
-
# TODO: Need to confirm that this method of splitting the accepted
|
45
|
-
# language string is correct.
|
46
|
-
if lang =~ /[,;\|]/
|
47
|
-
langs = lang.split(/[,;\|]/)
|
48
|
-
else
|
49
|
-
langs = [lang]
|
50
|
-
end
|
51
|
-
|
52
|
-
# TODO: This method of selecting the desired language might not be
|
53
|
-
# standards-compliant. For example, http://www.w3.org/TR/ltli/
|
54
|
-
# suggests that de-de and de-*-DE might be acceptable identifiers
|
55
|
-
# for selecting various wildcards. The algorithm below does not
|
56
|
-
# currently support anything like this.
|
57
|
-
|
58
|
-
available = available_locales
|
59
|
-
|
60
|
-
if available.length == 1
|
61
|
-
$LOG.warn "Only the #{available.first.inspect} localization is available. You should run `rake localization:mo` to compile support for additional languages!"
|
62
|
-
elsif available.length == 0 # this should never actually happen
|
63
|
-
$LOG.error "No localizations available! Run `rake localization:mo` to compile support for additional languages."
|
64
|
-
end
|
65
|
-
|
66
|
-
# Try to pick a locale exactly matching the desired identifier, otherwise
|
67
|
-
# fall back to locale without region (i.e. given "en-US; de-DE", we would
|
68
|
-
# first look for "en-US", then "en", then "de-DE", then "de").
|
69
|
-
|
70
|
-
chosen_lang = nil
|
71
|
-
langs.each do |l|
|
72
|
-
a = available.find{ |a| a =~ Regexp.new("\\A#{l}\\Z", 'i') ||
|
73
|
-
a =~ Regexp.new("#{l}-\w*", 'i') }
|
74
|
-
if a
|
75
|
-
chosen_lang = a
|
76
|
-
break
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
chosen_lang = "en" if chosen_lang.blank?
|
81
|
-
|
82
|
-
$LOG.debug "Chosen locale is #{chosen_lang.inspect}"
|
83
|
-
|
84
|
-
return chosen_lang
|
85
|
-
end
|
86
|
-
|
87
|
-
def available_locales
|
88
|
-
(Dir.glob(File.join(File.dirname(File.expand_path(__FILE__)), "../../locale/[a-z]*")).map{|path| File.basename(path)} << "en").uniq.collect{|l| l.gsub('_','-')}
|
89
|
-
end
|
90
12
|
end
|
91
13
|
end
|
data/lib/casserver/server.rb
CHANGED
@@ -19,8 +19,14 @@ module CASServer
|
|
19
19
|
include CASServer::CAS # CAS protocol helpers
|
20
20
|
include Localization
|
21
21
|
|
22
|
+
# Use :public_folder for Sinatra >= 1.3, and :public for older versions.
|
23
|
+
def self.use_public_folder?
|
24
|
+
Sinatra.const_defined?("VERSION") && Gem::Version.new(Sinatra::VERSION) >= Gem::Version.new("1.3.0")
|
25
|
+
end
|
26
|
+
|
22
27
|
set :app_file, __FILE__
|
23
|
-
set :public_folder
|
28
|
+
set( use_public_folder? ? :public_folder : :public, # Workaround for differences in Sinatra versions.
|
29
|
+
Proc.new { settings.config[:public_dir] || File.join(root, "..", "..", "public") } )
|
24
30
|
|
25
31
|
config = HashWithIndifferentAccess.new(
|
26
32
|
:maximum_unused_login_ticket_lifetime => 5.minutes,
|
@@ -34,11 +40,13 @@ module CASServer
|
|
34
40
|
def self.uri_path
|
35
41
|
config[:uri_path]
|
36
42
|
end
|
37
|
-
|
43
|
+
|
38
44
|
# Strip the config.uri_path from the request.path_info...
|
39
45
|
# FIXME: do we really need to override all of Sinatra's #static! to make this happen?
|
40
46
|
def static!
|
41
|
-
|
47
|
+
# Workaround for differences in Sinatra versions.
|
48
|
+
public_dir = Server.use_public_folder? ? settings.public_folder : settings.public
|
49
|
+
return if public_dir.nil?
|
42
50
|
public_dir = File.expand_path(public_dir)
|
43
51
|
|
44
52
|
path = File.expand_path(public_dir + unescape(request.path_info.gsub(/^#{settings.config[:uri_path]}/,'')))
|
@@ -281,7 +289,6 @@ module CASServer
|
|
281
289
|
end
|
282
290
|
|
283
291
|
before do
|
284
|
-
GetText.locale = determine_locale(request)
|
285
292
|
content_type :html, 'charset' => 'utf-8'
|
286
293
|
@theme = settings.config[:theme]
|
287
294
|
@organization = settings.config[:organization]
|
@@ -320,7 +327,7 @@ module CASServer
|
|
320
327
|
|
321
328
|
if tgt and !tgt_error
|
322
329
|
@message = {:type => 'notice',
|
323
|
-
:message =>
|
330
|
+
:message => t.notice.logged_in_as(tgt.username)}
|
324
331
|
elsif tgt_error
|
325
332
|
$LOG.debug("Ticket granting cookie could not be validated: #{tgt_error}")
|
326
333
|
elsif !tgt
|
@@ -329,7 +336,7 @@ module CASServer
|
|
329
336
|
|
330
337
|
if params['redirection_loop_intercepted']
|
331
338
|
@message = {:type => 'mistake',
|
332
|
-
:message =>
|
339
|
+
:message => t.error.unable_to_authenticate}
|
333
340
|
end
|
334
341
|
|
335
342
|
begin
|
@@ -351,14 +358,14 @@ module CASServer
|
|
351
358
|
elsif @gateway
|
352
359
|
$LOG.error("This is a gateway request but no service parameter was given!")
|
353
360
|
@message = {:type => 'mistake',
|
354
|
-
:message =>
|
361
|
+
:message => t.error.no_service_parameter_given}
|
355
362
|
else
|
356
363
|
$LOG.info("Proceeding with CAS login without a target service.")
|
357
364
|
end
|
358
365
|
rescue URI::InvalidURIError
|
359
366
|
$LOG.error("The service '#{@service}' is not a valid URI!")
|
360
367
|
@message = {:type => 'mistake',
|
361
|
-
:message =>
|
368
|
+
:message => t.error.invalid_target_service}
|
362
369
|
end
|
363
370
|
|
364
371
|
lt = generate_login_ticket
|
@@ -387,7 +394,7 @@ module CASServer
|
|
387
394
|
render :login_form
|
388
395
|
else
|
389
396
|
status 500
|
390
|
-
render
|
397
|
+
render t.error.invalid_submit_to_uri
|
391
398
|
end
|
392
399
|
else
|
393
400
|
render @template_engine, :login
|
@@ -420,7 +427,7 @@ module CASServer
|
|
420
427
|
# generate another login ticket to allow for re-submitting the form
|
421
428
|
@lt = generate_login_ticket.ticket
|
422
429
|
status 500
|
423
|
-
render @template_engine, :login
|
430
|
+
return render @template_engine, :login
|
424
431
|
end
|
425
432
|
|
426
433
|
# generate another login ticket to allow for re-submitting the form after a post
|
@@ -468,7 +475,7 @@ module CASServer
|
|
468
475
|
|
469
476
|
if @service.blank?
|
470
477
|
$LOG.info("Successfully authenticated user '#{@username}' at '#{tgt.client_hostname}'. No service param was given, so we will not redirect.")
|
471
|
-
@message = {:type => 'confirmation', :message =>
|
478
|
+
@message = {:type => 'confirmation', :message => t.notice.success_logged_in}
|
472
479
|
else
|
473
480
|
@st = generate_service_ticket(@service, @username, tgt)
|
474
481
|
|
@@ -481,20 +488,20 @@ module CASServer
|
|
481
488
|
$LOG.error("The service '#{@service}' is not a valid URI!")
|
482
489
|
@message = {
|
483
490
|
:type => 'mistake',
|
484
|
-
:message =>
|
491
|
+
:message => t.error.invalid_target_service
|
485
492
|
}
|
486
493
|
end
|
487
494
|
end
|
488
495
|
else
|
489
496
|
$LOG.warn("Invalid credentials given for user '#{@username}'")
|
490
|
-
@message = {:type => 'mistake', :message =>
|
497
|
+
@message = {:type => 'mistake', :message => t.error.incorrect_username_or_password}
|
491
498
|
status 401
|
492
499
|
end
|
493
500
|
rescue CASServer::AuthenticatorError => e
|
494
501
|
$LOG.error(e)
|
495
502
|
# generate another login ticket to allow for re-submitting the form
|
496
503
|
@lt = generate_login_ticket.ticket
|
497
|
-
@message = {:type => 'mistake', :message =>
|
504
|
+
@message = {:type => 'mistake', :message => e.to_s}
|
498
505
|
status 401
|
499
506
|
end
|
500
507
|
|
@@ -553,9 +560,9 @@ module CASServer
|
|
553
560
|
$LOG.warn("User tried to log out without a valid ticket-granting ticket.")
|
554
561
|
end
|
555
562
|
|
556
|
-
@message = {:type => 'confirmation', :message =>
|
563
|
+
@message = {:type => 'confirmation', :message => t.notice.success_logged_out}
|
557
564
|
|
558
|
-
@message[:message] +=
|
565
|
+
@message[:message] += t.notice.click_to_continue if @continue_url
|
559
566
|
|
560
567
|
@lt = generate_login_ticket
|
561
568
|
|
@@ -581,7 +588,7 @@ module CASServer
|
|
581
588
|
|
582
589
|
status 422
|
583
590
|
|
584
|
-
|
591
|
+
t.error.login_ticket_needs_post_request
|
585
592
|
end
|
586
593
|
|
587
594
|
|
@@ -1,11 +1,11 @@
|
|
1
1
|
<%# coding: UTF-8 -%>
|
2
2
|
<form method="post" action="<%= @form_action || "login" %>" id="login-form"
|
3
|
-
onsubmit="submitbutton = document.getElementById('login-submit'); submitbutton.value='<%=
|
3
|
+
onsubmit="submitbutton = document.getElementById('login-submit'); submitbutton.value='<%= t.notice.please_wait %>'; submitbutton.disabled=true; return true;">
|
4
4
|
<table id="form-layout">
|
5
5
|
<tr>
|
6
6
|
<td id="username-label-container">
|
7
7
|
<label id="username-label" for="username">
|
8
|
-
<%=
|
8
|
+
<%= t.label.username %>
|
9
9
|
</label>
|
10
10
|
</td>
|
11
11
|
<td id="username-container">
|
@@ -16,7 +16,7 @@
|
|
16
16
|
<tr>
|
17
17
|
<td id="password-label-container">
|
18
18
|
<label id="password-label" for="password">
|
19
|
-
<%=
|
19
|
+
<%= t.label.password %>
|
20
20
|
</label>
|
21
21
|
</td>
|
22
22
|
<td id="password-container">
|
@@ -29,7 +29,7 @@
|
|
29
29
|
<td id="submit-container">
|
30
30
|
<input type="hidden" id="lt" name="lt" value="<%= escape_html @lt %>" />
|
31
31
|
<input type="hidden" id="service" name="service" value="<%= escape_html @service %>" />
|
32
|
-
<input type="submit" class="button" accesskey="l" value="<%=
|
32
|
+
<input type="submit" class="button" accesskey="l" value="<%= t.button.login %>"
|
33
33
|
tabindex="4" id="login-submit" />
|
34
34
|
</td>
|
35
35
|
</tr>
|