Skip to content

Commit

Permalink
Sign the CMS token
Browse files Browse the repository at this point in the history
  • Loading branch information
jlledom committed Nov 26, 2024
1 parent 7d9a3a2 commit a87b1a7
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 12 deletions.
28 changes: 28 additions & 0 deletions app/controllers/provider/admin/cms/visit_portal_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

class Provider::Admin::CMS::VisitPortalController < Provider::Admin::CMS::BaseController
# Encrypt the CMS token under a temporary SSO token and redirect to the Developer Portal
def with_token
cms_token = current_account.settings.cms_token!
expires_at = Time.now.utc.round + 1.minute
signature = CMS::Signature.generate(cms_token, expires_at)

redirect_to access_code_url(
host: current_account.external_domain,
signature:,
expires_at: expires_at.to_i,
access_code: current_account.site_access_code,
return_to:,
cms: cms_mode)
end

private

def return_to
params.permit(:return_to)[:return_to]
end

def cms_mode
session[:cms]
end
end
8 changes: 2 additions & 6 deletions app/helpers/cms/url_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,8 @@ def cms_published_url(page)
}.freeze

def cms_uri(page)
uri = URI.parse(access_code_url)
uri.host = page.provider.external_domain
uri.query = { return_to: page.path ? page.path : HARD_WIRED_PATHS[page.system_name],
access_code: current_account.site_access_code,
cms_token: page.provider.settings.cms_token! }.to_query
uri.port = request.port if Rails.env.development?
uri = URI.parse(provider_admin_cms_visit_portal_path)
uri.query = { return_to: page.path ? page.path : HARD_WIRED_PATHS[page.system_name]}.to_query
uri
end

Expand Down
2 changes: 1 addition & 1 deletion app/helpers/vertical_nav_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def audience_portal_items # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticC
end

items << {id: 'separator 0'} # Separator
items << {title: 'Visit Portal', path: access_code_url(host: current_account.external_domain, cms_token: current_account.settings.cms_token!, access_code: current_account.site_access_code).html_safe, target: '_blank'}
items << {title: 'Visit Portal', path: provider_admin_cms_visit_portal_path.html_safe, target: '_blank'}
items << {id: 'separator 1'} # Separator

if can?(:manage, :portal)
Expand Down
11 changes: 11 additions & 0 deletions app/lib/cms/signature.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module CMS
class Signature
def self.generate(token, expires_at)
verifier = Rails.application.message_verifier(:cms_token)
signing_options = { purpose: :cms_edit_mode, expires_at: expires_at.utc.floor }
verifier.generate(token.b, **signing_options).split("--").last
end
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@
end

end
get 'visit_portal' => 'visit_portal#with_token'
end

namespace :user do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def show
private

def cms_params
params.permit(:cms_token, :cms)
params.permit(:signature, :expires_at, :cms)
end

def return_url
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ div class="pf-c-drawer pf-m-inline #{'pf-m-expanded' unless hidden}"
div class="pf-c-drawer__actions" id="cms-toolbar-menu-right"
div class="pf-c-drawer__close"
- unless draft
a class="pf-c-button pf-m-plain" type="button" href=url_for(request.query_parameters.merge(cms_token: "")) title="Close the CMS toolbar"
a class="pf-c-button pf-m-plain" type="button" href=url_for(request.query_parameters.merge(signature: "")) title="Close the CMS toolbar"
i class="fa fa-times-circle" aria-hidden="true"

div class="pf-c-drawer__body"
Expand Down
30 changes: 27 additions & 3 deletions lib/developer_portal/lib/cms/toolbar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ def cms_toolbar
protected

def handle_cms_token
token = params.delete(:cms_token)
token = validate_and_extract_cms_token

if cms.valid_token?(token)
session[:cms_token] = token
Rails.logger.info "CMS edit mode enabled"
Rails.logger.info "CMS edit mode enabled for portal #{site_account.external_domain}"
elsif token
session[:cms_token] = nil
session[:cms] = nil
Rails.logger.info "Invalid CMS edit mode token."
Rails.logger.info "Invalid CMS edit mode signature for portal #{site_account.external_domain}"
end

if (mode = params.delete(:cms).presence)
Expand All @@ -39,6 +39,30 @@ def draft?

private

def validate_and_extract_cms_token
signature = params.delete(:signature)
return signature if signature.blank?

expires_at = Time.at(params.delete(:expires_at).to_i).utc
raise ActiveSupport::MessageVerifier::InvalidSignature unless expires_at > Time.now.utc

cms_token = site_account.settings.cms_token!
valid_signature = signature == CMS::Signature.generate(cms_token, expires_at)

raise ActiveSupport::MessageVerifier::InvalidSignature unless valid_signature

# We don't need the signature after processing, better remove it to avoid resending it with future redirections
request.query_parameters.delete(:expires_at)
request.query_parameters.delete(:signature)

cms_token
rescue StandardError
# In the case the signature is invalid or any other problem, I don't think we have to bother the client and
# BugSnag with an exception. Better return an empty token which means "Disable CMS edit mode (hide toolbar)"
flash[:error] = 'Disabling CMS edit mode due to an invalid or expired signature'
''
end

def cms_toolbar_enabled?
return false if @_exception_handled

Expand Down

0 comments on commit a87b1a7

Please sign in to comment.