Skip to content

Commit

Permalink
Merge pull request #45 from Makespace/mark_trained_improvements
Browse files Browse the repository at this point in the history
Mark training complete improvements
  • Loading branch information
Lan2u authored Jul 16, 2024
2 parents eb3ab38 + 0659730 commit dea425b
Show file tree
Hide file tree
Showing 57 changed files with 846 additions and 332 deletions.
6 changes: 3 additions & 3 deletions scripts/populate-local-dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ curl -X POST -H 'Authorization: Bearer secret' -H 'Content-Type: application/jso
http://localhost:8080/api/equipment/add-training-sheet

curl -X POST -H 'Authorization: Bearer secret' -H 'Content-Type: application/json' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","email": "[email protected]","score": 13,"maxScore": 20,"percentage": 65,"fullMarks": false,"timestampEpochS": 1718411504,"quizAnswers": {}}' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","emailProvided": "[email protected]","memberNumberProvided":1234,"score": 13,"maxScore": 20,"percentage": 65,"fullMarks": false,"timestampEpochS": 1718411504,"quizAnswers": {}}' \
http://localhost:8080/api/equipment/add-training-quiz-result

curl -X POST -H 'Authorization: Bearer secret' -H 'Content-Type: application/json' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","email": "[email protected]","score": 20,"maxScore": 20,"percentage": 100,"fullMarks": true,"timestampEpochS": 1718413504,"quizAnswers": {}}' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","emailProvided": "[email protected]","memberNumberProvided":1234,"score": 20,"maxScore": 20,"percentage": 100,"fullMarks": true,"timestampEpochS": 1718413504,"quizAnswers": {}}' \
http://localhost:8080/api/equipment/add-training-quiz-result

# Demonstrates html injection on the equipment page.
curl -X POST -H 'Authorization: Bearer secret' -H 'Content-Type: application/json' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","email": "<h1>myemail.com</h1>","score": 20,"maxScore": 20,"percentage": 100,"fullMarks": true,"timestampEpochS": 1718413504,"quizAnswers": {}}' \
--data '{"equipmentId": "4224ee94-09b0-47d4-ae60-fac46b8ca93e","trainingSheetId": "fakeTrainingSheetId","id": "2d0e6174-a827-4331-9dc2-ffb05ea863c3","emailProvided":"<h1>myemail.com</h1>","memberNumberProvided":1234,"score": 20,"maxScore": 20,"percentage": 100,"fullMarks": true,"timestampEpochS": 1718413504,"quizAnswers": {}}' \
http://localhost:8080/api/equipment/add-training-quiz-result
17 changes: 10 additions & 7 deletions src/authentication/auth-routes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import {Dependencies} from '../dependencies';
import {Route, get, post} from '../types/route';
import {auth, callback, invalidLink, logIn, logOut} from './handlers';

export const logInPath = '/log-in';
const invalidLinkPath = '/auth/invalid-magic-link';

export const authRoutes: ReadonlyArray<Route> = [
get(logInPath, logIn),
get('/log-out', logOut),
post('/auth', auth),
get('/auth/callback', callback(invalidLinkPath)),
get(invalidLinkPath, invalidLink(logInPath)),
];
export const authRoutes = (deps: Dependencies): ReadonlyArray<Route> => {
return [
get(logInPath, logIn(deps)),
get('/log-out', logOut),
post('/auth', auth),
get('/auth/callback', callback(invalidLinkPath)),
get(invalidLinkPath, invalidLink(logInPath)),
];
};
8 changes: 2 additions & 6 deletions src/authentication/check-your-mail.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as O from 'fp-ts/Option';
import {pageTemplate} from '../templates';
import Handlebars from 'handlebars';
import {isolatedPageTemplate} from '../templates/page-template';

