sorcery 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sorcery might be problematic. Click here for more details.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +129 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +139 -0
- data/Rakefile +61 -0
- data/VERSION +1 -0
- data/features/support/env.rb +13 -0
- data/lib/sorcery.rb +28 -0
- data/lib/sorcery/controller.rb +156 -0
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +89 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +43 -0
- data/lib/sorcery/controller/submodules/session_timeout.rb +42 -0
- data/lib/sorcery/crypto_providers/aes256.rb +44 -0
- data/lib/sorcery/crypto_providers/bcrypt.rb +96 -0
- data/lib/sorcery/crypto_providers/md5.rb +39 -0
- data/lib/sorcery/crypto_providers/sha1.rb +40 -0
- data/lib/sorcery/crypto_providers/sha256.rb +55 -0
- data/lib/sorcery/crypto_providers/sha512.rb +55 -0
- data/lib/sorcery/engine.rb +20 -0
- data/lib/sorcery/model.rb +175 -0
- data/lib/sorcery/model/submodules/password_reset.rb +64 -0
- data/lib/sorcery/model/submodules/remember_me.rb +42 -0
- data/lib/sorcery/model/submodules/user_activation.rb +84 -0
- data/spec/Gemfile +11 -0
- data/spec/Gemfile.lock +108 -0
- data/spec/Rakefile +11 -0
- data/spec/rails3/.rspec +1 -0
- data/spec/rails3/Gemfile +12 -0
- data/spec/rails3/Gemfile.lock +114 -0
- data/spec/rails3/Rakefile +10 -0
- data/spec/rails3/app_root/.gitignore +4 -0
- data/spec/rails3/app_root/README +256 -0
- data/spec/rails3/app_root/Rakefile.unused +7 -0
- data/spec/rails3/app_root/app/controllers/application_controller.rb +61 -0
- data/spec/rails3/app_root/app/helpers/application_helper.rb +2 -0
- data/spec/rails3/app_root/app/mailers/sorcery_mailer.rb +25 -0
- data/spec/rails3/app_root/app/models/user.rb +3 -0
- data/spec/rails3/app_root/app/views/layouts/application.html.erb +14 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/activation_email.html.erb +17 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/activation_email.text.erb +9 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
- data/spec/rails3/app_root/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
- data/spec/rails3/app_root/config.ru +4 -0
- data/spec/rails3/app_root/config/application.rb +48 -0
- data/spec/rails3/app_root/config/boot.rb +13 -0
- data/spec/rails3/app_root/config/database.yml +27 -0
- data/spec/rails3/app_root/config/environment.rb +5 -0
- data/spec/rails3/app_root/config/environments/development.rb +26 -0
- data/spec/rails3/app_root/config/environments/in_memory.rb +0 -0
- data/spec/rails3/app_root/config/environments/production.rb +49 -0
- data/spec/rails3/app_root/config/environments/test.rb +35 -0
- data/spec/rails3/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails3/app_root/config/initializers/inflections.rb +10 -0
- data/spec/rails3/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails3/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails3/app_root/config/initializers/session_store.rb +8 -0
- data/spec/rails3/app_root/config/locales/en.yml +5 -0
- data/spec/rails3/app_root/config/routes.rb +67 -0
- data/spec/rails3/app_root/db/migrate/activation/20101224223622_add_activation_to_users.rb +15 -0
- data/spec/rails3/app_root/db/migrate/core/20101224223620_create_users.rb +16 -0
- data/spec/rails3/app_root/db/migrate/password_reset/20101224223622_add_password_reset_to_users.rb +9 -0
- data/spec/rails3/app_root/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
- data/spec/rails3/app_root/db/schema.rb +23 -0
- data/spec/rails3/app_root/db/seeds.rb +7 -0
- data/spec/rails3/app_root/lib/tasks/.gitkeep +0 -0
- data/spec/rails3/app_root/public/404.html +26 -0
- data/spec/rails3/app_root/public/422.html +26 -0
- data/spec/rails3/app_root/public/500.html +26 -0
- data/spec/rails3/app_root/public/favicon.ico +0 -0
- data/spec/rails3/app_root/public/images/rails.png +0 -0
- data/spec/rails3/app_root/public/index.html +239 -0
- data/spec/rails3/app_root/public/javascripts/application.js +2 -0
- data/spec/rails3/app_root/public/javascripts/controls.js +965 -0
- data/spec/rails3/app_root/public/javascripts/dragdrop.js +974 -0
- data/spec/rails3/app_root/public/javascripts/effects.js +1123 -0
- data/spec/rails3/app_root/public/javascripts/prototype.js +6001 -0
- data/spec/rails3/app_root/public/javascripts/rails.js +175 -0
- data/spec/rails3/app_root/public/robots.txt +5 -0
- data/spec/rails3/app_root/public/stylesheets/.gitkeep +0 -0
- data/spec/rails3/app_root/script/rails +6 -0
- data/spec/rails3/app_root/test/fixtures/users.yml +9 -0
- data/spec/rails3/app_root/test/performance/browsing_test.rb +9 -0
- data/spec/rails3/app_root/test/test_helper.rb +13 -0
- data/spec/rails3/app_root/test/unit/user_test.rb +8 -0
- data/spec/rails3/app_root/vendor/plugins/.gitkeep +0 -0
- data/spec/rails3/controller_brute_force_protection_spec.rb +72 -0
- data/spec/rails3/controller_remember_me_spec.rb +65 -0
- data/spec/rails3/controller_session_timeout_spec.rb +49 -0
- data/spec/rails3/controller_spec.rb +115 -0
- data/spec/rails3/spec_helper.rb +115 -0
- data/spec/rails3/user_activation_spec.rb +148 -0
- data/spec/rails3/user_password_reset_spec.rb +76 -0
- data/spec/rails3/user_remember_me_spec.rb +66 -0
- data/spec/rails3/user_spec.rb +283 -0
- data/spec/sorcery_crypto_providers_spec.rb +182 -0
- data/spec/spec_helper.rb +18 -0
- metadata +341 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require "digest/sha2"
|
2
|
+
|
3
|
+
module Sorcery
|
4
|
+
# The activate_sorcery method has a custom_crypto_provider configuration 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.
|
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
|
+
|
48
|
+
def reset!
|
49
|
+
@stretches = 20
|
50
|
+
@join_token = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'sorcery'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module Sorcery
|
5
|
+
# The Sorcery engine takes care of extending ActiveRecord (if used) and ActionController,
|
6
|
+
# With the plugin logic.
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
config.sorcery = ::Sorcery::Controller::Config
|
9
|
+
|
10
|
+
initializer "extend Model with sorcery" do |app|
|
11
|
+
ActiveRecord::Base.send(:include, Sorcery::Model) if defined?(ActiveRecord)
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer "extend Controller with sorcery" do |app|
|
15
|
+
ActionController::Base.send(:include, Sorcery::Controller)
|
16
|
+
ActionController::Base.helper_method :logged_in_user
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Sorcery
|
2
|
+
# This module handles all plugin operations which are related to the Model layer in the MVC pattern.
|
3
|
+
# It should be included into the ORM base class.
|
4
|
+
# In the case of Rails this is usually ActiveRecord (actually, in that case, the plugin does this automatically).
|
5
|
+
#
|
6
|
+
# When included it defines a single method: 'activate_sorcery!' which when called adds the other capabilities to the class.
|
7
|
+
# This method is also the place to configure the plugin in the Model layer.
|
8
|
+
module Model
|
9
|
+
def self.included(klass)
|
10
|
+
klass.class_eval do
|
11
|
+
class << self
|
12
|
+
def activate_sorcery!
|
13
|
+
@sorcery_config = Config.new
|
14
|
+
self.class_eval do
|
15
|
+
extend ClassMethods # included here, before submodules, so they can be overriden by them.
|
16
|
+
include InstanceMethods
|
17
|
+
::Sorcery::Controller::Config.user_class = self
|
18
|
+
@sorcery_config.submodules = ::Sorcery::Controller::Config.submodules
|
19
|
+
@sorcery_config.submodules.each do |mod|
|
20
|
+
begin
|
21
|
+
include Submodules.const_get(mod.to_s.split("_").map {|p| p.capitalize}.join(""))
|
22
|
+
rescue NameError
|
23
|
+
# don't stop on a missing submodule.
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
yield @sorcery_config if block_given?
|
29
|
+
|
30
|
+
self.class_eval do
|
31
|
+
attr_accessor @sorcery_config.password_attribute_name
|
32
|
+
attr_protected @sorcery_config.crypted_password_attribute_name, @sorcery_config.salt_attribute_name
|
33
|
+
before_save :encrypt_password, :if => Proc.new { |record| record.send(sorcery_config.password_attribute_name).present? }
|
34
|
+
after_save :clear_virtual_password, :if => Proc.new { |record| record.send(sorcery_config.password_attribute_name).present? }
|
35
|
+
end
|
36
|
+
@sorcery_config.after_config.each { |c| send(c) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
# Returns the class instance variable for configuration, when called by the class itself.
|
44
|
+
def sorcery_config
|
45
|
+
@sorcery_config
|
46
|
+
end
|
47
|
+
|
48
|
+
# The default authentication method.
|
49
|
+
# Takes a username and password,
|
50
|
+
# Finds the user by the username and compares the user's password to the one supplied to the method.
|
51
|
+
# returns the user if success, nil otherwise.
|
52
|
+
def authenticate(*credentials)
|
53
|
+
raise ArgumentError, "at least 2 arguments required" if credentials.size < 2
|
54
|
+
user = where("#{@sorcery_config.username_attribute_name} = ?", credentials[0]).first
|
55
|
+
salt = user.send(@sorcery_config.salt_attribute_name) if user && !@sorcery_config.salt_attribute_name.nil?
|
56
|
+
user if user && @sorcery_config.before_authenticate.all? {|c| user.send(c)} && (user.send(@sorcery_config.crypted_password_attribute_name)) == encrypt(credentials[1],salt)
|
57
|
+
end
|
58
|
+
|
59
|
+
def encrypt(*tokens)
|
60
|
+
return tokens.first if @sorcery_config.encryption_provider.nil?
|
61
|
+
|
62
|
+
@sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches
|
63
|
+
@sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token
|
64
|
+
CryptoProviders::AES256.key = @sorcery_config.encryption_key if @sorcery_config.encryption_algorithm == :aes256
|
65
|
+
@sorcery_config.encryption_provider.encrypt(*tokens)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module InstanceMethods
|
70
|
+
# Returns the class instance variable for configuration, when called by an instance.
|
71
|
+
def sorcery_config
|
72
|
+
self.class.sorcery_config
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
# creates new salt and saves it.
|
78
|
+
# encrypts password with salt and save it.
|
79
|
+
def encrypt_password
|
80
|
+
config = sorcery_config
|
81
|
+
self.send(:"#{config.salt_attribute_name}=", generate_random_code) if !config.salt_attribute_name.nil?
|
82
|
+
self.send(:"#{config.crypted_password_attribute_name}=", self.class.encrypt(self.send(config.password_attribute_name),salt))
|
83
|
+
end
|
84
|
+
|
85
|
+
def clear_virtual_password
|
86
|
+
config = sorcery_config
|
87
|
+
self.send(:"#{config.password_attribute_name}=", nil)
|
88
|
+
end
|
89
|
+
|
90
|
+
# calls the requested email method on the configured mailer
|
91
|
+
# supports both the ActionMailer 3 way of calling, and the plain old Ruby object way.
|
92
|
+
def generic_send_email(method)
|
93
|
+
config = sorcery_config
|
94
|
+
mail = config.sorcery_mailer.send(config.send(method),self)
|
95
|
+
if defined?(ActionMailer) and config.sorcery_mailer.superclass == ActionMailer::Base
|
96
|
+
mail.deliver
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Random code, used for salt and temporary tokens.
|
101
|
+
def generate_random_code
|
102
|
+
return Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Each class which calls 'activate_sorcery!' receives an instance of this class.
|
107
|
+
# Every submodule which gets loaded may add accessors to this class so that all options will be configured from a single place.
|
108
|
+
class Config
|
109
|
+
|
110
|
+
attr_accessor :username_attribute_name, # change default username attribute, for example, to use :email as the login.
|
111
|
+
:password_attribute_name, # change *virtual* password attribute, the one which is used until an encrypted one is generated.
|
112
|
+
:email_attribute_name, # change default email attribute.
|
113
|
+
:crypted_password_attribute_name, # change default crypted_password attribute.
|
114
|
+
:salt_join_token, # what pattern to use to join the password with the salt
|
115
|
+
:salt_attribute_name, # change default salt attribute.
|
116
|
+
:stretches, # how many times to apply encryption to the password.
|
117
|
+
:encryption_key, # encryption key used to encrypt reversible encryptions such as AES256.
|
118
|
+
|
119
|
+
:submodules, # configured in config/application.rb
|
120
|
+
:before_authenticate, # an array of method names to call before authentication completes. used internally.
|
121
|
+
:after_config # an array of method names to call after configuration by user. used internally.
|
122
|
+
|
123
|
+
attr_reader :encryption_provider, # change default encryption_provider.
|
124
|
+
:custom_encryption_provider, # use an external encryption class.
|
125
|
+
:encryption_algorithm # encryption algorithm name. See 'encryption_algorithm=' below for available options.
|
126
|
+
|
127
|
+
def initialize
|
128
|
+
@defaults = {
|
129
|
+
:@submodules => [],
|
130
|
+
:@username_attribute_name => :username,
|
131
|
+
:@password_attribute_name => :password,
|
132
|
+
:@email_attribute_name => :email,
|
133
|
+
:@crypted_password_attribute_name => :crypted_password,
|
134
|
+
:@encryption_algorithm => :sha256,
|
135
|
+
:@encryption_provider => CryptoProviders::BCrypt,
|
136
|
+
:@custom_encryption_provider => nil,
|
137
|
+
:@encryption_key => nil,
|
138
|
+
:@salt_join_token => "",
|
139
|
+
:@salt_attribute_name => :salt,
|
140
|
+
:@stretches => nil,
|
141
|
+
:@before_authenticate => [],
|
142
|
+
:@after_config => []
|
143
|
+
}
|
144
|
+
reset!
|
145
|
+
end
|
146
|
+
|
147
|
+
# Resets all configuration options to their default values.
|
148
|
+
def reset!
|
149
|
+
@defaults.each do |k,v|
|
150
|
+
instance_variable_set(k,v)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def custom_encryption_provider=(provider)
|
155
|
+
@custom_encryption_provider = @encryption_provider = provider
|
156
|
+
end
|
157
|
+
|
158
|
+
def encryption_algorithm=(algo)
|
159
|
+
@encryption_algorithm = algo
|
160
|
+
@encryption_provider = case @encryption_algorithm
|
161
|
+
when :none then nil
|
162
|
+
when :md5 then CryptoProviders::MD5
|
163
|
+
when :sha1 then CryptoProviders::SHA1
|
164
|
+
when :sha256 then CryptoProviders::SHA256
|
165
|
+
when :sha512 then CryptoProviders::SHA512
|
166
|
+
when :aes256 then CryptoProviders::AES256
|
167
|
+
when :bcrypt then CryptoProviders::BCrypt
|
168
|
+
when :custom then @custom_encryption_provider
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Sorcery
|
2
|
+
module Model
|
3
|
+
module Submodules
|
4
|
+
# This submodule adds the ability to reset password via email confirmation.
|
5
|
+
module PasswordReset
|
6
|
+
def self.included(base)
|
7
|
+
base.sorcery_config.class_eval do
|
8
|
+
attr_accessor :reset_password_code_attribute_name, # reset password code attribute name.
|
9
|
+
:sorcery_mailer, # mailer class. Needed.
|
10
|
+
:reset_password_email_method_name # reset password email method on your mailer class.
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
base.sorcery_config.instance_eval do
|
15
|
+
@defaults.merge!(:@reset_password_code_attribute_name => :reset_password_code,
|
16
|
+
:@sorcery_mailer => nil,
|
17
|
+
:@reset_password_email_method_name => :reset_password_email)
|
18
|
+
|
19
|
+
reset!
|
20
|
+
end
|
21
|
+
|
22
|
+
base.class_eval do
|
23
|
+
clear_reset_password_code_proc = Proc.new do |record|
|
24
|
+
record.valid? && record.send(sorcery_config.password_attribute_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
before_save :clear_reset_password_code, :if =>clear_reset_password_code_proc
|
28
|
+
end
|
29
|
+
|
30
|
+
base.sorcery_config.after_config << :validate_mailer_defined
|
31
|
+
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
base.send(:include, InstanceMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
def validate_mailer_defined
|
38
|
+
msg = "To use password_reset submodule, you must define a mailer (config.sorcery_mailer = YourMailerClass)."
|
39
|
+
raise ArgumentError, msg if @sorcery_config.sorcery_mailer == nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module InstanceMethods
|
44
|
+
def reset_password!
|
45
|
+
config = sorcery_config
|
46
|
+
self.send(:"#{config.reset_password_code_attribute_name}=", generate_random_code)
|
47
|
+
self.class.transaction do
|
48
|
+
self.save!(:validate => false)
|
49
|
+
generic_send_email(:reset_password_email_method_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def clear_reset_password_code
|
56
|
+
config = sorcery_config
|
57
|
+
self.send(:"#{config.reset_password_code_attribute_name}=", nil)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Sorcery
|
2
|
+
module Model
|
3
|
+
module Submodules
|
4
|
+
module RememberMe
|
5
|
+
def self.included(base)
|
6
|
+
base.sorcery_config.class_eval do
|
7
|
+
attr_accessor :remember_me_token_attribute_name, # the attribute in the model class.
|
8
|
+
:remember_me_token_expires_at_attribute_name, # the expires attribute in the model class.
|
9
|
+
:remember_me_for # how long in seconds to remember.
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
base.sorcery_config.instance_eval do
|
14
|
+
@defaults.merge!(:@remember_me_token_attribute_name => :remember_me_token,
|
15
|
+
:@remember_me_token_expires_at_attribute_name => :remember_me_token_expires_at,
|
16
|
+
:@remember_me_for => 7 * 60 * 60 * 24)
|
17
|
+
|
18
|
+
reset!
|
19
|
+
end
|
20
|
+
|
21
|
+
base.send(:include, InstanceMethods)
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
def remember_me!
|
26
|
+
config = sorcery_config
|
27
|
+
self.send(:"#{config.remember_me_token_attribute_name}=", generate_random_code)
|
28
|
+
self.send(:"#{config.remember_me_token_expires_at_attribute_name}=", Time.now + config.remember_me_for)
|
29
|
+
self.save!(:validate => false)
|
30
|
+
end
|
31
|
+
|
32
|
+
def forget_me!
|
33
|
+
config = sorcery_config
|
34
|
+
self.send(:"#{config.remember_me_token_attribute_name}=", nil)
|
35
|
+
self.send(:"#{config.remember_me_token_expires_at_attribute_name}=", nil)
|
36
|
+
self.save!(:validate => false)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Sorcery
|
2
|
+
module Model
|
3
|
+
module Submodules
|
4
|
+
# This submodule adds the ability to make the user activate his account via email
|
5
|
+
# or any other way in which he can recieve an activation code.
|
6
|
+
# with the activation code the user may activate his account.
|
7
|
+
# When using this submodule, supplying a mailer is mandatory.
|
8
|
+
module UserActivation
|
9
|
+
def self.included(base)
|
10
|
+
base.sorcery_config.class_eval do
|
11
|
+
attr_accessor :activation_state_attribute_name, # the attribute name to hold activation state (active/pending).
|
12
|
+
:activation_code_attribute_name, # the attribute name to hold activation code (sent by email).
|
13
|
+
:sorcery_mailer, # your mailer class. Needed.
|
14
|
+
:activation_needed_email_method_name, # activation needed email method on your mailer class.
|
15
|
+
:activation_success_email_method_name, # activation success email method on your mailer class.
|
16
|
+
:prevent_non_active_users_to_login # do you want to prevent or allow users that did not activate by email to login?
|
17
|
+
end
|
18
|
+
|
19
|
+
base.sorcery_config.instance_eval do
|
20
|
+
@defaults.merge!(:@activation_state_attribute_name => :activation_state,
|
21
|
+
:@activation_code_attribute_name => :activation_code,
|
22
|
+
:@sorcery_mailer => nil,
|
23
|
+
:@activation_needed_email_method_name => :activation_needed_email,
|
24
|
+
:@activation_success_email_method_name => :activation_success_email,
|
25
|
+
:@prevent_non_active_users_to_login => true)
|
26
|
+
reset!
|
27
|
+
end
|
28
|
+
|
29
|
+
base.class_eval do
|
30
|
+
before_create :setup_activation
|
31
|
+
after_create :send_activation_needed_email!
|
32
|
+
end
|
33
|
+
|
34
|
+
base.sorcery_config.after_config << :validate_mailer_defined
|
35
|
+
|
36
|
+
base.sorcery_config.before_authenticate << :prevent_non_active_login
|
37
|
+
|
38
|
+
base.extend(ClassMethods)
|
39
|
+
base.send(:include, InstanceMethods)
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def validate_mailer_defined
|
44
|
+
msg = "To use user_activation submodule, you must define a mailer (config.sorcery_mailer = YourMailerClass)."
|
45
|
+
raise ArgumentError, msg if @sorcery_config.sorcery_mailer == nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module InstanceMethods
|
50
|
+
def activate!
|
51
|
+
config = sorcery_config
|
52
|
+
self.send(:"#{config.activation_code_attribute_name}=", nil)
|
53
|
+
self.send(:"#{config.activation_state_attribute_name}=", "active")
|
54
|
+
send_activation_success_email!
|
55
|
+
save!(:validate => false) # don't run validations
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def setup_activation
|
61
|
+
config = sorcery_config
|
62
|
+
generated_activation_code = CryptoProviders::SHA1.encrypt( Time.now.to_s.split(//).sort_by {rand}.join )
|
63
|
+
self.send(:"#{config.activation_code_attribute_name}=", generated_activation_code)
|
64
|
+
self.send(:"#{config.activation_state_attribute_name}=", "pending")
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_activation_needed_email!
|
68
|
+
generic_send_email(:activation_needed_email_method_name) unless sorcery_config.activation_needed_email_method_name.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_activation_success_email!
|
72
|
+
generic_send_email(:activation_success_email_method_name) unless sorcery_config.activation_success_email_method_name.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def prevent_non_active_login
|
76
|
+
config = sorcery_config
|
77
|
+
config.prevent_non_active_users_to_login ? self.send(config.activation_state_attribute_name) == "active" : true
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|