Compare commits

...

26 Commits

Author SHA1 Message Date
José Valim
9f763d082a Bump version. 2011-04-29 14:13:35 +02:00
José Valim
c62915e2bd Refactor. 2011-04-29 14:12:29 +02:00
Emanuel Carnevale
6153a52e2d fix for issues #999: HTTP_ACCEPT=*/* should redirect to the default location 2011-04-29 14:12:22 +02:00
José Valim
39b59142ea Update CHANGELOG. 2011-04-21 19:19:35 +02:00
José Valim
624fb566fb Mark the token as expired, because invalid gives no clue of what to do next. 2011-04-21 19:18:40 +02:00
José Valim
76edb49c9d Release 1.3.2. 2011-04-21 13:59:40 +02:00
José Valim
b7d86ac014 Add tests to previous commit.
Conflicts:

	Gemfile.lock
2011-04-21 13:57:09 +02:00
Alexander Dreher
7097189de1 Fixes error on missing reset_password_sent_at column.
If the column is not present, you are unabled to reset your password.
2011-04-21 13:56:29 +02:00
José Valim
c4e451b896 Merge branch 'master' into v1.3
Conflicts:
	test/integration/authenticatable_test.rb
2011-04-19 10:41:17 +02:00
José Valim
b6cf1df659 Release 1.3.1. 2011-04-19 10:39:56 +02:00
José Valim
29afe2d21c Other minor improvements in the REST code. 2011-04-19 08:36:52 +02:00
José Valim
a722c6236c to_json does not guarantee the order. 2011-04-18 13:03:31 +02:00
José Valim
fd6ba32812 to_json does not guarantee the order. 2011-04-18 13:03:22 +02:00
José Valim
14aedc416a Wording. 2011-04-18 10:03:39 +02:00
José Valim
9fe0cb4954 use @example.com 2011-04-18 10:00:00 +02:00
SixArm
2a5669967f Change test email addresses to ues RFC 2606 reserved domain example.com 2011-04-18 15:59:13 +08:00
durrantm
13376d22de Edited README.rdoc via GitHub 2011-04-18 15:58:40 +08:00
José Valim
6b21531916 Update CHANGELOG. 2011-04-18 09:56:44 +02:00
José Valim
4a4dcb30ef sessions/new also responds to xml and json now 2011-04-18 09:56:24 +02:00
José Valim
2cfa58b433 Create shared_helpers for duplicated logic in Devise controllers and failure app. 2011-04-17 19:43:54 +02:00
José Valim
c6dd846718 Move the catch to the test level. 2011-04-17 19:37:19 +02:00
José Valim
7a2d76d002 Update changelog, improve coverage. 2011-04-17 18:06:29 +02:00
Andre Arko
e5a8febe3b Change the XML error tag from <hash> to <errors>. Makes way more sense. 2011-04-17 23:54:06 +08:00
Steve Hodgkiss
60809719b8 Fix bug when the reset_password_sent_at field doesn't exist generate_password_token returns nil causing the token not to be saved. 2011-04-17 23:53:50 +08:00
José Valim
ee6a8ab93a Temporarily remove rubinius because C extensions are not compiling. 2011-04-17 00:57:02 -07:00
Juan M. Cuello
c3d92095f8 Update README.
Replace success/failure with notice/alert in I18n section.
2011-04-17 15:15:49 +08:00
27 changed files with 225 additions and 123 deletions

View File

@@ -3,5 +3,4 @@ rvm:
- 1.8.7
- 1.9.2
- ree
- rbx
- jruby

View File

@@ -1,3 +1,27 @@
== 1.3.4
* bug fix
* Do not add formats if html or "*/*"
== 1.3.3
* bug fix
* Explicitly mark the token as expired if so
== 1.3.2
* bug fix
* Fix another regression related to reset_password_sent_at (by github.com/alexdreher)
== 1.3.1
* enhancements
* Improve failure_app responses (by github.com/indirect)
* sessions/new and registrations/new also respond to xml and json now
* bug fix
* Fix a regression that occurred if reset_password_sent_at is not present (by github.com/stevehodgkiss)
== 1.3.0
* enhancements

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
devise (1.3.0.dev)
devise (1.3.1)
bcrypt-ruby (~> 2.1.2)
orm_adapter (~> 0.0.3)
warden (~> 1.0.3)

