# HG changeset patch # User moriq@moriq.com # Date 1204657074 -32400 # Node ID ac1024130232e51313f4944fff0e5ccb53f41225 # Parent ebbe8fce79fbe1c5d1c77cc826fc942f38b096db generate authenticated again. mercurial import したときに db/migrate lib が消えてた。orz --- a/app/controllers/application.rb Wed Mar 05 03:47:33 2008 +0900 +++ b/app/controllers/application.rb Wed Mar 05 03:57:54 2008 +0900 @@ -4,6 +4,10 @@ class ApplicationController < ActionCont class ApplicationController < ActionController::Base helper :all # include all helpers, all the time + # Be sure to include AuthenticationSystem in Application Controller instead + include AuthenticatedSystem + + # See ActionController::RequestForgeryProtection for details # Uncomment the :secret if you're not using the cookie session store protect_from_forgery # :secret => '34d199dc292649987bc6a6f4e7174608' --- a/app/controllers/users_controller.rb Wed Mar 05 03:47:33 2008 +0900 +++ b/app/controllers/users_controller.rb Wed Mar 05 03:57:54 2008 +0900 @@ -1,8 +1,4 @@ class UsersController < ApplicationContr class UsersController < ApplicationController - # Be sure to include AuthenticationSystem in Application Controller instead - include AuthenticatedSystem - - # render new.rhtml def new end --- a/app/views/home/index.html.erb Wed Mar 05 03:47:33 2008 +0900 +++ b/app/views/home/index.html.erb Wed Mar 05 03:57:54 2008 +0900 @@ -1,2 +1,12 @@ -

Home#index

-

Find me in app/views/home/index.html.erb

+

Welcome

+ +<%-if logged_in?-%> +

You are logged in as <%=h current_user.login %>.

+

<%= link_to 'Logout', session_path, :method => :delete %>

+<%-else-%> +

You are currently not logged in.

+

+ <%= link_to 'Login', new_session_path %> or + <%= link_to 'Sign Up', new_user_path %> +

+<%-end-%> --- a/config/routes.rb Wed Mar 05 03:47:33 2008 +0900 +++ b/config/routes.rb Wed Mar 05 03:57:54 2008 +0900 @@ -1,4 +1,16 @@ ActionController::Routing::Routes.draw d ActionController::Routing::Routes.draw do |map| + map.resources :users + + map.resource :session + + map.resources :users + + map.resource :session + + map.resources :users + + map.resource :session + # The priority is based upon order of creation: first created -> highest priority. # Sample of regular route: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/helpers/sessions_helper.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,2 @@ +module SessionsHelper +end \ No newline at end of file --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/helpers/users_helper.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,2 @@ +module UsersHelper +end \ No newline at end of file --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/sessions/new.html.erb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,14 @@ +<% form_tag session_path do -%> +


+<%= text_field_tag 'login' %>

+ +


+<%= password_field_tag 'password' %>

+ + + +

<%= submit_tag 'Log in' %>

+<% end -%> --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/users/new.html.erb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,16 @@ +<%= error_messages_for :user %> +<% form_for :user, :url => users_path do |f| -%> +


+<%= f.text_field :login %>

+ +


+<%= f.text_field :email %>

+ +


+<%= f.password_field :password %>

+ +


+<%= f.password_field :password_confirmation %>

+ +

<%= submit_tag 'Sign up' %>

