From 8b611a7621770341d06daca288227ce3d48d5d50 Mon Sep 17 00:00:00 2001 From: Paul Gray Date: Wed, 17 Apr 2024 09:52:38 -0400 Subject: [PATCH] Make Dynamic Registration configuration extensible This change allows the Dynamic Registration configuration to be extended with properties that can be set per-installation (As opposed to once at the top-level for all installs). This is done by taking the third (currently unused) parameter to the `lti.DynamicRegistration.register`, and performing a deep merge with the default registration object, which I assume was the original intent of that parameter. ```javascript lti.DynamicRegistration.register( req.query.openid_configuration, req.query.registration_token, { 'https://purl.imsglobal.org/spec/lti-tool-configuration': { custom_parameters: { 'custom1': 'value1', 'custom2': 'value2' } } } ) ``` This fixes issue #144 --- dist/Provider/Services/DynamicRegistration.js | 40 +++++++++++++++++-- src/Provider/Services/DynamicRegistration.js | 38 ++++++++++++++++-- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/dist/Provider/Services/DynamicRegistration.js b/dist/Provider/Services/DynamicRegistration.js index 06143e0..77eb9fb 100644 --- a/dist/Provider/Services/DynamicRegistration.js +++ b/dist/Provider/Services/DynamicRegistration.js @@ -11,6 +11,40 @@ const crypto = require('crypto'); const _url = require('fast-url-parser'); const provDynamicRegistrationDebug = require('debug')('provider:dynamicRegistrationService'); +/** + * Simple object check. taken from https://stackoverflow.com/a/34749873 + * @param item + * @returns {boolean} + */ +function isObject(item) { + return item && typeof item === 'object' && !Array.isArray(item); +} + +/** + * Deep merge two objects. taken from https://stackoverflow.com/a/34749873 + * @param target + * @param ...sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) return target; + const source = sources.shift(); + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) Object.assign(target, { + [key]: {} + }); + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { + [key]: source[key] + }); + } + } + } + return mergeDeep(target, ...sources); +} + // Helper method to build URLs const buildUrl = (url, path) => { if (path === '/') return url; @@ -94,7 +128,7 @@ class DynamicRegistration { * @param {String} [registrationToken] - Registration Token. Retrieved from req.query.registration_token. * @param {Object} [options] - Replacements or extensions to default registration options. */ - async register(openidConfiguration, registrationToken, options) { + async register(openidConfiguration, registrationToken, options = {}) { if (!openidConfiguration) throw new Error('MISSING_OPENID_CONFIGURATION'); provDynamicRegistrationDebug('Starting dynamic registration process'); // Get Platform registration configurations @@ -107,7 +141,7 @@ class DynamicRegistration { if (_classPrivateFieldGet(_useDeepLinking, this)) messages.push({ type: 'LtiDeepLinkingRequest' }); - const registration = { + const registration = mergeDeep({ application_type: 'web', response_types: ['id_token'], grant_types: ['implicit', 'client_credentials'], @@ -126,7 +160,7 @@ class DynamicRegistration { claims: configuration.claims_supported, messages } - }; + }, options); provDynamicRegistrationDebug('Tool registration request:'); provDynamicRegistrationDebug(registration); provDynamicRegistrationDebug('Sending Tool registration request'); diff --git a/src/Provider/Services/DynamicRegistration.js b/src/Provider/Services/DynamicRegistration.js index 5fb67f8..ec73241 100644 --- a/src/Provider/Services/DynamicRegistration.js +++ b/src/Provider/Services/DynamicRegistration.js @@ -5,6 +5,38 @@ const _url = require('fast-url-parser') const provDynamicRegistrationDebug = require('debug')('provider:dynamicRegistrationService') +/** + * Simple object check. taken from https://stackoverflow.com/a/34749873 + * @param item + * @returns {boolean} + */ +function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge two objects. taken from https://stackoverflow.com/a/34749873 + * @param target + * @param ...sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) return target; + const source = sources.shift(); + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) Object.assign(target, { [key]: {} }); + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return mergeDeep(target, ...sources); +} + // Helper method to build URLs const buildUrl = (url, path) => { if (path === '/') return url @@ -90,7 +122,7 @@ class DynamicRegistration { * @param {String} [registrationToken] - Registration Token. Retrieved from req.query.registration_token. * @param {Object} [options] - Replacements or extensions to default registration options. */ - async register (openidConfiguration, registrationToken, options) { + async register (openidConfiguration, registrationToken, options = {}) { if (!openidConfiguration) throw new Error('MISSING_OPENID_CONFIGURATION') provDynamicRegistrationDebug('Starting dynamic registration process') // Get Platform registration configurations @@ -99,7 +131,7 @@ class DynamicRegistration { // Building registration object const messages = [{ type: 'LtiResourceLink' }] if (this.#useDeepLinking) messages.push({ type: 'LtiDeepLinkingRequest' }) - const registration = { + const registration = mergeDeep({ application_type: 'web', response_types: ['id_token'], grant_types: ['implicit', 'client_credentials'], @@ -118,7 +150,7 @@ class DynamicRegistration { claims: configuration.claims_supported, messages } - } + }, options) provDynamicRegistrationDebug('Tool registration request:') provDynamicRegistrationDebug(registration) provDynamicRegistrationDebug('Sending Tool registration request')