From 54d154f7aa692da2dc07969774b90d061872a4a8 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Thu, 10 Mar 2016 09:41:56 -0500 Subject: [PATCH] Centralizes AuthData validation --- spec/RestCreate.spec.js | 3 +- spec/helper.js | 3 +- src/Config.js | 1 - src/RestWrite.js | 57 +++------------------ src/index.js | 6 +-- src/oauth/index.js | 111 ++++++++++++++++++++++++++++++++-------- 6 files changed, 103 insertions(+), 78 deletions(-) diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index f9b94b37..f593e221 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -148,7 +148,8 @@ describe('rest create', () => { }); it('handles no anonymous users config', (done) => { - var NoAnnonConfig = Object.assign({}, config, {enableAnonymousUsers: false}); + var NoAnnonConfig = Object.assign({}, config); + NoAnnonConfig.oauth.setEnableAnonymousUsers(false); var data1 = { authData: { anonymous: { diff --git a/spec/helper.js b/spec/helper.js index e2daa6ed..8ed9fe26 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -7,6 +7,7 @@ var DatabaseAdapter = require('../src/DatabaseAdapter'); var express = require('express'); var facebook = require('../src/oauth/facebook'); var ParseServer = require('../src/index').ParseServer; +var path = require('path'); var databaseURI = process.env.DATABASE_URI; var cloudMain = process.env.CLOUD_CODE_MAIN || '../spec/cloud/main.js'; @@ -36,7 +37,7 @@ var defaultConfiguration = { oauth: { // Override the facebook provider facebook: mockFacebook(), myoauth: { - module: "../spec/myoauth" // relative path as it's run from src + module: path.resolve(__dirname, "myoauth") // relative path as it's run from src } } }; diff --git a/src/Config.js b/src/Config.js index 8042d6db..f65b17d7 100644 --- a/src/Config.js +++ b/src/Config.js @@ -20,7 +20,6 @@ export class Config { this.restAPIKey = cacheInfo.restAPIKey; this.fileKey = cacheInfo.fileKey; this.facebookAppIds = cacheInfo.facebookAppIds; - this.enableAnonymousUsers = cacheInfo.enableAnonymousUsers; this.allowClientClassCreation = cacheInfo.allowClientClassCreation; this.database = DatabaseAdapter.getDatabaseConnection(applicationId, cacheInfo.collectionPrefix); diff --git a/src/RestWrite.js b/src/RestWrite.js index f54d6a73..b0719975 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -9,7 +9,6 @@ var Auth = require('./Auth'); var Config = require('./Config'); var cryptoUtils = require('./cryptoUtils'); var passwordCrypto = require('./password'); -var oauth = require("./oauth"); var Parse = require('parse/node'); var triggers = require('./triggers'); @@ -213,13 +212,7 @@ RestWrite.prototype.validateAuthData = function() { var authData = this.data.authData; var providers = Object.keys(authData); if (providers.length == 1) { - - var provider = providers[0]; - if (provider == 'anonymous' && !this.config.enableAnonymousUsers) { - throw new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, - 'This authentication method is unsupported.'); - } - + var provider = providers[0]; var providerAuthData = authData[provider]; var hasToken = (providerAuthData && providerAuthData.id); if (providerAuthData === null || hasToken) { @@ -238,52 +231,15 @@ RestWrite.prototype.handleOAuthAuthData = function(provider) { return; } - var appIds; - var oauthOptions = this.config.oauth[provider]; - if (oauthOptions) { - appIds = oauthOptions.appIds; - } else if (provider == "facebook") { - appIds = this.config.facebookAppIds; - } + let validateAuthData = this.config.oauth.getValidatorForProvider(provider); - var validateAuthData; - var validateAppId; - - if (oauth[provider]) { - validateAuthData = oauth[provider].validateAuthData; - validateAppId = oauth[provider].validateAppId; - } - - // Try the configuration methods - if (oauthOptions) { - if (oauthOptions.module) { - validateAuthData = require(oauthOptions.module).validateAuthData; - validateAppId = require(oauthOptions.module).validateAppId; - }; - - if (oauthOptions.validateAuthData) { - validateAuthData = oauthOptions.validateAuthData; - } - if (oauthOptions.validateAppId) { - validateAppId = oauthOptions.validateAppId; - } - } - // try the custom provider first, fallback on the oauth implementation - - if (!validateAuthData || !validateAppId) { - return false; + if (!validateAuthData) { + throw new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, + 'This authentication method is unsupported.'); }; - return validateAuthData(authData, oauthOptions) + return validateAuthData(authData) .then(() => { - if (appIds && typeof validateAppId === "function") { - return validateAppId(appIds, authData, oauthOptions); - } - - // No validation required by the developer - return Promise.resolve(); - - }).then(() => { // Check if this user already exists // TODO: does this handle re-linking correctly? var query = {}; @@ -314,7 +270,6 @@ RestWrite.prototype.handleOAuthAuthData = function(provider) { // are different if (results[0].objectId !== this.query.objectId) { delete this.data["_auth_data_" + provider ]; - console.log("alerady linked!"); throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } diff --git a/src/index.js b/src/index.js index 131f1f69..5c53a413 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,8 @@ var batch = require('./batch'), express = require('express'), middlewares = require('./middlewares'), multer = require('multer'), - Parse = require('parse/node').Parse; + Parse = require('parse/node').Parse, + oauthManager = require('./oauth'); //import passwordReset from './passwordReset'; import cache from './cache'; @@ -163,9 +164,8 @@ function ParseServer({ hooksController: hooksController, userController: userController, verifyUserEmails: verifyUserEmails, - enableAnonymousUsers: enableAnonymousUsers, allowClientClassCreation: allowClientClassCreation, - oauth: oauth, + oauth: oauthManager(oauth, enableAnonymousUsers), appName: appName, publicServerURL: publicServerURL, customPages: customPages, diff --git a/src/oauth/index.js b/src/oauth/index.js index 5067b4a1..04ef8971 100644 --- a/src/oauth/index.js +++ b/src/oauth/index.js @@ -1,25 +1,94 @@ -var facebook = require('./facebook'); -var instagram = require("./instagram"); -var linkedin = require("./linkedin"); -var meetup = require("./meetup"); -var google = require("./google"); -var github = require("./github"); -var twitter = require("./twitter"); +let facebook = require('./facebook'); +let instagram = require("./instagram"); +let linkedin = require("./linkedin"); +let meetup = require("./meetup"); +let google = require("./google"); +let github = require("./github"); +let twitter = require("./twitter"); -module.exports = { - facebook: facebook, - github: github, - google: google, - instagram: instagram, - linkedin: linkedin, - meetup: meetup, - twitter: twitter, - anonymous: { - validateAuthData: function() { - return Promise.resolve(); - }, - validateAppId: function() { - return Promise.resolve(); +let anonymous = { + validateAuthData: () => { + return Promise.resolve(); + }, + validateAppId: () => { + return Promise.resolve(); + } +} + +let providers = { + facebook, + instagram, + linkedin, + meetup, + google, + github, + twitter, + anonymous +} + +module.exports = function(oauthOptions = {}, enableAnonymousUsers = true) { + let _enableAnonymousUsers = enableAnonymousUsers; + let setEnableAnonymousUsers = function(enable) { + _enableAnonymousUsers = enable; + } + // To handle the test cases on configuration + let getValidatorForProvider = function(provider) { + + if (provider === 'anonymous' && !_enableAnonymousUsers) { + return; + } + + let defaultProvider = providers[provider]; + let optionalProvider = oauthOptions[provider]; + + if (!defaultProvider && !optionalProvider) { + return; + } + + let appIds; + if (optionalProvider) { + appIds = optionalProvider.appIds; + } + + var validateAuthData; + var validateAppId; + + if (defaultProvider) { + validateAuthData = defaultProvider.validateAuthData; + validateAppId = defaultProvider.validateAppId; + } + + // Try the configuration methods + if (optionalProvider) { + if (optionalProvider.module) { + validateAuthData = require(optionalProvider.module).validateAuthData; + validateAppId = require(optionalProvider.module).validateAppId; + }; + + if (optionalProvider.validateAuthData) { + validateAuthData = optionalProvider.validateAuthData; + } + if (optionalProvider.validateAppId) { + validateAppId = optionalProvider.validateAppId; + } + } + + if (!validateAuthData || !validateAppId) { + return; + } + + return function(authData) { + return validateAuthData(authData, optionalProvider).then(() => { + if (appIds) { + return validateAppId(appIds, authData, optionalProvider); + } + return Promise.resolve(); + }) } } + + return Object.freeze({ + getValidatorForProvider, + setEnableAnonymousUsers, + }) } \ No newline at end of file