diff --git a/Dockerfile b/Dockerfile index 09e85642..7145e100 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,11 @@ RUN apt-get update && apt-get install -y \ RUN apt clean && rm -rf /var/lib/apt/lists/* -RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath sockets gettext apcu +RUN pecl install apcu \ + && docker-php-ext-enable apcu \ + && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath sockets gettext \ + && pecl clear-cache + # XDEBUG RUN yes | pecl install ${XDEBUG_VERSION} COPY docker-compose/php/docker-php-ext-xdebug.ini $PHP_DIR/conf.d/docker-php-ext-xdebug.ini diff --git a/config/auth.php b/config/auth.php index a687a880..ca6f2558 100644 --- a/config/auth.php +++ b/config/auth.php @@ -102,8 +102,9 @@ 'password_reset_lifetime' => env('AUTH_PASSWORD_RESET_LIFETIME', 1800), 'password_min_length' => env('AUTH_PASSWORD_MIN_LENGTH', 8), 'password_max_length' => env('AUTH_PASSWORD_MAX_LENGTH', 30), - 'password_shape_pattern' => env('AUTH_PASSWORD_SHAPE_PATTERN', '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-])'), - 'password_shape_warning' => env('AUTH_PASSWORD_SHAPE_WARNING', 'Password must include at least one uppercase letter, one lowercase letter, one number, and one special character.'), + 'password_allowed_special_characters' => env('AUTH_PASSWORD_ALLOWED_SPECIAL_CHARACTERS', '[A-Za-z0-9#?!@$%^&*-+]'), + 'password_shape_pattern' => env('AUTH_PASSWORD_SHAPE_PATTERN', '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-+])[A-Za-z0-9#?!@$%^&*-+]+$'), + 'password_shape_warning' => env('AUTH_PASSWORD_SHAPE_WARNING', 'Password must include at least one uppercase letter, one lowercase letter, one number, and one special character (#?!@$%^&*-+).'), 'verification_email_lifetime' => env("AUTH_VERIFICATION_EMAIL_LIFETIME", 600), 'allows_native_auth' => env('AUTH_ALLOWS_NATIVE_AUTH', 1), 'allows_native_on_config' => env('AUTH_ALLOWS_NATIVE_AUTH_CONFIG', 1), diff --git a/resources/js/reset_password/reset_password.js b/resources/js/reset_password/reset_password.js index afbc21a9..0addb10a 100644 --- a/resources/js/reset_password/reset_password.js +++ b/resources/js/reset_password/reset_password.js @@ -167,7 +167,7 @@ const ResetPasswordPage = ({   - {`The Password must be ${passwordPolicy.min_length}–${passwordPolicy.max_length} characters, and ${passwordPolicy.shape_warning}`} +
diff --git a/resources/js/set_password/set_password.js b/resources/js/set_password/set_password.js index 43e4e756..86336491 100644 --- a/resources/js/set_password/set_password.js +++ b/resources/js/set_password/set_password.js @@ -276,7 +276,7 @@ const SetPasswordPage = ({   - {`The Password must be ${passwordPolicy.min_length}–${passwordPolicy.max_length} characters, and ${passwordPolicy.shape_warning}`} +
diff --git a/resources/js/signup/signup.js b/resources/js/signup/signup.js index f0fbc2ff..c7651d64 100644 --- a/resources/js/signup/signup.js +++ b/resources/js/signup/signup.js @@ -270,7 +270,7 @@ const SignUpPage = ({   - {`The Password must be ${passwordPolicy.min_length}–${passwordPolicy.max_length} characters, and ${passwordPolicy.shape_warning}`} +
diff --git a/resources/js/validator.js b/resources/js/validator.js index ccb7bf5d..afae1824 100644 --- a/resources/js/validator.js +++ b/resources/js/validator.js @@ -1,5 +1,36 @@ import {ref, string} from "yup"; +const validatePasswordPattern = (shapePattern, allowed_special_characters, warning) => { + return function(value) { + if (!value) return true; + + const pattern = new RegExp(shapePattern); + + if (pattern.test(value)) { + return true; + } + + // Check invalid characters + const allowedCharsRegEx = new RegExp(allowed_special_characters); + + for (let i = 0; i < value.length; i++) { + const char = value[i]; + if (!allowedCharsRegEx.test(char)) { + return this.createError({ + message: `Invalid character "${char}" at position ${i + 1}`, + path: this.path, + }); + } + } + + // Check remain requirements + return this.createError({ + message: warning, + path: this.path, + }); + } +} + export const emailValidator = (value) => { return /^\S+@\S+(\.\S+)*$/.test(value) } @@ -14,17 +45,13 @@ export const buildPasswordValidationSchema = (passwordPolicy, required = false) }) .min(passwordPolicy.min_length, `Password must be at least ${passwordPolicy.min_length} characters`) .max(passwordPolicy.max_length, `Password must be at most ${passwordPolicy.max_length} characters`) - .matches( - new RegExp(passwordPolicy.shape_pattern), - passwordPolicy.shape_warning - ), + .test('password-requirements', validatePasswordPattern( + passwordPolicy.shape_pattern, passwordPolicy.allowed_special_characters, passwordPolicy.shape_warning)), password_confirmation: string() .min(passwordPolicy.min_length, `Password confirmation must be at least ${passwordPolicy.min_length} characters`) .max(passwordPolicy.max_length, `Password confirmation must be at most ${passwordPolicy.max_length} characters`) - .matches( - new RegExp(passwordPolicy.shape_pattern), - passwordPolicy.shape_warning - ) + .test('password-requirements', validatePasswordPattern( + passwordPolicy.shape_pattern, passwordPolicy.allowed_special_characters, passwordPolicy.shape_warning)) .oneOf([ref('password'), null], 'Passwords must match') }; return res; diff --git a/resources/views/admin/edit-user.blade.php b/resources/views/admin/edit-user.blade.php index 1479ca56..cdb7b24c 100644 --- a/resources/views/admin/edit-user.blade.php +++ b/resources/views/admin/edit-user.blade.php @@ -63,6 +63,7 @@ min_length: {{ Config::get("auth.password_min_length") }}, max_length: {{ Config::get("auth.password_max_length") }}, shape_pattern: '{{ Config::get("auth.password_shape_pattern") }}', + allowed_special_characters: '{{ Config::get("auth.password_allowed_special_characters") }}', shape_warning: '{{ Config::get("auth.password_shape_warning") }}' } diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php index d81eaccb..10e4c20a 100644 --- a/resources/views/auth/passwords/reset.blade.php +++ b/resources/views/auth/passwords/reset.blade.php @@ -23,6 +23,7 @@ min_length: {{ Config::get("auth.password_min_length") }}, max_length: {{ Config::get("auth.password_max_length") }}, shape_pattern: '{{ Config::get("auth.password_shape_pattern") }}', + allowed_special_characters: '{{ Config::get("auth.password_allowed_special_characters") }}', shape_warning: '{{ Config::get("auth.password_shape_warning") }}' } @if ($errors->any()) diff --git a/resources/views/auth/passwords/set.blade.php b/resources/views/auth/passwords/set.blade.php index dcea1050..3952990c 100644 --- a/resources/views/auth/passwords/set.blade.php +++ b/resources/views/auth/passwords/set.blade.php @@ -27,6 +27,7 @@ min_length: {{ Config::get("auth.password_min_length") }}, max_length: {{ Config::get("auth.password_max_length") }}, shape_pattern: '{{ Config::get("auth.password_shape_pattern") }}', + allowed_special_characters: '{{ Config::get("auth.password_allowed_special_characters") }}', shape_warning: '{{ Config::get("auth.password_shape_warning") }}' } @if ($errors->any()) diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 9a19b9ce..110b9a01 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -27,6 +27,7 @@ min_length: {{ Config::get("auth.password_min_length") }}, max_length: {{ Config::get("auth.password_max_length") }}, shape_pattern: '{{ Config::get("auth.password_shape_pattern") }}', + allowed_special_characters: '{{ Config::get("auth.password_allowed_special_characters") }}', shape_warning: '{{ Config::get("auth.password_shape_warning") }}' } @if ($errors->any()) diff --git a/resources/views/profile.blade.php b/resources/views/profile.blade.php index 0686d11e..811648d0 100644 --- a/resources/views/profile.blade.php +++ b/resources/views/profile.blade.php @@ -81,6 +81,7 @@ min_length: {{ Config::get("auth.password_min_length") }}, max_length: {{ Config::get("auth.password_max_length") }}, shape_pattern: '{{ Config::get("auth.password_shape_pattern") }}', + allowed_special_characters: '{{ Config::get("auth.password_allowed_special_characters") }}', shape_warning: '{{ Config::get("auth.password_shape_warning") }}' }