Browse Source

CLDC-2479 Allow deleting users (#2288)

* Add delete confirmation page

* Add status to the user

* Allow deleting user

* Refactor sign_in user in tests

* Add delete user button and update policy

* Update user policy

* Do not display deleted users as an answer option

* Update user table and details

* Do not show deleted users

* Disable delete user on production

* Update test
pull/2365/head
kosiakkatrina 9 months ago committed by GitHub
parent
commit
a65cc59713
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      app/controllers/organisations_controller.rb
  2. 13
      app/controllers/users_controller.rb
  3. 2
      app/helpers/tag_helper.rb
  4. 4
      app/helpers/user_helper.rb
  5. 6
      app/models/form/lettings/questions/created_by_id.rb
  6. 4
      app/models/form/sales/questions/created_by_id.rb
  7. 17
      app/models/user.rb
  8. 25
      app/policies/user_policy.rb
  9. 4
      app/services/feature_toggle.rb
  10. 6
      app/views/users/_user_list.html.erb
  11. 24
      app/views/users/delete_confirmation.html.erb
  12. 18
      app/views/users/show.html.erb
  13. 1
      config/locales/en.yml
  14. 2
      config/routes.rb
  15. 5
      db/migrate/20240305091927_add_discarded_at_column_to_users.rb
  16. 1
      db/schema.rb
  17. 8
      spec/features/user_spec.rb
  18. 21
      spec/models/form/lettings/questions/created_by_id_spec.rb
  19. 20
      spec/models/form/sales/questions/created_by_id_spec.rb
  20. 9
      spec/models/user_spec.rb
  21. 124
      spec/policies/user_policy_spec.rb
  22. 260
      spec/requests/users_controller_spec.rb

4
app/controllers/organisations_controller.rb

@ -46,14 +46,14 @@ class OrganisationsController < ApplicationController
end end
def users def users
organisation_users = @organisation.users.sorted_by_organisation_and_role organisation_users = @organisation.users.visible.sorted_by_organisation_and_role
unpaginated_filtered_users = filter_manager.filtered_users(organisation_users, search_term, session_filters) unpaginated_filtered_users = filter_manager.filtered_users(organisation_users, search_term, session_filters)
respond_to do |format| respond_to do |format|
format.html do format.html do
@pagy, @users = pagy(unpaginated_filtered_users) @pagy, @users = pagy(unpaginated_filtered_users)
@searched = search_term.presence @searched = search_term.presence
@total_count = @organisation.users.size @total_count = @organisation.users.visible.size
@filter_type = "users" @filter_type = "users"
if current_user.support? if current_user.support?

13
app/controllers/users_controller.rb

@ -13,7 +13,7 @@ class UsersController < ApplicationController
def index def index
redirect_to users_organisation_path(current_user.organisation) unless current_user.support? redirect_to users_organisation_path(current_user.organisation) unless current_user.support?
all_users = User.sorted_by_organisation_and_role all_users = User.visible.sorted_by_organisation_and_role
filtered_users = filter_manager.filtered_users(all_users, search_term, session_filters) filtered_users = filter_manager.filtered_users(all_users, search_term, session_filters)
@pagy, @users = pagy(filtered_users) @pagy, @users = pagy(filtered_users)
@searched = search_term.presence @searched = search_term.presence
@ -122,6 +122,16 @@ class UsersController < ApplicationController
end end
end end
def delete_confirmation
authorize @user
end
def delete
authorize @user
@user.discard!
redirect_to users_organisation_path(@user.organisation), notice: I18n.t("notification.user_deleted", name: @user.name)
end
private private
def validate_attributes def validate_attributes
@ -197,6 +207,7 @@ private
if action_name == "create" if action_name == "create"
head :unauthorized and return unless current_user.data_coordinator? || current_user.support? head :unauthorized and return unless current_user.data_coordinator? || current_user.support?
else else
render_not_found and return if @user.status == :deleted
render_not_found and return unless (current_user.organisation == @user.organisation) || current_user.support? render_not_found and return unless (current_user.organisation == @user.organisation) || current_user.support?
render_not_found and return if action_name == "edit_password" && current_user != @user render_not_found and return if action_name == "edit_password" && current_user != @user
render_not_found and return unless action_name == "show" || render_not_found and return unless action_name == "show" ||

2
app/helpers/tag_helper.rb

@ -30,7 +30,7 @@ module TagHelper
deactivated: "grey", deactivated: "grey",
deleted: "red", deleted: "red",
merged: "orange", merged: "orange",
unconfirmed: "red", unconfirmed: "blue",
}.freeze }.freeze
def status_tag(status, classes = []) def status_tag(status, classes = [])

4
app/helpers/user_helper.rb

@ -10,4 +10,8 @@ module UserHelper
def can_edit_org?(current_user) def can_edit_org?(current_user)
current_user.data_coordinator? || current_user.support? current_user.data_coordinator? || current_user.support?
end end
def delete_user_link(user)
govuk_button_link_to "Delete this user", delete_confirmation_user_path(user), warning: true
end
end end

6
app/models/form/lettings/questions/created_by_id.rb

