Skip to content

Commit

Permalink
Merge pull request #3938 from 3scale/THREESCALE-11439-signed-cms-token
Browse files Browse the repository at this point in the history
THREESCALE-11439: Sign the CMS token
  • Loading branch information
jlledom authored Nov 26, 2024
2 parents 7d9a3a2 + 80abd96 commit 31fc483
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 18 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
11 changes: 8 additions & 3 deletions features/developer_portal/cms_toolbar.feature
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Feature: CMS Toolbar
And go to the homepage
Then there should not be a CMS toolbar

Scenario: Hide the toolbar
Scenario: Hide the toolbar when seeing drafts
When they visit the developer portal in CMS mode
And follow "Draft"
And press "Toggle toolbar"
Expand All @@ -27,17 +27,22 @@ Feature: CMS Toolbar
But they press "Toggle toolbar"
And the cms toolbar should be visible

Scenario:
Scenario: Hide the toolbar when seeing published pages
When they visit the developer portal in CMS mode
And follow "Published"
And follow "Close the CMS toolbar"
Then there should not be a CMS toolbar

Scenario: Hide the toolbar when providing an expired signature
When they visit the developer portal in CMS mode with an expired signature
Then there should not be a CMS toolbar
And should see "Invalid or expired signature"

Rule: There is a John Doe admin user
Background:
When the admin user is John Doe

Scenario: An admin visist de dev portal
Scenario: An admin visits the dev portal
When they visit the developer portal in CMS mode
Then the cms toolbar should be visible
And should see "Templates used on this page"
Expand Down
10 changes: 8 additions & 2 deletions features/step_definitions/developer_portal/cms_toolbar_steps.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# frozen_string_literal: true

Given "they visit the developer portal in CMS mode" do
Given /^they visit the developer portal in CMS mode(\swith an expired signature)?/ do |token_is_expired|
cms_token = @provider.settings.cms_token!
expires_at = Time.now.utc.round - 30.seconds
expires_at += 1.minute unless token_is_expired
signature = CMS::Signature.generate(cms_token, expires_at)

visit access_code_url(host: @provider.external_domain,
cms: 'draft',
cms_token: @provider.settings.cms_token!,
expires_at: expires_at.to_i,
signature:,
access_code: @provider.site_access_code)
end

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
5 changes: 4 additions & 1 deletion test/integration/cms/toolbar_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ def setup
end

test 'CMS toolbar rendering' do
cms_token = @provider.settings.cms_token!
expires_at = Time.now.utc.round + 1.minute
signature = CMS::Signature.generate(cms_token, expires_at)
host! @provider.internal_domain

get "/?cms_token=#{@provider.settings.cms_token!}"
get "/", params: { expires_at: expires_at.to_i, signature: }
assert_response :success

get '/api_docs/login'
Expand Down

0 comments on commit 31fc483

Please sign in to comment.