View File

@@ -86,7 +86,7 @@ The generator will install an initializer which describes ALL Devise's configura
rails generate devise MODEL
Replace MODEL by the class name you want to add devise, like User, Admin, etc. This will create a model (if one does not exist) and configure it with default Devise modules. The generator will also create a migration file (if your ORM support them) and configure your routes. Continue reading this file to understand exactly what the generator produces and how to use it.
Replace MODEL by the class name used for the applications users, it's frequently 'User' but could also be 'Admin'. This will create a model (if one does not exist) and configure it with default Devise modules. Next, you'll usually run db:migrate as the generator will have created a migration file (if your ORM supports them). This generator also configures your config/routes.rb file, continue reading this file to understand exactly what the generator produces and how to use it.
Support for Rails 2.3.x can be found by installing Devise 1.0.x from the v1.0 branch.
@@ -259,7 +259,7 @@ Feel free to choose the one you prefer!
=== I18n
Devise uses flash messages with I18n with the flash keys :success and :failure. To customize your app, you can set up your locale file:
Devise uses flash messages with I18n with the flash keys :notice and :alert. To customize your app, you can set up your locale file:
en:
devise:

View File

@@ -5,8 +5,8 @@ class Devise::RegistrationsController < ApplicationController
# GET /resource/sign_up
def new
build_resource({})
render_with_scope :new
resource = build_resource({})
respond_with_navigational(resource){ render_with_scope :new }
end
# POST /resource

View File

@@ -4,24 +4,14 @@ class Devise::SessionsController < ApplicationController
# GET /resource/sign_in
def new
clean_up_passwords(build_resource)
render_with_scope :new
resource = build_resource
clean_up_passwords(resource)
respond_with_navigational(resource, stub_options(resource)){ render_with_scope :new }
end
# POST /resource/sign_in
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
# In the running app, the previous line would actually cause this method to
# exit by throwing `:warden` if the authentication failed. Unfortunately,
# this doesn't happen in the Rails test environment if you have included the
# Devise::TestHelpers (see `Devise::TestHelpers::TestWarden#authenticate!`),
# which makes it difficult to unit test extensions to this controller. Since
# the resource is nil if authentication fails, just short-circuit the method
# in that case. This should not affect the running app.
return if resource.nil?
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
@@ -44,4 +34,12 @@ class Devise::SessionsController < ApplicationController
end
end
end
end
protected
def stub_options(resource)
array = resource_class.authentication_keys.dup
array << :password if resource.respond_to?(:password)
{ :methods => array, :only => [:password] }
end
end

View File

@@ -3,6 +3,7 @@
en:
errors:
messages:
expired: "has expired, please request a new one"
not_found: "not found"
already_confirmed: "was already confirmed, please try signing in"
not_locked: "was not locked"

View File

@@ -16,6 +16,7 @@ module Devise
autoload :InternalHelpers, 'devise/controllers/internal_helpers'
autoload :Rememberable, 'devise/controllers/rememberable'
autoload :ScopedViews, 'devise/controllers/scoped_views'
autoload :SharedHelpers, 'devise/controllers/shared_helpers'
autoload :UrlHelpers, 'devise/controllers/url_helpers'
end

View File

