Skip to content

Commit

Permalink
LTI-383: added support for multiple tools per issuer (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
jfederico authored Jun 28, 2024
1 parent 147f2a1 commit 5596a31
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ gem 'repost', '~> 0.3.8'
gem 'lodash-rails'
gem 'react-rails', '>= 3.2.1'

gem 'rails_lti2_provider', git: 'https://github.com/blindsidenetworks/rails_lti2_provider.git', tag: '0.2.0'
gem 'rails_lti2_provider', git: 'https://github.com/blindsidenetworks/rails_lti2_provider.git', tag: '0.2.1'

gem 'ims-lti', git: 'https://github.com/blindsidenetworks/ims-lti.git', tag: 'v2.3.2.1'

Expand Down
7 changes: 4 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ GIT

GIT
remote: https://github.com/blindsidenetworks/rails_lti2_provider.git
revision: 0738cf37bb439a31413bd704bc9610c86f658f5d
tag: 0.2.0
revision: b95288778985f3d2fd833bd726d9f4f6b63f3faf
tag: 0.2.1
specs:
rails_lti2_provider (0.2.0)
rails_lti2_provider (0.2.1)
ims-lti (~> 2.3.2.1)

GIT
Expand Down Expand Up @@ -173,6 +173,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
ffi (1.17.0)
ffi (1.17.0-x86_64-linux-gnu)
ffi-compiler (1.3.2)
ffi (>= 1.15.5)
rake
Expand Down
8 changes: 4 additions & 4 deletions app/controllers/auth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ def login
def validate_oidc_login
raise CustomError, :could_not_find_issuer unless params.key?('iss')
raise CustomError, :could_not_find_login_hint unless params.key?('login_hint')
raise CustomError, :could_not_find_client_id unless params.key?('client_id')

options = {}
options['client_id'] = params[:client_id] if params.key?('client_id')
lti_registration = RailsLti2Provider::Tool.find_by_issuer(iss, { client_id: params[:client_id] })

unless lti_registration_exists?(params[:iss], options)
if lti_registration.blank?
render(file: Rails.root.join('public/500.html'), layout: false, status: :not_found)
logger.error('ERROR: The app is not currently registered within the lti broker.')
return
end

@registration = lti_registration_params(params[:iss], options)
@registration = JSON.parse(lti_registration.tool_settings)
end
end
42 changes: 0 additions & 42 deletions app/controllers/concerns/platform_validator.rb

This file was deleted.

17 changes: 11 additions & 6 deletions app/controllers/message_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class MessageController < ApplicationController
@error = "Authentication failed with: #{output}"
@message = IMS::LTI::Models::Messages::Message.generate(request.request_parameters)
@header = SimpleOAuth::Header.new(:post, request.url, @message.post_params, consumer_key: @message.oauth_consumer_key,
consumer_secret: lti_secret(@message.oauth_consumer_key), callback: 'about:blank')
consumer_secret: consumer_secret(@message.oauth_consumer_key), callback: 'about:blank')
logger.debug("ERROR: #{@error}")
if request.request_parameters.key?('launch_presentation_return_url')
launch_presentation_return_url = "#{request.request_parameters['launch_presentation_return_url']}&lti_errormsg=#{@error}"
Expand Down Expand Up @@ -188,9 +188,8 @@ def deep_link
apps.each do |app|
resource = deep_link_resource(openid_launch_url, "My #{app.singularize}", '', secure_url(lti_app_icon_url(app)), { 'broker_app': app })

options = {}
options['client_id'] = @jwt_body['aud']
deep_link_jwt_message = deep_link_jwt_response(lti_registration_params(@jwt_body['iss'], options), @jwt_body, [resource])
lti_registration = RailsLti2Provider::Tool.find_by_issuer(@jwt_body['iss'], { client_id: @jwt_body['aud'] })
deep_link_jwt_message = deep_link_jwt_response(JSON.parse(lti_registration.tool_settings), @jwt_body, [resource])

@apps << { app_name: app, deep_link_jwt_message: deep_link_jwt_message }
end
Expand All @@ -206,7 +205,7 @@ def process_blti_message
@message = @lti_launch&.message || IMS::LTI::Models::Messages::Message.generate(request.request_parameters)
tc_instance_guid = tool_consumer_instance_guid(request.referer, params)
@header = SimpleOAuth::Header.new(:post, request.url, @message.post_params, consumer_key: @message.oauth_consumer_key,
consumer_secret: lti_secret(@message.oauth_consumer_key), callback: 'about:blank')
consumer_secret: consumer_secret(@message.oauth_consumer_key), callback: 'about:blank')
@current_user = User.find_or_create_by(context: tc_instance_guid, uid: params['user_id']) do |user|
user.update(user_params(tc_instance_guid, params))
end
Expand All @@ -226,7 +225,7 @@ def process_openid_message
@jwt_body = jwt[:body]
logger.debug("JWT Body: #{@jwt_body}")

tool = RailsLti2Provider::Tool.find_by(uuid: @jwt_body['iss'], shared_secret: @jwt_body['aud'])
tool = RailsLti2Provider::Tool.find_by_issuer(@jwt_body['iss'], { client_id: @jwt_body['aud'] })
# Cleanups the lti_launches table from old launches.
tool.lti_launches.where('created_at < ?', 1.day.ago).delete_all
# Create a new lti_launch.
Expand Down Expand Up @@ -310,4 +309,10 @@ def handler_legacy_patterns(tenant_uid)

