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:
@ -341,9 +341,9 @@
|
||||
"model": "article",
|
||||
"foreignKey": "externalId"
|
||||
},
|
||||
"webhookTokens": {
|
||||
"userTokens": {
|
||||
"type": "hasMany",
|
||||
"model": "WebhookToken",
|
||||
"model": "UserToken",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
|
@ -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 => {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
25
api-server/src/server/middlewares/delete-user-token.js
Normal file
25
api-server/src/server/middlewares/delete-user-token.js
Normal 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();
|
||||
};
|
||||
}
|
@ -59,7 +59,7 @@
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
},
|
||||
"WebhookToken": {
|
||||
"UserToken": {
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "WebhookToken",
|
||||
"name": "UserToken",
|
||||
"description": "Tokens for submitting curricula through CodeRoad",
|
||||
"base": "AccessToken",
|
||||
"idInjection": true,
|
Reference in New Issue
Block a user