dancroak-clearance 0.1
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.textile +78 -0
- data/clearance.gemspec +11 -0
- data/lib/clearance.rb +8 -0
- data/lib/clearance/application_controller.rb +62 -0
- data/lib/clearance/model.rb +84 -0
- data/lib/clearance/sessions_controller.rb +66 -0
- data/lib/clearance/sessions_controller_test.rb +84 -0
- data/lib/clearance/test_helper.rb +75 -0
- data/lib/clearance/unit_test.rb +186 -0
- data/lib/clearance/users_controller.rb +22 -0
- data/lib/clearance/users_controller_test.rb +72 -0
- metadata +65 -0
data/README.textile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
h1. Clearance
|
2
|
+
|
3
|
+
Simple, complete Rails authentication.
|
4
|
+
|
5
|
+
"We have clearance, Clarence.":http://www.youtube.com/v/mNRXJEE3Nz8
|
6
|
+
|
7
|
+
h2. Features
|
8
|
+
|
9
|
+
* email & password
|
10
|
+
* modules, not a generator
|
11
|
+
* gem, not a plugin
|
12
|
+
* should & factory_girl tests included
|
13
|
+
|
14
|
+
h2. Schema
|
15
|
+
|
16
|
+
Change your User model so it has these attributes.
|
17
|
+
|
18
|
+
change_table(:users) do |t|
|
19
|
+
t.column :email, :string
|
20
|
+
t.column :crypted_password, :string, :limit => 40
|
21
|
+
t.column :salt, :string, :limit => 40
|
22
|
+
t.column :remember_token, :string
|
23
|
+
t.column :remember_token_expires_at, :datetime
|
24
|
+
end
|
25
|
+
|
26
|
+
add_index :users, [:email, :password]
|
27
|
+
|
28
|
+
h2. Model
|
29
|
+
|
30
|
+
In app/models/user.rb:
|
31
|
+
|
32
|
+
include Clearance::Model
|
33
|
+
|
34
|
+
h2. Controllers
|
35
|
+
|
36
|
+
In app/controllers/application_controller.rb:
|
37
|
+
|
38
|
+
include Clearance::ApplicationController
|
39
|
+
|
40
|
+
In app/controllers/sessions_controller.rb:
|
41
|
+
|
42
|
+
include Clearance::SessionsController
|
43
|
+
|
44
|
+
In app/controllers/users_controller.rb:
|
45
|
+
|
46
|
+
include Clearance::UsersController
|
47
|
+
|
48
|
+
h2. Routes
|
49
|
+
|
50
|
+
map.login '/login', :controller => 'sessions', :action => 'new'
|
51
|
+
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
|
52
|
+
map.resource :session
|
53
|
+
|
54
|
+
h2. Tests
|
55
|
+
|
56
|
+
The tests use Shoulda and Factory Girl.
|
57
|
+
|
58
|
+
In test/test_helper.rb:
|
59
|
+
|
60
|
+
include Clearance::TestHelper
|
61
|
+
|
62
|
+
In test/unit/user_test.rb:
|
63
|
+
|
64
|
+
include Clearance::UnitTest
|
65
|
+
|
66
|
+
In test/functional/sessions_controller_test.rb:
|
67
|
+
|
68
|
+
include Clearance::SessionsControllerTest
|
69
|
+
|
70
|
+
In test/functional/users_controller_test.rb:
|
71
|
+
|
72
|
+
include Clearance::UsersControllerTest
|
73
|
+
|
74
|
+
h2. Authors
|
75
|
+
|
76
|
+
* thoughtbot, inc.
|
77
|
+
* Dan Croak
|
78
|
+
* Josh Nichols
|
data/clearance.gemspec
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "clearance"
|
3
|
+
s.version = "0.1"
|
4
|
+
s.date = "2008-09-06"
|
5
|
+
s.summary = "Simple, complete Rails authentication."
|
6
|
+
s.email = "dcroak@thoughtbot.com"
|
7
|
+
s.homepage = "http://github.com/dancroak/clearance"
|
8
|
+
s.description = "Simple, complete Rails authentication scheme."
|
9
|
+
s.authors = ["thoughtbot, inc.", "Dan Croak", "Josh Nichols"]
|
10
|
+
s.files = ["README.textile", "clearance.gemspec", "lib/clearance.rb", "lib/clearance/application_controller.rb", "lib/clearance/model.rb", "lib/clearance/sessions_controller.rb", "lib/clearance/sessions_controller_test.rb", "lib/clearance/test_helper.rb", "lib/clearance/unit_test.rb", "lib/clearance/users_controller.rb", "lib/clearance/users_controller_test.rb"]
|
11
|
+
end
|
data/lib/clearance.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'clearance/application_controller'
|
2
|
+
require 'clearance/sessions_controller'
|
3
|
+
require 'clearance/users_controller'
|
4
|
+
require 'clearance/model'
|
5
|
+
require 'clearance/test_helper'
|
6
|
+
require 'clearance/sessions_controller_test'
|
7
|
+
require 'clearance/users_controller_test'
|
8
|
+
require 'clearance/unit_test'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Clearance
|
2
|
+
module ApplicationController
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
attr_accessor :current_user
|
7
|
+
helper_method :current_user
|
8
|
+
|
9
|
+
include InstanceMethods
|
10
|
+
|
11
|
+
protected
|
12
|
+
include ProtectedInstanceMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def current_user
|
18
|
+
@current_user ||= (user_from_session || user_from_cookie)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ProtectedInstanceMethods
|
23
|
+
def authenticate
|
24
|
+
deny_access if current_user.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def user_from_session
|
28
|
+
User.find_by_id(session[:user_id])
|
29
|
+
end
|
30
|
+
|
31
|
+
def user_from_cookie
|
32
|
+
user = User.find_by_remember_token(cookies[:auth_token]) if cookies[:auth_token]
|
33
|
+
user && user.remember_token? ? user : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def login(user)
|
37
|
+
create_session_for user
|
38
|
+
@current_user = user
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_session_for(user)
|
42
|
+
session[:user_id] = user.id if user
|
43
|
+
end
|
44
|
+
|
45
|
+
def redirect_back_or(default)
|
46
|
+
session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(default)
|
47
|
+
session[:return_to] = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def store_location
|
51
|
+
session[:return_to] = request.request_uri
|
52
|
+
end
|
53
|
+
|
54
|
+
def deny_access(flash_message = nil, opts = {})
|
55
|
+
opts[:redirect] ||= login_url
|
56
|
+
store_location
|
57
|
+
flash[:error] = flash_message if flash_message
|
58
|
+
redirect_to opts[:redirect]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Clearance
|
2
|
+
module Model
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
|
7
|
+
attr_accessible :email, :password, :password_confirmation
|
8
|
+
attr_accessor :password, :password_confirmation
|
9
|
+
|
10
|
+
validates_presence_of :email
|
11
|
+
validates_presence_of :password, :if => :password_required?
|
12
|
+
validates_length_of :password, :within => 3..40, :if => :password_required?
|
13
|
+
validates_confirmation_of :password, :if => :password_required?
|
14
|
+
validates_uniqueness_of :email
|
15
|
+
|
16
|
+
before_save :initialize_salt, :encrypt_password
|
17
|
+
|
18
|
+
extend ClassMethods
|
19
|
+
include InstanceMethods
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
include ProtectedInstanceMethods
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def authenticate(email, password)
|
30
|
+
user = find_by_email(email) # need to get the salt
|
31
|
+
user && user.authenticated?(password) ? user : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def authenticate_via_auth_token(token)
|
35
|
+
return nil if token.blank?
|
36
|
+
find_by_auth_token(token)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module InstanceMethods
|
41
|
+
def authenticated?(password)
|
42
|
+
crypted_password == encrypt(password)
|
43
|
+
end
|
44
|
+
|
45
|
+
def encrypt(password)
|
46
|
+
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
47
|
+
end
|
48
|
+
|
49
|
+
def remember_token?
|
50
|
+
remember_token_expires_at && Time.now.utc < remember_token_expires_at
|
51
|
+
end
|
52
|
+
|
53
|
+
def remember_me!
|
54
|
+
remember_me_until 2.weeks.from_now.utc
|
55
|
+
end
|
56
|
+
|
57
|
+
def remember_me_until(time)
|
58
|
+
self.update_attribute :remember_token_expires_at, time
|
59
|
+
self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def forget_me!
|
63
|
+
self.update_attribute :remember_token_expires_at, nil
|
64
|
+
self.update_attribute :remember_token, nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module ProtectedInstanceMethods
|
69
|
+
def initialize_salt
|
70
|
+
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
|
71
|
+
end
|
72
|
+
|
73
|
+
def encrypt_password
|
74
|
+
return if password.blank?
|
75
|
+
self.crypted_password = encrypt(password)
|
76
|
+
end
|
77
|
+
|
78
|
+
def password_required?
|
79
|
+
crypted_password.blank? || !password.blank?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Clearance
|
2
|
+
module SessionsController
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
skip_before_filter :authenticate
|
7
|
+
protect_from_forgery :except => :create
|
8
|
+
filter_parameter_logging :password
|
9
|
+
|
10
|
+
include InstanceMethods
|
11
|
+
|
12
|
+
protected
|
13
|
+
include ProtectedInstanceMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def create
|
19
|
+
remember_me = params[:session][:remember_me] if params[:session]
|
20
|
+
login_via_password(params[:session][:email], params[:session][:password], remember_me)
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy
|
24
|
+
forget current_user
|
25
|
+
reset_session
|
26
|
+
flash[:notice] = 'You have been logged out.'
|
27
|
+
redirect_to login_url
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ProtectedInstanceMethods
|
32
|
+
def login_via_password(email, password, remember_me)
|
33
|
+
user = User.authenticate(email, password)
|
34
|
+
if login(user)
|
35
|
+
create_session_for(user)
|
36
|
+
remember(user) if remember_me == '1'
|
37
|
+
login_successful
|
38
|
+
else
|
39
|
+
login_failure
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def login_successful
|
44
|
+
flash[:notice] = 'Logged in successfully'
|
45
|
+
redirect_back_or root_url
|
46
|
+
end
|
47
|
+
|
48
|
+
def login_failure(message = "Bad email or password.")
|
49
|
+
flash.now[:notice] = message
|
50
|
+
render :action => :new
|
51
|
+
end
|
52
|
+
|
53
|
+
def remember(user)
|
54
|
+
user.remember_me!
|
55
|
+
cookies[:auth_token] = { :value => user.remember_token,
|
56
|
+
:expires => user.remember_token_expires_at }
|
57
|
+
end
|
58
|
+
|
59
|
+
def forget(user)
|
60
|
+
user.forget_me! if user
|
61
|
+
cookies.delete :auth_token
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Clearance
|
2
|
+
module SessionsControllerTest
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
context "Given a user" do
|
7
|
+
setup do
|
8
|
+
@user = Factory(:user)
|
9
|
+
end
|
10
|
+
|
11
|
+
should_filter :password
|
12
|
+
|
13
|
+
# context "on GET to /sessions/new" do
|
14
|
+
# setup { get :new }
|
15
|
+
#
|
16
|
+
# should_respond_with :success
|
17
|
+
# should_render_template :new
|
18
|
+
# should_not_set_the_flash
|
19
|
+
# should "render a login form" do
|
20
|
+
# assert_select "form[action=/session]" do
|
21
|
+
# assert_select "input[type=text][name=?]", "session[email]"
|
22
|
+
# assert_select "input[type=password][name=?]", "session[password]"
|
23
|
+
# assert_select "input[type=checkbox][name=?]", "session[remember_me]"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
|
28
|
+
context "a POST to #create with good credentials" do
|
29
|
+
setup do
|
30
|
+
post :create, :session => { :email => @user.email, :password => @user.password }
|
31
|
+
end
|
32
|
+
|
33
|
+
should_set_the_flash_to /success/i
|
34
|
+
should_redirect_to 'root_url'
|
35
|
+
end
|
36
|
+
|
37
|
+
context "a POST to #create with bad credentials" do
|
38
|
+
setup do
|
39
|
+
post :create, :session => { :email => @user.email, :password => "bad value" }
|
40
|
+
end
|
41
|
+
|
42
|
+
should_set_the_flash_to /bad/i
|
43
|
+
should_render_template :new
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
context "While logged out" do
|
49
|
+
setup { logout }
|
50
|
+
|
51
|
+
context "logging out again" do
|
52
|
+
setup { delete :destroy }
|
53
|
+
should_redirect_to "login_url"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
logged_in_user_context do
|
58
|
+
context "a DELETE to #destroy without a cookie" do
|
59
|
+
setup { delete :destroy }
|
60
|
+
|
61
|
+
should_set_the_flash_to(/logged out/i)
|
62
|
+
should_redirect_to "login_url"
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'a DELETE to #destroy with a cookie' do
|
66
|
+
setup do
|
67
|
+
cookies['auth_token'] = CGI::Cookie.new 'token', 'value'
|
68
|
+
delete :destroy
|
69
|
+
end
|
70
|
+
|
71
|
+
should 'delete the cookie' do
|
72
|
+
assert cookies['auth_token'].empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'delete the remember me token in users table' do
|
76
|
+
assert_nil @current_user.reload.remember_token
|
77
|
+
assert_nil @current_user.reload.remember_token_expires_at
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Clearance
|
2
|
+
module TestHelper
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
extend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def login_as(user = nil)
|
13
|
+
user ||= Factory(:user)
|
14
|
+
@request.session[:user_id] = user.id
|
15
|
+
return user
|
16
|
+
end
|
17
|
+
|
18
|
+
def logout
|
19
|
+
@request.session[:user_id] = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def should_deny_access_on(command, opts = {})
|
25
|
+
opts[:redirect] ||= "login_url"
|
26
|
+
|
27
|
+
context "on #{command}" do
|
28
|
+
setup { eval command }
|
29
|
+
should_redirect_to opts[:redirect]
|
30
|
+
if opts[:flash]
|
31
|
+
should_set_the_flash_to opts[:flash]
|
32
|
+
else
|
33
|
+
should_not_set_the_flash
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def should_filter(*keys)
|
39
|
+
keys.each do |key|
|
40
|
+
should "filter #{key}" do
|
41
|
+
assert @controller.respond_to?(:filter_parameters),
|
42
|
+
"The key #{key} is not filtered"
|
43
|
+
filtered = @controller.send(:filter_parameters, {key.to_s => key.to_s})
|
44
|
+
assert_equal '[FILTERED]', filtered[key.to_s],
|
45
|
+
"The key #{key} is not filtered"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def should_have_user_form
|
51
|
+
should "have the user form" do
|
52
|
+
assert_select "form" do
|
53
|
+
%w(name email openid_url).each do |field|
|
54
|
+
assert_select "input[type=text][name=?]", "user[#{field}]"
|
55
|
+
end
|
56
|
+
%w(password password_confirmation).each do |field|
|
57
|
+
assert_select "input[type=password][name=?]", "user[#{field}]"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def logged_in_user_context(user_name = nil, &blk)
|
64
|
+
context "When logged in as a user" do
|
65
|
+
setup do
|
66
|
+
user = user_name ? instance_variable_get("@#{user_name}") : Factory(:user)
|
67
|
+
assert @current_user = login_as(user)
|
68
|
+
end
|
69
|
+
merge_block(&blk)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module Clearance
|
2
|
+
module UnitTest
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
should_require_attributes :email, :password
|
7
|
+
|
8
|
+
should "require password validation on create" do
|
9
|
+
user = Factory.build(:user, :password => 'blah', :password_confirmation => 'boogidy')
|
10
|
+
assert !user.save
|
11
|
+
assert_match(/confirmation/i, user.errors.on(:password))
|
12
|
+
end
|
13
|
+
|
14
|
+
should "create a crypted_password on save" do
|
15
|
+
assert_not_nil Factory(:user, :crypted_password => nil).crypted_password
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'updating a password' do
|
19
|
+
setup do
|
20
|
+
@user = Factory(:user)
|
21
|
+
assert_not_nil @user.crypted_password
|
22
|
+
@crypt = @user.crypted_password
|
23
|
+
assert_not_nil @user.salt
|
24
|
+
@salt = @user.salt
|
25
|
+
@user.password = 'a_new_password'
|
26
|
+
@user.password_confirmation = 'a_new_password'
|
27
|
+
assert @user.save
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'update a crypted_password' do
|
31
|
+
@user.reload
|
32
|
+
assert @user.crypted_password != @crypt
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'A user' do
|
37
|
+
setup do
|
38
|
+
@password = 'sekrit'
|
39
|
+
@salt = 'salt'
|
40
|
+
User.any_instance.stubs(:initialize_salt)
|
41
|
+
@user = Factory(:user, :password => @password, :salt => @salt)
|
42
|
+
end
|
43
|
+
|
44
|
+
should "require password validation on update" do
|
45
|
+
@user.update_attributes(:password => "blah", :password_confirmation => "boogidy")
|
46
|
+
assert !@user.save
|
47
|
+
assert_match(/confirmation/i, @user.errors.on(:password))
|
48
|
+
end
|
49
|
+
|
50
|
+
should_require_unique_attributes :email
|
51
|
+
|
52
|
+
context 'authenticating a user' do
|
53
|
+
context 'with good credentials' do
|
54
|
+
setup do
|
55
|
+
@result = User.authenticate @user.email, 'sekrit'
|
56
|
+
end
|
57
|
+
|
58
|
+
should 'return true' do
|
59
|
+
assert @result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with bad credentials' do
|
64
|
+
setup do
|
65
|
+
@result = User.authenticate @user.email, 'horribly_wrong_password'
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'return true' do
|
69
|
+
assert !@result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'authenticated?' do
|
75
|
+
context 'with good credentials' do
|
76
|
+
setup do
|
77
|
+
@result = @user.authenticated? @password
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'return true' do
|
81
|
+
assert @result
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with bad credentials' do
|
86
|
+
setup do
|
87
|
+
@result = @user.authenticated? 'horribly_wrong_password'
|
88
|
+
end
|
89
|
+
|
90
|
+
should 'return true' do
|
91
|
+
assert !@result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'encrypt' do
|
97
|
+
setup do
|
98
|
+
@crypted = @user.encrypt(@password)
|
99
|
+
@expected = Digest::SHA1.hexdigest("--#{@salt}--#{@password}--")
|
100
|
+
end
|
101
|
+
|
102
|
+
should 'create a Hash using SHA1 encryption' do
|
103
|
+
assert_equal @expected, @crypted
|
104
|
+
assert_not_equal @password, @crypted
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'remember_me!' do
|
109
|
+
setup do
|
110
|
+
assert_nil @user.remember_token
|
111
|
+
assert_nil @user.remember_token_expires_at
|
112
|
+
@user.remember_me!
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'set the remember token and expiration date' do
|
116
|
+
assert_not_nil @user.remember_token
|
117
|
+
assert_not_nil @user.remember_token_expires_at
|
118
|
+
end
|
119
|
+
|
120
|
+
should 'remember_token?' do
|
121
|
+
assert @user.remember_token?
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'forget_me!' do
|
125
|
+
setup do
|
126
|
+
@user.forget_me!
|
127
|
+
end
|
128
|
+
|
129
|
+
should 'unset the remember token and expiration date' do
|
130
|
+
assert_nil @user.remember_token
|
131
|
+
assert_nil @user.remember_token_expires_at
|
132
|
+
end
|
133
|
+
|
134
|
+
should 'not remember_token?' do
|
135
|
+
assert ! @user.remember_token?
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'remember_token?' do
|
141
|
+
context 'when token expires in the future' do
|
142
|
+
setup do
|
143
|
+
@user.update_attribute :remember_token_expires_at, 2.weeks.from_now.utc
|
144
|
+
end
|
145
|
+
|
146
|
+
should 'be true' do
|
147
|
+
assert @user.remember_token?
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when token expired' do
|
152
|
+
setup do
|
153
|
+
@user.update_attribute :remember_token_expires_at, 2.weeks.ago.utc
|
154
|
+
end
|
155
|
+
|
156
|
+
should 'be false' do
|
157
|
+
assert ! @user.remember_token?
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "User.authenticate with a valid email and password" do
|
163
|
+
setup do
|
164
|
+
@found_user = User.authenticate @user.email, @user.password
|
165
|
+
end
|
166
|
+
|
167
|
+
should "find that user" do
|
168
|
+
assert_equal @user, @found_user
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "When sent authenticate with an invalid email and password" do
|
173
|
+
setup do
|
174
|
+
@found_user = User.authenticate "not", "valid"
|
175
|
+
end
|
176
|
+
|
177
|
+
should "find nothing" do
|
178
|
+
assert_nil @found_user
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Clearance
|
2
|
+
module UsersController
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
before_filter :authenticate
|
7
|
+
before_filter :ensure_user_is_accessing_self, :only => [:edit, :update, :show]
|
8
|
+
|
9
|
+
filter_parameter_logging :password
|
10
|
+
private
|
11
|
+
include PrivateInstanceMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module PrivateInstanceMethods
|
16
|
+
def ensure_user_is_accessing_self
|
17
|
+
deny_access 'You cannot edit that user.' unless current_user.id.to_i == params[:id].to_i
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Clearance
|
2
|
+
module UsersControllerTest
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
logged_in_user_context do
|
7
|
+
|
8
|
+
should_deny_access_on "get :index"
|
9
|
+
should_deny_access_on "get :new"
|
10
|
+
should_deny_access_on "post :create, :user => {}"
|
11
|
+
should_filter :password
|
12
|
+
|
13
|
+
context "dealing with their own account" do
|
14
|
+
context "on GET to /users/:id/show" do
|
15
|
+
setup { get :show, :id => @user.to_param }
|
16
|
+
should_redirect_to "edit_user_url(@user)"
|
17
|
+
should_not_set_the_flash
|
18
|
+
end
|
19
|
+
|
20
|
+
should_deny_access_on "delete :destroy, :id => @user.to_param"
|
21
|
+
|
22
|
+
context "on GET to /users/:id/edit" do
|
23
|
+
setup { get :edit, :id => @user.to_param }
|
24
|
+
|
25
|
+
should_respond_with :success
|
26
|
+
should_render_template :edit
|
27
|
+
should_not_set_the_flash
|
28
|
+
should_assign_to :user
|
29
|
+
should_have_user_form
|
30
|
+
end
|
31
|
+
|
32
|
+
context "on PUT to /users/:id" do
|
33
|
+
setup do
|
34
|
+
put :update,
|
35
|
+
:id => @user.to_param,
|
36
|
+
:user => {:email => "none@example.com"}
|
37
|
+
end
|
38
|
+
should_set_the_flash_to /updated/i
|
39
|
+
should_redirect_to "root_url"
|
40
|
+
should_assign_to :user
|
41
|
+
should "update the user's attributes" do
|
42
|
+
assert_equal "none@example.com", assigns(:user).email
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "on PUT to /users/:id with invalid attributes" do
|
47
|
+
setup { put :update, :id => @user.to_param, :user => {:email => ''} }
|
48
|
+
should_not_set_the_flash
|
49
|
+
should_assign_to :user
|
50
|
+
should_render_template 'edit'
|
51
|
+
should "display errors" do
|
52
|
+
assert_select '#errorExplanation'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "dealing with another user's account" do
|
58
|
+
setup do
|
59
|
+
@target_user = Factory(:user, :account => @user.account)
|
60
|
+
assert_equal @user.account, @target_user.account
|
61
|
+
end
|
62
|
+
|
63
|
+
should_deny_access_on "get :show, :id => @target_user.to_param", :flash => /cannot edit/i
|
64
|
+
should_deny_access_on "get :edit, :id => @target_user.to_param", :flash => /cannot edit/i
|
65
|
+
should_deny_access_on "put :update, :id => @target_user.to_param, :user => {}", :flash => /cannot edit/i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dancroak-clearance
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- thoughtbot, inc.
|
8
|
+
- Dan Croak
|
9
|
+
- Josh Nichols
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2008-09-06 00:00:00 -07:00
|
15
|
+
default_executable:
|
16
|
+
dependencies: []
|
17
|
+
|
18
|
+
description: Simple, complete Rails authentication scheme.
|
19
|
+
email: dcroak@thoughtbot.com
|
20
|
+
executables: []
|
21
|
+
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files: []
|
25
|
+
|
26
|
+
files:
|
27
|
+
- README.textile
|
28
|
+
- clearance.gemspec
|
29
|
+
- lib/clearance.rb
|
30
|
+
- lib/clearance/application_controller.rb
|
31
|
+
- lib/clearance/model.rb
|
32
|
+
- lib/clearance/sessions_controller.rb
|
33
|
+
- lib/clearance/sessions_controller_test.rb
|
34
|
+
- lib/clearance/test_helper.rb
|
35
|
+
- lib/clearance/unit_test.rb
|
36
|
+
- lib/clearance/users_controller.rb
|
37
|
+
- lib/clearance/users_controller_test.rb
|
38
|
+
has_rdoc: false
|
39
|
+
homepage: http://github.com/dancroak/clearance
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.2.0
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: Simple, complete Rails authentication.
|
64
|
+
test_files: []
|
65
|
+
|