Skip to content

Commit

Permalink
Merge pull request #8716 from alphagov/gift-week-allow-admins-to-togg…
Browse files Browse the repository at this point in the history
…le-access-limited

Allow govuk_admins to toggle access controls for access limited documents
  • Loading branch information
davidgisbey authored Jan 10, 2024
2 parents 0180405 + 63c01b7 commit 8e1468d
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 1 deletion.
76 changes: 76 additions & 0 deletions app/controllers/admin/edition_access_limited_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class Admin::EditionAccessLimitedController < Admin::BaseController
before_action :find_edition
before_action :enforce_permissions!
before_action :clean_organisation_params, only: %i[update]

def edit; end

def update
editorial_remark = edition_params.delete(:editorial_remark)
@edition.assign_attributes(edition_params)

if changed?
if editorial_remark.blank?
@edition.errors.add(:editorial_remark, "can't be blank")

render :edit
else
@edition.save!

EditorialRemark.create!(
edition: @edition,
body: "Access options updated by GDS Admin: #{editorial_remark}",
author: current_user,
created_at: Time.zone.now,
updated_at: Time.zone.now,
)

redirect_to admin_editions_path, notice: "Access updated for #{@edition.title}"
end
else
redirect_to admin_editions_path, notice: "Access updated for #{@edition.title}"
end
end

private

def find_edition
@edition = Edition.find(params[:id])
end

def enforce_permissions!
enforce_permission!(:perform_administrative_tasks, Edition)
end

def edition_params
@edition_params ||= params
.fetch(:edition, {})
.permit(
:access_limited,
:editorial_remark,
{
lead_organisation_ids: [],
supporting_organisation_ids: [],
},
)
end

def clean_organisation_params
if edition_params[:lead_organisation_ids]
edition_params[:lead_organisation_ids] = edition_params[:lead_organisation_ids].reject(&:blank?)
end
if edition_params[:supporting_organisation_ids]
edition_params[:supporting_organisation_ids] = edition_params[:supporting_organisation_ids].reject(&:blank?)
end
end

def changed?
if @edition.can_be_related_to_organisations?
@edition.changed? ||
@edition.lead_organisation_ids.map(&:to_s) != edition_params[:lead_organisation_ids] ||
@edition.supporting_organisations.map(&:id).map(&:to_s) != edition_params[:supporting_organisation_ids]
else
@edition.changed?
end
end
end
21 changes: 21 additions & 0 deletions app/helpers/admin/editions_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,25 @@ def reset_search_fields_query_string_params(user, filter_action, anchor)

filter_action + query_string_params + anchor
end

def search_results_table_actions(edition)
actions = ""
if can?(:see, edition)
actions << link_to(
sanitize("View #{tag.span(edition.title, class: 'govuk-visually-hidden')}"),
admin_edition_path(edition),
class: "govuk-link",
)
end

