feat: Add option logLevels.signupUsernameTaken to change log level of username already exists sign-up rejection (#9962)

This commit is contained in:
Lucas
2025-12-14 01:59:56 +01:00
committed by GitHub
parent 3b38dff6ca
commit f18f3073d7
6 changed files with 78 additions and 1 deletions

View File

@@ -81,6 +81,59 @@ describe('Parse.User testing', () => {
}
});
it('logs username taken with configured log level', async () => {
await reconfigureServer({ logLevels: { signupUsernameTaken: 'warn' } });
const logger = require('../lib/logger').default;
loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
const loggerWarnSpy = spyOn(logger, 'warn').and.callThrough();
const user = new Parse.User();
user.setUsername('dupUser');
user.setPassword('pass');
await user.signUp();
const user2 = new Parse.User();
user2.setUsername('dupUser');
user2.setPassword('pass2');
expect(loggerWarnSpy).not.toHaveBeenCalled();
try {
await user2.signUp();
fail('should have thrown');
} catch (e) {
expect(e.code).toBe(Parse.Error.USERNAME_TAKEN);
}
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
expect(loggerErrorSpy.calls.count()).toBe(0);
});
it('can silence username taken log event', async () => {
await reconfigureServer({ logLevels: { signupUsernameTaken: 'silent' } });
const logger = require('../lib/logger').default;
loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
const loggerWarnSpy = spyOn(logger, 'warn').and.callThrough();
const user = new Parse.User();
user.setUsername('dupUser');
user.setPassword('pass');
await user.signUp();
const user2 = new Parse.User();
user2.setUsername('dupUser');
user2.setPassword('pass2');
try {
await user2.signUp();
fail('should have thrown');
} catch (e) {
expect(e.code).toBe(Parse.Error.USERNAME_TAKEN);
}
expect(loggerWarnSpy).not.toHaveBeenCalled();
expect(loggerErrorSpy.calls.count()).toBe(0);
});
it('user login with context', async () => {
let hit = 0;
const context = { foo: 'bar' };

View File

@@ -1481,6 +1481,12 @@ module.exports.LogLevels = {
'Log level used by the Cloud Code Functions on success. Default is `info`. See [LogLevel](LogLevel.html) for available values.',
default: 'info',
},
signupUsernameTaken: {
env: 'PARSE_SERVER_LOG_LEVELS_SIGNUP_USERNAME_TAKEN',
help:
'Log level used when a sign-up fails because the username already exists. Default is `info`. See [LogLevel](LogLevel.html) for available values.',
default: 'info',
},
triggerAfter: {
env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER',
help:

View File

@@ -324,6 +324,7 @@
* @interface LogLevels
* @property {String} cloudFunctionError Log level used by the Cloud Code Functions on error. Default is `error`. See [LogLevel](LogLevel.html) for available values.
* @property {String} cloudFunctionSuccess Log level used by the Cloud Code Functions on success. Default is `info`. See [LogLevel](LogLevel.html) for available values.
* @property {String} signupUsernameTaken Log level used when a sign-up fails because the username already exists. Default is `info`. See [LogLevel](LogLevel.html) for available values.
* @property {String} triggerAfter Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`. See [LogLevel](LogLevel.html) for available values.
* @property {String} triggerBeforeError Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`. See [LogLevel](LogLevel.html) for available values.
* @property {String} triggerBeforeSuccess Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`. See [LogLevel](LogLevel.html) for available values.

View File

@@ -786,4 +786,8 @@ export interface LogLevels {
:DEFAULT: error
*/
cloudFunctionError: ?string;
/* Log level used when a sign-up fails because the username already exists. Default is `info`. See [LogLevel](LogLevel.html) for available values.
:DEFAULT: info
*/
signupUsernameTaken: ?string;
}

View File

@@ -466,6 +466,8 @@ export function handleParseErrors(err, req, res, next) {
if (req.config && req.config.enableExpressErrorHandler) {
return next(err);
}
const signupUsernameTakenLevel =
req.config?.logLevels?.signupUsernameTaken || 'info';
let httpStatus;
// TODO: fill out this mapping
switch (err.code) {
@@ -480,7 +482,17 @@ export function handleParseErrors(err, req, res, next) {
}
res.status(httpStatus);
res.json({ code: err.code, error: err.message });
log.error('Parse error: ', err);
if (err.code === Parse.Error.USERNAME_TAKEN) {
if (signupUsernameTakenLevel !== 'silent') {
const loggerMethod =
typeof log[signupUsernameTakenLevel] === 'function'
? log[signupUsernameTakenLevel].bind(log)
: log.error.bind(log);
loggerMethod('Parse error: ', err);
}
} else {
log.error('Parse error: ', err);
}
} else if (err.status && err.message) {
res.status(err.status);
res.json({ error: err.message });

View File

@@ -296,5 +296,6 @@ export interface LogLevels {
triggerBeforeError?: string;
cloudFunctionSuccess?: string;
cloudFunctionError?: string;
signupUsernameTaken?: string;
}
export {};