ggoodale-restful-authentication 1.1.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 (54) hide show
  1. data/CHANGELOG +68 -0
  2. data/README.textile +224 -0
  3. data/Rakefile +32 -0
  4. data/TODO +15 -0
  5. data/generators/authenticated/USAGE +1 -0
  6. data/generators/authenticated/authenticated_generator.rb +478 -0
  7. data/generators/authenticated/lib/insert_routes.rb +54 -0
  8. data/generators/authenticated/templates/_model_partial.html.erb +8 -0
  9. data/generators/authenticated/templates/activation.erb +3 -0
  10. data/generators/authenticated/templates/authenticated_system.rb +189 -0
  11. data/generators/authenticated/templates/authenticated_test_helper.rb +22 -0
  12. data/generators/authenticated/templates/controller.rb +43 -0
  13. data/generators/authenticated/templates/helper.rb +2 -0
  14. data/generators/authenticated/templates/login.html.erb +16 -0
  15. data/generators/authenticated/templates/mailer.rb +25 -0
  16. data/generators/authenticated/templates/migration.rb +26 -0
  17. data/generators/authenticated/templates/model.rb +83 -0
  18. data/generators/authenticated/templates/model_controller.rb +85 -0
  19. data/generators/authenticated/templates/model_helper.rb +93 -0
  20. data/generators/authenticated/templates/model_helper_spec.rb +158 -0
  21. data/generators/authenticated/templates/observer.rb +11 -0
  22. data/generators/authenticated/templates/signup.html.erb +19 -0
  23. data/generators/authenticated/templates/signup_notification.erb +8 -0
  24. data/generators/authenticated/templates/site_keys.rb +38 -0
  25. data/generators/authenticated/templates/spec/controllers/access_control_spec.rb +90 -0
  26. data/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb +102 -0
  27. data/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb +139 -0
  28. data/generators/authenticated/templates/spec/controllers/users_controller_spec.rb +198 -0
  29. data/generators/authenticated/templates/spec/fixtures/users.yml +60 -0
  30. data/generators/authenticated/templates/spec/helpers/users_helper_spec.rb +141 -0
  31. data/generators/authenticated/templates/spec/models/user_spec.rb +290 -0
  32. data/generators/authenticated/templates/stories/rest_auth_stories.rb +22 -0
  33. data/generators/authenticated/templates/stories/rest_auth_stories_helper.rb +81 -0
  34. data/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb +49 -0
  35. data/generators/authenticated/templates/stories/steps/ra_resource_steps.rb +179 -0
  36. data/generators/authenticated/templates/stories/steps/ra_response_steps.rb +171 -0
  37. data/generators/authenticated/templates/stories/steps/user_steps.rb +153 -0
  38. data/generators/authenticated/templates/stories/users/accounts.story +186 -0
  39. data/generators/authenticated/templates/stories/users/sessions.story +134 -0
  40. data/generators/authenticated/templates/test/functional_test.rb +82 -0
  41. data/generators/authenticated/templates/test/mailer_test.rb +31 -0
  42. data/generators/authenticated/templates/test/model_functional_test.rb +93 -0
  43. data/generators/authenticated/templates/test/unit_test.rb +164 -0
  44. data/init.rb +1 -0
  45. data/lib/authentication.rb +40 -0
  46. data/lib/authentication/by_cookie_token.rb +82 -0
  47. data/lib/authentication/by_password.rb +64 -0
  48. data/lib/authorization.rb +14 -0
  49. data/lib/authorization/aasm_roles.rb +63 -0
  50. data/lib/authorization/stateful_roles.rb +62 -0
  51. data/lib/trustification.rb +14 -0
  52. data/lib/trustification/email_validation.rb +20 -0
  53. data/rails/init.rb +3 -0
  54. metadata +115 -0
