diff --git a/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js b/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js index f3523bf..2cf6e5d 100644 --- a/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js +++ b/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js @@ -100,6 +100,7 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithSocialProviders = extend(remoteProvider, { enabled: true }); extend(localProvider, remoteProvider); } + next(); }); } else { @@ -114,10 +115,10 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithSocialProviders = // Finds and returns an Application's default Account Store (Directory) // object. If one doesn't exist, nothing will be returned. -EnrichIntegrationFromRemoteConfigStrategy.prototype._resolveDirectoryHref = function (application, callback) { +EnrichIntegrationFromRemoteConfigStrategy.prototype._resolveDirectoryHref = function (app, callback) { var outerScope = this; - application.getAccountStoreMappings(function(err, mappings) { + app.getAccountStoreMappings(function(err, mappings) { if (err) { return callback(err); } @@ -172,11 +173,16 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithDirectoryPolicies // Enrich config with with directory policies. client.getDirectory(directoryHref, { expand: 'passwordPolicy,accountCreationPolicy' }, stopIfError(function (directory) { + var resetEmailStatusEnabled = isEnabled(directory.passwordPolicy.resetEmailStatus); + // Enrich config with account policies. extend(config, { web: { forgotPassword: { - enabled: config.web.changePassword.enabled = isEnabled(directory.passwordPolicy.resetEmailStatus) + enabled: resetEmailStatusEnabled + }, + changePassword: { + enabled: resetEmailStatusEnabled }, verifyEmail: { enabled: isEnabled(directory.accountCreationPolicy.verificationEmailStatus) @@ -184,7 +190,12 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithDirectoryPolicies } }); - // Enrich config with password policies + // Validate that auto login and email verification aren't enabled at the same time. + if (config.web.register.autoLogin && config.web.verifyEmail.enabled) { + return callback(new Error(strings.CONFLICTING_AUTO_LOGIN_AND_EMAIL_VERIFICATION_CONFIG)); + } + + // Enrich config with password policies. directory.getPasswordPolicy(stopIfError(function (policy) { policy.getStrength(stopIfError(function (strength) { // Remove the href property from the Strength Resource, we don't want diff --git a/lib/strings.js b/lib/strings.js index 5c58d4f..2d448ec 100644 --- a/lib/strings.js +++ b/lib/strings.js @@ -1,6 +1,7 @@ module.exports = { APP_HREF_NOT_FOUND: 'The provided application could not be found.\n\nThe provided application href was: %href%\n', APP_NAME_NOT_FOUND: 'The provided application could not be found.\n\nThe provided application name was: %name%\n', + CONFLICTING_AUTO_LOGIN_AND_EMAIL_VERIFICATION_CONFIG: 'Invalid configuration: stormpath.web.register.autoLogin is true, but the default account store of the specified application has the email verification workflow enabled. Auto login is only possible if email verification is disabled. Please disable this workflow on this application\'s default account store.', NO_ACCOUNT_STORES_MAPPED: 'No account stores are mapped to the specified application. Account stores are required for login and registration.', NO_DEFAULT_ACCOUNT_STORE_MAPPED: 'No default account store is mapped to the specified application. A default account store is required for registration.', UNABLE_TO_AUTO_RESOLVE_APP: 'Could not automatically resolve a Stormpath Application. \n\nPlease specify your Stormpath Application in your configuration.\n', diff --git a/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js b/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js index 4c9a578..d109e80 100644 --- a/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js +++ b/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js @@ -7,12 +7,16 @@ var assert = common.assert; var sinon = common.sinon; var strings = common.strings; +var Directory = require('stormpath/lib/resource/Directory'); var Application = require('stormpath/lib/resource/Application'); var Collection = require('stormpath/lib/resource/CollectionResource'); +var AccountStoreMapping = require('stormpath/lib/resource/AccountStoreMapping'); var strategy = require('../../lib/strategy'); var EnrichIntegrationFromRemoteConfigStrategy = strategy.EnrichIntegrationFromRemoteConfigStrategy; +var sandbox; + describe('EnrichIntegrationFromRemoteConfigStrategy', function () { var client, testStrategy; @@ -22,12 +26,14 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { }; var mockDirectory = { + href: 'https://stormpath.mock/directories/stormpath', accountCreationPolicy: { verificationEmailStatus: 'ENABLED' }, passwordPolicy: { resetEmailStatus: 'ENABLED' - } + }, + getPasswordPolicy: function () {} }; function makeMockConfig(mockApplication) { @@ -36,6 +42,15 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { web: { register: { enabled: true + }, + forgotPassword: { + enabled: true + }, + changePassword: { + enabled: true + }, + verifyEmail: { + enabled: true } } }; @@ -53,6 +68,17 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { return mockApplication; } + function makeMockDirectory() { + var directory = new Directory({ href: 'http://mock.api/directories/mock' }); + + sinon.stub(directory, 'getProvider') + .yields(null, { + providerId: 'mock' + }); + + return directory; + } + before(function (done) { client = new stormpath.Client({ skipRemoteConfig: true @@ -73,6 +99,14 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { }); }); + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + describe('when resolving an application', function () { it('should error if invalid', function (done) { testStrategy.process({ application: { href: '123' } }, function (err) { @@ -109,6 +143,35 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { done(); }); }); + + it('should error if both web.register.autoLogin and config.web.verifyEmail.enabled is set at same time', function (done) { + var mockAccountStoreMapping = new AccountStoreMapping({ accountStore: makeMockDirectory() }); + var mockAccountStoreMappings = new Collection({ size: 1, items: [mockAccountStoreMapping] }, null, AccountStoreMapping); + + mockAccountStoreMappings.detect = function (iter, complete) { + complete(mockAccountStoreMapping); + }; + + mockAccountStoreMappings.each = function (iter, complete) { + iter(mockAccountStoreMapping, function () { + complete(); + }); + }; + + sinon.stub(mockAccountStoreMapping, 'getAccountStore') + .yields(null, mockAccountStoreMapping.accountStore); + + var mockApplication = makeMockApplication(null, mockAccountStoreMappings); + var mockConfig = makeMockConfig(mockApplication); + + mockConfig.web.register.autoLogin = true; + + testStrategy.process(mockConfig, function (err) { + assert.isNotNull(err); + assert.equal(err.message, strings.CONFLICTING_AUTO_LOGIN_AND_EMAIL_VERIFICATION_CONFIG); + done(); + }); + }); }); describe('when enriching', function () { @@ -138,4 +201,74 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { }); }); }); + + describe('_enrichWithDirectoryPolicies method', function () { + var enrichWithDirectoryPolicies; + var mockConfig; + + beforeEach(function () { + var mockAccountStoreMappings = new Collection({ size: 0 }); + var mockApplication = makeMockApplication(null, mockAccountStoreMappings); + + var policy = { + getStrength: function () {} + }; + + enrichWithDirectoryPolicies = testStrategy._enrichWithDirectoryPolicies; + mockConfig = makeMockConfig(mockApplication); + + sandbox.stub(mockDirectory, 'getPasswordPolicy').yields(null, policy); + sandbox.stub(policy, 'getStrength').yields(null, {}); + }); + + describe('directory.passwordPolicy.resetEmailStatus property', function () { + describe('when "ENABLED"', function () { + beforeEach(function () { + mockDirectory.passwordPolicy.resetEmailStatus = 'ENABLED'; + }); + + it('should set config.web.forgotPassword.enabled to true', function (done) { + mockConfig.web.forgotPassword.enabled = false; + + enrichWithDirectoryPolicies(client, mockConfig, mockDirectory.href, function () { + assert.equal(mockConfig.web.forgotPassword.enabled, true); + done(); + }); + }); + + it('should set config.web.changePassword.enabled to true', function (done) { + mockConfig.web.changePassword.enabled = false; + + enrichWithDirectoryPolicies(client, mockConfig, mockDirectory.href, function () { + assert.equal(mockConfig.web.changePassword.enabled, true); + done(); + }); + }); + }); + + describe('when undefined', function () { + beforeEach(function () { + mockDirectory.passwordPolicy.resetEmailStatus = undefined; + }); + + it('should set config.web.forgotPassword.enabled to false', function (done) { + mockConfig.web.forgotPassword.enabled = true; + + enrichWithDirectoryPolicies(client, mockConfig, mockDirectory.href, function () { + assert.equal(mockConfig.web.forgotPassword.enabled, false); + done(); + }); + }); + + it('should set config.web.changePassword.enabled to false', function (done) { + mockConfig.web.changePassword.enabled = true; + + enrichWithDirectoryPolicies(client, mockConfig, mockDirectory.href, function () { + assert.equal(mockConfig.web.changePassword.enabled, false); + done(); + }); + }); + }); + }); + }); });