Exploring the interface of a mail adapter
Add some tests and demonstrate the adapter loading interface
This commit is contained in:
committed by
Florent Vilmart
parent
d9f1e00345
commit
8dc37b9d30
@@ -26,6 +26,7 @@
|
||||
"commander": "^2.9.0",
|
||||
"deepcopy": "^0.6.1",
|
||||
"express": "^4.13.4",
|
||||
"mailgun-js": "^0.7.7",
|
||||
"mime": "^1.3.4",
|
||||
"mongodb": "~2.1.0",
|
||||
"multer": "^1.1.0",
|
||||
|
||||
3
spec/MockEmailAdapter.js
Normal file
3
spec/MockEmailAdapter.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
sendVerificationEmail: () => Promise.resolve();
|
||||
}
|
||||
8
spec/MockEmailAdapterWithOptions.js
Normal file
8
spec/MockEmailAdapterWithOptions.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = options => {
|
||||
if (!options) {
|
||||
throw "Options were not provided"
|
||||
}
|
||||
return {
|
||||
sendVerificationEmail: () => Promise.resolve()
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,217 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sends verification email if email verification is enabled', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send verification email if email verification is disabled', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: false,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(emailAdapter.sendVerificationEmail.calls.count()).toEqual(0);
|
||||
expect(user.get('emailVerified')).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('receives the app name and user in the adapter', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
expect(options.appName).toEqual('emailing app');
|
||||
expect(options.user.get('email')).toEqual('user@parse.com');
|
||||
done();
|
||||
}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp(null, {
|
||||
success: () => {},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
it('when you click the link in the email it sets emailVerified to true and redirects you', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
request.get(options.link, {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/verify_email_success.html?username=zxcv');
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp();
|
||||
});
|
||||
|
||||
it('redirects you to invalid link if you try to verify email incorrecly', done => {
|
||||
request.get('http://localhost:8378/1/verify_email', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/invalid_link.html');
|
||||
done()
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects you to invalid link if you try to validate a nonexistant users email', done => {
|
||||
request.get('http://localhost:8378/1/verify_email?token=asdfasdf&username=sadfasga', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/invalid_link.html');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not update email verified if you use an invalid token', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
request.get('http://localhost:8378/1/verify_email?token=invalid&username=zxcv', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/invalid_link.html');
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp(null, {
|
||||
success: () => {},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("user login wrong username", (done) => {
|
||||
Parse.User.signUp("asdf", "zxcv", null, {
|
||||
success: function(user) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var request = require('request');
|
||||
var MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
||||
|
||||
describe('server', () => {
|
||||
it('requires a master key and app id', done => {
|
||||
@@ -37,4 +38,114 @@ describe('server', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can load email adapter via object', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: MockEmailAdapterWithOptions({
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}),
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via class', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
class: MockEmailAdapterWithOptions,
|
||||
options: {
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via module name', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
module: './Email/SimpleMailgunAdapter',
|
||||
options: {
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via only module name', done => {
|
||||
expect(() => setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: './Email/SimpleMailgunAdapter',
|
||||
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
|
||||
done();
|
||||
});
|
||||
|
||||
it('throws if you initialize email adapter incorrecly', done => {
|
||||
expect(() => setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
module: './Email/SimpleMailgunAdapter',
|
||||
options: {
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
export function loadAdapter(options, defaultAdapter) {
|
||||
let adapter;
|
||||
|
||||
@@ -33,3 +32,5 @@ export function loadAdapter(options, defaultAdapter) {
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
module.exports = { loadAdapter }
|
||||
|
||||
39
src/Adapters/Email/SimpleMailgunAdapter.js
Normal file
39
src/Adapters/Email/SimpleMailgunAdapter.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import Mailgun from 'mailgun-js';
|
||||
|
||||
let SimpleMailgunAdapter = mailgunOptions => {
|
||||
if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain) {
|
||||
throw 'SimpleMailgunAdapter requires an API Key and domain.';
|
||||
}
|
||||
let mailgun = Mailgun(mailgunOptions);
|
||||
|
||||
let sendMail = (to, subject, text) => {
|
||||
let data = {
|
||||
from: mailgunOptions.fromAddress,
|
||||
to: to,
|
||||
subject: subject,
|
||||
text: text,
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
mailgun.messages().send(data, (err, body) => {
|
||||
if (typeof err !== 'undefined') {
|
||||
reject(err);
|
||||
}
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
sendVerificationEmail: ({ link, user, appName, }) => {
|
||||
let verifyMessage =
|
||||
"Hi,\n\n" +
|
||||
"You are being asked to confirm the e-mail address " + user.email + " with " + appName + "\n\n" +
|
||||
"" +
|
||||
"Click here to confirm it:\n" + link;
|
||||
return sendMail(user.email, 'Please verify your e-mail for ' + appName, verifyMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SimpleMailgunAdapter
|
||||
25
src/Adapters/loadAdapter.js
Normal file
25
src/Adapters/loadAdapter.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export default options => {
|
||||
if (!options) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof options === 'string') {
|
||||
//Configuring via module name with no options
|
||||
return require(options)();
|
||||
}
|
||||
|
||||
if (!options.module && !options.class) {
|
||||
//Configuring via object
|
||||
return options;
|
||||
}
|
||||
|
||||
if (options.module) {
|
||||
//Configuring via module name + options
|
||||
return require(options.module)(options.options)
|
||||
}
|
||||
|
||||
if (options.class) {
|
||||
//Configuring via class + options
|
||||
return options.class(options.options);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ export class Config {
|
||||
this.enableAnonymousUsers = cacheInfo.enableAnonymousUsers;
|
||||
this.allowClientClassCreation = cacheInfo.allowClientClassCreation;
|
||||
this.database = DatabaseAdapter.getDatabaseConnection(applicationId, cacheInfo.collectionPrefix);
|
||||
|
||||
this.verifyUserEmails = cacheInfo.verifyUserEmails;
|
||||
this.emailAdapter = cacheInfo.emailAdapter;
|
||||
this.appName = cacheInfo.appName;
|
||||
|
||||
this.hooksController = cacheInfo.hooksController;
|
||||
this.filesController = cacheInfo.filesController;
|
||||
this.pushController = cacheInfo.pushController;
|
||||
|
||||
@@ -8,7 +8,8 @@ import rest from '../rest';
|
||||
import Auth from '../Auth';
|
||||
import passwordCrypto from '../password';
|
||||
import RestWrite from '../RestWrite';
|
||||
import { newToken } from '../cryptoUtils';
|
||||
let cryptoUtils = require('../cryptoUtils');
|
||||
let triggers = require('../triggers');
|
||||
|
||||
export class UsersRouter extends ClassesRouter {
|
||||
handleFind(req) {
|
||||
@@ -25,7 +26,26 @@ export class UsersRouter extends ClassesRouter {
|
||||
let data = deepcopy(req.body);
|
||||
req.body = data;
|
||||
req.params.className = '_User';
|
||||
return super.handleCreate(req);
|
||||
|
||||
if (req.config.verifyUserEmails) {
|
||||
req.body._email_verify_token = cryptoUtils.randomString(25);
|
||||
req.body.emailVerified = false;
|
||||
}
|
||||
|
||||
let p = super.handleCreate(req);
|
||||
|
||||
if (req.config.verifyUserEmails) {
|
||||
// Send email as fire-and-forget once the user makes it into the DB.
|
||||
p.then(() => {
|
||||
let link = req.config.mount + "/verify_email?token=" + encodeURIComponent(req.body._email_verify_token) + "&username=" + encodeURIComponent(req.body.username);
|
||||
req.config.emailAdapter.sendVerificationEmail({
|
||||
appName: req.config.appName,
|
||||
link: link,
|
||||
user: triggers.inflate('_User', req.body),
|
||||
});
|
||||
});
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
handleUpdate(req) {
|
||||
@@ -87,7 +107,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
|
||||
}
|
||||
|
||||
let token = 'r:' + newToken();
|
||||
let token = 'r:' + cryptoUtils.newToken();
|
||||
user.sessionToken = token;
|
||||
delete user.password;
|
||||
|
||||
@@ -153,6 +173,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
this.route('POST', '/requestPasswordReset', () => {
|
||||
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, 'This path is not implemented yet.');
|
||||
});
|
||||
this.route('POST', '/requestPasswordReset', req => this.handleReset(req));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
77
src/index.js
77
src/index.js
@@ -11,32 +11,34 @@ var batch = require('./batch'),
|
||||
Parse = require('parse/node').Parse;
|
||||
|
||||
import cache from './cache';
|
||||
import PromiseRouter from './PromiseRouter';
|
||||
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
|
||||
import { S3Adapter } from './Adapters/Files/S3Adapter';
|
||||
import { FilesController } from './Controllers/FilesController';
|
||||
|
||||
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
|
||||
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';
|
||||
import { RolesRouter } from './Routers/RolesRouter';
|
||||
//import passwordReset from './passwordReset';
|
||||
import PromiseRouter from './PromiseRouter';
|
||||
import verifyEmail from './verifyEmail';
|
||||
import loadAdapter from './Adapters/loadAdapter';
|
||||
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
|
||||
import { FunctionsRouter } from './Routers/FunctionsRouter';
|
||||
import { SchemasRouter } from './Routers/SchemasRouter';
|
||||
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
|
||||
import { PushRouter } from './Routers/PushRouter';
|
||||
import { ClassesRouter } from './Routers/ClassesRouter';
|
||||
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
|
||||
import { FilesController } from './Controllers/FilesController';
|
||||
import { FilesRouter } from './Routers/FilesRouter';
|
||||
import { FunctionsRouter } from './Routers/FunctionsRouter';
|
||||
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
|
||||
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
|
||||
import { LogsRouter } from './Routers/LogsRouter';
|
||||
import { HooksRouter } from './Routers/HooksRouter';
|
||||
|
||||
import { loadAdapter } from './Adapters/AdapterLoader';
|
||||
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
|
||||
import { LoggerController } from './Controllers/LoggerController';
|
||||
import { HooksController } from './Controllers/HooksController';
|
||||
import { InstallationsRouter } from './Routers/InstallationsRouter';
|
||||
import { AdapterLoader } from './Adapters/AdapterLoader';
|
||||
import { LoggerController } from './Controllers/LoggerController';
|
||||
import { PushController } from './Controllers/PushController';
|
||||
import { PushRouter } from './Routers/PushRouter';
|
||||
import { RolesRouter } from './Routers/RolesRouter';
|
||||
import { S3Adapter } from './Adapters/Files/S3Adapter';
|
||||
import { SchemasRouter } from './Routers/SchemasRouter';
|
||||
import { SessionsRouter } from './Routers/SessionsRouter';
|
||||
import { UsersRouter } from './Routers/UsersRouter';
|
||||
|
||||
import requiredParameter from './requiredParameter';
|
||||
import { randomString } from './cryptoUtils';
|
||||
@@ -69,9 +71,24 @@ addParseCloud();
|
||||
// "javascriptKey": optional key from Parse dashboard
|
||||
// "push": optional key from configure push
|
||||
|
||||
let validateEmailConfiguration = (verifyUserEmails, appName, emailAdapter) => {
|
||||
if (verifyUserEmails) {
|
||||
if (typeof appName !== 'string') {
|
||||
throw 'An app name is required when using email verification.';
|
||||
}
|
||||
if (!emailAdapter) {
|
||||
throw 'User email verification was enabled, but no email adapter was provided';
|
||||
}
|
||||
if (typeof emailAdapter.sendVerificationEmail !== 'function') {
|
||||
throw 'Invalid email adapter: no sendVerificationEmail() function was provided';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ParseServer({
|
||||
appId = requiredParameter('You must provide an appId!'),
|
||||
masterKey = requiredParameter('You must provide a masterKey!'),
|
||||
appName,
|
||||
databaseAdapter,
|
||||
filesAdapter,
|
||||
push,
|
||||
@@ -89,7 +106,9 @@ function ParseServer({
|
||||
allowClientClassCreation = true,
|
||||
oauth = {},
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
maxUploadSize = '20mb'
|
||||
maxUploadSize = '20mb',
|
||||
verifyUserEmails = false,
|
||||
emailAdapter,
|
||||
}) {
|
||||
|
||||
// Initialize the node client SDK automatically
|
||||
@@ -141,10 +160,18 @@ function ParseServer({
|
||||
hooksController: hooksController,
|
||||
enableAnonymousUsers: enableAnonymousUsers,
|
||||
allowClientClassCreation: allowClientClassCreation,
|
||||
oauth: oauth
|
||||
oauth: oauth,
|
||||
appName: appName,
|
||||
});
|
||||
|
||||
// To maintain compatibility. TODO: Remove in v2.1
|
||||
if (verifyUserEmails && process.env.PARSE_EXPERIMENTAL_EMAIL_VERIFICATION_ENABLED || process.env.TESTING == 1) {
|
||||
emailAdapter = loadAdapter(emailAdapter);
|
||||
validateEmailConfiguration(verifyUserEmails, appName, emailAdapter);
|
||||
cache.apps[appId].verifyUserEmails = verifyUserEmails;
|
||||
cache.apps[appId].emailAdapter = emailAdapter;
|
||||
}
|
||||
|
||||
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
|
||||
if (process.env.FACEBOOK_APP_ID) {
|
||||
cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
|
||||
}
|
||||
@@ -158,6 +185,12 @@ function ParseServer({
|
||||
maxUploadSize: maxUploadSize
|
||||
}));
|
||||
|
||||
if (process.env.PARSE_EXPERIMENTAL_EMAIL_VERIFICATION_ENABLED || process.env.TESTING == 1) {
|
||||
//api.use('/request_password_reset', passwordReset.reset(appName, appId));
|
||||
//api.get('/password_reset_success', passwordReset.success);
|
||||
api.get('/verify_email', verifyEmail(appId, serverURL));
|
||||
}
|
||||
|
||||
// TODO: separate this from the regular ParseServer object
|
||||
if (process.env.TESTING == 1) {
|
||||
api.use('/', require('./testing-routes').router);
|
||||
@@ -222,5 +255,5 @@ function addParseCloud() {
|
||||
|
||||
module.exports = {
|
||||
ParseServer: ParseServer,
|
||||
S3Adapter: S3Adapter
|
||||
S3Adapter: S3Adapter,
|
||||
};
|
||||
|
||||
@@ -42,6 +42,9 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
key = '_updated_at';
|
||||
timeField = true;
|
||||
break;
|
||||
case '_email_verify_token':
|
||||
key = "_email_verify_token";
|
||||
break;
|
||||
case 'sessionToken':
|
||||
case '_session_token':
|
||||
key = '_session_token';
|
||||
|
||||
27
src/verifyEmail.js
Normal file
27
src/verifyEmail.js
Normal file
@@ -0,0 +1,27 @@
|
||||
function verifyEmail(appId, serverURL) {
|
||||
var DatabaseAdapter = require('./DatabaseAdapter');
|
||||
var database = DatabaseAdapter.getDatabaseConnection(appId);
|
||||
return (req, res) => {
|
||||
var token = req.query.token;
|
||||
var username = req.query.username;
|
||||
if (!token || !username) {
|
||||
res.redirect(302, serverURL + '/invalid_link.html');
|
||||
return;
|
||||
}
|
||||
database.collection('_User').then(coll => {
|
||||
// Need direct database access because verification token is not a parse field
|
||||
coll.findAndModify({
|
||||
username: username,
|
||||
_email_verify_token: token,
|
||||
}, null, {$set: {emailVerified: true}}, (err, doc) => {
|
||||
if (err || !doc.value) {
|
||||
res.redirect(302, serverURL + '/invalid_link.html');
|
||||
} else {
|
||||
res.redirect(302, serverURL + '/verify_email_success.html?username=' + username);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = verifyEmail;
|
||||
Reference in New Issue
Block a user