tenant.settings['handler_legacy_patterns']
end

# Get the the shared secret for the consumer key under the assumption that the consumer key is the UUID of the tool and it is unique.
def consumer_secret(consumer_key)
tool = RailsLti2Provider::Tool.find_by_uuid(consumer_key)
tool&.shared_secret
end
end
5 changes: 1 addition & 4 deletions app/controllers/registration_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,6 @@ def process_registration_initiation_request
key_pair_id = JSON.parse(tool.tool_settings)['rsa_key_pair_id']
RsaKeyPair.delete(key_pair_id)
end
elsif RailsLti2Provider::Tool.exists?(uuid: openid_configuration['issuer'], tenant: tenant) # new
@error_message = "Issuer or Platform ID has already been registered for tenant '#{tenant.uid}'"
raise CustomError, :tool_duplicated
end

# 3.5 Step 3: Client Registration
Expand Down Expand Up @@ -215,7 +212,7 @@ def process_registration_initiation_request
}

begin
@tool = RailsLti2Provider::Tool.find_by(uuid: openid_configuration['issuer'], tenant: tenant)
@tool = RailsLti2Provider::Tool.find_by_issuer(reg['issuer'], { client_id: reg['client_id'] })
if @tool
@tool.update(tool_attributes)
else
Expand Down
10 changes: 6 additions & 4 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_04_15_203259) do
ActiveRecord::Schema.define(version: 2024_06_27_170310) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -97,9 +97,11 @@
t.integer "tenant_id"
t.datetime "expired_at"
t.integer "status", default: 0, null: false
t.index ["id", "tenant_id"], name: "index_tool_id_tenant_id", unique: true
t.index ["tenant_id", "uuid"], name: "index_tenant_id_uuid", unique: true
t.index ["tenant_id"], name: "index_tenant_id"
t.index ["id"], name: "index_id"
t.index ["uuid", "shared_secret"], name: "index_uuid_and_shared_secret_lti_1_3", unique: true, where: "((lti_version)::text = '1.3.0'::text)"
t.index ["uuid", "shared_secret"], name: "index_uuid_shared_secret"
t.index ["uuid"], name: "index_uuid"
t.index ["uuid"], name: "index_uuid_lti_1_1", unique: true, where: "((lti_version)::text = 'LTI-1p0'::text)"
end

create_table "rsa_key_pairs", force: :cascade do |t|
Expand Down
7 changes: 5 additions & 2 deletions lib/tasks/tenant.rake
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ namespace :tenant do

namespace :activation_code do
desc 'New activation_code for a tenant'
task :new, [:uid] => :environment do |_t, args|
task :new, [:uid, :hours] => :environment do |_t, args|
# Key.
uid = args[:uid]
if uid.blank?
Expand All @@ -251,6 +251,9 @@ namespace :tenant do
end
abort('The UID cannot be blank.') if uid.blank?

# Hours to expire.
hours = args[:hours] || 1

tenant = RailsLti2Provider::Tenant.find_by(uid: uid)
if tenant.nil?
puts("Tenant '#{args[:uuid]}' does not exist")
Expand All @@ -259,7 +262,7 @@ namespace :tenant do

# Add the activation_code
tenant.metadata['activation_code'] = Digest::MD5.hexdigest(SecureRandom.uuid)
tenant.metadata['activation_code_expire'] = 1.hour.from_now
tenant.metadata['activation_code_expire'] = hours.hour.from_now
tenant.save!
puts("Metadata for tenant #{tenant.uid}: \n #{tenant.metadata.to_yaml}") unless tenant.nil?
end
Expand Down
13 changes: 7 additions & 6 deletions lib/tasks/tool.rake
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ namespace :tool do
end
abort('The Issuer must be valid.') if issuer.blank?

# Validate the tenant as it is a point of failure and if not valid, there is no point on continuing.
tenant = RailsLti2Provider::Tenant.find_by(uid: args[:tenant_uid]) if args[:tenant_uid].present?
tenant = RailsLti2Provider::Tenant.first if tenant.nil?
abort('Tenant not found. Tenant UID must be valid or Deafult Tenant must exist.') if tenant.nil?
abort("Issuer or Platform ID has already been registered for tenant '#{tenant.uid}'.") if RailsLti2Provider::Tool.exists?(uuid: issuer, tenant: tenant)

# Client ID.
client_id = args[:client_id]
if client_id.blank?
$stdout.puts('What is the Client ID?')
client_id = $stdin.gets.strip
end
abort('The Client ID must be valid.') if client_id.blank?
# Validate if the tool already exists.
abort("The tool with issuer = #{issuer} and client_id = #{client_id} already exists.") if RailsLti2Provider::Tool.find_by_issuer(issuer, { client_id: client_id }).present?

# Validate the tenant as it is a point of failure and if not valid, there is no point on continuing.
tenant = RailsLti2Provider::Tenant.find_by(uid: args[:tenant_uid]) if args[:tenant_uid].present?
tenant = RailsLti2Provider::Tenant.first if tenant.nil?
abort('Tenant not found. Tenant UID must be valid or Deafult Tenant must exist.') if tenant.nil?

# Deployment ID.
deployment_id = args[:deployment_id]
Expand Down

0 comments on commit 5596a31

Please sign in to comment.