Skip to content

Commit

Permalink
⏪ Revert removing deprecated register/update avatar APIs and CSRF
Browse files Browse the repository at this point in the history
  • Loading branch information
nwingt committed Apr 19, 2021
1 parent ec95ee5 commit 56ef0a3
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 22 deletions.
82 changes: 66 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"cookie-parser": "^1.4.3",
"cors": "^2.8.5",
"create-hash": "^1.2.0",
"csurf": "^1.9.0",
"disposable-email-domains": "^1.0.56",
"eth-sig-util": "^1.4.2",
"express": "^4.17.0",
Expand Down
5 changes: 5 additions & 0 deletions src/constant/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ export const BUTTON_COOKIE_OPTION = {
sameSite: TEST_MODE ? false : 'none',
};

export const CSRF_COOKIE_OPTION = {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
};

// TODO: duplicate with ../../constant.js
export const W3C_EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
export const EMAIL_REGEX = IS_TESTNET ? /.*/ : W3C_EMAIL_REGEX;
Expand Down
3 changes: 3 additions & 0 deletions src/middleware/errorHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export default function errorHandler(err, req, res, next) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).send('FILE_TOO_LARGE');
}
if (err.code === 'EBADCSRFTOKEN') {
return res.status(400).send('BAD_CSRF_TOKEN');
}
}
return res.sendStatus(500);
}
48 changes: 46 additions & 2 deletions src/routes/users/registerLogin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Router } from 'express';
import bodyParser from 'body-parser';
import csrf from 'csurf';
import {
CSRF_COOKIE_OPTION,
PUBSUB_TOPIC_MISC,
TEST_MODE,
} from '../../constant';
Expand All @@ -21,7 +24,10 @@ import {
normalizeUserEmail,
getUserAgentIsApp,
} from '../../util/api/users';
import { handleUserRegistration } from '../../util/api/users/register';
import {
handleUserRegistration,
getAvatarUrl,
} from '../../util/api/users/register';
import { handleAppReferrer, handleUpdateAppMetaData } from '../../util/api/users/app';
import { ValidationError } from '../../util/ValidationError';
import { handleAvatarUploadAndGetURL } from '../../util/fileupload';
Expand Down Expand Up @@ -63,8 +69,36 @@ const apiLimiter = new RateLimit({

router.use(loginPlatforms);

function csrfCheck(req, res, next) {
const { 'user-agent': userAgent = '' } = req.headers;
if (userAgent.includes('LikeCoinApp')) {
next();
} else {
csrf({ cookie: CSRF_COOKIE_OPTION })(req, res, next);
}
}

// deprecated
function isJson(req) {
return !!req.is('application/json');
}

// deprecated
function loadMiddlewareByContentType(req, res, next) {
if (!isJson(req)) {
csrfCheck(req, res, (csrfErr) => {
if (csrfErr) next(csrfErr);
bodyParser.urlencoded({ extended: false })(req, res, (bpErr) => {
if (bpErr) next(bpErr);
multer.single('avatarFile')(req, res, next);
});
});
} else next();
}

router.post(
'/new',
loadMiddlewareByContentType, // deprecated
apiLimiter,
async (req, res, next) => {
const {
Expand Down Expand Up @@ -192,6 +226,7 @@ router.post(
router.post(
'/update',
jwtAuth('write'),
loadMiddlewareByContentType, // deprecated
async (req, res, next) => {
try {
const { user } = req.user;
Expand All @@ -217,12 +252,21 @@ router.post(
locale: oldLocale,
} = oldUserObj.data();

// update avatar
const updateObj = {
displayName,
isEmailEnabled,
locale,
};

// deprecated
let avatarUrl;
const isLegacyReq = !isJson(req);
if (isLegacyReq) {
avatarUrl = await getAvatarUrl(req, user);
updateObj.avatar = avatarUrl;
}

if (!oldEmail && email) {
await userByEmailQuery(user, email);
updateObj.email = email;
Expand Down Expand Up @@ -255,7 +299,7 @@ router.post(
email: email || oldEmail,
displayName: displayName || oldDisplayName,
wallet,
avatar,
avatar: avatarUrl || avatar,
referrer,
locale: locale || oldLocale,
registerTime: timestamp,
Expand Down
17 changes: 17 additions & 0 deletions src/util/api/users/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,20 @@ export async function handleUserRegistration({
socialPayload,
};
}

// deprecated
export async function getAvatarUrl(req, user) {
const { file } = req;
const { avatarSHA256 } = req.body;
let avatarUrl;
if (file) {
try {
avatarUrl = await handleAvatarUploadAndGetURL(user, file, avatarSHA256);
} catch (err) {
console.error('Avatar file handling error:');
console.error(err);
throw new ValidationError('INVALID_AVATAR');
}
}
return avatarUrl;
}
34 changes: 30 additions & 4 deletions test/api/user.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test.serial('USER: Login user. Case: success', async (t) => {
t.is(res.status, 200);
});

test.serial('USER: Edit user. Case: success', async (t) => {
test.serial('USER: Edit user by JSON. Case: success', async (t) => {
const user = testingUser1;
const token = jwtSign({ user });
const payload = {
Expand All @@ -72,7 +72,7 @@ test.serial('USER: Edit user. Case: success', async (t) => {
t.is(res.status, 200);
});

test.serial('USER: Edit user by form-data. Case: invalid content type', async (t) => {
test.serial('USER: Edit user by form-data. Case: success', async (t) => {
const user = testingUser1;
const token = jwtSign({ user });
const payload = new FormData();
Expand All @@ -83,13 +83,34 @@ test.serial('USER: Edit user by form-data. Case: invalid content type', async (t
payload.append('email', '[email protected]');
const res = await axiosist.post('/api/users/update', payload, {
headers: {
Cookie: `likecoin_auth=${token};`,
Cookie: `likecoin_auth=${token}; _csrf=unit_test`,
'x-csrf-token': '73fb9061-W0SmQvlNKd0uKS4d2nKoZd0u7SA',
...payload.getHeaders(),
},
});

t.is(res.status, 200);
});

test.serial('USER: Edit user by form-data. Case: invalid csrf token', async (t) => {
const user = testingUser1;
const token = jwtSign({ user });
const payload = new FormData();
payload.append('user', user);
payload.append('displayName', testingDisplayName1);
payload.append('ts', Date.now());
payload.append('wallet', testingWallet1);
payload.append('email', '[email protected]');
const res = await axiosist.post('/api/users/update', payload, {
headers: {
Cookie: `likecoin_auth=${token}; _csrf=unit_test`,
'x-csrf-token': 'invalid-token',
...payload.getHeaders(),
},
}).catch(err => err.response);

t.is(res.status, 400);
t.is(res.data, 'INVALID_PAYLOAD');
t.is(res.data, 'BAD_CSRF_TOKEN');
});

test.serial('USER: Update avatar. Case: success', async (t) => {
Expand Down Expand Up @@ -303,6 +324,11 @@ for (let i = 0; i < userCases.length; i += 1) {
payload: formatedPayload,
sign,
platform: 'wallet',
}, {
headers: {
Cookie: '_csrf=unit_test',
'x-csrf-token': '73fb9061-W0SmQvlNKd0uKS4d2nKoZd0u7SA',
},
}).catch(err => err.response);

t.is(res.status, 400);
Expand Down

0 comments on commit 56ef0a3

Please sign in to comment.