require 'browser' module PandaPal::Helpers::ControllerHelper def save_session current_session.try(:save) end def current_session @current_session ||= PandaPal::Session.find_by(session_key: session_key) if session_key @current_session ||= PandaPal::Session.new(panda_pal_organization_id: current_organization.id) end 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 def current_session_data current_session.data end def session_changed? current_session.changed? && current_session.changes[:data].present? end def validate_launch! authorized = false use_secure_headers_override(:safari_override) if browser.safari? 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 # short-circuit if we know the user is not authorized. if !authorized render plain: 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized return authorized end if cookies_need_iframe_fix? fix_iframe_cookies return false end # For safari we may have been launched temporarily full-screen by canvas. This allows us to set the session cookie. # In this case, we should make sure the session cookie is fixed and redirect back to canvas to properly launch the embedded LTI. if params[:platform_redirect_url] session[:safari_cookie_fixed] = true redirect_to params[:platform_redirect_url] return false end return authorized end 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 # Browsers that prevent 3rd party cookies by default (Safari and IE) run into problems # with CSRF handling because the Rails session cookie isn't set. To fix this, we # redirect the current page to the LTI using JavaScript, which will set the cookie, # and then immediately redirect back to Canvas. def fix_iframe_cookies if params[:safari_cookie_fix].present? session[:safari_cookie_fixed] = true redirect_to params[:return_to] else use_secure_headers_override(:safari_override) render 'panda_pal/lti/iframe_cookie_fix', layout: false end end def cookies_need_iframe_fix? browser.safari? && !request.referrer&.include?('sessionless_launch') && !session[:safari_cookie_fixed] && !params[:platform_redirect_url] end def forbid_access_if_lacking_session if cookies_need_iframe_fix? fix_iframe_cookies else render plain: 'You should do an LTI Tool Launch.', status: :unauthorized unless valid_session? end end def valid_session? [ current_session.persisted?, current_organization, current_session.panda_pal_organization_id == current_organization.id, Apartment::Tenant.current == current_organization.name ].all? end private def organization_key params[:oauth_consumer_key] || session[:organization_key] end def organization_id params[:organization_id] end def session_key params[:session_key] || session_key_header || flash[:session_key] || session[:session_key] end def session_key_header if match = request.headers['Authorization'].try(:match, /token=(.+)/) match[1] end end end