merbful_authentication 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +25 -0
- data/README +69 -0
- data/Rakefile +35 -0
- data/TODO +5 -0
- data/activerecord_generators/merbful_authentication_model/merbful_authentication_model_generator.rb +65 -0
- data/activerecord_generators/merbful_authentication_model/templates/authenticated_system_orm_map.rb +34 -0
- data/activerecord_generators/merbful_authentication_model/templates/migration.rb +20 -0
- data/activerecord_generators/merbful_authentication_model/templates/model.rb +63 -0
- data/datamapper_generators/merbful_authentication_model/merbful_authentication_model_generator.rb +60 -0
- data/datamapper_generators/merbful_authentication_model/templates/authenticated_system_orm_map.rb +34 -0
- data/datamapper_generators/merbful_authentication_model/templates/model.rb +78 -0
- data/lib/merbful_authentication.rb +10 -0
- data/lib/merbful_authentication/merbtasks.rb +6 -0
- data/merb_generators/authentication/USAGE +5 -0
- data/merb_generators/authentication/authentication_generator.rb +256 -0
- data/merb_generators/authentication/templates/activation.html.erb +1 -0
- data/merb_generators/authentication/templates/activation.text.erb +1 -0
- data/merb_generators/authentication/templates/authenticated_system_controller.rb +132 -0
- data/merb_generators/authentication/templates/authenticated_system_model.rb +97 -0
- data/merb_generators/authentication/templates/login.html.erb +14 -0
- data/merb_generators/authentication/templates/mail_controller.rb +13 -0
- data/merb_generators/authentication/templates/model_controller.rb +33 -0
- data/merb_generators/authentication/templates/new_model.html.erb +18 -0
- data/merb_generators/authentication/templates/session_controller.rb +33 -0
- data/merb_generators/authentication/templates/signup.html.erb +8 -0
- data/merb_generators/authentication/templates/signup.text.erb +8 -0
- data/rspec_generators/merbful_authentication_tests/merbful_authentication_tests_generator.rb +83 -0
- data/rspec_generators/merbful_authentication_tests/templates/authenticated_system_spec_helper.rb +22 -0
- data/rspec_generators/merbful_authentication_tests/templates/model_controller_spec.rb +78 -0
- data/rspec_generators/merbful_authentication_tests/templates/model_spec.rb +357 -0
- data/rspec_generators/merbful_authentication_tests/templates/model_spec_helper.rb +8 -0
- data/rspec_generators/merbful_authentication_tests/templates/session_controller_spec.rb +101 -0
- data/rspec_generators/merbful_authentication_tests/templates/user_mailer_spec.rb +70 -0
- data/test_unit_generators/merbful_authentication_tests/USAGE +5 -0
- data/test_unit_generators/merbful_authentication_tests/merbful_authentication_tests_generator.rb +84 -0
- data/test_unit_generators/merbful_authentication_tests/templates/authenticated_system_test_helper.rb +50 -0
- data/test_unit_generators/merbful_authentication_tests/templates/functional_test.rb +92 -0
- data/test_unit_generators/merbful_authentication_tests/templates/mailer_test.rb +66 -0
- data/test_unit_generators/merbful_authentication_tests/templates/model_functional_test.rb +92 -0
- data/test_unit_generators/merbful_authentication_tests/templates/model_test_helper.rb +8 -0
- data/test_unit_generators/merbful_authentication_tests/templates/unit_test.rb +142 -0
- metadata +114 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# make sure we're running inside Merb
|
2
|
+
if defined?(Merb::Plugins)
|
3
|
+
|
4
|
+
# Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
|
5
|
+
Merb::Plugins.config[:merbful_authentication] = {
|
6
|
+
:chickens => false
|
7
|
+
}
|
8
|
+
|
9
|
+
Merb::Plugins.add_rakefiles "merbful_authentication/merbtasks"
|
10
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'merb'
|
2
|
+
class AuthenticationGenerator < RubiGen::Base
|
3
|
+
|
4
|
+
default_options :author => nil
|
5
|
+
|
6
|
+
attr_reader :name,
|
7
|
+
:class_name,
|
8
|
+
:class_path,
|
9
|
+
:file_name,
|
10
|
+
:class_nesting,
|
11
|
+
:class_nesting_depth,
|
12
|
+
:plural_name,
|
13
|
+
:singular_name,
|
14
|
+
:controller_name,
|
15
|
+
:controller_class_path,
|
16
|
+
:controller_file_path,
|
17
|
+
:controller_class_nesting,
|
18
|
+
:controller_class_nesting_depth,
|
19
|
+
:controller_class_name,
|
20
|
+
:controller_singular_name,
|
21
|
+
:controller_plural_name
|
22
|
+
alias_method :controller_file_name, :controller_singular_name
|
23
|
+
alias_method :controller_table_name, :controller_plural_name
|
24
|
+
attr_reader :model_controller_name,
|
25
|
+
:model_controller_class_path,
|
26
|
+
:model_controller_file_path,
|
27
|
+
:model_controller_class_nesting,
|
28
|
+
:model_controller_class_nesting_depth,
|
29
|
+
:model_controller_class_name,
|
30
|
+
:model_controller_singular_name,
|
31
|
+
:model_controller_plural_name
|
32
|
+
alias_method :model_controller_file_name, :model_controller_singular_name
|
33
|
+
alias_method :model_controller_table_name, :model_controller_plural_name
|
34
|
+
attr_reader :include_activation
|
35
|
+
|
36
|
+
def initialize(runtime_args, runtime_options = {})
|
37
|
+
usage if runtime_args.empty?
|
38
|
+
super
|
39
|
+
extract_options
|
40
|
+
assign_names!(runtime_args.shift)
|
41
|
+
@include_activation = options[:include_activation]
|
42
|
+
|
43
|
+
@controller_name = runtime_args.shift || 'sessions'
|
44
|
+
@model_controller_name = @name.pluralize
|
45
|
+
@mailer_controller_name = @name
|
46
|
+
|
47
|
+
# sessions controller
|
48
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
49
|
+
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
|
50
|
+
|
51
|
+
if @controller_class_nesting.empty?
|
52
|
+
@controller_class_name = @controller_class_name_without_nesting
|
53
|
+
else
|
54
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# model controller
|
58
|
+
base_name, @model_controller_class_path, @model_controller_file_path, @model_controller_class_nesting, @model_controller_class_nesting_depth = extract_modules(@model_controller_name)
|
59
|
+
@model_controller_class_name_without_nesting, @model_controller_singular_name, @model_controller_plural_name = inflect_names(base_name)
|
60
|
+
|
61
|
+
if @model_controller_class_nesting.empty?
|
62
|
+
@model_controller_class_name = @model_controller_class_name_without_nesting
|
63
|
+
else
|
64
|
+
@model_controller_class_name = "#{@model_controller_class_nesting}::#{@model_controller_class_name_without_nesting}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def manifest
|
69
|
+
manifest_result = record do |m|
|
70
|
+
# Check for class naming collisions.
|
71
|
+
m.class_collisions controller_class_path, "#{controller_class_name}", # Sessions Controller
|
72
|
+
"Merb::#{controller_class_name}Helper"
|
73
|
+
m.class_collisions model_controller_class_path, "#{model_controller_class_name}", # Model Controller
|
74
|
+
"Merb::#{model_controller_class_name}Helper"
|
75
|
+
m.class_collisions class_path, "#{class_name}", "#{class_name}Mailer"# , "#{class_name}MailerTest", "#{class_name}Observer"
|
76
|
+
m.class_collisions [], 'AuthenticatedSystem::Controller', 'AuthenticatedSystem::Model'
|
77
|
+
|
78
|
+
# Controller, helper, views, and test directories.
|
79
|
+
|
80
|
+
m.directory File.join('app/controllers', controller_class_path)
|
81
|
+
m.directory File.join('app/controllers', model_controller_class_path)
|
82
|
+
|
83
|
+
m.directory File.join('app/helpers', controller_class_path)
|
84
|
+
m.directory File.join('app/views', controller_class_path, controller_file_name)
|
85
|
+
|
86
|
+
m.directory File.join('app/controllers', model_controller_class_path)
|
87
|
+
m.directory File.join('app/helpers', model_controller_class_path)
|
88
|
+
m.directory File.join('app/views', model_controller_class_path, model_controller_file_name)
|
89
|
+
|
90
|
+
# Generate the authenticated system" libraries
|
91
|
+
m.directory "lib"
|
92
|
+
m.template "authenticated_system_controller.rb", "lib/authenticated_system_controller.rb"
|
93
|
+
m.template "authenticated_system_model.rb", "lib/authenticated_system_model.rb"
|
94
|
+
|
95
|
+
# Mailer directory for activation
|
96
|
+
if options[:include_activation]
|
97
|
+
m.directory File.join('app/mailers/views', "#{singular_name}_mailer")
|
98
|
+
m.template "mail_controller.rb", File.join('app/mailers',
|
99
|
+
"#{singular_name}_mailer.rb")
|
100
|
+
[:html, :text].each do |format|
|
101
|
+
[:signup, :activation].each do |action|
|
102
|
+
m.template "#{action}.#{format}.erb", File.join('app/mailers/views',
|
103
|
+
"#{singular_name}_mailer",
|
104
|
+
"#{action}_notification.#{format}.erb")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Generate the model
|
110
|
+
model_attributes = {
|
111
|
+
:class_name => class_name,
|
112
|
+
:class_path => class_path,
|
113
|
+
:file_name => file_name,
|
114
|
+
:class_nesting => class_nesting,
|
115
|
+
:class_nesting_depth => class_nesting_depth,
|
116
|
+
:plural_name => plural_name,
|
117
|
+
:singular_name => singular_name,
|
118
|
+
:include_activation => options[:include_activation]
|
119
|
+
}
|
120
|
+
m.dependency "merbful_authentication_model", [name], model_attributes
|
121
|
+
|
122
|
+
# Generate the sessions controller
|
123
|
+
m.template "session_controller.rb", File.join('app/controllers',
|
124
|
+
controller_class_path,
|
125
|
+
"#{controller_file_name}.rb")
|
126
|
+
|
127
|
+
# Generate the model controller
|
128
|
+
m.template "model_controller.rb", File.join('app/controllers',
|
129
|
+
model_controller_class_path,
|
130
|
+
"#{model_controller_file_name}.rb")
|
131
|
+
|
132
|
+
# Controller templates
|
133
|
+
m.template 'login.html.erb', File.join('app/views', controller_class_path, controller_file_name, "new.html.erb")
|
134
|
+
m.template 'new_model.html.erb', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.html.erb")
|
135
|
+
|
136
|
+
|
137
|
+
controller_attributes = {
|
138
|
+
:controller_name => controller_name,
|
139
|
+
:controller_class_path => controller_class_path,
|
140
|
+
:controller_file_path => controller_file_path,
|
141
|
+
:controller_class_nesting => controller_class_nesting,
|
142
|
+
:controller_class_nesting_depth => controller_class_nesting_depth,
|
143
|
+
:controller_class_name => controller_class_name,
|
144
|
+
:controller_singular_name => controller_singular_name,
|
145
|
+
:controller_plural_name => controller_plural_name,
|
146
|
+
:model_controller_name => model_controller_name,
|
147
|
+
:model_controller_class_path => model_controller_class_path,
|
148
|
+
:model_controller_file_path => model_controller_file_path,
|
149
|
+
:model_controller_class_nesting => model_controller_class_nesting,
|
150
|
+
:model_controller_class_nesting_depth => model_controller_class_nesting_depth,
|
151
|
+
:model_controller_class_name => model_controller_class_name,
|
152
|
+
:model_controller_singular_name => model_controller_singular_name,
|
153
|
+
:model_controller_plural_name => model_controller_plural_name,
|
154
|
+
:include_activation => options[:include_activation]
|
155
|
+
}
|
156
|
+
# Generate the tests
|
157
|
+
m.dependency "merbful_authentication_tests", [name], model_attributes.dup.merge!(controller_attributes)
|
158
|
+
end
|
159
|
+
|
160
|
+
action = nil
|
161
|
+
action = $0.split("/")[1]
|
162
|
+
case action
|
163
|
+
when 'generate'
|
164
|
+
puts finishing_message
|
165
|
+
when 'destroy'
|
166
|
+
puts "Thanx for using merbful_authentication"
|
167
|
+
end
|
168
|
+
|
169
|
+
manifest_result
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
protected
|
174
|
+
# Override with your own usage banner.
|
175
|
+
def banner
|
176
|
+
out = <<-EOD;
|
177
|
+
Usage: #{$0} authenticated ModelName [ControllerName]
|
178
|
+
EOD
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_options!(opt)
|
182
|
+
opt.separator ''
|
183
|
+
opt.separator 'Options:'
|
184
|
+
opt.on("--skip-migration",
|
185
|
+
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
186
|
+
opt.on("--include-activation",
|
187
|
+
"Generate signup 'activation code' confirmation via email") { |v| options[:include_activation] = true }
|
188
|
+
end
|
189
|
+
|
190
|
+
def extract_options
|
191
|
+
# for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
|
192
|
+
# Templates can access these value via the attr_reader-generated methods, but not the
|
193
|
+
# raw instance variable value.
|
194
|
+
# @author = options[:author]
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# Borrowed from RailsGenerators
|
199
|
+
# Extract modules from filesystem-style or ruby-style path:
|
200
|
+
# good/fun/stuff
|
201
|
+
# Good::Fun::Stuff
|
202
|
+
# produce the same results.
|
203
|
+
def extract_modules(name)
|
204
|
+
modules = name.include?('/') ? name.split('/') : name.split('::')
|
205
|
+
name = modules.pop
|
206
|
+
path = modules.map { |m| m.underscore }
|
207
|
+
file_path = (path + [name.underscore]).join('/')
|
208
|
+
nesting = modules.map { |m| m.camelize }.join('::')
|
209
|
+
[name, path, file_path, nesting, modules.size]
|
210
|
+
end
|
211
|
+
|
212
|
+
def inflect_names(name)
|
213
|
+
camel = name.camelize
|
214
|
+
under = camel.underscore
|
215
|
+
plural = under.pluralize
|
216
|
+
[camel, under, plural]
|
217
|
+
end
|
218
|
+
|
219
|
+
def assign_names!(name)
|
220
|
+
@name = name
|
221
|
+
base_name, @class_path, @file_name, @class_nesting, @class_nesting_depth = extract_modules(@name)
|
222
|
+
@class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
|
223
|
+
@table_name = @name.pluralize
|
224
|
+
# @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name
|
225
|
+
@table_name.gsub! '/', '_'
|
226
|
+
if @class_nesting.empty?
|
227
|
+
@class_name = @class_name_without_nesting
|
228
|
+
else
|
229
|
+
@table_name = @class_nesting.underscore << "_" << @table_name
|
230
|
+
@class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def finishing_message
|
237
|
+
output = <<-EOD
|
238
|
+
#{"-" * 70}
|
239
|
+
Don't forget to:
|
240
|
+
|
241
|
+
- add named routes for authentication. These are currently required
|
242
|
+
In config/router.rb
|
243
|
+
|
244
|
+
r.resources :#{plural_name}
|
245
|
+
r.match("/login").to(:controller => "#{controller_class_name}", :action => "create").name(:login)
|
246
|
+
r.match("/logout").to(:controller => "#{controller_class_name}", :action => "destroy").name(:logout)
|
247
|
+
EOD
|
248
|
+
if options[:include_activation]
|
249
|
+
output << " r.match(\"/#{plural_name}/activate/:activation_code\").to(:controller => \"#{model_controller_class_name}\", :action => \"activate\").name(:#{singular_name}_activation)"
|
250
|
+
end
|
251
|
+
|
252
|
+
output << "\n\n" << ("-" * 70)
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Your email has been authenticated <%%= @<%= singular_name %>.login %>
|
@@ -0,0 +1 @@
|
|
1
|
+
Your email has been authenticated <%%= @<%= singular_name %>.login %>
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module AuthenticatedSystem
|
2
|
+
module Controller
|
3
|
+
protected
|
4
|
+
# Returns true or false if the <%= singular_name %> is logged in.
|
5
|
+
# Preloads @current_<%= singular_name %> with the <%= singular_name %> model if they're logged in.
|
6
|
+
def logged_in?
|
7
|
+
current_<%= singular_name %> != :false
|
8
|
+
end
|
9
|
+
|
10
|
+
# Accesses the current <%= singular_name %> from the session. Set it to :false if login fails
|
11
|
+
# so that future calls do not hit the database.
|
12
|
+
def current_<%= singular_name %>
|
13
|
+
@current_<%= singular_name %> ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Store the given <%= singular_name %> in the session.
|
17
|
+
def current_<%= singular_name %>=(new_<%= singular_name %>)
|
18
|
+
session[:<%= singular_name %>] = (new_<%= singular_name %>.nil? || new_<%= singular_name %>.is_a?(Symbol)) ? nil : new_<%= singular_name %>.id
|
19
|
+
@current_<%= singular_name %> = new_<%= singular_name %>
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if the <%= singular_name %> is authorized
|
23
|
+
#
|
24
|
+
# Override this method in your controllers if you want to restrict access
|
25
|
+
# to only a few actions or if you want to check if the <%= singular_name %>
|
26
|
+
# has the correct rights.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# # only allow nonbobs
|
31
|
+
# def authorized?
|
32
|
+
# current_<%= singular_name %>.login != "bob"
|
33
|
+
# end
|
34
|
+
def authorized?
|
35
|
+
logged_in?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Filter method to enforce a login requirement.
|
39
|
+
#
|
40
|
+
# To require logins for all actions, use this in your controllers:
|
41
|
+
#
|
42
|
+
# before_filter :login_required
|
43
|
+
#
|
44
|
+
# To require logins for specific actions, use this in your controllers:
|
45
|
+
#
|
46
|
+
# before_filter :login_required, :only => [ :edit, :update ]
|
47
|
+
#
|
48
|
+
# To skip this in a subclassed controller:
|
49
|
+
#
|
50
|
+
# skip_before_filter :login_required
|
51
|
+
#
|
52
|
+
def login_required
|
53
|
+
authorized? || throw(:halt, :access_denied)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Redirect as appropriate when an access request fails.
|
57
|
+
#
|
58
|
+
# The default action is to redirect to the login screen.
|
59
|
+
#
|
60
|
+
# Override this method in your controllers if you want to have special
|
61
|
+
# behavior in case the <%= singular_name %> is not authorized
|
62
|
+
# to access the requested action. For example, a popup window might
|
63
|
+
# simply close itself.
|
64
|
+
def access_denied
|
65
|
+
case content_type
|
66
|
+
when :html
|
67
|
+
store_location
|
68
|
+
redirect url(:login)
|
69
|
+
when :xml
|
70
|
+
headers["Status"] = "Unauthorized"
|
71
|
+
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
72
|
+
set_status(401)
|
73
|
+
render :text => "Couldn't authenticate you"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Store the URI of the current request in the session.
|
78
|
+
#
|
79
|
+
# We can return to this location by calling #redirect_back_or_default.
|
80
|
+
def store_location
|
81
|
+
session[:return_to] = request.uri
|
82
|
+
end
|
83
|
+
|
84
|
+
# Redirect to the URI stored by the most recent store_location call or
|
85
|
+
# to the passed default.
|
86
|
+
def redirect_back_or_default(default)
|
87
|
+
redirect(session[:return_to] || default)
|
88
|
+
session[:return_to] = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# Inclusion hook to make #current_<%= singular_name %> and #logged_in?
|
92
|
+
# available as ActionView helper methods.
|
93
|
+
# def self.included(base)
|
94
|
+
# base.send :helper_method, :current_<%= singular_name %>, :logged_in?
|
95
|
+
# end
|
96
|
+
|
97
|
+
# Called from #current_<%= singular_name %>. First attempt to login by the <%= singular_name %> id stored in the session.
|
98
|
+
def login_from_session
|
99
|
+
self.current_<%= singular_name %> = <%= class_name %>.find_authenticated_model_with_id(session[:<%= singular_name %>]) if session[:<%= singular_name %>]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Called from #current_<%= singular_name %>. Now, attempt to login by basic authentication information.
|
103
|
+
def login_from_basic_auth
|
104
|
+
<%= singular_name %>name, passwd = get_auth_data
|
105
|
+
self.current_<%= singular_name %> = <%= class_name %>.authenticate(<%= singular_name %>name, passwd) if <%= singular_name %>name && passwd
|
106
|
+
end
|
107
|
+
|
108
|
+
# Called from #current_<%= singular_name %>. Finaly, attempt to login by an expiring token in the cookie.
|
109
|
+
def login_from_cookie
|
110
|
+
<%= singular_name %> = cookies[:auth_token] && <%= class_name %>.find_authenticated_model_with_remember_token(cookies[:auth_token])
|
111
|
+
if <%= singular_name %> && <%= singular_name %>.remember_token?
|
112
|
+
<%= singular_name %>.remember_me
|
113
|
+
cookies[:auth_token] = { :value => <%= singular_name %>.remember_token, :expires => <%= singular_name %>.remember_token_expires_at }
|
114
|
+
self.current_<%= singular_name %> = <%= singular_name %>
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def reset_session
|
119
|
+
session.data.each{|k,v| session.data.delete(k)}
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
@@http_auth_headers = %w(Authorization HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION)
|
124
|
+
|
125
|
+
# gets BASIC auth info
|
126
|
+
def get_auth_data
|
127
|
+
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
|
128
|
+
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
|
129
|
+
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'authenticated_system_orm_map'
|
2
|
+
module AuthenticatedSystem
|
3
|
+
module Model
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
base.send(:extend, ClassMethods)
|
8
|
+
base.send(:extend, AuthenticatedSystem::OrmMap )
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def authenticated?(password)
|
13
|
+
crypted_password == encrypt(password)
|
14
|
+
end
|
15
|
+
|
16
|
+
# before filter
|
17
|
+
def encrypt_password
|
18
|
+
return if password.blank?
|
19
|
+
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
|
20
|
+
self.crypted_password = encrypt(password)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Encrypts the password with the <%= singular_name %> salt
|
24
|
+
def encrypt(password)
|
25
|
+
self.class.encrypt(password, salt)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remember_token?
|
29
|
+
remember_token_expires_at && DateTime.now < DateTime.parse(remember_token_expires_at.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def remember_me_until(time)
|
33
|
+
self.remember_token_expires_at = time
|
34
|
+
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
|
35
|
+
save
|
36
|
+
end
|
37
|
+
|
38
|
+
def remember_me_for(time)
|
39
|
+
remember_me_until (Time.now + time)
|
40
|
+
end
|
41
|
+
|
42
|
+
# These create and unset the fields required for remembering <%= singular_name %>s between browser closes
|
43
|
+
# Default of 2 weeks
|
44
|
+
def remember_me
|
45
|
+
remember_me_for (Merb::Const::WEEK * 2)
|
46
|
+
end
|
47
|
+
|
48
|
+
def forget_me
|
49
|
+
self.remember_token_expires_at = nil
|
50
|
+
self.remember_token = nil
|
51
|
+
self.save
|
52
|
+
end
|
53
|
+
<% if include_activation -%>
|
54
|
+
# Returns true if the <%= singular_name %> has just been activated.
|
55
|
+
def recently_activated?
|
56
|
+
@activated
|
57
|
+
end
|
58
|
+
|
59
|
+
def activated?
|
60
|
+
return false if self.new_record?
|
61
|
+
!! activation_code.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
def active?
|
65
|
+
# the existence of an activation code means they have not activated yet
|
66
|
+
activation_code.nil?
|
67
|
+
end
|
68
|
+
<% end %>
|
69
|
+
protected
|
70
|
+
<% if include_activation -%>
|
71
|
+
def make_activation_code
|
72
|
+
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
|
73
|
+
end
|
74
|
+
<% end -%>
|
75
|
+
|
76
|
+
def password_required?
|
77
|
+
crypted_password.blank? || !password.blank?
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
module ClassMethods
|
83
|
+
# Encrypts some data with the salt.
|
84
|
+
def encrypt(password, salt)
|
85
|
+
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Authenticates a <%= singular_name %> by their login name and unencrypted password. Returns the <%= singular_name %> or nil.
|
89
|
+
def authenticate(login, password)
|
90
|
+
u = find_activated_authenticated_model_with_login(login) # need to get the salt
|
91
|
+
u && u.authenticated?(password) ? u : nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|