nagybence-clearance 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/LICENSE +21 -0
  2. data/README.textile +205 -0
  3. data/Rakefile +53 -0
  4. data/TODO.textile +8 -0
  5. data/generators/clearance/USAGE +1 -0
  6. data/generators/clearance/clearance_generator.rb +92 -0
  7. data/generators/clearance/lib/insert_commands.rb +103 -0
  8. data/generators/clearance/lib/rake_commands.rb +22 -0
  9. data/generators/clearance/templates/README +54 -0
  10. data/generators/clearance/templates/app/controllers/application.rb +5 -0
  11. data/generators/clearance/templates/app/controllers/confirmations_controller.rb +3 -0
  12. data/generators/clearance/templates/app/controllers/passwords_controller.rb +3 -0
  13. data/generators/clearance/templates/app/controllers/sessions_controller.rb +3 -0
  14. data/generators/clearance/templates/app/controllers/users_controller.rb +3 -0
  15. data/generators/clearance/templates/app/models/clearance_mailer.rb +5 -0
  16. data/generators/clearance/templates/app/models/user.rb +3 -0
  17. data/generators/clearance/templates/app/views/clearance_mailer/change_password.html.erb +10 -0
  18. data/generators/clearance/templates/app/views/clearance_mailer/confirmation.html.erb +1 -0
  19. data/generators/clearance/templates/app/views/passwords/edit.html.erb +25 -0
  20. data/generators/clearance/templates/app/views/passwords/new.html.erb +15 -0
  21. data/generators/clearance/templates/app/views/sessions/new.html.erb +28 -0
  22. data/generators/clearance/templates/app/views/users/_form.html.erb +13 -0
  23. data/generators/clearance/templates/app/views/users/edit.html.erb +6 -0
  24. data/generators/clearance/templates/app/views/users/new.html.erb +6 -0
  25. data/generators/clearance/templates/db/migrate/create_users_with_clearance_columns.rb +21 -0
  26. data/generators/clearance/templates/db/migrate/update_users_with_clearance_columns.rb +42 -0
  27. data/generators/clearance/templates/test/factories/clearance.rb +16 -0
  28. data/generators/clearance/templates/test/functional/confirmations_controller_test.rb +5 -0
  29. data/generators/clearance/templates/test/functional/passwords_controller_test.rb +5 -0
  30. data/generators/clearance/templates/test/functional/sessions_controller_test.rb +5 -0
  31. data/generators/clearance/templates/test/functional/users_controller_test.rb +5 -0
  32. data/generators/clearance/templates/test/unit/clearance_mailer_test.rb +6 -0
  33. data/generators/clearance/templates/test/unit/user_test.rb +5 -0
  34. data/lib/clearance/app/controllers/application_controller.rb +84 -0
  35. data/lib/clearance/app/controllers/confirmations_controller.rb +42 -0
  36. data/lib/clearance/app/controllers/passwords_controller.rb +67 -0
  37. data/lib/clearance/app/controllers/sessions_controller.rb +68 -0
  38. data/lib/clearance/app/controllers/users_controller.rb +40 -0
  39. data/lib/clearance/app/models/clearance_mailer.rb +29 -0
  40. data/lib/clearance/app/models/user.rb +89 -0
  41. data/lib/clearance/test/functional/confirmations_controller_test.rb +44 -0
  42. data/lib/clearance/test/functional/passwords_controller_test.rb +175 -0
  43. data/lib/clearance/test/functional/sessions_controller_test.rb +194 -0
  44. data/lib/clearance/test/functional/users_controller_test.rb +72 -0
  45. data/lib/clearance/test/test_helper.rb +28 -0
  46. data/lib/clearance/test/unit/clearance_mailer_test.rb +65 -0
  47. data/lib/clearance/test/unit/user_test.rb +167 -0
  48. data/lib/clearance.rb +14 -0
  49. data/rails/init.rb +1 -0
  50. data/shoulda_macros/clearance.rb +173 -0
  51. metadata +132 -0