@ -22,15 +22,15 @@ class Form::Lettings::Questions::CreatedById < ::Form::Question
[ [
( (
if log.owning_organisation if log.owning_organisation
log.owning_organisation.absorbing_organisation.present? ? log.owning_organisation&.absorbing_organisation&.users : log.owning_organisation&.users log.owning_organisation.absorbing_organisation.present? ? log.owning_organisation&.absorbing_organisation&.users&.visible : log.owning_organisation&.users&.visible
end), end),
( (
if log.managing_organisation if log.managing_organisation
log.managing_organisation.absorbing_organisation.present? ? log.managing_organisation&.absorbing_organisation&.users : log.managing_organisation.users log.managing_organisation.absorbing_organisation.present? ? log.managing_organisation&.absorbing_organisation&.users&.visible : log.managing_organisation.users&.visible
end), end),
].flatten ].flatten
else else
current_user.organisation.users current_user.organisation.users.visible
end.uniq.compact end.uniq.compact
users.each_with_object(ANSWER_OPTS.dup) do |user, hsh| users.each_with_object(ANSWER_OPTS.dup) do |user, hsh|

4
app/models/form/sales/questions/created_by_id.rb

@ -23,11 +23,11 @@ class Form::Sales::Questions::CreatedById < ::Form::Question
[ [
( (
if log.managing_organisation if log.managing_organisation
log.managing_organisation.absorbing_organisation.present? ? log.managing_organisation&.absorbing_organisation&.users : log.managing_organisation.users log.managing_organisation.absorbing_organisation.present? ? log.managing_organisation&.absorbing_organisation&.users&.visible : log.managing_organisation.users.visible
end), end),
].flatten ].flatten
else else
log.managing_organisation.users log.managing_organisation.users.visible
end.uniq.compact end.uniq.compact
users.each_with_object(ANSWER_OPTS.dup) do |user, hsh| users.each_with_object(ANSWER_OPTS.dup) do |user, hsh|
hsh[user.id] = present_user(user) hsh[user.id] = present_user(user)

17
app/models/user.rb

@ -76,6 +76,7 @@ class User < ApplicationRecord
scope :not_signed_in, -> { where(last_sign_in_at: nil, active: true) } scope :not_signed_in, -> { where(last_sign_in_at: nil, active: true) }
scope :deactivated, -> { where(active: false) } scope :deactivated, -> { where(active: false) }
scope :active_status, -> { where(active: true).where.not(last_sign_in_at: nil) } scope :active_status, -> { where(active: true).where.not(last_sign_in_at: nil) }
scope :visible, -> { where(discarded_at: nil) }
def lettings_logs def lettings_logs
if support? if support?
@ -238,13 +239,15 @@ class User < ApplicationRecord
end end
def status def status
if active == false return :deleted if discarded_at.present?
:deactivated return :deactivated unless active
elsif confirmed? == false return :unconfirmed unless confirmed?
:unconfirmed
else :active
:active end
end
def discard!
update!(discarded_at: Time.zone.now)
end end
protected protected

25
app/policies/user_policy.rb

@ -33,4 +33,29 @@ class UserPolicy
(@current_user == @user || @current_user.data_coordinator? || @current_user.support?) && @user.active? (@current_user == @user || @current_user.data_coordinator? || @current_user.support?) && @user.active?
end end
end end
def delete_confirmation?
delete?
end
def delete?
return false unless current_user.support?
return false unless user.status == :deactivated
!has_any_logs_in_editable_collection_period && !has_signed_data_protection_agreement?
end
private
def has_any_logs_in_editable_collection_period
editable_from_date = FormHandler.instance.earliest_open_for_editing_collection_start_date
LettingsLog.where(created_by_id: user.id).after_date(editable_from_date).or(LettingsLog.where(startdate: nil, created_by_id: user.id)).any?
end
def has_signed_data_protection_agreement?
return false unless user.is_dpo? && user.organisation.data_protection_confirmed?
user.organisation.data_protection_confirmation.data_protection_officer == user
end
end end

4
app/services/feature_toggle.rb

@ -34,4 +34,8 @@ class FeatureToggle
def self.delete_location_enabled? def self.delete_location_enabled?
!Rails.env.production? !Rails.env.production?
end end
def self.delete_user_enabled?
!Rails.env.production?
end
end end

6
app/views/users/_user_list.html.erb

@ -18,6 +18,9 @@
<% row.with_cell(header: true, text: "Last logged in", html_attributes: { <% row.with_cell(header: true, text: "Last logged in", html_attributes: {
scope: "col", scope: "col",
}) %> }) %>
<% row.with_cell(header: true, text: "Status", html_attributes: {
scope: "col",
}) %>
<% end %> <% end %>
<% end %> <% end %>
<% users.each do |user| %> <% users.each do |user| %>
@ -50,7 +53,8 @@
<% end %> <% end %>
<% end %> <% end %>
<% row.with_cell(text: simple_format(org_cell(user), {}, wrapper_tag: "div")) %> <% row.with_cell(text: simple_format(org_cell(user), {}, wrapper_tag: "div")) %>
<% row.with_cell(text: user.active? ? user.last_sign_in_at&.to_formatted_s(:govuk_date) : status_tag(user.status)) %> <% row.with_cell(text: user.last_sign_in_at&.to_formatted_s(:govuk_date)) %>
<% row.with_cell(text: status_tag(user.status)) %>
<%= govuk_link_to users_path(user) do %> <%= govuk_link_to users_path(user) do %>
<span class="govuk-visually-hidden">User </span><%= user.id %> <span class="govuk-visually-hidden">User </span><%= user.id %>
<% end %> <% end %>

