godfat-rubycas-server 0.8.0.20090918

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/CHANGELOG.txt +1 -0
  2. data/History.txt +273 -0
  3. data/LICENSE.txt +504 -0
  4. data/Manifest.txt +83 -0
  5. data/PostInstall.txt +3 -0
  6. data/README.rdoc +26 -0
  7. data/Rakefile +115 -0
  8. data/bin/rubycas-server +13 -0
  9. data/bin/rubycas-server-ctl +9 -0
  10. data/config.example.yml +555 -0
  11. data/config.ru +38 -0
  12. data/config/hoe.rb +78 -0
  13. data/config/requirements.rb +15 -0
  14. data/custom_views.example.rb +11 -0
  15. data/lib/casserver.rb +58 -0
  16. data/lib/casserver/authenticators/active_directory_ldap.rb +11 -0
  17. data/lib/casserver/authenticators/authlogic_crypto_providers/aes256.rb +43 -0
  18. data/lib/casserver/authenticators/authlogic_crypto_providers/bcrypt.rb +92 -0
  19. data/lib/casserver/authenticators/authlogic_crypto_providers/md5.rb +34 -0
  20. data/lib/casserver/authenticators/authlogic_crypto_providers/sha1.rb +35 -0
  21. data/lib/casserver/authenticators/authlogic_crypto_providers/sha512.rb +50 -0
  22. data/lib/casserver/authenticators/base.rb +48 -0
  23. data/lib/casserver/authenticators/client_certificate.rb +46 -0
  24. data/lib/casserver/authenticators/google.rb +54 -0
  25. data/lib/casserver/authenticators/ldap.rb +147 -0
  26. data/lib/casserver/authenticators/ntlm.rb +88 -0
  27. data/lib/casserver/authenticators/open_id.rb +22 -0
  28. data/lib/casserver/authenticators/sql.rb +119 -0
  29. data/lib/casserver/authenticators/sql_authlogic.rb +92 -0
  30. data/lib/casserver/authenticators/sql_encrypted.rb +92 -0
  31. data/lib/casserver/authenticators/sql_md5.rb +19 -0
  32. data/lib/casserver/authenticators/sql_rest_auth.rb +71 -0
  33. data/lib/casserver/authenticators/test.rb +19 -0
  34. data/lib/casserver/cas.rb +322 -0
  35. data/lib/casserver/conf.rb +75 -0
  36. data/lib/casserver/controllers.rb +463 -0
  37. data/lib/casserver/load_picnic.rb +19 -0
  38. data/lib/casserver/localization.rb +82 -0
  39. data/lib/casserver/models.rb +265 -0
  40. data/lib/casserver/postambles.rb +174 -0
  41. data/lib/casserver/utils.rb +30 -0
  42. data/lib/casserver/version.rb +9 -0
  43. data/lib/casserver/views.rb +249 -0
  44. data/lib/rubycas-server.rb +1 -0
  45. data/lib/rubycas-server/version.rb +1 -0
  46. data/po/de_DE/rubycas-server.po +119 -0
  47. data/po/es_ES/rubycas-server.po +115 -0
  48. data/po/fr_FR/rubycas-server.po +116 -0
  49. data/po/ja_JP/rubycas-server.po +118 -0
  50. data/po/pl_PL/rubycas-server.po +115 -0
  51. data/po/pt_BR/rubycas-server.po +115 -0
  52. data/po/ru_RU/rubycas-server.po +110 -0
  53. data/po/rubycas-server.pot +104 -0
  54. data/public/themes/cas.css +121 -0
  55. data/public/themes/notice.png +0 -0
  56. data/public/themes/ok.png +0 -0
  57. data/public/themes/simple/bg.png +0 -0
  58. data/public/themes/simple/login_box_bg.png +0 -0
  59. data/public/themes/simple/logo.png +0 -0
  60. data/public/themes/simple/theme.css +28 -0
  61. data/public/themes/urbacon/bg.png +0 -0
  62. data/public/themes/urbacon/login_box_bg.png +0 -0
  63. data/public/themes/urbacon/logo.png +0 -0
  64. data/public/themes/urbacon/theme.css +33 -0
  65. data/public/themes/warning.png +0 -0
  66. data/resources/init.d.sh +58 -0
  67. data/script/console +10 -0
  68. data/script/destroy +14 -0
  69. data/script/generate +14 -0
  70. data/script/txt2html +82 -0
  71. data/setup.rb +1585 -0
  72. data/tasks/deployment.rake +34 -0
  73. data/tasks/environment.rake +7 -0
  74. data/tasks/localization.rake +11 -0
  75. data/tasks/website.rake +17 -0
  76. data/vendor/isaac_0.9.1/LICENSE +26 -0
  77. data/vendor/isaac_0.9.1/README +78 -0
  78. data/vendor/isaac_0.9.1/TODO +3 -0
  79. data/vendor/isaac_0.9.1/VERSIONS +3 -0
  80. data/vendor/isaac_0.9.1/crypt/ISAAC.rb +171 -0
  81. data/vendor/isaac_0.9.1/isaac.gemspec +39 -0
  82. data/vendor/isaac_0.9.1/setup.rb +596 -0
  83. data/vendor/isaac_0.9.1/test/TC_ISAAC.rb +76 -0
  84. metadata +200 -0