@@ -0,0 +1,28 @@
1
+ <h2>Sign in</h2>
2
+
3
+ <% form_for :session, :url => session_path do |form| %>
4
+ <div class="text_field">
5
+ <%= form.label :email %>
6
+ <%= form.text_field :email %>
7
+ </div>
8
+ <div class="text_field">
9
+ <%= form.label :password %>
10
+ <%= form.password_field :password %>
11
+ </div>
12
+ <div class="text_field">
13
+ <%= form.check_box :remember_me %>
14
+ <%= form.label :remember_me %>
15
+ </div>
16
+ <div class="submit_field">
17
+ <%= form.submit "Sign in", :disable_with => "Please wait..." %>
18
+ </div>
19
+ <% end %>
20
+
21
+ <ul>
22
+ <li>
23
+ <%= link_to "Register", new_user_path %>
24
+ </li>
25
+ <li>
26
+ <%= link_to "Forgot password?", new_password_path %>
27
+ </li>
28
+ </ul>
@@ -0,0 +1,13 @@
1
+ <%= form.error_messages %>
2
+ <div class="text_field">
3
+ <%= form.label :email %>
4
+ <%= form.text_field :email %>
5
+ </div>
6
+ <div class="password_field">
7
+ <%= form.label :password %>
8
+ <%= form.password_field :password %>
9
+ </div>
10
+ <div class="password_field">
11
+ <%= form.label :password_confirmation, "Verify password" %>
12
+ <%= form.password_field :password_confirmation %>
13
+ </div>
@@ -0,0 +1,6 @@
1
+ <h2>Edit user information</h2>
2
+
3
+ <% form_for @user do |form| %>
4
+ <%= render :partial => '/users/form', :object => form %>
5
+ <%= form.submit 'Update', :disable_with => 'Please wait...' %>
6
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h2>Register</h2>
2
+
3
+ <% form_for @user do |form| %>
4
+ <%= render :partial => '/users/form', :object => form %>
5
+ <%= form.submit 'Sign up', :disable_with => 'Please wait...' %>
6
+ <% end %>
@@ -0,0 +1,21 @@
1
+ class CreateOrUpdateUsersWithClearanceColumns < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:users) do |t|
4
+ t.string :email
5
+ t.string :encrypted_password, :limit => 128
6
+ t.string :salt, :limit => 128
7
+ t.string :remember_token
8
+ t.datetime :remember_token_expires_at
9
+ t.boolean :email_confirmed, :default => false, :null => false
10
+ end
11
+
12
+ add_index :users, [:email, :encrypted_password]
13
+ add_index :users, [:id, :salt]
14
+ add_index :users, :email
15
+ add_index :users, :remember_token
16
+ end
17
+
18
+ def self.down
19
+ drop_table :users
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ class CreateOrUpdateUsersWithClearanceColumns < ActiveRecord::Migration
2
+ def self.up
3
+ <%
4
+ existing_columns = ActiveRecord::Base.connection.columns(:users).collect { |each| each.name }
5
+ columns = [
6
+ [:email, 't.string :email'],
7
+ [:encrypted_password, 't.string :encrypted_password, :limit => 180'],
8
+ [:salt, 't.string :salt, :limit => 180'],
9
+ [:remember_token, 't.string :remember_token'],
10
+ [:remember_token_expires_at, 't.datetime :remember_token_expires_at'],
11
+ [:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false']
12
+ ].delete_if {|c| existing_columns.include?(c.first.to_s)}
13
+ -%>
14
+ change_table(:users) do |t|
15
+ <% columns.each do |c| -%>
16
+ <%= c.last %>
17
+ <% end -%>
18
+ end
19
+
20
+ <%
21
+ existing_indexes = ActiveRecord::Base.connection.indexes(:users)
22
+ index_names = existing_indexes.collect { |each| each.name }
23
+ new_indexes = [
24
+ [:index_users_on_email_and_encrypted_password, 'add_index :users, [:email, :encrypted_password]'],
25
+ [:index_users_on_id_and_salt, 'add_index :users, [:id, :salt]'],
26
+ [:index_users_on_email, 'add_index :users, :email'],
27
+ [:index_users_on_remember_token, 'add_index :users, :remember_token']
28
+ ].delete_if { |each| index_names.include?(each.first.to_s) }
29
+ -%>
30
+ <% new_indexes.each do |each| -%>
31
+ <%= each.last %>
32
+ <% end -%>
33
+ end
34
+
35
+ def self.down
36
+ change_table(:users) do |t|
37
+ <% unless columns.empty? -%>
38
+ t.remove <%= columns.collect { |each| ":#{each.first}" }.join(',') %>
39
+ <% end -%>
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ Factory.sequence :email do |n|
2
+ "user#{n}@example.com"
3
+ end
4
+
5
+ Factory.define :registered_user, :class => 'user' do |user|
6
+ user.email { Factory.next :email }
7
+ user.password { "password" }
8
+ user.password_confirmation { "password" }
9
+ end
10
+
11
+ Factory.define :email_confirmed_user, :class => 'user' do |user|
12
+ user.email { Factory.next :email }
13
+ user.password { "password" }
14
+ user.password_confirmation { "password" }
15
+ user.email_confirmed { true }
16
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class ConfirmationsControllerTest < ActionController::TestCase
4
+ include Clearance::Test::Functional::ConfirmationsControllerTest
5
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class PasswordsControllerTest < ActionController::TestCase
4
+ include Clearance::Test::Functional::PasswordsControllerTest
5
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class SessionsControllerTest < ActionController::TestCase
4
+ include Clearance::Test::Functional::SessionsControllerTest
5
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UsersControllerTest < ActionController::TestCase
4
+ include Clearance::Test::Functional::UsersControllerTest
5
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class ClearanceMailerTest < ActionMailer::TestCase
4
+ tests ClearanceMailer
5
+ include Clearance::Test::Unit::ClearanceMailerTest
6
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UserTest < Test::Unit::TestCase
4
+ include Clearance::Test::Unit::UserTest
5
+ end
@@ -0,0 +1,84 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module ApplicationController
5
+
6
+ def self.included(controller)
7
+ controller.class_eval do
8
+
9
+ helper_method :current_user
10
+ helper_method :signed_in?
11
+
12
+ hide_action :current_user, :signed_in?
13
+
14
+ def current_user
15
+ @_current_user ||= (user_from_session || user_from_cookie)
16
+ end
17
+
18
+ def signed_in?
19
+ ! current_user.nil?
20
+ end
21
+
22
+ protected
23
+
24
+ def authenticate
25
+ deny_access unless signed_in?
26
+ end
27
+
28
+ def user_from_session
29
+ if session[:user_id] && session[:salt]
30
+ user = User.find_by_id_and_salt(session[:user_id], session[:salt])
31
+ end
32
+ user && user.email_confirmed? ? user : nil
33
+ end
34
+
35
+ def user_from_cookie
36
+ if cookies[:remember_token]
37
+ user = User.find_by_remember_token(cookies[:remember_token])
38
+ end
39
+ user && user.remember? ? user : nil
40
+ end
41
+
42
+ # Hook
43
+ def sign_user_in(user)
44
+ sign_in(user)
45
+ end
46
+
47
+ def sign_in(user)
48
+ if user
49
+ session[:user_id] = user.id
50
+ session[:salt] = user.salt
51
+ end
52
+ end
53
+
54
+ def redirect_back_or(default)
55
+ session[:return_to] ||= params[:return_to]
56
+ if session[:return_to]
57
+ redirect_to(session[:return_to])
58
+ else
59
+ redirect_to(default)
60
+ end
61
+ session[:return_to] = nil
62
+ end
63
+
64
+ def redirect_to_root
65
+ redirect_to root_url
66
+ end
67
+
68
+ def store_location
69
+ session[:return_to] = request.request_uri
70
+ end
71
+
72
+ def deny_access(flash_message = nil, opts = {})
73
+ store_location
74
+ flash[:failure] = flash_message if flash_message
75
+ render :template => "/sessions/new", :status => :unauthorized
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,42 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module ConfirmationsController
5
+
6
+ def self.included(controller)
7
+ controller.class_eval do
8
+
9
+ before_filter :existing_user?, :only => :new
10
+ filter_parameter_logging :salt
11
+
12
+ def new
13
+ create
14
+ end
15
+
16
+ def create
17
+ @user.confirm_email!
18
+ sign_user_in(@user)
19
+ flash[:success] = "Confirmed email and signed in."
20
+ redirect_to url_after_create
21
+ end
22
+
23
+ private
24
+
25
+ def existing_user?
26
+ @user = User.find_by_id_and_salt(params[:user_id], params[:salt])
27
+ if @user.nil?
28
+ render :nothing => true, :status => :not_found
29
+ end
30
+ end
31
+
32
+ def url_after_create
33
+ root_url
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,67 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module PasswordsController
5
+
6
+ def self.included(controller)
7
+ controller.class_eval do
8
+
9
+ before_filter :existing_user?, :only => [:edit, :update]
10
+ filter_parameter_logging :password, :password_confirmation
11
+
12
+ def new
13
+ end
14
+
15
+ def create
16
+ user = User.find_by_email(params[:password][:email])
17
+ if user.nil?
18
+ flash.now[:notice] = "Unknown email"
19
+ render :action => :new
20
+ else
21
+ ClearanceMailer.deliver_change_password user
22
+ flash[:notice] = "Details for changing your password have been sent"
23
+ redirect_to url_after_create
24
+ end
25
+ end
26
+
27
+ def edit
28
+ @user = User.find_by_email_and_encrypted_password(params[:email],
29
+ params[:password])
30
+ end
31
+
32
+ def update
33
+ @user = User.find_by_email_and_encrypted_password(params[:email],
34
+ params[:password])
35
+ if @user.update_attributes params[:user]
36
+ sign_user_in(@user)
37
+ redirect_to url_after_update
38
+ else
39
+ render :action => :edit
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def existing_user?
46
+ user = User.find_by_email_and_encrypted_password(params[:email],
47
+ params[:password])
48
+ if user.nil?
49
+ render :nothing => true, :status => :not_found
50
+ end
51
+ end
52
+
53
+ def url_after_create
54
+ new_session_url
55
+ end
56
+
57
+ def url_after_update
58
+ root_url
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,68 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module SessionsController
5
+
6
+ def self.included(controller)
7
+ controller.class_eval do
8
+
9
+ protect_from_forgery :except => :create
10
+ filter_parameter_logging :password
11
+
12
+ def create
13
+ @user = User.authenticate(params[:session][:email],
14
+ params[:session][:password])
15
+ if @user.nil?
16
+ flash.now[:notice] = "Bad email or password."
17
+ render :action => :new
18
+ else
19
+ if @user.email_confirmed?
20
+ remember(@user) if remember?
21
+ sign_user_in(@user)
22
+ flash[:notice] = "Signed in successfully"
23
+ redirect_back_or url_after_create
24
+ else
25
+ deny_access("User has not confirmed email.")
26
+ end
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ forget(current_user)
32
+ reset_session
33
+ flash[:notice] = "You have been signed out."
34
+ redirect_to url_after_destroy
35
+ end
36
+
37
+ private
38
+
39
+ def remember?
40
+ params[:session] && params[:session][:remember_me] == "1"
41
+ end
42
+
43
+ def remember(user)
44
+ user.remember_me!
45
+ cookies[:remember_token] = { :value => user.remember_token,
46
+ :expires => user.remember_token_expires_at }
47
+ end
48
+
49
+ def forget(user)
50
+ user.forget_me! if user
51
+ cookies.delete :remember_token
52
+ end
53
+
54
+ def url_after_create
55
+ root_url
56
+ end
57
+
58
+ def url_after_destroy
59
+ new_session_url
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module UsersController
5
+
6
+ def self.included(controller)
7
+ controller.class_eval do
8
+
9
+ before_filter :redirect_to_root, :only => [:new, :create], :if => :signed_in?
10
+ filter_parameter_logging :password
11
+
12
+ def new
13
+ @user = User.new(params[:user])
14
+ end
15
+
16
+ def create
17
+ @user = User.new params[:user]
18
+ if @user.save
19
+ ClearanceMailer.deliver_confirmation @user
20
+ flash[:notice] = "You will receive an email within the next few minutes. " <<
21
+ "It contains instructions for you to confirm your account."
22
+ redirect_to url_after_create
23
+ else
24
+ render :action => "new"
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def url_after_create
31
+ new_session_url
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ module Clearance
2
+ module App
3
+ module Models
4
+ module ClearanceMailer
5
+
6
+ def self.included(mailer)
7
+ mailer.class_eval do
8
+
9
+ def change_password(user)
10
+ from DO_NOT_REPLY
11
+ recipients user.email
12
+ subject "Change your password"
13
+ body :user => user
14
+ end
15
+
16
+ def confirmation(user)
17
+ from DO_NOT_REPLY
18
+ recipients user.email
19
+ subject "Account confirmation"
20
+ body :user => user
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ require 'digest/sha2'
2
+
3
+ module Clearance
4
+ module App
5
+ module Models
6
+ module User
7
+
8
+ def self.included(model)
9
+ model.class_eval do
10
+
11
+ attr_accessible :email, :password, :password_confirmation
12
+ attr_accessor :password, :password_confirmation
13
+
14
+ validates_presence_of :email
15
+ validates_presence_of :password, :if => :password_required?
16
+ validates_confirmation_of :password, :if => :password_required?
17
+ validates_uniqueness_of :email, :case_sensitive => false
18
+ validates_format_of :email, :with => %r{.+@.+\..+}
19
+
20
+ before_save :initialize_salt, :encrypt_password, :downcase_email
21
+
22
+ def self.authenticate(email, password)
23
+ user = find(:first, :conditions => ['LOWER(email) = ?', email.to_s.downcase])
24
+ user && user.authenticated?(password) ? user : nil
25
+ end
26
+
27
+ def authenticated?(password)
28
+ encrypted_password == encrypt(password)
29
+ end
30
+
31
+ def encrypt(string)
32
+ hash("--#{salt}--#{string}--")
33
+ end
34
+
35
+ def remember?
36
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
37
+ end
38
+
39
+ def remember_me!
40
+ remember_me_until 2.weeks.from_now.utc
41
+ end
42
+
43
+ def remember_me_until(time)
44
+ self.update_attribute :remember_token_expires_at, time
45
+ self.update_attribute :remember_token,
46
+ encrypt("--#{remember_token_expires_at}--#{password}--")
47
+ end
48
+
49
+ def forget_me!
50
+ self.update_attribute :remember_token_expires_at, nil
51
+ self.update_attribute :remember_token, nil
52
+ end
53
+
54
+ def confirm_email!
55
+ self.update_attribute :email_confirmed, true
56
+ end
57
+
58
+ protected
59
+
60
+ def hash(string)
61
+ Digest::SHA512.hexdigest(string)
62
+ end
63
+
64
+ def initialize_salt
65
+ if new_record?
66
+ self.salt = hash("--#{Time.now.utc.to_s}--#{password}--")
67
+ end
68
+ end
69
+
70
+ def encrypt_password
71
+ return if password.blank?
72
+ self.encrypted_password = encrypt(password)
73
+ end
74
+
75
+ def password_required?
76
+ encrypted_password.blank? || !password.blank?
77
+ end
78
+
79
+ def downcase_email
80
+ self.email = email.to_s.downcase
81
+ end
82
+
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,44 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module ConfirmationsControllerTest
5
+
6
+ def self.included(controller_test)
7
+ controller_test.class_eval do
8
+
9
+ should_filter_params :salt
10
+
11
+ context "Given a user whose email has not been confirmed" do
12
+ setup { @user = Factory(:registered_user) }
13
+
14
+ context "on GET to #new with correct id and salt" do
15
+ setup do
16
+ get :new, :user_id => @user.to_param, :salt => @user.salt
17
+ end
18
+
19
+ should_set_the_flash_to /confirmed email/i
20
+ should_set_the_flash_to /signed in/i
21
+ should_be_signed_in_and_email_confirmed_as { @user }
22
+ should_redirect_to_url_after_create
23
+ end
24
+
25
+ context "on GET to #new with incorrect salt" do
26
+ setup do
27
+ salt = ""
28
+ assert_not_equal salt, @user.salt
29
+
30
+ get :new, :user_id => @user.to_param, :salt => salt
31
+ end
32
+
33
+ should_respond_with :not_found
34
+ should_render_nothing
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end