123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = exports.UsersRouter = void 0;
- var _node = _interopRequireDefault(require("parse/node"));
- var _Config = _interopRequireDefault(require("../Config"));
- var _AccountLockout = _interopRequireDefault(require("../AccountLockout"));
- var _ClassesRouter = _interopRequireDefault(require("./ClassesRouter"));
- var _rest = _interopRequireDefault(require("../rest"));
- var _Auth = _interopRequireDefault(require("../Auth"));
- var _password = _interopRequireDefault(require("../password"));
- var _triggers = require("../triggers");
- var _middlewares = require("../middlewares");
- var _RestWrite = _interopRequireDefault(require("../RestWrite"));
- var _logger = require("../logger");
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
- class UsersRouter extends _ClassesRouter.default {
- className() {
- return '_User';
- }
-
- static removeHiddenProperties(obj) {
- for (var key in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
-
- if (key !== '__type' && !/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
- delete obj[key];
- }
- }
- }
- }
-
- _sanitizeAuthData(user) {
- delete user.password;
-
-
- if (user.authData) {
- Object.keys(user.authData).forEach(provider => {
- if (user.authData[provider] === null) {
- delete user.authData[provider];
- }
- });
- if (Object.keys(user.authData).length == 0) {
- delete user.authData;
- }
- }
- }
-
- _authenticateUserFromRequest(req) {
- return new Promise((resolve, reject) => {
-
- let payload = req.body;
- if (!payload.username && req.query && req.query.username || !payload.email && req.query && req.query.email) {
- payload = req.query;
- }
- const {
- username,
- email,
- password,
- ignoreEmailVerification
- } = payload;
-
- if (!username && !email) {
- throw new _node.default.Error(_node.default.Error.USERNAME_MISSING, 'username/email is required.');
- }
- if (!password) {
- throw new _node.default.Error(_node.default.Error.PASSWORD_MISSING, 'password is required.');
- }
- if (typeof password !== 'string' || email && typeof email !== 'string' || username && typeof username !== 'string') {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
- }
- let user;
- let isValidPassword = false;
- let query;
- if (email && username) {
- query = {
- email,
- username
- };
- } else if (email) {
- query = {
- email
- };
- } else {
- query = {
- $or: [{
- username
- }, {
- email: username
- }]
- };
- }
- return req.config.database.find('_User', query, {}, _Auth.default.maintenance(req.config)).then(results => {
- if (!results.length) {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
- }
- if (results.length > 1) {
-
- req.config.loggerController.warn("There is a user which email is the same as another user's username, logging in based on username");
- user = results.filter(user => user.username === username)[0];
- } else {
- user = results[0];
- }
- return _password.default.compare(password, user.password);
- }).then(correct => {
- isValidPassword = correct;
- const accountLockoutPolicy = new _AccountLockout.default(user, req.config);
- return accountLockoutPolicy.handleLoginAttempt(isValidPassword);
- }).then(async () => {
- if (!isValidPassword) {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
- }
-
-
-
-
- if (!req.auth.isMaster && user.ACL && Object.keys(user.ACL).length == 0) {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
- }
-
- const request = {
- master: req.auth.isMaster,
- ip: req.config.ip,
- installationId: req.auth.installationId,
- object: _node.default.User.fromJSON(Object.assign({
- className: '_User'
- }, user))
- };
-
- if (!((req.auth.isMaster || req.auth.isMaintenance) && ignoreEmailVerification)) {
-
-
-
- const verifyUserEmails = async () => req.config.verifyUserEmails === true || typeof req.config.verifyUserEmails === 'function' && (await Promise.resolve(req.config.verifyUserEmails(request))) === true;
- const preventLoginWithUnverifiedEmail = async () => req.config.preventLoginWithUnverifiedEmail === true || typeof req.config.preventLoginWithUnverifiedEmail === 'function' && (await Promise.resolve(req.config.preventLoginWithUnverifiedEmail(request))) === true;
- if ((await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail()) && !user.emailVerified) {
- throw new _node.default.Error(_node.default.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
- }
- }
- this._sanitizeAuthData(user);
- return resolve(user);
- }).catch(error => {
- return reject(error);
- });
- });
- }
- handleMe(req) {
- if (!req.info || !req.info.sessionToken) {
- throw new _node.default.Error(_node.default.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
- }
- const sessionToken = req.info.sessionToken;
- return _rest.default.find(req.config, _Auth.default.master(req.config), '_Session', {
- sessionToken
- }, {
- include: 'user'
- }, req.info.clientSDK, req.info.context).then(response => {
- if (!response.results || response.results.length == 0 || !response.results[0].user) {
- throw new _node.default.Error(_node.default.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
- } else {
- const user = response.results[0].user;
-
- user.sessionToken = sessionToken;
-
- UsersRouter.removeHiddenProperties(user);
- return {
- response: user
- };
- }
- });
- }
- async handleLogIn(req) {
- const user = await this._authenticateUserFromRequest(req);
- const authData = req.body && req.body.authData;
-
- _Auth.default.checkIfUserHasProvidedConfiguredProvidersForLogin(req, authData, user.authData, req.config);
- let authDataResponse;
- let validatedAuthData;
- if (authData) {
- const res = await _Auth.default.handleAuthDataValidation(authData, new _RestWrite.default(req.config, req.auth, '_User', {
- objectId: user.objectId
- }, req.body, user, req.info.clientSDK, req.info.context), user);
- authDataResponse = res.authDataResponse;
- validatedAuthData = res.authData;
- }
-
- if (req.config.passwordPolicy && req.config.passwordPolicy.maxPasswordAge) {
- let changedAt = user._password_changed_at;
- if (!changedAt) {
-
-
- changedAt = new Date();
- req.config.database.update('_User', {
- username: user.username
- }, {
- _password_changed_at: _node.default._encode(changedAt)
- });
- } else {
-
- if (changedAt.__type == 'Date') {
- changedAt = new Date(changedAt.iso);
- }
-
- const expiresAt = new Date(changedAt.getTime() + 86400000 * req.config.passwordPolicy.maxPasswordAge);
- if (expiresAt < new Date())
-
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'Your password has expired. Please reset your password.');
- }
- }
-
- UsersRouter.removeHiddenProperties(user);
- await req.config.filesController.expandFilesInObject(req.config, user);
-
- await (0, _triggers.maybeRunTrigger)(_triggers.Types.beforeLogin, req.auth, _node.default.User.fromJSON(Object.assign({
- className: '_User'
- }, user)), null, req.config, req.info.context);
-
- if (validatedAuthData && Object.keys(validatedAuthData).length) {
- await req.config.database.update('_User', {
- objectId: user.objectId
- }, {
- authData: validatedAuthData
- }, {});
- }
- const {
- sessionData,
- createSession
- } = _RestWrite.default.createSession(req.config, {
- userId: user.objectId,
- createdWith: {
- action: 'login',
- authProvider: 'password'
- },
- installationId: req.info.installationId
- });
- user.sessionToken = sessionData.sessionToken;
- await createSession();
- const afterLoginUser = _node.default.User.fromJSON(Object.assign({
- className: '_User'
- }, user));
- await (0, _triggers.maybeRunTrigger)(_triggers.Types.afterLogin, _objectSpread(_objectSpread({}, req.auth), {}, {
- user: afterLoginUser
- }), afterLoginUser, null, req.config, req.info.context);
- if (authDataResponse) {
- user.authDataResponse = authDataResponse;
- }
- await req.config.authDataManager.runAfterFind(req, user.authData);
- return {
- response: user
- };
- }
-
- async handleLogInAs(req) {
- if (!req.auth.isMaster) {
- throw new _node.default.Error(_node.default.Error.OPERATION_FORBIDDEN, 'master key is required');
- }
- const userId = req.body.userId || req.query.userId;
- if (!userId) {
- throw new _node.default.Error(_node.default.Error.INVALID_VALUE, 'userId must not be empty, null, or undefined');
- }
- const queryResults = await req.config.database.find('_User', {
- objectId: userId
- });
- const user = queryResults[0];
- if (!user) {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'user not found');
- }
- this._sanitizeAuthData(user);
- const {
- sessionData,
- createSession
- } = _RestWrite.default.createSession(req.config, {
- userId,
- createdWith: {
- action: 'login',
- authProvider: 'masterkey'
- },
- installationId: req.info.installationId
- });
- user.sessionToken = sessionData.sessionToken;
- await createSession();
- return {
- response: user
- };
- }
- handleVerifyPassword(req) {
- return this._authenticateUserFromRequest(req).then(user => {
-
- UsersRouter.removeHiddenProperties(user);
- return {
- response: user
- };
- }).catch(error => {
- throw error;
- });
- }
- async handleLogOut(req) {
- const success = {
- response: {}
- };
- if (req.info && req.info.sessionToken) {
- const records = await _rest.default.find(req.config, _Auth.default.master(req.config), '_Session', {
- sessionToken: req.info.sessionToken
- }, undefined, req.info.clientSDK, req.info.context);
- if (records.results && records.results.length) {
- await _rest.default.del(req.config, _Auth.default.master(req.config), '_Session', records.results[0].objectId, req.info.context);
- await (0, _triggers.maybeRunTrigger)(_triggers.Types.afterLogout, req.auth, _node.default.Session.fromJSON(Object.assign({
- className: '_Session'
- }, records.results[0])), null, req.config);
- }
- }
- return success;
- }
- _throwOnBadEmailConfig(req) {
- try {
- _Config.default.validateEmailConfiguration({
- emailAdapter: req.config.userController.adapter,
- appName: req.config.appName,
- publicServerURL: req.config.publicServerURL,
- emailVerifyTokenValidityDuration: req.config.emailVerifyTokenValidityDuration,
- emailVerifyTokenReuseIfValid: req.config.emailVerifyTokenReuseIfValid
- });
- } catch (e) {
- if (typeof e === 'string') {
-
- throw new _node.default.Error(_node.default.Error.INTERNAL_SERVER_ERROR, 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.');
- } else {
- throw e;
- }
- }
- }
- async handleResetRequest(req) {
- this._throwOnBadEmailConfig(req);
- const {
- email
- } = req.body;
- if (!email) {
- throw new _node.default.Error(_node.default.Error.EMAIL_MISSING, 'you must provide an email');
- }
- if (typeof email !== 'string') {
- throw new _node.default.Error(_node.default.Error.INVALID_EMAIL_ADDRESS, 'you must provide a valid email string');
- }
- const userController = req.config.userController;
- try {
- await userController.sendPasswordResetEmail(email);
- return {
- response: {}
- };
- } catch (err) {
- if (err.code === _node.default.Error.OBJECT_NOT_FOUND) {
- var _req$config$passwordP;
- if (((_req$config$passwordP = req.config.passwordPolicy) === null || _req$config$passwordP === void 0 ? void 0 : _req$config$passwordP.resetPasswordSuccessOnInvalidEmail) ?? true) {
- return {
- response: {}
- };
- }
- err.message = `A user with that email does not exist.`;
- }
- throw err;
- }
- }
- async handleVerificationEmailRequest(req) {
- this._throwOnBadEmailConfig(req);
- const {
- email
- } = req.body;
- if (!email) {
- throw new _node.default.Error(_node.default.Error.EMAIL_MISSING, 'you must provide an email');
- }
- if (typeof email !== 'string') {
- throw new _node.default.Error(_node.default.Error.INVALID_EMAIL_ADDRESS, 'you must provide a valid email string');
- }
- const results = await req.config.database.find('_User', {
- email: email
- }, {}, _Auth.default.maintenance(req.config));
- if (!results.length || results.length < 1) {
- throw new _node.default.Error(_node.default.Error.EMAIL_NOT_FOUND, `No user found with email ${email}`);
- }
- const user = results[0];
-
- delete user.password;
- if (user.emailVerified) {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, `Email ${email} is already verified.`);
- }
- const userController = req.config.userController;
- const send = await userController.regenerateEmailVerifyToken(user, req.auth.isMaster, req.auth.installationId, req.ip);
- if (send) {
- userController.sendVerificationEmail(user, req);
- }
- return {
- response: {}
- };
- }
- async handleChallenge(req) {
- const {
- username,
- email,
- password,
- authData,
- challengeData
- } = req.body;
-
- let user;
- if (username || email) {
- if (!password) {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'You provided username or email, you need to also provide password.');
- }
- user = await this._authenticateUserFromRequest(req);
- }
- if (!challengeData) {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'Nothing to challenge.');
- }
- if (typeof challengeData !== 'object') {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'challengeData should be an object.');
- }
- let request;
- let parseUser;
-
- if (authData) {
- if (typeof authData !== 'object') {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'authData should be an object.');
- }
- if (user) {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'You cannot provide username/email and authData, only use one identification method.');
- }
- if (Object.keys(authData).filter(key => authData[key].id).length > 1) {
- throw new _node.default.Error(_node.default.Error.OTHER_CAUSE, 'You cannot provide more than one authData provider with an id.');
- }
- const results = await _Auth.default.findUsersWithAuthData(req.config, authData);
- try {
- if (!results[0] || results.length > 1) {
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'User not found.');
- }
-
- const provider = Object.keys(authData).find(key => authData[key].id);
- parseUser = _node.default.User.fromJSON(_objectSpread({
- className: '_User'
- }, results[0]));
- request = (0, _triggers.getRequestObject)(undefined, req.auth, parseUser, parseUser, req.config);
- request.isChallenge = true;
-
- const {
- validator
- } = req.config.authDataManager.getValidatorForProvider(provider);
- const validatorResponse = await validator(authData[provider], req, parseUser, request);
- if (validatorResponse && validatorResponse.validator) {
- await validatorResponse.validator();
- }
- } catch (e) {
-
- _logger.logger.error(e);
- throw new _node.default.Error(_node.default.Error.OBJECT_NOT_FOUND, 'User not found.');
- }
- }
- if (!parseUser) {
- parseUser = user ? _node.default.User.fromJSON(_objectSpread({
- className: '_User'
- }, user)) : undefined;
- }
- if (!request) {
- request = (0, _triggers.getRequestObject)(undefined, req.auth, parseUser, parseUser, req.config);
- request.isChallenge = true;
- }
- const acc = {};
-
-
- for (const provider of Object.keys(challengeData).sort()) {
- try {
- const authAdapter = req.config.authDataManager.getValidatorForProvider(provider);
- if (!authAdapter) {
- continue;
- }
- const {
- adapter: {
- challenge
- }
- } = authAdapter;
- if (typeof challenge === 'function') {
- const providerChallengeResponse = await challenge(challengeData[provider], authData && authData[provider], req.config.auth[provider], request);
- acc[provider] = providerChallengeResponse || true;
- }
- } catch (err) {
- const e = (0, _triggers.resolveError)(err, {
- code: _node.default.Error.SCRIPT_FAILED,
- message: 'Challenge failed. Unknown error.'
- });
- const userString = req.auth && req.auth.user ? req.auth.user.id : undefined;
- _logger.logger.error(`Failed running auth step challenge for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), {
- authenticationStep: 'challenge',
- error: e,
- user: userString,
- provider
- });
- throw e;
- }
- }
- return {
- response: {
- challengeData: acc
- }
- };
- }
- mountRoutes() {
- this.route('GET', '/users', req => {
- return this.handleFind(req);
- });
- this.route('POST', '/users', _middlewares.promiseEnsureIdempotency, req => {
- return this.handleCreate(req);
- });
- this.route('GET', '/users/me', req => {
- return this.handleMe(req);
- });
- this.route('GET', '/users/:objectId', req => {
- return this.handleGet(req);
- });
- this.route('PUT', '/users/:objectId', _middlewares.promiseEnsureIdempotency, req => {
- return this.handleUpdate(req);
- });
- this.route('DELETE', '/users/:objectId', req => {
- return this.handleDelete(req);
- });
- this.route('GET', '/login', req => {
- return this.handleLogIn(req);
- });
- this.route('POST', '/login', req => {
- return this.handleLogIn(req);
- });
- this.route('POST', '/loginAs', req => {
- return this.handleLogInAs(req);
- });
- this.route('POST', '/logout', req => {
- return this.handleLogOut(req);
- });
- this.route('POST', '/requestPasswordReset', req => {
- return this.handleResetRequest(req);
- });
- this.route('POST', '/verificationEmailRequest', req => {
- return this.handleVerificationEmailRequest(req);
- });
- this.route('GET', '/verifyPassword', req => {
- return this.handleVerifyPassword(req);
- });
- this.route('POST', '/verifyPassword', req => {
- return this.handleVerifyPassword(req);
- });
- this.route('POST', '/challenge', req => {
- return this.handleChallenge(req);
- });
- }
- }
- exports.UsersRouter = UsersRouter;
- var _default = exports.default = UsersRouter;
|