Skip to content

Commit

Permalink
Merge pull request lynndylanhurley#317 from jakubrohleder/feature/pas…
Browse files Browse the repository at this point in the history
…sword-check-before-update

Old password check before password update
  • Loading branch information
booleanbetrayal committed Jul 21, 2015
2 parents 07ca81c + 2ce397b commit 32c1329
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 83 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ The following routes are available for use by your client. These routes live rel
|:-----|:-------|:--------|
| / | POST | Email registration. Accepts **`email`**, **`password`**, and **`password_confirmation`** params. A verification email will be sent to the email address provided. Accepted params can be customized using the [`devise_parameter_sanitizer`](https://github.com/plataformatec/devise#strong-parameters) system. |
| / | DELETE | Account deletion. This route will destroy users identified by their **`uid`** and **`auth_token`** headers. |
| / | PUT | Account updates. This route will update an existing user's account settings. The default accepted params are **`password`** and **`password_confirmation`**, but this can be customized using the [`devise_parameter_sanitizer`](https://github.com/plataformatec/devise#strong-parameters) system. |
| / | PUT | Account updates. This route will update an existing user's account settings. The default accepted params are **`password`** and **`password_confirmation`**, but this can be customized using the [`devise_parameter_sanitizer`](https://github.com/plataformatec/devise#strong-parameters) system. If **`config.check_current_password_before_update`** is set to `:attributes` the **`current_password`** param is checked before any update, if it is set to `:password` the **`current_password`** param is checked only if the request updates user password. |
| /sign_in | POST | Email authentication. Accepts **`email`** and **`password`** as params. This route will return a JSON representation of the `User` model on successful login. |
| /sign_out | DELETE | Use this route to end the user's current session. This route will invalidate the user's authentication token. |
| /:provider | GET | Set this route as the destination for client authentication. Ideally this will happen in an external window or popup. [Read more](#omniauth-authentication). |
| /:provider/callback | GET/POST | Destination for the oauth2 provider's callback uri. `postMessage` events containing the authenticated user's data will be sent back to the main client window from this page. [Read more](#omniauth-authentication). |
| /validate_token | GET | Use this route to validate tokens on return visits to the client. Accepts **`uid`** and **`access-token`** as params. These values should correspond to the columns in your `User` table of the same names. |
| /password | POST | Use this route to send a password reset confirmation email to users that registered by email. Accepts **`email`** and **`redirect_url`** as params. The user matching the `email` param will be sent instructions on how to reset their password. `redirect_url` is the url to which the user will be redirected after visiting the link contained in the email. |
| /password | PUT | Use this route to change users' passwords. Accepts **`password`** and **`password_confirmation`** as params. This route is only valid for users that registered by email (OAuth2 users will receive an error). |
| /password | PUT | Use this route to change users' passwords. Accepts **`password`** and **`password_confirmation`** as params. This route is only valid for users that registered by email (OAuth2 users will receive an error). It also checks **`current_password`** if **`config.check_current_password_before_update`** is not set `false` (disabled by default). |
| /password/edit | GET | Verify user by password reset token. This route is the destination URL for password reset confirmation. This route must contain **`reset_password_token`** and **`redirect_url`** params. These values will be set automatically by the confirmation email that is generated by the password reset request. |

[Jump here](#usage-cont) for more usage information.
Expand Down Expand Up @@ -608,7 +608,7 @@ For example, the default behavior of the [`validate_token`](https://github.com/l
~~~ruby
# config/routes.rb
Rails.application.routes.draw do
...
...
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
token_validations: 'overrides/token_validations'
}
Expand Down
12 changes: 10 additions & 2 deletions app/controllers/devise_token_auth/passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def update
}, status: 422
end

if @resource.update_attributes(password_resource_params)
if @resource.send(resource_update_method, password_resource_params)
yield if block_given?
return render json: {
success: true,
Expand All @@ -165,12 +165,20 @@ def update
end
end

def resource_update_method
if DeviseTokenAuth.check_current_password_before_update != false
"update_with_password"
else
"update_attributes"
end
end

def password_resource_params
params.permit(devise_parameter_sanitizer.for(:account_update))
end

def resource_params
params.permit(:email, :password, :password_confirmation, :reset_password_token)
params.permit(:email, :password, :password_confirmation, :current_password, :reset_password_token)
end

end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ def account_update_params
private

def resource_update_method
if account_update_params.has_key?(:current_password)
if DeviseTokenAuth.check_current_password_before_update == :attributes
"update_with_password"
elsif DeviseTokenAuth.check_current_password_before_update == :password and account_update_params.has_key?(:password)
"update_with_password"
elsif account_update_params.has_key?(:current_password)
"update_with_password"
else
"update_attributes"
Expand Down
18 changes: 10 additions & 8 deletions lib/devise_token_auth/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ class Engine < ::Rails::Engine
:omniauth_prefix,
:default_confirm_success_url,
:default_password_reset_url,
:redirect_whitelist
:redirect_whitelist,
:check_current_password_before_update

self.change_headers_on_each_request = true
self.token_lifespan = 2.weeks
self.batch_request_buffer_throttle = 5.seconds
self.omniauth_prefix = '/omniauth'
self.default_confirm_success_url = nil
self.default_password_reset_url = nil
self.redirect_whitelist = nil
self.change_headers_on_each_request = true
self.token_lifespan = 2.weeks
self.batch_request_buffer_throttle = 5.seconds
self.omniauth_prefix = '/omniauth'
self.default_confirm_success_url = nil
self.default_password_reset_url = nil
self.redirect_whitelist = nil
self.check_current_password_before_update = false

def self.setup(&block)
yield self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@
# example, using the default '/omniauth', the github oauth2 provider will
# redirect successful authentications to '/omniauth/github/callback'
#config.omniauth_prefix = "/omniauth"

# By defult sending current password is not needed for the password update.
# Uncomment to enforce current_password param to be checked before all
# attribute updates. Set it to :password if you want it to be checked only if
# password is updated.
# config.check_current_password_before_update = :attributes
end
50 changes: 50 additions & 0 deletions test/controllers/devise_token_auth/passwords_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,56 @@ class DeviseTokenAuth::PasswordsControllerTest < ActionController::TestCase
end
end

describe "change password with current password required" do
before do
DeviseTokenAuth.check_current_password_before_update = :password
end

after do
DeviseTokenAuth.check_current_password_before_update = false
end

describe 'success' do
before do
@auth_headers = @resource.create_new_auth_token
request.headers.merge!(@auth_headers)
@new_password = Faker::Internet.password
@resource.update password: 'secret123', password_confirmation: 'secret123'

xhr :put, :update, {
password: @new_password,
password_confirmation: @new_password,
current_password: 'secret123'
}

@data = JSON.parse(response.body)
@resource.reload
end

test "request should be successful" do
assert_equal 200, response.status
end
end

describe 'current password mismatch error' do
before do
@auth_headers = @resource.create_new_auth_token
request.headers.merge!(@auth_headers)
@new_password = Faker::Internet.password

xhr :put, :update, {
password: @new_password,
password_confirmation: @new_password,
current_password: 'not_very_secret321'
}
end

test 'response should fail unauthorized' do
assert_equal 422, response.status
end
end
end

describe "change password" do
describe 'success' do
before do
Expand Down
Loading

0 comments on commit 32c1329

Please sign in to comment.