@@ -0,0 +1,82 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= controller_file_name %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
8
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9
+ # Then, you can remove it from this and the units test.
10
+ include AuthenticatedTestHelper
11
+
12
+ fixtures :<%= table_name %>
13
+
14
+ def test_should_login_and_redirect
15
+ post :create, :login => 'quentin', :password => 'monkey'
16
+ assert session[:<%= file_name %>_id]
17
+ assert_response :redirect
18
+ end
19
+
20
+ def test_should_fail_login_and_not_redirect
21
+ post :create, :login => 'quentin', :password => 'bad password'
22
+ assert_nil session[:<%= file_name %>_id]
23
+ assert_response :success
24
+ end
25
+
26
+ def test_should_logout
27
+ login_as :quentin
28
+ get :destroy
29
+ assert_nil session[:<%= file_name %>_id]
30
+ assert_response :redirect
31
+ end
32
+
33
+ def test_should_remember_me
34
+ @request.cookies["auth_token"] = nil
35
+ post :create, :login => 'quentin', :password => 'monkey', :remember_me => "1"
36
+ assert_not_nil @response.cookies["auth_token"]
37
+ end
38
+
39
+ def test_should_not_remember_me
40
+ @request.cookies["auth_token"] = nil
41
+ post :create, :login => 'quentin', :password => 'monkey', :remember_me => "0"
42
+ puts @response.cookies["auth_token"]
43
+ assert @response.cookies["auth_token"].blank?
44
+ end
45
+
46
+ def test_should_delete_token_on_logout
47
+ login_as :quentin
48
+ get :destroy
49
+ assert @response.cookies["auth_token"].blank?
50
+ end
51
+
52
+ def test_should_login_with_cookie
53
+ <%= table_name %>(:quentin).remember_me
54
+ @request.cookies["auth_token"] = cookie_for(:quentin)
55
+ get :new
56
+ assert @controller.send(:logged_in?)
57
+ end
58
+
59
+ def test_should_fail_expired_cookie_login
60
+ <%= table_name %>(:quentin).remember_me
61
+ <%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
62
+ @request.cookies["auth_token"] = cookie_for(:quentin)
63
+ get :new
64
+ assert !@controller.send(:logged_in?)
65
+ end
66
+
67
+ def test_should_fail_cookie_login
68
+ <%= table_name %>(:quentin).remember_me
69
+ @request.cookies["auth_token"] = auth_token('invalid_auth_token')
70
+ get :new
71
+ assert !@controller.send(:logged_in?)
72
+ end
73
+
74
+ protected
75
+ def auth_token(token)
76
+ CGI::Cookie.new('name' => 'auth_token', 'value' => token)
77
+ end
78
+
79
+ def cookie_for(<%= file_name %>)
80
+ auth_token <%= table_name %>(<%= file_name %>).remember_token
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= file_name %>_mailer'
3
+
4
+ class <%= class_name %>MailerTest < Test::Unit::TestCase
5
+ FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
6
+ CHARSET = "utf-8"
7
+
8
+ include ActionMailer::Quoting
9
+
10
+ def setup
11
+ ActionMailer::Base.delivery_method = :test
12
+ ActionMailer::Base.perform_deliveries = true
13
+ ActionMailer::Base.deliveries = []
14
+
15
+ @expected = TMail::Mail.new
16
+ @expected.set_content_type "text", "plain", { "charset" => CHARSET }
17
+ end
18
+
19
+ def test_dummy_test
20
+ #do nothing
21
+ end
22
+
23
+ private
24
+ def read_fixture(action)
25
+ IO.readlines("#{FIXTURES_PATH}/<%= file_name %>_mailer/#{action}")
26
+ end
27
+
28
+ def encode(subject)
29
+ quoted_printable(subject, CHARSET)
30
+ end
31
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= model_controller_file_name %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= model_controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= model_controller_class_name %>ControllerTest < ActionController::TestCase
8
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9
+ # Then, you can remove it from this and the units test.
10
+ include AuthenticatedTestHelper
11
+
12
+ fixtures :<%= table_name %>
13
+
14
+ def test_should_allow_signup
15
+ assert_difference '<%= class_name %>.count' do
16
+ create_<%= file_name %>
17
+ assert_response :redirect
18
+ end
19
+ end
20
+
21
+ def test_should_require_login_on_signup
22
+ assert_no_difference '<%= class_name %>.count' do
23
+ create_<%= file_name %>(:login => nil)
24
+ assert assigns(:<%= file_name %>).errors.on(:login)
25
+ assert_response :success
26
+ end
27
+ end
28
+
29
+ def test_should_require_password_on_signup
30
+ assert_no_difference '<%= class_name %>.count' do
31
+ create_<%= file_name %>(:password => nil)
32
+ assert assigns(:<%= file_name %>).errors.on(:password)
33
+ assert_response :success
34
+ end
35
+ end
36
+
37
+ def test_should_require_password_confirmation_on_signup
38
+ assert_no_difference '<%= class_name %>.count' do
39
+ create_<%= file_name %>(:password_confirmation => nil)
40
+ assert assigns(:<%= file_name %>).errors.on(:password_confirmation)
41
+ assert_response :success
42
+ end
43
+ end
44
+
45
+ def test_should_require_email_on_signup
46
+ assert_no_difference '<%= class_name %>.count' do
47
+ create_<%= file_name %>(:email => nil)
48
+ assert assigns(:<%= file_name %>).errors.on(:email)
49
+ assert_response :success
50
+ end
51
+ end
52
+ <% if options[:stateful] %>
53
+ def test_should_sign_up_user_in_pending_state
54
+ create_<%= file_name %>
55
+ assigns(:<%= file_name %>).reload
56
+ assert assigns(:<%= file_name %>).pending?
57
+ end<% end %>
58
+
59
+ <% if options[:include_activation] %>
60
+ def test_should_sign_up_user_with_activation_code
61
+ create_<%= file_name %>
62
+ assigns(:<%= file_name %>).reload
63
+ assert_not_nil assigns(:<%= file_name %>).activation_code
64
+ end
65
+
66
+ def test_should_activate_user
67
+ assert_nil <%= class_name %>.authenticate('aaron', 'test')
68
+ get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
69
+ assert_redirected_to '/<%= controller_routing_path %>/new'
70
+ assert_not_nil flash[:notice]
71
+ assert_equal <%= table_name %>(:aaron), <%= class_name %>.authenticate('aaron', 'monkey')
72
+ end
73
+
74
+ def test_should_not_activate_user_without_key
75
+ get :activate
76
+ assert_nil flash[:notice]
77
+ rescue ActionController::RoutingError
78
+ # in the event your routes deny this, we'll just bow out gracefully.
79
+ end
80
+
81
+ def test_should_not_activate_user_with_blank_key
82
+ get :activate, :activation_code => ''
83
+ assert_nil flash[:notice]
84
+ rescue ActionController::RoutingError
85
+ # well played, sir
86
+ end<% end %>
87
+
88
+ protected
89
+ def create_<%= file_name %>(options = {})
90
+ post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
91
+ :password => 'quire69', :password_confirmation => 'quire69' }.merge(options)
92
+ end
93
+ end
@@ -0,0 +1,164 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class <%= class_name %>Test < ActiveSupport::TestCase
4
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
5
+ # Then, you can remove it from this and the functional test.
6
+ include AuthenticatedTestHelper
7
+ fixtures :<%= table_name %>
8
+
9
+ def test_should_create_<%= file_name %>
10
+ assert_difference '<%= class_name %>.count' do
11
+ <%= file_name %> = create_<%= file_name %>
12
+ assert !<%= file_name %>.new_record?, "#{<%= file_name %>.errors.full_messages.to_sentence}"
13
+ end
14
+ end
15
+ <% if options[:include_activation] %>
16
+ def test_should_initialize_activation_code_upon_creation
17
+ <%= file_name %> = create_<%= file_name %>
18
+ <%= file_name %>.reload
19
+ assert_not_nil <%= file_name %>.activation_code
20
+ end
21
+ <% end %><% if options[:stateful] %>
22
+ def test_should_create_and_start_in_pending_state
23
+ <%= file_name %> = create_<%= file_name %>
24
+ <%= file_name %>.reload
25
+ assert <%= file_name %>.pending?
26
+ end
27
+
28
+ <% end %>
29
+ def test_should_require_login
30
+ assert_no_difference '<%= class_name %>.count' do
31
+ u = create_<%= file_name %>(:login => nil)
32
+ assert u.errors.on(:login)
33
+ end
34
+ end
35
+
36
+ def test_should_require_password
37
+ assert_no_difference '<%= class_name %>.count' do
38
+ u = create_<%= file_name %>(:password => nil)
39
+ assert u.errors.on(:password)
40
+ end
41
+ end
42
+
43
+ def test_should_require_password_confirmation
44
+ assert_no_difference '<%= class_name %>.count' do
45
+ u = create_<%= file_name %>(:password_confirmation => nil)
46
+ assert u.errors.on(:password_confirmation)
47
+ end
48
+ end
49
+
50
+ def test_should_require_email
51
+ assert_no_difference '<%= class_name %>.count' do
52
+ u = create_<%= file_name %>(:email => nil)
53
+ assert u.errors.on(:email)
54
+ end
55
+ end
56
+
57
+ def test_should_reset_password
58
+ <%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
59
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'new password')
60
+ end
61
+
62
+ def test_should_not_rehash_password
63
+ <%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
64
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin2', 'monkey')
65
+ end
66
+
67
+ def test_should_authenticate_<%= file_name %>
68
+ assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'monkey')
69
+ end
70
+
71
+ def test_should_set_remember_token
72
+ <%= table_name %>(:quentin).remember_me
73
+ assert_not_nil <%= table_name %>(:quentin).remember_token
74
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
75
+ end
76
+
77
+ def test_should_unset_remember_token
78
+ <%= table_name %>(:quentin).remember_me
79
+ assert_not_nil <%= table_name %>(:quentin).remember_token
80
+ <%= table_name %>(:quentin).forget_me
81
+ assert_nil <%= table_name %>(:quentin).remember_token
82
+ end
83
+
84
+ def test_should_remember_me_for_one_week
85
+ before = 1.week.from_now.utc
86
+ <%= table_name %>(:quentin).remember_me_for 1.week
87
+ after = 1.week.from_now.utc
88
+ assert_not_nil <%= table_name %>(:quentin).remember_token
89
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
90
+ assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
91
+ end
92
+
93
+ def test_should_remember_me_until_one_week
94
+ time = 1.week.from_now.utc
95
+ <%= table_name %>(:quentin).remember_me_until time
96
+ assert_not_nil <%= table_name %>(:quentin).remember_token
97
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
98
+ assert_equal <%= table_name %>(:quentin).remember_token_expires_at, time
99
+ end
100
+
101
+ def test_should_remember_me_default_two_weeks
102
+ before = 2.weeks.from_now.utc
103
+ <%= table_name %>(:quentin).remember_me
104
+ after = 2.weeks.from_now.utc
105
+ assert_not_nil <%= table_name %>(:quentin).remember_token
106
+ assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
107
+ assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
108
+ end
109
+ <% if options[:stateful] %>
110
+ def test_should_register_passive_<%= file_name %>
111
+ <%= file_name %> = create_<%= file_name %>(:password => nil, :password_confirmation => nil)
112
+ assert <%= file_name %>.passive?
113
+ <%= file_name %>.update_attributes(:password => 'new password', :password_confirmation => 'new password')
114
+ <%= file_name %>.register!
115
+ assert <%= file_name %>.pending?
116
+ end
117
+
118
+ def test_should_suspend_<%= file_name %>
119
+ <%= table_name %>(:quentin).suspend!
120
+ assert <%= table_name %>(:quentin).suspended?
121
+ end
122
+
123
+ def test_suspended_<%= file_name %>_should_not_authenticate
124
+ <%= table_name %>(:quentin).suspend!
125
+ assert_not_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
126
+ end
127
+
128
+ def test_should_unsuspend_<%= file_name %>_to_active_state
129
+ <%= table_name %>(:quentin).suspend!
130
+ assert <%= table_name %>(:quentin).suspended?
131
+ <%= table_name %>(:quentin).unsuspend!
132
+ assert <%= table_name %>(:quentin).active?
133
+ end
134
+
135
+ def test_should_unsuspend_<%= file_name %>_with_nil_activation_code_and_activated_at_to_passive_state
136
+ <%= table_name %>(:quentin).suspend!
137
+ <%= class_name %>.update_all :activation_code => nil, :activated_at => nil
138
+ assert <%= table_name %>(:quentin).suspended?
139
+ <%= table_name %>(:quentin).reload.unsuspend!
140
+ assert <%= table_name %>(:quentin).passive?
141
+ end
142
+
143
+ def test_should_unsuspend_<%= file_name %>_with_activation_code_and_nil_activated_at_to_pending_state
144
+ <%= table_name %>(:quentin).suspend!
145
+ <%= class_name %>.update_all :activation_code => 'foo-bar', :activated_at => nil
146
+ assert <%= table_name %>(:quentin).suspended?
147
+ <%= table_name %>(:quentin).reload.unsuspend!
148
+ assert <%= table_name %>(:quentin).pending?
149
+ end
150
+
151
+ def test_should_delete_<%= file_name %>
152
+ assert_nil <%= table_name %>(:quentin).deleted_at
153
+ <%= table_name %>(:quentin).delete!
154
+ assert_not_nil <%= table_name %>(:quentin).deleted_at
155
+ assert <%= table_name %>(:quentin).deleted?
156
+ end
157
+ <% end %>
158
+ protected
159
+ def create_<%= file_name %>(options = {})
160
+ record = <%= class_name %>.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire69', :password_confirmation => 'quire69' }.merge(options))
161
+ record.<% if options[:stateful] %>register! if record.valid?<% else %>save<% end %>
162
+ record
163
+ end
164
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "rails", "init")
@@ -0,0 +1,40 @@
1
+ module Authentication
2
+ mattr_accessor :login_regex, :bad_login_message,
3
+ :name_regex, :bad_name_message,
4
+ :email_name_regex, :domain_head_regex, :domain_tld_regex, :email_regex, :bad_email_message
5
+
6
+ self.login_regex = /\A\w[\w\.\-_@]+\z/ # ASCII, strict
7
+ # self.login_regex = /\A[[:alnum:]][[:alnum:]\.\-_@]+\z/ # Unicode, strict
8
+ # self.login_regex = /\A[^[:cntrl:]\\<>\/&]*\z/ # Unicode, permissive
9
+
10
+ self.bad_login_message = "use only letters, numbers, and .-_@ please.".freeze
11
+
12
+ self.name_regex = /\A[^[:cntrl:]\\<>\/&]*\z/ # Unicode, permissive
13
+ self.bad_name_message = "avoid non-printing characters and \\&gt;&lt;&amp;/ please.".freeze
14
+
15
+ self.email_name_regex = '[\w\.%\+\-]+'.freeze
16
+ self.domain_head_regex = '(?:[A-Z0-9\-]+\.)+'.freeze
17
+ self.domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'.freeze
18
+ self.email_regex = /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
19
+ self.bad_email_message = "should look like an email address.".freeze
20
+
21
+ def self.included(recipient)
22
+ recipient.extend(ModelClassMethods)
23
+ recipient.class_eval do
24
+ include ModelInstanceMethods
25
+ end
26
+ end
27
+
28
+ module ModelClassMethods
29
+ def secure_digest(*args)
30
+ Digest::SHA1.hexdigest(args.flatten.join('--'))
31
+ end
32
+
33
+ def make_token
34
+ secure_digest(Time.now, (1..10).map{ rand.to_s })
35
+ end
36
+ end # class methods
37
+
38
+ module ModelInstanceMethods
39
+ end # instance methods
40
+ end
@@ -0,0 +1,82 @@
1
+ # -*- coding: mule-utf-8 -*-
2
+ module Authentication
3
+ module ByCookieToken
4
+ # Stuff directives into including module
5
+ def self.included(recipient)
6
+ recipient.extend(ModelClassMethods)
7
+ recipient.class_eval do
8
+ include ModelInstanceMethods
9
+ end
10
+ end
11
+
12
+ #
13
+ # Class Methods
14
+ #
15
+ module ModelClassMethods
16
+ end # class methods
17
+
18
+ #
19
+ # Instance Methods
20
+ #
21
+ module ModelInstanceMethods
22
+ def remember_token?
23
+ (!remember_token.blank?) &&
24
+ remember_token_expires_at && (Time.now.utc < remember_token_expires_at.utc)
25
+ end
26
+
27
+ # These create and unset the fields required for remembering users between browser closes
28
+ def remember_me
29
+ remember_me_for 2.weeks
30
+ end
31
+
32
+ def remember_me_for(time)
33
+ remember_me_until time.from_now.utc
34
+ end
35
+
36
+ def remember_me_until(time)
37
+ self.remember_token_expires_at = time
38
+ self.remember_token = self.class.make_token
39
+ save(false)
40
+ end
41
+
42
+ # refresh token (keeping same expires_at) if it exists
43
+ def refresh_token
44
+ if remember_token?
45
+ self.remember_token = self.class.make_token
46
+ save(false)
47
+ end
48
+ end
49
+
50
+ #
51
+ # Deletes the server-side record of the authentication token. The
52
+ # client-side (browser cookie) and server-side (this remember_token) must
53
+ # always be deleted together.
54
+ #
55
+ def forget_me
56
+ self.remember_token_expires_at = nil
57
+ self.remember_token = nil
58
+ save(false)
59
+ end
60
+ end # instance methods
61
+ end
62
+
63
+ module ByCookieTokenController
64
+ # Stuff directives into including module
65
+ def self.included( recipient )
66
+ recipient.extend( ControllerClassMethods )
67
+ recipient.class_eval do
68
+ include ControllerInstanceMethods
69
+ end
70
+ end
71
+
72
+ #
73
+ # Class Methods
74
+ #
75
+ module ControllerClassMethods
76
+ end # class methods
77
+
78
+ module ControllerInstanceMethods
79
+ end # instance methods
80
+ end
81
+ end
82
+