data/config.ru ADDED
@@ -0,0 +1,38 @@
1
+ # This is the Rackup initialization script for running RubyCAS-Server under Passenger/Rack.
2
+ #
3
+ # This file doesn't really have anything to do with your server's "configuration",
4
+ # and you almost certainly don't need to modify it. Instead, a config file should
5
+ # have been created for you (probably under /etc/rubycas-server/config.yml) -- this
6
+ # is the file you'll want to modify. If for some reason the configuration file
7
+ # was not created for you, have a look at the config.example.yml template and
8
+ # go from there.
9
+
10
+ require 'rubygems'
11
+ require 'rack'
12
+
13
+ $APP_NAME = 'rubycas-server'
14
+ $APP_ROOT = ::File.dirname(::File.expand_path(__FILE__))
15
+
16
+ if ::File.exist?("#{$APP_ROOT}/tmp/debug.txt")
17
+ require 'ruby-debug'
18
+ Debugger.wait_connection = true
19
+ Debugger.start_remote
20
+ end
21
+
22
+ $: << $APP_ROOT + "/lib"
23
+
24
+ require 'casserver/load_picnic'
25
+ require 'picnic'
26
+ require 'casserver'
27
+
28
+ CASServer.create
29
+
30
+ if $CONF.uri_path
31
+ map($CONF.uri_path) do
32
+ # FIXME: this probably isn't the smartest way of remapping the themes dir to uri_path/themes
33
+ use Rack::Static, $CONF[:static] if $CONF[:static]
34
+ run CASServer
35
+ end
36
+ else
37
+ run CASServer
38
+ end
data/config/hoe.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'rubycas-server/version'
2
+
3
+ AUTHOR = 'Matt Zukowski' # can also be an array of Authors
4
+ EMAIL = "matt@zukowski.ca"
5
+ DESCRIPTION = "Provides single sign-on authentication for web applications using the CAS protocol."
6
+ GEM_NAME = 'rubycas-server' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rubycas-server' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ 'activesupport',
12
+ 'activerecord',
13
+ 'locale',
14
+ 'gettext',
15
+ ['picnic', '>= 0.8.1']
16
+ ] # An array of rubygem dependencies [name, version]
17
+
18
+ @config_file = "~/.rubyforge/user-config.yml"
19
+ @config = nil
20
+ RUBYFORGE_USERNAME = "unknown"
21
+ def rubyforge_username
22
+ unless @config
23
+ begin
24
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
25
+ rescue
26
+ puts <<-EOS
27
+ ERROR: No rubyforge config file found: #{@config_file}
28
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
29
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
30
+ EOS
31
+ exit
32
+ end
33
+ end
34
+ RUBYFORGE_USERNAME.replace @config["username"]
35
+ end
36
+
37
+ ENV['NODOT'] = '1'
38
+
39
+ #REV = nil
40
+ # UNCOMMENT IF REQUIRED:
41
+ REV = Time.now.strftime('%Y%m%d%H%M')
42
+ VERS = CASServer::VERSION::STRING + (REV ? ".#{REV}" : "")
43
+ RDOC_OPTS = ['--quiet', '--title', 'rubycas-server documentation',
44
+ "--opname", "index.html",
45
+ "--line-numbers",
46
+ "--main", "README",
47
+ "--inline-source"]
48
+
49
+ class Hoe
50
+ def extra_deps
51
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
52
+ @extra_deps
53
+ end
54
+ end
55
+
56
+ # Generate all the Rake tasks
57
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
58
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
59
+ p.developer(AUTHOR, EMAIL)
60
+ p.description = DESCRIPTION
61
+ p.summary = DESCRIPTION
62
+ p.url = HOMEPATH
63
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
64
+ p.test_globs = ["test/**/test_*.rb"]
65
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
66
+
67
+ # == Optional
68
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
69
+ p.extra_deps = EXTRA_DEPENDENCIES
70
+
71
+ p.spec_extras = {:executables => ['rubycas-server', 'rubycas-server-ctl']} # A hash of extra values to set in the gemspec.
72
+ end
73
+
74
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
75
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}"
76
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
77
+ $hoe.rsync_args = '-av --delete --ignore-errors'
78
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,11 @@
1
+ # Custom views file; add methods to the module definition below
2
+
3
+ module CASServer::Views
4
+
5
+ # Override views here, for example, a custom login form:
6
+ def login_form
7
+ # Add your custom login form here, using Markaby
8
+ # See the original views.rb file at lib/casserver/views.rb for method names and usage
9
+ end
10
+
11
+ end
data/lib/casserver.rb ADDED
@@ -0,0 +1,58 @@
1
+ unless Object.const_defined?(:Picnic)
2
+ $APP_NAME ||= 'rubycas-server'
3
+ $APP_ROOT ||= File.expand_path(File.dirname(__FILE__)+'/..')
4
+
5
+ require 'casserver/load_picnic'
6
+ end
7
+
8
+ require 'yaml'
9
+ require 'markaby'
10
+
11
+ require "casserver/conf"
12
+ require "picnic/logger"
13
+
14
+ $: << File.dirname(File.expand_path(__FILE__))
15
+
16
+ $: << File.expand_path("#{File.dirname(__FILE__)}/../vendor/isaac_0.9.1")
17
+ require 'crypt/ISAAC'
18
+
19
+ Camping.goes :CASServer
20
+
21
+ Picnic::Logger.init_global_logger!
22
+
23
+ require "casserver/utils"
24
+ require "casserver/models"
25
+ require "casserver/cas"
26
+ require "casserver/views"
27
+ require "casserver/controllers"
28
+ require "casserver/localization"
29
+
30
+ def CASServer.create
31
+ $LOG.info "Creating RubyCAS-Server with pid #{Process.pid}."
32
+
33
+
34
+ CASServer::Models::Base.establish_connection($CONF.database) unless CASServer::Models::Base.connected?
35
+ CASServer::Models.create_schema
36
+
37
+ #TODO: these warnings should eventually be deleted
38
+ if $CONF.service_ticket_expiry
39
+ $LOG.warn "The 'service_ticket_expiry' option has been renamed to 'maximum_unused_service_ticket_lifetime'. Please make the necessary change to your config file!"
40
+ $CONF.maximum_unused_service_ticket_lifetime ||= $CONF.service_ticket_expiry
41
+ end
42
+ if $CONF.login_ticket_expiry
43
+ $LOG.warn "The 'login_ticket_expiry' option has been renamed to 'maximum_unused_login_ticket_lifetime'. Please make the necessary change to your config file!"
44
+ $CONF.maximum_unused_login_ticket_lifetime ||= $CONF.login_ticket_expiry
45
+ end
46
+ if $CONF.ticket_granting_ticket_expiry || $CONF.proxy_granting_ticket_expiry
47
+ $LOG.warn "The 'ticket_granting_ticket_expiry' and 'proxy_granting_ticket_expiry' options have been renamed to 'maximum_session_lifetime'. Please make the necessary change to your config file!"
48
+ $CONF.maximum_session_lifetime ||= $CONF.ticket_granting_ticket_expiry || $CONF.proxy_granting_ticket_expiry
49
+ end
50
+
51
+ if $CONF.maximum_session_lifetime
52
+ CASServer::Models::ServiceTicket.cleanup($CONF.maximum_session_lifetime, $CONF.maximum_unused_service_ticket_lifetime)
53
+ CASServer::Models::LoginTicket.cleanup($CONF.maximum_session_lifetime, $CONF.maximum_unused_login_ticket_lifetime)
54
+ CASServer::Models::ProxyGrantingTicket.cleanup($CONF.maximum_session_lifetime)
55
+ CASServer::Models::TicketGrantingTicket.cleanup($CONF.maximum_session_lifetime)
56
+ end
57
+ end
58
+
@@ -0,0 +1,11 @@
1
+ require 'casserver/authenticators/ldap'
2
+
3
+ # Slightly modified version of the LDAP authenticator for Microsoft's ActiveDirectory.
4
+ # The only difference is that the default_username_attribute for AD is 'sAMAccountName'
5
+ # rather than 'uid'.
6
+ class CASServer::Authenticators::ActiveDirectoryLDAP < CASServer::Authenticators::LDAP
7
+ protected
8
+ def default_username_attribute
9
+ "sAMAccountName"
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ require "openssl"
2
+
3
+ module Authlogic
4
+ module CryptoProviders
5
+ # This encryption method is reversible if you have the supplied key. So in order to use this encryption method you must supply it with a key first.
6
+ # In an initializer, or before your application initializes, you should do the following:
7
+ #
8
+ # Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrably a bunch of random characters"
9
+ #
10
+ # My final comment is that this is a strong encryption method, but its main weakness is that its reversible. If you do not need to reverse the hash
11
+ # then you should consider Sha512 or BCrypt instead.
12
+ #
13
+ # Keep your key in a safe place, some even say the key should be stored on a separate server.
14
+ # This won't hurt performance because the only time it will try and access the key on the separate server is during initialization, which only
15
+ # happens once. The reasoning behind this is if someone does compromise your server they won't have the key also. Basically, you don't want to
16
+ # store the key with the lock.
17
+ class AES256
18
+ class << self
19
+ attr_writer :key
20
+
21
+ def encrypt(*tokens)
22
+ aes.encrypt
23
+ aes.key = @key
24
+ [aes.update(tokens.join) + aes.final].pack("m").chomp
25
+ end
26
+
27
+ def matches?(crypted, *tokens)
28
+ aes.decrypt
29
+ aes.key = @key
30
+ (aes.update(crypted.unpack("m").first) + aes.final) == tokens.join
31
+ rescue OpenSSL::CipherError
32
+ false
33
+ end
34
+
35
+ private
36
+ def aes
37
+ raise ArgumentError.new("You must provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
38
+ @aes ||= OpenSSL::Cipher::Cipher.new("AES-256-ECB")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,92 @@
1
+ begin
2
+ require "bcrypt"
3
+ rescue LoadError
4
+ end
5
+
6
+ module Authlogic
7
+ module CryptoProviders
8
+ # For most apps Sha512 is plenty secure, but if you are building an app that stores nuclear launch codes you might want to consier BCrypt. This is an extremely
9
+ # secure hashing algorithm, mainly because it is slow. A brute force attack on a BCrypt encrypted password would take much longer than a brute force attack on a
10
+ # password encrypted with a Sha algorithm. Keep in mind you are sacrificing performance by using this, generating a password takes exponentially longer than any
11
+ # of the Sha algorithms. I did some benchmarking to save you some time with your decision:
12
+ #
13
+ # require "bcrypt"
14
+ # require "digest"
15
+ # require "benchmark"
16
+ #
17
+ # Benchmark.bm(18) do |x|
18
+ # x.report("BCrypt (cost = 10:") { 100.times { BCrypt::Password.create("mypass", :cost => 10) } }
19
+ # x.report("BCrypt (cost = 2:") { 100.times { BCrypt::Password.create("mypass", :cost => 2) } }
20
+ # x.report("Sha512:") { 100.times { Digest::SHA512.hexdigest("mypass") } }
21
+ # x.report("Sha1:") { 100.times { Digest::SHA1.hexdigest("mypass") } }
22
+ # end
23
+ #
24
+ # user system total real
25
+ # BCrypt (cost = 10): 10.780000 0.060000 10.840000 ( 11.100289)
26
+ # BCrypt (cost = 2): 0.180000 0.000000 0.180000 ( 0.181914)
27
+ # Sha512: 0.000000 0.000000 0.000000 ( 0.000829)
28
+ # Sha1: 0.000000 0.000000 0.000000 ( 0.000395)
29
+ #
30
+ # You can play around with the cost to get that perfect balance between performance and security.
31
+ #
32
+ # Decided BCrypt is for you? Just insall the bcrypt gem:
33
+ #
34
+ # gem install bcrypt-ruby
35
+ #
36
+ # Tell acts_as_authentic to use it:
37
+ #
38
+ # acts_as_authentic do |c|
39
+ # c.crypto_provider = Authlogic::CryptoProviders::BCrypt
40
+ # end
41
+ #
42
+ # You are good to go!
43
+ class BCrypt
44
+ class << self
45
+ # This is the :cost option for the BCrpyt library. The higher the cost the more secure it is and the longer is take the generate a hash. By default this is 10.
46
+ # Set this to whatever you want, play around with it to get that perfect balance between security and performance.
47
+ def cost
48
+ @cost ||= 10
49
+ end
50
+ attr_writer :cost
51
+
52
+ # Creates a BCrypt hash for the password passed.
53
+ def encrypt(*tokens)
54
+ ::BCrypt::Password.create(join_tokens(tokens), :cost => cost)
55
+ end
56
+
57
+ # Does the hash match the tokens? Uses the same tokens that were used to encrypt.
58
+ def matches?(hash, *tokens)
59
+ $LOG.debug hash
60
+ $LOG.debug tokens.inspect
61
+
62
+ hash = new_from_hash(hash)
63
+ return false if hash.blank?
64
+ hash == join_tokens(tokens)
65
+ end
66
+
67
+ # This method is used as a flag to tell Authlogic to "resave" the password upon a successful login, using the new cost
68
+ def cost_matches?(hash)
69
+ hash = new_from_hash(hash)
70
+ if hash.blank?
71
+ false
72
+ else
73
+ hash.cost == cost
74
+ end
75
+ end
76
+
77
+ private
78
+ def join_tokens(tokens)
79
+ tokens.flatten.join
80
+ end
81
+
82
+ def new_from_hash(hash)
83
+ begin
84
+ ::BCrypt::Password.new(hash)
85
+ rescue ::BCrypt::Errors::InvalidHash
86
+ return nil
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,34 @@
1
+ require "digest/md5"
2
+
3
+ module Authlogic
4
+ module CryptoProviders
5
+ # This class was made for the users transitioning from md5 based systems.
6
+ # I highly discourage using this crypto provider as it superbly inferior
7
+ # to your other options.
8
+ #
9
+ # Please use any other provider offered by Authlogic.
10
+ class MD5
11
+ class << self
12
+ attr_accessor :join_token
13
+
14
+ # The number of times to loop through the encryption.
15
+ def stretches
16
+ @stretches ||= 1
17
+ end
18
+ attr_writer :stretches
19
+
20
+ # Turns your raw password into a MD5 hash.
21
+ def encrypt(*tokens)
22
+ digest = tokens.flatten.join(join_token)
23
+ stretches.times { digest = Digest::MD5.hexdigest(digest) }
24
+ digest
25
+ end
26
+
27
+ # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
28
+ def matches?(crypted, *tokens)
29
+ encrypt(*tokens) == crypted
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ require "digest/sha1"
2
+
3
+ module Authlogic
4
+ module CryptoProviders
5
+ # This class was made for the users transitioning from restful_authentication. I highly discourage using this
6
+ # crypto provider as it inferior to your other options. Please use any other provider offered by Authlogic.
7
+ class Sha1
8
+ class << self
9
+ def join_token
10
+ @join_token ||= "--"
11
+ end
12
+ attr_writer :join_token
13
+
14
+ # The number of times to loop through the encryption. This is ten because that is what restful_authentication defaults to.
15
+ def stretches
16
+ @stretches ||= 10
17
+ end
18
+ attr_writer :stretches
19
+
20
+ # Turns your raw password into a Sha1 hash.
21
+ def encrypt(*tokens)
22
+ tokens = tokens.flatten
23
+ digest = tokens.shift
24
+ stretches.times { digest = Digest::SHA1.hexdigest([digest, *tokens].join(join_token)) }
25
+ digest
26
+ end
27
+
28
+ # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
29
+ def matches?(crypted, *tokens)
30
+ encrypt(*tokens) == crypted
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ require "digest/sha2"
2
+
3
+ module Authlogic
4
+ # The acts_as_authentic method has a crypto_provider option. This allows you to use any type of encryption you like.
5
+ # Just create a class with a class level encrypt and matches? method. See example below.
6
+ #
7
+ # === Example
8
+ #
9
+ # class MyAwesomeEncryptionMethod
10
+ # def self.encrypt(*tokens)
11
+ # # the tokens passed will be an array of objects, what type of object is irrelevant,
12
+ # # just do what you need to do with them and return a single encrypted string.
13
+ # # for example, you will most likely join all of the objects into a single string and then encrypt that string
14
+ # end
15
+ #
16
+ # def self.matches?(crypted, *tokens)
17
+ # # return true if the crypted string matches the tokens.
18
+ # # depending on your algorithm you might decrypt the string then compare it to the token, or you might
19
+ # # encrypt the tokens and make sure it matches the crypted string, its up to you
20
+ # end
21
+ # end
22
+ module CryptoProviders
23
+ # = Sha512
24
+ #
25
+ # Uses the Sha512 hash algorithm to encrypt passwords.
26
+ class Sha512
27
+ class << self
28
+ attr_accessor :join_token
29
+
30
+ # The number of times to loop through the encryption. This is ten because that is what restful_authentication defaults to.
31
+ def stretches
32
+ @stretches ||= 20
33
+ end
34
+ attr_writer :stretches
35
+
36
+ # Turns your raw password into a Sha512 hash.
37
+ def encrypt(*tokens)
38
+ digest = tokens.flatten.join(join_token)
39
+ stretches.times { digest = Digest::SHA512.hexdigest(digest) }
40
+ digest
41
+ end
42
+
43
+ # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
44
+ def matches?(crypted, *tokens)
45
+ encrypt(*tokens) == crypted
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end