Module: PandaPal::Helpers::ControllerHelper

Extended by:
ActiveSupport::Concern
Includes:
SessionReplacement
Defined in:
lib/panda_pal/helpers/controller_helper.rb

Instance Method Summary collapse

Methods included from SessionReplacement

#current_session, #current_session_data, #link_nonce, #link_nonce_type, #link_with_session_to, #redirect_with_session_to, #save_session, #session_changed?, #session_expiration_period_minutes, #session_url_for, #url_with_session, #verify_authenticity_token

Instance Method Details

#current_lti_platformObject



15
16
17
18
19
20
21
22
23
24
# File 'lib/panda_pal/helpers/controller_helper.rb', line 15

def current_lti_platform
  return @current_lti_platform if @current_lti_platform.present?
  # TODO: (Future) This could be expanded more to take better advantage of the LTI 1.3 Multi-Tenancy model.
  if (canvas_url = current_organization&.settings&.dig(:canvas, :base_url)).present?
    @current_lti_platform ||= PandaPal::Platform::Canvas.new(canvas_url)
  end
  @current_lti_platform ||= PandaPal::Platform::Canvas.new('http://localhost:3000') if Rails.env.development?
  @current_lti_platform ||= PandaPal::Platform::CANVAS
  @current_lti_platform
end

#current_organizationObject



9
10
11
12
13
# File 'lib/panda_pal/helpers/controller_helper.rb', line 9

def current_organization
  @organization ||= PandaPal::Organization.find_by!(key: organization_key) if organization_key
  @organization ||= PandaPal::Organization.find_by(id: organization_id) if organization_id
  @organization ||= PandaPal::Organization.find_by_name(Apartment::Tenant.current)
end

#forbid_access_if_lacking_sessionObject



99
100
101
102
# File 'lib/panda_pal/helpers/controller_helper.rb', line 99

def forbid_access_if_lacking_session
  super
  safari_override
end

#lti_launch_paramsObject



26
27
28
# File 'lib/panda_pal/helpers/controller_helper.rb', line 26

def lti_launch_params
  current_session_data[:launch_params]
end

#safari_overrideObject



114
115
116
# File 'lib/panda_pal/helpers/controller_helper.rb', line 114

def safari_override
  use_secure_headers_override(:safari_override) if browser.safari?
end

#switch_tenant(organization = current_organization, &block) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/panda_pal/helpers/controller_helper.rb', line 90

def switch_tenant(organization = current_organization, &block)
  return unless organization
  raise 'This method should be called in an around_action callback' unless block_given?

  Apartment::Tenant.switch(organization.name) do
    yield
  end
end

#valid_session?Boolean

Returns:

  • (Boolean)


104
105
106
107
108
109
110
111
112
# File 'lib/panda_pal/helpers/controller_helper.rb', line 104

def valid_session?
  return false unless current_session(create_missing: false)&.persisted?
  return false unless current_organization
  return false unless current_session.panda_pal_organization_id == current_organization.id
  return false unless Apartment::Tenant.current == current_organization.name
  true
rescue SessionNonceMismatch
  false
end

#validate_launch!Object



30
31
32
33
34
35
36
37
38
# File 'lib/panda_pal/helpers/controller_helper.rb', line 30

def validate_launch!
  safari_override

  if params[:id_token].present?
    validate_v1p3_launch
  elsif params[:oauth_consumer_key].present?
    validate_v1p0_launch
  end
end

#validate_v1p0_launchObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/panda_pal/helpers/controller_helper.rb', line 40

def validate_v1p0_launch
  authorized = false
  if @organization = params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
    sanitized_params = request.request_parameters
    # These params come over with a safari-workaround launch.  The authenticator doesn't like them, so clean them out.
    safe_unexpected_params = ["full_win_launch_requested", "platform_redirect_url", "dummy_param"]
    safe_unexpected_params.each do |p|
      sanitized_params.delete(p)
    end
    authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.original_url, sanitized_params, @organization.secret)
    authorized = authenticator.valid_signature?
  end

  if !authorized
    render plain: 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
  end

  authorized
end

#validate_v1p3_launchObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/panda_pal/helpers/controller_helper.rb', line 60

def validate_v1p3_launch
  decoded_jwt = JSON::JWT.decode(params.require(:id_token), :skip_verification)
  raise JSON::JWT::VerificationFailed, 'error decoding id_token' if decoded_jwt.blank?

  client_id = decoded_jwt['aud']
  @organization = PandaPal::Organization.find_by!(key: client_id)
  raise JSON::JWT::VerificationFailed, 'Unrecognized Organization' unless @organization.present?

  decoded_jwt.verify!(current_lti_platform.public_jwks)

  params[:session_key] = params[:state]
  raise JSON::JWT::VerificationFailed, 'State is invalid' unless current_session_data[:lti_oauth_nonce] == decoded_jwt['nonce']

  jwt_verifier = PandaPal::LtiJwtValidator.new(decoded_jwt, client_id)
  raise JSON::JWT::VerificationFailed, jwt_verifier.errors unless jwt_verifier.valid?

  @decoded_lti_jwt = decoded_jwt
rescue JSON::JWT::VerificationFailed => e
  payload = Array(e.message)

  render json: {
    message: [
      { errors: payload },
      { id_token: params.require(:id_token) },
    ],
  }, status: :unauthorized

  false
end