const CHECK_YOUR_MAIL_TEMPLATE = Handlebars.compile(
`
Expand All @@ -14,10 +13,7 @@ const CHECK_YOUR_MAIL_TEMPLATE = Handlebars.compile(
);

export const checkYourMailPage = (submittedEmailAddress: string) =>
pageTemplate(
'Check your mail',
O.none
)(
isolatedPageTemplate('Check your mail')(
new Handlebars.SafeString(
CHECK_YOUR_MAIL_TEMPLATE({
submittedEmailAddress,
Expand Down
21 changes: 18 additions & 3 deletions src/authentication/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,29 @@ import {Request, Response} from 'express';
import {pipe} from 'fp-ts/lib/function';
import {parseEmailAddressFromBody} from './parse-email-address-from-body';
import * as E from 'fp-ts/Either';
import * as O from 'fp-ts/Option';
import {publish} from 'pubsub-js';
import passport from 'passport';
import {magicLink} from './magic-link';
import {logInPage} from './log-in-page';
import {checkYourMailPage} from './check-your-mail';
import {oopsPage} from '../templates';
import {StatusCodes} from 'http-status-codes';
import {SafeString} from 'handlebars';
import {getUserFromSession} from './get-user-from-session';
import {Dependencies} from '../dependencies';

export const logIn = (req: Request, res: Response) => {
res.status(StatusCodes.OK).send(logInPage);
export const logIn = (deps: Dependencies) => (req: Request, res: Response) => {
pipe(
req.session,
getUserFromSession(deps),
O.match(
() => {
res.status(StatusCodes.OK).send(logInPage);
},
_user => res.redirect('/')
)
);
};

export const logOut = (req: Request, res: Response) => {
Expand Down Expand Up @@ -41,7 +54,9 @@ export const invalidLink =
.status(StatusCodes.UNAUTHORIZED)
.send(
oopsPage(
`The link you have used is (no longer) valid. Go back to the <a href=${logInPath}>log in</a>`
new SafeString(
`The link you have used is (no longer) valid. Go back to the <a href=${logInPath}>log in</a>`
)
)
);
};
Expand Down
10 changes: 4 additions & 6 deletions src/authentication/log-in-page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as O from 'fp-ts/Option';
import {pageTemplate} from '../templates';
import Handlebars, {SafeString} from 'handlebars';
import {isolatedPageTemplate} from '../templates/page-template';

const LOGIN_PAGE_TEMPLATE = Handlebars.compile(
`
Expand All @@ -14,7 +13,6 @@ const LOGIN_PAGE_TEMPLATE = Handlebars.compile(
`
);

export const logInPage = pageTemplate(
'MakeSpace Members App',
O.none
)(new SafeString(LOGIN_PAGE_TEMPLATE({})));
export const logInPage = isolatedPageTemplate('MakeSpace Members App')(
new SafeString(LOGIN_PAGE_TEMPLATE({}))
);
20 changes: 12 additions & 8 deletions src/authentication/magic-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import jwt from 'jsonwebtoken';
import {Config} from '../configuration';
import {Dependencies} from '../dependencies';
import {User} from '../types/user';
import {logPassThru} from '../util';
import {Logger} from 'pino';

const createMagicLink = (conf: Config) => (user: User) =>
pipe(
Expand All @@ -24,19 +26,21 @@ const verifyToken = (token: string, secret: Config['TOKEN_SECRET']) =>
failure('Could not verify token')
);

const decodeMagicLinkFromQuery = (conf: Config) => (input: unknown) =>
pipe(
input,
MagicLinkQuery.decode,
E.chainW(({token}) => verifyToken(token, conf.TOKEN_SECRET)),
E.chainW(User.decode)
);
const decodeMagicLinkFromQuery =
(logger: Logger, conf: Config) => (input: unknown) =>
pipe(
input,
logPassThru(logger, 'Attempting to decode magic link from query'), // Logging is required as a basic form of auth enumeration detection.
MagicLinkQuery.decode,
E.chainW(({token}) => verifyToken(token, conf.TOKEN_SECRET)),
E.chainW(User.decode)
);

const strategy = (deps: Dependencies, conf: Config) => {
return new CustomStrategy((req, done) => {
pipe(
req.query,
decodeMagicLinkFromQuery(conf),
decodeMagicLinkFromQuery(deps.logger, conf),
E.match(
error => {
deps.logger.info(
Expand Down
6 changes: 4 additions & 2 deletions src/commands/area/add-owner-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ Handlebars.registerPartial(
);

Handlebars.registerHelper('member_name_or_contact', (member: MemberDetails) =>
O.isSome(member.name) ? member.name : `${member.number} ${member.email}`
O.isSome(member.name)
? member.name
: `${member.memberNumber} ${member.emailAddress}`
);

Handlebars.registerPartial(
Expand Down Expand Up @@ -101,7 +103,7 @@ const ADD_OWNER_FORM_TEMPLATE = Handlebars.compile(
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Add Owner',
O.some(viewModel.user)
viewModel.user
)(new SafeString(ADD_OWNER_FORM_TEMPLATE(viewModel)));

const paramsCodec = t.strict({
Expand Down
3 changes: 1 addition & 2 deletions src/commands/area/create-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {User} from '../../types';
import {v4} from 'uuid';
import {Form} from '../../types/form';
Expand All @@ -25,7 +24,7 @@ const CREATE_FORM_TEMPLATE = Handlebars.compile(
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Create Area',
O.some(viewModel.user)
viewModel.user
)(
new SafeString(
CREATE_FORM_TEMPLATE({
Expand Down
3 changes: 1 addition & 2 deletions src/commands/equipment/add-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {pipe} from 'fp-ts/lib/function';
import * as t from 'io-ts';
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {DomainEvent, User} from '../../types';
import {v4} from 'uuid';
import {Form} from '../../types/form';
Expand Down Expand Up @@ -33,7 +32,7 @@ const RENDER_ADD_EQUIPMENT_FORM_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Create Equipment',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_ADD_EQUIPMENT_FORM_TEMPLATE(viewModel)));

const getAreaId = (input: unknown) =>
Expand Down
3 changes: 1 addition & 2 deletions src/commands/equipment/register-training-sheet-form.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {pipe} from 'fp-ts/lib/function';
import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either';
import {User} from '../../types';
import {Form} from '../../types/form';
Expand Down Expand Up @@ -33,7 +32,7 @@ const RENDER_REGISTER_TRAINING_SHEET_TEMPLATE = Handlebars.compile(
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Register training sheet',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_REGISTER_TRAINING_SHEET_TEMPLATE(viewModel)));

const constructForm: Form<ViewModel>['constructForm'] =
Expand Down
5 changes: 3 additions & 2 deletions src/commands/equipment/register-training-sheet-quiz-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import * as RA from 'fp-ts/ReadonlyArray';
import {Command} from '../command';
import {isAdminOrSuperUser} from '../is-admin-or-super-user';
import {pipe} from 'fp-ts/lib/function';
import {QzEventDuplicate} from '../../training-sheets/events';
import {QzEventDuplicate} from '../../types/qz-event';

const codec = t.strict({
equipmentId: tt.UUID,
trainingSheetId: t.string,
id: tt.UUID,
email: t.string,
emailProvided: t.string,
memberNumberProvided: t.number,
score: t.number,
maxScore: t.number,
percentage: t.number,
Expand Down
3 changes: 1 addition & 2 deletions src/commands/member-numbers/link-number-to-email-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {User} from '../../types';
import {Form} from '../../types/form';
import Handlebars, {SafeString} from 'handlebars';
Expand All @@ -26,7 +25,7 @@ const RENDER_LINK_NUMBER_TO_EMAIL_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Link a member number to an e-mail address',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_LINK_NUMBER_TO_EMAIL_TEMPLATE(viewModel)));

const constructForm: Form<ViewModel>['constructForm'] =
Expand Down
3 changes: 1 addition & 2 deletions src/commands/members/edit-name-form.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {flow, pipe} from 'fp-ts/lib/function';
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {User} from '../../types';
import {Form} from '../../types/form';
import * as t from 'io-ts';
Expand Down Expand Up @@ -33,7 +32,7 @@ const RENDER_EDIT_NAME_FORM_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Edit name',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_EDIT_NAME_FORM_TEMPLATE(viewModel)));

const paramsCodec = t.strict({
Expand Down
3 changes: 1 addition & 2 deletions src/commands/members/edit-pronouns-form.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {flow, pipe} from 'fp-ts/lib/function';
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {User} from '../../types';
import {Form} from '../../types/form';
import * as t from 'io-ts';
Expand Down Expand Up @@ -33,7 +32,7 @@ const RENDER_EDIT_PRONOUNS_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Edit pronouns',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_EDIT_PRONOUNS_TEMPLATE(viewModel)));

const paramsCodec = t.strict({
Expand Down
3 changes: 1 addition & 2 deletions src/commands/members/sign-owner-agreement-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {Form} from '../../types/form';
import {User} from '../../types';
import Handlebars, {SafeString} from 'handlebars';
Expand Down Expand Up @@ -31,7 +30,7 @@ const SIGN_OWNER_AGREEMENT_FORM_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Sign Owner Agreement',
O.some(viewModel.user)
viewModel.user
)(new SafeString(SIGN_OWNER_AGREEMENT_FORM_TEMPLATE(viewModel)));

const constructForm: Form<ViewModel>['constructForm'] =
Expand Down
2 changes: 1 addition & 1 deletion src/commands/owner-agreement-invite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const constructEmail: SendEmail<OwnerAgreementInvite>['constructEmail'] = (
),
E.map(
(member): Email => ({
recipient: member.email as EmailAddress,
recipient: member.emailAddress as EmailAddress,
text: textEmailTemplate(conf.PUBLIC_URL),
subject: 'Sign the MS Owner Agreement',
html: htmlEmailTemplate(conf.PUBLIC_URL),
Expand Down
3 changes: 1 addition & 2 deletions src/commands/super-user/declare-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as E from 'fp-ts/Either';
import {pipe} from 'fp-ts/lib/function';
import * as O from 'fp-ts/Option';
import {pageTemplate} from '../../templates';
import {User, MemberDetails} from '../../types';
import {Form} from '../../types/form';
Expand Down Expand Up @@ -28,7 +27,7 @@ const RENDER_DECLARE_SUPER_USER_TEMPLATE = Handlebars.compile(
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Declare super user',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_DECLARE_SUPER_USER_TEMPLATE(viewModel)));

const constructForm: Form<ViewModel>['constructForm'] =
Expand Down
3 changes: 1 addition & 2 deletions src/commands/super-user/revoke-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as tt from 'io-ts-types';
import * as t from 'io-ts';
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import * as O from 'fp-ts/Option';
import {User} from '../../types';
import {failureWithStatus} from '../../types/failure-with-status';
import {StatusCodes} from 'http-status-codes';
Expand Down Expand Up @@ -39,7 +38,7 @@ const RENDER_REVOKE_SUPER_USER_TEMPLATE = Handlebars.compile(`
const renderForm = (viewModel: ViewModel) =>
pageTemplate(
'Revoke super user',
O.some(viewModel.user)
viewModel.user
)(new SafeString(RENDER_REVOKE_SUPER_USER_TEMPLATE(viewModel)));

const paramsCodec = t.strict({
Expand Down
Loading

0 comments on commit dea425b

Please sign in to comment.