2016-02-25 19:04:27 -05:00
|
|
|
import PromiseRouter from '../PromiseRouter';
|
|
|
|
|
import Config from '../Config';
|
|
|
|
|
import express from 'express';
|
|
|
|
|
import path from 'path';
|
2016-02-27 10:51:12 -05:00
|
|
|
import fs from 'fs';
|
2016-03-12 10:27:34 -05:00
|
|
|
import qs from 'querystring';
|
2019-03-14 23:06:18 +02:00
|
|
|
import { Parse } from 'parse/node';
|
2025-01-12 11:59:40 +11:00
|
|
|
import Deprecator from '../Deprecator/Deprecator';
|
2016-02-27 10:51:12 -05:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
const public_html = path.resolve(__dirname, '../../public_html');
|
2016-12-07 15:17:05 -08:00
|
|
|
const views = path.resolve(__dirname, '../../views');
|
2016-02-25 19:04:27 -05:00
|
|
|
|
|
|
|
|
export class PublicAPIRouter extends PromiseRouter {
|
2025-01-12 11:59:40 +11:00
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
Deprecator.logRuntimeDeprecation({
|
|
|
|
|
usage: 'PublicAPIRouter',
|
|
|
|
|
solution: 'pages.enableRouter'
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-02-25 19:04:27 -05:00
|
|
|
verifyEmail(req) {
|
2025-03-02 12:32:43 +11:00
|
|
|
const { token: rawToken } = req.query;
|
2020-10-25 15:06:58 -05:00
|
|
|
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
2020-03-02 15:46:01 -08:00
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const appId = req.params.appId;
|
2017-10-23 08:43:05 -04:00
|
|
|
const config = Config.get(appId);
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!config) {
|
2017-11-11 22:42:20 +08:00
|
|
|
this.invalidRequest();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 20:51:13 -05:00
|
|
|
if (!config.publicServerURL) {
|
|
|
|
|
return this.missingPublicServerURL();
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
if (!token) {
|
2016-02-27 10:51:12 -05:00
|
|
|
return this.invalidLink(req);
|
2016-02-25 19:04:27 -05:00
|
|
|
}
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
const userController = config.userController;
|
2025-03-02 12:32:43 +11:00
|
|
|
return userController.verifyEmail(token).then(
|
2018-09-01 13:58:06 -04:00
|
|
|
() => {
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
2025-03-02 12:32:43 +11:00
|
|
|
location: `${config.verifyEmailSuccessURL}`,
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
() => {
|
2025-03-02 12:32:43 +11:00
|
|
|
return this.invalidVerificationLink(req, token);
|
2018-09-01 13:58:06 -04:00
|
|
|
}
|
|
|
|
|
);
|
2017-05-10 14:02:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resendVerificationEmail(req) {
|
2025-03-03 16:11:42 -05:00
|
|
|
const username = req.body?.username;
|
2017-05-10 14:02:16 +01:00
|
|
|
const appId = req.params.appId;
|
2017-10-23 08:43:05 -04:00
|
|
|
const config = Config.get(appId);
|
2017-05-10 14:02:16 +01:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!config) {
|
2017-11-11 22:42:20 +08:00
|
|
|
this.invalidRequest();
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-10 14:02:16 +01:00
|
|
|
if (!config.publicServerURL) {
|
|
|
|
|
return this.missingPublicServerURL();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
const token = req.body.token;
|
|
|
|
|
|
|
|
|
|
if (!username && !token) {
|
2016-02-27 10:51:12 -05:00
|
|
|
return this.invalidLink(req);
|
2017-05-10 14:02:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userController = config.userController;
|
|
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
return userController.resendVerificationEmail(username, req, token).then(
|
2018-09-01 13:58:06 -04:00
|
|
|
() => {
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
|
|
|
|
location: `${config.linkSendSuccessURL}`,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
|
|
|
|
location: `${config.linkSendFailURL}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-02-27 10:51:12 -05:00
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
changePassword(req) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2017-10-23 08:43:05 -04:00
|
|
|
const config = Config.get(req.query.id);
|
2017-11-11 22:42:20 +08:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!config) {
|
2017-11-11 22:42:20 +08:00
|
|
|
this.invalidRequest();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 20:51:13 -05:00
|
|
|
if (!config.publicServerURL) {
|
|
|
|
|
return resolve({
|
2016-02-27 14:46:29 -05:00
|
|
|
status: 404,
|
2018-09-01 13:58:06 -04:00
|
|
|
text: 'Not found.',
|
2016-02-27 14:46:29 -05:00
|
|
|
});
|
|
|
|
|
}
|
2016-02-27 10:51:12 -05:00
|
|
|
// Should we keep the file in memory or leave like that?
|
2020-10-25 15:06:58 -05:00
|
|
|
fs.readFile(path.resolve(views, 'choose_password'), 'utf-8', (err, data) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
return reject(err);
|
2016-02-27 10:51:12 -05:00
|
|
|
}
|
2020-10-25 15:06:58 -05:00
|
|
|
data = data.replace('PARSE_SERVER_URL', `'${config.publicServerURL}'`);
|
|
|
|
|
resolve({
|
|
|
|
|
text: data,
|
|
|
|
|
});
|
|
|
|
|
});
|
2016-02-27 10:51:12 -05:00
|
|
|
});
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
requestResetPassword(req) {
|
2016-12-07 15:17:05 -08:00
|
|
|
const config = req.config;
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!config) {
|
2017-11-11 22:42:20 +08:00
|
|
|
this.invalidRequest();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 20:51:13 -05:00
|
|
|
if (!config.publicServerURL) {
|
|
|
|
|
return this.missingPublicServerURL();
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
const { token: rawToken } = req.query;
|
2020-10-25 15:06:58 -05:00
|
|
|
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
if (!token) {
|
2016-02-27 10:51:12 -05:00
|
|
|
return this.invalidLink(req);
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
return config.userController.checkResetTokenValidity(token).then(
|
2018-09-01 13:58:06 -04:00
|
|
|
() => {
|
|
|
|
|
const params = qs.stringify({
|
|
|
|
|
token,
|
|
|
|
|
id: config.applicationId,
|
|
|
|
|
app: config.appName,
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
|
|
|
|
location: `${config.choosePasswordURL}?${params}`,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
return this.invalidLink(req);
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-02-25 19:04:27 -05:00
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
resetPassword(req) {
|
2016-12-07 15:17:05 -08:00
|
|
|
const config = req.config;
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
if (!config) {
|
2017-11-11 22:42:20 +08:00
|
|
|
this.invalidRequest();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 20:51:13 -05:00
|
|
|
if (!config.publicServerURL) {
|
|
|
|
|
return this.missingPublicServerURL();
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-03 16:11:42 -05:00
|
|
|
const { new_password, token: rawToken } = req.body || {};
|
2020-10-25 15:06:58 -05:00
|
|
|
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
if ((!token || !new_password) && req.xhr === false) {
|
2016-02-27 14:46:29 -05:00
|
|
|
return this.invalidLink(req);
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2019-03-14 23:06:18 +02:00
|
|
|
if (!token) {
|
|
|
|
|
throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!new_password) {
|
|
|
|
|
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password');
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
return config.userController
|
2025-03-02 12:32:43 +11:00
|
|
|
.updatePassword(token, new_password)
|
2018-09-01 13:58:06 -04:00
|
|
|
.then(
|
|
|
|
|
() => {
|
|
|
|
|
return Promise.resolve({
|
2019-03-14 23:06:18 +02:00
|
|
|
success: true,
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
|
|
|
|
},
|
2020-07-13 17:13:08 -05:00
|
|
|
err => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return Promise.resolve({
|
2019-03-14 23:06:18 +02:00
|
|
|
success: false,
|
|
|
|
|
err,
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
|
|
|
|
}
|
2019-03-14 23:06:18 +02:00
|
|
|
)
|
2020-07-13 17:13:08 -05:00
|
|
|
.then(result => {
|
2025-03-02 12:32:43 +11:00
|
|
|
const queryString = {
|
2019-03-14 23:06:18 +02:00
|
|
|
token: token,
|
|
|
|
|
id: config.applicationId,
|
|
|
|
|
error: result.err,
|
|
|
|
|
app: config.appName,
|
2025-03-02 12:32:43 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (result?.err === 'The password reset link has expired') {
|
|
|
|
|
delete queryString.token;
|
|
|
|
|
queryString.token = token;
|
|
|
|
|
}
|
|
|
|
|
const params = qs.stringify(queryString);
|
2019-03-14 23:06:18 +02:00
|
|
|
|
|
|
|
|
if (req.xhr) {
|
|
|
|
|
if (result.success) {
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 200,
|
|
|
|
|
response: 'Password successfully reset',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (result.err) {
|
|
|
|
|
throw new Parse.Error(Parse.Error.OTHER_CAUSE, `${result.err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-17 03:15:29 +01:00
|
|
|
const location = result.success
|
2025-03-02 12:32:43 +11:00
|
|
|
? `${config.passwordResetSuccessURL}`
|
2019-12-17 03:15:29 +01:00
|
|
|
: `${config.choosePasswordURL}?${params}`;
|
|
|
|
|
|
2019-03-14 23:06:18 +02:00
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
2019-12-17 03:15:29 +01:00
|
|
|
location,
|
2019-03-14 23:06:18 +02:00
|
|
|
});
|
|
|
|
|
});
|
2016-02-27 14:46:29 -05:00
|
|
|
}
|
2016-02-27 10:51:12 -05:00
|
|
|
|
|
|
|
|
invalidLink(req) {
|
|
|
|
|
return Promise.resolve({
|
2016-11-24 15:47:41 -05:00
|
|
|
status: 302,
|
2018-09-01 13:58:06 -04:00
|
|
|
location: req.config.invalidLinkURL,
|
2016-02-27 10:51:12 -05:00
|
|
|
});
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2025-03-02 12:32:43 +11:00
|
|
|
invalidVerificationLink(req, token) {
|
2017-05-10 14:02:16 +01:00
|
|
|
const config = req.config;
|
2025-03-02 12:32:43 +11:00
|
|
|
if (req.params.appId) {
|
2018-09-01 13:58:06 -04:00
|
|
|
const params = qs.stringify({
|
|
|
|
|
appId: req.params.appId,
|
2025-03-02 12:32:43 +11:00
|
|
|
token,
|
2018-09-01 13:58:06 -04:00
|
|
|
});
|
2017-05-10 14:02:16 +01:00
|
|
|
return Promise.resolve({
|
|
|
|
|
status: 302,
|
2018-09-01 13:58:06 -04:00
|
|
|
location: `${config.invalidVerificationLinkURL}?${params}`,
|
2017-05-10 14:02:16 +01:00
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
return this.invalidLink(req);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-29 20:51:13 -05:00
|
|
|
missingPublicServerURL() {
|
|
|
|
|
return Promise.resolve({
|
2018-09-01 13:58:06 -04:00
|
|
|
text: 'Not found.',
|
|
|
|
|
status: 404,
|
2016-02-29 20:51:13 -05:00
|
|
|
});
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2017-11-11 22:42:20 +08:00
|
|
|
invalidRequest() {
|
|
|
|
|
const error = new Error();
|
|
|
|
|
error.status = 403;
|
2018-09-01 13:58:06 -04:00
|
|
|
error.message = 'unauthorized';
|
2017-11-11 22:42:20 +08:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
setConfig(req) {
|
2017-10-23 08:43:05 -04:00
|
|
|
req.config = Config.get(req.params.appId);
|
2016-02-27 10:51:12 -05:00
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2016-02-25 19:04:27 -05:00
|
|
|
mountRoutes() {
|
2018-09-01 13:58:06 -04:00
|
|
|
this.route(
|
|
|
|
|
'GET',
|
|
|
|
|
'/apps/:appId/verify_email',
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
this.setConfig(req);
|
|
|
|
|
},
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return this.verifyEmail(req);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.route(
|
|
|
|
|
'POST',
|
|
|
|
|
'/apps/:appId/resend_verification_email',
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
this.setConfig(req);
|
|
|
|
|
},
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return this.resendVerificationEmail(req);
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2020-07-13 17:13:08 -05:00
|
|
|
this.route('GET', '/apps/choose_password', req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return this.changePassword(req);
|
|
|
|
|
});
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
this.route(
|
|
|
|
|
'POST',
|
|
|
|
|
'/apps/:appId/request_password_reset',
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
this.setConfig(req);
|
|
|
|
|
},
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return this.resetPassword(req);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.route(
|
|
|
|
|
'GET',
|
|
|
|
|
'/apps/:appId/request_password_reset',
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
this.setConfig(req);
|
|
|
|
|
},
|
2020-07-13 17:13:08 -05:00
|
|
|
req => {
|
2018-09-01 13:58:06 -04:00
|
|
|
return this.requestResetPassword(req);
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-02-25 19:04:27 -05:00
|
|
|
}
|
2016-03-12 10:27:34 -05:00
|
|
|
|
2016-08-07 23:02:53 -04:00
|
|
|
expressRouter() {
|
2016-12-07 15:17:05 -08:00
|
|
|
const router = express.Router();
|
2018-09-01 13:58:06 -04:00
|
|
|
router.use('/apps', express.static(public_html));
|
|
|
|
|
router.use('/', super.expressRouter());
|
2016-02-25 19:04:27 -05:00
|
|
|
return router;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default PublicAPIRouter;
|