24
app/views/users/delete_confirmation.html.erb

@ -0,0 +1,24 @@
<% content_for :before_content do %>
<% content_for :title, "Are you sure you want to delete this user?" %>
<%= govuk_back_link(href: :back) %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<span class="govuk-caption-xl">Delete <%= @user.name %></span>
<h1 class="govuk-heading-xl">
<%= content_for(:title) %>
</h1>
<%= govuk_warning_text(text: "You will not be able to undo this action.") %>
<div class="govuk-button-group">
<%= govuk_button_to(
"Delete this user",
delete_user_path(@user),
method: :delete,
) %>
<%= govuk_button_link_to "Cancel", user_path(@user), html: { method: :get }, secondary: true %>
</div>
</div>
</div>

18
app/views/users/show.html.erb

@ -112,11 +112,18 @@
row.with_action row.with_action
end end
end %> end %>
<%= summary_list.with_row do |row| %>
<% row.with_key { "Status" } %>
<% row.with_value do %>
<%= details_html({ name: "Status", value: status_tag(@user.status), id: "status" }) %>
<% if @user.status == :deactivated && current_user.support? && !UserPolicy.new(current_user, @user).delete? %>
<span class="app-!-colour-muted">This user was active in an open or editable collection year, and cannot be deleted.</span>
<% end %>
<% end %>
<% end %>
<%= summary_list.with_row do |row| <%= summary_list.with_row do |row|
row.with_key { "Status" } row.with_key { "Last logged in" }
row.with_value { status_tag(@user.status) } row.with_value { @user.last_sign_in_at&.to_formatted_s(:govuk_date) }
row.with_action
end %> end %>
<% end %> <% end %>
@ -133,6 +140,9 @@
</span> </span>
<% end %> <% end %>
<% end %> <% end %>
<% if UserPolicy.new(current_user, @user).delete? && FeatureToggle.delete_user_enabled? %>
<%= delete_user_link(@user) %>
<% end %>
</div> </div>
</div> </div>
</div> </div>

1
config/locales/en.yml

@ -198,6 +198,7 @@ en:
other: "There are %{count} sets of duplicate logs" other: "There are %{count} sets of duplicate logs"
location_deleted: "%{postcode} has been deleted." location_deleted: "%{postcode} has been deleted."
scheme_deleted: "%{service_name} has been deleted." scheme_deleted: "%{service_name} has been deleted."
user_deleted: "%{name} has been deleted."
validations: validations:
organisation: organisation:

2
config/routes.rb

@ -129,6 +129,8 @@ Rails.application.routes.draw do
get "deactivate", to: "users#deactivate" get "deactivate", to: "users#deactivate"
get "reactivate", to: "users#reactivate" get "reactivate", to: "users#reactivate"
post "resend-invite", to: "users#resend_invite" post "resend-invite", to: "users#resend_invite"
get "delete-confirmation", to: "users#delete_confirmation"
delete "delete", to: "users#delete"
end end
end end

5
db/migrate/20240305091927_add_discarded_at_column_to_users.rb

@ -0,0 +1,5 @@
class AddDiscardedAtColumnToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :discarded_at, :datetime
end
end

1
db/schema.rb

@ -763,6 +763,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_03_19_122706) do
t.datetime "confirmation_sent_at" t.datetime "confirmation_sent_at"
t.string "unconfirmed_email" t.string "unconfirmed_email"
t.boolean "initial_confirmation_sent" t.boolean "initial_confirmation_sent"
t.datetime "discarded_at"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true

8
spec/features/user_spec.rb

@ -480,14 +480,14 @@ RSpec.describe "User Features" do
it "allows to cancel user deactivation" do it "allows to cancel user deactivation" do
click_link("No – I’ve changed my mind") click_link("No – I’ve changed my mind")
expect(page).to have_current_path("/users/#{other_user.id}") expect(page).to have_current_path("/users/#{other_user.id}")
expect(page).to have_no_content("This user has been deactivated.") assert_selector ".govuk-tag", text: /Deactivated/, count: 0
expect(page).to have_no_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_no_css(".govuk-notification-banner.govuk-notification-banner--success")
end end
it "allows to deactivate the user" do it "allows to deactivate the user" do
click_button("I’m sure – deactivate this user") click_button("I’m sure – deactivate this user")
expect(page).to have_current_path("/users/#{other_user.id}") expect(page).to have_current_path("/users/#{other_user.id}")
expect(page).to have_content("Deactivated") assert_selector ".govuk-tag", text: /Deactivated/, count: 1
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
end end
end end
@ -517,7 +517,7 @@ RSpec.describe "User Features" do
it "allows to cancel user reactivation" do it "allows to cancel user reactivation" do
click_link("No – I’ve changed my mind") click_link("No – I’ve changed my mind")
expect(page).to have_current_path("/users/#{other_user.id}") expect(page).to have_current_path("/users/#{other_user.id}")
expect(page).to have_content("Deactivated") assert_selector ".govuk-tag", text: /Deactivated/, count: 1
expect(page).to have_no_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_no_css(".govuk-notification-banner.govuk-notification-banner--success")
end end
@ -525,7 +525,7 @@ RSpec.describe "User Features" do
expect(notify_client).to receive(:send_email).with(email_address: other_user.email, template_id: User::USER_REACTIVATED_TEMPLATE_ID, personalisation:).once expect(notify_client).to receive(:send_email).with(email_address: other_user.email, template_id: User::USER_REACTIVATED_TEMPLATE_ID, personalisation:).once
click_button("I’m sure – reactivate this user") click_button("I’m sure – reactivate this user")
expect(page).to have_current_path("/users/#{other_user.id}") expect(page).to have_current_path("/users/#{other_user.id}")
expect(page).to have_no_content("This user has been deactivated.") assert_selector ".govuk-tag", text: /Deactivated/, count: 0
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
end end
end end

