Commit a3d9f0bbfdae40889c49c78c33044e2001012559
1 parent
4f51c61b
Exists in
master
and in
29 other branches
ActionItem9: adding user accounts model and controller, just generated acts_as_authenticated code
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@94 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
15 changed files
with
690 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,43 @@ |
1 | +class AccountController < ApplicationController | |
2 | + # Be sure to include AuthenticationSystem in Application Controller instead | |
3 | + include AuthenticatedSystem | |
4 | + # If you want "remember me" functionality, add this before_filter to Application Controller | |
5 | + before_filter :login_from_cookie | |
6 | + | |
7 | + # say something nice, you goof! something sweet. | |
8 | + def index | |
9 | + redirect_to(:action => 'signup') unless logged_in? || User.count > 0 | |
10 | + end | |
11 | + | |
12 | + def login | |
13 | + return unless request.post? | |
14 | + self.current_user = User.authenticate(params[:login], params[:password]) | |
15 | + if logged_in? | |
16 | + if params[:remember_me] == "1" | |
17 | + self.current_user.remember_me | |
18 | + cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at } | |
19 | + end | |
20 | + redirect_back_or_default(:controller => '/account', :action => 'index') | |
21 | + flash[:notice] = "Logged in successfully" | |
22 | + end | |
23 | + end | |
24 | + | |
25 | + def signup | |
26 | + @user = User.new(params[:user]) | |
27 | + return unless request.post? | |
28 | + @user.save! | |
29 | + self.current_user = @user | |
30 | + redirect_back_or_default(:controller => '/account', :action => 'index') | |
31 | + flash[:notice] = "Thanks for signing up!" | |
32 | + rescue ActiveRecord::RecordInvalid | |
33 | + render :action => 'signup' | |
34 | + end | |
35 | + | |
36 | + def logout | |
37 | + self.current_user.forget_me if logged_in? | |
38 | + cookies.delete :auth_token | |
39 | + reset_session | |
40 | + flash[:notice] = "You have been logged out." | |
41 | + redirect_back_or_default(:controller => '/account', :action => 'index') | |
42 | + end | |
43 | +end | ... | ... |
... | ... | @@ -0,0 +1,64 @@ |
1 | +require 'digest/sha1' | |
2 | +class User < ActiveRecord::Base | |
3 | + # Virtual attribute for the unencrypted password | |
4 | + attr_accessor :password | |
5 | + | |
6 | + validates_presence_of :login, :email | |
7 | + validates_presence_of :password, :if => :password_required? | |
8 | + validates_presence_of :password_confirmation, :if => :password_required? | |
9 | + validates_length_of :password, :within => 4..40, :if => :password_required? | |
10 | + validates_confirmation_of :password, :if => :password_required? | |
11 | + validates_length_of :login, :within => 3..40 | |
12 | + validates_length_of :email, :within => 3..100 | |
13 | + validates_uniqueness_of :login, :email, :case_sensitive => false | |
14 | + before_save :encrypt_password | |
15 | + | |
16 | + # Authenticates a user by their login name and unencrypted password. Returns the user or nil. | |
17 | + def self.authenticate(login, password) | |
18 | + u = find_by_login(login) # need to get the salt | |
19 | + u && u.authenticated?(password) ? u : nil | |
20 | + end | |
21 | + | |
22 | + # Encrypts some data with the salt. | |
23 | + def self.encrypt(password, salt) | |
24 | + Digest::SHA1.hexdigest("--#{salt}--#{password}--") | |
25 | + end | |
26 | + | |
27 | + # Encrypts the password with the user salt | |
28 | + def encrypt(password) | |
29 | + self.class.encrypt(password, salt) | |
30 | + end | |
31 | + | |
32 | + def authenticated?(password) | |
33 | + crypted_password == encrypt(password) | |
34 | + end | |
35 | + | |
36 | + def remember_token? | |
37 | + remember_token_expires_at && Time.now.utc < remember_token_expires_at | |
38 | + end | |
39 | + | |
40 | + # These create and unset the fields required for remembering users between browser closes | |
41 | + def remember_me | |
42 | + self.remember_token_expires_at = 2.weeks.from_now.utc | |
43 | + self.remember_token = encrypt("#{email}--#{remember_token_expires_at}") | |
44 | + save(false) | |
45 | + end | |
46 | + | |
47 | + def forget_me | |
48 | + self.remember_token_expires_at = nil | |
49 | + self.remember_token = nil | |
50 | + save(false) | |
51 | + end | |
52 | + | |
53 | + protected | |
54 | + # before filter | |
55 | + def encrypt_password | |
56 | + return if password.blank? | |
57 | + self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record? | |
58 | + self.crypted_password = encrypt(password) | |
59 | + end | |
60 | + | |
61 | + def password_required? | |
62 | + crypted_password.blank? || !password.blank? | |
63 | + end | |
64 | +end | ... | ... |
... | ... | @@ -0,0 +1,56 @@ |
1 | +<h1>In the Caboose.</h1> | |
2 | + | |
3 | +<% content_for 'poem' do -%> | |
4 | +"Train delayed? and what's to say?" | |
5 | +"Blocked by last night's snow they say." | |
6 | +Seven hours or so to wait; | |
7 | +Well, that's pleasant! but there's the freight. | |
8 | +Depot loafing no one fancies, | |
9 | +We'll try the caboose and take our chances. | |
10 | + | |
11 | +Cool this morning in Watertown, | |
12 | +Somewhat frosty___mercury down; | |
13 | +Enter caboose___roaring fire, | |
14 | +With never an air-hole; heat so dire | |
15 | +That we shrivel and pant; we are roasted through- | |
16 | +Outside, thermometer thirty-two. | |
17 | + | |
18 | +We start with a jerk and suddenly stop. | |
19 | +"What's broke?" says one; another "What's up?", | |
20 | +"Oh, nothing," they answer, "That's our way: | |
21 | +You must stand the jerking, sorry to say." | |
22 | +We "stand it" with oft this painful thought: | |
23 | +Are our heads on yet, or are they not? | |
24 | + | |
25 | +Comrades in misery___let me see; | |
26 | +Girl like a statue opposite me; | |
27 | +Back and forth the others jostle___ | |
28 | +She never winks, nor moves a muscle; | |
29 | +See her, as she sits there now; | |
30 | +She's "well balanced," anyhow. | |
31 | + | |
32 | +Woman in trouble, tearful eyes, | |
33 | +Sits by the window, softly cries, | |
34 | +Pity___for griefs we may not know, | |
35 | +For breasts that ache, for tears that flow, | |
36 | +Though we know not why. Her eyelids red | |
37 | +Tell a sorrowful tale___some hope is dead. | |
38 | + | |
39 | +Man who follows the Golden Rule, | |
40 | +And lends his papers___a pocket full, | |
41 | +Has a blank book___once in a minute | |
42 | +Has an idea, and writes it in it. | |
43 | +Guess him? Yes, of course I can, | |
44 | +He's a___well___a newspaper man. | |
45 | + | |
46 | +Blue-eyed fairy, wrapped in fur; | |
47 | +Sweet young mother tending her. | |
48 | +Fairy thinks it's "awful far," | |
49 | +Wants to get off this "naughty car." | |
50 | +So do we, young golden-hair; | |
51 | +All this crowd are with you there! | |
52 | +<% end -%> | |
53 | + | |
54 | +<%= simple_format @content_for_poem %> | |
55 | + | |
56 | +<p><a href="http://skyways.lib.ks.us/poetry/walls/caboose.html">-- Ellen P. Allerton.</a></p> | |
0 | 57 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +<% form_tag do -%> | |
2 | +<p><label for="login">Login</label><br/> | |
3 | +<%= text_field_tag 'login' %></p> | |
4 | + | |
5 | +<p><label for="password">Password</label><br/> | |
6 | +<%= password_field_tag 'password' %></p> | |
7 | + | |
8 | +<!-- Uncomment this if you want this functionality | |
9 | +<p><label for="remember_me">Remember me:</label> | |
10 | +<%= check_box_tag 'remember_me' %></p> | |
11 | +--> | |
12 | + | |
13 | +<p><%= submit_tag 'Log in' %></p> | |
14 | +<% end -%> | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +<%= error_messages_for :user %> | |
2 | +<% form_for :user do |f| -%> | |
3 | +<p><label for="login">Login</label><br/> | |
4 | +<%= f.text_field :login %></p> | |
5 | + | |
6 | +<p><label for="email">Email</label><br/> | |
7 | +<%= f.text_field :email %></p> | |
8 | + | |
9 | +<p><label for="password">Password</label><br/> | |
10 | +<%= f.password_field :password %></p> | |
11 | + | |
12 | +<p><label for="password_confirmation">Confirm Password</label><br/> | |
13 | +<%= f.password_field :password_confirmation %></p> | |
14 | + | |
15 | +<p><%= submit_tag 'Sign up' %></p> | |
16 | +<% end -%> | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +<html> | |
2 | + <head> | |
3 | + <%= javascript_include_tag :defaults %> | |
4 | + <%= javascript_include_tag_template @chosen_template %> | |
5 | + <%= stylesheet_link_tag_template @chosen_template %> | |
6 | + | |
7 | + </head> | |
8 | + <body> | |
9 | + | |
10 | + <div id='main'> | |
11 | + <%= yield %> | |
12 | + </div> | |
13 | + | |
14 | + </body> | |
15 | + | |
16 | +</html> | ... | ... |
config/routes.rb
... | ... | @@ -13,6 +13,9 @@ ActionController::Routing::Routes.draw do |map| |
13 | 13 | # -- just remember to delete public/index.html. |
14 | 14 | map.connect '', :controller => "home" |
15 | 15 | |
16 | + # user account controller | |
17 | + map.connect 'account/:action', :controller => 'account' | |
18 | + | |
16 | 19 | # administrative tasks for a virtual community |
17 | 20 | map.connect 'admin/:controller/:action/:id' |
18 | 21 | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +class CreateUsers < ActiveRecord::Migration | |
2 | + def self.up | |
3 | + create_table "users", :force => true do |t| | |
4 | + t.column :login, :string | |
5 | + t.column :email, :string | |
6 | + t.column :crypted_password, :string, :limit => 40 | |
7 | + t.column :salt, :string, :limit => 40 | |
8 | + t.column :created_at, :datetime | |
9 | + t.column :updated_at, :datetime | |
10 | + t.column :remember_token, :string | |
11 | + t.column :remember_token_expires_at, :datetime | |
12 | + end | |
13 | + end | |
14 | + | |
15 | + def self.down | |
16 | + drop_table "users" | |
17 | + end | |
18 | +end | ... | ... |
... | ... | @@ -0,0 +1,120 @@ |
1 | +module AuthenticatedSystem | |
2 | + protected | |
3 | + # Returns true or false if the user is logged in. | |
4 | + # Preloads @current_user with the user model if they're logged in. | |
5 | + def logged_in? | |
6 | + current_user != :false | |
7 | + end | |
8 | + | |
9 | + # Accesses the current user from the session. | |
10 | + def current_user | |
11 | + @current_user ||= (session[:user] && User.find_by_id(session[:user])) || :false | |
12 | + end | |
13 | + | |
14 | + # Store the given user in the session. | |
15 | + def current_user=(new_user) | |
16 | + session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id | |
17 | + @current_user = new_user | |
18 | + end | |
19 | + | |
20 | + # Check if the user is authorized. | |
21 | + # | |
22 | + # Override this method in your controllers if you want to restrict access | |
23 | + # to only a few actions or if you want to check if the user | |
24 | + # has the correct rights. | |
25 | + # | |
26 | + # Example: | |
27 | + # | |
28 | + # # only allow nonbobs | |
29 | + # def authorize? | |
30 | + # current_user.login != "bob" | |
31 | + # end | |
32 | + def authorized? | |
33 | + true | |
34 | + end | |
35 | + | |
36 | + # Filter method to enforce a login requirement. | |
37 | + # | |
38 | + # To require logins for all actions, use this in your controllers: | |
39 | + # | |
40 | + # before_filter :login_required | |
41 | + # | |
42 | + # To require logins for specific actions, use this in your controllers: | |
43 | + # | |
44 | + # before_filter :login_required, :only => [ :edit, :update ] | |
45 | + # | |
46 | + # To skip this in a subclassed controller: | |
47 | + # | |
48 | + # skip_before_filter :login_required | |
49 | + # | |
50 | + def login_required | |
51 | + username, passwd = get_auth_data | |
52 | + self.current_user ||= User.authenticate(username, passwd) || :false if username && passwd | |
53 | + logged_in? && authorized? ? true : access_denied | |
54 | + end | |
55 | + | |
56 | + # Redirect as appropriate when an access request fails. | |
57 | + # | |
58 | + # The default action is to redirect to the login screen. | |
59 | + # | |
60 | + # Override this method in your controllers if you want to have special | |
61 | + # behavior in case the user is not authorized | |
62 | + # to access the requested action. For example, a popup window might | |
63 | + # simply close itself. | |
64 | + def access_denied | |
65 | + respond_to do |accepts| | |
66 | + accepts.html do | |
67 | + store_location | |
68 | + redirect_to :controller => '/account', :action => 'login' | |
69 | + end | |
70 | + accepts.xml do | |
71 | + headers["Status"] = "Unauthorized" | |
72 | + headers["WWW-Authenticate"] = %(Basic realm="Web Password") | |
73 | + render :text => "Could't authenticate you", :status => '401 Unauthorized' | |
74 | + end | |
75 | + end | |
76 | + false | |
77 | + end | |
78 | + | |
79 | + # Store the URI of the current request in the session. | |
80 | + # | |
81 | + # We can return to this location by calling #redirect_back_or_default. | |
82 | + def store_location | |
83 | + session[:return_to] = request.request_uri | |
84 | + end | |
85 | + | |
86 | + # Redirect to the URI stored by the most recent store_location call or | |
87 | + # to the passed default. | |
88 | + def redirect_back_or_default(default) | |
89 | + session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default) | |
90 | + session[:return_to] = nil | |
91 | + end | |
92 | + | |
93 | + # Inclusion hook to make #current_user and #logged_in? | |
94 | + # available as ActionView helper methods. | |
95 | + def self.included(base) | |
96 | + base.send :helper_method, :current_user, :logged_in? | |
97 | + end | |
98 | + | |
99 | + # When called with before_filter :login_from_cookie will check for an :auth_token | |
100 | + # cookie and log the user back in if apropriate | |
101 | + def login_from_cookie | |
102 | + return unless cookies[:auth_token] && !logged_in? | |
103 | + user = User.find_by_remember_token(cookies[:auth_token]) | |
104 | + if user && user.remember_token? | |
105 | + user.remember_me | |
106 | + self.current_user = user | |
107 | + cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at } | |
108 | + flash[:notice] = "Logged in successfully" | |
109 | + end | |
110 | + end | |
111 | + | |
112 | + private | |
113 | + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization) | |
114 | + # gets BASIC auth info | |
115 | + def get_auth_data | |
116 | + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) } | |
117 | + auth_data = request.env[auth_key].to_s.split unless auth_key.blank? | |
118 | + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil] | |
119 | + end | |
120 | +end | ... | ... |
... | ... | @@ -0,0 +1,113 @@ |
1 | +module AuthenticatedTestHelper | |
2 | + # Sets the current user in the session from the user fixtures. | |
3 | + def login_as(user) | |
4 | + @request.session[:user] = user ? users(user).id : nil | |
5 | + end | |
6 | + | |
7 | + def content_type(type) | |
8 | + @request.env['Content-Type'] = type | |
9 | + end | |
10 | + | |
11 | + def accept(accept) | |
12 | + @request.env["HTTP_ACCEPT"] = accept | |
13 | + end | |
14 | + | |
15 | + def authorize_as(user) | |
16 | + if user | |
17 | + @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}" | |
18 | + accept 'application/xml' | |
19 | + content_type 'application/xml' | |
20 | + else | |
21 | + @request.env["HTTP_AUTHORIZATION"] = nil | |
22 | + accept nil | |
23 | + content_type nil | |
24 | + end | |
25 | + end | |
26 | + | |
27 | + # http://project.ioni.st/post/217#post-217 | |
28 | + # | |
29 | + # def test_new_publication | |
30 | + # assert_difference(Publication, :count) do | |
31 | + # post :create, :publication => {...} | |
32 | + # # ... | |
33 | + # end | |
34 | + # end | |
35 | + # | |
36 | + def assert_difference(object, method = nil, difference = 1) | |
37 | + initial_value = object.send(method) | |
38 | + yield | |
39 | + assert_equal initial_value + difference, object.send(method), "#{object}##{method}" | |
40 | + end | |
41 | + | |
42 | + def assert_no_difference(object, method, &block) | |
43 | + assert_difference object, method, 0, &block | |
44 | + end | |
45 | + | |
46 | + # Assert the block redirects to the login | |
47 | + # | |
48 | + # assert_requires_login(:bob) { |c| c.get :edit, :id => 1 } | |
49 | + # | |
50 | + def assert_requires_login(login = nil) | |
51 | + yield HttpLoginProxy.new(self, login) | |
52 | + end | |
53 | + | |
54 | + def assert_http_authentication_required(login = nil) | |
55 | + yield XmlLoginProxy.new(self, login) | |
56 | + end | |
57 | + | |
58 | + def reset!(*instance_vars) | |
59 | + instance_vars = [:controller, :request, :response] unless instance_vars.any? | |
60 | + instance_vars.collect! { |v| "@#{v}".to_sym } | |
61 | + instance_vars.each do |var| | |
62 | + instance_variable_set(var, instance_variable_get(var).class.new) | |
63 | + end | |
64 | + end | |
65 | +end | |
66 | + | |
67 | +class BaseLoginProxy | |
68 | + attr_reader :controller | |
69 | + attr_reader :options | |
70 | + def initialize(controller, login) | |
71 | + @controller = controller | |
72 | + @login = login | |
73 | + end | |
74 | + | |
75 | + private | |
76 | + def authenticated | |
77 | + raise NotImplementedError | |
78 | + end | |
79 | + | |
80 | + def check | |
81 | + raise NotImplementedError | |
82 | + end | |
83 | + | |
84 | + def method_missing(method, *args) | |
85 | + @controller.reset! | |
86 | + authenticate | |
87 | + @controller.send(method, *args) | |
88 | + check | |
89 | + end | |
90 | +end | |
91 | + | |
92 | +class HttpLoginProxy < BaseLoginProxy | |
93 | + protected | |
94 | + def authenticate | |
95 | + @controller.login_as @login if @login | |
96 | + end | |
97 | + | |
98 | + def check | |
99 | + @controller.assert_redirected_to :controller => 'account', :action => 'login' | |
100 | + end | |
101 | +end | |
102 | + | |
103 | +class XmlLoginProxy < BaseLoginProxy | |
104 | + protected | |
105 | + def authenticate | |
106 | + @controller.accept 'application/xml' | |
107 | + @controller.authorize_as @login if @login | |
108 | + end | |
109 | + | |
110 | + def check | |
111 | + @controller.assert_response 401 | |
112 | + end | |
113 | +end | |
0 | 114 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,17 @@ |
1 | +quentin: | |
2 | + id: 1 | |
3 | + login: quentin | |
4 | + email: quentin@example.com | |
5 | + salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd | |
6 | + crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test | |
7 | + #crypted_password: "ce2/iFrNtQ8=\n" # quentin, use only if you're using 2-way encryption | |
8 | + created_at: <%= 5.days.ago.to_s :db %> | |
9 | + # activated_at: <%= 5.days.ago.to_s :db %> # only if you're activating new signups | |
10 | +aaron: | |
11 | + id: 2 | |
12 | + login: aaron | |
13 | + email: aaron@example.com | |
14 | + salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd | |
15 | + crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test | |
16 | + # activation_code: aaronscode # only if you're activating new signups | |
17 | + created_at: <%= 1.days.ago.to_s :db %> | |
0 | 18 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,129 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | +require 'account_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class AccountController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class AccountControllerTest < Test::Unit::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 :users | |
13 | + | |
14 | + def setup | |
15 | + @controller = AccountController.new | |
16 | + @request = ActionController::TestRequest.new | |
17 | + @response = ActionController::TestResponse.new | |
18 | + end | |
19 | + | |
20 | + def test_should_login_and_redirect | |
21 | + post :login, :login => 'quentin', :password => 'test' | |
22 | + assert session[:user] | |
23 | + assert_response :redirect | |
24 | + end | |
25 | + | |
26 | + def test_should_fail_login_and_not_redirect | |
27 | + post :login, :login => 'quentin', :password => 'bad password' | |
28 | + assert_nil session[:user] | |
29 | + assert_response :success | |
30 | + end | |
31 | + | |
32 | + def test_should_allow_signup | |
33 | + assert_difference User, :count do | |
34 | + create_user | |
35 | + assert_response :redirect | |
36 | + end | |
37 | + end | |
38 | + | |
39 | + def test_should_require_login_on_signup | |
40 | + assert_no_difference User, :count do | |
41 | + create_user(:login => nil) | |
42 | + assert assigns(:user).errors.on(:login) | |
43 | + assert_response :success | |
44 | + end | |
45 | + end | |
46 | + | |
47 | + def test_should_require_password_on_signup | |
48 | + assert_no_difference User, :count do | |
49 | + create_user(:password => nil) | |
50 | + assert assigns(:user).errors.on(:password) | |
51 | + assert_response :success | |
52 | + end | |
53 | + end | |
54 | + | |
55 | + def test_should_require_password_confirmation_on_signup | |
56 | + assert_no_difference User, :count do | |
57 | + create_user(:password_confirmation => nil) | |
58 | + assert assigns(:user).errors.on(:password_confirmation) | |
59 | + assert_response :success | |
60 | + end | |
61 | + end | |
62 | + | |
63 | + def test_should_require_email_on_signup | |
64 | + assert_no_difference User, :count do | |
65 | + create_user(:email => nil) | |
66 | + assert assigns(:user).errors.on(:email) | |
67 | + assert_response :success | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + def test_should_logout | |
72 | + login_as :quentin | |
73 | + get :logout | |
74 | + assert_nil session[:user] | |
75 | + assert_response :redirect | |
76 | + end | |
77 | + | |
78 | + def test_should_remember_me | |
79 | + post :login, :login => 'quentin', :password => 'test', :remember_me => "1" | |
80 | + assert_not_nil @response.cookies["auth_token"] | |
81 | + end | |
82 | + | |
83 | + def test_should_not_remember_me | |
84 | + post :login, :login => 'quentin', :password => 'test', :remember_me => "0" | |
85 | + assert_nil @response.cookies["auth_token"] | |
86 | + end | |
87 | + | |
88 | + def test_should_delete_token_on_logout | |
89 | + login_as :quentin | |
90 | + get :logout | |
91 | + assert_equal @response.cookies["auth_token"], [] | |
92 | + end | |
93 | + | |
94 | + def test_should_login_with_cookie | |
95 | + users(:quentin).remember_me | |
96 | + @request.cookies["auth_token"] = cookie_for(:quentin) | |
97 | + get :index | |
98 | + assert @controller.send(:logged_in?) | |
99 | + end | |
100 | + | |
101 | + def test_should_fail_expired_cookie_login | |
102 | + users(:quentin).remember_me | |
103 | + users(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago | |
104 | + @request.cookies["auth_token"] = cookie_for(:quentin) | |
105 | + get :index | |
106 | + assert !@controller.send(:logged_in?) | |
107 | + end | |
108 | + | |
109 | + def test_should_fail_cookie_login | |
110 | + users(:quentin).remember_me | |
111 | + @request.cookies["auth_token"] = auth_token('invalid_auth_token') | |
112 | + get :index | |
113 | + assert !@controller.send(:logged_in?) | |
114 | + end | |
115 | + | |
116 | + protected | |
117 | + def create_user(options = {}) | |
118 | + post :signup, :user => { :login => 'quire', :email => 'quire@example.com', | |
119 | + :password => 'quire', :password_confirmation => 'quire' }.merge(options) | |
120 | + end | |
121 | + | |
122 | + def auth_token(token) | |
123 | + CGI::Cookie.new('name' => 'auth_token', 'value' => token) | |
124 | + end | |
125 | + | |
126 | + def cookie_for(user) | |
127 | + auth_token users(user).remember_token | |
128 | + end | |
129 | +end | ... | ... |
test/integration/routing_test.rb
... | ... | @@ -6,4 +6,8 @@ class RoutingTest < ActionController::IntegrationTest |
6 | 6 | assert_routing('admin/features', :controller => 'features', :action => 'index') |
7 | 7 | end |
8 | 8 | |
9 | + def test_account_controller | |
10 | + assert_routing('account', :controller => 'account', :action => 'index') | |
11 | + end | |
12 | + | |
9 | 13 | end | ... | ... |
... | ... | @@ -0,0 +1,75 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class UserTest < Test::Unit::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 :users | |
8 | + | |
9 | + def test_should_create_user | |
10 | + assert_difference User, :count do | |
11 | + user = create_user | |
12 | + assert !user.new_record?, "#{user.errors.full_messages.to_sentence}" | |
13 | + end | |
14 | + end | |
15 | + | |
16 | + def test_should_require_login | |
17 | + assert_no_difference User, :count do | |
18 | + u = create_user(:login => nil) | |
19 | + assert u.errors.on(:login) | |
20 | + end | |
21 | + end | |
22 | + | |
23 | + def test_should_require_password | |
24 | + assert_no_difference User, :count do | |
25 | + u = create_user(:password => nil) | |
26 | + assert u.errors.on(:password) | |
27 | + end | |
28 | + end | |
29 | + | |
30 | + def test_should_require_password_confirmation | |
31 | + assert_no_difference User, :count do | |
32 | + u = create_user(:password_confirmation => nil) | |
33 | + assert u.errors.on(:password_confirmation) | |
34 | + end | |
35 | + end | |
36 | + | |
37 | + def test_should_require_email | |
38 | + assert_no_difference User, :count do | |
39 | + u = create_user(:email => nil) | |
40 | + assert u.errors.on(:email) | |
41 | + end | |
42 | + end | |
43 | + | |
44 | + def test_should_reset_password | |
45 | + users(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password') | |
46 | + assert_equal users(:quentin), User.authenticate('quentin', 'new password') | |
47 | + end | |
48 | + | |
49 | + def test_should_not_rehash_password | |
50 | + users(:quentin).update_attributes(:login => 'quentin2') | |
51 | + assert_equal users(:quentin), User.authenticate('quentin2', 'test') | |
52 | + end | |
53 | + | |
54 | + def test_should_authenticate_user | |
55 | + assert_equal users(:quentin), User.authenticate('quentin', 'test') | |
56 | + end | |
57 | + | |
58 | + def test_should_set_remember_token | |
59 | + users(:quentin).remember_me | |
60 | + assert_not_nil users(:quentin).remember_token | |
61 | + assert_not_nil users(:quentin).remember_token_expires_at | |
62 | + end | |
63 | + | |
64 | + def test_should_unset_remember_token | |
65 | + users(:quentin).remember_me | |
66 | + assert_not_nil users(:quentin).remember_token | |
67 | + users(:quentin).forget_me | |
68 | + assert_nil users(:quentin).remember_token | |
69 | + end | |
70 | + | |
71 | + protected | |
72 | + def create_user(options = {}) | |
73 | + User.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) | |
74 | + end | |
75 | +end | ... | ... |