save context_id on lti launch
fixes: PS-1538 **test plan configure and launch lti tool, upon lti tool launch the lti_context_id for user should be set, and if course launch the lti_context_id on course object, if account launch, then lti_context_id on account. Once these are set, api calls to the corresponding object can be made using the syntax lti_context_id:id Change-Id: Icdf02e4f99691be417c024adb2a2751ba2aa9335 Reviewed-on: https://gerrit.instructure.com/35380 Reviewed-by: Brad Humphrey <brad@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Adam Phillipps <adam@instructure.com> QA-Review: Adam Phillipps <adam@instructure.com>
This commit is contained in:
parent
0708f5bbb7
commit
de263055ce
|
@ -29,7 +29,7 @@ class Account < ActiveRecord::Base
|
|||
:enable_user_notes, :allowed_services, :turnitin_pledge, :turnitin_comments,
|
||||
:turnitin_account_id, :allow_sis_import, :sis_source_id, :equella_endpoint,
|
||||
:settings, :uuid, :default_locale, :default_user_storage_quota, :turnitin_host,
|
||||
:created_by_id, :lti_guid, :default_group_storage_quota
|
||||
:created_by_id, :lti_guid, :default_group_storage_quota, :lti_context_id
|
||||
]
|
||||
|
||||
EXPORTABLE_ASSOCIATIONS = [
|
||||
|
|
|
@ -541,14 +541,36 @@ class ContextExternalTool < ActiveRecord::Base
|
|||
|
||||
def self.opaque_identifier_for(asset, shard)
|
||||
shard.activate do
|
||||
str = asset.asset_string.to_s
|
||||
raise "Empty value" if str.blank?
|
||||
Canvas::Security.hmac_sha1(str, shard.settings[:encryption_key])
|
||||
lti_context_id = context_id_for(asset, shard)
|
||||
set_asset_context_id(asset, lti_context_id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.set_asset_context_id(asset, context_id)
|
||||
lti_context_id = context_id
|
||||
if asset.respond_to?('lti_context_id')
|
||||
if asset.new_record?
|
||||
asset.lti_context_id = context_id
|
||||
else
|
||||
asset.reload unless asset.lti_context_id?
|
||||
unless asset.lti_context_id
|
||||
asset.lti_context_id = context_id
|
||||
asset.save!
|
||||
end
|
||||
lti_context_id = asset.lti_context_id
|
||||
end
|
||||
end
|
||||
lti_context_id
|
||||
end
|
||||
|
||||
def self.context_id_for(asset, shard)
|
||||
str = asset.asset_string.to_s
|
||||
raise "Empty value" if str.blank?
|
||||
Canvas::Security.hmac_sha1(str, shard.settings[:encryption_key])
|
||||
end
|
||||
|
||||
def tool_setting(setting, hash, *keys)
|
||||
if !hash || !hash.is_a?(Hash)
|
||||
settings.delete setting
|
||||
|
|
|
@ -74,7 +74,7 @@ class Course < ActiveRecord::Base
|
|||
:created_at, :updated_at, :show_public_context_messages, :syllabus_body, :allow_student_forum_attachments, :default_wiki_editing_roles, :wiki_id, :allow_student_organized_groups,
|
||||
:course_code, :default_view, :root_account_id, :enrollment_term_id, :sis_source_id, :sis_batch_id, :show_all_discussion_entries, :open_enrollment, :storage_quota,
|
||||
:tab_configuration, :allow_wiki_comments, :turnitin_comments, :self_enrollment, :license, :indexed, :restrict_enrollments_to_course_dates, :template_course_id,
|
||||
:locale, :settings, :replacement_course_id, :public_description, :self_enrollment_code, :self_enrollment_limit, :abstract_course_id, :course_account_associations
|
||||
:locale, :settings, :replacement_course_id, :public_description, :self_enrollment_code, :self_enrollment_limit, :abstract_course_id, :course_account_associations, :lti_context_id
|
||||
]
|
||||
|
||||
EXPORTABLE_ASSOCIATIONS = [
|
||||
|
|
|
@ -33,7 +33,7 @@ class User < ActiveRecord::Base
|
|||
:id, :name, :sortable_name, :workflow_state, :time_zone, :uuid, :created_at, :updated_at, :visibility, :avatar_image_url, :avatar_image_source, :avatar_image_updated_at,
|
||||
:phone, :school_name, :school_position, :short_name, :deleted_at, :show_user_services, :gender, :page_views_count, :unread_inbox_items_count, :reminder_time_for_due_dates,
|
||||
:reminder_time_for_grading, :storage_quota, :visible_inbox_types, :last_user_note, :subscribe_to_emails, :features_used, :preferences, :avatar_state, :locale, :browser_locale,
|
||||
:unread_conversations_count, :public, :birthdate, :otp_communication_channel_id, :initial_enrollment_type, :crocodoc_id, :last_logged_out
|
||||
:unread_conversations_count, :public, :birthdate, :otp_communication_channel_id, :initial_enrollment_type, :crocodoc_id, :last_logged_out, :lti_context_id
|
||||
]
|
||||
|
||||
EXPORTABLE_ASSOCIATIONS = [
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
class AddLtiContextIdToAccountsCoursesUsers < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :accounts, :lti_context_id, :string
|
||||
add_column :courses, :lti_context_id, :string
|
||||
add_column :users, :lti_context_id, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :accounts, :lti_context_id
|
||||
remove_column :courses, :lti_context_id
|
||||
remove_column :users, :lti_context_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
class AddUniqueIndexOnLtiContextId < ActiveRecord::Migration
|
||||
tag :postdeploy
|
||||
disable_ddl_transaction!
|
||||
|
||||
def self.up
|
||||
add_index :accounts, :lti_context_id, :unique => true, algorithm: :concurrently
|
||||
add_index :courses, :lti_context_id, :unique => true, algorithm: :concurrently
|
||||
add_index :users, :lti_context_id, :unique => true, algorithm: :concurrently
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_index :accounts, :lti_context_id
|
||||
remove_index :courses, :lti_context_id
|
||||
remove_index :users, :lti_context_id
|
||||
end
|
||||
end
|
15
lib/api.rb
15
lib/api.rb
|
@ -63,7 +63,6 @@ module Api
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
find_params = Api.sis_find_params_for_collection(collection, ids, options[:account] || @domain_root_account, @current_user)
|
||||
return [] if find_params == :not_found
|
||||
find_params[:limit] = options[:limit] unless options[:limit].nil?
|
||||
|
@ -91,7 +90,7 @@ module Api
|
|||
|
||||
SIS_MAPPINGS = {
|
||||
'courses' =>
|
||||
{ :lookups => { 'sis_course_id' => 'sis_source_id', 'id' => 'id', 'sis_integration_id' => 'integration_id' },
|
||||
{ :lookups => { 'sis_course_id' => 'sis_source_id', 'id' => 'id', 'sis_integration_id' => 'integration_id', 'lti_context_id' => 'lti_context_id' },
|
||||
:is_not_scoped_to_account => ['id'].to_set,
|
||||
:scope => 'root_account_id' },
|
||||
'enrollment_terms' =>
|
||||
|
@ -99,13 +98,13 @@ module Api
|
|||
:is_not_scoped_to_account => ['id'].to_set,
|
||||
:scope => 'root_account_id' },
|
||||
'users' =>
|
||||
{ :lookups => { 'sis_user_id' => 'pseudonyms.sis_user_id', 'sis_login_id' => 'pseudonyms.unique_id', 'id' => 'users.id', 'sis_integration_id' => 'pseudonyms.integration_id' },
|
||||
:is_not_scoped_to_account => ['users.id'].to_set,
|
||||
{ :lookups => { 'sis_user_id' => 'pseudonyms.sis_user_id', 'sis_login_id' => 'pseudonyms.unique_id', 'id' => 'users.id', 'sis_integration_id' => 'pseudonyms.integration_id', 'lti_context_id' => 'users.lti_context_id', 'lti_user_id' => 'users.lti_context_id' },
|
||||
:is_not_scoped_to_account => ['users.id', 'users.lti_context_id'].to_set,
|
||||
:scope => 'pseudonyms.account_id',
|
||||
:joins => [:pseudonym] },
|
||||
'accounts' =>
|
||||
{ :lookups => { 'sis_account_id' => 'sis_source_id', 'id' => 'id', 'sis_integration_id' => 'integration_id' },
|
||||
:is_not_scoped_to_account => ['id'].to_set,
|
||||
{ :lookups => { 'sis_account_id' => 'sis_source_id', 'id' => 'id', 'sis_integration_id' => 'integration_id', 'lti_context_id' => 'lti_context_id' },
|
||||
:is_not_scoped_to_account => ['id', 'lti_context_id'].to_set,
|
||||
:scope => 'root_account_id' },
|
||||
'course_sections' =>
|
||||
{ :lookups => { 'sis_section_id' => 'sis_source_id', 'id' => 'id' , 'sis_integration_id' => 'integration_id' },
|
||||
|
@ -127,10 +126,10 @@ module Api
|
|||
# returns column_name, column_value
|
||||
return lookups['id'], id if id.is_a?(Numeric) || id.is_a?(ActiveRecord::Base)
|
||||
id = id.to_s.strip
|
||||
if id =~ %r{\Ahex:(sis_[\w_]+):(([0-9A-Fa-f]{2})+)\z}
|
||||
if id =~ %r{\Ahex:(lti_[\w_]+|sis_[\w_]+):(([0-9A-Fa-f]{2})+)\z}
|
||||
sis_column = $1
|
||||
sis_id = [$2].pack('H*')
|
||||
elsif id =~ %r{\A(sis_[\w_]+):(.+)\z}
|
||||
elsif id =~ %r{\A(lti_[\w_]+|sis_[\w_]+):(.+)\z}
|
||||
sis_column = $1
|
||||
sis_id = $2
|
||||
elsif id =~ ID_REGEX
|
||||
|
|
|
@ -152,6 +152,26 @@ describe Api do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "should find user by lti_context_id" do
|
||||
@user.lti_context_id = Canvas::Security.hmac_sha1(@user.asset_string.to_s, 'key')
|
||||
@user.save!
|
||||
@api.api_find(User, "lti_context_id:#{@user.lti_context_id}").should == @user
|
||||
end
|
||||
|
||||
it "should find course by lti_context_id" do
|
||||
lti_course = course
|
||||
lti_course.lti_context_id = Canvas::Security.hmac_sha1(lti_course.asset_string.to_s, 'key')
|
||||
lti_course.save!
|
||||
@api.api_find(Course, "lti_context_id:#{lti_course.lti_context_id}").should == lti_course
|
||||
end
|
||||
|
||||
it "should find account by lti_context_id" do
|
||||
account = Account.create!(name: 'account')
|
||||
account.lti_context_id = Canvas::Security.hmac_sha1(account.asset_string.to_s, 'key')
|
||||
account.save!
|
||||
@api.api_find(Account, "lti_context_id:#{account.lti_context_id}").should == account
|
||||
end
|
||||
end
|
||||
|
||||
context 'api_find_all' do
|
||||
|
|
|
@ -689,4 +689,23 @@ describe ContextExternalTool do
|
|||
expect { ContextExternalTool.find_for("horseshoes", @course, :course_navigation) }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
describe "opaque_identifier_for" do
|
||||
it "should create lti_context_id for asset" do
|
||||
@course.lti_context_id.should == nil
|
||||
@tool = @course.context_external_tools.create!(:name => "a", :domain => "google.com", :consumer_key => '12345', :shared_secret => 'secret')
|
||||
context_id = @tool.opaque_identifier_for(@course)
|
||||
@course.reload
|
||||
@course.lti_context_id.should == context_id
|
||||
end
|
||||
|
||||
it "should not create new lti_context for asset if exists" do
|
||||
@course.lti_context_id = 'dummy_context_id'
|
||||
@course.save!
|
||||
@tool = @course.context_external_tools.create!(:name => "a", :domain => "google.com", :consumer_key => '12345', :shared_secret => 'secret')
|
||||
context_id = @tool.opaque_identifier_for(@course)
|
||||
@course.reload
|
||||
@course.lti_context_id.should == 'dummy_context_id'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue