canvas-lms/app/models/developer_key_account_bindi...

141 lines
4.7 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2018 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class DeveloperKeyAccountBinding < ApplicationRecord
include Workflow
workflow do
state :off
state :on
state :allow
end
belongs_to :account
belongs_to :developer_key
belongs_to :root_account, class_name: 'Account'
validates :account, :developer_key, presence: true
after_update :clear_cache_if_site_admin
after_update :update_tools!
before_save :set_root_account
scope :active_in_account, -> (account) do
where(account_id: account.account_chain_ids, workflow_state: 'on')
end
# run this once on the local shard and again on site_admin to get all avaiable dev_keys with
# tool configurations
scope :lti_1_3_tools, -> (bindings) do
bindings.joins(developer_key: :tool_configuration).
where(developer_keys: { visible: true, workflow_state: 'active' }).
eager_load(developer_key: :tool_configuration)
end
# Find a DeveloperKeyAccountBinding in order of account_ids. The search for a binding will
# be prioritized by the order of account_ids. If a binding is found for the first account
# that binding will be returned, otherwise the next account will be searched and so on.
#
# By default only bindings with a workflow set to “on” or “off” are considered. To include
# bindings with workflow state “allow” set the explicitly_set parameter to false.
#
# For example consider four accounts with ids 1, 2, 3, and 4. Accounts 2, 3, and 4 have a binding
# with the developer key. The workflow state of the binding for account 2 is "allow." The
# workflow state of the binding for account 3 is "off." The workflow state of the binding for
# account 4 is "on."
#
# find_in_account_priority([1, 2, 3, 4], developer_key.id) would return the binding for
# account 3. Account 4 is not returned because it is after account 3 in the parameters.
#
# find_in_account_priority([1, 2, 3, 4], developer_key.id, false) would return the binding for
# account 2.
def self.find_in_account_priority(account_ids, developer_key_id, explicitly_set = true)
raise 'Account ids must be integers' if account_ids.any? { |id| !id.is_a?(Integer) }
account_ids_string = "{#{account_ids.join(',')}}"
relation = DeveloperKeyAccountBinding
.joins("JOIN unnest('#{account_ids_string}'::int8[]) WITH ordinality AS i (id, ord) ON i.id=account_id")
.where(developer_key_id: developer_key_id)
.order(:ord)
relation = relation.where.not(workflow_state: 'allow') if explicitly_set
relation.take
end
def self.find_site_admin_cached(developer_key)
# Site admin bindings don't exists for non-site admin developer keys
return nil if developer_key.account_id.present?
Shard.default.activate do
MultiCache.fetch(site_admin_cache_key(developer_key)) do
GuardRail.activate(:secondary) do
binding = self.where.not(workflow_state: 'allow').find_by(
account: Account.site_admin,
developer_key: developer_key
)
binding
end
end
end
end
def self.clear_site_admin_cache(developer_key)
Shard.default.activate do
MultiCache.delete(site_admin_cache_key(developer_key))
end
end
def self.site_admin_cache_key(developer_key)
"accounts/site_admin/developer_key_account_bindings/#{developer_key.global_id}"
end
alias allowed? allow?
private
def set_root_account
self.root_account_id ||= account&.resolved_root_account_id
end
def update_tools!
if disable_tools?
developer_key.disable_external_tools!(account)
elsif enable_tools?
developer_key.enable_external_tools!(account)
elsif restore_tools?
developer_key.restore_external_tools!(account)
end
end
def enable_tools?
saved_change_to_workflow_state? && on?
end
def disable_tools?
saved_change_to_workflow_state? && off?
end
def restore_tools?
saved_change_to_workflow_state? && allowed?
end
def clear_cache_if_site_admin
self.class.clear_site_admin_cache(developer_key) if account.site_admin?
end
end