From 36f82aa68c1f0f47fb70412b762a01c5fd966abf Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Thu, 3 Mar 2016 23:31:31 +0530 Subject: [PATCH] FEATURE: enforce admin password validation when signing up via developer email --- .../controllers/create-account.js.es6 | 60 +++++++++---------- app/controllers/users_controller.rb | 3 +- app/services/username_checker_service.rb | 10 +++- lib/validators/password_validator.rb | 6 +- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/create-account.js.es6 b/app/assets/javascripts/discourse/controllers/create-account.js.es6 index adbd78f6c58..33efac1000f 100644 --- a/app/assets/javascripts/discourse/controllers/create-account.js.es6 +++ b/app/assets/javascripts/discourse/controllers/create-account.js.es6 @@ -16,6 +16,7 @@ export default Ember.Controller.extend(ModalFunctionality, { rejectedPasswords: Em.A([]), prefilledUsername: null, userFields: null, + isDeveloper: false, hasAuthOptions: Em.computed.notEmpty('authOptions'), canCreateLocal: setting('enable_local_logins'), @@ -37,6 +38,7 @@ export default Ember.Controller.extend(ModalFunctionality, { rejectedEmails: [], rejectedPasswords: [], prefilledUsername: null, + isDeveloper: false }); this._createUserFields(); }, @@ -70,8 +72,8 @@ export default Ember.Controller.extend(ModalFunctionality, { }.property('authOptions.auth_provider'), passwordInstructions: function() { - return I18n.t('user.password.instructions', {count: Discourse.SiteSettings.min_password_length}); - }.property(), + return this.get('isDeveloper') ? I18n.t('user.password.instructions', {count: Discourse.SiteSettings.min_admin_password_length}) : I18n.t('user.password.instructions', {count: Discourse.SiteSettings.min_password_length}); + }.property('isDeveloper'), nameInstructions: function() { return I18n.t(Discourse.SiteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions'); @@ -228,41 +230,27 @@ export default Ember.Controller.extend(ModalFunctionality, { const _this = this; if (this.shouldCheckUsernameMatch()) { return Discourse.User.checkUsername(this.get('accountUsername'), this.get('accountEmail')).then(function(result) { - _this.set('globalNicknameExists', false); + _this.set('isDeveloper', false); if (result.available) { - if (result.global_match) { - _this.set('globalNicknameExists', true); - return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ - ok: true, - reason: I18n.t('user.username.global_match') - })); - } else { - return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ - ok: true, - reason: I18n.t('user.username.available') - })); + if (result.is_developer) { + _this.set('isDeveloper', true); } + return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ + ok: true, + reason: I18n.t('user.username.available') + })); } else { if (result.suggestion) { - if (result.global_match !== void 0 && result.global_match === false) { - _this.set('globalNicknameExists', true); - return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ - failed: true, - reason: I18n.t('user.username.global_mismatch', result) - })); - } else { - return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ - failed: true, - reason: I18n.t('user.username.not_available', result) - })); - } + return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ + failed: true, + reason: I18n.t('user.username.not_available', result) + })); } else if (result.errors) { return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ failed: true, reason: result.errors.join(' ') })); } else { - _this.set('globalNicknameExists', true); return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ failed: true, reason: I18n.t('user.username.enter_email') @@ -296,8 +284,16 @@ export default Ember.Controller.extend(ModalFunctionality, { return Discourse.InputValidation.create({ failed: true }); } - // If too short - if (password.length < Discourse.SiteSettings.min_password_length) { + // If too short for Admin + if (this.get('isDeveloper') && password.length < Discourse.SiteSettings.min_admin_password_length) { + return Discourse.InputValidation.create({ + failed: true, + reason: I18n.t('user.password.too_short') + }); + } + + // If too short for normal user + if (!this.get('isDeveloper') && password.length < Discourse.SiteSettings.min_password_length) { return Discourse.InputValidation.create({ failed: true, reason: I18n.t('user.password.too_short') @@ -330,7 +326,7 @@ export default Ember.Controller.extend(ModalFunctionality, { ok: true, reason: I18n.t('user.password.ok') }); - }.property('accountPassword', 'rejectedPasswords.@each', 'accountUsername', 'accountEmail'), + }.property('accountPassword', 'rejectedPasswords.@each', 'accountUsername', 'accountEmail', 'isDeveloper'), @on('init') fetchConfirmationValue() { @@ -360,6 +356,7 @@ export default Ember.Controller.extend(ModalFunctionality, { this.set('formSubmitted', true); return Discourse.User.createAccount(attrs).then(function(result) { + self.set('isDeveloper', false); if (result.success) { // Trigger the browser's password manager using the hidden static login form: const $hidden_login_form = $('#hidden-login-form'); @@ -369,6 +366,9 @@ export default Ember.Controller.extend(ModalFunctionality, { $hidden_login_form.submit(); } else { self.flash(result.message || I18n.t('create_account.failed'), 'error'); + if (result.is_developer) { + self.set('isDeveloper', true); + } if (result.errors && result.errors.email && result.errors.email.length > 0 && result.values) { self.get('rejectedEmails').pushObject(result.values.email); } diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3a9f77942f4..574354022c5 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -343,7 +343,8 @@ class UsersController < ApplicationController errors: user.errors.full_messages.join("\n") ), errors: user.errors.to_hash, - values: user.attributes.slice('name', 'username', 'email') + values: user.attributes.slice('name', 'username', 'email'), + is_developer: UsernameCheckerService.new.is_developer?(user.email) } end rescue ActiveRecord::StatementInvalid diff --git a/app/services/username_checker_service.rb b/app/services/username_checker_service.rb index caeea977aef..64beba09b60 100644 --- a/app/services/username_checker_service.rb +++ b/app/services/username_checker_service.rb @@ -6,17 +6,21 @@ class UsernameCheckerService if !validator.valid_format? {errors: validator.errors} else - check_username_availability(username) + check_username_availability(username, email) end end end - def check_username_availability(username) + def check_username_availability(username, email) if User.username_available?(username) - { available: true } + { available: true, is_developer: is_developer?(email) } else { available: false, suggestion: UserNameSuggester.suggest(username) } end end + def is_developer?(value) + Rails.configuration.respond_to?(:developer_emails) && Rails.configuration.developer_emails.include?(value) + end + end diff --git a/lib/validators/password_validator.rb b/lib/validators/password_validator.rb index 3eb94a095a8..7b8ee765e2a 100644 --- a/lib/validators/password_validator.rb +++ b/lib/validators/password_validator.rb @@ -6,7 +6,7 @@ class PasswordValidator < ActiveModel::EachValidator return unless record.password_required? if value.nil? record.errors.add(attribute, :blank) - elsif value.length < SiteSetting.min_admin_password_length && record.admin? + elsif value.length < SiteSetting.min_admin_password_length && (record.admin? || is_developer?(record.email)) record.errors.add(attribute, :too_short, count: SiteSetting.min_admin_password_length) elsif value.length < SiteSetting.min_password_length record.errors.add(attribute, :too_short, count: SiteSetting.min_password_length) @@ -19,4 +19,8 @@ class PasswordValidator < ActiveModel::EachValidator end end + def is_developer?(value) + Rails.configuration.respond_to?(:developer_emails) && Rails.configuration.developer_emails.include?(value) + end + end