@@ -6,19 +6,7 @@ module Devise
module InternalHelpers #:nodoc:
extend ActiveSupport::Concern
include Devise::Controllers::ScopedViews
MIME_REFERENCES = Mime::HTML.respond_to?(:ref)
# Helper used by FailureApp and Devise controllers to retrieve proper formats.
def self.request_format(request)
if request.format.respond_to?(:ref)
request.format.ref
elsif MIME_REFERENCES
request.format
elsif request.format # Rails < 3.0.4
request.format.to_sym
end
end
include Devise::Controllers::SharedHelpers
included do
helper DeviseHelper
@@ -65,10 +53,6 @@ module Devise
protected
def request_format
@request_format ||= Devise::Controllers::InternalHelpers.request_format(request)
end
# Checks whether it's a devise mapped resource or not.
def is_devise_resource? #:nodoc:
unknown_action! <<-MESSAGE unless devise_mapping
@@ -81,11 +65,6 @@ Maybe you forgot to wrap your route inside the scope block? For example:
MESSAGE
end
# Check whether it's navigational format, such as :html or :iphone, or not.
def is_navigational_format?
Devise.navigational_formats.include?(request_format)
end
# Returns real navigational formats which are supported by Rails
def navigational_formats
@navigational_formats ||= Devise.navigational_formats.select{ |format| Mime::EXTENSION_LOOKUP[format.to_s] }

View File

@@ -0,0 +1,26 @@
module Devise
module Controllers
# Helpers used in both FailureApp and Devise controllers.
module SharedHelpers
MIME_REFERENCES = Mime::HTML.respond_to?(:ref)
protected
# Helper used by FailureApp and Devise controllers to retrieve proper formats.
def request_format
@request_format ||= if request.format.respond_to?(:ref)
request.format.ref
elsif MIME_REFERENCES
request.format
elsif request.format # Rails < 3.0.4
request.format.to_sym
end
end
# Check whether it's navigational format, such as :html or :iphone, or not.
def is_navigational_format?
Devise.navigational_formats.include?(request_format)
end
end
end
end

View File