21
spec/models/form/lettings/questions/created_by_id_spec.rb

@ -60,6 +60,17 @@ RSpec.describe Form::Lettings::Questions::CreatedById, type: :model do
it "only displays users that belong to owning and managing organisations" do it "only displays users that belong to owning and managing organisations" do
expect(question.displayed_answer_options(lettings_log, support_user)).to eq(expected_option_for_users(managing_org_user.organisation.users + owning_org_user.organisation.users)) expect(question.displayed_answer_options(lettings_log, support_user)).to eq(expected_option_for_users(managing_org_user.organisation.users + owning_org_user.organisation.users))
end end
context "when organisation has deleted users" do
before do
create(:user, name: "Deleted user", discarded_at: Time.zone.yesterday, organisation: owning_org_user.organisation)
create(:user, name: "Deleted managing user", discarded_at: Time.zone.yesterday, organisation: managing_org_user.organisation)
end
it "does not display deleted users" do
expect(question.displayed_answer_options(lettings_log, support_user)).to eq(expected_option_for_users(managing_org_user.organisation.users.visible + owning_org_user.organisation.users.visible))
end
end
end end
end end
@ -77,6 +88,16 @@ RSpec.describe Form::Lettings::Questions::CreatedById, type: :model do
it "only displays users that belong user's org" do it "only displays users that belong user's org" do
expect(question.displayed_answer_options(lettings_log, data_coordinator)).to eq(expected_option_for_users(data_coordinator.organisation.users)) expect(question.displayed_answer_options(lettings_log, data_coordinator)).to eq(expected_option_for_users(data_coordinator.organisation.users))
end end
context "when organisation has deleted users" do
before do
create(:user, name: "Deleted user", discarded_at: Time.zone.yesterday, organisation: data_coordinator.organisation)
end
it "does not display deleted users" do
expect(question.displayed_answer_options(lettings_log, data_coordinator)).to eq(expected_option_for_users(data_coordinator.organisation.users.visible))
end
end
end end
end end
end end

20
spec/models/form/sales/questions/created_by_id_spec.rb

@ -57,6 +57,16 @@ RSpec.describe Form::Sales::Questions::CreatedById, type: :model do
it "only displays users that belong to the managing organisation" do it "only displays users that belong to the managing organisation" do
expect(question.displayed_answer_options(sales_log, support_user)).to eq(expected_option_for_users(owning_org_user.organisation.users)) expect(question.displayed_answer_options(sales_log, support_user)).to eq(expected_option_for_users(owning_org_user.organisation.users))
end end
context "when organisation has deleted users" do
before do
create(:user, name: "Deleted user", discarded_at: Time.zone.yesterday, organisation: owning_org_user.organisation)
end
it "does not display deleted users" do
expect(question.displayed_answer_options(sales_log, support_user)).to eq(expected_option_for_users(owning_org_user.organisation.users.visible))
end
end
end end
end end
@ -78,6 +88,16 @@ RSpec.describe Form::Sales::Questions::CreatedById, type: :model do
it "only displays users that belong to managing organisation" do it "only displays users that belong to managing organisation" do
expect(question.displayed_answer_options(sales_log, data_coordinator)).to eq(expected_option_for_users(owning_org_user.organisation.users)) expect(question.displayed_answer_options(sales_log, data_coordinator)).to eq(expected_option_for_users(owning_org_user.organisation.users))
end end
context "when organisation has deleted users" do
before do
create(:user, name: "Deleted user", discarded_at: Time.zone.yesterday, organisation: owning_org_user.organisation)
end
it "does not display deleted users" do
expect(question.displayed_answer_options(sales_log, data_coordinator)).to eq(expected_option_for_users(owning_org_user.organisation.users.visible))
end
end
end end
end end
end end

9
spec/models/user_spec.rb

@ -517,5 +517,14 @@ RSpec.describe User, type: :model do
expect(user.status).to eq(:active) expect(user.status).to eq(:active)
end end
context "when the user is deleted" do
let(:user) { create(:user, discarded_at: Time.zone.yesterday) }
it "returns the status of the user" do
user.destroy!
expect(user.status).to eq(:deleted)
end
end
end end
end end

124
spec/policies/user_policy_spec.rb