if can?(:perform_administrative_tasks, Edition) && edition.access_limited
actions << link_to(
sanitize("Edit access #{tag.span("for #{edition.title}", class: 'govuk-visually-hidden')}"),
edit_access_limited_admin_edition_path(edition),
class: "govuk-link",
)
end

sanitize(actions)
end
end
2 changes: 2 additions & 0 deletions app/models/admin/edition_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def editions_with_translations(locale = nil)
end

def permitted_only(requested_editions)
return requested_editions if Whitehall::Authority::Enforcer.new(@current_user, Edition).can?(:perform_administrative_tasks)

requested_editions.select do |edition|
Whitehall::Authority::Enforcer.new(@current_user, edition).can?(:see)
end
Expand Down
39 changes: 39 additions & 0 deletions app/views/admin/edition_access_limited/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<% content_for :page_title, "#{@edition.title}: Update access" %>
<% content_for :context, @edition.title %>
<% content_for :title, "Update access" %>
<% content_for :error_summary, render(Admin::ErrorSummaryComponent.new(object: @edition, parent_class: "edition")) %>
<% content_for :back_link do %>
<%= render "govuk_publishing_components/components/back_link", {
href: admin_editions_path,
} %>
<% end %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_for @edition, url: update_access_limited_admin_edition_path(@edition.id), as: :edition, method: :patch do |form| %>

<%= render "govuk_publishing_components/components/warning_text", {
text: "This page in only available to GDS Admins. Information in this document could be sensitive and access controls should only be changed at the request of the user.",
} %>

<%= render "admin/editions/organisation_fields", form: form, edition: @edition %>
<%= render "admin/editions/access_limiting_fields", form: form, edition: @edition %>

<%= render "govuk_publishing_components/components/textarea", {
label: {
text: "Editorial remark (required)",
heading_size: "l",
},
name: "edition[editorial_remark]",
id: "edition_editorial_remark",
error_items: errors_for(@edition.errors, :editorial_remark),
hint: "Please explain why this change is required",
rows: 20,
} %>

<%= render "govuk_publishing_components/components/button", {
text: "Save",
} %>
<% end %>
</div>
</div>
2 changes: 1 addition & 1 deletion app/views/admin/editions/_search_results.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
text: render(Admin::Editions::TagsComponent.new(edition)),
},
{
text: link_to(sanitize("View #{tag.span(edition.title, class: 'govuk-visually-hidden')}"), admin_edition_path(edition), class: "govuk-link"),
text: search_results_table_actions(edition),
},
]
end,
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ def redirect(path, options = { prefix: Whitehall.router_prefix })
patch :update_bypass_id
patch :update_image_display_option, controller: "case_studies"
get :confirm_destroy
get :edit_access_limited, to: "edition_access_limited#edit"
patch :update_access_limited, to: "edition_access_limited#update"
end
resources :link_check_reports
resource :unpublishing, controller: "edition_unpublishing", only: %i[edit update]
Expand Down
22 changes: 22 additions & 0 deletions docs/access_controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Access controls for editions

Publishers can limit access for a document on current editions edit page. This is used to keep sensitive data private prior to the publication
of documents.

When access is limited, only users that belong one of the lead or supporting organisations assigned to the document will be able to access it.

On 2ndline, we often get requests involving documents which have limited access. These tend to fall into 2 categories:

- A Publisher has limited the access to the document but limited it to the wrong organisation
- There is an issue/bug with a document that has limited access

Previously we have dealt with these requests by investigating the issues via console.

GDS Admins can now update access control for these documents by searching for the document on the document search page and clicking the "Edit access" link.

This page allows you to update the editions associated organisations and/or remove limited access completely. If you make changes on this
page you will be required to provide an editorial remark that will be used to audit the change.

If you need to investigate an issue with the document you can add temporarily gain access to the document by adding GDS as a supporting organisation, then removing it
once you've completed your investigation.

2 changes: 2 additions & 0 deletions lib/whitehall/authority/rules/edition_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ def not_publishing_scheduled_edition_without_authority?

def can_with_a_class?(action)
case action
when :perform_administrative_tasks
actor.gds_admin?
when :export, :confirm_export
actor.gds_admin? || actor.gds_editor? || actor.managing_editor? || actor.departmental_editor?
when :create, :see
Expand Down
175 changes: 175 additions & 0 deletions test/functional/admin/edition_access_limited_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
require "test_helper"

class Admin::EditionAccessLimitedControllerTest < ActionController::TestCase
setup do
login_as :gds_admin
end

should_be_an_admin_controller

test "GET :edit should be forbidden unless user is a GDS Admin" do
edition = create(:consultation)
login_as :gds_editor
get :edit, params: { id: edition }
assert_response :forbidden
end

view_test "GET :edit should display the correct fields" do
organisation = create(:organisation)

edition = create(
:consultation,
access_limited: true,
create_default_organisation: false,
lead_organisations: [organisation],
)

get :edit, params: { id: edition }

assert_select "form[action='#{update_access_limited_admin_edition_path(edition.id)}']" do
assert_select "input[name='edition[access_limited]'][type=checkbox][checked=checked]"
assert_select "textarea[name='edition[editorial_remark]']"

(1..4).each do |i|
assert_select "label[for=edition_lead_organisation_ids_#{i}]", text: "Lead organisation #{i}"
assert_select("#edition_lead_organisation_ids_#{i}")
end

assert_select("#edition_lead_organisation_ids_1") do
assert_select "option[selected='selected']", value: organisation.id
end

refute_select "#edition_lead_organisation_ids_5"
assert_select("#edition_supporting_organisation_ids_")
end
end

test "PATCH :update should be forbidden unless user is a GDS Admin" do
edition = create(:consultation)
login_as :gds_editor
get :edit, params: { id: edition }
assert_response :forbidden
end

