mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
[refactor] Use built-in Ajv regexp validator; For bug 77233
This commit is contained in:
7
AdminPanel/client/package-lock.json
generated
7
AdminPanel/client/package-lock.json
generated
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "docscloud",
|
||||
"name": "onlyoffice-adminpanel-client",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
@ -1901,6 +1901,11 @@
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"ajv-errors": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz",
|
||||
"integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ=="
|
||||
},
|
||||
"ajv-formats": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "docscloud",
|
||||
"name": "onlyoffice-adminpanel-client",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@ -10,6 +10,7 @@
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-errors": "^3.0.0",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"axios": "1.7.4",
|
||||
"prop-types": "^15.8.1",
|
||||
|
||||
@ -2,6 +2,7 @@ import {useState, useEffect, useCallback} from 'react';
|
||||
import {useSelector} from 'react-redux';
|
||||
import Ajv from 'ajv';
|
||||
import addFormats from 'ajv-formats';
|
||||
import addErrors from 'ajv-errors';
|
||||
import {selectSchema, selectSchemaLoading, selectSchemaError} from '../store/slices/configSlice';
|
||||
|
||||
/**
|
||||
@ -22,19 +23,7 @@ export const useFieldValidation = () => {
|
||||
// Build AJV validator with custom and standard formats
|
||||
const ajv = new Ajv({allErrors: true, strict: false});
|
||||
addFormats(ajv); // Add standard formats including email
|
||||
|
||||
// Register formats from schema $defs.formats (regex strings)
|
||||
const formats = schema?.$defs?.formats;
|
||||
if (formats && typeof formats === 'object') {
|
||||
for (const [name, patternString] of Object.entries(formats)) {
|
||||
try {
|
||||
const re = new RegExp(patternString);
|
||||
ajv.addFormat(name, re);
|
||||
} catch (e) {
|
||||
console.warn('Invalid format regex in schema $defs.formats:', name, e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
addErrors(ajv);
|
||||
|
||||
const validateFn = ajv.compile(schema);
|
||||
setValidator(() => validateFn);
|
||||
|
||||
5
AdminPanel/server/package-lock.json
generated
5
AdminPanel/server/package-lock.json
generated
@ -55,6 +55,11 @@
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"ajv-errors": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz",
|
||||
"integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ=="
|
||||
},
|
||||
"ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "adminpanel",
|
||||
"name": "onlyoffice-adminpanel-server",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://www.onlyoffice.com",
|
||||
"private": true,
|
||||
@ -19,6 +19,7 @@
|
||||
"express": "^4.19.2",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"ajv-errors": "^3.0.0",
|
||||
"joi": "^17.13.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ms": "^2.1.3"
|
||||
|
||||
@ -140,7 +140,9 @@ router.post('/setup', async (req, res) => {
|
||||
|
||||
const passwordValidationResult = validatePassword(ctx, password);
|
||||
if (!passwordValidationResult.isValid) {
|
||||
return res.status(400).json({error: 'Password is too weak'});
|
||||
return res
|
||||
.status(400)
|
||||
.json({error: 'Password must me at least 8 characters long, contain at least one digit, one uppercase letter and one special character'});
|
||||
}
|
||||
|
||||
await passwordManager.saveAdminPassword(ctx, password);
|
||||
@ -174,7 +176,9 @@ router.post('/change-password', requireAuth, async (req, res) => {
|
||||
|
||||
const passwordValidationResult = validatePassword(ctx, newPassword);
|
||||
if (!passwordValidationResult.isValid) {
|
||||
return res.status(400).json({error: 'Password is too weak'});
|
||||
return res
|
||||
.status(400)
|
||||
.json({error: 'Password must me at least 8 characters long, contain at least one digit, one uppercase letter and one special character'});
|
||||
}
|
||||
|
||||
if (currentPassword === newPassword) {
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
'use strict';
|
||||
const Ajv = require('ajv');
|
||||
const addFormats = require('ajv-formats');
|
||||
const addErrors = require('ajv-errors');
|
||||
const logger = require('../../../../../Common/sources/logger');
|
||||
const tenantManager = require('../../../../../Common/sources/tenantManager');
|
||||
const supersetSchema = require('../../../../../Common/config/schemas/config.schema.json');
|
||||
@ -48,14 +49,6 @@ const AJV_FILTER_CONFIG = {allErrors: true, strict: false, removeAdditional: tru
|
||||
*/
|
||||
function registerAjvExtras(instance) {
|
||||
instance.addKeyword({keyword: X_SCOPE_KEYWORD, schemaType: ['string', 'array'], errors: false});
|
||||
|
||||
const formats = supersetSchema?.$defs?.formats;
|
||||
if (formats && typeof formats === 'object') {
|
||||
for (const [name, patternString] of Object.entries(formats)) {
|
||||
const re = new RegExp(patternString);
|
||||
instance.addFormat(name, re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,6 +59,7 @@ function registerAjvExtras(instance) {
|
||||
function createAjvInstance(config) {
|
||||
const instance = new Ajv(config);
|
||||
addFormats(instance);
|
||||
addErrors(instance);
|
||||
registerAjvExtras(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -6,12 +6,26 @@
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"formats": {
|
||||
"cron6": "^\\s*\\S+(?:\\s+\\S+){5}\\s*$",
|
||||
"passlength": "^.{8,128}$",
|
||||
"passdigit": ".*\\d.*",
|
||||
"passupper": ".*[A-Z].*",
|
||||
"passspecial": ".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*"
|
||||
"cron6": {
|
||||
"type": "string",
|
||||
"pattern": "^\\s*\\S+(?:\\s+\\S+){5}\\s*$",
|
||||
"errorMessage": "Cron expression must have exactly 6 parts"
|
||||
},
|
||||
"passlength": {
|
||||
"type": "string",
|
||||
"pattern": "^.{8,128}$"
|
||||
},
|
||||
"passdigit": {
|
||||
"type": "string",
|
||||
"pattern": ".*\\d.*"
|
||||
},
|
||||
"passupper": {
|
||||
"type": "string",
|
||||
"pattern": ".*[A-Z].*"
|
||||
},
|
||||
"passspecial": {
|
||||
"type": "string",
|
||||
"pattern": ".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
@ -88,8 +102,8 @@
|
||||
"additionalProperties": false,
|
||||
"x-scope": ["admin", "tenant"],
|
||||
"properties": {
|
||||
"filesCron": {"type": "string", "format": "cron6", "x-scope": "admin"},
|
||||
"documentsCron": {"type": "string", "format": "cron6", "x-scope": "admin"},
|
||||
"filesCron": {"$ref": "#/$defs/cron6", "x-scope": "admin"},
|
||||
"documentsCron": {"$ref": "#/$defs/cron6", "x-scope": "admin"},
|
||||
"files": {"type": "integer", "minimum": 0, "x-scope": "admin"},
|
||||
"filesremovedatonce": {"type": "integer", "minimum": 0, "x-scope": "admin"},
|
||||
"sessionidle": {"type": "string", "x-scope": ["admin", "tenant"]},
|
||||
@ -415,26 +429,22 @@
|
||||
"description": "Password validation requirements using custom format types",
|
||||
"properties": {
|
||||
"minLength": {
|
||||
"type": "string",
|
||||
"format": "passlength",
|
||||
"$ref": "#/$defs/passlength",
|
||||
"description": "be at least 8 characters long",
|
||||
"x-scope": ["admin", "tenant"]
|
||||
},
|
||||
"hasDigit": {
|
||||
"type": "string",
|
||||
"format": "passdigit",
|
||||
"$ref": "#/$defs/passdigit",
|
||||
"description": "contain at least one digit",
|
||||
"x-scope": ["admin", "tenant"]
|
||||
},
|
||||
"hasUppercase": {
|
||||
"type": "string",
|
||||
"format": "passupper",
|
||||
"$ref": "#/$defs/passupper",
|
||||
"description": "contain at least one uppercase letter",
|
||||
"x-scope": ["admin", "tenant"]
|
||||
},
|
||||
"hasSpecialChar": {
|
||||
"type": "string",
|
||||
"format": "passspecial",
|
||||
"$ref": "#/$defs/passspecial",
|
||||
"description": "contain at least one special character",
|
||||
"x-scope": ["admin", "tenant"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user