@@ -10,6 +10,7 @@ module Devise
include ActionController::UrlFor
include ActionController::Redirecting
include Rails.application.routes.url_helpers
include Devise::Controllers::SharedHelpers
delegate :flash, :to => :request
@@ -64,13 +65,17 @@ module Devise
end
def redirect_url
if request_format == :html
if skip_format?
send(:"new_#{scope}_session_path")
else
send(:"new_#{scope}_session_path", :format => request_format)
end
end
def skip_format?
%w(html */*).include? request_format.to_s
end
# Choose whether we should respond in a http authentication fashion,
# including 401 and optional headers.
#
@@ -83,7 +88,7 @@ module Devise
if request.xhr?
Devise.http_authenticatable_on_xhr
else
!(request_format && Devise.navigational_formats.include?(request_format))
!(request_format && is_navigational_format?)
end
end
@@ -96,7 +101,13 @@ module Devise
def http_auth_body
return i18n_message unless request_format
method = "to_#{request_format}"
{}.respond_to?(method) ? { :error => i18n_message }.send(method) : i18n_message
if method == "to_xml"
{ :error => i18n_message }.to_xml(:root => "errors")
elsif {}.respond_to?(method)
{ :error => i18n_message }.send(method)
else
i18n_message
end
end
def recall_app(app)
@@ -129,9 +140,5 @@ module Devise
def store_location!
session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
end
def request_format
@request_format ||= Devise::Controllers::InternalHelpers.request_format(request)
end
end
end

View File

@@ -76,6 +76,19 @@ module Devise
def authenticatable_salt
end
%w(to_xml to_json).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__
def #{method}(options={})
if self.class.respond_to?(:accessible_attributes)
options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
super(options)
else
super
end
end
RUBY
end
module ClassMethods
Devise::Models.config(self, :authentication_keys, :request_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)

View File

@@ -41,7 +41,7 @@ module Devise
# Set password and password confirmation to nil
def clean_up_passwords
self.password = self.password_confirmation = nil
self.password = self.password_confirmation = ""
end
# Update record attributes when :current_password matches, otherwise returns

View File

@@ -42,6 +42,7 @@ module Devise
# Checks if the reset password token sent is within the limit time.
# We do this by calculating if the difference between today and the
# sending date does not exceed the confirm in time configured.
# Returns true if the ressource is not responding to reset_password_sent_at at all.
# reset_password_within is a model configuration, must always be an integer value.
#
# Example:
@@ -59,8 +60,8 @@ module Devise
# reset_password_period_valid? # will always return false
#
def reset_password_period_valid?
respond_to?(:reset_password_sent_at) && reset_password_sent_at &&
reset_password_sent_at.utc >= self.class.reset_password_within.ago
return true unless respond_to?(:reset_password_sent_at)
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
end
protected
@@ -73,6 +74,7 @@ module Devise
def generate_reset_password_token
self.reset_password_token = self.class.reset_password_token
self.reset_password_sent_at = Time.now.utc if respond_to?(:reset_password_sent_at=)
self.reset_password_token
end
# Resets the reset password token with and save the record without
@@ -114,7 +116,7 @@ module Devise
if recoverable.reset_password_period_valid?
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
else
recoverable.errors.add(:reset_password_token, :invalid)
recoverable.errors.add(:reset_password_token, :expired)
end
end
recoverable

View File

@@ -13,48 +13,11 @@ module Devise
end
end
# This is a Warden::Proxy customized for functional tests. It's meant to
# some of Warden::Manager responsibilities, as retrieving configuration
# options and calling the FailureApp.
class TestWarden < Warden::Proxy #:nodoc:
attr_reader :controller
def initialize(controller)
@controller = controller
manager = Warden::Manager.new(nil) do |config|
config.merge! Devise.warden_config
end
super(controller.request.env, manager)
end
def authenticate!(*args)
catch_with_redirect { super }
end
def user(*args)
catch_with_redirect { super }
end
def catch_with_redirect(&block)
result = catch(:warden, &block)
if result.is_a?(Hash) && !custom_failure? && !@controller.send(:performed?)
result[:action] ||= :unauthenticated
env = @controller.request.env
env["PATH_INFO"] = "/#{result[:action]}"
env["warden.options"] = result
Warden::Manager._run_callbacks(:before_failure, env, result)
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
@controller.send :render, :status => status, :text => body,
:content_type => headers["Content-Type"], :location => headers["Location"]
nil
else
result
end
end
# Override process to consider warden.
def process(*)
result = nil
_catch_warden { result = super }
result
end
# We need to setup the environment variables and the response in the controller.
@@ -64,7 +27,12 @@ module Devise
# Quick access to Warden::Proxy.
def warden #:nodoc:
@warden ||= (@request.env['warden'] = TestWarden.new(@controller))
@warden ||= begin
manager = Warden::Manager.new(nil) do |config|
config.merge! Devise.warden_config
end
@request.env['warden'] = Warden::Proxy.new(@request.env, manager)
end
end
# sign_in a given resource by storing its keys in the session.
@@ -96,5 +64,27 @@ module Devise
warden.session_serializer.delete(scope, user)
end
protected
def _catch_warden(&block)
result = catch(:warden, &block)
if result.is_a?(Hash) && !warden.custom_failure? && !@controller.send(:performed?)
result[:action] ||= :unauthenticated
env = @controller.request.env
env["PATH_INFO"] = "/#{result[:action]}"
env["warden.options"] = result
Warden::Manager._run_callbacks(:before_failure, env, result)
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
@controller.send :render, :status => status, :text => body,
:content_type => headers["Content-Type"], :location => headers["Location"]
nil
else
result
end
end
end
end

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "1.3.0".freeze
VERSION = "1.3.4".freeze
end

View File

@@ -3,7 +3,7 @@
Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in DeviseMailer.
config.mailer_sender = "please-change-me@config-initializers-devise.com"
config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
# Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer"

View File

@@ -4,14 +4,13 @@ class SessionsControllerTest < ActionController::TestCase
tests Devise::SessionsController
include Devise::TestHelpers
test "#create doesn't raise exception after Warden authentication fails " \
+ "when TestHelpers included" do
test "#create doesn't raise exception after Warden authentication fails when TestHelpers included" do
request.env["devise.mapping"] = Devise.mappings[:user]
assert_nothing_raised(NoMethodError) do
post :create, :user => {
:email => "nosuchuser@example.com",
:password => "wevdude"
}
end
post :create, :user => {
:email => "nosuchuser@example.com",
:password => "wevdude"
}
assert_equal 200, @response.status
assert_template "devise/sessions/new"
end
end

View File

@@ -39,6 +39,11 @@ class FailureTest < ActiveSupport::TestCase
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
end
test 'return to the default redirect location for wildcard requests' do
call_failure 'action_dispatch.request.formats' => nil, 'HTTP_ACCEPT' => '*/*'
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
end
test 'uses the proxy failure message as symbol' do
call_failure('warden' => OpenStruct.new(:message => :test))
assert_equal 'test', @request.flash[:alert]
@@ -84,6 +89,18 @@ class FailureTest < ActiveSupport::TestCase
assert_equal 401, @response.first
end
test 'return appropriate body for xml' do
call_failure('formats' => :xml)
result = %(<?xml version="1.0" encoding="UTF-8"?>\n<errors>\n <error>You need to sign in or sign up before continuing.</error>\n</errors>\n)
assert_equal result, @response.last.body
end
test 'return appropriate body for json' do
call_failure('formats' => :json)
result = %({"error":"You need to sign in or sign up before continuing."})
assert_equal result, @response.last.body
end
test 'return 401 status for unknown formats' do
call_failure 'formats' => []
assert_equal 401, @response.first

View File

@@ -336,9 +336,24 @@ class AuthenticationOthersTest < ActionController::IntegrationTest
end
end
test 'registration in xml format works when recognizing path' do
assert_nothing_raised do
post user_registration_path(:format => 'xml', :user => {:email => "test@example.com", :password => "invalid"} )
test 'sign in stub in xml format' do
get new_user_session_path(:format => 'xml')
assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>\n <email></email>\n <password></password>\n</user>\n", response.body
end
test 'sign in stub in json format' do
get new_user_session_path(:format => 'json')
assert_match '{"user":{', response.body
assert_match '"email":""', response.body
assert_match '"password":""', response.body
end
test 'sign in stub in json with non attribute key' do
swap Devise, :authentication_keys => [:other_key] do
get new_user_session_path(:format => 'json')
assert_match '{"user":{', response.body
assert_match '"other_key":null', response.body
assert_match '"password":""', response.body
end
end

View File

@@ -118,14 +118,14 @@ class RegistrationTest < ActionController::IntegrationTest
sign_in_as_user
get edit_user_registration_path
fill_in 'email', :with => 'user.new@email.com'
fill_in 'email', :with => 'user.new@example.com'
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_current_url '/'
assert_contain 'You updated your account successfully.'
assert_equal "user.new@email.com", User.first.email
assert_equal "user.new@example.com", User.first.email
end
test 'a signed in user should still be able to use the website after changing his password' do
@@ -146,13 +146,13 @@ class RegistrationTest < ActionController::IntegrationTest
sign_in_as_user
get edit_user_registration_path
fill_in 'email', :with => 'user.new@email.com'
fill_in 'email', :with => 'user.new@example.com'
fill_in 'current password', :with => 'invalid'
click_button 'Update'
assert_template 'registrations/edit'
assert_contain 'user@test.com'
assert_have_selector 'form input[value="user.new@email.com"]'
assert_have_selector 'form input[value="user.new@example.com"]'
assert_equal "user@test.com", User.first.email
end
@@ -207,6 +207,20 @@ class RegistrationTest < ActionController::IntegrationTest
assert_redirected_to new_user_registration_path
end
test 'a user with XML sign up stub' do
get new_user_registration_path(:format => 'xml')
assert_response :success
assert_match %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>), response.body
assert_no_match(/<confirmation_token>/, response.body) if DEVISE_ORM == :active_record
end
test 'a user with JSON sign up stub' do
get new_user_registration_path(:format => 'json')
assert_response :success
assert_match %({"user":), response.body
assert_no_match(/"confirmation_token"/, response.body) if DEVISE_ORM == :active_record
end
test 'an admin sign up with valid information in XML format should return valid response' do
post admin_registration_path(:format => 'xml'), :admin => { :email => 'new_user@test.com', :password => 'new_user123', :password_confirmation => 'new_user123' }
assert_response :success

View File

@@ -111,12 +111,12 @@ class ConfirmableTest < ActiveSupport::TestCase
end
test 'should return a new user if no email was found' do
confirmation_user = User.send_confirmation_instructions(:email => "invalid@email.com")
confirmation_user = User.send_confirmation_instructions(:email => "invalid@example.com")
assert_not confirmation_user.persisted?
end
test 'should add error to new user email if no email was found' do
confirmation_user = User.send_confirmation_instructions(:email => "invalid@email.com")
confirmation_user = User.send_confirmation_instructions(:email => "invalid@example.com")
assert confirmation_user.errors[:email]
assert_equal "not found", confirmation_user.errors[:email].join
end

View File

@@ -89,8 +89,8 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
test 'should ignore password and its confirmation if they are blank' do
user = create_user
assert user.update_with_password(:current_password => '123456', :email => "new@email.com")
assert_equal "new@email.com", user.email
assert user.update_with_password(:current_password => '123456', :email => "new@example.com")
assert_equal "new@example.com", user.email
end
test 'should not update password with invalid confirmation' do

View File

@@ -163,12 +163,12 @@ class LockableTest < ActiveSupport::TestCase
end
test 'should return a new user if no email was found' do
unlock_user = User.send_unlock_instructions(:email => "invalid@email.com")
unlock_user = User.send_unlock_instructions(:email => "invalid@example.com")
assert_not unlock_user.persisted?
end
test 'should add error to new user email if no email was found' do
unlock_user = User.send_unlock_instructions(:email => "invalid@email.com")
unlock_user = User.send_unlock_instructions(:email => "invalid@example.com")
assert_equal 'not found', unlock_user.errors[:email].join
end

View File

@@ -72,7 +72,7 @@ class RecoverableTest < ActiveSupport::TestCase
end
test 'should return a new record with errors if user was not found by e-mail' do
reset_password_user = User.send_reset_password_instructions(:email => "invalid@email.com")
reset_password_user = User.send_reset_password_instructions(:email => "invalid@example.com")
assert_not reset_password_user.persisted?
assert_equal "not found", reset_password_user.errors[:email].join
end
@@ -192,8 +192,23 @@ class RecoverableTest < ActiveSupport::TestCase
assert user.valid_password?(old_password)
assert_not user.valid_password?('new_password')
assert_equal "is invalid", reset_password_user.errors[:reset_password_token].join
assert_equal "has expired, please request a new one", reset_password_user.errors[:reset_password_token].join
end
end
test 'should save the model when the reset_password_sent_at doesnt exist' do
user = create_user
user.stubs(:respond_to?).with(:reset_password_sent_at=).returns(false)
user.stubs(:respond_to?).with(:headers_for).returns(false)
user.send_reset_password_instructions
user.reload
assert_not_nil user.reset_password_token
end
test 'should have valid period if does not respond to reset_password_sent_at' do
user = create_user
user.stubs(:respond_to?).with(:reset_password_sent_at).returns(false)
assert user.reset_password_period_valid?
end
end

View File

@@ -6,6 +6,8 @@ module SharedUser
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable, :omniauthable
attr_accessor :other_key
# They need to be included after Devise is called.
extend ExtendMethods
end

View File

@@ -19,7 +19,7 @@ class ActiveSupport::TestCase
def generate_unique_email
@@email_count ||= 0
@@email_count += 1
"test#{@@email_count}@email.com"
"test#{@@email_count}@example.com"
end
def valid_attributes(attributes={})
@@ -57,4 +57,4 @@ class ActiveSupport::TestCase
object.send :"#{key}=", value
end
end
end
end