Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 21 additions & 22 deletions src/routes/internal-sync.route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express, { Request, Response } from 'express';
import { IdToken, Provider as lti } from 'ltijs';
import { Config } from '../config';
import { LtiLaunchPayload } from '../types';
import UnitLink from '../schema/unitLink.model';

export const INTERNAL_SYNC_ROUTE_PATH = '/lti/api/internal/test-members';
export const InternalSyncRoute = express.Router();
Expand All @@ -15,41 +15,40 @@
return res.status(401).json({ error: 'Unauthorized' });
}

const { issuer, clientId, contextId, user } = req.body as Record<string, unknown>;
if (
typeof issuer !== 'string' ||
typeof clientId !== 'string' ||
typeof contextId !== 'string' ||
typeof user !== 'string'
) {
const { unitId, contextId } = req.body as Record<string, unknown>;
const hasContextId = typeof contextId === 'string' && contextId.length > 0;
const hasUnitId = (typeof unitId === 'string' && unitId.length > 0) || typeof unitId === 'number';
if (!hasUnitId && !hasContextId) {
return res.status(400).json({
error: 'issuer, clientId, contextId and user must be strings',
error: 'unitId or contextId must be provided',
});
}

try {
const storedContexts = await lti.Database.Get(false, 'contexttoken', {
contextId,
user,
});

if (!Array.isArray(storedContexts) || !storedContexts[0]) {
const link = await UnitLink.findOne(hasContextId ? { contextId } : { unitId: String(unitId) });

Check failure on line 28 in src/routes/internal-sync.route.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Change this code to not construct database queries directly from user-controlled data.

See more on https://sonarcloud.io/project/issues?id=doubtfire-lms_doubtfire-lti&issues=AZ-Nc6v3GImCKUVeYtUY&open=AZ-Nc6v3GImCKUVeYtUY&pullRequest=21
if (!link) {
return res.status(404).json({
error: 'Stored LTI context not found; perform a new LMS launch',
error: 'Linked LMS context not found',
});
}

const platformContext = storedContexts[0] as LtiLaunchPayload['platformContext'];
if (!platformContext?.namesRoles?.context_memberships_url) {
if (!link.issuer || !link.clientId || !link.deploymentId || !link.membershipsUrl) {
return res.status(422).json({
error: 'Stored LTI context does not include an NRPS memberships URL',
error: 'Link does not include NRPS service details; launch and link the unit again',
});
}

const serviceToken = {
iss: issuer,
clientId,
platformContext,
iss: link.issuer,
clientId: link.clientId,
deploymentId: link.deploymentId,
platformContext: {
context: { id: link.contextId },
namesRoles: {
context_memberships_url: link.membershipsUrl,
service_versions: ['2.0'],
},
},
} as unknown as IdToken;

// Ltijs supports `pages: false` to retrieve every page, although its bundled
Expand Down
18 changes: 16 additions & 2 deletions src/routes/unit-link.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ UnitLinkRouter.post('/link', async (req: Request, res: Response) => {
const _token = res.locals.token;
const token = _token as unknown as LtiLaunchPayload;
const contextId = token.platformContext?.context?.id;
const issuer = token.iss;
const clientId = token.clientId;
const deploymentId = token.deploymentId;
const membershipsUrl = token.platformContext?.namesRoles?.context_memberships_url;

if (!contextId || !issuer || !clientId || !deploymentId || !membershipsUrl) {
return sendError(res, 'LTI launch does not include the required NRPS context', 422);
}

const newToken = {
unit_id: unitId,
Expand Down Expand Up @@ -58,8 +66,14 @@ UnitLinkRouter.post('/link', async (req: Request, res: Response) => {
// Current OnTrack user has permissions to enrol students into requested unit_id
const result = await UnitLink.findOneAndUpdate(
{ contextId },
{ unitId },
{ upsert: true, new: true },
{
unitId,
issuer,
clientId,
deploymentId,
membershipsUrl,
},
{ upsert: true, new: true, runValidators: true },
);
res.json(result);
});
Expand Down
4 changes: 4 additions & 0 deletions src/schema/unitLink.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import mongoose from 'mongoose';
const UnitLinkSchema = new mongoose.Schema({
contextId: { type: String, required: true, unique: true },
unitId: { type: String, required: true },
issuer: { type: String, required: true },
clientId: { type: String, required: true },
deploymentId: { type: String, required: true },
membershipsUrl: { type: String, required: true },
});

const UnitLink = mongoose.model('UnitLink', UnitLinkSchema);
Expand Down
Loading