kellyfelkins-clearance 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +84 -0
- data/LICENSE +21 -0
- data/README.textile +76 -0
- data/Rakefile +73 -0
- data/generators/clearance/USAGE +1 -0
- data/generators/clearance/clearance_generator.rb +96 -0
- data/generators/clearance/lib/insert_commands.rb +103 -0
- data/generators/clearance/lib/rake_commands.rb +22 -0
- data/generators/clearance/templates/README +45 -0
- data/generators/clearance/templates/app/controllers/application.rb +5 -0
- data/generators/clearance/templates/app/controllers/confirmations_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/passwords_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/sessions_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/users_controller.rb +3 -0
- data/generators/clearance/templates/app/models/clearance_mailer.rb +5 -0
- data/generators/clearance/templates/app/models/user.rb +3 -0
- data/generators/clearance/templates/app/views/clearance_mailer/change_password.html.erb +7 -0
- data/generators/clearance/templates/app/views/clearance_mailer/confirmation.html.erb +2 -0
- data/generators/clearance/templates/app/views/passwords/edit.html.erb +23 -0
- data/generators/clearance/templates/app/views/passwords/new.html.erb +15 -0
- data/generators/clearance/templates/app/views/sessions/new.html.erb +28 -0
- data/generators/clearance/templates/app/views/users/_form.html.erb +13 -0
- data/generators/clearance/templates/app/views/users/edit.html.erb +6 -0
- data/generators/clearance/templates/app/views/users/new.html.erb +6 -0
- data/generators/clearance/templates/db/migrate/create_users_with_clearance_columns.rb +20 -0
- data/generators/clearance/templates/db/migrate/update_users_with_clearance_columns.rb +41 -0
- data/generators/clearance/templates/test/factories/clearance.rb +16 -0
- data/generators/clearance/templates/test/functional/confirmations_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/passwords_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/sessions_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/users_controller_test.rb +5 -0
- data/generators/clearance/templates/test/unit/clearance_mailer_test.rb +6 -0
- data/generators/clearance/templates/test/unit/user_test.rb +5 -0
- data/generators/clearance_features/USAGE +1 -0
- data/generators/clearance_features/clearance_features_generator.rb +20 -0
- data/generators/clearance_features/templates/features/password_reset.feature +31 -0
- data/generators/clearance_features/templates/features/sign_in.feature +41 -0
- data/generators/clearance_features/templates/features/sign_out.feature +22 -0
- data/generators/clearance_features/templates/features/sign_up.feature +30 -0
- data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +110 -0
- data/generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb +5 -0
- data/generators/clearance_features/templates/features/support/paths.rb +25 -0
- data/lib/clearance.rb +15 -0
- data/lib/clearance/app/controllers/application_controller.rb +84 -0
- data/lib/clearance/app/controllers/confirmations_controller.rb +63 -0
- data/lib/clearance/app/controllers/passwords_controller.rb +79 -0
- data/lib/clearance/app/controllers/sessions_controller.rb +74 -0
- data/lib/clearance/app/controllers/users_controller.rb +45 -0
- data/lib/clearance/app/models/clearance_mailer.rb +23 -0
- data/lib/clearance/app/models/user.rb +119 -0
- data/lib/clearance/lib/extensions/errors.rb +4 -0
- data/lib/clearance/lib/extensions/rescue.rb +1 -0
- data/lib/clearance/test/functional/confirmations_controller_test.rb +72 -0
- data/lib/clearance/test/functional/passwords_controller_test.rb +180 -0
- data/lib/clearance/test/functional/sessions_controller_test.rb +187 -0
- data/lib/clearance/test/functional/users_controller_test.rb +60 -0
- data/lib/clearance/test/unit/clearance_mailer_test.rb +65 -0
- data/lib/clearance/test/unit/user_test.rb +243 -0
- data/rails/init.rb +1 -0
- data/shoulda_macros/clearance.rb +241 -0
- metadata +157 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: Sign out
|
2
|
+
To protect my account from unauthorized access
|
3
|
+
A signed in user
|
4
|
+
Should be able to sign out
|
5
|
+
|
6
|
+
Scenario: User signs out
|
7
|
+
Given I am signed up and confirmed as "email@person.com/password"
|
8
|
+
When I sign in as "email@person.com/password"
|
9
|
+
Then I should be signed in
|
10
|
+
And I sign out
|
11
|
+
Then I should see "You have been signed out"
|
12
|
+
And I should not be signed in
|
13
|
+
|
14
|
+
Scenario: User who was remembered signs out
|
15
|
+
Given I am signed up and confirmed as "email@person.com/password"
|
16
|
+
When I sign in with "remember me" as "email@person.com/password"
|
17
|
+
Then I should be signed in
|
18
|
+
And I sign out
|
19
|
+
Then I should see "You have been signed out"
|
20
|
+
And I should not be signed in
|
21
|
+
When I return next time
|
22
|
+
Then I should not be signed in
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Feature: Sign up
|
2
|
+
In order to get access to protected sections of the site
|
3
|
+
A user
|
4
|
+
Should be able to sign up
|
5
|
+
|
6
|
+
Scenario: User signs up with invalid data
|
7
|
+
When I go to the sign up page
|
8
|
+
And I fill in "Email" with "invalidemail"
|
9
|
+
And I fill in "Password" with "password"
|
10
|
+
And I fill in "Confirm password" with ""
|
11
|
+
And I press "Sign Up"
|
12
|
+
Then I should see error messages
|
13
|
+
|
14
|
+
Scenario: User signs up with valid data
|
15
|
+
When I go to the sign up page
|
16
|
+
And I fill in "Email" with "email@person.com"
|
17
|
+
And I fill in "Password" with "password"
|
18
|
+
And I fill in "Confirm password" with "password"
|
19
|
+
And I press "Sign Up"
|
20
|
+
Then I should see "instructions for confirming"
|
21
|
+
And a confirmation message should be sent to "email@person.com"
|
22
|
+
|
23
|
+
Scenario: User confirms his account
|
24
|
+
Given I signed up with "email@person.com/password"
|
25
|
+
When I follow the confirmation link sent to "email@person.com"
|
26
|
+
Then I should see "Confirmed email and signed in"
|
27
|
+
And I should be signed in
|
28
|
+
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# General
|
2
|
+
|
3
|
+
Then /^I should see error messages$/ do
|
4
|
+
Then %{I should see "error(s)? prohibited"}
|
5
|
+
end
|
6
|
+
|
7
|
+
# Database
|
8
|
+
|
9
|
+
Given /^no user exists with an email of "(.*)"$/ do |email|
|
10
|
+
assert_nil User.find_by_email(email)
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^I signed up with "(.*)\/(.*)"$/ do |email, password|
|
14
|
+
user = Factory :user,
|
15
|
+
:email => email,
|
16
|
+
:password => password,
|
17
|
+
:password_confirmation => password
|
18
|
+
end
|
19
|
+
|
20
|
+
Given /^I am signed up and confirmed as "(.*)\/(.*)"$/ do |email, password|
|
21
|
+
user = Factory :email_confirmed_user,
|
22
|
+
:email => email,
|
23
|
+
:password => password,
|
24
|
+
:password_confirmation => password
|
25
|
+
end
|
26
|
+
|
27
|
+
# Session
|
28
|
+
|
29
|
+
Then /^I should be signed in$/ do
|
30
|
+
assert_not_nil request.session[:user_id]
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^I should not be signed in$/ do
|
34
|
+
assert_nil request.session[:user_id]
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^session is cleared$/ do
|
38
|
+
request.session[:user_id] = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Emails
|
42
|
+
|
43
|
+
Then /^a confirmation message should be sent to "(.*)"$/ do |email|
|
44
|
+
user = User.find_by_email(email)
|
45
|
+
sent = ActionMailer::Base.deliveries.first
|
46
|
+
assert_equal [user.email], sent.to
|
47
|
+
assert_match /confirm/i, sent.subject
|
48
|
+
assert !user.token.blank?
|
49
|
+
assert_match /#{user.token}/, sent.body
|
50
|
+
end
|
51
|
+
|
52
|
+
When /^I follow the confirmation link sent to "(.*)"$/ do |email|
|
53
|
+
user = User.find_by_email(email)
|
54
|
+
visit new_user_confirmation_path(:user_id => user, :token => user.token)
|
55
|
+
end
|
56
|
+
|
57
|
+
Then /^a password reset message should be sent to "(.*)"$/ do |email|
|
58
|
+
user = User.find_by_email(email)
|
59
|
+
sent = ActionMailer::Base.deliveries.first
|
60
|
+
assert_equal [user.email], sent.to
|
61
|
+
assert_match /password/i, sent.subject
|
62
|
+
assert !user.token.blank?
|
63
|
+
assert_match /#{user.token}/, sent.body
|
64
|
+
end
|
65
|
+
|
66
|
+
When /^I follow the password reset link sent to "(.*)"$/ do |email|
|
67
|
+
user = User.find_by_email(email)
|
68
|
+
visit edit_user_password_path(:user_id => user, :token => user.token)
|
69
|
+
end
|
70
|
+
|
71
|
+
When /^I try to change the password of "(.*)" without token$/ do |email|
|
72
|
+
user = User.find_by_email(email)
|
73
|
+
visit edit_user_password_path(:user_id => user)
|
74
|
+
end
|
75
|
+
|
76
|
+
Then /^I should be forbidden$/ do
|
77
|
+
assert_response :forbidden
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Actions
|
82
|
+
|
83
|
+
When /^I sign in( with "remember me")? as "(.*)\/(.*)"$/ do |remember, email, password|
|
84
|
+
When %{I go to the sign in page}
|
85
|
+
And %{I fill in "Email" with "#{email}"}
|
86
|
+
And %{I fill in "Password" with "#{password}"}
|
87
|
+
And %{I check "Remember me"} if remember
|
88
|
+
And %{I press "Sign In"}
|
89
|
+
end
|
90
|
+
|
91
|
+
When /^I sign out$/ do
|
92
|
+
visit '/session', :delete
|
93
|
+
end
|
94
|
+
|
95
|
+
When /^I request password reset link to be sent to "(.*)"$/ do |email|
|
96
|
+
When %{I go to the password reset request page}
|
97
|
+
And %{I fill in "Email address" with "#{email}"}
|
98
|
+
And %{I press "Reset password"}
|
99
|
+
end
|
100
|
+
|
101
|
+
When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
|
102
|
+
And %{I fill in "Choose password" with "#{password}"}
|
103
|
+
And %{I fill in "Confirm password" with "#{confirmation}"}
|
104
|
+
And %{I press "Save this password"}
|
105
|
+
end
|
106
|
+
|
107
|
+
When /^I return next time$/ do
|
108
|
+
When %{session is cleared}
|
109
|
+
And %{I go to the homepage}
|
110
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
def path_to(page_name)
|
3
|
+
case page_name
|
4
|
+
|
5
|
+
when /the homepage/i
|
6
|
+
root_path
|
7
|
+
when /the sign up page/i
|
8
|
+
new_user_path
|
9
|
+
when /the sign in page/i
|
10
|
+
new_session_path
|
11
|
+
when /the password reset request page/i
|
12
|
+
new_password_path
|
13
|
+
|
14
|
+
# Add more page name => path mappings here
|
15
|
+
|
16
|
+
else
|
17
|
+
raise "Can't find mapping from \"#{page_name}\" to a path."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
World do |world|
|
23
|
+
world.extend NavigationHelpers
|
24
|
+
world
|
25
|
+
end
|
data/lib/clearance.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'clearance/lib/extensions/errors'
|
2
|
+
require 'clearance/lib/extensions/rescue'
|
3
|
+
require 'clearance/app/controllers/application_controller'
|
4
|
+
require 'clearance/app/controllers/confirmations_controller'
|
5
|
+
require 'clearance/app/controllers/passwords_controller'
|
6
|
+
require 'clearance/app/controllers/sessions_controller'
|
7
|
+
require 'clearance/app/controllers/users_controller'
|
8
|
+
require 'clearance/app/models/clearance_mailer'
|
9
|
+
require 'clearance/app/models/user'
|
10
|
+
require 'clearance/test/functional/confirmations_controller_test'
|
11
|
+
require 'clearance/test/functional/passwords_controller_test'
|
12
|
+
require 'clearance/test/functional/sessions_controller_test'
|
13
|
+
require 'clearance/test/functional/users_controller_test'
|
14
|
+
require 'clearance/test/unit/clearance_mailer_test'
|
15
|
+
require 'clearance/test/unit/user_test'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Clearance
|
2
|
+
module App
|
3
|
+
module Controllers
|
4
|
+
module ApplicationController
|
5
|
+
|
6
|
+
def self.included(controller)
|
7
|
+
controller.send(:include, InstanceMethods)
|
8
|
+
|
9
|
+
controller.class_eval do
|
10
|
+
helper_method :current_user
|
11
|
+
helper_method :signed_in?
|
12
|
+
|
13
|
+
hide_action :current_user, :signed_in?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def current_user
|
19
|
+
@_current_user ||= (user_from_cookie || user_from_session)
|
20
|
+
end
|
21
|
+
|
22
|
+
def signed_in?
|
23
|
+
! current_user.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def authenticate
|
29
|
+
deny_access unless signed_in?
|
30
|
+
end
|
31
|
+
|
32
|
+
def user_from_session
|
33
|
+
if session[:user_id]
|
34
|
+
return nil unless user = User.find_by_id(session[:user_id])
|
35
|
+
return user if user.email_confirmed?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def user_from_cookie
|
40
|
+
if token = cookies[:remember_token]
|
41
|
+
return nil unless user = User.find_by_token(token)
|
42
|
+
return user if user.remember?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def sign_user_in(user)
|
47
|
+
sign_in(user)
|
48
|
+
end
|
49
|
+
|
50
|
+
def sign_in(user)
|
51
|
+
if user
|
52
|
+
session[:user_id] = user.id
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def redirect_back_or(default)
|
57
|
+
session[:return_to] ||= params[:return_to]
|
58
|
+
if session[:return_to]
|
59
|
+
redirect_to(session[:return_to])
|
60
|
+
else
|
61
|
+
redirect_to(default)
|
62
|
+
end
|
63
|
+
session[:return_to] = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def redirect_to_root
|
67
|
+
redirect_to root_url
|
68
|
+
end
|
69
|
+
|
70
|
+
def store_location
|
71
|
+
session[:return_to] = request.request_uri if request.get?
|
72
|
+
end
|
73
|
+
|
74
|
+
def deny_access(flash_message = nil, opts = {})
|
75
|
+
store_location
|
76
|
+
flash[:failure] = flash_message if flash_message
|
77
|
+
redirect_to new_session_url
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Clearance
|
2
|
+
module App
|
3
|
+
module Controllers
|
4
|
+
module ConfirmationsController
|
5
|
+
|
6
|
+
def self.included(controller)
|
7
|
+
controller.send(:include, Actions)
|
8
|
+
controller.send(:include, PrivateMethods)
|
9
|
+
|
10
|
+
controller.class_eval do
|
11
|
+
before_filter :forbid_confirmed_user, :only => :new
|
12
|
+
before_filter :forbid_missing_token, :only => :new
|
13
|
+
before_filter :forbid_non_existant_user, :only => :new
|
14
|
+
filter_parameter_logging :token
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Actions
|
19
|
+
def new
|
20
|
+
create
|
21
|
+
end
|
22
|
+
|
23
|
+
def create
|
24
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
25
|
+
@user.confirm_email!
|
26
|
+
|
27
|
+
sign_user_in(@user)
|
28
|
+
flash[:success] = "Confirmed email and signed in."
|
29
|
+
redirect_to url_after_create
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module PrivateMethods
|
34
|
+
private
|
35
|
+
|
36
|
+
def forbid_confirmed_user
|
37
|
+
user = User.find_by_id(params[:user_id])
|
38
|
+
if user && user.email_confirmed?
|
39
|
+
raise ActionController::Forbidden, "confirmed user"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def forbid_missing_token
|
44
|
+
if params[:token].blank?
|
45
|
+
raise ActionController::Forbidden, "missing token"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def forbid_non_existant_user
|
50
|
+
unless User.find_by_id_and_token(params[:user_id], params[:token])
|
51
|
+
raise ActionController::Forbidden, "non-existant user"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def url_after_create
|
56
|
+
root_url
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Clearance
|
2
|
+
module App
|
3
|
+
module Controllers
|
4
|
+
module PasswordsController
|
5
|
+
|
6
|
+
def self.included(controller)
|
7
|
+
controller.send(:include, Actions)
|
8
|
+
controller.send(:include, PrivateMethods)
|
9
|
+
|
10
|
+
controller.class_eval do
|
11
|
+
before_filter :forbid_missing_token, :only => [:edit, :update]
|
12
|
+
before_filter :forbid_non_existant_user, :only => [:edit, :update]
|
13
|
+
filter_parameter_logging :password, :password_confirmation
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Actions
|
18
|
+
def new
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
if user = User.find_by_email(params[:password][:email])
|
23
|
+
user.forgot_password!
|
24
|
+
ClearanceMailer.deliver_change_password user
|
25
|
+
flash[:notice] = "You will receive an email within the next few minutes. " <<
|
26
|
+
"It contains instructions for changing your password."
|
27
|
+
redirect_to url_after_create
|
28
|
+
else
|
29
|
+
flash.now[:notice] = "Unknown email"
|
30
|
+
render :action => :new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def edit
|
35
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
36
|
+
end
|
37
|
+
|
38
|
+
def update
|
39
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
40
|
+
|
41
|
+
if @user.update_password(params[:user][:password],
|
42
|
+
params[:user][:password_confirmation])
|
43
|
+
@user.confirm_email! unless @user.email_confirmed?
|
44
|
+
sign_user_in(@user)
|
45
|
+
redirect_to url_after_update
|
46
|
+
else
|
47
|
+
render :action => :edit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module PrivateMethods
|
53
|
+
private
|
54
|
+
|
55
|
+
def forbid_missing_token
|
56
|
+
if params[:token].blank?
|
57
|
+
raise ActionController::Forbidden, "missing token"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def forbid_non_existant_user
|
62
|
+
unless User.find_by_id_and_token(params[:user_id], params[:token])
|
63
|
+
raise ActionController::Forbidden, "non-existant user"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def url_after_create
|
68
|
+
new_session_url
|
69
|
+
end
|
70
|
+
|
71
|
+
def url_after_update
|
72
|
+
root_url
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|