Update authentication system, database models, and UI components
This commit is contained in:
169
static/uid-validator.js
Normal file
169
static/uid-validator.js
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* UID Validation Utility
|
||||
*
|
||||
* Provides comprehensive UID format validation and sanitization
|
||||
* to ensure all UIDs are properly formatted as email addresses.
|
||||
*/
|
||||
|
||||
export class UidValidator {
|
||||
constructor() {
|
||||
// RFC 5322 compliant email regex (basic validation)
|
||||
this.emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
|
||||
// Common invalid patterns to check against
|
||||
this.invalidPatterns = [
|
||||
/^devuser$/i, // Legacy username pattern
|
||||
/^user\d+$/i, // Generic user patterns
|
||||
/^test$/i, // Test user
|
||||
/^admin$/i, // Admin user
|
||||
/^\d+$/, // Pure numeric
|
||||
/^[a-zA-Z]+$/, // Pure alphabetic (no @ symbol)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate UID format - must be a valid email address
|
||||
*/
|
||||
isValidFormat(uid) {
|
||||
if (!uid || typeof uid !== 'string') {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'UID must be a non-empty string',
|
||||
code: 'INVALID_TYPE'
|
||||
};
|
||||
}
|
||||
|
||||
const trimmed = uid.trim();
|
||||
if (trimmed.length === 0) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'UID cannot be empty',
|
||||
code: 'EMPTY_UID'
|
||||
};
|
||||
}
|
||||
|
||||
// Check against invalid patterns
|
||||
for (const pattern of this.invalidPatterns) {
|
||||
if (pattern.test(trimmed)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `UID matches invalid pattern: ${pattern}`,
|
||||
code: 'INVALID_PATTERN'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
if (!this.emailRegex.test(trimmed)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'UID must be a valid email address',
|
||||
code: 'INVALID_EMAIL_FORMAT'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
sanitized: trimmed.toLowerCase()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize and validate UID - ensures consistent format
|
||||
*/
|
||||
sanitize(uid) {
|
||||
const validation = this.isValidFormat(uid);
|
||||
|
||||
if (!validation.valid) {
|
||||
console.error('[UID-VALIDATOR] Validation failed:', validation.error, { uid });
|
||||
return null;
|
||||
}
|
||||
|
||||
return validation.sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and throw error if invalid
|
||||
*/
|
||||
validateOrThrow(uid, context = 'UID') {
|
||||
const validation = this.isValidFormat(uid);
|
||||
|
||||
if (!validation.valid) {
|
||||
throw new Error(`${context} validation failed: ${validation.error} (${validation.code})`);
|
||||
}
|
||||
|
||||
return validation.sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a UID needs migration (legacy format)
|
||||
*/
|
||||
needsMigration(uid) {
|
||||
if (!uid || typeof uid !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const trimmed = uid.trim();
|
||||
|
||||
// Check if it's already a valid email
|
||||
if (this.emailRegex.test(trimmed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it matches known legacy patterns
|
||||
for (const pattern of this.invalidPatterns) {
|
||||
if (pattern.test(trimmed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Any non-email format needs migration
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validation statistics for debugging
|
||||
*/
|
||||
getValidationStats(uids) {
|
||||
const stats = {
|
||||
total: uids.length,
|
||||
valid: 0,
|
||||
invalid: 0,
|
||||
needsMigration: 0,
|
||||
errors: {}
|
||||
};
|
||||
|
||||
uids.forEach(uid => {
|
||||
const validation = this.isValidFormat(uid);
|
||||
|
||||
if (validation.valid) {
|
||||
stats.valid++;
|
||||
} else {
|
||||
stats.invalid++;
|
||||
const code = validation.code || 'UNKNOWN';
|
||||
stats.errors[code] = (stats.errors[code] || 0) + 1;
|
||||
}
|
||||
|
||||
if (this.needsMigration(uid)) {
|
||||
stats.needsMigration++;
|
||||
}
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const uidValidator = new UidValidator();
|
||||
|
||||
// Legacy exports for backward compatibility
|
||||
export function validateUidFormat(uid) {
|
||||
return uidValidator.isValidFormat(uid).valid;
|
||||
}
|
||||
|
||||
export function sanitizeUid(uid) {
|
||||
return uidValidator.sanitize(uid);
|
||||
}
|
||||
|
||||
export function validateUidOrThrow(uid, context) {
|
||||
return uidValidator.validateOrThrow(uid, context);
|
||||
}
|
Reference in New Issue
Block a user