Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark training complete improvements #45

Merged
merged 42 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5990d13
Update equipment training quiz event - as not deployed yet
Lan2u Jul 6, 2024
9f046df
Handle more sheet types/formats
Lan2u Jul 6, 2024
361af42
Member number / email are optional
Lan2u Jul 6, 2024
86ccfe9
Restructuring to support handling quiz results with conflicting membe…
Lan2u Jul 6, 2024
17e909c
Merge branch 'main' into mark_trained_improvements
Lan2u Jul 6, 2024
8985ad3
Remove now unused
Lan2u Jul 6, 2024
c0f0028
Merge branch 'main' into mark_trained_improvements
Lan2u Jul 6, 2024
2271e9a
Separate quiz results with correctly matched member numbers and emails
Lan2u Jul 6, 2024
722bf05
Thinking about how to structure this - start with low efficiency gath…
Lan2u Jul 8, 2024
a810af0
Get members trained on
Lan2u Jul 8, 2024
9268b7a
Query should be under equipment section
Lan2u Jul 8, 2024
d5b93e9
Determine quiz passed not trained and failed quiz not trained
Lan2u Jul 8, 2024
71d3542
Update rendering quiz results
Lan2u Jul 9, 2024
e7e388a
Render email/number provided
Lan2u Jul 9, 2024
6d45e04
Refined rules for quiz results inc. support for member number or emai…
Lan2u Jul 9, 2024
ddb31ad
Type fixes
Lan2u Jul 10, 2024
1418bef
Fix populate local dev
Lan2u Jul 10, 2024
7dee6aa
Fix equal quiz results not being treated as such
Lan2u Jul 10, 2024
3489de5
Test quiz result inequality
Lan2u Jul 10, 2024
20cbb7b
Merge branch 'main' into mark_trained_improvements
Lan2u Jul 10, 2024
35be7e7
Fix tests being wip
Lan2u Jul 10, 2024
08be128
Trying to make constructing view model more functional
Lan2u Jul 14, 2024
2b4456f
Start writing tests for equipment view model
Lan2u Jul 14, 2024
5a36428
Fix imports
Lan2u Jul 14, 2024
a0c47b7
Log attempts to decode magic link
Lan2u Jul 14, 2024
1106c72
Fix invalid login magic link display of login redirect
Lan2u Jul 14, 2024
d8f6622
Fix lint
Lan2u Jul 14, 2024
a25dddb
Auto redirect if already logged in on login page
Lan2u Jul 14, 2024
cebe35f
Remove log in / out dependency in navbar which requires logged in user
Lan2u Jul 14, 2024
d187944
Update usages of pageTemplate
Lan2u Jul 14, 2024
c3764b2
Start on logged in user square to display the thumbnail avatar of the…
Lan2u Jul 14, 2024
50ad55e
Missing alt text member number for avatar profile
Lan2u Jul 15, 2024
32f6a2f
Use emailAddress and memberNumber for memberdetails to match that use…
Lan2u Jul 15, 2024
b24c074
Updating usages of memberDetails and user as they now share propertie…
Lan2u Jul 15, 2024
769b943
Fix last of the usages of email vs emailAddress
Lan2u Jul 15, 2024
347c406
Use Member type rather than self-defined for add trainer form
Lan2u Jul 15, 2024
df6d9b3
Update usages in add trainer form template of old email vs emailAddre…
Lan2u Jul 15, 2024
85d95a7
Fix unused imports / failed tests
Lan2u Jul 15, 2024
f5e02ab
Reduce indent
Lan2u Jul 15, 2024
38b48b3
Fixed calling loggedInUserSquare template
Lan2u Jul 16, 2024
3cd2a8e
Logged in user square working
Lan2u Jul 16, 2024
0659730
Merge pull request #50 from Makespace/fix_login_out
Lan2u Jul 16, 2024
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
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