nagybence-clearance 0.4.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.
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