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 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
@@ -1,4 +1,4 @@
1
- Portions of RubyCAS-Server contributed by Matt Zukowski are copyright (c) 2009 Urbacon Ltd.
1
+ Portions of RubyCAS-Server contributed by Matt Zukowski are copyright (c) 2011 Urbacon Ltd.
2
2
  Other portions are copyright of their respective authors.
3
3
 
4
4
  The MIT License
data/README.md CHANGED
@@ -1,5 +1,19 @@
1
- # MOVED!
1
+ # RubyCAS-Server ![http://stillmaintained.com/rubycas/rubycas-server](http://stillmaintained.com/rubycas/rubycas-server.png)
2
2
 
3
- This repo has been moved to https://github.com/rubycas/rubycas-server.
3
+ ## Copyright
4
4
 
5
- The fork you are looking at is no longer updated. Please change your git remotes to the new rubycas URL.
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
- require 'bundler/setup'
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"
@@ -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 exampe configurations for each of these three methods:
18
+ # The following are example configurations for each of these three methods:
19
19
  #
20
20
 
21
21
 
@@ -3,7 +3,6 @@ require 'casserver/authenticators/sql'
3
3
  require 'digest/sha1'
4
4
  require 'digest/sha2'
5
5
  require 'crypt-isaac'
6
- require 'bcrypt'
7
6
 
8
7
  # This is a more secure version of the SQL authenticator. Passwords are encrypted
9
8
  # rather than being stored in plain text.
@@ -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 = @options[:site_key]
76
- @options[:digest_streches].times do
77
- digest = secure_digest(digest, salt, password, @options[:site_key])
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
@@ -119,20 +119,20 @@ module CASServer::CAS
119
119
 
120
120
  success = false
121
121
  if ticket.nil?
122
- error = _("Your login request did not include a login ticket. There may be a problem with the authentication system.")
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 = _("The login ticket you provided has already been used up. Please try logging in again.")
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 = _("You took too long to enter your credentials. Please try again.")
131
+ error = t.error.login_timeout
132
132
  $LOG.warn "Expired login ticket '#{ticket}'"
133
133
  end
134
134
  else
135
- error = _("The login ticket you provided is invalid. There may be a problem with the authentication system.")
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
- response = Net::HTTP.post_form(uri, {'logoutRequest' => URI.escape(%{<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
- if response.kind_of? Net::HTTPSuccess
254
- $LOG.info "Logout notification successfully posted to #{st.service.inspect}."
255
- return true
256
- else
257
- $LOG.error "Service #{st.service.inspect} responed to logout notification with code '#{response.code}'!"
258
- return false
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 "gettext"
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
- include GetText
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
@@ -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, Proc.new { settings.config[:public_dir] || File.join(root, "..", "..", "public") }
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
- return if (public_dir = settings.public_folder).nil?
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 => _("You are currently logged in as '%s'. If this is not you, please log in below.") % tgt.username }
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 => _("The client and server are unable to negotiate authentication. Please try logging in again later.")}
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 => _("The server cannot fulfill this gateway request because no service parameter was given.")}
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 => _("The target service your browser supplied appears to be invalid. Please contact your system administrator for help.")}
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 _("Could not guess the CAS login URI. Please supply a submitToURI parameter with your request.")
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 => _("You have successfully logged in.")}
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 => _("The target service your browser supplied appears to be invalid. Please contact your system administrator for help.")
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 => _("Incorrect username or password.")}
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 => _(e.to_s)}
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 => _("You have successfully logged out.")}
563
+ @message = {:type => 'confirmation', :message => t.notice.success_logged_out}
557
564
 
558
- @message[:message] +=_(" Please click on the following link to continue:") if @continue_url
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
- "To generate a login ticket, you must make a POST request."
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='<%= _("Please wait...") %>'; submitbutton.disabled=true; return true;">
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
- <%= _("Username") %>
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
- <%= _("Password") %>
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="<%= _("LOGIN") %>"
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>