+<% end -%> --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/001_create_users.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,20 @@ +class CreateUsers < ActiveRecord::Migration + def self.up + create_table "users", :force => true do |t| + t.column :login, :string + t.column :email, :string + t.column :crypted_password, :string, :limit => 40 + t.column :salt, :string, :limit => 40 + t.column :created_at, :datetime + t.column :updated_at, :datetime + t.column :remember_token, :string + t.column :remember_token_expires_at, :datetime + + + end + end + + def self.down + drop_table "users" + end +end --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/authenticated_system.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,116 @@ +module AuthenticatedSystem + protected + # Returns true or false if the user is logged in. + # Preloads @current_user with the user model if they're logged in. + def logged_in? + current_user != :false + end + + # Accesses the current user from the session. Set it to :false if login fails + # so that future calls do not hit the database. + def current_user + @current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false) + end + + # Store the given user id in the session. + def current_user=(new_user) + session[:user_id] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id + @current_user = new_user || :false + end + + # Check if the user is authorized + # + # Override this method in your controllers if you want to restrict access + # to only a few actions or if you want to check if the user + # has the correct rights. + # + # Example: + # + # # only allow nonbobs + # def authorized? + # current_user.login != "bob" + # end + def authorized? + logged_in? + end + + # Filter method to enforce a login requirement. + # + # To require logins for all actions, use this in your controllers: + # + # before_filter :login_required + # + # To require logins for specific actions, use this in your controllers: + # + # before_filter :login_required, :only => [ :edit, :update ] + # + # To skip this in a subclassed controller: + # + # skip_before_filter :login_required + # + def login_required + authorized? || access_denied + end + + # Redirect as appropriate when an access request fails. + # + # The default action is to redirect to the login screen. + # + # Override this method in your controllers if you want to have special + # behavior in case the user is not authorized + # to access the requested action. For example, a popup window might + # simply close itself. + def access_denied + respond_to do |format| + format.html do + store_location + redirect_to new_session_path + end + format.any do + request_http_basic_authentication 'Web Password' + end + end + end + + # Store the URI of the current request in the session. + # + # We can return to this location by calling #redirect_back_or_default. + def store_location + session[:return_to] = request.request_uri + end + + # Redirect to the URI stored by the most recent store_location call or + # to the passed default. + def redirect_back_or_default(default) + redirect_to(session[:return_to] || default) + session[:return_to] = nil + end + + # Inclusion hook to make #current_user and #logged_in? + # available as ActionView helper methods. + def self.included(base) + base.send :helper_method, :current_user, :logged_in? + end + + # Called from #current_user. First attempt to login by the user id stored in the session. + def login_from_session + self.current_user = User.find_by_id(session[:user_id]) if session[:user_id] + end + + # Called from #current_user. Now, attempt to login by basic authentication information. + def login_from_basic_auth + authenticate_with_http_basic do |username, password| + self.current_user = User.authenticate(username, password) + end + end + + # Called from #current_user. Finaly, attempt to login by an expiring token in the cookie. + def login_from_cookie + user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token]) + if user && user.remember_token? + user.remember_me + cookies[:auth_token] = { :value => user.remember_token, :expires => user.remember_token_expires_at } + self.current_user = user + end + end +end --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/authenticated_test_helper.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,10 @@ +module AuthenticatedTestHelper + # Sets the current user in the session from the user fixtures. + def login_as(user) + @request.session[:user_id] = user ? users(user).id : nil + end + + def authorize_as(user) + @request.env["HTTP_AUTHORIZATION"] = user ? ActionController::HttpAuthentication::Basic.encode_credentials(users(user).login, 'test') : nil + end +end --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fixtures/users.yml Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,19 @@ +quentin: + id: 1 + login: quentin + email: quentin@example.com + salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd + crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test + created_at: <%= 5.days.ago.to_s :db %> + + + +aaron: + id: 2 + login: aaron + email: aaron@example.com + salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd + crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test + created_at: <%= 1.days.ago.to_s :db %> + + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/functional/sessions_controller_test.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,85 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'sessions_controller' + +# Re-raise errors caught by the controller. +class SessionsController; def rescue_action(e) raise e end; end + +class SessionsControllerTest < Test::Unit::TestCase + # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead + # Then, you can remove it from this and the units test. + include AuthenticatedTestHelper + + fixtures :users + + def setup + @controller = SessionsController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_should_login_and_redirect + post :create, :login => 'quentin', :password => 'test' + assert session[:user_id] + assert_response :redirect + end + + def test_should_fail_login_and_not_redirect + post :create, :login => 'quentin', :password => 'bad password' + assert_nil session[:user_id] + assert_response :success + end + + def test_should_logout + login_as :quentin + get :destroy + assert_nil session[:user_id] + assert_response :redirect + end + + def test_should_remember_me + post :create, :login => 'quentin', :password => 'test', :remember_me => "1" + assert_not_nil @response.cookies["auth_token"] + end + + def test_should_not_remember_me + post :create, :login => 'quentin', :password => 'test', :remember_me => "0" + assert_nil @response.cookies["auth_token"] + end + + def test_should_delete_token_on_logout + login_as :quentin + get :destroy + assert_equal @response.cookies["auth_token"], [] + end + + def test_should_login_with_cookie + users(:quentin).remember_me + @request.cookies["auth_token"] = cookie_for(:quentin) + get :new + assert @controller.send(:logged_in?) + end + + def test_should_fail_expired_cookie_login + users(:quentin).remember_me + users(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago + @request.cookies["auth_token"] = cookie_for(:quentin) + get :new + assert !@controller.send(:logged_in?) + end + + def test_should_fail_cookie_login + users(:quentin).remember_me + @request.cookies["auth_token"] = auth_token('invalid_auth_token') + get :new + assert !@controller.send(:logged_in?) + end + + protected + def auth_token(token) + CGI::Cookie.new('name' => 'auth_token', 'value' => token) + end + + def cookie_for(user) + auth_token users(user).remember_token + end +end --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/functional/users_controller_test.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,65 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'users_controller' + +# Re-raise errors caught by the controller. +class UsersController; def rescue_action(e) raise e end; end + +class UsersControllerTest < Test::Unit::TestCase + # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead + # Then, you can remove it from this and the units test. + include AuthenticatedTestHelper + + fixtures :users + + def setup + @controller = UsersController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_should_allow_signup + assert_difference 'User.count' do + create_user + assert_response :redirect + end + end + + def test_should_require_login_on_signup + assert_no_difference 'User.count' do + create_user(:login => nil) + assert assigns(:user).errors.on(:login) + assert_response :success + end + end + + def test_should_require_password_on_signup + assert_no_difference 'User.count' do + create_user(:password => nil) + assert assigns(:user).errors.on(:password) + assert_response :success + end + end + + def test_should_require_password_confirmation_on_signup + assert_no_difference 'User.count' do + create_user(:password_confirmation => nil) + assert assigns(:user).errors.on(:password_confirmation) + assert_response :success + end + end + + def test_should_require_email_on_signup + assert_no_difference 'User.count' do + create_user(:email => nil) + assert assigns(:user).errors.on(:email) + assert_response :success + end + end + + + protected + def create_user(options = {}) + post :create, :user => { :login => 'quire', :email => 'quire@example.com', + :password => 'quire', :password_confirmation => 'quire' }.merge(options) + end +end --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unit/user_test.rb Wed Mar 05 03:57:54 2008 +0900 @@ -0,0 +1,101 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class UserTest < Test::Unit::TestCase + # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead. + # Then, you can remove it from this and the functional test. + include AuthenticatedTestHelper + fixtures :users + + def test_should_create_user + assert_difference 'User.count' do + user = create_user + assert !user.new_record?, "#{user.errors.full_messages.to_sentence}" + end + end + + def test_should_require_login + assert_no_difference 'User.count' do + u = create_user(:login => nil) + assert u.errors.on(:login) + end + end + + def test_should_require_password + assert_no_difference 'User.count' do + u = create_user(:password => nil) + assert u.errors.on(:password) + end + end + + def test_should_require_password_confirmation + assert_no_difference 'User.count' do + u = create_user(:password_confirmation => nil) + assert u.errors.on(:password_confirmation) + end + end + + def test_should_require_email + assert_no_difference 'User.count' do + u = create_user(:email => nil) + assert u.errors.on(:email) + end + end + + def test_should_reset_password + users(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password') + assert_equal users(:quentin), User.authenticate('quentin', 'new password') + end + + def test_should_not_rehash_password + users(:quentin).update_attributes(:login => 'quentin2') + assert_equal users(:quentin), User.authenticate('quentin2', 'test') + end + + def test_should_authenticate_user + assert_equal users(:quentin), User.authenticate('quentin', 'test') + end + + def test_should_set_remember_token + users(:quentin).remember_me + assert_not_nil users(:quentin).remember_token + assert_not_nil users(:quentin).remember_token_expires_at + end + + def test_should_unset_remember_token + users(:quentin).remember_me + assert_not_nil users(:quentin).remember_token + users(:quentin).forget_me + assert_nil users(:quentin).remember_token + end + + def test_should_remember_me_for_one_week + before = 1.week.from_now.utc + users(:quentin).remember_me_for 1.week + after = 1.week.from_now.utc + assert_not_nil users(:quentin).remember_token + assert_not_nil users(:quentin).remember_token_expires_at + assert users(:quentin).remember_token_expires_at.between?(before, after) + end + + def test_should_remember_me_until_one_week + time = 1.week.from_now.utc + users(:quentin).remember_me_until time + assert_not_nil users(:quentin).remember_token + assert_not_nil users(:quentin).remember_token_expires_at + assert_equal users(:quentin).remember_token_expires_at, time + end + + def test_should_remember_me_default_two_weeks + before = 2.weeks.from_now.utc + users(:quentin).remember_me + after = 2.weeks.from_now.utc + assert_not_nil users(:quentin).remember_token + assert_not_nil users(:quentin).remember_token_expires_at + assert users(:quentin).remember_token_expires_at.between?(before, after) + end + +protected + def create_user(options = {}) + User.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) + end +end