thoughtbot-clearance 0.1.4

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 ADDED
@@ -0,0 +1,178 @@
1
+ h1. Clearance
2
+
3
+ Simple, complete Ruby web app 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
+ * tests included using "shoulda":http://thoughtbot.com/projects/shoulda & "factory_girl":http://thoughtbot.com/projects/factory_girl
13
+
14
+ h2. Gem installation (Rails 2.1+)
15
+
16
+ Specify the gem dependency in your config/environment.rb file:
17
+
18
+ Rails::Initializer.run do |config|
19
+ # ...
20
+ config.gem 'thoughtbot-shoulda', :lib => 'shoulda', :source => "http://gems.github.com"
21
+ config.gem 'thoughtbot-factory_girl', :lib => 'factory_girl', :source => "http://gems.github.com"
22
+ config.gem "dancroak-clearance", :lib => 'clearance', :source => 'http://gems.github.com/'
23
+ end
24
+
25
+ Then:
26
+
27
+ rake gems:install
28
+ rake gems:unpack
29
+
30
+ h2. Tests
31
+
32
+ The tests use "Shoulda":http://thoughtbot.com/projects/shoulda and "Factory Girl":http://thoughtbot.com/projects/factory_girl. You should create a User Factory:
33
+
34
+ Factory.sequence :email do |n|
35
+ "user#{n}@example.com"
36
+ end
37
+
38
+ Factory.define :user do |user|
39
+ user.email { Factory.next :email }
40
+ user.password 'sekrit'
41
+ user.password_confirmation 'sekrit'
42
+ end
43
+
44
+ In test/test_helper.rb:
45
+
46
+ class Test::Unit::TestCase
47
+ self.use_transactional_fixtures = true
48
+ self.use_instantiated_fixtures = false
49
+ include Clearance::TestHelper
50
+ end
51
+
52
+ In test/unit/user_test.rb:
53
+
54
+ require File.dirname(__FILE__) + '/../test_helper'
55
+
56
+ class UserTest < Test::Unit::TestCase
57
+ include Clearance::UserTest
58
+ end
59
+
60
+ In test/functional/sessions_controller_test.rb:
61
+
62
+ require File.dirname(__FILE__) + '/../test_helper'
63
+
64
+ class SessionsControllerTest < ActionController::TestCase
65
+ include Clearance::SessionsControllerTest
66
+
67
+ private
68
+
69
+ def url_after_create
70
+ root_url # the default
71
+ end
72
+
73
+ def url_after_destroy
74
+ login_url # the default
75
+ end
76
+ end
77
+
78
+ In test/functional/users_controller_test.rb:
79
+
80
+ require File.dirname(__FILE__) + '/../test_helper'
81
+
82
+ class UsersControllerTest < ActionController::TestCase
83
+ include Clearance::UsersControllerTest
84
+
85
+ private
86
+
87
+ def url_after_create
88
+ root_url # the default
89
+ end
90
+
91
+ def url_after_update
92
+ root_url # the default
93
+ end
94
+ end
95
+
96
+ h2. Schema
97
+
98
+ Change your User model so it has these attributes:
99
+
100
+ change_table(:users) do |t|
101
+ t.column :email, :string
102
+ t.column :crypted_password, :string, :limit => 40
103
+ t.column :salt, :string, :limit => 40
104
+ t.column :remember_token, :string
105
+ t.column :remember_token_expires_at, :datetime
106
+ end
107
+
108
+ add_index :users, [:email, :crypted_password]
109
+
110
+ h2. User Model
111
+
112
+ In app/models/user.rb:
113
+
114
+ class User < ActiveRecord::Base
115
+ include Clearance::Model
116
+ end
117
+
118
+ h2. Controllers
119
+
120
+ In app/controllers/application_controller.rb:
121
+
122
+ class ApplicationController < ActionController::Base
123
+ helper :all
124
+ protect_from_forgery
125
+ include Clearance::ApplicationController
126
+ end
127
+
128
+ In app/controllers/sessions_controller.rb:
129
+
130
+ class SessionsController < ApplicationController
131
+ include Clearance::SessionsController
132
+ end
133
+
134
+ In app/controllers/users_controller.rb:
135
+
136
+ class UsersController < ApplicationController
137
+ include Clearance::UsersController
138
+ end
139
+
140
+ h2. Routes
141
+
142
+ map.with_options :controller => 'sessions' do |m|
143
+ m.login '/login', :action => 'new'
144
+ m.logout '/logout', :action => 'destroy'
145
+ end
146
+ map.resource :session
147
+ map.resources :users
148
+
149
+ h2. Views
150
+
151
+ In app/views/sessions/new.html.erb
152
+
153
+ <% form_for :session, :url => session_path do |f| %>
154
+ <div class="group">
155
+ <%= f.label :email %>
156
+ <%= f.text_field :email %>
157
+ </div>
158
+ <div class="group">
159
+ <%= f.label :password %>
160
+ <%= f.password_field :password %>
161
+ </div>
162
+ <div class="checkbox">
163
+ <%= f.check_box :remember_me %>
164
+ <%= f.label :remember_me %>
165
+ </div>
166
+ <div class="group buttons">
167
+ <%= f.submit 'Login', :disable_with => 'Please wait...' %>
168
+ </div>
169
+ <% end %>
170
+
171
+ h2. Authors
172
+
173
+ * thoughtbot, inc.
174
+ * Dan Croak
175
+ * Josh Nichols
176
+ * Mike Breen
177
+ * Mike Burns
178
+ * Jason Morrison
data/clearance.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "clearance"
3
+ s.version = "0.1.4"
4
+ s.date = "2008-09-24"
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", "Mike Breen", "Mike Burns", "Jason Morrison"]
10
+ s.files = ["README.textile",
11
+ "clearance.gemspec",
12
+ "lib/clearance.rb",
13
+ "lib/clearance/app/controllers/application_controller.rb",
14
+ "lib/clearance/app/models/model.rb",
15
+ "lib/clearance/app/controllers/sessions_controller.rb",
16
+ "lib/clearance/test/functionals/sessions_controller_test.rb",
17
+ "lib/clearance/test/test_helper.rb",
18
+ "lib/clearance/test/units/user_test.rb",
19
+ "lib/clearance/app/controllers/users_controller.rb",
20
+ "lib/clearance/test/functionals/users_controller_test.rb"]
21
+ end
data/lib/clearance.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'clearance/app/controllers/application_controller'
2
+ require 'clearance/app/controllers/sessions_controller'
3
+ require 'clearance/app/controllers/users_controller'
4
+ require 'clearance/app/models/model'
5
+ require 'clearance/test/test_helper'
6
+ require 'clearance/test/functionals/sessions_controller_test'
7
+ require 'clearance/test/functionals/users_controller_test'
8
+ require 'clearance/test/units/user_test'
@@ -0,0 +1,70 @@
1
+ module Clearance
2
+ module ApplicationController
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ helper_method :current_user
7
+ helper_method :logged_in?
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
+
21
+ def logged_in?
22
+ ! current_user.nil?
23
+ end
24
+ end
25
+
26
+ module ProtectedInstanceMethods
27
+ def authenticate
28
+ deny_access unless self.current_user
29
+ end
30
+
31
+ def user_from_session
32
+ User.find_by_id session[:user_id]
33
+ end
34
+
35
+ def user_from_cookie
36
+ user = User.find_by_remember_token(cookies[:auth_token]) if cookies[:auth_token]
37
+ user && user.remember_token? ? user : nil
38
+ end
39
+
40
+ def login(user)
41
+ create_session_for user
42
+ @current_user = user
43
+ end
44
+
45
+ def create_session_for(user)
46
+ session[:user_id] = user.id if user
47
+ end
48
+
49
+ def redirect_back_or(default)
50
+ session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(default)
51
+ session[:return_to] = nil
52
+ end
53
+
54
+ def redirect_to_root
55
+ redirect_to root_url
56
+ end
57
+
58
+ def store_location
59
+ session[:return_to] = request.request_uri
60
+ end
61
+
62
+ def deny_access(flash_message = nil, opts = {})
63
+ opts[:redirect] ||= login_url
64
+ store_location
65
+ flash[:error] = flash_message if flash_message
66
+ redirect_to opts[:redirect]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,73 @@
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 url_after_destroy
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 url_after_create
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
+
64
+ def url_after_create
65
+ root_url
66
+ end
67
+
68
+ def url_after_destroy
69
+ login_url
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,81 @@
1
+ module Clearance
2
+ module UsersController
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ before_filter :authenticate, :except => [:new, :create]
7
+ before_filter :redirect_to_root, :only => [:new, :create], :if => :logged_in?
8
+ before_filter :ensure_user_is_accessing_self, :only => [:edit, :update, :show]
9
+
10
+ filter_parameter_logging :password
11
+
12
+ include InstanceMethods
13
+
14
+ private
15
+ include PrivateInstanceMethods
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def index
21
+ @users = User.find :all
22
+ end
23
+
24
+ def new
25
+ @user = User.new
26
+ end
27
+
28
+ def show
29
+ @user = User.find params[:id]
30
+ end
31
+
32
+ def create
33
+ @user = User.new params[:user]
34
+ if @user.save
35
+ current_user = @user
36
+ flash[:notice] = "User created and logged in."
37
+ redirect_back_or root_url
38
+ else
39
+ render :action => "new"
40
+ end
41
+ end
42
+
43
+ def edit
44
+ @user = User.find params[:id]
45
+ end
46
+
47
+ def update
48
+ @user = User.find params[:id]
49
+
50
+ if @user.update_attributes params[:user]
51
+ flash[:notice] = "User updated."
52
+ redirect_back_or root_url
53
+ else
54
+ render :action => "edit"
55
+ end
56
+ end
57
+
58
+ def destroy
59
+ @user = User.find params[:id]
60
+ @user.destroy
61
+ redirect_to root_url
62
+ end
63
+ end
64
+
65
+ module PrivateInstanceMethods
66
+ def ensure_user_is_accessing_self
67
+ return if current_user and current_user.respond_to?(:admin?) and current_user.admin?
68
+ deny_access('You cannot edit that user.', :redirect => root_url) unless current_user.id.to_i == params[:id].to_i
69
+ end
70
+
71
+ def url_after_create
72
+ root_url
73
+ end
74
+
75
+ def url_after_update
76
+ root_url
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,78 @@
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_confirmation_of :password, :if => :password_required?
13
+ validates_uniqueness_of :email
14
+
15
+ before_save :initialize_salt, :encrypt_password
16
+
17
+ extend ClassMethods
18
+ include InstanceMethods
19
+
20
+ protected
21
+
22
+ include ProtectedInstanceMethods
23
+
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def authenticate(email, password)
29
+ user = find_by_email email
30
+ user && user.authenticated?(password) ? user : nil
31
+ end
32
+ end
33
+
34
+ module InstanceMethods
35
+ def authenticated?(password)
36
+ crypted_password == encrypt(password)
37
+ end
38
+
39
+ def encrypt(password)
40
+ Digest::SHA1.hexdigest "--#{salt}--#{password}--"
41
+ end
42
+
43
+ def remember_token?
44
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
45
+ end
46
+
47
+ def remember_me!
48
+ remember_me_until 2.weeks.from_now.utc
49
+ end
50
+
51
+ def remember_me_until(time)
52
+ self.update_attribute :remember_token_expires_at, time
53
+ self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
54
+ end
55
+
56
+ def forget_me!
57
+ self.update_attribute :remember_token_expires_at, nil
58
+ self.update_attribute :remember_token, nil
59
+ end
60
+ end
61
+
62
+ module ProtectedInstanceMethods
63
+ def initialize_salt
64
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
65
+ end
66
+
67
+ def encrypt_password
68
+ return if password.blank?
69
+ self.crypted_password = encrypt(password)
70
+ end
71
+
72
+ def password_required?
73
+ crypted_password.blank? || !password.blank?
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,82 @@
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 { @user = Factory :user }
8
+
9
+ should_filter :password
10
+
11
+ context "on GET to /sessions/new" do
12
+ setup { get :new }
13
+
14
+ should_respond_with :success
15
+ should_render_template :new
16
+ should_not_set_the_flash
17
+ should_have_form :action => "session_path",
18
+ :fields => { "session[email]" => :text,
19
+ "session[password]" => :password,
20
+ "session[remember_me]" => :checkbox }
21
+ end
22
+
23
+ context "a POST to #create with good credentials" do
24
+ setup do
25
+ post :create, :session => { :email => @user.email, :password => @user.password }
26
+ end
27
+
28
+ should_set_the_flash_to /success/i
29
+ should_redirect_to '@controller.send(:url_after_create)'
30
+ # TODO: should set session
31
+ end
32
+
33
+ context "a POST to #create with bad credentials" do
34
+ setup do
35
+ post :create, :session => { :email => @user.email, :password => "bad value" }
36
+ end
37
+
38
+ should_set_the_flash_to /bad/i
39
+ should_render_template :new
40
+ # TODO: should not set session
41
+ end
42
+
43
+ # TODO: two tests for remember me - success and failure
44
+ end
45
+
46
+ context "While logged out" do
47
+ setup { logout }
48
+
49
+ context "logging out again" do
50
+ setup { delete :destroy }
51
+ should_redirect_to '@controller.send(:url_after_destroy)'
52
+ end
53
+ end
54
+
55
+ logged_in_user_context do
56
+ context "a DELETE to #destroy without a cookie" do
57
+ setup { delete :destroy }
58
+
59
+ should_set_the_flash_to(/logged out/i)
60
+ should_redirect_to '@controller.send(:url_after_destroy)'
61
+ end
62
+
63
+ context 'a DELETE to #destroy with a cookie' do
64
+ setup do
65
+ cookies['auth_token'] = CGI::Cookie.new 'token', 'value'
66
+ delete :destroy
67
+ end
68
+
69
+ should 'delete the cookie' do
70
+ assert cookies['auth_token'].empty?
71
+ end
72
+
73
+ should 'delete the remember me token in users table' do
74
+ assert_nil @user.reload.remember_token
75
+ assert_nil @user.reload.remember_token_expires_at
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,113 @@
1
+ module Clearance
2
+ module UsersControllerTest
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ public_context do
7
+
8
+ context "on GET to /users/new" do
9
+ setup { get :new }
10
+ should_respond_with :success
11
+ should_render_template :new
12
+ should_not_set_the_flash
13
+ should_have_form :action => "users_path",
14
+ :method => :post,
15
+ :fields => { :email => :text,
16
+ :password => :password,
17
+ :password_confirmation => :password }
18
+ end
19
+
20
+ context "on POST to /users" do
21
+ setup do
22
+ post :create, :user => {
23
+ :email => Factory.next(:email),
24
+ :password => 'skerit',
25
+ :password_confirmation => 'skerit'
26
+ }
27
+ end
28
+
29
+ should_set_the_flash_to /created/i
30
+ should_redirect_to "@controller.send(:url_after_create)"
31
+ should_assign_to :user
32
+ should_change 'User.count', :by => 1
33
+ end
34
+
35
+ should_deny_access_on "get :edit, :id => 1", :redirect => "login_url"
36
+ should_deny_access_on "put :update, :id => 1", :redirect => "login_url"
37
+ should_deny_access_on "get :show, :id => 1", :redirect => "login_url"
38
+ should_deny_access_on "delete :destroy, :id => 1", :redirect => "login_url"
39
+
40
+ end
41
+
42
+ logged_in_user_context do
43
+
44
+ should_deny_access_on "get :new"
45
+ should_deny_access_on "post :create, :user => {}"
46
+ should_filter :password
47
+
48
+ context "viewing their account" do
49
+ context "on GET to /users/:id/show" do
50
+ setup { get :show, :id => @user.to_param }
51
+ should_respond_with :success
52
+ should_render_template :show
53
+ should_not_set_the_flash
54
+
55
+ should 'assign to @user' do
56
+ assert_equal @user, assigns(:user)
57
+ end
58
+ end
59
+
60
+ should_deny_access_on "delete :destroy, :id => @user.to_param"
61
+
62
+ context "on GET to /users/:id/edit" do
63
+ setup { get :edit, :id => @user.to_param }
64
+
65
+ should_respond_with :success
66
+ should_render_template :edit
67
+ should_not_set_the_flash
68
+ should_assign_to :user
69
+ should_have_form :action => "user_path(@user)",
70
+ :method => :put,
71
+ :fields => { :email => :text,
72
+ :password => :password,
73
+ :password_confirmation => :password }
74
+ end
75
+
76
+ context "on PUT to /users/:id" do
77
+ setup do
78
+ put :update,
79
+ :id => @user.to_param,
80
+ :user => { :email => "none@example.com" }
81
+ end
82
+ should_set_the_flash_to /updated/i
83
+ should_redirect_to "@controller.send(:url_after_update)"
84
+ should_assign_to :user
85
+ should "update the user's attributes" do
86
+ assert_equal "none@example.com", assigns(:user).email
87
+ end
88
+ end
89
+
90
+ context "on PUT to /users/:id with invalid attributes" do
91
+ setup { put :update, :id => @user.to_param, :user => {:email => ''} }
92
+ should_not_set_the_flash
93
+ should_assign_to :user
94
+ should_render_template 'edit'
95
+ should "display errors" do
96
+ assert_select '#errorExplanation'
97
+ end
98
+ end
99
+ end
100
+
101
+ context "trying to access another user's account" do
102
+ setup { @user = Factory :user }
103
+
104
+ should_deny_access_on "get :show, :id => @user.to_param", :flash => /cannot edit/i
105
+ should_deny_access_on "get :edit, :id => @user.to_param", :flash => /cannot edit/i
106
+ should_deny_access_on "put :update, :id => @user.to_param, :user => {}", :flash => /cannot edit/i
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,100 @@
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] ||= "root_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
+ # should_have_form :action => 'admin_users_path',
51
+ # :method => :get,
52
+ # :fields => { 'email' => :text }
53
+ # TODO: http_method should be pulled out
54
+ def should_have_form(opts)
55
+ model = self.name.gsub(/ControllerTest$/, '').singularize.downcase
56
+ model = model[model.rindex('::')+2..model.size] if model.include?('::')
57
+ http_method, hidden_http_method = form_http_method opts[:method]
58
+ should "have a #{model} form" do
59
+ assert_select "form[action=?][method=#{http_method}]", eval(opts[:action]) do
60
+ if hidden_http_method
61
+ assert_select "input[type=hidden][name=_method][value=#{hidden_http_method}]"
62
+ end
63
+ opts[:fields].each do |attribute, type|
64
+ attribute = attribute.is_a?(Symbol) ? "#{model}[#{attribute.to_s}]" : attribute
65
+ assert_select "input[type=#{type.to_s}][name=?]", attribute
66
+ end
67
+ assert_select "input[type=submit]"
68
+ end
69
+ end
70
+ end
71
+
72
+ def form_http_method(http_method)
73
+ http_method = http_method.nil? ? 'post' : http_method.to_s
74
+ if http_method == "post" || http_method == "get"
75
+ return http_method, nil
76
+ else
77
+ return "post", http_method
78
+ end
79
+ end
80
+
81
+ def logged_in_user_context(&blk)
82
+ context "A logged in user" do
83
+ setup do
84
+ @user = Factory :user
85
+ login_as @user
86
+ end
87
+ merge_block(&blk)
88
+ end
89
+ end
90
+
91
+ def public_context(&blk)
92
+ context "The public" do
93
+ setup { logout }
94
+ merge_block(&blk)
95
+ end
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,186 @@
1
+ module Clearance
2
+ module UserTest
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 false' 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 false' 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
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thoughtbot-clearance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - thoughtbot, inc.
8
+ - Dan Croak
9
+ - Josh Nichols
10
+ - Mike Breen
11
+ - Mike Burns
12
+ - Jason Morrison
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2008-09-24 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Simple, complete Rails authentication scheme.
22
+ email: dcroak@thoughtbot.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - README.textile
31
+ - clearance.gemspec
32
+ - lib/clearance.rb
33
+ - lib/clearance/app/controllers/application_controller.rb
34
+ - lib/clearance/app/models/model.rb
35
+ - lib/clearance/app/controllers/sessions_controller.rb
36
+ - lib/clearance/test/functionals/sessions_controller_test.rb
37
+ - lib/clearance/test/test_helper.rb
38
+ - lib/clearance/test/units/user_test.rb
39
+ - lib/clearance/app/controllers/users_controller.rb
40
+ - lib/clearance/test/functionals/users_controller_test.rb
41
+ has_rdoc: false
42
+ homepage: http://github.com/dancroak/clearance
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.2.0
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: Simple, complete Rails authentication.
67
+ test_files: []
68
+