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") }}'
}