Skip to content

Commit

Permalink
Merge pull request #2599 from alphagov/manage-permissions-for-api-users
Browse files Browse the repository at this point in the history
Move API user permission management to design system
  • Loading branch information
chrisroos authored Jan 9, 2024
2 parents ca17a91 + 541c038 commit b3e2ebd
Show file tree
Hide file tree
Showing 19 changed files with 709 additions and 259 deletions.
13 changes: 13 additions & 0 deletions app/controllers/api_users/applications_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class ApiUsers::ApplicationsController < ApplicationController
layout "admin_layout"

before_action :authenticate_user!

def index
@api_user = ApiUser.find(params[:api_user_id])

authorize @api_user

@applications = @api_user.authorised_applications.merge(Doorkeeper::AccessToken.not_revoked)
end
end
49 changes: 49 additions & 0 deletions app/controllers/api_users/permissions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class ApiUsers::PermissionsController < ApplicationController
layout "admin_layout"

before_action :authenticate_user!
before_action :set_user
before_action :set_application
before_action :set_permissions

def edit
authorize @api_user
end

def update
authorize @api_user

UserUpdate.new(@api_user, build_user_update_params, current_user, user_ip_address).call

flash[:application_id] = @application.id
redirect_to api_user_applications_path(@api_user)
end

private

def update_params
params.require(:application).permit(supported_permission_ids: [])
end

def set_user
@api_user = ApiUser.find(params[:api_user_id])
end

def set_application
@application = @api_user.authorised_applications.merge(Doorkeeper::AccessToken.not_revoked).find(params[:application_id])
end

def set_permissions
@permissions = @application.sorted_supported_permissions_grantable_from_ui(include_signin: false)
end

def build_user_update_params
permissions_user_has = @api_user.supported_permissions.pluck(:id)
updatable_permissions_for_this_app = @permissions.pluck(:id)
selected_permissions = update_params[:supported_permission_ids].map(&:to_i)
permissions_to_add = updatable_permissions_for_this_app.intersection(selected_permissions)
permissions_to_remove = updatable_permissions_for_this_app.difference(selected_permissions)

{ supported_permission_ids: (permissions_user_has + permissions_to_add - permissions_to_remove).sort }
end
end
22 changes: 2 additions & 20 deletions app/controllers/api_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ApiUsersController < ApplicationController
layout "admin_layout", only: %w[index new create edit manage_tokens]

before_action :authenticate_user!
before_action :load_and_authorize_api_user, only: %i[edit manage_permissions manage_tokens update]
before_action :load_and_authorize_api_user, only: %i[edit manage_tokens]
helper_method :api_user_applications_and_permissions, :visible_applications

respond_to :html
Expand All @@ -21,8 +21,6 @@ def new

def edit; end

def manage_permissions; end

def manage_tokens; end

def create
Expand All @@ -38,17 +36,6 @@ def create
end
end

def update
if @api_user.update(api_user_params_for_update)
@api_user.application_permissions.reload
PermissionUpdater.perform_on(@api_user)

redirect_to api_users_path, notice: "Updated API user #{@api_user.email} successfully"
else
render :manage_permissions
end
end

private

def load_and_authorize_api_user
Expand All @@ -60,10 +47,6 @@ def api_user_params_for_create
sanitise(params.require(:api_user).permit(:name, :email))
end

def api_user_params_for_update
sanitise(params.require(:api_user).permit(supported_permission_ids: []))
end

