Merge pull request #1723 from AymanAlSuleihi/fix/create-admin-user-seed-validation

fix: Create admin user seed validation
This commit is contained in:
Daniel Hiller
2026-09-17 00:44:37 +02:00
committed by GitHub
2 changed files with 124 additions and 10 deletions
+68
View File
@@ -7,6 +7,8 @@
/* eslint-disable no-console */
const { read } = require('read');
const validator = require('validator');
const zxcvbn = require('zxcvbn');
const initKnex = require('knex');
const knexfile = require('./knexfile');
@@ -47,21 +49,87 @@ const input = async (fieldName, options = {}) => {
try {
await knex.migrate.latest();
let isEmailValid = false;
while (!isEmailValid) {
process.env.DEFAULT_ADMIN_EMAIL = await input('Email', {
isRequired: true,
});
if (
!validator.isEmail(process.env.DEFAULT_ADMIN_EMAIL) ||
process.env.DEFAULT_ADMIN_EMAIL.length > 256
) {
console.log('Email must be a valid e-mail address with no more than 256 characters.');
} else {
const existingUser = await knex('user_account')
.where('email', process.env.DEFAULT_ADMIN_EMAIL.toLowerCase())
.first();
if (existingUser) {
console.log('Email is already in use.');
} else {
isEmailValid = true;
}
}
}
let isPasswordValid = false;
while (!isPasswordValid) {
process.env.DEFAULT_ADMIN_PASSWORD = await input('Password', {
isRequired: true,
isPassword: true,
});
if (zxcvbn(process.env.DEFAULT_ADMIN_PASSWORD).score >= 2) {
isPasswordValid = true;
} else {
console.log('Password is too weak. Choose a stronger password.');
}
}
process.env.DEFAULT_ADMIN_NAME = await input('Name', {
isRequired: true,
});
while (process.env.DEFAULT_ADMIN_NAME.length > 128) {
console.log('Name must be no more than 128 characters.');
process.env.DEFAULT_ADMIN_NAME = await input('Name', {
isRequired: true,
});
}
const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
let isUsernameValid = false;
while (!isUsernameValid) {
process.env.DEFAULT_ADMIN_USERNAME = await input('Username');
if (!process.env.DEFAULT_ADMIN_USERNAME) {
break;
}
const isValid =
process.env.DEFAULT_ADMIN_USERNAME.length >= 3 &&
process.env.DEFAULT_ADMIN_USERNAME.length <= 32 &&
USERNAME_REGEX.test(process.env.DEFAULT_ADMIN_USERNAME);
if (!isValid) {
console.log(
'Username must be 3-32 characters and contain only letters, digits, underscores, and dots (e.g., john_doe).',
);
} else {
const existingUser = await knex('user_account')
.where('username', process.env.DEFAULT_ADMIN_USERNAME.toLowerCase())
.first();
if (existingUser) {
console.log('Username is already in use.');
} else {
isUsernameValid = true;
}
}
}
await knex.seed.run({
specific: 'default.js',
});
+47 -1
View File
@@ -3,7 +3,12 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
/* eslint-disable no-console */
const bcrypt = require('bcrypt');
const validator = require('validator');
const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
const buildUserData = () => {
const data = {
@@ -15,10 +20,23 @@ const buildUserData = () => {
data.password = bcrypt.hashSync(process.env.DEFAULT_ADMIN_PASSWORD, 10);
}
if (process.env.DEFAULT_ADMIN_NAME) {
if (process.env.DEFAULT_ADMIN_NAME.length > 128) {
console.warn('Warning: DEFAULT_ADMIN_NAME exceeds 128 characters; truncating.');
data.name = process.env.DEFAULT_ADMIN_NAME.slice(0, 128);
} else {
data.name = process.env.DEFAULT_ADMIN_NAME;
}
}
if (process.env.DEFAULT_ADMIN_USERNAME) {
data.username = process.env.DEFAULT_ADMIN_USERNAME.toLowerCase();
const username = process.env.DEFAULT_ADMIN_USERNAME.toLowerCase();
if (username.length < 3 || username.length > 32 || !USERNAME_REGEX.test(username)) {
console.warn(
`Warning: DEFAULT_ADMIN_USERNAME "${process.env.DEFAULT_ADMIN_USERNAME}" is invalid; skipping.`,
);
} else {
data.username = username;
}
}
return data;
@@ -45,8 +63,36 @@ exports.seed = async (knex) => {
process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase();
if (defaultAdminEmail) {
if (!validator.isEmail(defaultAdminEmail)) {
throw new Error(
`DEFAULT_ADMIN_EMAIL "${process.env.DEFAULT_ADMIN_EMAIL}" is not a valid e-mail address.`,
);
}
if (defaultAdminEmail.length > 256) {
throw new Error(
`DEFAULT_ADMIN_EMAIL "${process.env.DEFAULT_ADMIN_EMAIL}" exceeds 256 characters.`,
);
}
const existingEmailUser = await knex('user_account').where('email', defaultAdminEmail).first();
if (existingEmailUser) {
throw new Error(`User with DEFAULT_ADMIN_EMAIL "${defaultAdminEmail}" already exists.`);
}
const userData = buildUserData();
if (userData.username) {
const existingUsernameUser = await knex('user_account')
.where('username', userData.username)
.first();
if (existingUsernameUser) {
throw new Error(`User with DEFAULT_ADMIN_USERNAME "${userData.username}" already exists.`);
}
}
let userId;
try {
[{ id: userId }] = await knex('user_account').insert(