From 0d9adc0d6457fed69bcdaf21aab203d80176b9c6 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 31 Dec 2014 10:04:14 +0100 Subject: [PATCH] Added browser tests for password reset. --- .../app/controllers/reset_password.js.coffee | 182 +++++++++++------- .../app/views/generic/hero_message.jst.eco | 8 - .../app/views/password/reset_change.jst.eco | 10 +- .../app/views/password/reset_failed.jst.eco | 9 + app/controllers/users_controller.rb | 22 ++- app/models/user.rb | 4 +- db/migrate/20141231000001_add_develop_mode.rb | 16 ++ db/seeds.rb | 9 + lib/auth.rb | 2 +- lib/auth/{test.rb => developer.rb} | 4 +- script/local_browser_tests.sh | 6 +- ... signup_password_change_and_reset_test.rb} | 152 ++++++++++++++- 12 files changed, 324 insertions(+), 100 deletions(-) delete mode 100644 app/assets/javascripts/app/views/generic/hero_message.jst.eco create mode 100644 app/assets/javascripts/app/views/password/reset_failed.jst.eco create mode 100644 db/migrate/20141231000001_add_develop_mode.rb rename lib/auth/{test.rb => developer.rb} (67%) rename test/browser/{signup_test.rb => signup_password_change_and_reset_test.rb} (56%) diff --git a/app/assets/javascripts/app/controllers/reset_password.js.coffee b/app/assets/javascripts/app/controllers/reset_password.js.coffee index 77ce03857..7c1d352cf 100644 --- a/app/assets/javascripts/app/controllers/reset_password.js.coffee +++ b/app/assets/javascripts/app/controllers/reset_password.js.coffee @@ -44,120 +44,156 @@ class Index extends App.ControllerContent # get data @ajax( - id: 'password_reset' - type: 'POST' - url: @apiPath + '/users/password_reset' - data: JSON.stringify(params) + id: 'password_reset' + type: 'POST' + url: @apiPath + '/users/password_reset' + data: JSON.stringify(params) processData: true - success: @success - error: @error + success: @success ) - success: (data, status, xhr) => - @render( sent: true ) + success: (data) => + if data.message is 'ok' - error: (data, status, xhr) => - @notify( - type: 'error' - msg: App.i18n.translateContent( 'Username or email address invalid, please try again.' ) - ) - @formEnable( @el.find('.form-password') ) + # if in developer mode, redirect to set new password + if data.token && @Config.get('developer_mode') is true + redirect = => + @navigate "#password_reset_verify/#{data.token}" + @delay( redirect, 2000 ) + @render( sent: true ) + + else + @$('[name=username]').val('') + @notify( + type: 'error' + msg: App.i18n.translateContent( 'Username or email address invalid, please try again.' ) + ) + @formEnable( @el.find('.form-password') ) App.Config.set( 'reset_password', Index, 'Routes' ) class Verify extends App.ControllerContent events: - 'submit form': 'submit' + 'submit form': 'submit' 'click .submit': 'submit' constructor: -> super + @navHide() + # set title @title 'Reset Password' @navupdate '#reset_password_verify' # get data - params = {} - params['token'] = @token + params = + token: @token @ajax( - id: 'password_reset_verify' - type: 'POST' - url: @apiPath + '/users/password_reset_verify' - data: JSON.stringify(params) + id: 'password_reset_verify' + type: 'POST' + url: @apiPath + '/users/password_reset_verify' + data: JSON.stringify(params) processData: true - success: @render_success - error: @render_failed + success: @render_change ) - render_success: => - configure_attributes = [ - { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input span4', }, - ] + render_change: (data) => + if data.message is 'ok' + configure_attributes = [ + { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', }, + ] - @html App.view('password/reset_change')() + @html App.view('password/reset_change')() - new App.ControllerForm( - el: @el.find('#form-password-change') - model: { configure_attributes: configure_attributes } - autofocus: true - ) - - render_failed: => - @html App.view('generic/hero_message')( - head: 'Failed!' - message: 'Token is not valid!' - ) + new App.ControllerForm( + el: @el.find('.form-password-change') + model: { configure_attributes: configure_attributes } + autofocus: true + ) + else + @html App.view('password/reset_failed')( + head: 'Reset Password failed!' + message: 'Token is invalid!' + ) submit: (e) -> e.preventDefault() - params = @formParam(e.target) + params = @formParam(e.target) params['token'] = @token - @password = params['password'] + @password = params['password'] + + # disable form + @formDisable(e) + + # validate + if params['password_confirm'] isnt params['password'] + @formEnable(e) + @$('[name=password]').val('') + @$('[name=password_confirm]').val('') + @notify + type: 'error' + msg: 'Can\'t update password, your new passwords do not match. Please try again!' + removeAll: true + return + if !params['password'] + @formEnable(e) + @notify + type: 'error' + msg: 'Please supply your new password!' + removeAll: true + return # get data @ajax( - id: 'password_reset_verify' - type: 'POST' - url: @apiPath + '/users/password_reset_verify' - data: JSON.stringify(params) + id: 'password_reset_verify' + type: 'POST' + url: @apiPath + '/users/password_reset_verify' + data: JSON.stringify(params) processData: true - success: @render_changed_success - error: @render_changed_failed + success: @render_changed ) - render_changed_success: (data, status, xhr) => - App.Auth.login( - data: - username: data.user_login - password: @password - success: => + render_changed: (data, status, xhr) => + if data.message is 'ok' + App.Auth.login( + data: + username: data.user_login + password: @password + success: => - # login check - App.Auth.loginCheck() + # login check + App.Auth.loginCheck() - # add notify - @notify - type: 'success' - msg: 'Woo hoo! Your password has been changed!' - removeAll: true + # add notify + @notify + type: 'success' + msg: 'Woo hoo! Your password has been changed!' + removeAll: true - # redirect to # - @navigate '#' + # redirect to # + @navigate '#' - error: => + error: => + @formEnable( @$('form') ) - # add notify + # add notify + @notify + type: 'error' + msg: 'Something went wrong. Please contact your administrator.' + removeAll: true + ) + else + if data.notice @notify type: 'error' - msg: 'Something went wrong. Please contact your administrator.' + msg: App.i18n.translateContent( data.notice[0], data.notice[1] ) removeAll: true - ) + else + @notify + type: 'error' + msg: 'Unable to set password. Please contact your administrator.' + removeAll: true + @formEnable( @$('form') ) - render_changed_failed: => - @html App.view('generic/hero_message')( - head: 'Failed!' - message: 'Ask your admin!' - ) - -App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' ) +App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' ) \ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/hero_message.jst.eco b/app/assets/javascripts/app/views/generic/hero_message.jst.eco deleted file mode 100644 index fce7de609..000000000 --- a/app/assets/javascripts/app/views/generic/hero_message.jst.eco +++ /dev/null @@ -1,8 +0,0 @@ -
-