@ -99,5 +99,129 @@ RSpec.describe UserPolicy do
expect(policy).to permit(support, data_provider) expect(policy).to permit(support, data_provider)
end end
end end
permissions :delete? do
context "with active user" do
let(:user) { create(:user, last_sign_in_at: Time.zone.yesterday) }
it "does not allow deleting a user as a provider" do
expect(user.status).to eq(:active)
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "does not allow deleting a user as a support user" do
expect(policy).not_to permit(support, user)
end
end
context "with unconfirmed user" do
let(:user) { create(:user) }
before do
user.confirmed_at = nil
user.save!(validate: false)
end
it "does not allow deleting a user as a provider" do
expect(user.status).to eq(:unconfirmed)
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "does not allow deleting a user as a support user" do
expect(policy).not_to permit(support, user)
end
end
context "with deactivated user" do
let(:user) { create(:user, active: false) }
before do
Timecop.freeze(Time.utc(2024, 4, 10))
log = create(:lettings_log, owning_organisation: user.organisation, created_by: user)
log.startdate = Time.zone.local(2022, 10, 10)
log.save!(validate: false)
end
after do
Timecop.unfreeze
end
context "and associated logs in editable collection period" do
before do
create(:lettings_log, :sh, owning_organisation: user.organisation, created_by: user, startdate: Time.zone.local(2024, 4, 9))
end
it "does not allow deleting a user as a provider" do
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "does not allow deleting a user as a support user" do
expect(policy).not_to permit(support, user)
end
end
context "and no associated logs in editable collection period" do
it "does not allow deleting a user as a provider" do
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "allows deleting a user as a support user" do
expect(policy).to permit(support, user)
end
end
context "and user is the DPO that has signed the agreement" do
let(:user) { create(:user, active: false, is_dpo: true) }
before do
user.organisation.data_protection_confirmation.update!(data_protection_officer: user)
end
it "does not allow deleting a user as a provider" do
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "does not allow deleting a user as a support user" do
expect(policy).not_to permit(support, user)
end
end
context "and user is the DPO that hasn't signed the agreement" do
let(:user) { create(:user, active: false, is_dpo: true) }
it "does not allow deleting a user as a provider" do
expect(policy).not_to permit(data_provider, user)
end
it "does not allow allows deleting a user as a coordinator" do
expect(policy).not_to permit(data_coordinator, user)
end
it "allows deleting a user as a support user" do
expect(policy).to permit(support, user)
end
end
end
end
end end
# rubocop:enable RSpec/RepeatedExample # rubocop:enable RSpec/RepeatedExample

260
spec/requests/users_controller_spec.rb

