janus 0.5.0 → 0.6.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/README.rdoc +65 -29
- data/lib/janus.rb +1 -13
- data/lib/janus/config.rb +9 -5
- data/lib/janus/controllers/confirmations_controller.rb +2 -0
- data/lib/janus/controllers/helpers.rb +17 -0
- data/lib/janus/controllers/internal_helpers.rb +12 -0
- data/lib/janus/controllers/passwords_controller.rb +3 -0
- data/lib/janus/controllers/sessions_controller.rb +58 -21
- data/lib/janus/manager.rb +7 -2
- data/lib/janus/models/database_authenticatable.rb +22 -6
- data/lib/janus/rails.rb +17 -0
- data/lib/janus/routes.rb +1 -2
- data/lib/janus/sinatra.rb +51 -0
- metadata +63 -169
- data/test/functional/home_controller_test.rb +0 -8
- data/test/functional/janus/mailer_test.rb +0 -14
- data/test/functional/janus/manager_test.rb +0 -94
- data/test/functional/users/confirmations_controller_test.rb +0 -59
- data/test/functional/users/passwords_controller_test.rb +0 -101
- data/test/functional/users/registrations_controller_test.rb +0 -112
- data/test/functional/users/sessions_controller_test.rb +0 -100
- data/test/functional/users_controller_test.rb +0 -22
- data/test/integration/users/rememberable_test.rb +0 -32
- data/test/integration/users/remote_test.rb +0 -72
- data/test/integration/users/sessions_test.rb +0 -18
- data/test/integration/users/trackable_test.rb +0 -22
- data/test/rails_app/app/controllers/application_controller.rb +0 -9
- data/test/rails_app/app/controllers/blogs_controller.rb +0 -6
- data/test/rails_app/app/controllers/home_controller.rb +0 -4
- data/test/rails_app/app/controllers/users/confirmations_controller.rb +0 -3
- data/test/rails_app/app/controllers/users/passwords_controller.rb +0 -3
- data/test/rails_app/app/controllers/users/registrations_controller.rb +0 -7
- data/test/rails_app/app/controllers/users/sessions_controller.rb +0 -11
- data/test/rails_app/app/controllers/users_controller.rb +0 -9
- data/test/rails_app/app/helpers/application_helper.rb +0 -2
- data/test/rails_app/app/mailers/janus_mailer.rb +0 -2
- data/test/rails_app/app/models/remote_token.rb +0 -6
- data/test/rails_app/app/models/user.rb +0 -8
- data/test/rails_app/config/application.rb +0 -42
- data/test/rails_app/config/boot.rb +0 -6
- data/test/rails_app/config/environment.rb +0 -5
- data/test/rails_app/config/environments/development.rb +0 -26
- data/test/rails_app/config/environments/production.rb +0 -49
- data/test/rails_app/config/environments/test.rb +0 -36
- data/test/rails_app/config/initializers/janus.rb +0 -11
- data/test/rails_app/config/initializers/secret_token.rb +0 -7
- data/test/rails_app/config/initializers/session_store.rb +0 -8
- data/test/rails_app/config/routes.rb +0 -12
- data/test/rails_app/db/migrate/20110323153820_create_users.rb +0 -34
- data/test/rails_app/db/migrate/20110331153546_create_remote_tokens.rb +0 -15
- data/test/rails_app/db/schema.rb +0 -45
- data/test/rails_app/db/seeds.rb +0 -7
- data/test/test_helper.rb +0 -103
- data/test/unit/confirmable_test.rb +0 -36
- data/test/unit/janus_test.rb +0 -27
- data/test/unit/rememberable_test.rb +0 -50
- data/test/unit/remote_authenticatable_test.rb +0 -37
- data/test/unit/remote_token_test.rb +0 -9
- data/test/unit/reset_password_test.rb +0 -45
- data/test/unit/trackable_test.rb +0 -21
- data/test/unit/user_test.rb +0 -60
data/README.rdoc
CHANGED
@@ -2,48 +2,86 @@
|
|
2
2
|
|
3
3
|
Janus is an authentication engine for Ruby on Rails 3 and is an alternative
|
4
4
|
to the Warden + Devise combo, without the Rack middleware. The whole project
|
5
|
-
is inspired by the Warden and Devise API
|
6
|
-
different since everything happens within ActionDispatch and not
|
7
|
-
level.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
is inspired by the Warden and Devise API (in order to somehow compatible)
|
6
|
+
but is quite different since everything happens within ActionDispatch and not
|
7
|
+
at the Rack level.
|
8
|
+
|
9
|
+
This Rails instead of Rack difference, allows to actually have the main logic
|
10
|
+
within plain Rails controllers. For instance the database authentication is
|
11
|
+
called from SessionsController, and it's not just another strategy operating
|
12
|
+
at the Rack level within Warden (which requires to check that it's being
|
13
|
+
called from the correct URL). There ain't no factory to add strategy modules
|
14
|
+
to your models too. You must manually include the necessary ones.
|
15
|
+
|
16
|
+
Another difference is that you must actually create the necessary controllers,
|
17
|
+
models, mailers and views within your project (extending the default ones).
|
18
|
+
You will eventually need to have those controllers anyway, and having those
|
19
|
+
from the beginning allows to skip some configuration. The burdensome of
|
20
|
+
manually creating all those classes should eventually be leveraged by using
|
21
|
+
some rails generators.
|
22
|
+
|
23
|
+
Janus also provides a finer control over setting and unsetting a user than
|
24
|
+
Warden provides. Janus uses +login+ and +logout+ to actually sign the user
|
25
|
+
in and out, just like Warden, but actually uses +set_user+ and +unset_user+
|
26
|
+
to manually set the session, without dispatching the +after_login+ and
|
27
|
+
+after_logout+ hooks, of course.
|
28
|
+
|
29
|
+
Emails are also sent from the controllers, not from the models, because I
|
30
|
+
believe this is actually the job of controllers, not models.
|
15
31
|
|
16
32
|
== Features
|
17
33
|
|
34
|
+
The main feature is of course having a framework for authenticating users
|
35
|
+
painleslly. Yet a very usefull feature is the cross domain authentication
|
36
|
+
--which allows a user to single sign in and out across top level domains.
|
37
|
+
|
38
|
+
How is the cross domain authentication strategy usefull? Let's imagine you
|
39
|
+
host of blogging website where users may host their blogs on other domains.
|
40
|
+
Since you don't rely on subdomains, it will be a pain to keep the user
|
41
|
+
authentified, because you can't rely on '.' domain cookie trick.
|
42
|
+
|
43
|
+
This is where RemoteAuthentication comes in, and allows you to painlessly
|
44
|
+
keep your users connected across the main website and their blogs. This
|
45
|
+
without actually tracking connections since this strategy takes advantage
|
46
|
+
of the +set_user+ and +unset_user+ methods, because they're not really
|
47
|
+
signing in, they just stay authentified across domains.
|
48
|
+
|
49
|
+
So, Janus provides the following API:
|
50
|
+
|
51
|
+
- full authentication system with strategies and hooks
|
52
|
+
- scoped authentications with parallel authentication (like `users`, `admin_users`, etc.)
|
53
|
+
- database authentication with password encryption (bcrypt) and validation
|
54
|
+
- remote authentication for cross domain single sign in / sign out
|
55
|
+
- abstract controllers for session management, registration, email confirmation and password reset
|
56
|
+
- route generation for the above controllers
|
57
|
+
|
58
|
+
And for the strategies and hooks:
|
59
|
+
|
18
60
|
- DatabaseAuthenticatable
|
19
61
|
- RemoteAuthenticatable
|
20
62
|
- Confirmable
|
21
63
|
- Rememberable
|
22
|
-
- Trackable
|
23
|
-
|
24
|
-
Note: login through Janus::Manager#set_user won't track the user.
|
25
|
-
|
26
|
-
- authentication system with strategies and hooks
|
27
|
-
- scoped authentications with parallel authentication
|
28
|
-
- database authentication with password encryption, validation and remember me strategy
|
29
|
-
- remote authentication for cross domain sign in / sign out
|
30
|
-
- controllers: sessions, registrations, confirmations, passwords and their routes
|
31
|
-
- route generation for above controllers
|
32
|
-
- trackable hook
|
64
|
+
- Trackable (note that login through Janus::Manager#set_user won't track the user).
|
33
65
|
|
34
66
|
== TODO
|
35
67
|
|
36
|
-
-
|
37
|
-
-
|
68
|
+
- Simple configuration to use scrypt instead of bcrypt for password encryption.
|
69
|
+
- Reconfirmable when email changes.
|
70
|
+
- TokenAuthenticatable.
|
71
|
+
- Remember me on remote authenticated domains.
|
72
|
+
- Differenciate mailers per resource, by looking for Users::Mailer class.
|
73
|
+
- Generators: `janus:install` and `janus <scope>`.
|
74
|
+
- Integrate OmniAuth, or shall we let the user do it himself?
|
75
|
+
- Providing an OAuth 2.0 server whould be cool.
|
38
76
|
|
39
77
|
== Install
|
40
78
|
|
41
|
-
There is no automated way to install Janus yet,
|
42
|
-
|
79
|
+
There is no automated way to install Janus yet, because generators are missing.
|
80
|
+
Also remember that Janus is only compatible with Rails 3+.
|
43
81
|
|
44
82
|
First add the gem to your Gemfile:
|
45
83
|
|
46
|
-
$ gem 'janus'
|
84
|
+
$ gem 'janus'
|
47
85
|
|
48
86
|
Configure your user models by including all or a selection of the Janus::Models
|
49
87
|
modules:
|
@@ -60,7 +98,6 @@ modules:
|
|
60
98
|
class Admin < ActiveRecord::Base
|
61
99
|
include Janus::Models::Base
|
62
100
|
include Janus::Models::DatabaseAuthenticatable
|
63
|
-
include Janus::Models::RemoteAuthenticatable
|
64
101
|
end
|
65
102
|
|
66
103
|
Configure your routes:
|
@@ -68,7 +105,6 @@ Configure your routes:
|
|
68
105
|
Name::Application.routes.map do
|
69
106
|
janus :users, :session => true, :registration => true, :password => true, :confirmation => true
|
70
107
|
janus :admins, :session => true
|
71
|
-
|
72
108
|
root :to => "home#index"
|
73
109
|
end
|
74
110
|
|
@@ -86,7 +122,7 @@ Create the required controllers:
|
|
86
122
|
respond_to :html
|
87
123
|
end
|
88
124
|
|
89
|
-
class Users::
|
125
|
+
class Users::ConfirmationsController < Janus::ConfirmationsController
|
90
126
|
respond_to :html
|
91
127
|
end
|
92
128
|
|
data/lib/janus.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
+
require 'active_support/core_ext/class'
|
1
2
|
require 'janus/config'
|
2
3
|
require 'janus/hooks'
|
3
4
|
require 'janus/strategies'
|
4
5
|
require 'janus/manager'
|
5
|
-
require 'janus/routes'
|
6
6
|
|
7
7
|
autoload :JanusHelper, 'janus/helper'
|
8
8
|
|
@@ -16,18 +16,6 @@ module Janus
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
autoload :Mailer, 'janus/mailer'
|
20
|
-
autoload :TestHelper, 'janus/test_helper'
|
21
|
-
|
22
|
-
autoload :Helpers, 'janus/controllers/helpers'
|
23
|
-
autoload :UrlHelpers, 'janus/controllers/url_helpers'
|
24
|
-
autoload :InternalHelpers, 'janus/controllers/internal_helpers'
|
25
|
-
|
26
|
-
autoload :SessionsController, 'janus/controllers/sessions_controller'
|
27
|
-
autoload :RegistrationsController, 'janus/controllers/registrations_controller'
|
28
|
-
autoload :ConfirmationsController, 'janus/controllers/confirmations_controller'
|
29
|
-
autoload :PasswordsController, 'janus/controllers/passwords_controller'
|
30
|
-
|
31
19
|
module Models
|
32
20
|
autoload :Base, 'janus/models/base'
|
33
21
|
autoload :DatabaseAuthenticatable, 'janus/models/database_authenticatable'
|
data/lib/janus/config.rb
CHANGED
@@ -5,17 +5,21 @@ module Janus
|
|
5
5
|
mattr_accessor :contact_email
|
6
6
|
|
7
7
|
# DatabaseAuthenticatable
|
8
|
-
mattr_accessor :authentication_keys, :stretches, :pepper
|
9
|
-
self.authentication_keys = [:email]
|
8
|
+
mattr_accessor :authentication_keys, :encryptor, :stretches, :pepper, :scrypt_options
|
9
|
+
self.authentication_keys = [ :email ]
|
10
|
+
self.encryptor = :bcrypt
|
10
11
|
self.stretches = 10
|
12
|
+
self.pepper = nil
|
13
|
+
self.scrypt_options = { :max_time => 0.25 }
|
11
14
|
|
12
15
|
# Confirmable
|
13
|
-
mattr_accessor :confirmation_key
|
16
|
+
mattr_accessor :confirmation_key #,reconfirmable
|
14
17
|
self.confirmation_key = :confirm_token
|
18
|
+
# self.reconfirmable = true
|
15
19
|
|
16
20
|
# Rememberable
|
17
|
-
mattr_accessor :remember_for, :extend_remember_period
|
18
|
-
self.remember_for =
|
21
|
+
mattr_accessor :remember_for, :extend_remember_period #, :remember_across_browsers
|
22
|
+
self.remember_for = 1.year
|
19
23
|
self.extend_remember_period = false
|
20
24
|
# self.remember_across_browsers = false
|
21
25
|
|
@@ -13,19 +13,36 @@ module Janus
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
# Returns the current instance of Janus::Manager.
|
16
17
|
def janus
|
17
18
|
@janus ||= Janus::Manager.new(request, cookies)
|
18
19
|
end
|
19
20
|
|
21
|
+
# Signs the current user out (from all scopes at once) in case of a CSRF attack.
|
22
|
+
# See ActionController::RequestForgeryProtection for documentation.
|
20
23
|
def handle_unverified_requests
|
21
24
|
janus.logout
|
25
|
+
super
|
22
26
|
end
|
23
27
|
|
28
|
+
# Returns true if a scope user is currently authenticated.
|
24
29
|
def signed_in?(scope)
|
25
30
|
janus.authenticate?(scope)
|
26
31
|
end
|
27
32
|
|
28
33
|
module ClassMethods
|
34
|
+
# Aliases some Janus methods for convenience. For instance calling
|
35
|
+
# `janus(:user, :admin)` will generate the following methods:
|
36
|
+
#
|
37
|
+
# authenticate_user! # => janus.authenticate!(:user)
|
38
|
+
# current_user # => janus.authenticate(:user)
|
39
|
+
# user_signed_in? # => janus.authenticate?(:user)
|
40
|
+
# user_session # => janus.sesssion(:user)
|
41
|
+
#
|
42
|
+
# authenticate_admin! # => janus.authenticate!(:admin)
|
43
|
+
# current_admin # => janus.authenticate(:admin)
|
44
|
+
# admin_signed_in? # => janus.authenticate?(:admin)
|
45
|
+
# admin_session # => janus.sesssion(:admin)
|
29
46
|
def janus(*scopes)
|
30
47
|
scopes.each do |scope|
|
31
48
|
class_eval <<-EOV
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module Janus
|
2
|
+
# A collection of abstraction helper methods used in Janus controllers and views.
|
3
|
+
# This should be of no particular outside of abstract controllers for Janus that
|
4
|
+
# must be working for all scopes at once.
|
2
5
|
module InternalHelpers
|
3
6
|
extend ActiveSupport::Concern
|
4
7
|
|
@@ -6,26 +9,35 @@ module Janus
|
|
6
9
|
helper_method :janus_scope, :resource, :resource_class, :resource_name
|
7
10
|
end
|
8
11
|
|
12
|
+
# Abstract method for the authenticate_scope! before filter, with scope
|
13
|
+
# as detected by janus_scope.
|
9
14
|
def authenticate!
|
10
15
|
send("authenticate_#{janus_scope}!")
|
11
16
|
end
|
12
17
|
|
18
|
+
# Detects the scope from the controller name.
|
13
19
|
def janus_scope
|
14
20
|
@janus_scope ||= self.class.name.split('::', 2).first.underscore.singularize
|
15
21
|
end
|
16
22
|
|
23
|
+
# Returns the `@user` instance variable (or `@admin` or whatever),
|
24
|
+
# as detected by janus_scope.
|
17
25
|
def resource
|
18
26
|
instance_variable_get(:"@#{janus_scope}")
|
19
27
|
end
|
20
28
|
|
29
|
+
# Sets the `@user` instance variable (or `@admin` or whatever),
|
30
|
+
# as detected by janus_scope.
|
21
31
|
def resource=(value)
|
22
32
|
instance_variable_set(:"@#{janus_scope}", value)
|
23
33
|
end
|
24
34
|
|
35
|
+
# Returns the `User` class (or `Admin` or whatever) as detected by janus_scope.
|
25
36
|
def resource_class
|
26
37
|
@resource_class ||= janus_scope.camelize.constantize
|
27
38
|
end
|
28
39
|
|
40
|
+
# Alias for janus_scope.
|
29
41
|
def resource_name
|
30
42
|
janus_scope
|
31
43
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# This controller is responsible for resetting a lost password. It sends an
|
2
|
+
# email with an unique token, on demand by the user. Then allows the user to
|
3
|
+
# change its password (as long as the token is valid).
|
1
4
|
class Janus::PasswordsController < ApplicationController
|
2
5
|
include Janus::InternalHelpers
|
3
6
|
|
@@ -1,5 +1,13 @@
|
|
1
1
|
require 'addressable/uri'
|
2
2
|
|
3
|
+
# This controller is responsible for creating and destroying
|
4
|
+
# authenticated user sessions.
|
5
|
+
#
|
6
|
+
# The creation uses the DatabaseAuthenticatable strategy, while the destruction
|
7
|
+
# simply destroys any session, whatever strategy it was created with. Janus
|
8
|
+
# hooks will be called, of course, allowing to destroy any Rememberable cookies
|
9
|
+
# for instance, as well as any user defined behavior.
|
10
|
+
#
|
3
11
|
class Janus::SessionsController < ApplicationController
|
4
12
|
include Janus::InternalHelpers
|
5
13
|
# include Janus::UrlHelpers
|
@@ -34,7 +42,6 @@ class Janus::SessionsController < ApplicationController
|
|
34
42
|
self.resource ||= resource_class.new(params[resource_name])
|
35
43
|
resource.clean_up_passwords
|
36
44
|
resource.errors.add(:base, :not_found)
|
37
|
-
|
38
45
|
render "new", :status => :unauthorized
|
39
46
|
end
|
40
47
|
format.any { head :unauthorized }
|
@@ -51,41 +58,71 @@ class Janus::SessionsController < ApplicationController
|
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
61
|
+
# An overridable method that returns the default path to return the just
|
62
|
+
# signed in user to. Defaults to return the user object, which will be
|
63
|
+
# interpreted by rails as `user_path(user)`.
|
54
64
|
def after_sign_in_url(user)
|
55
65
|
user
|
56
66
|
end
|
57
67
|
|
68
|
+
# An overridable method that returns the default path to return the just
|
69
|
+
# signed out user to. Defaults to `root_url`.
|
58
70
|
def after_sign_out_url(scope)
|
59
71
|
root_url
|
60
72
|
end
|
61
73
|
|
62
|
-
# Returns true if
|
63
|
-
#
|
64
|
-
#
|
74
|
+
# Returns true if host is known and that we allow to redirect the user
|
75
|
+
# with an auth_token.
|
76
|
+
#
|
77
|
+
# Warning: must be overwritten by child classes because it always
|
78
|
+
# returns false by default!
|
65
79
|
def valid_remote_host?(host)
|
66
|
-
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an Array of URL that we shouldn't automatically return to. It
|
84
|
+
# actually returns URL to prevent infinite loops. We must for instance
|
85
|
+
# never return to new_sesssion_path.
|
86
|
+
#
|
87
|
+
# If you ever needd to override this method, don't forget to call `super`.
|
88
|
+
# For instance:
|
89
|
+
#
|
90
|
+
# def never_return_to(scope)
|
91
|
+
# super + [ my_peculiar_path, another_path ]
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
def never_return_to(scope)
|
95
|
+
scope = Janus.scope_for(scope)
|
96
|
+
[
|
97
|
+
new_session_path(scope),
|
98
|
+
new_password_path(scope),
|
99
|
+
edit_password_path(scope)
|
100
|
+
]
|
67
101
|
end
|
68
102
|
|
69
|
-
# Either redirects the user to after_sign_in_url or to
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
103
|
+
# Either redirects the user to after_sign_in_url or to <tt>params[:return_to]</tt>.
|
104
|
+
#
|
105
|
+
# If <tt>params[:return_to] is an absolute URL, and not just a path,
|
106
|
+
# valid_remote_host? will be invoked to check wether we should redirect
|
107
|
+
# to this URL or not, in order to secure auth tokens for
|
108
|
+
# RemoteAuthenticatable to leak into the wild.
|
74
109
|
def redirect_after_sign_in(user)
|
75
110
|
unless params[:return_to].blank?
|
76
111
|
return_to = Addressable::URI.parse(params[:return_to])
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
112
|
+
|
113
|
+
unless never_return_to(user).include?(return_to.path)
|
114
|
+
if return_to.host.nil? || return_to.host == request.host
|
115
|
+
redirect_to params[:return_to]
|
116
|
+
return
|
117
|
+
elsif valid_remote_host?(return_to.host)
|
118
|
+
if user.class.include?(Janus::Models::RemoteAuthenticatable)
|
119
|
+
query = return_to.query_values || {}
|
120
|
+
return_to.query_values = query.merge(user.class.remote_authentication_key => user.generate_remote_token!)
|
121
|
+
end
|
122
|
+
|
123
|
+
redirect_to return_to.to_s
|
124
|
+
return
|
85
125
|
end
|
86
|
-
|
87
|
-
redirect_to return_to.to_s
|
88
|
-
return
|
89
126
|
end
|
90
127
|
end
|
91
128
|
|
data/lib/janus/manager.rb
CHANGED
@@ -34,7 +34,7 @@ module Janus
|
|
34
34
|
|
35
35
|
# Logs a user in.
|
36
36
|
#
|
37
|
-
# FIXME: what should happen when a user signs in but a user is already signed in?!
|
37
|
+
# FIXME: what should happen when a user signs in but a user is already signed in for the same scope?!
|
38
38
|
def login(user, options = {})
|
39
39
|
options[:scope] ||= Janus.scope_for(user)
|
40
40
|
set_user(user, options)
|
@@ -76,8 +76,13 @@ module Janus
|
|
76
76
|
|
77
77
|
if authenticated?(scope)
|
78
78
|
if @users[scope].nil?
|
79
|
+
begin
|
79
80
|
@users[scope] = session(scope)[:user_class].find(session(scope)[:user_id])
|
80
|
-
|
81
|
+
rescue ActiveRecord::RecordNotFound
|
82
|
+
unset_user(scope)
|
83
|
+
else
|
84
|
+
Janus::Manager.run_callbacks(:fetch, @users[scope], self, :scope => scope)
|
85
|
+
end
|
81
86
|
end
|
82
87
|
|
83
88
|
@users[scope]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'bcrypt'
|
2
|
+
require 'scrypt'
|
2
3
|
|
3
4
|
module Janus
|
4
5
|
module Models
|
@@ -24,13 +25,13 @@ module Janus
|
|
24
25
|
|
25
26
|
included do
|
26
27
|
attr_protected :encrypted_password, :reset_password_token, :reset_password_sent_at
|
27
|
-
attr_reader
|
28
|
-
attr_accessor
|
28
|
+
attr_reader :password
|
29
|
+
attr_accessor :current_password
|
29
30
|
|
30
31
|
validates :password, :presence => true, :confirmation => true, :if => :password_required?
|
31
32
|
validate :validate_current_password, :on => :update, :if => :current_password
|
32
33
|
|
33
|
-
janus_config(:authentication_keys, :stretches, :pepper)
|
34
|
+
janus_config(:authentication_keys, :encryptor, :stretches, :pepper, :scrypt_options)
|
34
35
|
end
|
35
36
|
|
36
37
|
def password=(password)
|
@@ -38,13 +39,28 @@ module Janus
|
|
38
39
|
self.encrypted_password = digest_password(@password) unless @password.blank?
|
39
40
|
end
|
40
41
|
|
41
|
-
# Checks if a given password matches this user password.
|
42
|
+
# Checks if a given password matches this user's password.
|
42
43
|
def valid_password?(password)
|
43
|
-
|
44
|
+
case self.class.encryptor
|
45
|
+
when :bcrypt
|
46
|
+
::BCrypt::Password.new(encrypted_password) == salted_password(password)
|
47
|
+
when :scrypt
|
48
|
+
::SCrypt::Password.new(encrypted_password) == salted_password(password)
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
52
|
+
# Digests a password using either bcrypt or scrypt (as configured by `config.encryptor`).
|
46
53
|
def digest_password(password)
|
47
|
-
|
54
|
+
case self.class.encryptor
|
55
|
+
when :bcrypt
|
56
|
+
::BCrypt::Password.create(salted_password(password), :cost => self.class.stretches).to_s
|
57
|
+
when :scrypt
|
58
|
+
::SCrypt::Password.create(salted_password(password), self.class.scrypt_options).to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def salted_password(password)
|
63
|
+
"#{password}#{self.class.pepper}"
|
48
64
|
end
|
49
65
|
|
50
66
|
def clean_up_passwords
|