-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3080 from alphagov/application-access-logs
Add admin UI to view application access events and monthly stats
- Loading branch information
Showing
12 changed files
with
367 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<% content_for :title, "#{@application.name} access log" %> | ||
<% content_for :breadcrumbs, | ||
render("govuk_publishing_components/components/breadcrumbs", { | ||
collapse_on_mobile: true, | ||
breadcrumbs: [ | ||
{ | ||
title: "Dashboard", | ||
url: root_path, | ||
}, | ||
{ | ||
title: "Applications", | ||
url: doorkeeper_applications_path, | ||
}, | ||
{ | ||
title: @application.name, | ||
url: edit_doorkeeper_application_path(@application), | ||
} | ||
] | ||
}) | ||
%> | ||
|
||
<form> | ||
<%= render "govuk_publishing_components/components/checkboxes", { | ||
name: "include_smokey_users", | ||
items: [ | ||
{ | ||
label: "Include Smokey Users", | ||
value: "true", | ||
checked: params["include_smokey_users"] == "true" | ||
} | ||
] | ||
} %> | ||
<%= render "govuk_publishing_components/components/input", { | ||
label: { | ||
text: "Year and month" | ||
}, | ||
name: "month", | ||
hint: "In YYYY-mm format", | ||
value: params["month"] | ||
} %> | ||
<div class="govuk-form-group"> | ||
<%= render "govuk_publishing_components/components/button", { | ||
text: "Submit" | ||
} %> | ||
</div> | ||
</form> | ||
|
||
<%= render "govuk_publishing_components/components/details", { | ||
title: "About this data" | ||
} do %> | ||
<p class="govuk-body"> | ||
Signon records a "successful authorization" event whenever a user uses Signon to access one of the publishing | ||
applications. This is a record of all of these events for <%= @application.name %>. | ||
</p> | ||
<p class="govuk-body"> | ||
Applications cache authentications for around 20 hours, so if a user clicks an application multiple | ||
times a day, they may only appear in the event log once. | ||
</p> | ||
<p class="govuk-body"> | ||
<% if DateTime.current.before? DateTime.new(2025, 11, 1) # This branch can be removed after November 2025 %> | ||
Note that authorization data has only been recorded in the Signon event log since November 2023, so it is not | ||
possible to view events before that date. | ||
<% else %> | ||
Note that data in the event log in Signon is only retained for 2 years, so it is not possible to view events | ||
before that date. | ||
<% end %> | ||
</p> | ||
<% end %> | ||
<% if @logs.any? %> | ||
<%= render "components/table", { | ||
caption: pluralize(number_with_delimiter(@logs.total_count), "event"), | ||
caption_classes: "govuk-heading-m", | ||
head: [ | ||
{ text: "Time" }, | ||
{ text: "Event" }, | ||
], | ||
rows: @logs.map do |log| | ||
next if log.requires_admin? && !current_user.govuk_admin? | ||
|
||
[ | ||
{ text: formatted_date(log), format: "event-log-date" }, | ||
{ text: "#{formatted_message(log)} for #{link_to(log.user.name, log.user, class: "govuk-link")}".html_safe }, | ||
] | ||
end.compact | ||
} %> | ||
<%= paginate(@logs, theme: "gds") %> | ||
<% else %> | ||
<%= render "govuk_publishing_components/components/notice", { | ||
title: "No activity logged" | ||
} %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
app/views/doorkeeper_applications/monthly_access_stats.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<% content_for :title, "Monthly access counts to #{@application.name}" %> | ||
<% content_for :breadcrumbs, | ||
render("govuk_publishing_components/components/breadcrumbs", { | ||
collapse_on_mobile: true, | ||
breadcrumbs: [ | ||
{ | ||
title: "Dashboard", | ||
url: root_path, | ||
}, | ||
{ | ||
title: "Applications", | ||
url: doorkeeper_applications_path, | ||
}, | ||
{ | ||
title: @application.name, | ||
url: edit_doorkeeper_application_path(@application), | ||
} | ||
] | ||
}) | ||
%> | ||
|
||
|
||
<form class="govuk-form-group"> | ||
<%= render "govuk_publishing_components/components/checkboxes", { | ||
name: "include_smokey_users", | ||
items: [ | ||
{ | ||
label: "Include Smokey Users", | ||
value: "true", | ||
checked: params["include_smokey_users"] == "true" | ||
} | ||
] | ||
} %> | ||
<%= render "govuk_publishing_components/components/button", { | ||
text: "Submit" | ||
} %> | ||
</form> | ||
|
||
<%= render "govuk_publishing_components/components/details", { | ||
title: "About this data" | ||
} do %> | ||
<p class="govuk-body"> | ||
Signon records a "successful authorization" event whenever a user uses Signon to access one of the publishing | ||
applications. This is a monthly count of all of these events for <%= @application.name %>. | ||
</p> | ||
<p class="govuk-body"> | ||
Applications cache authentications for around 20 hours, so if a user clicks an application multiple | ||
times a day, they may only appear in the event log once. | ||
</p> | ||
<p class="govuk-body"> | ||
The total authorization count is the total number of events recorded in the log for the month (if a | ||
user accesses the same app multiple times, this number will increase). | ||
</p> | ||
<p class="govuk-body"> | ||
The unique users authorization count is the number of distinct users who recorded events in the log for the month | ||
(if a user accesses the same app multiple times in a month, this will only count as one unique user). | ||
</p> | ||
<p class="govuk-body"> | ||
<% if DateTime.current.before? DateTime.new(2025, 11, 1) # This branch can be removed after November 2025 %> | ||
Note that authorization data has only been recorded in the Signon event log since November 2023, so it is not | ||
possible to view events before that date. | ||
<% else %> | ||
Note that data in the event log in Signon is only retained for 2 years, so it is not possible to view events | ||
before that date. | ||
<% end %> | ||
</p> | ||
<% end %> | ||
<% if @monthly_access_stats.any? %> | ||
<%= render "components/table", { | ||
head: [ | ||
{ text: "Month" }, | ||
{ text: "Total authorization count" }, | ||
{ text: "Unique users authorization count" }, | ||
{ text: "Access logs" }, | ||
], | ||
rows: @monthly_access_stats.map do |month, total_count, unique_users_count| | ||
[ | ||
{ text: month }, | ||
{ text: total_count }, | ||
{ text: unique_users_count }, | ||
{ text: link_to("#{month} access logs", access_logs_doorkeeper_application_path(@application, month:), class: "govuk-link")}, | ||
] | ||
end | ||
} %> | ||
<% else %> | ||
<%= render "govuk_publishing_components/components/notice", { | ||
title: "No activity logged" | ||
} %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
class IndexEventLogsColumns < ActiveRecord::Migration[7.1] | ||
def change | ||
change_table :event_logs, bulk: true do | ||
add_index :event_logs, :application_id | ||
add_index :event_logs, :event_id | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
require "test_helper" | ||
|
||
class ApplicationAccessLogPageIntegrationTest < ActionDispatch::IntegrationTest | ||
setup do | ||
@application = create(:application, name: "app-name", description: "app-description") | ||
@user = create(:user, name: "Normal User") | ||
end | ||
|
||
test "users don't have permission to view account access log" do | ||
visit root_path | ||
signin_with(@user) | ||
|
||
visit access_logs_doorkeeper_application_path(@application) | ||
flash = find("div[role='alert']") | ||
assert flash.has_content?("You do not have permission to perform this action.") | ||
end | ||
|
||
context "logged in as an superadmin" do | ||
setup do | ||
visit new_user_session_path | ||
@superadmin = create(:superadmin_user) | ||
signin_with(@superadmin) | ||
end | ||
|
||
should "have permission to view account access log" do | ||
visit access_logs_doorkeeper_application_path(@application) | ||
assert_equal page.title, "app-name access log - GOV.UK Signon" | ||
end | ||
|
||
context "when there are no matching events" do | ||
should "see a message stating that there is no activity logged" do | ||
visit access_logs_doorkeeper_application_path(@application) | ||
assert_text "app-name access log" | ||
assert_text "No activity logged" | ||
end | ||
end | ||
|
||
context "when there are matching events" do | ||
setup do | ||
event_id = 47 | ||
create(:event_log, event_id:, application_id: @application.id, uid: @superadmin.uid) | ||
create(:event_log, event_id:, application_id: @application.id, uid: @user.uid) | ||
end | ||
|
||
should "see a list of events for the application" do | ||
visit access_logs_doorkeeper_application_path(@application) | ||
assert_text "#{@application.name} access log" | ||
assert_text "Successful user application authorization for #{@application.name} for #{@superadmin.name}" | ||
assert_text "Successful user application authorization for #{@application.name} for #{@user.name}" | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.