module CanvasSync::Concerns module AbilityHelper extend ActiveSupport::Concern # PandaPal Domain def panda_pal_session @panda_pal_session ||= @controller.current_session&.data || {} end def rails_session @rails_session ||= @controller.session end def is_lti_launch? panda_pal_session.present? end def launch_params @launch_params ||= panda_pal_session[:launch_params] || {} end def get_lti_cust_param(key) nkey = key.gsub(/^custom_/, '') launch_params.dig("https://purl.imsglobal.org/spec/lti/claim/custom", nkey) || launch_params[nkey] || launch_params["custom_#{nkey}"] end def lti_launch_placement launch_params['https://www.instructure.com/placement'] || launch_params[:launch_type] end def canvas_role_labels labels = get_lti_cust_param('custom_canvas_role') labels.is_a?(String) ? labels.split(',') : [] end def lti_roles @lti_roles ||= RoleStore.new(launch_params["https://purl.imsglobal.org/spec/lti/claim/roles"] || launch_params['ext_roles'] || '') end def canvas_site_admin? lti_roles.system_roles.include?("sys_admin") end class RoleStore ContextTypeURN = 'urn:lti:context-type:ims/lis/'.freeze ROLE_TYPES = [ { type: "system", urn: "urn:lti:sysrole:ims/lis/", url: "http://purl.imsglobal.org/vocab/lis/v2/system/person#" }, { type: "institution", urn: "urn:lti:instrole:ims/lis/", url: "http://purl.imsglobal.org/vocab/lis/v2/institution/person#" }, { type: "context", urn: "urn:lti:role:ims/lis/", url: "http://purl.imsglobal.org/vocab/lis/v2/membership#" }, ] attr_accessor :roles, :context_types, :other_roles def initialize(roles = '') roles = roles.split(',') if roles.is_a?(String) @roles = roles || [] @context_types = [] @other_roles = [] @parsed_roles = HashWithIndifferentAccess.new map_roles end def system_roles; @parsed_roles[:system] || []; end def institution_roles; @parsed_roles[:institution] || []; end def context_roles; @parsed_roles[:context] || []; end def to_h { 'roles' => roles, 'context_type' => context_types, 'system_roles' => system_roles, 'institution_roles' => institution_roles, 'context_roles' => context_roles } end protected def map_roles roles.each do |role| if role.downcase.include?(ContextTypeURN) context_types << clean_role(ContextTypeURN, role) elsif (simple_role, type = match_role(role)).present? (@parsed_roles[type[:type]] ||= []) << simple_role else other_roles << role end end end def match_role(role) ROLE_TYPES.each do |rt| # LIS V1 if role.downcase.start_with?(rt[:urn]) return clean_role(ContextTypeURN, role), rt end # LIS V2 if role.downcase.start_with?(rt[:url]) return role.split('#')[1].underscore, rt end end nil end def clean_role(urn_prefix, role) role.gsub(/#{Regexp.quote(urn_prefix)}/i, '').gsub('/', '').underscore end end # Middle Domain def launch_context @launch_context ||= begin if lti_launch_placement == "global_navigation" :global elsif get_lti_cust_param('custom_canvas_course_id').present? ::Course.find_by(canvas_id: get_lti_cust_param('custom_canvas_course_id')) else ::Account.find_by(canvas_id: get_lti_cust_param('custom_canvas_account_id')) end end end def launch_account @launch_account ||= launch_context.respond_to?(:account) ? launch_context.account : ::Account.find_by(canvas_id: get_lti_cust_param('custom_canvas_account_id')) end def cache_on_session(key, &blk) panda_pal_session[:ability_cache] ||= {} panda_pal_session[:ability_cache][key] ||= blk.call end # CanvasSync Domain def canvas_permissions panda_pal_session[:canvas_permissions] ||= ::Role.role_permissions(canvas_roles) end def canvas_roles @canvas_roles ||= Role.for_labels(canvas_role_labels, launch_account) end def canvas_root_account_roles role_labels = if "::Admin".safe_constantize && ::Admin < ::ActiveRecord::Base adm_query = ::Admin.where(canvas_account_id: current_organization.canvas_account_id, workflow_state: "active") adm_query.pluck(:role_name) else Rails.cache.fetch([self.class.name, "RootAccountAdminLinks", canvas_user_id], expires_in: 1.hour) do admin_entries = canvas_sync_client.account_admins('self', user_id: [canvas_user_id]) admin_entries = admin_entries.select{|ent| ent[:workflow_state] == 'active' } admin_entries.map{|ent| ent[:role] } end end ::Role.for_labels(role_labels, ::Account.find_by(canvas_parent_account_id: nil)) end def canvas_account_roles canvas_roles.where(base_role_type: 'AccountMembership') end def canvas_course_roles canvas_roles.where.not(base_role_type: 'AccountMembership') end def canvas_super_user? cache_on_session(:canvas_super_user?) do canvas_site_admin? || canvas_root_account_roles.map(&:label).include?("Account Admin") end end def canvas_user_id user&.canvas_id end end end