diff --git a/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js b/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js index 2e65161..1d9ad38 100644 --- a/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js +++ b/lib/strategy/EnrichIntegrationFromRemoteConfigStrategy.js @@ -3,6 +3,7 @@ var async = require('async'); var extend = require('../helpers/clone-extend').extend; var strings = require('../strings'); +var util = require('util'); /** * Retrieves Stormpath settings from the API service, and ensures the local @@ -206,12 +207,72 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithDirectoryPolicies config.passwordPolicy = strength; - callback(null, null); + callback(null, directory); })); })); })); }; +EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithDirectoryAccountModel = function (client, config, directory, callback) { + if (!directory) { + return callback(null, null); + } + + // Returns the callback immediately if there is an error. + // Continues processing if there isn't. + var stopIfError = function (process) { + return function (err, result) { + if (err) { + callback(err); + } else { + process(result); + } + }; + }; + + var webConfig = config.web; + + // Map the registration fields defined in the account schema. + directory.getAccountSchema(stopIfError(function (accountSchema) { + accountSchema.getFields(stopIfError(function (fields) { + var registrationFields = webConfig.register.form.fields; + + fields.each(function (field, next) { + var fieldToConfigure = registrationFields[field.name]; + + if (fieldToConfigure) { + var localValue = registrationFields[field.name].required; + var remoteValue = field.required; + + if (localValue === null) { + fieldToConfigure.required = remoteValue; + } + + if (localValue === false && remoteValue === true){ + console.warn(util.format(strings.ACCOUNT_SCHEMA_REMOTE_FIELD_REQUIRED_WARNING,directory.name,field.name)); + fieldToConfigure.required = true; + } + + if (localValue === true && remoteValue === false){ + console.warn(util.format(strings.ACCOUNT_SCHEMA_LOCAL_FIELD_REQUIRED_WARNING,directory.name,field.name)); + } + } + + next(); + }, function (err) { + // If a field is not configured, then don't require it. + for (var key in registrationFields) { + if (typeof registrationFields[key].required !== 'boolean') { + registrationFields[key].required = false; + } + } + + callback(err, null); + }); + })); + })); +}; + EnrichIntegrationFromRemoteConfigStrategy.prototype.process = function (config, callback) { var tasks = []; @@ -228,7 +289,8 @@ EnrichIntegrationFromRemoteConfigStrategy.prototype.process = function (config, this._enrichWithOAuthPolicy.bind(this), this._enrichWithSocialProviders.bind(this, config), this._resolveDirectoryHref.bind(this), - this._enrichWithDirectoryPolicies.bind(this, client, config) + this._enrichWithDirectoryPolicies.bind(this, client, config), + this._enrichWithDirectoryAccountModel.bind(this, client, config) ]); } diff --git a/lib/strings.js b/lib/strings.js index 2d448ec..aa3261d 100644 --- a/lib/strings.js +++ b/lib/strings.js @@ -1,6 +1,8 @@ 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', + ACCOUNT_SCHEMA_REMOTE_FIELD_REQUIRED_WARNING: 'The directory \'%s\' requires the \'%s\' account field. Local configuration has attempted to disable this requirement. The directory schema configuration will override the local configuration.', + ACCOUNT_SCHEMA_LOCAL_FIELD_REQUIRED_WARNING: 'The directory \'%s\' does not require the \'%s\' account field. Local configuration has required this field. Field enforcment will only happen locally.', 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.', diff --git a/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js b/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js index ef19f1e..7f5bd57 100644 --- a/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js +++ b/test/strategy/EnrichIntegrationFromRemoteConfigStrategyTest.js @@ -333,4 +333,95 @@ describe('EnrichIntegrationFromRemoteConfigStrategy', function () { }); }); }); + + describe('._enrichWithDirectoryAccountModel(client, config, directory, callback)', function() { + var testMethod; + var mockClient; + var mockConfig; + var mockDirectory; + var mockFields; + var sinonSandbox; + + beforeEach(function () { + sinonSandbox = sinon.sandbox.create(); + + testMethod = EnrichIntegrationFromRemoteConfigStrategy.prototype._enrichWithDirectoryAccountModel; + + mockClient = {}; + + mockConfig = { + web: { + register: { + form: { + fields: { + givenName: { + enabled: true, + required: false + }, + surname: { + enabled: true, + required: false + } + } + } + } + } + }; + + mockFields = { + items: [{ + name: 'givenName', + required: true + }, { + name: 'surname', + required: true + }], + each: function (iterator, onComplete) { + var self = this; + this.items.forEach(function (item, index) { + iterator(item, function () { + if (onComplete && index === self.items.length - 1) { + onComplete(); + } + }); + }); + } + }; + + mockDirectory = { + getAccountSchema: function (callback) { + callback(null, { + getFields: function (callback) { + callback(null, mockFields); + } + }); + } + }; + }); + + afterEach(function () { + sinonSandbox.restore(); + }); + + describe('when givenName and surname registration fields in config are not required', function () { + describe('but remote accountSchema has fields set to required', function () { + it('should warn and set fields in config as required', function (done) { + var testFormFields = mockConfig.web.register.form.fields; + var consoleMock = sinonSandbox.mock(console); + + consoleMock.expects('warn').twice(); + + testMethod(mockClient, mockConfig, mockDirectory, function (err) { + assert.equal(err, null); + assert.equal(testFormFields.givenName.required, true); + assert.equal(testFormFields.surname.required, true); + + consoleMock.verify(); + + done(); + }); + }); + }); + }); + }); });