Skip to content

Commit

Permalink
Merge pull request #1791 from jrjohnson/aws-sdk-v3
Browse files Browse the repository at this point in the history
Switch out our AWS SDK
  • Loading branch information
stopfstedt authored Jul 17, 2024
2 parents ca616ff + 2d055bb commit 1ddc35c
Show file tree
Hide file tree
Showing 10 changed files with 1,278 additions and 418 deletions.
4 changes: 1 addition & 3 deletions bin/lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import createJWT from '../lib/createJWT.js';
import meow from 'meow';

import fetch from 'node-fetch';
import aws from 'aws-sdk';

const cli = meow(`
Usage
Expand All @@ -30,8 +29,7 @@ const lookup = ([ltiUserId, apiServer, apiNameSpace, iliosSecret, searchString])
fetch,
createJWT,
config,
searchString,
aws
searchString
}).then(userId => {
process.stdout.write(`User ID: ${userId}\n`);
});
Expand Down
13 changes: 9 additions & 4 deletions handler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import aws from 'aws-sdk';
import { S3Client } from '@aws-sdk/client-s3';
import fetch from 'node-fetch';
import eventToRequest from './lib/eventToRequest.js';
import readSchoolConfig from './lib/readSchoolConfig.js';
Expand All @@ -8,10 +8,15 @@ import findIliosUser from './lib/findIliosUser.js';
import createJWT from './lib/createJWT.js';
import ltiRequestValidator from './lib/ltiRequestValidator.js';


//create the client outside of the handler:
//https://github.com/aws/aws-sdk-js-v3?tab=readme-ov-file#best-practices
const s3Client = new S3Client({});

const dashboard = async(event, context, callback) => {
console.log('Starting generation of dashboard redirect response');
try {
const response = await launchDashboard({ event, ltiRequestValidator, aws, eventToRequest, readSchoolConfig, fetch, createJWT, findIliosUser });
const response = await launchDashboard({ event, ltiRequestValidator, s3Client, eventToRequest, readSchoolConfig, fetch, createJWT, findIliosUser });
callback(null, response);
} catch (error) {
console.error(error);
Expand All @@ -29,7 +34,7 @@ const dashboard = async(event, context, callback) => {
const courseManager = async(event, context, callback) => {
console.log('Starting generation of dashboard redirect response');
try {
const response = await launchCourseManager({ event, ltiRequestValidator, aws, eventToRequest, readSchoolConfig, fetch, createJWT, findIliosUser });
const response = await launchCourseManager({ event, ltiRequestValidator, s3Client, eventToRequest, readSchoolConfig, fetch, createJWT, findIliosUser });
callback(null, response);
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -69,4 +74,4 @@ const payload = (event, context, callback) => {
console.log('Done generating payload analysis');
};

export { dashboard, courseManager, payload};
export { dashboard, courseManager, payload};
64 changes: 19 additions & 45 deletions lib/findIliosUser.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,24 @@
import { createHash } from 'crypto';
const sha256 = x => createHash('sha256').update(x, 'utf8').digest('hex');
export default async ({ fetch, createJWT, config, searchString }) => {
let url = config.apiServer + config.apiNameSpace;
switch (config.iliosMatchField) {
case 'authentication-username':
url += `/authentications?filters[username]=${searchString}`;
break;
}
const adminToken = createJWT(config.ltiUserId, config.apiServer, config.apiNameSpace, config.iliosSecret);
console.log(`Searching for user ${searchString} at ${url} with token ${adminToken}`);
const data = await fetch(url, {
headers: {
'X-JWT-Authorization': `Token ${adminToken}`
}
});
const result = await data.json();

export default async ({ fetch, createJWT, config, searchString, aws }) => {
const db = new aws.SimpleDB();
const key = `${config.apiServer}:${config.apiNameSpace}:${config.ltiPostField}:${config.iliosMatchField}:userId:${searchString}`;
const keyHash = sha256(key);
console.log(`Hashed key ${key} to ${keyHash}`);
console.log(`Searching for cached ${searchString} in SimpleDB ${process.env.USERID_SIMPLEDB_DOMAIN}`);
const result = await db.getAttributes({
DomainName: process.env.USERID_SIMPLEDB_DOMAIN,
ItemName: keyHash
}).promise();
if (('Attributes' in result) && result.Attributes.length > 0 && result.Attributes[0].Name === 'userId') {
const userId = result.Attributes[0].Value;
console.log(`User ${userId} was found in cache for ${keyHash}`);
if ('authentications' in result && result.authentications.length > 0) {
const userId = result.authentications[0].user;
console.log(`User ${userId} found`);
return userId;
} else {
console.log(`No User found in cache for ${keyHash}`);
let url = config.apiServer + config.apiNameSpace;
switch (config.iliosMatchField) {
case 'authentication-username':
url += `/authentications?filters[username]=${searchString}`;
break;
}
const adminToken = createJWT(config.ltiUserId, config.apiServer, config.apiNameSpace, config.iliosSecret);
console.log(`Searching for user ${searchString} at ${url} with token ${adminToken}`);
const data = await fetch(url, {
headers: {
'X-JWT-Authorization': `Token ${adminToken}`
}
});
const result = await data.json();

if ('authentications' in result && result.authentications.length > 0) {
const userId = result.authentications[0].user;
await db.putAttributes({
DomainName: process.env.USERID_SIMPLEDB_DOMAIN,
ItemName: keyHash,
Attributes: [
{ Name: 'userId', Value: userId }
]
});
console.log(`User ${userId} found and saved to cache as ${keyHash}`);
return userId;
} else {
return `Unable to find Ilios account for ${searchString}.`;
}
return `Unable to find Ilios account for ${searchString}.`;
}
};
6 changes: 3 additions & 3 deletions lib/launchCourseManager.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//Inspiration and code from: https://github.com/atomicjolt/lti-lambda

export default async ({event, ltiRequestValidator, aws, eventToRequest, readSchoolConfig, findIliosUser, fetch, createJWT}) => {
export default async ({event, ltiRequestValidator, s3Client, eventToRequest, readSchoolConfig, findIliosUser, fetch, createJWT}) => {

const request = eventToRequest(event);

Expand All @@ -10,13 +10,13 @@ export default async ({event, ltiRequestValidator, aws, eventToRequest, readScho
}

const consumerKey = request.body.oauth_consumer_key;
const config = await readSchoolConfig(consumerKey, aws);
const config = await readSchoolConfig(consumerKey, s3Client);

console.log(`Configuration for ${consumerKey} read successfully`);
const isValid = ltiRequestValidator(config.consumerSecret, '', request);
if (isValid) {
const searchString = request.body[config.ltiPostField];
const userId = await findIliosUser({ fetch, createJWT, config, searchString, aws });
const userId = await findIliosUser({ fetch, createJWT, config, searchString });
if (userId) {
console.log(`Found user ${userId}.`);
const token = createJWT(userId, config.apiServer, config.apiNameSpace, config.iliosSecret);
Expand Down
6 changes: 3 additions & 3 deletions lib/launchDashboard.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//Inspiration and code from: https://github.com/atomicjolt/lti-lambda

export default async ({event, ltiRequestValidator, aws, eventToRequest, readSchoolConfig, findIliosUser, fetch, createJWT}) => {
export default async ({event, ltiRequestValidator, s3Client, eventToRequest, readSchoolConfig, findIliosUser, fetch, createJWT}) => {

const request = eventToRequest(event);
const consumerKey = request.body.oauth_consumer_key;
const config = await readSchoolConfig(consumerKey, aws);
const config = await readSchoolConfig(consumerKey, s3Client);

console.log(`Configuration for ${consumerKey} read succesfully`);
const isValid = ltiRequestValidator(config.consumerSecret, '', request);
if (isValid) {
const searchString = request.body[config.ltiPostField];
const userId = await findIliosUser({ fetch, createJWT, config, searchString, aws });
const userId = await findIliosUser({ fetch, createJWT, config, searchString });
if (userId) {
console.log(`Found user ${userId}.`);
const token = createJWT(userId, config.apiServer, config.apiNameSpace, config.iliosSecret);
Expand Down
11 changes: 7 additions & 4 deletions lib/readSchoolConfig.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
export default async(key, aws) => {
const s3 = new aws.S3();
import { GetObjectCommand } from '@aws-sdk/client-s3';

export default async(key, s3Client) => {
const params = {
Bucket: process.env.CONFIG_BUCKET,
Key: 'config.json',
};
const command = new GetObjectCommand(params);
console.log('Looking for configuration for school in: ');
console.log(params);
const data = await s3.getObject(params).promise();
const obj = JSON.parse(data.Body);
const response = await s3Client.send(command);
const str = await response.Body.transformToString();
const obj = JSON.parse(str);
if (!(key in obj)) {
throw new Error(`The Consumer Key "${key}" is not known to Ilios. Please contact [email protected] to set it up.`);
} else {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"author": "The Ilios Team",
"license": "MIT",
"devDependencies": {
"@aws-sdk/client-s3": "^3.614.0",
"@stylistic/eslint-plugin-js": "^2.3.0",
"aws-sdk": "^2.1659.0",
"eslint": "^9.7.0",
"eslint-plugin-mocha": "^10.4.3",
"eslint-plugin-n": "^17.2.1",
Expand Down
Loading

0 comments on commit 1ddc35c

Please sign in to comment.