[FIX] users registered via third party apps bypass custom required fields (#22396)

* copy custom field logic to username screen

* remove login state from username screen

* remove custom fields from initial registration form

* add saveCustomFields method

* skip custom fields in register user method

* remove console logs

* add method call in username form

Co-authored-by: gabriellsh <40830821+gabriellsh@users.noreply.github.com>
Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat>
pull/22778/head^2
Gabriel Thomé 4 years ago committed by GitHub
parent 14f8379844
commit e1f0e79619
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/lib/server/index.js
  2. 14
      app/lib/server/methods/saveCustomFields.ts
  3. 2
      app/ui-login/client/login/form.html
  4. 43
      app/ui-login/client/login/form.js
  5. 1
      app/ui-login/client/username/username.html
  6. 95
      app/ui-login/client/username/username.js
  7. 4
      server/methods/registerUser.js

@ -65,6 +65,7 @@ import './methods/setUsername';
import './methods/unarchiveRoom';
import './methods/unblockUser';
import './methods/updateMessage';
import './methods/saveCustomFields';
export * from './lib';
export * from './functions';

@ -0,0 +1,14 @@
import { Meteor } from 'meteor/meteor';
import { RateLimiter } from '../lib';
import { saveCustomFields } from '../functions/saveCustomFields';
Meteor.methods({
saveCustomFields(fields = {}) {
saveCustomFields(Meteor.userId(), fields);
},
});
RateLimiter.limitMethod('saveCustomFields', 1, 1000, {
userId() { return true; },
});

@ -66,8 +66,6 @@
</label>
</div>
{{> customFieldsForm hideFromForm=true}}
<div class="rc-input">
<label class="rc-input__label" for="pass">
<div class="rc-input__wrapper">

@ -177,20 +177,8 @@ Template.loginForm.events({
Template.loginForm.onCreated(function() {
const instance = this;
this.customFields = new ReactiveVar();
this.loading = new ReactiveVar(false);
Tracker.autorun(() => {
const Accounts_CustomFields = settings.get('Accounts_CustomFields');
if (typeof Accounts_CustomFields === 'string' && Accounts_CustomFields.trim() !== '') {
try {
return this.customFields.set(JSON.parse(settings.get('Accounts_CustomFields')));
} catch (error1) {
return console.error('Invalid JSON for Accounts_CustomFields');
}
} else {
return this.customFields.set(null);
}
});
if (Session.get('loginDefaultState')) {
this.state = new ReactiveVar(Session.get('loginDefaultState'));
} else {
@ -205,34 +193,6 @@ Template.loginForm.onCreated(function() {
});
this.validSecretURL = new ReactiveVar(false);
const validateCustomFields = function(formObj, validationObj) {
const customFields = instance.customFields.get();
if (!customFields) {
return;
}
for (const field in formObj) {
if (formObj.hasOwnProperty(field)) {
const value = formObj[field];
if (customFields[field] == null) {
continue;
}
const customField = customFields[field];
if (customField.required === true && !value) {
validationObj[field] = t('Field_required');
return validationObj[field];
}
if ((customField.maxLength != null) && value.length > customField.maxLength) {
validationObj[field] = t('Max_length_is', customField.maxLength);
return validationObj[field];
}
if ((customField.minLength != null) && value.length < customField.minLength) {
validationObj[field] = t('Min_length_is', customField.minLength);
return validationObj[field];
}
}
}
};
this.validate = function() {
const formData = $('#login-card').serializeArray();
const formObj = {};
@ -266,7 +226,6 @@ Template.loginForm.onCreated(function() {
if (settings.get('Accounts_ManuallyApproveNewUsers') && !formObj.reason) {
validationObj.reason = t('Invalid_reason');
}
validateCustomFields(formObj, validationObj);
}
$('#login-card h2').removeClass('error');
$('#login-card input.error, #login-card select.error').removeClass('error');

@ -39,6 +39,7 @@
</div>
</div>
{{>customFieldsForm}}
{{#if username.ready}}
<div class="rc-button__group rc-button__group--vertical">
<button data-loading-text="{{_ " Please_wait "}}..." class='rc-button rc-button--primary login'>{{_ "Use_this_username"}}</button>

@ -1,16 +1,88 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import _ from 'underscore';
import toastr from 'toastr';
import { settings } from '../../../settings';
import { Button } from '../../../ui';
import { t } from '../../../utils';
import { callbacks } from '../../../callbacks';
Template.username.onCreated(function() {
const self = this;
self.customFields = new ReactiveVar();
self.username = new ReactiveVar();
Tracker.autorun(() => {
const Accounts_CustomFields = settings.get('Accounts_CustomFields');
if (typeof Accounts_CustomFields === 'string' && Accounts_CustomFields.trim() !== '') {
try {
return this.customFields.set(JSON.parse(settings.get('Accounts_CustomFields')));
} catch (error1) {
return console.error('Invalid JSON for Accounts_CustomFields');
}
} else {
return this.customFields.set(null);
}
});
const validateCustomFields = function(formObj, validationObj) {
const customFields = self.customFields.get();
if (!customFields) {
return;
}
for (const field in formObj) {
if (formObj.hasOwnProperty(field)) {
const value = formObj[field];
if (customFields[field] == null) {
continue;
}
const customField = customFields[field];
if (customField.required === true && !value) {
validationObj[field] = t('Field_required');
return validationObj[field];
}
if ((customField.maxLength != null) && value.length > customField.maxLength) {
validationObj[field] = t('Max_length_is', customField.maxLength);
return validationObj[field];
}
if ((customField.minLength != null) && value.length < customField.minLength) {
validationObj[field] = t('Min_length_is', customField.minLength);
return validationObj[field];
}
}
}
};
this.validate = function() {
const formData = $('#login-card').serializeArray();
const formObj = {};
const validationObj = {};
formData.forEach((field) => {
formObj[field.name] = field.value;
});
$('#login-card h2').removeClass('error');
$('#login-card input.error, #login-card select.error').removeClass('error');
$('#login-card .input-error').text('');
validateCustomFields(formObj, validationObj);
if (!_.isEmpty(validationObj)) {
$('#login-card h2').addClass('error');
Object.keys(validationObj).forEach((key) => {
const value = validationObj[key];
$(`#login-card input[name=${ key }], #login-card select[name=${ key }]`).addClass('error');
$(`#login-card input[name=${ key }]~.input-error, #login-card select[name=${ key }]~.input-error`).text(value);
});
return false;
}
return formObj;
};
return Meteor.call('getUsernameSuggestion', function(error, username) {
self.username.set({
ready: true,
@ -50,6 +122,8 @@ Template.username.events({
'submit #login-card'(event, instance) {
event.preventDefault();
const formData = instance.validate();
const username = instance.username.get();
username.empty = false;
username.error = false;
@ -59,15 +133,26 @@ Template.username.events({
const button = $(event.target).find('button.login');
Button.loading(button);
const value = $('#username').val().trim();
if (value === '') {
if (!formData) {
Button.reset(button);
return;
}
const usernameValue = $('#username').val().trim();
if (usernameValue === '') {
username.empty = true;
instance.username.set(username);
Button.reset(button);
return;
}
return Meteor.call('setUsername', value, function(err) {
Meteor.call('saveCustomFields', formData, function(err) {
if (err != null) {
toastr.error(err.error);
}
});
Meteor.call('setUsername', usernameValue, function(err) {
if (err != null) {
if (err.error === 'username-invalid') {
username.invalid = true;
@ -76,8 +161,8 @@ Template.username.events({
} else {
username.unavailable = true;
}
username.username = value;
username.escaped = _.escape(value);
username.username = usernameValue;
username.escaped = _.escape(usernameValue);
}
Button.reset(button);

@ -5,7 +5,7 @@ import s from 'underscore.string';
import { Users } from '../../app/models';
import { settings } from '../../app/settings';
import { saveCustomFields, validateEmailDomain, passwordPolicy } from '../../app/lib';
import { validateEmailDomain, passwordPolicy } from '../../app/lib';
import { validateInviteToken } from '../../app/invites/server/functions/validateInviteToken';
Meteor.methods({
@ -79,8 +79,6 @@ Meteor.methods({
Users.setReason(userId, reason);
}
saveCustomFields(userId, formData);
try {
Accounts.sendVerificationEmail(userId, userData.email);
} catch (error) {

Loading…
Cancel
Save