@ -103,13 +103,30 @@ RSpec.describe UsersController, type: :request do
expect(response).to redirect_to(new_user_session_path) expect(response).to redirect_to(new_user_session_path)
end end
end end
describe "#delete-confirmation" do
it "redirects to the sign in page" do
get "/users/#{user.id}/delete-confirmation"
expect(response).to redirect_to("/account/sign-in")
end
end
describe "#delete" do
it "redirects to the sign in page" do
delete "/users/#{user.id}/delete"
expect(response).to redirect_to("/account/sign-in")
end
end
end end
context "when user is signed in as a data provider" do context "when user is signed in as a data provider" do
before do
sign_in user
end
describe "#show" do describe "#show" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}", headers:, params: {} get "/users/#{user.id}", headers:, params: {}
end end
@ -155,7 +172,6 @@ RSpec.describe UsersController, type: :request do
let(:user) { create(:user, role: nil) } let(:user) { create(:user, role: nil) }
before do before do
sign_in user
get "/users/#{user.id}", headers:, params: {} get "/users/#{user.id}", headers:, params: {}
end end
@ -166,7 +182,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}", headers:, params: {} get "/users/#{other_user.id}", headers:, params: {}
end end
@ -223,7 +238,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit" do describe "#edit" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}/edit", headers:, params: {} get "/users/#{user.id}/edit", headers:, params: {}
end end
@ -242,7 +256,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}/edit", headers:, params: {} get "/users/#{other_user.id}/edit", headers:, params: {}
end end
@ -255,7 +268,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit_password" do describe "#edit_password" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/account/edit/password", headers:, params: {} get "/account/edit/password", headers:, params: {}
end end
@ -270,7 +282,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}/edit", headers:, params: {} get "/users/#{other_user.id}/edit", headers:, params: {}
end end
@ -283,7 +294,6 @@ RSpec.describe UsersController, type: :request do
describe "#update" do describe "#update" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -313,7 +323,6 @@ RSpec.describe UsersController, type: :request do
context "when the update fails to persist" do context "when the update fails to persist" do
before do before do
sign_in user
allow(User).to receive(:find_by).and_return(user) allow(User).to receive(:find_by).and_return(user)
allow(user).to receive(:update).and_return(false) allow(user).to receive(:update).and_return(false)
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
@ -328,7 +337,6 @@ RSpec.describe UsersController, type: :request do
let(:params) { { id: other_user.id, user: { name: new_name } } } let(:params) { { id: other_user.id, user: { name: new_name } } }
before do before do
sign_in user
patch "/users/#{other_user.id}", headers:, params: patch "/users/#{other_user.id}", headers:, params:
end end
@ -345,7 +353,6 @@ RSpec.describe UsersController, type: :request do
end end
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -368,10 +375,6 @@ RSpec.describe UsersController, type: :request do
end end
let(:request) { post "/users/", headers:, params: } let(:request) { post "/users/", headers:, params: }
before do
sign_in user
end
it "does not invite a new user" do it "does not invite a new user" do
expect { request }.not_to change(User, :count) expect { request }.not_to change(User, :count)
end end
@ -381,17 +384,37 @@ RSpec.describe UsersController, type: :request do
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
end end
end end
describe "#delete-confirmation" do
before do
get "/users/#{user.id}/delete-confirmation"
end
it "returns 401 unauthorized" do
expect(response).to have_http_status(:unauthorized)
end
end
describe "#delete" do
before do
delete "/users/#{user.id}/delete"
end
it "returns 401 unauthorized" do
expect(response).to have_http_status(:unauthorized)
end
end
end end
context "when user is signed in as a data coordinator" do context "when user is signed in as a data coordinator" do
let(:user) { create(:user, :data_coordinator, email: "coordinator@example.com", organisation: create(:organisation, :without_dpc)) } let(:user) { create(:user, :data_coordinator, email: "coordinator@example.com", organisation: create(:organisation, :without_dpc)) }
let!(:other_user) { create(:user, organisation: user.organisation, name: "filter name", email: "filter@example.com") } let!(:other_user) { create(:user, organisation: user.organisation, name: "filter name", email: "filter@example.com") }
describe "#index" do before do
before do sign_in user
sign_in user end
end
describe "#index" do
context "when there are no url params" do context "when there are no url params" do
before do before do
get "/users", headers:, params: {} get "/users", headers:, params: {}
@ -532,7 +555,6 @@ RSpec.describe UsersController, type: :request do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
sign_in user
get "/users", headers:, params: {} get "/users", headers:, params: {}
end end
@ -544,7 +566,6 @@ RSpec.describe UsersController, type: :request do
describe "#show" do describe "#show" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}", headers:, params: {} get "/users/#{user.id}", headers:, params: {}
end end
@ -579,12 +600,15 @@ RSpec.describe UsersController, type: :request do
it "does not allow resending invitation emails" do it "does not allow resending invitation emails" do
expect(page).not_to have_button("Resend invite link") expect(page).not_to have_button("Resend invite link")
end end
it "does not allow deleting the the user" do
expect(page).not_to have_link("Delete this user", href: "/users/#{user.id}/delete-confirmation")
end
end end
end end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}", headers:, params: {} get "/users/#{other_user.id}", headers:, params: {}
end end
@ -622,7 +646,7 @@ RSpec.describe UsersController, type: :request do
end end
it "shows if user is not active" do it "shows if user is not active" do
expect(page).to have_content("Deactivated") assert_select ".govuk-tag", text: /Deactivated/, count: 1
end end
it "allows reactivating the user" do it "allows reactivating the user" do
@ -652,7 +676,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit" do describe "#edit" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}/edit", headers:, params: {} get "/users/#{user.id}/edit", headers:, params: {}
end end
@ -673,7 +696,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}/edit", headers:, params: {} get "/users/#{other_user.id}/edit", headers:, params: {}
end end
@ -706,7 +728,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit_password" do describe "#edit_password" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/account/edit/password", headers:, params: {} get "/account/edit/password", headers:, params: {}
end end
@ -720,10 +741,6 @@ RSpec.describe UsersController, type: :request do
end end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do
sign_in user
end
it "there is no route" do it "there is no route" do
expect { expect {
get "/users/#{other_user.id}/password/edit", headers:, params: {} get "/users/#{other_user.id}/password/edit", headers:, params: {}
@ -735,7 +752,6 @@ RSpec.describe UsersController, type: :request do
describe "#update" do describe "#update" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -770,7 +786,6 @@ RSpec.describe UsersController, type: :request do
end end
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -782,10 +797,6 @@ RSpec.describe UsersController, type: :request do
end end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do
sign_in user
end
context "when the user is part of the same organisation as the current user" do context "when the user is part of the same organisation as the current user" do
it "updates the user" do it "updates the user" do
expect { patch "/users/#{other_user.id}", headers:, params: } expect { patch "/users/#{other_user.id}", headers:, params: }
@ -871,7 +882,6 @@ RSpec.describe UsersController, type: :request do
let(:params) { { id: other_user.id, user: { name: new_name } } } let(:params) { { id: other_user.id, user: { name: new_name } } }
before do before do
sign_in user
patch "/users/#{other_user.id}", headers:, params: patch "/users/#{other_user.id}", headers:, params:
end end
@ -884,7 +894,6 @@ RSpec.describe UsersController, type: :request do
context "when the update fails to persist" do context "when the update fails to persist" do
before do before do
sign_in user
allow(User).to receive(:find_by).and_return(user) allow(User).to receive(:find_by).and_return(user)
allow(user).to receive(:update).and_return(false) allow(user).to receive(:update).and_return(false)
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
@ -905,7 +914,6 @@ RSpec.describe UsersController, type: :request do
end end
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -977,10 +985,6 @@ RSpec.describe UsersController, type: :request do
end end
let(:request) { post "/users/", headers:, params: } let(:request) { post "/users/", headers:, params: }
before do
sign_in user
end
it "invites a new user" do it "invites a new user" do
expect { request }.to change(User, :count).by(1) expect { request }.to change(User, :count).by(1)
end end
@ -1102,10 +1106,6 @@ RSpec.describe UsersController, type: :request do
end end
describe "#new" do describe "#new" do
before do
sign_in user
end
it "cannot assign support role to the new user" do it "cannot assign support role to the new user" do
get "/users/new" get "/users/new"
expect(page).not_to have_field("user-role-support-field") expect(page).not_to have_field("user-role-support-field")
@ -1113,10 +1113,6 @@ RSpec.describe UsersController, type: :request do
end end
describe "#deactivate" do describe "#deactivate" do
before do
sign_in user
end
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
get "/users/#{user.id}/deactivate", headers:, params: {} get "/users/#{user.id}/deactivate", headers:, params: {}
@ -1143,10 +1139,6 @@ RSpec.describe UsersController, type: :request do
end end
describe "#reactivate" do describe "#reactivate" do
before do
sign_in user
end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
other_user.update!(active: false) other_user.update!(active: false)
@ -1162,6 +1154,26 @@ RSpec.describe UsersController, type: :request do
end end
end end
end end
describe "#delete-confirmation" do
before do
get "/users/#{user.id}/delete-confirmation"
end
it "returns 401 unauthorized" do
expect(response).to have_http_status(:unauthorized)
end
end
describe "#delete" do
before do
delete "/users/#{user.id}/delete"
end
it "returns 401 unauthorized" do
expect(response).to have_http_status(:unauthorized)
end
end
end end
context "when user is signed in as a support user" do context "when user is signed in as a support user" do
@ -1170,15 +1182,15 @@ RSpec.describe UsersController, type: :request do
before do before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false) allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end end
describe "#index" do describe "#index" do
let!(:other_user) { create(:user, organisation: user.organisation, name: "User 2", email: "other@example.com") } let!(:other_user) { create(:user, organisation: user.organisation, name: "User 2", email: "other@example.com") }
let!(:inactive_user) { create(:user, organisation: user.organisation, active: false, name: "User 3", email: "inactive@example.com") } let!(:inactive_user) { create(:user, organisation: user.organisation, active: false, name: "User 3", email: "inactive@example.com", last_sign_in_at: Time.zone.local(2022, 10, 10)) }
let!(:other_org_user) { create(:user, name: "User 4", email: "otherorg@otherexample.com", organisation: create(:organisation, :without_dpc)) } let!(:other_org_user) { create(:user, name: "User 4", email: "otherorg@otherexample.com", organisation: create(:organisation, :without_dpc)) }
before do before do
sign_in user
get "/users", headers:, params: {} get "/users", headers:, params: {}
end end
@ -1189,7 +1201,11 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_content(other_org_user.name) expect(page).to have_content(other_org_user.name)
end end
it "shows last logged in as deactivated for inactive users" do it "shows last logged in date for all users" do
expect(page).to have_content("10 October 2022")
end
it "shows status tag as deactivated for inactive users" do
expect(page).to have_content("Deactivated") expect(page).to have_content("Deactivated")
end end
@ -1326,7 +1342,6 @@ RSpec.describe UsersController, type: :request do
before do before do
create_list(:user, 25) create_list(:user, 25)
sign_in user
end end
context "when there is no search param" do context "when there is no search param" do
@ -1371,7 +1386,6 @@ RSpec.describe UsersController, type: :request do
describe "#show" do describe "#show" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}", headers:, params: {} get "/users/#{user.id}", headers:, params: {}
end end
@ -1396,7 +1410,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}", headers:, params: {} get "/users/#{other_user.id}", headers:, params: {}
end end
@ -1427,6 +1440,10 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_link("Deactivate user", href: "/users/#{other_user.id}/deactivate") expect(page).to have_link("Deactivate user", href: "/users/#{other_user.id}/deactivate")
end end
it "does not alow deleting the the user" do
expect(page).not_to have_link("Delete this user", href: "/users/#{other_user.id}/delete-confirmation")
end
context "when user never logged in" do context "when user never logged in" do
before do before do
other_user.update!(last_sign_in_at: nil) other_user.update!(last_sign_in_at: nil)
@ -1458,6 +1475,10 @@ RSpec.describe UsersController, type: :request do
it "allows you to resend invitation emails" do it "allows you to resend invitation emails" do
expect(page).to have_button("Resend invite link") expect(page).to have_button("Resend invite link")
end end
it "does not allow deleting the the user" do
expect(page).not_to have_link("Delete this user", href: "/users/#{other_user.id}/delete-confirmation")
end
end end
context "when user is deactivated" do context "when user is deactivated" do
@ -1467,12 +1488,39 @@ RSpec.describe UsersController, type: :request do
end end
it "shows if user is not active" do it "shows if user is not active" do
expect(page).to have_content("Deactivated") assert_select ".govuk-tag", text: /Deactivated/, count: 1
end end
it "allows reactivating the user" do it "allows reactivating the user" do
expect(page).to have_link("Reactivate user", href: "/users/#{other_user.id}/reactivate") expect(page).to have_link("Reactivate user", href: "/users/#{other_user.id}/reactivate")
end end
it "allows deleting the the user" do
expect(page).to have_link("Delete this user", href: "/users/#{other_user.id}/delete-confirmation")
end
it "does not render informative text about deleting the user" do
expect(response).to have_http_status(:ok)
expect(page).not_to have_content("This user was active in an open or editable collection year, and cannot be deleted.")
end
context "and has associated logs in editable collection period" do
before do
create(:data_protection_confirmation, organisation: other_user.organisation, confirmed: true)
create(:lettings_log, owning_organisation: other_user.organisation, created_by: other_user)
get "/users/#{other_user.id}"
end
it "does not render delete this user" do
expect(response).to have_http_status(:ok)
expect(page).not_to have_link("Delete this user", href: "/users/#{user.id}/delete-confirmation")
end
it "adds informative text about deleting the user" do
expect(response).to have_http_status(:ok)
expect(page).to have_content("This user was active in an open or editable collection year, and cannot be deleted.")
end
end
end end
end end
@ -1503,7 +1551,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit" do describe "#edit" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/users/#{user.id}/edit", headers:, params: {} get "/users/#{user.id}/edit", headers:, params: {}
end end
@ -1525,7 +1572,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do before do
sign_in user
get "/users/#{other_user.id}/edit", headers:, params: {} get "/users/#{other_user.id}/edit", headers:, params: {}
end end
@ -1581,7 +1627,6 @@ RSpec.describe UsersController, type: :request do
describe "#edit_password" do describe "#edit_password" do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
before do before do
sign_in user
get "/account/edit/password", headers:, params: {} get "/account/edit/password", headers:, params: {}
end end
@ -1595,10 +1640,6 @@ RSpec.describe UsersController, type: :request do
end end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do
sign_in user
end
it "there is no route" do it "there is no route" do
expect { expect {
get "/users/#{other_user.id}/password/edit", headers:, params: {} get "/users/#{other_user.id}/password/edit", headers:, params: {}
@ -1611,10 +1652,6 @@ RSpec.describe UsersController, type: :request do
context "when the current user matches the user ID" do context "when the current user matches the user ID" do
let(:request) { patch "/users/#{user.id}", headers:, params: } let(:request) { patch "/users/#{user.id}", headers:, params: }
before do
sign_in user
end
it "updates the user" do it "updates the user" do
request request
user.reload user.reload
@ -1726,7 +1763,6 @@ RSpec.describe UsersController, type: :request do
end end
before do before do
sign_in user
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
end end
@ -1738,10 +1774,6 @@ RSpec.describe UsersController, type: :request do
end end
context "when the current user does not match the user ID" do context "when the current user does not match the user ID" do
before do
sign_in user
end
context "when the user is part of the same organisation as the current user" do context "when the user is part of the same organisation as the current user" do
it "updates the user" do it "updates the user" do
expect { patch "/users/#{other_user.id}", headers:, params: } expect { patch "/users/#{other_user.id}", headers:, params: }
@ -1796,10 +1828,6 @@ RSpec.describe UsersController, type: :request do
let(:other_user) { create(:user) } let(:other_user) { create(:user) }
let(:params) { { id: other_user.id, user: { name: new_name } } } let(:params) { { id: other_user.id, user: { name: new_name } } }
before do
sign_in user
end
it "updates the user" do it "updates the user" do
expect { patch "/users/#{other_user.id}", headers:, params: } expect { patch "/users/#{other_user.id}", headers:, params: }
.to change { other_user.reload.name }.from(other_user.name).to(new_name) .to change { other_user.reload.name }.from(other_user.name).to(new_name)
@ -1886,7 +1914,6 @@ RSpec.describe UsersController, type: :request do
context "when the update fails to persist" do context "when the update fails to persist" do
before do before do
sign_in user
allow(User).to receive(:find_by).and_return(user) allow(User).to receive(:find_by).and_return(user)
allow(user).to receive(:update).and_return(false) allow(user).to receive(:update).and_return(false)
patch "/users/#{user.id}", headers:, params: patch "/users/#{user.id}", headers:, params:
@ -1914,10 +1941,6 @@ RSpec.describe UsersController, type: :request do
end end
let(:request) { post "/users/", headers:, params: } let(:request) { post "/users/", headers:, params: }
before do
sign_in user
end
it "invites a new user" do it "invites a new user" do
expect { request }.to change(User, :count).by(1) expect { request }.to change(User, :count).by(1)
end end
@ -1990,7 +2013,6 @@ RSpec.describe UsersController, type: :request do
describe "#new" do describe "#new" do
before do before do
sign_in user
create(:organisation, name: "other org") create(:organisation, name: "other org")
end end
@ -2018,6 +2040,68 @@ RSpec.describe UsersController, type: :request do
end end
end end
end end
describe "#delete-confirmation" do
let(:other_user) { create(:user, active: false) }
before do
get "/users/#{other_user.id}/delete-confirmation"
end
it "shows the correct title" do
expect(page.find("h1").text).to include "Are you sure you want to delete this user?"
end
it "shows a warning to the user" do
expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action")
end
it "shows a button to delete the selected user" do
expect(page).to have_selector("form.button_to button", text: "Delete this user")
end
it "the delete user button submits the correct data to the correct path" do
form_containing_button = page.find("form.button_to")
expect(form_containing_button[:action]).to eq delete_user_path(other_user)
expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete"
end
it "shows a cancel link with the correct style" do
expect(page).to have_selector("a.govuk-button--secondary", text: "Cancel")
end
it "shows cancel link that links back to the user page" do
expect(page).to have_link(text: "Cancel", href: user_path(other_user))
end
end
describe "#delete" do
let(:other_user) { create(:user, name: "User to be deleted", active: false) }
before do
delete "/users/#{other_user.id}/delete"
end
it "deletes the user" do
other_user.reload
expect(other_user.status).to eq(:deleted)
expect(other_user.discarded_at).not_to be nil
end
it "redirects to the users list and displays a notice that the user has been deleted" do
expect(response).to redirect_to users_organisation_path(other_user.organisation)
follow_redirect!
expect(page).to have_selector(".govuk-notification-banner--success")
expect(page).to have_selector(".govuk-notification-banner--success", text: "User to be deleted has been deleted.")
end
it "does not display the deleted user" do
expect(response).to redirect_to users_organisation_path(other_user.organisation)
follow_redirect!
expect(page).not_to have_link("User to be deleted")
end
end
end end
describe "title link" do describe "title link" do

Loading…
Cancel
Save