<%- @T( @head ) %> <%- @T( @head_small ) %>

-
-

- <%- @message %> -

-
-
diff --git a/app/assets/javascripts/app/views/password/reset_change.jst.eco b/app/assets/javascripts/app/views/password/reset_change.jst.eco index 135d2cd75..f8961dabb 100644 --- a/app/assets/javascripts/app/views/password/reset_change.jst.eco +++ b/app/assets/javascripts/app/views/password/reset_change.jst.eco @@ -1,8 +1,12 @@

<%- @T( 'Choose your new password.' ) %>

-
- + +
+
+ <%- @T( 'Cancel & Go Back' ) %> + +
-
+ \ No newline at end of file diff --git a/app/assets/javascripts/app/views/password/reset_failed.jst.eco b/app/assets/javascripts/app/views/password/reset_failed.jst.eco new file mode 100644 index 000000000..eb3926b95 --- /dev/null +++ b/app/assets/javascripts/app/views/password/reset_failed.jst.eco @@ -0,0 +1,9 @@ +
+
+
+

<%- @T( @head ) %>

+

<%- @message %>

+ » <%- @T('try again') %> « +
+
+
\ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e14a06753..8d7e7ac87 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -367,12 +367,22 @@ curl http://localhost/api/v1/users/password_reset.json -v -u #{login}:#{password return end - success = User.password_reset_send( params[:username] ) - if success + token = User.password_reset_send( params[:username] ) + if token + + # only if system is in develop mode, send token back to browser for browser tests + if Setting.get('developer_mode') == true + render :json => { :message => 'ok', :token => token.name }, :status => :ok + return + end + + # token sent to user, send ok to browser render :json => { :message => 'ok' }, :status => :ok - else - render :json => { :message => 'failed' }, :status => :unprocessable_entity + return end + + # unable to generate token + render :json => { :message => 'failed' }, :status => :ok end =begin @@ -443,12 +453,12 @@ curl http://localhost/api/v1/users/password_change.json -v -u #{login}:#{passwor # check old password if !params[:password_old] - render :json => { :message => 'failed', :notice => ['Old password needed!'] }, :status => :ok + render :json => { :message => 'failed', :notice => ['Current password needed!'] }, :status => :ok return end user = User.authenticate( current_user.login, params[:password_old] ) if !user - render :json => { :message => 'failed', :notice => ['Old password is wrong!'] }, :status => :ok + render :json => { :message => 'failed', :notice => ['Current password is wrong!'] }, :status => :ok return end diff --git a/app/models/user.rb b/app/models/user.rb index 9c8ef6199..c920e7dc5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -267,7 +267,7 @@ send reset password email with token to user returns - result = true|false + result = token =end @@ -325,7 +325,7 @@ returns :subject => data[:subject], :body => data[:body] ) - true + token end =begin diff --git a/db/migrate/20141231000001_add_develop_mode.rb b/db/migrate/20141231000001_add_develop_mode.rb new file mode 100644 index 000000000..fa2cf165b --- /dev/null +++ b/db/migrate/20141231000001_add_develop_mode.rb @@ -0,0 +1,16 @@ +class AddDevelopMode < ActiveRecord::Migration + def up + Setting.create_if_not_exists( + :title => 'Develop System', + :name => 'developer_mode', + :area => 'Core::Develop', + :description => 'Defines if application is in developer mode (useful for developer, all users have the same password, password reset will work without email delivery).', + :options => {}, + :state => false, + :frontend => true + ) + end + + def down + end +end \ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index fa6998fe1..5a6c3c488 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -15,6 +15,15 @@ Setting.create_if_not_exists( :state => false, :frontend => true ) +Setting.create_if_not_exists( + :title => 'Developer System', + :name => 'developer_mode', + :area => 'Core::Develop', + :description => 'Defines if application is in developer mode (useful for developer, all users have the same password, password reset will work without email delivery).', + :options => {}, + :state => false, + :frontend => true +) Setting.create_if_not_exists( :title => 'Online Service', :name => 'system_online_service', diff --git a/lib/auth.rb b/lib/auth.rb index 1a8918d50..03f2bdd46 100644 --- a/lib/auth.rb +++ b/lib/auth.rb @@ -23,7 +23,7 @@ returns :adapter => 'Auth::Internal', }, { - :adapter => 'Auth::Test', + :adapter => 'Auth::Developer', }, ] diff --git a/lib/auth/test.rb b/lib/auth/developer.rb similarity index 67% rename from lib/auth/test.rb rename to lib/auth/developer.rb index a06a2f330..ce006d19d 100644 --- a/lib/auth/test.rb +++ b/lib/auth/developer.rb @@ -1,10 +1,10 @@ # Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/ -module Auth::Test +module Auth::Developer def self.check( username, password, config, user ) # development systems - if !ENV['RAILS_ENV'] || ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test' + if Setting.get('developer_mode') == true return user if password == 'test' end diff --git a/script/local_browser_tests.sh b/script/local_browser_tests.sh index 2d6b7ba18..c19a7e2d4 100755 --- a/script/local_browser_tests.sh +++ b/script/local_browser_tests.sh @@ -28,8 +28,8 @@ rake db:seed # modify production.rb to serve assets cat config/environments/production.rb | sed -e 's/config.serve_static_assets = false/config.serve_static_assets = true/' > /tmp/production.rb && cp /tmp/production.rb config/environments/production.rb -# mofidy auth backend -cat lib/auth/test.rb | sed "s/\] == 'test'/] == 'production'/" > /tmp/test.rb && cp /tmp/test.rb lib/auth/test.rb +# set system to develop mode +rails r "Setting.set('developer_mode', true)" pumactl --pidfile tmp/pids/puma.pid stop script/websocket-server.rb stop @@ -42,7 +42,7 @@ sleep 15 #export REMOTE_URL='http://medenhofer:765d0dd4-994b-4e15-9f89-13f3aedeb462@ondemand.saucelabs.com:80/wd/hub' BROWSER_OS='Windows 2012' BROWSER_VERSION=20 BROWSER=firefox rake test:browser["BROWSER_URL=http://localhost:4444"] -#rake test:browser["BROWSER_URL=http://192.168.178.20:4444"] +#rake test:browser["BROWSER_URL=http://192.168.178.28:4444"] script/websocket-server.rb stop diff --git a/test/browser/signup_test.rb b/test/browser/signup_password_change_and_reset_test.rb similarity index 56% rename from test/browser/signup_test.rb rename to test/browser/signup_password_change_and_reset_test.rb index 70c4cc762..1979beb28 100644 --- a/test/browser/signup_test.rb +++ b/test/browser/signup_password_change_and_reset_test.rb @@ -1,7 +1,7 @@ # encoding: utf-8 require 'browser_test_helper' -class SignupTest < TestCase +class SignupPasswordChangeAndResetTest < TestCase def test_signup signup_user_email = 'signup-test-' + rand(999999).to_s + '@example.com' tests = [ @@ -112,7 +112,7 @@ class SignupTest < TestCase { :execute => 'watch_for', :area => 'body', - :value => 'old password is wrong', + :value => 'current password is wrong', }, { :execute => 'set', @@ -198,6 +198,154 @@ class SignupTest < TestCase :username => signup_user_email, :password => 'some-pass-new2', }, + { + :execute => 'logout', + }, + ], + }, + { + :name => 'reset password', + :action => [ + # got to wrong url + { + :execute => 'navigate', + :to => browser_url + '/#password_reset_verify/not_existing_token', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'Token is invalid', + }, + + # correct way + { + :execute => 'click', + :css => 'a[href="#reset_password"]', + }, + { + :execute => 'set', + :css => 'input[name="username"]', + :value => 'nonexisiting', + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'address invalid', + }, + { + :execute => 'set', + :css => 'input[name="username"]', + :value => signup_user_email, + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'sent password reset instructions', + }, + + # redirect to "#password_reset_verify/#{token}" url by app, because of "developer_mode" + { + :execute => 'watch_for', + :area => 'body', + :value => 'Choose your new password', + }, + + # set new password + { + :execute => 'set', + :css => 'input[name="password"]', + :value => 'some', + }, + { + :execute => 'set', + :css => 'input[name="password_confirm"]', + :value => 'some2', + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'passwords do not match', + }, + { + :execute => 'set', + :css => 'input[name="password"]', + :value => 'some', + }, + { + :execute => 'set', + :css => 'input[name="password_confirm"]', + :value => 'some', + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'it must be at least', + }, + { + :execute => 'set', + :css => 'input[name="password"]', + :value => 'some-pass-new', + }, + { + :execute => 'set', + :css => 'input[name="password_confirm"]', + :value => 'some-pass-new', + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'must contain at least 1 digit', + }, + { + :execute => 'set', + :css => 'input[name="password"]', + :value => 'some-pass-new2', + }, + { + :execute => 'set', + :css => 'input[name="password_confirm"]', + :value => 'some-pass-new2', + }, + { + :execute => 'click', + :css => '.content .btn--primary', + }, + { + :execute => 'watch_for', + :area => 'body', + :value => 'Your password has been changed', + }, + { + :execute => 'wait', + :value => 5, + }, + { + :execute => 'match', + :css => '.user-menu .user a', + :attribute => 'title', + :value => signup_user_email, + :match_result => true, + }, ], }, ]