test "PATCH :update should update editions organisations correctly and creates an editorial remark" do
first_organisation = create(:organisation)
second_organisation = create(:organisation)
third_organisation = create(:organisation)

edition = create(
:consultation,
access_limited: true,
create_default_organisation: false,
lead_organisations: [first_organisation],
supporting_organisations: [second_organisation],
)

editorial_remark = "Updating the organisations at the users request."

put :update,
params: {
id: edition,
edition: {
lead_organisation_ids: [second_organisation.id],
supporting_organisation_ids: [third_organisation.id],
editorial_remark:,
access_limited: "1",
},
}

assert_equal [second_organisation], edition.reload.lead_organisations
assert_equal [third_organisation], edition.supporting_organisations
assert_redirected_to admin_editions_path
assert_equal "Access updated for #{edition.title}", flash[:notice]
assert_equal "Access options updated by GDS Admin: #{editorial_remark}", edition.editorial_remarks.last.body
end

test "PATCH :update allows access_limited to be updated and creates an editorial remark" do
first_organisation = create(:organisation)
second_organisation = create(:organisation)

edition = create(
:consultation,
access_limited: true,
create_default_organisation: false,
lead_organisations: [first_organisation],
supporting_organisations: [second_organisation],
)

editorial_remark = "Removing access limited at the users request."

put :update,
params: {
id: edition,
edition: {
lead_organisation_ids: [first_organisation.id],
supporting_organisation_ids: [second_organisation.id],
editorial_remark:,
access_limited: "0",
},
}

assert_equal [first_organisation], edition.reload.lead_organisations
assert_equal [second_organisation], edition.supporting_organisations
assert_not edition.access_limited
assert_redirected_to admin_editions_path
assert_equal "Access updated for #{edition.title}", flash[:notice]
assert_equal "Access options updated by GDS Admin: #{editorial_remark}", edition.editorial_remarks.last.body
end

test "PATCH :update re-renders edit template if editorial remark is not provided" do
first_organisation = create(:organisation)
second_organisation = create(:organisation)

edition = create(
:consultation,
access_limited: true,
create_default_organisation: false,
lead_organisations: [first_organisation],
supporting_organisations: [second_organisation],
)

put :update,
params: {
id: edition,
edition: {
lead_organisation_ids: [first_organisation.id],
supporting_organisation_ids: [second_organisation.id],
editorial_remark: "",
access_limited: "0",
},
}

assert_template :edit
assert_equal ["Editorial remark can't be blank"], assigns(:edition).errors.full_messages
assert edition.reload.access_limited
end

test "PATCH :update doesn't create an editorial remark or re-render with an error when nothing has changed" do
first_organisation = create(:organisation)
second_organisation = create(:organisation)

edition = create(
:consultation,
access_limited: true,
create_default_organisation: false,
lead_organisations: [first_organisation],
supporting_organisations: [second_organisation],
)

put :update,
params: {
id: edition,
edition: {
lead_organisation_ids: [first_organisation.id],
supporting_organisation_ids: [second_organisation.id],
editorial_remark: "",
access_limited: "1",
},
}

assert_redirected_to admin_editions_path
assert_equal "Access updated for #{edition.title}", flash[:notice]
assert_equal 0, edition.editorial_remarks.count
end
end
14 changes: 14 additions & 0 deletions test/functional/admin/editions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,20 @@ class Admin::EditionsControllerTest < ActionController::TestCase
end
end

view_test "GET :index admin do not see a view link, but are given a link to edit acccess controls when an access limited edition doesn't belong to their org" do
login_as create(:gds_admin)
publication = create(:draft_publication, access_limited: true)

get :index, params: { state: :active }

assert_select ".govuk-table__row" do
assert_select ".govuk-table__header:nth-child(1)", text: publication.title
assert_select ".govuk-table__cell:nth-child(3)", text: "Draft Limited access"
assert_select ".govuk-table__cell:nth-child(4) a[href='#{edit_access_limited_admin_edition_path(publication)}']", text: "Edit access for #{publication.title}"
assert_select ".govuk-table__cell:nth-child(4) .govuk-link", text: "View #{publication.title}", count: 0
end
end

test "prevents revising of access-limited editions" do
my_organisation = create(:organisation)
other_organisation = create(:organisation)
Expand Down
Loading

0 comments on commit 8e1468d

Please sign in to comment.