From ab841b5ab475b32a11549c942a9e4663f63b9163 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Thu, 11 Feb 2016 20:01:14 -0800 Subject: [PATCH 1/3] Refactor and deduplicate logic in UsersRouter. --- src/Routers/UsersRouter.js | 163 ++++++++++++++++++++++++++++ src/index.js | 3 +- src/users.js | 212 ------------------------------------- 3 files changed, 165 insertions(+), 213 deletions(-) create mode 100644 src/Routers/UsersRouter.js delete mode 100644 src/users.js diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js new file mode 100644 index 0000000000..4f22e07a27 --- /dev/null +++ b/src/Routers/UsersRouter.js @@ -0,0 +1,163 @@ +// These methods handle the User-related routes. + +import hat from 'hat'; +import deepcopy from 'deepcopy'; + +import ClassesRouter from './ClassesRouter'; +import PromiseRouter from '../PromiseRouter'; +import rest from '../rest'; +import Auth from '../Auth'; +import passwordCrypto from '../password'; +import RestWrite from '../RestWrite'; + +const rack = hat.rack(); + +export class UsersRouter extends ClassesRouter { + handleFind(req) { + req.params.className = '_User'; + return super.handleFind(req); + } + + handleGet(req) { + req.params.className = '_User'; + return super.handleGet(req); + } + + handleCreate(req) { + let data = deepcopy(req.body); + data.installationId = req.info.installationId; + req.body = data; + req.params.className = '_User'; + return super.handleCreate(req); + } + + handleUpdate(req) { + req.params.className = '_User'; + return super.handleUpdate(req); + } + + handleDelete(req) { + req.params.className = '_User'; + return super.handleDelete(req); + } + + handleMe(req) { + if (!req.info || !req.info.sessionToken) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, + 'Object not found.'); + } + return rest.find(req.config, Auth.master(req.config), '_Session', + { _session_token: req.info.sessionToken }, + { include: 'user' }) + .then((response) => { + if (!response.results || + response.results.length == 0 || + !response.results[0].user) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, + 'Object not found.'); + } else { + let user = response.results[0].user; + return { response: user }; + } + }); + } + + handleLogIn(req) { + // Use query parameters instead if provided in url + if (!req.body.username && req.query.username) { + req.body = req.query; + } + + // TODO: use the right error codes / descriptions. + if (!req.body.username) { + throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username is required.'); + } + if (!req.body.password) { + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.'); + } + + let user; + return req.database.find('_User', { username: req.body.username }) + .then((results) => { + if (!results.length) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + user = results[0]; + return passwordCrypto.compare(req.body.password, user.password); + }).then((correct) => { + if (!correct) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + + let token = 'r:' + rack(); + user.sessionToken = token; + delete user.password; + + req.config.filesController.expandFilesInObject(req.config, user); + + let expiresAt = new Date(); + expiresAt.setFullYear(expiresAt.getFullYear() + 1); + + let sessionData = { + sessionToken: token, + user: { + __type: 'Pointer', + className: '_User', + objectId: user.objectId + }, + createdWith: { + 'action': 'login', + 'authProvider': 'password' + }, + restricted: false, + expiresAt: Parse._encode(expiresAt) + }; + + if (req.info.installationId) { + sessionData.installationId = req.info.installationId + } + + let create = new RestWrite(req.config, Auth.master(req.config), '_Session', null, sessionData); + return create.execute(); + }).then(() => { + return { response: user }; + }); + } + + handleLogOut(req) { + let success = {response: {}}; + if (req.info && req.info.sessionToken) { + return rest.find(req.config, Auth.master(req.config), '_Session', + { _session_token: req.info.sessionToken } + ).then((records) => { + if (records.results && records.results.length) { + return rest.del(req.config, Auth.master(req.config), '_Session', + records.results[0].objectId + ).then(() => { + return Promise.resolve(success); + }); + } + return Promise.resolve(success); + }); + } + return Promise.resolve(success); + } + + getExpressRouter() { + let router = new PromiseRouter(); + router.route('GET', '/users', req => { return this.handleFind(req); }); + router.route('POST', '/users', req => { return this.handleCreate(req); }); + router.route('GET', '/users/:objectId', req => { return this.handleGet(req); }); + router.route('PUT', '/users/:objectId', req => { return this.handleUpdate(req); }); + router.route('DELETE', '/users/:objectId', req => { return this.handleDelete(req); }); + router.route('GET', '/users/me', req => { return this.handleMe(req); }); + router.route('GET', '/login', req => { return this.handleLogIn(req); }); + router.route('POST', '/logout', req => { return this.handleLogOut(req); }); + router.route('POST', '/requestPasswordReset', () => { + throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, 'This path is not implemented yet.'); + }); + return router; + } +} + +export default UsersRouter; diff --git a/src/index.js b/src/index.js index c29934000b..fcc573e23c 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,7 @@ import { PushController } from './Controllers/PushController'; import { ClassesRouter } from './Routers/ClassesRouter'; import { InstallationsRouter } from './Routers/InstallationsRouter'; +import { UsersRouter } from './Routers/UsersRouter'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -129,7 +130,7 @@ function ParseServer(args) { let routers = [ new ClassesRouter().getExpressRouter(), - require('./users'), + new UsersRouter().getExpressRouter(), require('./sessions'), require('./roles'), require('./analytics'), diff --git a/src/users.js b/src/users.js deleted file mode 100644 index 4205c66631..0000000000 --- a/src/users.js +++ /dev/null @@ -1,212 +0,0 @@ -// These methods handle the User-related routes. - -var mongodb = require('mongodb'); -var Parse = require('parse/node').Parse; -var rack = require('hat').rack(); - -var Auth = require('./Auth'); -var passwordCrypto = require('./password'); -var facebook = require('./facebook'); -var PromiseRouter = require('./PromiseRouter'); -var rest = require('./rest'); -var RestWrite = require('./RestWrite'); -var deepcopy = require('deepcopy'); - -var router = new PromiseRouter(); - -// Returns a promise for a {status, response, location} object. -function handleCreate(req) { - var data = deepcopy(req.body); - data.installationId = req.info.installationId; - return rest.create(req.config, req.auth, - '_User', data); -} - -// Returns a promise for a {response} object. -function handleLogIn(req) { - - // Use query parameters instead if provided in url - if (!req.body.username && req.query.username) { - req.body = req.query; - } - - // TODO: use the right error codes / descriptions. - if (!req.body.username) { - throw new Parse.Error(Parse.Error.USERNAME_MISSING, - 'username is required.'); - } - if (!req.body.password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, - 'password is required.'); - } - - var user; - return req.database.find('_User', {username: req.body.username}) - .then((results) => { - if (!results.length) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.'); - } - user = results[0]; - return passwordCrypto.compare(req.body.password, user.password); - }).then((correct) => { - if (!correct) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.'); - } - var token = 'r:' + rack(); - user.sessionToken = token; - delete user.password; - - req.config.filesController.expandFilesInObject(req.config, user); - - var expiresAt = new Date(); - expiresAt.setFullYear(expiresAt.getFullYear() + 1); - - var sessionData = { - sessionToken: token, - user: { - __type: 'Pointer', - className: '_User', - objectId: user.objectId - }, - createdWith: { - 'action': 'login', - 'authProvider': 'password' - }, - restricted: false, - expiresAt: Parse._encode(expiresAt) - }; - - if (req.info.installationId) { - sessionData.installationId = req.info.installationId - } - - var create = new RestWrite(req.config, Auth.master(req.config), - '_Session', null, sessionData); - return create.execute(); - }).then(() => { - return {response: user}; - }); -} - -// Returns a promise that resolves to a {response} object. -// TODO: share code with ClassesRouter.js -function handleFind(req) { - var options = {}; - if (req.body.skip) { - options.skip = Number(req.body.skip); - } - if (req.body.limit) { - options.limit = Number(req.body.limit); - } - if (req.body.order) { - options.order = String(req.body.order); - } - if (req.body.count) { - options.count = true; - } - if (typeof req.body.keys == 'string') { - options.keys = req.body.keys; - } - if (req.body.include) { - options.include = String(req.body.include); - } - if (req.body.redirectClassNameForKey) { - options.redirectClassNameForKey = String(req.body.redirectClassNameForKey); - } - - return rest.find(req.config, req.auth, - '_User', req.body.where, options) - .then((response) => { - return {response: response}; - }); - -} - -// Returns a promise for a {response} object. -function handleGet(req) { - return rest.find(req.config, req.auth, '_User', - {objectId: req.params.objectId}) - .then((response) => { - if (!response.results || response.results.length == 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.'); - } else { - return {response: response.results[0]}; - } - }); -} - -function handleMe(req) { - if (!req.info || !req.info.sessionToken) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.'); - } - return rest.find(req.config, Auth.master(req.config), '_Session', - {_session_token: req.info.sessionToken}, - {include: 'user'}) - .then((response) => { - if (!response.results || response.results.length == 0 || - !response.results[0].user) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.'); - } else { - var user = response.results[0].user; - return {response: user}; - } - }); -} - -function handleDelete(req) { - return rest.del(req.config, req.auth, - req.params.className, req.params.objectId) - .then(() => { - return {response: {}}; - }); -} - -function handleLogOut(req) { - var success = {response: {}}; - if (req.info && req.info.sessionToken) { - return rest.find(req.config, Auth.master(req.config), '_Session', - {_session_token: req.info.sessionToken} - ).then((records) => { - if (records.results && records.results.length) { - return rest.del(req.config, Auth.master(req.config), '_Session', - records.results[0].objectId - ).then(() => { - return Promise.resolve(success); - }); - } - return Promise.resolve(success); - }); - } - return Promise.resolve(success); -} - -function handleUpdate(req) { - return rest.update(req.config, req.auth, '_User', - req.params.objectId, req.body) - .then((response) => { - return {response: response}; - }); -} - -function notImplementedYet(req) { - throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, - 'This path is not implemented yet.'); -} - -router.route('POST', '/users', handleCreate); -router.route('GET', '/login', handleLogIn); -router.route('POST', '/logout', handleLogOut); -router.route('GET', '/users/me', handleMe); -router.route('GET', '/users/:objectId', handleGet); -router.route('PUT', '/users/:objectId', handleUpdate); -router.route('GET', '/users', handleFind); -router.route('DELETE', '/users/:objectId', handleDelete); - -router.route('POST', '/requestPasswordReset', notImplementedYet); - -module.exports = router; From 99ac6c11610ea42bde65aac4b1b496d53c6e3bea Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Thu, 11 Feb 2016 20:40:15 -0800 Subject: [PATCH 2/3] Refactor and deduplicate logic in SessionsRouter. --- src/Routers/SessionsRouter.js | 63 ++++++++++++++++++++++ src/index.js | 3 +- src/sessions.js | 98 ----------------------------------- 3 files changed, 65 insertions(+), 99 deletions(-) create mode 100644 src/Routers/SessionsRouter.js delete mode 100644 src/sessions.js diff --git a/src/Routers/SessionsRouter.js b/src/Routers/SessionsRouter.js new file mode 100644 index 0000000000..ecffd80ac7 --- /dev/null +++ b/src/Routers/SessionsRouter.js @@ -0,0 +1,63 @@ + +import ClassesRouter from './ClassesRouter'; +import PromiseRouter from '../PromiseRouter'; +import rest from '../rest'; +import Auth from '../Auth'; + +export class SessionsRouter extends ClassesRouter { + handleFind(req) { + req.params.className = '_Session'; + return super.handleFind(req); + } + + handleGet(req) { + req.params.className = '_Session'; + return super.handleGet(req); + } + + handleCreate(req) { + req.params.className = '_Session'; + return super.handleCreate(req); + } + + handleUpdate(req) { + req.params.className = '_Session'; + return super.handleUpdate(req); + } + + handleDelete(req) { + req.params.className = '_Session'; + return super.handleDelete(req); + } + + handleMe(req) { + // TODO: Verify correct behavior + if (!req.info || !req.info.sessionToken) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, + 'Session token required.'); + } + return rest.find(req.config, Auth.master(req.config), '_Session', { _session_token: req.info.sessionToken }) + .then((response) => { + if (!response.results || response.results.length == 0) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, + 'Session token not found.'); + } + return { + response: response.results[0] + }; + }); + } + + getExpressRouter() { + let router = new PromiseRouter(); + router.route('GET','/sessions/me', req => { return this.handleMe(req); }); + router.route('GET', '/sessions', req => { return this.handleFind(req); }); + router.route('GET', '/sessions/:objectId', req => { return this.handleGet(req); }); + router.route('POST', '/sessions', req => { return this.handleCreate(req); }); + router.route('PUT', '/sessions/:objectId', req => { return this.handleUpdate(req); }); + router.route('DELETE', '/sessions/:objectId', req => { return this.handleDelete(req); }); + return router; + } +} + +export default SessionsRouter; diff --git a/src/index.js b/src/index.js index fcc573e23c..9d9910bd4b 100644 --- a/src/index.js +++ b/src/index.js @@ -21,6 +21,7 @@ import { PushController } from './Controllers/PushController'; import { ClassesRouter } from './Routers/ClassesRouter'; import { InstallationsRouter } from './Routers/InstallationsRouter'; import { UsersRouter } from './Routers/UsersRouter'; +import { SessionsRouter } from './Routers/SessionsRouter'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -131,7 +132,7 @@ function ParseServer(args) { let routers = [ new ClassesRouter().getExpressRouter(), new UsersRouter().getExpressRouter(), - require('./sessions'), + new SessionsRouter().getExpressRouter(), require('./roles'), require('./analytics'), new InstallationsRouter().getExpressRouter(), diff --git a/src/sessions.js b/src/sessions.js deleted file mode 100644 index b979de4513..0000000000 --- a/src/sessions.js +++ /dev/null @@ -1,98 +0,0 @@ -// sessions.js - -var Auth = require('./Auth'), - Parse = require('parse/node').Parse, - PromiseRouter = require('./PromiseRouter'), - rest = require('./rest'); - -var router = new PromiseRouter(); - -function handleCreate(req) { - return rest.create(req.config, req.auth, - '_Session', req.body); -} - -function handleUpdate(req) { - return rest.update(req.config, req.auth, '_Session', - req.params.objectId, req.body) - .then((response) => { - return {response: response}; - }); -} - -function handleDelete(req) { - return rest.del(req.config, req.auth, - '_Session', req.params.objectId) - .then(() => { - return {response: {}}; - }); -} - -function handleGet(req) { - return rest.find(req.config, req.auth, '_Session', - {objectId: req.params.objectId}) - .then((response) => { - if (!response.results || response.results.length == 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.'); - } else { - return {response: response.results[0]}; - } - }); -} - -function handleFind(req) { - var options = {}; - if (req.body.skip) { - options.skip = Number(req.body.skip); - } - if (req.body.limit) { - options.limit = Number(req.body.limit); - } - if (req.body.order) { - options.order = String(req.body.order); - } - if (req.body.count) { - options.count = true; - } - if (typeof req.body.keys == 'string') { - options.keys = req.body.keys; - } - if (req.body.include) { - options.include = String(req.body.include); - } - - return rest.find(req.config, req.auth, - '_Session', req.body.where, options) - .then((response) => { - return {response: response}; - }); -} - -function handleMe(req) { - // TODO: Verify correct behavior - if (!req.info || !req.info.sessionToken) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, - 'Session token required.'); - } - return rest.find(req.config, Auth.master(req.config), '_Session', - { _session_token: req.info.sessionToken}) - .then((response) => { - if (!response.results || response.results.length == 0) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, - 'Session token not found.'); - } - return { - response: response.results[0] - }; - }); -} - -router.route('POST','/sessions', handleCreate); -router.route('GET','/sessions/me', handleMe); -router.route('GET','/sessions/:objectId', handleGet); -router.route('PUT','/sessions/:objectId', handleUpdate); -router.route('GET','/sessions', handleFind); -router.route('DELETE','/sessions/:objectId', handleDelete); - -module.exports = router; From b2570a9af85a131f4f86f3683c603133f54557b7 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Thu, 11 Feb 2016 23:17:20 -0800 Subject: [PATCH 3/3] Update style in InstallationsRouter. --- src/Routers/InstallationsRouter.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Routers/InstallationsRouter.js b/src/Routers/InstallationsRouter.js index 033366b70b..fca703e9a1 100644 --- a/src/Routers/InstallationsRouter.js +++ b/src/Routers/InstallationsRouter.js @@ -51,12 +51,12 @@ export class InstallationsRouter extends ClassesRouter { } getExpressRouter() { - var router = new PromiseRouter(); - router.route('GET','/installations', (req) => { return this.handleFind(req); }); - router.route('GET','/installations/:objectId', (req) => { return this.handleGet(req); }); - router.route('POST','/installations', (req) => { return this.handleCreate(req); }); - router.route('PUT','/installations/:objectId', (req) => { return this.handleUpdate(req); }); - router.route('DELETE','/installations/:objectId', (req) => { return this.handleDelete(req); }); + let router = new PromiseRouter(); + router.route('GET','/installations', req => { return this.handleFind(req); }); + router.route('GET','/installations/:objectId', req => { return this.handleGet(req); }); + router.route('POST','/installations', req => { return this.handleCreate(req); }); + router.route('PUT','/installations/:objectId', req => { return this.handleUpdate(req); }); + router.route('DELETE','/installations/:objectId', req => { return this.handleDelete(req); }); return router; } }