def sanitise(permitted_user_params)
UserParameterSanitiser.new(
user_params: permitted_user_params.to_h,
Expand All @@ -76,7 +59,6 @@ def api_user_applications_and_permissions(user)
end

def visible_applications(user)
api_user_authorised_apps = user.authorisations.not_revoked.pluck(:application_id)
Doorkeeper::Application.includes(:supported_permissions).where(id: api_user_authorised_apps)
user.authorised_applications.merge(Doorkeeper::AccessToken.not_revoked).includes(:supported_permissions)
end
end
2 changes: 1 addition & 1 deletion app/jobs/push_user_updates_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def perform(*_args)

class << self
def perform_on(user)
user.applications_used
user.authorised_applications
.each { |application| perform_later(user.uid, application.id) }
end
end
Expand Down
6 changes: 1 addition & 5 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class User < ApplicationRecord
validate :organisation_has_mandatory_2sv, on: :create

has_many :authorisations, -> { joins(:application) }, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id
has_many :authorised_applications, -> { distinct(:application) }, through: :authorisations, source: :application
has_many :application_permissions, -> { joins(:application) }, class_name: "UserApplicationPermission", inverse_of: :user, dependent: :destroy
has_many :supported_permissions, -> { joins(:application) }, through: :application_permissions
has_many :batch_invitations
Expand Down Expand Up @@ -185,11 +186,6 @@ def permissions_synced!(application)
application_permissions.where(application_id: application.id).update_all(last_synced_at: Time.zone.now)
end

def authorised_applications
authorisations.group_by(&:application).keys
end
alias_method :applications_used, :authorised_applications

def revoke_all_authorisations
authorisations.not_revoked.find_each(&:revoke)
end
Expand Down
1 change: 0 additions & 1 deletion app/policies/api_user_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ def new?
alias_method :edit?, :new?
alias_method :update?, :new?
alias_method :revoke?, :new?
alias_method :manage_permissions?, :new?
alias_method :manage_tokens?, :new?
alias_method :suspension?, :new?

Expand Down
10 changes: 6 additions & 4 deletions app/views/account/applications/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
%>

<% if flash[:application_id] %>
<%= render "govuk_publishing_components/components/success_alert", {
message: "Permissions updated",
description: message_for_success(flash[:application_id]),
} %>
<% content_for(:custom_alerts) do %>
<%= render "govuk_publishing_components/components/success_alert", {
message: "Permissions updated",
description: message_for_success(flash[:application_id]),
} %>
<% end %>
<% end %>

<table class="govuk-table">
Expand Down
60 changes: 60 additions & 0 deletions app/views/api_users/applications/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<% content_for :title_caption, "Manage API users" %>
<% content_for :title, "#{@api_user.name}'s applications" %>

<% content_for :breadcrumbs,
render("govuk_publishing_components/components/breadcrumbs", {
collapse_on_mobile: true,
breadcrumbs: [
{
title: "Dashboard",
url: root_path,
},
{
title: "API users",
url: api_users_path,
},
{
title: @api_user.name,
url: edit_api_user_path(@api_user),
},
{
title: "#{@api_user.name}'s applications",
}
]
})
%>

<% if flash[:application_id] %>
<% content_for(:custom_alerts) do %>
<%= render "govuk_publishing_components/components/success_alert", {
message: "Permissions updated",
description: message_for_success(flash[:application_id], @api_user),
} %>
<% end %>
<% end %>

<table class="govuk-table">
<caption class="govuk-table__caption govuk-table__caption--m">Apps <%= @api_user.name %> has access to</caption>
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header govuk-!-width-one-quarter">Name</th>
<th scope="col" class="govuk-table__header govuk-!-width-one-third">Description</th>
<th scope="col" class="govuk-table__header"><span class="govuk-visually-hidden">Permissions</span></th>
</tr>
</thead>
<tbody class="govuk-table__body">
<% @applications.each do |application| %>
<tr class="govuk-table__row">
<td class="govuk-table__cell"><%= application.name %></td>
<td class="govuk-table__cell"><%= application.description %></td>
<td class="govuk-table__cell govuk-!-text-align-right">
<% unless application.sorted_supported_permissions_grantable_from_ui(include_signin: false).empty? %>
<%= link_to edit_api_user_application_permissions_path(@api_user, application), class: "govuk-link" do %>
Update permissions<span class="govuk-visually-hidden"> for <%= application.name %></span>
<% end %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
2 changes: 1 addition & 1 deletion app/views/api_users/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<div class="govuk-button-group">
<%= render "govuk_publishing_components/components/button", {
text: "Manage permissions",
href: manage_permissions_api_user_path(@api_user)
href: api_user_applications_path(@api_user)
} %>
<%= render "govuk_publishing_components/components/button", {
text: "Manage tokens",
Expand Down
81 changes: 0 additions & 81 deletions app/views/api_users/manage_permissions.html.erb

This file was deleted.

47 changes: 47 additions & 0 deletions app/views/api_users/permissions/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<% content_for :title_caption, "Manage API users" %>
<% content_for :title, "Update #{@api_user.name}'s permissions for #{@application.name}" %>

<% content_for :breadcrumbs,
render("govuk_publishing_components/components/breadcrumbs", {
collapse_on_mobile: true,
breadcrumbs: [
{
title: "Dashboard",
url: root_path,
},
{
title: "API users",
url: api_users_path,
},
{
title: @api_user.name,
url: edit_api_user_path(@api_user),
},
{
title: "#{@api_user.name}'s applications",
url: api_user_applications_path(@api_user),
},
{
title: "Update #{@api_user.name}'s permissions for #{@application.name}",
}
]
})
%>

<%= form_tag api_user_application_permissions_path(@api_user, @application), method: :patch do |f| %>
<%= render "govuk_publishing_components/components/checkboxes", {
name: "application[supported_permission_ids][]",
heading: "Permissions",
items: @permissions.map { |permission| { label: permission.name, value: permission.id, checked: @api_user.has_permission?(permission) } },
} %>

<%= hidden_field_tag "application[supported_permission_ids][]", @application.signin_permission.id, id: "checkboxes-signin" %>

<div class="govuk-button-group">
<%= render "govuk_publishing_components/components/button", {
text: "Update permissions"
} %>

<%= link_to "Cancel", api_user_applications_path(@api_user), class: "govuk-link govuk-link--no-visited-state" %>
</div>
<% end %>
10 changes: 6 additions & 4 deletions app/views/users/applications/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
%>

<% if flash[:application_id] %>
<%= render "govuk_publishing_components/components/success_alert", {
message: "Permissions updated",
description: message_for_success(flash[:application_id], @user),
} %>
<% content_for(:custom_alerts) do %>
<%= render "govuk_publishing_components/components/success_alert", {
message: "Permissions updated",
description: message_for_success(flash[:application_id], @user),
} %>
<% end %>
<% end %>

<table class="govuk-table">
Expand Down
Loading

0 comments on commit b3e2ebd

Please sign in to comment.