From d84895b800dbe24792c40f4e4d5517a9a6507905 Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 15 Mar 2018 09:41:42 +0100 Subject: [PATCH 1/3] Add test suite for the feature The tests that will check the featute is working well added. There are four test: 1. Checks that the emails are been send to the user. The test looks for the link in there and takes the token. Visits the page and changes the password. 2 and 3. Both change the password by hand. One uses a password written by the manager, whilst the other uses the 'Generate random password' option. Both tests check that the user can sign in with the new passwords. 4. Checks that the password can be printed when it is saved. --- spec/features/management/account_spec.rb | 84 +++++++++++++++++++++++- spec/support/common_actions.rb | 10 +++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/spec/features/management/account_spec.rb b/spec/features/management/account_spec.rb index 0be7e473b..df1ca6efd 100644 --- a/spec/features/management/account_spec.rb +++ b/spec/features/management/account_spec.rb @@ -6,11 +6,14 @@ feature 'Account' do login_as_manager end - scenario "Should not allow unverified users to create spending proposals" do + scenario "Should not allow unverified users to edit their account" do user = create(:user) login_managed_user(user) - click_link "Edit user account" + visit management_root_path + + click_link 'Edit user account' + click_link 'Reset password via email' expect(page).to have_content "No verified user logged in yet" end @@ -29,4 +32,81 @@ feature 'Account' do expect(user.reload.erase_reason).to eq "Deleted by manager: manager_user_#{Manager.last.user_id}" end + scenario "Send reset password email to currently managed user session" do + user = create(:user, :level_three) + login_managed_user(user) + visit management_root_path + + click_link 'Edit user account' + click_link 'Reset password via email' + + click_link 'Send reset password email' + + expect(page).to have_content 'Email correctly sent.' + + email = ActionMailer::Base.deliveries.last + + expect(email).to have_text 'Change your password' + end + + scenario "Manager changes the password by hand (writen by them)" do + user = create(:user, :level_three) + login_managed_user(user) + visit management_root_path + + click_link 'Edit user account' + click_link 'Reset password manually' + + find(:css, "input[id$='user_password']").set("new_password") + + click_button 'Save' + + expect(page).to have_content 'Password reseted successfully' + + logout + + login_through_form_with_email_and_password(user.email, 'new_password') + + expect(page).to have_content 'You have been signed in successfully.' + end + + scenario "Manager generates random password", :js do + user = create(:user, :level_three) + login_managed_user(user) + visit management_root_path + + click_link 'Edit user account' + click_link 'Reset password manually' + click_link 'Generate random password' + + new_password = find_field('user_password').value + + click_button 'Save' + + expect(page).to have_content 'Password reseted successfully' + + logout + + login_through_form_with_email_and_password(user.username, new_password) + + expect(page).to have_content 'You have been signed in successfully.' + end + + scenario "The password is printed", :js do + user = create(:user, :level_three) + login_managed_user(user) + visit management_root_path + + click_link 'Edit user account' + click_link 'Reset password manually' + + find(:css, "input[id$='user_password']").set("another_new_password") + + click_button 'Save' + + expect(page).to have_content 'Password reseted successfully' + expect(page).to have_css("a[href='javascript:window.print();']", text: 'Print password') + expect(page).to have_css("div.for-print-only", text: 'another_new_password', visible: false) + end + end diff --git a/spec/support/common_actions.rb b/spec/support/common_actions.rb index 1704faed1..fb077c264 100644 --- a/spec/support/common_actions.rb +++ b/spec/support/common_actions.rb @@ -14,6 +14,16 @@ module CommonActions click_button 'Register' end + def login_through_form_with_email_and_password(email='manuela@consul.dev', password='judgementday') + visit root_path + click_link 'Sign in' + + fill_in 'user_login', with: email + fill_in 'user_password', with: password + + click_button 'Enter' + end + def login_through_form_as(user) visit root_path click_link 'Sign in' From 3a62fc9bda90c5b530caf454f2d60d1c3a5b5929 Mon Sep 17 00:00:00 2001 From: iagirre Date: Fri, 23 Mar 2018 09:17:48 +0100 Subject: [PATCH 2/3] Add UI to let manager change users password A submenu has been added to the side menu's 'Edit user account' option. This submenu has two options: - Reset password via email: an email is send so that the user can change their password by themselves. - Reset password manually: the manager has to write the password manually (or generate a random one). The passwords generated by the random password generator don't contain characters like $ or !. It uses some capital letters, some other lower case letters and some numbers. Ambiguous characters like 1, l, I has been removed. --- app/assets/javascripts/application.js | 2 ++ app/assets/javascripts/managers.js.coffee | 25 +++++++++++++++++++ app/assets/stylesheets/admin.scss | 4 +++ app/views/management/_menu.html.erb | 20 +++++++++++---- .../account/edit_password_email.html.erb | 3 +++ .../account/edit_password_manually.html.erb | 20 +++++++++++++++ .../account/print_password.html.erb | 21 ++++++++++++++++ config/locales/en/management.yml | 15 +++++++++++ config/locales/es/management.yml | 15 +++++++++++ 9 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/managers.js.coffee create mode 100644 app/views/management/account/edit_password_email.html.erb create mode 100644 app/views/management/account/edit_password_manually.html.erb create mode 100644 app/views/management/account/print_password.html.erb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b29eeecb0..7a110aa18 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -76,6 +76,7 @@ //= require table_sortable //= require investment_report_alert //= require send_newsletter_alert +//= require managers var initialize_modules = function() { App.Comments.initialize(); @@ -119,6 +120,7 @@ var initialize_modules = function() { App.TableSortable.initialize(); App.InvestmentReportAlert.initialize(); App.SendNewsletterAlert.initialize(); + App.Managers.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/managers.js.coffee b/app/assets/javascripts/managers.js.coffee new file mode 100644 index 000000000..7d4d702a1 --- /dev/null +++ b/app/assets/javascripts/managers.js.coffee @@ -0,0 +1,25 @@ +App.Managers = + + generatePassword: -> + chars = 'aAbcdeEfghiJkmnpqrstuUvwxyz23456789' + pass = '' + x = 0 + while x < 12 + i = Math.floor(Math.random() * chars.length) + pass += chars.charAt(i) + x++ + return pass + + togglePassword: (type) -> + $('#user_password').prop 'type', type + + initialize: -> + $(".generate-random-value").on "click", (event) -> + password = App.Managers.generatePassword() + $('#user_password').val(password) + + $(".show-password").on "click", (event) -> + if $("#user_password").is("input[type='password']") + App.Managers.togglePassword('text') + else + App.Managers.togglePassword('password') diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 827ba0c8b..5538a2c59 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -278,6 +278,10 @@ $sidebar-active: #f4fcd0; cursor: pointer; } +.no-margin-bottom { + margin-bottom: 0 !important; +} + // 02. Sidebar // ----------- diff --git a/app/views/management/_menu.html.erb b/app/views/management/_menu.html.erb index b01bbaf0c..75e832a24 100644 --- a/app/views/management/_menu.html.erb +++ b/app/views/management/_menu.html.erb @@ -1,5 +1,5 @@
-
    +
    • > @@ -9,11 +9,21 @@ <% end %>
    • -
    • > - <%= link_to management_account_path do %> +
    • + - <%= t("management.menu.edit_user_accounts") %> - <% end %> + <%= t("management.menu.edit_user_accounts") %> + +
        > + <% if managed_user.email %> +
      • + <%= link_to t("management.account.menu.reset_password_email"), edit_password_email_management_account_path %> +
      • + <% end %> +
      • + <%= link_to t("management.account.menu.reset_password_manually"), edit_password_manually_management_account_path %> +
      • +
    • > diff --git a/app/views/management/account/edit_password_email.html.erb b/app/views/management/account/edit_password_email.html.erb new file mode 100644 index 000000000..7d6f131e3 --- /dev/null +++ b/app/views/management/account/edit_password_email.html.erb @@ -0,0 +1,3 @@ +

      <%= t("management.account.menu.reset_password_email") %>

      + +<%= link_to t("management.account.edit.password.send_email"), reset_password_management_account_path(managed_user.id), class: "button hollow" %> diff --git a/app/views/management/account/edit_password_manually.html.erb b/app/views/management/account/edit_password_manually.html.erb new file mode 100644 index 000000000..8fe197d6b --- /dev/null +++ b/app/views/management/account/edit_password_manually.html.erb @@ -0,0 +1,20 @@ +

      <%= t("management.account.menu.reset_password_manually") %>

      + +<%= form_for managed_user, url: change_password_management_account_path do |f| %> +
      + <%= f.label :password %> +

      <%= t("management.account.edit.password.print_help") %>

      +
      + +
      + <%= f.password_field :password, class: "input-group-field no-margin-bottom", label: false, value: nil %> + + + +
      + + <%= link_to t("management.account.edit.password.random"), '#', class: 'generate-random-value float-right' %> +
      +
      + <%= f.submit t("management.account.edit.password.save"), class: "button success" %> +<% end %> diff --git a/app/views/management/account/print_password.html.erb b/app/views/management/account/print_password.html.erb new file mode 100644 index 000000000..844d216f0 --- /dev/null +++ b/app/views/management/account/print_password.html.erb @@ -0,0 +1,21 @@ +

      <%= t("management.account.edit.title") %>

      + +
      + <%= t("management.account.edit.password.password") %>: + <%= session[:new_password] %> +
      + +
      +
      +

      <%= t("management.account.edit.password.reseted") %>

      +
      +
      + +
      +
      + <%= link_to t("management.account.edit.back"), edit_password_manually_management_account_path(managed_user.id), class: "button hollow" %> + + <%= t('management.account.edit.password.print') %> + +
      +
      diff --git a/config/locales/en/management.yml b/config/locales/en/management.yml index 911916629..745bdc81a 100644 --- a/config/locales/en/management.yml +++ b/config/locales/en/management.yml @@ -1,10 +1,25 @@ en: management: account: + menu: + reset_password_email: Reset password via email + reset_password_manually: Reset password manually alert: unverified_user: No verified user logged in yet show: title: User account + edit: + title: 'Edit user account: Reset password' + back: Back + password: + password: Password + send_email: Send reset password email + reset_email_send: Email correctly sent. + reseted: Password reseted successfully + random: Generate random password + save: Save + print: Print password + print_help: You will be able to print the password when it is saved. account_info: change_user: Change user document_number_label: 'Document number:' diff --git a/config/locales/es/management.yml b/config/locales/es/management.yml index 57b758042..ac1eab472 100644 --- a/config/locales/es/management.yml +++ b/config/locales/es/management.yml @@ -1,10 +1,25 @@ es: management: account: + menu: + reset_password_email: Restablecer contraseña via email + reset_password_manually: Restablecer contraseña manualmente alert: unverified_user: Solo se pueden editar cuentas de usuarios verificados show: title: Cuenta de usuario + edit: + title: 'Editar cuenta de usuario: Restablecer contraseña' + back: Atrás + password: + password: Contraseña + send_email: Enviar email para restablecer la contraseña + reset_email_send: Email enviado correctamente. + reseted: Contraseña restablecida correctamente + random: Generar contraseña aleatoria + save: Guardar + print: Imprimir contraseña + print_help: Podrás imprimir la contraseña cuando se haya guardado. account_info: change_user: Cambiar usuario document_number_label: 'Número de documento:' From e8b91356b3c67ec04f876a3143f3174906d25960 Mon Sep 17 00:00:00 2001 From: iagirre Date: Fri, 23 Mar 2018 08:57:30 +0100 Subject: [PATCH 3/3] Backend functionality to let managers update users password The back button when the user changes the password (in the print password page) redirects to the edit manually page. The routes to access password edit pages has been added, along with the ones to send reset password email and reset password manually. --- .../management/account_controller.rb | 20 +++++++++++++++++++ app/controllers/management/base_controller.rb | 4 ++++ .../document_verifications_controller.rb | 1 + .../management/users_controller.rb | 1 + config/routes/management.rb | 9 ++++++++- 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/controllers/management/account_controller.rb b/app/controllers/management/account_controller.rb index 64e0ddf4d..fc99b25cc 100644 --- a/app/controllers/management/account_controller.rb +++ b/app/controllers/management/account_controller.rb @@ -5,6 +5,26 @@ class Management::AccountController < Management::BaseController def show end + def edit + end + + def print_password + end + + def reset_password + managed_user.send_reset_password_instructions + redirect_to management_account_path, notice: t("management.account.edit.password.reset_email_send") + end + + def change_password + if managed_user.reset_password(params[:user][:password], params[:user][:password]) + session[:new_password] = params[:user][:password] + redirect_to print_password_management_account_path + else + render :edit_password_manually + end + end + private def only_verified_users diff --git a/app/controllers/management/base_controller.rb b/app/controllers/management/base_controller.rb index c7610eba9..be6dd66c0 100644 --- a/app/controllers/management/base_controller.rb +++ b/app/controllers/management/base_controller.rb @@ -44,4 +44,8 @@ class Management::BaseController < ActionController::Base def current_budget Budget.current end + + def clear_password + session[:new_password] = nil + end end diff --git a/app/controllers/management/document_verifications_controller.rb b/app/controllers/management/document_verifications_controller.rb index f59c00c8f..1b3abc398 100644 --- a/app/controllers/management/document_verifications_controller.rb +++ b/app/controllers/management/document_verifications_controller.rb @@ -40,6 +40,7 @@ class Management::DocumentVerificationsController < Management::BaseController def set_document session[:document_type] = params[:document_verification][:document_type] session[:document_number] = params[:document_verification][:document_number] + clear_password end def clean_document_number diff --git a/app/controllers/management/users_controller.rb b/app/controllers/management/users_controller.rb index d46360d89..1c8c6ed53 100644 --- a/app/controllers/management/users_controller.rb +++ b/app/controllers/management/users_controller.rb @@ -44,6 +44,7 @@ class Management::UsersController < Management::BaseController def destroy_session session[:document_type] = nil session[:document_number] = nil + clear_password end def user_without_email diff --git a/config/routes/management.rb b/config/routes/management.rb index e7a746540..167fbfbe4 100644 --- a/config/routes/management.rb +++ b/config/routes/management.rb @@ -15,7 +15,14 @@ namespace :management do end end - resource :account, controller: "account", only: [:show] + resource :account, controller: "account", only: [:show] do + get :print_password + patch :change_password + get :reset_password + get :edit_password_email + get :edit_password_manually + end + resource :session, only: [:create, :destroy] get 'sign_in', to: 'sessions#create', as: :sign_in