Revoking a device redirected away from the storage dashboard, which closed the details modal and reloaded the page. Refreshing the affected parts instead keeps the modal open and shows the change where it happened. - Extract the card and the modal's access list into their own components, so either can be re-rendered without touching the modal's open state, and share the group summaries between the components - Derive the app's DOM ids from its client_id (the grouping key) and the device row ids from the authorization, through one helper used by both the templates and the controller - Revoke one of several devices as a Turbo Stream: remove the row and refresh the card behind the modal and the access list - Revoke an app's last device with a redirect, so the dashboard re-renders itself and grouping, sorting and the empty state stay in one place - Fade the removed row out, deferring Turbo's own removal until the animation has ended Part of #254
98 lines
3.2 KiB
Ruby
98 lines
3.2 KiB
Ruby
class Services::RsAuthsController < Services::BaseController
|
|
before_action :authenticate_user!
|
|
before_action :require_service_enabled
|
|
before_action :require_service_available
|
|
before_action :find_rs_auth, only: [:destroy]
|
|
|
|
def index
|
|
@rs_apps = current_user.remote_storage_authorizations
|
|
.includes(:web_app)
|
|
.group_by(&:client_id)
|
|
.values
|
|
.map { |auths| auths.sort_by(&:created_at).reverse }
|
|
.sort_by { |auths| app_name_for(auths.first).downcase }
|
|
end
|
|
|
|
def destroy
|
|
client_id = @auth.client_id
|
|
@auth.destroy!
|
|
|
|
remaining = current_user.remote_storage_authorizations
|
|
.where(client_id: client_id)
|
|
.order(created_at: :desc)
|
|
.includes(:web_app)
|
|
.to_a
|
|
|
|
respond_to do |format|
|
|
format.json { head :no_content }
|
|
format.html do
|
|
redirect_to apps_services_storage_url, flash: {
|
|
success: 'App authorization revoked'
|
|
}
|
|
end
|
|
format.turbo_stream { revoke_stream(client_id, remaining) }
|
|
end
|
|
end
|
|
|
|
def launch_app
|
|
auth = current_user.remote_storage_authorizations
|
|
.where(client_id: params[:client_id])
|
|
.order(created_at: :desc)
|
|
.first
|
|
http_status(:not_found) and return unless auth
|
|
|
|
user_address =
|
|
if Rails.env.development?
|
|
"#{current_user.cn}@#{request.host_with_port}"
|
|
else
|
|
current_user.address
|
|
end
|
|
|
|
# Storage-first bearer token issuance (remoteStorage spec §11): the app
|
|
# receives the address and runs the application-first flow itself.
|
|
launch_url = "#{auth.launch_url}#remotestorage=#{user_address}"
|
|
|
|
redirect_to launch_url, allow_other_host: true
|
|
end
|
|
|
|
private
|
|
|
|
# Revoking one of several devices updates the open details modal in place.
|
|
# Revoking the app's last device leaves nothing to show, so the dashboard
|
|
# re-renders itself instead: that keeps grouping, sorting and the empty
|
|
# state in one place.
|
|
def revoke_stream(client_id, remaining)
|
|
if remaining.empty?
|
|
redirect_to apps_services_storage_url, flash: {
|
|
success: 'App authorization revoked'
|
|
}
|
|
else
|
|
render turbo_stream: [
|
|
turbo_stream.remove(helpers.rs_device_dom_id(@auth)),
|
|
turbo_stream.replace(helpers.rs_app_dom_id(client_id, :card),
|
|
RsAuthCardComponent.new(authorizations: remaining)),
|
|
turbo_stream.replace(helpers.rs_app_dom_id(client_id, :access),
|
|
RsAuthAccessComponent.new(authorizations: remaining))
|
|
]
|
|
end
|
|
end
|
|
|
|
def app_name_for(auth)
|
|
auth.display_name
|
|
end
|
|
|
|
def require_service_enabled
|
|
unless current_user.service_enabled?(:remotestorage)
|
|
http_status :forbidden
|
|
end
|
|
end
|
|
|
|
def require_service_available
|
|
http_status :not_found unless Setting.remotestorage_enabled?
|
|
end
|
|
|
|
def find_rs_auth
|
|
@auth = current_user.remote_storage_authorizations.find_by(id: params[:id])
|
|
http_status :not_found unless @auth
|
|
end
|
|
end |