From f076fc5740853035357770bbee68320073ac3c44 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Fri, 21 Apr 2023 11:20:12 +0800 Subject: [PATCH] refactor: add validation for initializing super admin username (#3744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core #### What this PR does / why we need it: 对初始超级管理员用户名增加合法性校验 #### Which issue(s) this PR fixes: Fixes #3482 #### Does this PR introduce a user-facing change? ```release-note 对初始超级管理员用户名增加合法性校验 ``` --- .../run/halo/app/infra/ValidationUtils.java | 33 +++++++++++ .../app/infra/properties/HaloProperties.java | 2 + .../infra/properties/SecurityProperties.java | 13 +++- .../halo/app/infra/ValidationUtilsTest.java | 59 +++++++++++++++++++ .../security/SuperAdminInitializerTest.java | 1 - 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/run/halo/app/infra/ValidationUtils.java create mode 100644 application/src/test/java/run/halo/app/infra/ValidationUtilsTest.java diff --git a/application/src/main/java/run/halo/app/infra/ValidationUtils.java b/application/src/main/java/run/halo/app/infra/ValidationUtils.java new file mode 100644 index 000000000..65dd065c2 --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/ValidationUtils.java @@ -0,0 +1,33 @@ +package run.halo.app.infra; + +import java.util.regex.Pattern; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; + +@UtilityClass +public class ValidationUtils { + public static final Pattern NAME_PATTERN = + Pattern.compile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"); + + public static final String NAME_VALIDATION_MESSAGE = """ + Super administrator username must be a valid subdomain name, the name must: + 1. contain no more than 63 characters + 2. contain only lowercase alphanumeric characters, '-' or '.' + 3. start with an alphanumeric character + 4. end with an alphanumeric character + """; + + /** + * Validates the name. + * + * @param name name for validation + * @return true if the name is valid + */ + public static boolean validateName(String name) { + if (StringUtils.isBlank(name)) { + return false; + } + boolean matches = NAME_PATTERN.matcher(name).matches(); + return matches && name.length() <= 63; + } +} diff --git a/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java b/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java index 0a03bf632..77ac2a464 100644 --- a/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java +++ b/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java @@ -70,5 +70,7 @@ public class HaloProperties implements Validator { errors.rejectValue("externalUrl", "external-url.required.when-using-absolute-permalink", "External URL is required when property `use-absolute-permalink` is set to true."); } + SecurityProperties.Initializer.validateUsername(props.getSecurity().getInitializer(), + errors); } } diff --git a/application/src/main/java/run/halo/app/infra/properties/SecurityProperties.java b/application/src/main/java/run/halo/app/infra/properties/SecurityProperties.java index f64fad93a..54fa4e40f 100644 --- a/application/src/main/java/run/halo/app/infra/properties/SecurityProperties.java +++ b/application/src/main/java/run/halo/app/infra/properties/SecurityProperties.java @@ -3,8 +3,11 @@ package run.halo.app.infra.properties; import static org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN; import lombok.Data; +import org.springframework.lang.NonNull; import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy; import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter.Mode; +import org.springframework.validation.Errors; +import run.halo.app.infra.ValidationUtils; @Data public class SecurityProperties { @@ -39,6 +42,14 @@ public class SecurityProperties { private String superAdminPassword; + static void validateUsername(@NonNull Initializer initializer, @NonNull Errors errors) { + if (initializer.isDisabled() || ValidationUtils.validateName( + initializer.getSuperAdminUsername())) { + return; + } + errors.rejectValue("security.initializer.superAdminUsername", + "initializer.superAdminUsername.invalid", + ValidationUtils.NAME_VALIDATION_MESSAGE); + } } - } diff --git a/application/src/test/java/run/halo/app/infra/ValidationUtilsTest.java b/application/src/test/java/run/halo/app/infra/ValidationUtilsTest.java new file mode 100644 index 000000000..9f1962303 --- /dev/null +++ b/application/src/test/java/run/halo/app/infra/ValidationUtilsTest.java @@ -0,0 +1,59 @@ +package run.halo.app.infra; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link ValidationUtils}. + * + * @author guqing + * @since 2.5.0 + */ +class ValidationUtilsTest { + + @Nested + class NameValidationTest { + @Test + void nullName() { + assertThat(ValidationUtils.validateName(null)).isFalse(); + } + + @Test + void emptyUsername() { + assertThat(ValidationUtils.validateName("")).isFalse(); + } + + @Test + void startWithIllegalCharacter() { + assertThat(ValidationUtils.validateName("-abc")).isFalse(); + } + + @Test + void endWithIllegalCharacter() { + assertThat(ValidationUtils.validateName("abc-")).isFalse(); + assertThat(ValidationUtils.validateName("abcD")).isFalse(); + } + + @Test + void middleWithIllegalCharacter() { + assertThat(ValidationUtils.validateName("ab?c")).isFalse(); + } + + @Test + void moreThan63Characters() { + assertThat(ValidationUtils.validateName(StringUtils.repeat('a', 64))).isFalse(); + } + + @Test + void correctUsername() { + assertThat(ValidationUtils.validateName("abc")).isTrue(); + assertThat(ValidationUtils.validateName("ab-c")).isTrue(); + assertThat(ValidationUtils.validateName("1st")).isTrue(); + assertThat(ValidationUtils.validateName("ast1")).isTrue(); + assertThat(ValidationUtils.validateName("ast-1")).isTrue(); + } + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java b/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java index 445e0587b..909bee4f7 100644 --- a/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java +++ b/application/src/test/java/run/halo/app/security/SuperAdminInitializerTest.java @@ -59,5 +59,4 @@ class SuperAdminInitializerTest { return false; })); } - }