fix: webhook process (#45385)

* fix: token rework functional

fix: clean up

fix: more clean up

fix: more clean up

fix: add widget back to settings

fix:

fix:

fix: cypress

Apply suggestions from code review

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

fix: use flash enum

* chore: rename webhookToken -> userToken

fix: add translations I forgot to save

* fix: add missing tones for flash messages

* fix: node test
This commit is contained in:
Tom
2022-03-11 15:58:23 -06:00
committed by GitHub
parent 692605de3a
commit 9e5f9b2a7c
24 changed files with 370 additions and 434 deletions

View File

@ -341,9 +341,9 @@
"model": "article",
"foreignKey": "externalId"
},
"webhookTokens": {
"userTokens": {
"type": "hasMany",
"model": "WebhookToken",
"model": "UserToken",
"foreignKey": "userId"
}
},

View File

@ -16,6 +16,7 @@ import { wrapHandledError } from '../utils/create-handled-error.js';
import { removeCookies } from '../utils/getSetAccessToken';
import { ifUserRedirectTo, ifNoUserRedirectHome } from '../utils/middleware';
import { getRedirectParams } from '../utils/redirection';
import { createDeleteUserToken } from '../middlewares/delete-user-token';
const passwordlessGetValidators = [
check('email')
@ -38,6 +39,7 @@ module.exports = function enableAuthentication(app) {
const devSaveAuthCookies = devSaveResponseAuthCookies();
const devLoginSuccessRedirect = devLoginRedirect();
const api = app.loopback.Router();
const deleteUserToken = createDeleteUserToken(app);
// Use a local mock strategy for signing in if we are in dev mode.
// Otherwise we use auth0 login. We use a string for 'true' because values
@ -62,7 +64,7 @@ module.exports = function enableAuthentication(app) {
);
}
api.get('/signout', (req, res) => {
api.get('/signout', deleteUserToken, (req, res) => {
const { origin, returnTo } = getRedirectParams(req);
req.logout();
req.session.destroy(err => {

View File

@ -396,15 +396,15 @@ function createCoderoadChallengeCompleted(app) {
* req.headers: { coderoad-user-token: '8kFIlZiwMioY6hqqt...' }
*/
const { WebhookToken, User } = app.models;
const { UserToken, User } = app.models;
return async function coderoadChallengeCompleted(req, res) {
const { 'coderoad-user-token': userWebhookToken } = req.headers;
const { 'coderoad-user-token': userToken } = req.headers;
const { tutorialId } = req.body;
if (!tutorialId) return res.send(`'tutorialId' not found in request body`);
if (!userWebhookToken)
if (!userToken)
return res.send(`'coderoad-user-token' not found in request headers`);
const tutorialRepo = tutorialId?.split(':')[0];
@ -427,21 +427,21 @@ function createCoderoadChallengeCompleted(app) {
const { id: challengeId, challengeType } = challenge;
try {
// check if webhook token is in database
const tokenInfo = await WebhookToken.findOne({
where: { id: userWebhookToken }
// check if user token is in database
const tokenInfo = await UserToken.findOne({
where: { id: userToken }
});
if (!tokenInfo) return res.send('User webhook token not found');
if (!tokenInfo) return res.send('User token not found');
const { userId } = tokenInfo;
// check if user exists for webhook token
// check if user exists for user token
const user = await User.findOne({
where: { id: userId }
});
if (!user) return res.send('User for webhook token not found');
if (!user) return res.send('User for user token not found');
// submit challenge
const completedDate = Date.now();

View File

@ -17,6 +17,7 @@ import {
} from '../utils/publicUserProps';
import { getRedirectParams } from '../utils/redirection';
import { trimTags } from '../utils/validators';
import { createDeleteUserToken } from '../middlewares/delete-user-token';
const log = debugFactory('fcc:boot:user');
const sendNonUserToHome = ifNoUserRedirectHome();
@ -27,16 +28,19 @@ function bootUser(app) {
const getSessionUser = createReadSessionUser(app);
const postReportUserProfile = createPostReportUserProfile(app);
const postDeleteAccount = createPostDeleteAccount(app);
const postWebhookToken = createPostWebhookToken(app);
const deleteWebhookToken = createDeleteWebhookToken(app);
const postUserToken = createPostUserToken(app);
const deleteUserToken = createDeleteUserToken(app);
api.get('/account', sendNonUserToHome, getAccount);
api.get('/account/unlink/:social', sendNonUserToHome, getUnlinkSocial);
api.get('/user/get-session-user', getSessionUser);
api.post('/account/delete', ifNoUser401, postDeleteAccount);
api.post('/account/reset-progress', ifNoUser401, postResetProgress);
api.post('/user/webhook-token', postWebhookToken);
api.post('/account/delete', ifNoUser401, deleteUserToken, postDeleteAccount);
api.post(
'/account/reset-progress',
ifNoUser401,
deleteUserToken,
postResetProgress
);
api.post(
'/user/report-user/',
ifNoUser401,
@ -44,47 +48,41 @@ function bootUser(app) {
postReportUserProfile
);
api.delete('/user/webhook-token', deleteWebhookToken);
api.post('/user/user-token', ifNoUser401, postUserToken);
api.delete(
'/user/user-token',
ifNoUser401,
deleteUserToken,
deleteUserTokenResponse
);
app.use(api);
}
function createPostWebhookToken(app) {
const { WebhookToken } = app.models;
function createPostUserToken(app) {
const { UserToken } = app.models;
return async function postWebhookToken(req, res) {
return async function postUserToken(req, res) {
const ttl = 900 * 24 * 60 * 60 * 1000;
let newToken;
try {
await WebhookToken.destroyAll({ userId: req.user.id });
newToken = await WebhookToken.create({ ttl, userId: req.user.id });
await UserToken.destroyAll({ userId: req.user.id });
newToken = await UserToken.create({ ttl, userId: req.user.id });
} catch (e) {
return res.status(500).json({
type: 'danger',
message: 'flash.create-token-err'
});
return res.status(500).send('Error starting project');
}
return res.json(newToken?.id);
return res.json({ token: newToken?.id });
};
}
function createDeleteWebhookToken(app) {
const { WebhookToken } = app.models;
function deleteUserTokenResponse(req, res) {
if (!req.userTokenDeleted) {
return res.status(500).send('Error deleting user token');
}
return async function deleteWebhookToken(req, res) {
try {
await WebhookToken.destroyAll({ userId: req.user.id });
} catch (e) {
return res.status(500).json({
type: 'danger',
message: 'flash.delete-token-err'
});
}
return res.json(null);
};
return res.send({ token: null });
}
function createReadSessionUser(app) {
@ -93,10 +91,10 @@ function createReadSessionUser(app) {
return async function getSessionUser(req, res, next) {
const queryUser = req.user;
const webhookTokenArr = await queryUser.webhookTokens({
const userTokenArr = await queryUser.userTokens({
userId: queryUser.id
});
const webhookToken = webhookTokenArr[0]?.id;
const userToken = userTokenArr[0]?.id;
const source =
queryUser &&
@ -153,7 +151,7 @@ function createReadSessionUser(app) {
isWebsite: !!user.website,
...normaliseUserFields(user),
joinDate: user.id.getTimestamp(),
webhookToken
userToken
}
},
sessionMeta,
@ -263,20 +261,8 @@ function postResetProgress(req, res, next) {
}
function createPostDeleteAccount(app) {
const { User, WebhookToken } = app.models;
const { User } = app.models;
return async function postDeleteAccount(req, res, next) {
const {
user: { id: userId }
} = req;
try {
await WebhookToken.destroyAll({ userId });
} catch (err) {
log(
`An error occurred deleting webhook tokens for user with id ${userId} when they tried to delete their account`
);
}
return User.destroyById(req.user.id, function (err) {
if (err) {
return next(err);

View File

@ -0,0 +1,25 @@
import debugFactory from 'debug';
const log = debugFactory('fcc:boot:user');
/*
* User tokens for submitting external curriculum are deleted when they sign
* out, reset their account, or delete their account
*/
export function createDeleteUserToken(app) {
const { UserToken } = app.models;
return async function deleteUserToken(req, res, next) {
try {
await UserToken.destroyAll({ userId: req.user.id });
req.userTokenDeleted = true;
} catch (e) {
req.userTokenDeleted = false;
log(
`An error occurred deleting user token for user with id ${req.user.id}`
);
}
next();
};
}

View File

@ -59,7 +59,7 @@
"dataSource": "db",
"public": false
},
"WebhookToken": {
"UserToken": {
"dataSource": "db",
"public": false
}

View File

@ -1,5 +1,5 @@
{
"name": "WebhookToken",
"name": "UserToken",
"description": "Tokens for submitting curricula through CodeRoad",
"base": "AccessToken",
"idInjection": true,