From 150e9975bacde178a6853743b855ad5a5eb9fe1e Mon Sep 17 00:00:00 2001 From: John Niang Date: Thu, 27 Jul 2023 16:59:19 +0800 Subject: [PATCH] Suppress compilation warnings and remove deprecated method and classes (#4308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind cleanup /area core /milestone 2.8.x #### What this PR does / why we need it: - Suppress compilation warnings. - Remove deprecated methods and classes. - Remove unused methods. - Before ```bash ❯ ./gradlew compileJava compileTestJava > Task :application:compileJava /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:48: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal private final ThemePathPolicy themePathPolicy; ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:48: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal private final ThemePathPolicy themePathPolicy; ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:48: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal private final ThemePathPolicy themePathPolicy; ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:48: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal private final ThemePathPolicy themePathPolicy; ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:48: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal private final ThemePathPolicy themePathPolicy; ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java:60: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal themePathPolicy = new ThemePathPolicy(haloProperties.getWorkDir()); ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:64: warning: [removal] authorizeExchange() in ServerHttpSecurity has been deprecated and marked for removal .authorizeExchange().anyExchange() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:65: warning: [removal] and() in ServerHttpSecurity.AuthorizeExchangeSpec has been deprecated and marked for removal .access(new RequestInfoAuthorizationManager(roleService)).and() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:88: warning: [removal] authorizeExchange() in ServerHttpSecurity has been deprecated and marked for removal .authorizeExchange().anyExchange().permitAll().and() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:88: warning: [removal] and() in ServerHttpSecurity.AuthorizeExchangeSpec has been deprecated and marked for removal .authorizeExchange().anyExchange().permitAll().and() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:90: warning: [removal] headers() in ServerHttpSecurity has been deprecated and marked for removal .headers() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:100: warning: [removal] cache() in ServerHttpSecurity.HeaderSpec has been deprecated and marked for removal .cache().disable().and() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java:100: warning: [removal] and() in ServerHttpSecurity.HeaderSpec has been deprecated and marked for removal .cache().disable().and() ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/security/CsrfConfigurer.java:24: warning: [removal] csrf() in ServerHttpSecurity has been deprecated and marked for removal http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()) ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java:58: warning: [removal] visitRulesFor(UserDetails,RuleAccumulator) in AuthorizationRuleResolver has been deprecated and marked for removal public void visitRulesFor(UserDetails user, RuleAccumulator visitor) { ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java:43: warning: [removal] rulesFor(UserDetails) in AuthorizationRuleResolver has been deprecated and marked for removal public PolicyRuleList rulesFor(UserDetails user) { ^ Note: Some input files use or override a deprecated API. Note: Recompile with -Xlint:deprecation for details. Note: /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 16 warnings > Task :application:compileTestJava /Users/johnniang/workspaces/halo-dev/halo/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java:90: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal final ThemePathPolicy themePathPolicy = new ThemePathPolicy(testWorkDir); ^ /Users/johnniang/workspaces/halo-dev/halo/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java:90: warning: [removal] ThemePathPolicy in run.halo.app.theme has been deprecated and marked for removal final ThemePathPolicy themePathPolicy = new ThemePathPolicy(testWorkDir); ^ Note: /Users/johnniang/workspaces/halo-dev/halo/application/src/test/java/run/halo/app/security/authorization/RequestInfoResolverTest.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. Note: /Users/johnniang/workspaces/halo-dev/halo/application/src/test/java/run/halo/app/migration/BackupReconcilerTest.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 2 warnings ``` - After ```bash ❯ ./gradlew clean compileJava compileTestJava > Task :api:compileJava /Users/johnniang/workspaces/halo-dev/halo/api/src/main/java/run/halo/app/extension/Unstructured.java:69: warning: This field does not exist, or would have been excluded anyway. @EqualsAndHashCode(exclude = "version") ^ Note: /Users/johnniang/workspaces/halo-dev/halo/api/src/main/java/run/halo/app/extension/Unstructured.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 warning > Task :application:compileJava Note: /Users/johnniang/workspaces/halo-dev/halo/application/src/main/java/run/halo/app/plugin/SpringExtensionFactory.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. > Task :api:compileTestJava Note: /Users/johnniang/workspaces/halo-dev/halo/api/src/test/java/run/halo/app/infra/utils/JsonUtilsTest.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. BUILD SUCCESSFUL in 7s 22 actionable tasks: 15 executed, 7 up-to-date ``` #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../run/halo/app/core/extension/Menu.java | 6 +- .../run/halo/app/core/extension/MenuItem.java | 4 +- .../run/halo/app/core/extension/Role.java | 3 +- .../run/halo/app/core/extension/Setting.java | 10 +- .../run/halo/app/core/extension/Theme.java | 2 +- .../run/halo/app/core/extension/User.java | 15 +- .../core/extension/attachment/Attachment.java | 3 +- .../core/extension/attachment/Constant.java | 2 - .../app/core/extension/attachment/Group.java | 5 +- .../app/core/extension/content/Category.java | 9 +- .../app/core/extension/content/Comment.java | 25 +-- .../app/core/extension/content/Reply.java | 6 +- .../core/extension/content/SinglePage.java | 22 +-- .../app/core/extension/content/Snapshot.java | 10 +- .../halo/app/core/extension/content/Tag.java | 8 +- .../halo/app/extension/ExtensionOperator.java | 32 +--- .../run/halo/app/extension/ListResult.java | 24 ++- .../halo/app/extension/MetadataOperator.java | 4 +- .../main/java/run/halo/app/extension/Ref.java | 4 +- .../java/run/halo/app/infra/Condition.java | 12 +- .../java/run/halo/app/search/SearchParam.java | 4 +- .../app/config/WebServerSecurityConfig.java | 32 ++-- .../run/halo/app/content/ContentRequest.java | 10 +- .../java/run/halo/app/content/ListedPost.java | 14 +- .../halo/app/content/ListedSinglePage.java | 10 +- .../run/halo/app/content/PostRequest.java | 6 +- .../halo/app/content/SinglePageRequest.java | 6 +- .../app/content/comment/CommentRequest.java | 8 +- .../app/content/comment/ListedComment.java | 8 +- .../halo/app/content/comment/ListedReply.java | 8 +- .../app/content/comment/ReplyRequest.java | 6 +- .../endpoint/AttachmentEndpoint.java | 5 +- .../core/extension/endpoint/UserEndpoint.java | 6 +- .../extension/reconciler/ThemeReconciler.java | 15 +- .../extension/service/DefaultRoleService.java | 49 ----- .../core/extension/service/RoleService.java | 11 -- .../core/extension/theme/ThemeEndpoint.java | 4 +- .../ReactiveExtensionClientImpl.java | 3 + .../run/halo/app/metrics/VisitLogWriter.java | 169 ------------------ .../app/search/extension/SearchEngine.java | 6 +- .../run/halo/app/security/CsrfConfigurer.java | 8 +- .../AuthorizationRuleResolver.java | 23 --- .../authorization/DefaultRuleResolver.java | 86 +-------- .../run/halo/app/theme/ThemePathPolicy.java | 34 ---- .../halo/app/theme/finders/vo/ReplyVo.java | 10 +- .../config/ExtensionConfigurationTest.java | 3 - .../app/content/PostIntegrationTests.java | 6 - .../extension/endpoint/UserEndpointTest.java | 13 +- .../reconciler/ThemeReconcilerTest.java | 45 ++--- .../service/DefaultRoleServiceTest.java | 54 +----- .../halo/app/metrics/VisitLogWriterTest.java | 56 ------ .../app/migration/BackupReconcilerTest.java | 13 +- .../run/halo/app/security/LoginUtils.java | 23 --- .../jwt/JwtAuthenticationTest.java | 86 --------- .../authentication/jwt/LoginTest.java | 110 ------------ .../authorization/AuthorizationTest.java | 85 ++------- .../DefaultRuleResolverTest.java | 113 ++++++++++++ .../RequestInfoResolverTest.java | 99 +--------- 58 files changed, 367 insertions(+), 1086 deletions(-) delete mode 100644 application/src/main/java/run/halo/app/metrics/VisitLogWriter.java delete mode 100644 application/src/main/java/run/halo/app/theme/ThemePathPolicy.java delete mode 100644 application/src/test/java/run/halo/app/metrics/VisitLogWriterTest.java delete mode 100644 application/src/test/java/run/halo/app/security/LoginUtils.java delete mode 100644 application/src/test/java/run/halo/app/security/authentication/jwt/JwtAuthenticationTest.java delete mode 100644 application/src/test/java/run/halo/app/security/authentication/jwt/LoginTest.java create mode 100644 application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java diff --git a/api/src/main/java/run/halo/app/core/extension/Menu.java b/api/src/main/java/run/halo/app/core/extension/Menu.java index 950c3cbfa..02117a41f 100644 --- a/api/src/main/java/run/halo/app/core/extension/Menu.java +++ b/api/src/main/java/run/halo/app/core/extension/Menu.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import java.util.LinkedHashSet; @@ -15,14 +17,14 @@ import run.halo.app.extension.GVK; @GVK(group = "", version = "v1alpha1", kind = "Menu", plural = "menus", singular = "menu") public class Menu extends AbstractExtension { - @Schema(description = "The spec of menu.", required = true) + @Schema(description = "The spec of menu.", requiredMode = REQUIRED) private Spec spec; @Data @Schema(name = "MenuSpec") public static class Spec { - @Schema(description = "The display name of the menu.", required = true) + @Schema(description = "The display name of the menu.", requiredMode = REQUIRED) private String displayName; @Schema(description = "Names of menu children below this menu.") diff --git a/api/src/main/java/run/halo/app/core/extension/MenuItem.java b/api/src/main/java/run/halo/app/core/extension/MenuItem.java index 56bd9395b..51301d0b1 100644 --- a/api/src/main/java/run/halo/app/core/extension/MenuItem.java +++ b/api/src/main/java/run/halo/app/core/extension/MenuItem.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -19,7 +21,7 @@ import run.halo.app.extension.Ref; plural = "menuitems", singular = "menuitem") public class MenuItem extends AbstractExtension { - @Schema(description = "The spec of menu item.", required = true) + @Schema(description = "The spec of menu item.", requiredMode = REQUIRED) private MenuItemSpec spec; @Schema(description = "The status of menu item.") diff --git a/api/src/main/java/run/halo/app/core/extension/Role.java b/api/src/main/java/run/halo/app/core/extension/Role.java index 6faedcada..a75442865 100644 --- a/api/src/main/java/run/halo/app/core/extension/Role.java +++ b/api/src/main/java/run/halo/app/core/extension/Role.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static java.util.Arrays.compare; import static run.halo.app.core.extension.Role.GROUP; import static run.halo.app.core.extension.Role.KIND; @@ -44,7 +45,7 @@ public class Role extends AbstractExtension { public static final String VERSION = "v1alpha1"; public static final String KIND = "Role"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) List rules; /** diff --git a/api/src/main/java/run/halo/app/core/extension/Setting.java b/api/src/main/java/run/halo/app/core/extension/Setting.java index 918e859b1..7edd8f7a2 100644 --- a/api/src/main/java/run/halo/app/core/extension/Setting.java +++ b/api/src/main/java/run/halo/app/core/extension/Setting.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import lombok.Data; @@ -21,25 +23,25 @@ public class Setting extends AbstractExtension { public static final String KIND = "Setting"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private SettingSpec spec; @Data public static class SettingSpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private List forms; } @Data public static class SettingForm { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String group; private String label; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private List formSchema; } } diff --git a/api/src/main/java/run/halo/app/core/extension/Theme.java b/api/src/main/java/run/halo/app/core/extension/Theme.java index 07e39d971..d8dcba4ce 100644 --- a/api/src/main/java/run/halo/app/core/extension/Theme.java +++ b/api/src/main/java/run/halo/app/core/extension/Theme.java @@ -34,7 +34,7 @@ public class Theme extends AbstractExtension { public static final String THEME_NAME_LABEL = "theme.halo.run/theme-name"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private ThemeSpec spec; private ThemeStatus status; diff --git a/api/src/main/java/run/halo/app/core/extension/User.java b/api/src/main/java/run/halo/app/core/extension/User.java index 65ab52bec..045bf5f55 100644 --- a/api/src/main/java/run/halo/app/core/extension/User.java +++ b/api/src/main/java/run/halo/app/core/extension/User.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static run.halo.app.core.extension.User.GROUP; import static run.halo.app.core.extension.User.KIND; import static run.halo.app.core.extension.User.VERSION; @@ -41,7 +42,7 @@ public class User extends AbstractExtension { public static final String HIDDEN_USER_LABEL = "halo.run/hidden-user"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private UserSpec spec; private UserStatus status; @@ -49,12 +50,12 @@ public class User extends AbstractExtension { @Data public static class UserSpec { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String displayName; private String avatar; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String email; private String phone; @@ -87,16 +88,16 @@ public class User extends AbstractExtension { @Data public static class LoginHistory { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Instant loginAt; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String sourceIp; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String userAgent; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Boolean successful; private String reason; diff --git a/api/src/main/java/run/halo/app/core/extension/attachment/Attachment.java b/api/src/main/java/run/halo/app/core/extension/attachment/Attachment.java index e21b2b64c..974c97763 100644 --- a/api/src/main/java/run/halo/app/core/extension/attachment/Attachment.java +++ b/api/src/main/java/run/halo/app/core/extension/attachment/Attachment.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension.attachment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static run.halo.app.core.extension.attachment.Attachment.KIND; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -20,7 +21,7 @@ public class Attachment extends AbstractExtension { public static final String KIND = "Attachment"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private AttachmentSpec spec; private AttachmentStatus status; diff --git a/api/src/main/java/run/halo/app/core/extension/attachment/Constant.java b/api/src/main/java/run/halo/app/core/extension/attachment/Constant.java index 061525abf..9528df6a0 100644 --- a/api/src/main/java/run/halo/app/core/extension/attachment/Constant.java +++ b/api/src/main/java/run/halo/app/core/extension/attachment/Constant.java @@ -20,8 +20,6 @@ public enum Constant { * Do not use this key to set external link. You could implement * {@link AttachmentHandler#getPermalink} by your self. *

- * - * @deprecated Use your own group instead. */ public static final String EXTERNAL_LINK_ANNO_KEY = GROUP + "/external-link"; diff --git a/api/src/main/java/run/halo/app/core/extension/attachment/Group.java b/api/src/main/java/run/halo/app/core/extension/attachment/Group.java index d3b743110..dcc61791b 100644 --- a/api/src/main/java/run/halo/app/core/extension/attachment/Group.java +++ b/api/src/main/java/run/halo/app/core/extension/attachment/Group.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension.attachment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static run.halo.app.core.extension.attachment.Group.KIND; import io.swagger.v3.oas.annotations.media.Schema; @@ -20,7 +21,7 @@ public class Group extends AbstractExtension { public static final String KIND = "Group"; public static final String HIDDEN_LABEL = "halo.run/hidden"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private GroupSpec spec; private GroupStatus status; @@ -28,7 +29,7 @@ public class Group extends AbstractExtension { @Data public static class GroupSpec { - @Schema(required = true, description = "Display name of group") + @Schema(requiredMode = REQUIRED, description = "Display name of group") private String displayName; } diff --git a/api/src/main/java/run/halo/app/core/extension/content/Category.java b/api/src/main/java/run/halo/app/core/extension/content/Category.java index ba50b3ee1..bc5dfadf1 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Category.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Category.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static run.halo.app.core.extension.content.Category.KIND; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -28,7 +29,7 @@ public class Category extends AbstractExtension { public static final GroupVersionKind GVK = GroupVersionKind.fromExtension(Category.class); - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CategorySpec spec; @Schema @@ -42,10 +43,10 @@ public class Category extends AbstractExtension { @Data public static class CategorySpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String displayName; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String slug; private String description; @@ -54,7 +55,7 @@ public class Category extends AbstractExtension { private String template; - @Schema(required = true, defaultValue = "0") + @Schema(requiredMode = REQUIRED, defaultValue = "0") private Integer priority; private List children; diff --git a/api/src/main/java/run/halo/app/core/extension/content/Comment.java b/api/src/main/java/run/halo/app/core/extension/content/Comment.java index 5684826c7..2233a127a 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Comment.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Comment.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -30,7 +31,7 @@ public class Comment extends AbstractExtension { public static final String KIND = "Comment"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CommentSpec spec; @Schema @@ -49,7 +50,7 @@ public class Comment extends AbstractExtension { @EqualsAndHashCode(callSuper = true) public static class CommentSpec extends BaseCommentSpec { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Ref subjectRef; private Instant lastReadTime; @@ -58,13 +59,13 @@ public class Comment extends AbstractExtension { @Data public static class BaseCommentSpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String raw; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String content; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CommentOwner owner; private String userAgent; @@ -78,19 +79,19 @@ public class Comment extends AbstractExtension { */ private Instant creationTime; - @Schema(required = true, defaultValue = "0") + @Schema(requiredMode = REQUIRED, defaultValue = "0") private Integer priority; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean top; - @Schema(required = true, defaultValue = "true") + @Schema(requiredMode = REQUIRED, defaultValue = "true") private Boolean allowNotification; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean approved; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean hidden; } @@ -100,10 +101,10 @@ public class Comment extends AbstractExtension { public static final String AVATAR_ANNO = "avatar"; public static final String WEBSITE_ANNO = "website"; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String kind; - @Schema(required = true, maxLength = 64) + @Schema(requiredMode = REQUIRED, maxLength = 64) private String name; private String displayName; diff --git a/api/src/main/java/run/halo/app/core/extension/content/Reply.java b/api/src/main/java/run/halo/app/core/extension/content/Reply.java index 3b077812e..da6ff4022 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Reply.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Reply.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -21,14 +23,14 @@ public class Reply extends AbstractExtension { public static final String KIND = "Reply"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private ReplySpec spec; @Data @EqualsAndHashCode(callSuper = true) public static class ReplySpec extends Comment.BaseCommentSpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String commentName; private String quoteReply; diff --git a/api/src/main/java/run/halo/app/core/extension/content/SinglePage.java b/api/src/main/java/run/halo/app/core/extension/content/SinglePage.java index 413b241f7..37424825f 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/SinglePage.java +++ b/api/src/main/java/run/halo/app/core/extension/content/SinglePage.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import java.time.Instant; @@ -36,7 +38,7 @@ public class SinglePage extends AbstractExtension { public static final String OWNER_LABEL = "content.halo.run/owner"; public static final String VISIBLE_LABEL = "content.halo.run/visible"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private SinglePageSpec spec; @Schema @@ -58,10 +60,10 @@ public class SinglePage extends AbstractExtension { @Data public static class SinglePageSpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String title; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String slug; /** @@ -79,27 +81,27 @@ public class SinglePage extends AbstractExtension { private String cover; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean deleted; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean publish; private Instant publishTime; - @Schema(required = true, defaultValue = "false") + @Schema(requiredMode = REQUIRED, defaultValue = "false") private Boolean pinned; - @Schema(required = true, defaultValue = "true") + @Schema(requiredMode = REQUIRED, defaultValue = "true") private Boolean allowComment; - @Schema(required = true, defaultValue = "PUBLIC") + @Schema(requiredMode = REQUIRED, defaultValue = "PUBLIC") private Post.VisibleEnum visible; - @Schema(required = true, defaultValue = "0") + @Schema(requiredMode = REQUIRED, defaultValue = "0") private Integer priority; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Post.Excerpt excerpt; private List> htmlMetas; diff --git a/api/src/main/java/run/halo/app/core/extension/content/Snapshot.java b/api/src/main/java/run/halo/app/core/extension/content/Snapshot.java index 416392702..b9dae4240 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Snapshot.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Snapshot.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.time.Instant; import java.util.LinkedHashSet; @@ -26,19 +28,19 @@ public class Snapshot extends AbstractExtension { public static final String KIND = "Snapshot"; public static final String KEEP_RAW_ANNO = "content.halo.run/keep-raw"; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private SnapShotSpec spec; @Data public static class SnapShotSpec { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Ref subjectRef; /** * such as: markdown | html | json | asciidoc | latex. */ - @Schema(required = true, minLength = 1, maxLength = 50) + @Schema(requiredMode = REQUIRED, minLength = 1, maxLength = 50) private String rawType; private String rawPatch; @@ -49,7 +51,7 @@ public class Snapshot extends AbstractExtension { private Instant lastModifyTime; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String owner; private Set contributors; diff --git a/api/src/main/java/run/halo/app/core/extension/content/Tag.java b/api/src/main/java/run/halo/app/core/extension/content/Tag.java index 66b73e9dc..7cb047d0d 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Tag.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Tag.java @@ -1,5 +1,7 @@ package run.halo.app.core.extension.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -25,7 +27,7 @@ public class Tag extends AbstractExtension { public static final GroupVersionKind GVK = GroupVersionKind.fromExtension(Tag.class); - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private TagSpec spec; @Schema @@ -34,10 +36,10 @@ public class Tag extends AbstractExtension { @Data public static class TagSpec { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String displayName; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String slug; /** diff --git a/api/src/main/java/run/halo/app/extension/ExtensionOperator.java b/api/src/main/java/run/halo/app/extension/ExtensionOperator.java index d54e780d6..293426fb2 100644 --- a/api/src/main/java/run/halo/app/extension/ExtensionOperator.java +++ b/api/src/main/java/run/halo/app/extension/ExtensionOperator.java @@ -1,5 +1,7 @@ package run.halo.app.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -13,7 +15,7 @@ import org.springframework.util.StringUtils; */ public interface ExtensionOperator { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) @JsonProperty("apiVersion") default String getApiVersion() { final var gvk = getClass().getAnnotation(GVK.class); @@ -27,7 +29,7 @@ public interface ExtensionOperator { return gvk.version(); } - @Schema(required = true) + @Schema(requiredMode = REQUIRED) @JsonProperty("kind") default String getKind() { final var gvk = getClass().getAnnotation(GVK.class); @@ -38,7 +40,7 @@ public interface ExtensionOperator { return gvk.kind(); } - @Schema(required = true, implementation = Metadata.class) + @Schema(requiredMode = REQUIRED, implementation = Metadata.class) @JsonProperty("metadata") MetadataOperator getMetadata(); @@ -48,30 +50,6 @@ public interface ExtensionOperator { void setMetadata(MetadataOperator metadata); - /** - * This method is only for backward compatibility. Same as {@link #getMetadata()}. - * - * @return Extension metadata. - * @see #getMetadata() - */ - @JsonIgnore - @Deprecated(forRemoval = true) - default MetadataOperator metadata() { - return getMetadata(); - } - - /** - * This method is only for backward compatibility. Same as - * {@link #setMetadata(MetadataOperator)}. - * - * @param metadata is Extension metadata. - * @see #setMetadata(MetadataOperator) - */ - @Deprecated(forRemoval = true) - default void metadata(MetadataOperator metadata) { - setMetadata(metadata); - } - /** * Sets GroupVersionKind of the Extension. * diff --git a/api/src/main/java/run/halo/app/extension/ListResult.java b/api/src/main/java/run/halo/app/extension/ListResult.java index 6f7a3e290..3a487959f 100644 --- a/api/src/main/java/run/halo/app/extension/ListResult.java +++ b/api/src/main/java/run/halo/app/extension/ListResult.java @@ -1,5 +1,7 @@ package run.halo.app.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import java.util.ArrayList; @@ -16,17 +18,17 @@ import run.halo.app.infra.utils.GenericClassUtils; public class ListResult implements Iterable, Supplier> { @Schema(description = "Page number, starts from 1. If not set or equal to 0, it means no " - + "pagination.", required = true) + + "pagination.", requiredMode = REQUIRED) private final int page; @Schema(description = "Size of each page. If not set or equal to 0, it means no pagination.", - required = true) + requiredMode = REQUIRED) private final int size; - @Schema(description = "Total elements.", required = true) + @Schema(description = "Total elements.", requiredMode = REQUIRED) private final long total; - @Schema(description = "A chunk of items.", required = true) + @Schema(description = "A chunk of items.", requiredMode = REQUIRED) private final List items; public ListResult(int page, int size, long total, List items) { @@ -50,17 +52,20 @@ public class ListResult implements Iterable, Supplier> { this(0, 0, items.size(), items); } - @Schema(description = "Indicates whether current page is the first page.", required = true) + @Schema(description = "Indicates whether current page is the first page.", + requiredMode = REQUIRED) public boolean isFirst() { return !hasPrevious(); } - @Schema(description = "Indicates whether current page is the last page.", required = true) + @Schema(description = "Indicates whether current page is the last page.", + requiredMode = REQUIRED) public boolean isLast() { return !hasNext(); } - @Schema(description = "Indicates whether current page has previous page.", required = true) + @Schema(description = "Indicates whether current page has previous page.", + requiredMode = REQUIRED) @JsonProperty("hasNext") public boolean hasNext() { if (page <= 0) { @@ -69,7 +74,8 @@ public class ListResult implements Iterable, Supplier> { return page < getTotalPages(); } - @Schema(description = "Indicates whether current page has previous page.", required = true) + @Schema(description = "Indicates whether current page has previous page.", + requiredMode = REQUIRED) @JsonProperty("hasPrevious") public boolean hasPrevious() { return page > 1; @@ -80,7 +86,7 @@ public class ListResult implements Iterable, Supplier> { return items.iterator(); } - @Schema(description = "Indicates total pages.", required = true) + @Schema(description = "Indicates total pages.", requiredMode = REQUIRED) @JsonProperty("totalPages") public long getTotalPages() { return size == 0 ? 1 : (total + size - 1) / size; diff --git a/api/src/main/java/run/halo/app/extension/MetadataOperator.java b/api/src/main/java/run/halo/app/extension/MetadataOperator.java index 7f83a8b5a..13486f797 100644 --- a/api/src/main/java/run/halo/app/extension/MetadataOperator.java +++ b/api/src/main/java/run/halo/app/extension/MetadataOperator.java @@ -1,5 +1,7 @@ package run.halo.app.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.swagger.v3.oas.annotations.media.Schema; @@ -17,7 +19,7 @@ import java.util.Set; @Schema(implementation = Metadata.class) public interface MetadataOperator { - @Schema(name = "name", description = "Metadata name", required = true) + @Schema(name = "name", description = "Metadata name", requiredMode = REQUIRED) @JsonProperty("name") String getName(); diff --git a/api/src/main/java/run/halo/app/extension/Ref.java b/api/src/main/java/run/halo/app/extension/Ref.java index 54a789efd..1c464bf30 100644 --- a/api/src/main/java/run/halo/app/extension/Ref.java +++ b/api/src/main/java/run/halo/app/extension/Ref.java @@ -1,5 +1,7 @@ package run.halo.app.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.Objects; import lombok.Data; @@ -17,7 +19,7 @@ public class Ref { @Schema(description = "Extension kind") private String kind; - @Schema(required = true, description = "Extension name. This field is mandatory") + @Schema(requiredMode = REQUIRED, description = "Extension name. This field is mandatory") private String name; public static Ref of(String name) { diff --git a/api/src/main/java/run/halo/app/infra/Condition.java b/api/src/main/java/run/halo/app/infra/Condition.java index 2d834cf13..f8fd12a84 100644 --- a/api/src/main/java/run/halo/app/infra/Condition.java +++ b/api/src/main/java/run/halo/app/infra/Condition.java @@ -1,5 +1,7 @@ package run.halo.app.infra; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.time.Instant; import lombok.AllArgsConstructor; @@ -28,7 +30,7 @@ public class Condition { * example: Ready, Initialized. * maxLength: 316. */ - @Schema(required = true, maxLength = 316, + @Schema(requiredMode = REQUIRED, maxLength = 316, pattern = "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(" + "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$") private String type; @@ -36,26 +38,26 @@ public class Condition { /** * Status is the status of the condition. Can be True, False, Unknown. */ - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private ConditionStatus status; /** * Last time the condition transitioned from one status to another. */ - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Instant lastTransitionTime; /** * Human-readable message indicating details about last transition. * This may be an empty string. */ - @Schema(required = true, maxLength = 32768) + @Schema(requiredMode = REQUIRED, maxLength = 32768) private String message; /** * Unique, one-word, CamelCase reason for the condition's last transition. */ - @Schema(required = true, maxLength = 1024, + @Schema(requiredMode = REQUIRED, maxLength = 1024, pattern = "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$") private String reason; } diff --git a/api/src/main/java/run/halo/app/search/SearchParam.java b/api/src/main/java/run/halo/app/search/SearchParam.java index c7ba3640a..e17c448df 100644 --- a/api/src/main/java/run/halo/app/search/SearchParam.java +++ b/api/src/main/java/run/halo/app/search/SearchParam.java @@ -1,5 +1,7 @@ package run.halo.app.search; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -17,7 +19,7 @@ public class SearchParam { this.query = query; } - @Schema(name = "keyword", required = true) + @Schema(name = "keyword", requiredMode = REQUIRED) public String getKeyword() { var keyword = query.getFirst("keyword"); if (!StringUtils.hasText(keyword)) { diff --git a/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java b/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java index 83a858929..ab614192a 100644 --- a/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java +++ b/application/src/main/java/run/halo/app/config/WebServerSecurityConfig.java @@ -61,8 +61,9 @@ public class WebServerSecurityConfig { http.securityMatcher(pathMatchers("/api/**", "/apis/**", "/oauth2/**", "/login/**", "/logout", "/actuator/**")) - .authorizeExchange().anyExchange() - .access(new RequestInfoAuthorizationManager(roleService)).and() + .authorizeExchange(spec -> { + spec.anyExchange().access(new RequestInfoAuthorizationManager(roleService)); + }) .anonymous(spec -> { spec.authorities(AnonymousUserConst.Role); spec.principal(AnonymousUserConst.PRINCIPAL); @@ -85,19 +86,24 @@ public class WebServerSecurityConfig { var mediaTypeMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML); mediaTypeMatcher.setIgnoredMediaTypes(Set.of(MediaType.ALL)); http.securityMatcher(new AndServerWebExchangeMatcher(pathMatcher, mediaTypeMatcher)) - .authorizeExchange().anyExchange().permitAll().and() .securityContextRepository(securityContextRepository) - .headers() - .frameOptions(spec -> { - var frameOptions = haloProperties.getSecurity().getFrameOptions(); - spec.mode(frameOptions.getMode()); - if (frameOptions.isDisabled()) { - spec.disable(); - } + .authorizeExchange(spec -> { + spec.anyExchange().permitAll(); }) - .referrerPolicy( - spec -> spec.policy(haloProperties.getSecurity().getReferrerOptions().getPolicy())) - .cache().disable().and() + .headers(headerSpec -> headerSpec + .frameOptions(frameSpec -> { + var frameOptions = haloProperties.getSecurity().getFrameOptions(); + frameSpec.mode(frameOptions.getMode()); + if (frameOptions.isDisabled()) { + frameSpec.disable(); + } + }) + .referrerPolicy(referrerPolicySpec -> { + referrerPolicySpec.policy( + haloProperties.getSecurity().getReferrerOptions().getPolicy()); + }) + .cache(ServerHttpSecurity.HeaderSpec.CacheSpec::disable) + ) .anonymous(spec -> spec.authenticationFilter( new HaloAnonymousAuthenticationWebFilter("portal", AnonymousUserConst.PRINCIPAL, AuthorityUtils.createAuthorityList(AnonymousUserConst.Role), diff --git a/application/src/main/java/run/halo/app/content/ContentRequest.java b/application/src/main/java/run/halo/app/content/ContentRequest.java index 388cc0039..041815324 100644 --- a/application/src/main/java/run/halo/app/content/ContentRequest.java +++ b/application/src/main/java/run/halo/app/content/ContentRequest.java @@ -1,5 +1,7 @@ package run.halo.app.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.HashMap; import org.apache.commons.lang3.StringUtils; @@ -11,11 +13,11 @@ import run.halo.app.extension.Ref; * @author guqing * @since 2.0.0 */ -public record ContentRequest(@Schema(required = true) Ref subjectRef, +public record ContentRequest(@Schema(requiredMode = REQUIRED) Ref subjectRef, String headSnapshotName, - @Schema(required = true) String raw, - @Schema(required = true) String content, - @Schema(required = true) String rawType) { + @Schema(requiredMode = REQUIRED) String raw, + @Schema(requiredMode = REQUIRED) String content, + @Schema(requiredMode = REQUIRED) String rawType) { public Snapshot toSnapshot() { final Snapshot snapshot = new Snapshot(); diff --git a/application/src/main/java/run/halo/app/content/ListedPost.java b/application/src/main/java/run/halo/app/content/ListedPost.java index 5e790f13f..b037d8430 100644 --- a/application/src/main/java/run/halo/app/content/ListedPost.java +++ b/application/src/main/java/run/halo/app/content/ListedPost.java @@ -1,5 +1,7 @@ package run.halo.app.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import lombok.Data; @@ -17,21 +19,21 @@ import run.halo.app.core.extension.content.Tag; @Data public class ListedPost { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Post post; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private List categories; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private List tags; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private List contributors; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Contributor owner; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Stats stats; } diff --git a/application/src/main/java/run/halo/app/content/ListedSinglePage.java b/application/src/main/java/run/halo/app/content/ListedSinglePage.java index 120600f85..b406ce350 100644 --- a/application/src/main/java/run/halo/app/content/ListedSinglePage.java +++ b/application/src/main/java/run/halo/app/content/ListedSinglePage.java @@ -1,5 +1,7 @@ package run.halo.app.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import lombok.Data; @@ -15,15 +17,15 @@ import run.halo.app.core.extension.content.SinglePage; @Data public class ListedSinglePage { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private SinglePage page; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private List contributors; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Contributor owner; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Stats stats; } diff --git a/application/src/main/java/run/halo/app/content/PostRequest.java b/application/src/main/java/run/halo/app/content/PostRequest.java index 2c739b1e5..3f206239c 100644 --- a/application/src/main/java/run/halo/app/content/PostRequest.java +++ b/application/src/main/java/run/halo/app/content/PostRequest.java @@ -1,5 +1,7 @@ package run.halo.app.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import run.halo.app.core.extension.content.Post; import run.halo.app.extension.Ref; @@ -8,8 +10,8 @@ import run.halo.app.extension.Ref; * @author guqing * @since 2.0.0 */ -public record PostRequest(@Schema(required = true) Post post, - @Schema(required = true) Content content) { +public record PostRequest(@Schema(requiredMode = REQUIRED) Post post, + @Schema(requiredMode = REQUIRED) Content content) { public ContentRequest contentRequest() { Ref subjectRef = Ref.of(post); diff --git a/application/src/main/java/run/halo/app/content/SinglePageRequest.java b/application/src/main/java/run/halo/app/content/SinglePageRequest.java index 6c1130eb5..8fda8fc18 100644 --- a/application/src/main/java/run/halo/app/content/SinglePageRequest.java +++ b/application/src/main/java/run/halo/app/content/SinglePageRequest.java @@ -1,5 +1,7 @@ package run.halo.app.content; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import run.halo.app.core.extension.content.SinglePage; import run.halo.app.extension.Ref; @@ -10,8 +12,8 @@ import run.halo.app.extension.Ref; * @author guqing * @since 2.0.0 */ -public record SinglePageRequest(@Schema(required = true) SinglePage page, - @Schema(required = true) Content content) { +public record SinglePageRequest(@Schema(requiredMode = REQUIRED) SinglePage page, + @Schema(requiredMode = REQUIRED) Content content) { public ContentRequest contentRequest() { Ref subjectRef = Ref.of(page); diff --git a/application/src/main/java/run/halo/app/content/comment/CommentRequest.java b/application/src/main/java/run/halo/app/content/comment/CommentRequest.java index da25d2ef5..599cfca6d 100644 --- a/application/src/main/java/run/halo/app/content/comment/CommentRequest.java +++ b/application/src/main/java/run/halo/app/content/comment/CommentRequest.java @@ -1,5 +1,7 @@ package run.halo.app.content.comment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.UUID; import lombok.Data; @@ -16,15 +18,15 @@ import run.halo.app.extension.Ref; @Data public class CommentRequest { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Ref subjectRef; private CommentEmailOwner owner; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String raw; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String content; @Schema(defaultValue = "false") diff --git a/application/src/main/java/run/halo/app/content/comment/ListedComment.java b/application/src/main/java/run/halo/app/content/comment/ListedComment.java index d235dc3ab..eb5594d6d 100644 --- a/application/src/main/java/run/halo/app/content/comment/ListedComment.java +++ b/application/src/main/java/run/halo/app/content/comment/ListedComment.java @@ -1,5 +1,7 @@ package run.halo.app.content.comment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Data; @@ -16,14 +18,14 @@ import run.halo.app.extension.Extension; @Builder public class ListedComment { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Comment comment; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private OwnerInfo owner; private Extension subject; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CommentStats stats; } diff --git a/application/src/main/java/run/halo/app/content/comment/ListedReply.java b/application/src/main/java/run/halo/app/content/comment/ListedReply.java index 678aa98b7..01050c3c9 100644 --- a/application/src/main/java/run/halo/app/content/comment/ListedReply.java +++ b/application/src/main/java/run/halo/app/content/comment/ListedReply.java @@ -1,5 +1,7 @@ package run.halo.app.content.comment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Data; @@ -15,12 +17,12 @@ import run.halo.app.core.extension.content.Reply; @Builder public class ListedReply { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Reply reply; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private OwnerInfo owner; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CommentStats stats; } diff --git a/application/src/main/java/run/halo/app/content/comment/ReplyRequest.java b/application/src/main/java/run/halo/app/content/comment/ReplyRequest.java index 7b336fdc6..d7ad2c327 100644 --- a/application/src/main/java/run/halo/app/content/comment/ReplyRequest.java +++ b/application/src/main/java/run/halo/app/content/comment/ReplyRequest.java @@ -1,5 +1,7 @@ package run.halo.app.content.comment; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import java.util.UUID; import lombok.Data; @@ -15,10 +17,10 @@ import run.halo.app.extension.Metadata; @Data public class ReplyRequest { - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String raw; - @Schema(required = true, minLength = 1) + @Schema(requiredMode = REQUIRED, minLength = 1) private String content; @Schema(defaultValue = "false") diff --git a/application/src/main/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpoint.java b/application/src/main/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpoint.java index af0c4855b..4274507f0 100644 --- a/application/src/main/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpoint.java +++ b/application/src/main/java/run/halo/app/core/extension/attachment/endpoint/AttachmentEndpoint.java @@ -1,5 +1,6 @@ package run.halo.app.core.extension.attachment.endpoint; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import static java.util.Comparator.comparing; import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; @@ -274,10 +275,10 @@ public class AttachmentEndpoint implements CustomEndpoint { public interface IUploadRequest { - @Schema(required = true, description = "Attachment file") + @Schema(requiredMode = REQUIRED, description = "Attachment file") FilePart getFile(); - @Schema(required = true, description = "Storage policy name") + @Schema(requiredMode = REQUIRED, description = "Storage policy name") String getPolicyName(); @Schema(description = "The name of the group to which the attachment belongs") diff --git a/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java b/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java index 736bdb594..b7243eb7a 100644 --- a/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java +++ b/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java @@ -429,7 +429,7 @@ public class UserEndpoint implements CustomEndpoint { } record ChangePasswordRequest( - @Schema(description = "New password.", required = true, minLength = 6) + @Schema(description = "New password.", requiredMode = REQUIRED, minLength = 6) String password) { } @@ -522,8 +522,8 @@ public class UserEndpoint implements CustomEndpoint { .flatMapIterable(Function.identity()); } - record UserPermission(@Schema(required = true) Set roles, - @Schema(required = true) Set uiPermissions) { + record UserPermission(@Schema(requiredMode = REQUIRED) Set roles, + @Schema(requiredMode = REQUIRED) Set uiPermissions) { } public class ListRequest extends IListRequest.QueryListRequest { diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java index 038e8c237..0899e0a5e 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/ThemeReconciler.java @@ -3,7 +3,6 @@ package run.halo.app.core.extension.reconciler; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import java.io.IOException; -import java.nio.file.Path; import java.time.Instant; import java.util.HashSet; import java.util.List; @@ -28,11 +27,10 @@ import run.halo.app.extension.controller.Reconciler.Request; import run.halo.app.infra.Condition; import run.halo.app.infra.ConditionStatus; import run.halo.app.infra.SystemVersionSupplier; +import run.halo.app.infra.ThemeRootGetter; import run.halo.app.infra.exception.ThemeUninstallException; -import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.JsonUtils; import run.halo.app.infra.utils.VersionUtils; -import run.halo.app.theme.ThemePathPolicy; /** * Reconciler for theme. @@ -45,7 +43,8 @@ public class ThemeReconciler implements Reconciler { private static final String FINALIZER_NAME = "theme-protection"; private final ExtensionClient client; - private final ThemePathPolicy themePathPolicy; + + private final ThemeRootGetter themeRoot; private final SystemVersionSupplier systemVersionSupplier; private final RetryTemplate retryTemplate = RetryTemplate.builder() @@ -54,10 +53,10 @@ public class ThemeReconciler implements Reconciler { .retryOn(IllegalStateException.class) .build(); - public ThemeReconciler(ExtensionClient client, HaloProperties haloProperties, + public ThemeReconciler(ExtensionClient client, ThemeRootGetter themeRoot, SystemVersionSupplier systemVersionSupplier) { this.client = client; - themePathPolicy = new ThemePathPolicy(haloProperties.getWorkDir()); + this.themeRoot = themeRoot; this.systemVersionSupplier = systemVersionSupplier; } @@ -90,7 +89,7 @@ public class ThemeReconciler implements Reconciler { final Theme.ThemeStatus oldStatus = JsonUtils.deepCopy(status); theme.setStatus(status); - Path themePath = themePathPolicy.generate(theme); + var themePath = themeRoot.get().resolve(name); status.setLocation(themePath.toAbsolutePath().toString()); status.setPhase(Theme.ThemePhase.READY); @@ -216,7 +215,7 @@ public class ThemeReconciler implements Reconciler { } private void deleteThemeFiles(Theme theme) { - Path themeDir = themePathPolicy.generate(theme); + var themeDir = themeRoot.get().resolve(theme.getMetadata().getName()); try { FileSystemUtils.deleteRecursively(themeDir); } catch (IOException e) { diff --git a/application/src/main/java/run/halo/app/core/extension/service/DefaultRoleService.java b/application/src/main/java/run/halo/app/core/extension/service/DefaultRoleService.java index 528130a8c..12460b1bb 100644 --- a/application/src/main/java/run/halo/app/core/extension/service/DefaultRoleService.java +++ b/application/src/main/java/run/halo/app/core/extension/service/DefaultRoleService.java @@ -3,14 +3,10 @@ package run.halo.app.core.extension.service; import static run.halo.app.extension.MetadataUtil.nullSafeLabels; import com.fasterxml.jackson.core.type.TypeReference; -import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.Deque; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Predicate; import lombok.extern.slf4j.Slf4j; @@ -19,7 +15,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.RoleBinding; import run.halo.app.core.extension.RoleBinding.RoleRef; @@ -42,17 +37,6 @@ public class DefaultRoleService implements RoleService { this.extensionClient = extensionClient; } - @Override - @NonNull - public Role getRole(@NonNull String name) { - return extensionClient.fetch(Role.class, name).blockOptional().orElseThrow(); - } - - @Override - public Mono getMonoRole(String name) { - return extensionClient.get(Role.class, name); - } - @Override public Flux listRoleRefs(Subject subject) { return extensionClient.list(RoleBinding.class, @@ -61,39 +45,6 @@ public class DefaultRoleService implements RoleService { .map(RoleBinding::getRoleRef); } - @Override - @NonNull - public List listDependencies(Set names) { - List result = new ArrayList<>(); - if (names == null) { - return result; - } - Set visited = new HashSet<>(); - Deque queue = new ArrayDeque<>(names); - while (!queue.isEmpty()) { - String roleName = queue.poll(); - // detecting cycle in role dependencies - if (visited.contains(roleName)) { - log.warn("Detected a cycle in role dependencies: {},and skipped automatically", - roleName); - continue; - } - visited.add(roleName); - extensionClient.fetch(Role.class, roleName) - .blockOptional() - .ifPresent(role -> { - result.add(role); - Map annotations = role.getMetadata().getAnnotations(); - if (annotations != null) { - String roleNameDependencies = annotations.get(Role.ROLE_DEPENDENCIES_ANNO); - List roleDependencies = stringToList(roleNameDependencies); - queue.addAll(roleDependencies); - } - }); - } - return result; - } - @Override public Flux listDependenciesFlux(Set names) { if (names == null) { diff --git a/application/src/main/java/run/halo/app/core/extension/service/RoleService.java b/application/src/main/java/run/halo/app/core/extension/service/RoleService.java index bfe76d9ca..54f677389 100644 --- a/application/src/main/java/run/halo/app/core/extension/service/RoleService.java +++ b/application/src/main/java/run/halo/app/core/extension/service/RoleService.java @@ -1,10 +1,7 @@ package run.halo.app.core.extension.service; -import java.util.List; import java.util.Set; -import org.springframework.lang.NonNull; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.RoleBinding.RoleRef; import run.halo.app.core.extension.RoleBinding.Subject; @@ -15,16 +12,8 @@ import run.halo.app.core.extension.RoleBinding.Subject; */ public interface RoleService { - @NonNull - @Deprecated - Role getRole(String name); - - Mono getMonoRole(String name); - Flux listRoleRefs(Subject subject); - List listDependencies(Set names); - Flux listDependenciesFlux(Set names); Flux list(Set roleNames); diff --git a/application/src/main/java/run/halo/app/core/extension/theme/ThemeEndpoint.java b/application/src/main/java/run/halo/app/core/extension/theme/ThemeEndpoint.java index b42d9ec51..0ce93cb14 100644 --- a/application/src/main/java/run/halo/app/core/extension/theme/ThemeEndpoint.java +++ b/application/src/main/java/run/halo/app/core/extension/theme/ThemeEndpoint.java @@ -410,7 +410,7 @@ public class ThemeEndpoint implements CustomEndpoint { public interface IUpgradeRequest { - @Schema(required = true, description = "Theme zip file.") + @Schema(requiredMode = REQUIRED, description = "Theme zip file.") FilePart getFile(); } @@ -501,7 +501,7 @@ public class ThemeEndpoint implements CustomEndpoint { @Schema(name = "ThemeInstallRequest") public record InstallRequest( - @Schema(required = true, description = "Theme zip file.") FilePart file) { + @Schema(requiredMode = REQUIRED, description = "Theme zip file.") FilePart file) { } public record InstallFromUriRequest(@Schema(requiredMode = REQUIRED) URI uri) { diff --git a/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java b/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java index ad1a16497..a49535680 100644 --- a/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java +++ b/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java @@ -102,6 +102,7 @@ public class ReactiveExtensionClientImpl implements ReactiveExtensionClient { } @Override + @SuppressWarnings("unchecked") public Mono create(E extension) { return Mono.just(extension) .doOnNext(ext -> { @@ -133,6 +134,7 @@ public class ReactiveExtensionClientImpl implements ReactiveExtensionClient { } @Override + @SuppressWarnings("unchecked") public Mono update(E extension) { // Refactor the atomic reference if we have a better solution. final var statusChangeOnly = new AtomicBoolean(false); @@ -181,6 +183,7 @@ public class ReactiveExtensionClientImpl implements ReactiveExtensionClient { } @Override + @SuppressWarnings("unchecked") public Mono delete(E extension) { // set deletionTimestamp extension.getMetadata().setDeletionTimestamp(Instant.now()); diff --git a/application/src/main/java/run/halo/app/metrics/VisitLogWriter.java b/application/src/main/java/run/halo/app/metrics/VisitLogWriter.java deleted file mode 100644 index d7bde76b7..000000000 --- a/application/src/main/java/run/halo/app/metrics/VisitLogWriter.java +++ /dev/null @@ -1,169 +0,0 @@ -package run.halo.app.metrics; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.time.Instant; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.atomic.AtomicInteger; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import reactor.core.Disposable; -import run.halo.app.infra.properties.HaloProperties; -import run.halo.app.infra.utils.FileUtils; - -/** - * Visit log writer. - * - * @author guqing - * @since 2.0.0 - */ -@Slf4j -@Deprecated -public class VisitLogWriter implements InitializingBean, DisposableBean { - private static final String LOG_FILE_NAME = "visits.log"; - private static final String LOG_FILE_LOCATION = "logs"; - private final AsyncLogWriter asyncLogWriter; - private volatile boolean interruptThread = false; - private volatile boolean started = false; - private final ExecutorService executorService; - - private final Path logFilePath; - - public VisitLogWriter(HaloProperties haloProperties) throws IOException { - Path logsPath = haloProperties.getWorkDir() - .resolve(LOG_FILE_LOCATION); - if (!Files.exists(logsPath)) { - Files.createDirectories(logsPath); - } - this.logFilePath = logsPath.resolve(LOG_FILE_NAME); - this.asyncLogWriter = new AsyncLogWriter(logFilePath); - this.executorService = Executors.newFixedThreadPool(1); - } - - public void log(String logMsg) { - asyncLogWriter.put(logMsg); - } - - public Path getLogFilePath() { - return logFilePath; - } - - void start() { - if (started) { - return; - } - log.debug("Starting write visit log..."); - this.started = true; - executorService.submit(() -> { - while (!interruptThread && !Thread.currentThread().isInterrupted()) { - try { - asyncLogWriter.writeLog(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.warn("VisitLogWrite thread [{}] interrupted", - Thread.currentThread().getName()); - } - } - }); - } - - @Override - public void afterPropertiesSet() throws Exception { - start(); - } - - public boolean isStarted() { - return started; - } - - public long queuedSize() { - return asyncLogWriter.logQueue.size(); - } - - @Override - public void destroy() throws Exception { - this.started = false; - interruptThread = true; - asyncLogWriter.dispose(); - executorService.shutdownNow(); - } - - static class AsyncLogWriter implements Disposable { - private static final int MAX_LOG_SIZE = 10000; - private static final int BATCH_SIZE = 10; - private final BufferedOutputStream writer; - private final BlockingQueue logQueue; - private final AtomicInteger logBatch = new AtomicInteger(0); - private volatile boolean disposed = false; - - public AsyncLogWriter(Path logFilePath) { - OutputStream outputStream; - try { - outputStream = Files.newOutputStream(logFilePath, - StandardOpenOption.CREATE, - StandardOpenOption.APPEND); - } catch (IOException e) { - throw new RuntimeException(e); - } - this.writer = new BufferedOutputStream(outputStream); - this.logQueue = new LinkedBlockingDeque<>(MAX_LOG_SIZE); - } - - public void writeLog() throws InterruptedException { - String logMessage = logQueue.take(); - if (StringUtils.isBlank(logMessage)) { - return; - } - writeToDisk(logMessage); - log.debug("Consumption visit log message: [{}]", logMessage); - } - - void writeToDisk(String logMsg) { - String format = String.format("%s %s\n", Instant.now(), logMsg); - try { - writer.write(format.getBytes(), 0, format.length()); - int size = logBatch.incrementAndGet(); - if (size >= BATCH_SIZE) { - writer.flush(); - logBatch.set(0); - } - } catch (IOException e) { - log.warn("Record access log failure: ", ExceptionUtils.getRootCause(e)); - } - } - - public void put(String logMessage) { - // add log message to queue tail - logQueue.add(logMessage); - log.debug("Production a log messages [{}]", logMessage); - } - - @Override - public void dispose() { - this.disposed = true; - if (writer != null) { - try { - writer.flush(); - } catch (IOException e) { - // ignore this - } - FileUtils.closeQuietly(writer); - } - } - - @Override - public boolean isDisposed() { - return this.disposed; - } - } -} diff --git a/application/src/main/java/run/halo/app/search/extension/SearchEngine.java b/application/src/main/java/run/halo/app/search/extension/SearchEngine.java index 0834b0940..11b1d8082 100644 --- a/application/src/main/java/run/halo/app/search/extension/SearchEngine.java +++ b/application/src/main/java/run/halo/app/search/extension/SearchEngine.java @@ -1,5 +1,7 @@ package run.halo.app.search.extension; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +17,7 @@ import run.halo.app.extension.Ref; plural = "searchengines", singular = "searchengine") public class SearchEngine extends AbstractExtension { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private SearchEngineSpec spec; @Data @@ -25,7 +27,7 @@ public class SearchEngine extends AbstractExtension { private String website; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private String displayName; private String description; diff --git a/application/src/main/java/run/halo/app/security/CsrfConfigurer.java b/application/src/main/java/run/halo/app/security/CsrfConfigurer.java index 6161556ff..0dc3b25e3 100644 --- a/application/src/main/java/run/halo/app/security/CsrfConfigurer.java +++ b/application/src/main/java/run/halo/app/security/CsrfConfigurer.java @@ -1,9 +1,9 @@ package run.halo.app.security; +import static org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository.withHttpOnlyFalse; import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers; import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; import org.springframework.security.web.server.csrf.CsrfWebFilter; import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler; import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher; @@ -20,12 +20,12 @@ public class CsrfConfigurer implements SecurityConfigurer { CsrfWebFilter.DEFAULT_CSRF_MATCHER, new NegatedServerWebExchangeMatcher(pathMatchers("/api/**", "/apis/**") )); - - http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()) + http.csrf(csrfSpec -> csrfSpec + .csrfTokenRepository(withHttpOnlyFalse()) // TODO Use XorServerCsrfTokenRequestAttributeHandler instead when console implements // the algorithm .csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler()) - .requireCsrfProtectionMatcher(csrfMatcher); + .requireCsrfProtectionMatcher(csrfMatcher)); } } diff --git a/application/src/main/java/run/halo/app/security/authorization/AuthorizationRuleResolver.java b/application/src/main/java/run/halo/app/security/authorization/AuthorizationRuleResolver.java index e7297e2c0..57ebf3442 100644 --- a/application/src/main/java/run/halo/app/security/authorization/AuthorizationRuleResolver.java +++ b/application/src/main/java/run/halo/app/security/authorization/AuthorizationRuleResolver.java @@ -9,28 +9,5 @@ import reactor.core.publisher.Mono; */ public interface AuthorizationRuleResolver { - /** - * rulesFor returns the list of rules that apply to a given user. - * If an error is returned, the slice of PolicyRules may not be complete, - * but it contains all retrievable rules. - * This is done because policy rules are purely additive and policy determinations - * can be made on the basis of those rules that are found. - * - * @param user authenticated user info - */ - @Deprecated(forRemoval = true, since = "2.0.0") - PolicyRuleList rulesFor(UserDetails user); - - /** - * visitRulesFor invokes visitor() with each rule that applies to a given user - * and each error encountered resolving those rules. Rule may be null if err is non-nil. - * If visitor() returns false, visiting is short-circuited. - * - * @param user user info - * @param visitor visitor - */ - @Deprecated(forRemoval = true, since = "2.0.0") - void visitRulesFor(UserDetails user, RuleAccumulator visitor); - Mono visitRules(UserDetails user, RequestInfo requestInfo); } diff --git a/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java b/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java index 31540eda2..cbd55a39f 100644 --- a/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java +++ b/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java @@ -1,27 +1,17 @@ package run.halo.app.security.authorization; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Mono; -import run.halo.app.core.extension.Role; import run.halo.app.core.extension.service.DefaultRoleBindingService; import run.halo.app.core.extension.service.RoleBindingService; import run.halo.app.core.extension.service.RoleService; -import run.halo.app.extension.MetadataOperator; import run.halo.app.infra.AnonymousUserConst; -import run.halo.app.infra.utils.JsonUtils; /** * @author guqing @@ -33,57 +23,11 @@ public class DefaultRuleResolver implements AuthorizationRuleResolver { private static final String AUTHENTICATED_ROLE = "authenticated"; private RoleService roleService; - private RoleBindingService roleBindingService = new DefaultRoleBindingService(); + private RoleBindingService roleBindingService; public DefaultRuleResolver(RoleService roleService) { this.roleService = roleService; - } - - @Override - public PolicyRuleList rulesFor(UserDetails user) { - PolicyRuleList policyRules = new PolicyRuleList(); - visitRulesFor(user, (source, rule, err) -> { - if (rule != null) { - policyRules.add(rule); - } - if (err != null) { - policyRules.addError(err); - } - return true; - }); - return policyRules; - } - - @Override - public void visitRulesFor(UserDetails user, RuleAccumulator visitor) { - Set roleNamesImmutable = - roleBindingService.listBoundRoleNames(user.getAuthorities()); - Set roleNames = new HashSet<>(roleNamesImmutable); - if (!AnonymousUserConst.PRINCIPAL.equals(user.getUsername())) { - roleNames.add(AUTHENTICATED_ROLE); - roleNames.add(AnonymousUserConst.Role); - } - - List rules = Collections.emptyList(); - for (String roleName : roleNames) { - try { - Role role = roleService.getRole(roleName); - // fetch rules from role - rules = fetchRules(role); - } catch (Exception e) { - if (visitor.visit(null, null, e)) { - // if visitor returns true, we continue visiting - continue; - } - } - - String source = roleBindingDescriber(roleName, user.getUsername()); - for (Role.PolicyRule rule : rules) { - if (!visitor.visit(source, rule, null)) { - return; - } - } - } + this.roleBindingService = new DefaultRoleBindingService(); } @Override @@ -123,32 +67,6 @@ public class DefaultRuleResolver implements AuthorizationRuleResolver { .then(Mono.just(visitor)); } - private List fetchRules(Role role) { - MetadataOperator metadata = role.getMetadata(); - if (metadata == null || metadata.getAnnotations() == null) { - return role.getRules(); - } - - // merge policy rules - String roleDependencyRules = metadata.getAnnotations() - .get(Role.ROLE_DEPENDENCY_RULES); - List rules = convertFrom(roleDependencyRules); - rules.addAll(role.getRules()); - return rules; - } - - private List convertFrom(String json) { - if (StringUtils.isBlank(json)) { - return new ArrayList<>(); - } - try { - return JsonUtils.DEFAULT_JSON_MAPPER.readValue(json, new TypeReference<>() { - }); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - String roleBindingDescriber(String roleName, String subject) { return String.format("Binding role [%s] to [%s]", roleName, subject); } diff --git a/application/src/main/java/run/halo/app/theme/ThemePathPolicy.java b/application/src/main/java/run/halo/app/theme/ThemePathPolicy.java deleted file mode 100644 index c696ffdaa..000000000 --- a/application/src/main/java/run/halo/app/theme/ThemePathPolicy.java +++ /dev/null @@ -1,34 +0,0 @@ -package run.halo.app.theme; - -import java.nio.file.Path; -import org.springframework.util.Assert; -import run.halo.app.core.extension.Theme; - -/** - * Policy for generating theme directory path. - * - * @author guqing - * @since 2.0.0 - * @deprecated Use {@code run.halo.app.infra.ThemeRootGetter} - */ -@Deprecated(forRemoval = true) -public class ThemePathPolicy { - public static final String THEME_WORK_DIR = "themes"; - - private final Path workDir; - - public ThemePathPolicy(Path workDir) { - Assert.notNull(workDir, "The halo workDir must not be null."); - this.workDir = workDir; - } - - public Path generate(Theme theme) { - Assert.notNull(theme, "The theme must not be null."); - String name = theme.getMetadata().getName(); - return themesDir().resolve(name); - } - - public Path themesDir() { - return workDir.resolve(ThemePathPolicy.THEME_WORK_DIR); - } -} diff --git a/application/src/main/java/run/halo/app/theme/finders/vo/ReplyVo.java b/application/src/main/java/run/halo/app/theme/finders/vo/ReplyVo.java index a67640217..4f59d9d66 100644 --- a/application/src/main/java/run/halo/app/theme/finders/vo/ReplyVo.java +++ b/application/src/main/java/run/halo/app/theme/finders/vo/ReplyVo.java @@ -1,5 +1,7 @@ package run.halo.app.theme.finders.vo; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Data; @@ -21,16 +23,16 @@ import run.halo.app.extension.MetadataOperator; @EqualsAndHashCode public class ReplyVo implements ExtensionVoOperator { - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private MetadataOperator metadata; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private Reply.ReplySpec spec; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private OwnerInfo owner; - @Schema(required = true) + @Schema(requiredMode = REQUIRED) private CommentStatsVo stats; /** diff --git a/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java b/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java index e50fa118e..4b8e83302 100644 --- a/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java +++ b/application/src/test/java/run/halo/app/config/ExtensionConfigurationTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anySet; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; @@ -25,7 +24,6 @@ import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.service.RoleService; import run.halo.app.extension.ExtensionClient; @@ -60,7 +58,6 @@ class ExtensionConfigurationTest { role.setMetadata(new Metadata()); role.getMetadata().setName("supper-role"); role.setRules(List.of(rule)); - when(roleService.getMonoRole(anyString())).thenReturn(Mono.just(role)); when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.just(role)); // register scheme schemeManager.register(FakeExtension.class); diff --git a/application/src/test/java/run/halo/app/content/PostIntegrationTests.java b/application/src/test/java/run/halo/app/content/PostIntegrationTests.java index 982b669a9..629f3bbf9 100644 --- a/application/src/test/java/run/halo/app/content/PostIntegrationTests.java +++ b/application/src/test/java/run/halo/app/content/PostIntegrationTests.java @@ -17,13 +17,11 @@ import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.content.Post; import run.halo.app.core.extension.service.RoleService; import run.halo.app.extension.Metadata; import run.halo.app.extension.MetadataOperator; -import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.utils.JsonUtils; /** @@ -44,9 +42,6 @@ public class PostIntegrationTests { @MockBean RoleService roleService; - @Autowired - ReactiveExtensionClient client; - @BeforeEach void setUp() { var rule = new Role.PolicyRule.Builder() @@ -58,7 +53,6 @@ public class PostIntegrationTests { role.setMetadata(new Metadata()); role.getMetadata().setName("super-role"); role.setRules(List.of(rule)); - when(roleService.getMonoRole("authenticated")).thenReturn(Mono.just(role)); when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.just(role)); webTestClient = webTestClient.mutateWith(csrf()); } diff --git a/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java index 6cbcb718c..58a400c36 100644 --- a/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java +++ b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java @@ -81,17 +81,8 @@ class UserEndpointTest { @BeforeEach void setUp() { // disable authorization - var rule = new Role.PolicyRule.Builder() - .apiGroups("*") - .resources("*") - .verbs("*") - .build(); - var role = new Role(); - role.setRules(List.of(rule)); - when(roleService.getMonoRole("authenticated")).thenReturn(Mono.just(role)); - webClient = WebTestClient.bindToRouterFunction(endpoint.endpoint()) - .build(); - webClient = webClient.mutateWith(csrf()); + webClient = WebTestClient.bindToRouterFunction(endpoint.endpoint()).build() + .mutateWith(csrf()); } @Nested diff --git a/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java b/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java index 8b2b8e9d6..b18f928a8 100644 --- a/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java +++ b/application/src/test/java/run/halo/app/core/extension/reconciler/ThemeReconcilerTest.java @@ -19,11 +19,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; import org.json.JSONException; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; @@ -40,9 +41,8 @@ import run.halo.app.extension.Metadata; import run.halo.app.extension.MetadataOperator; import run.halo.app.extension.controller.Reconciler; import run.halo.app.infra.SystemVersionSupplier; -import run.halo.app.infra.properties.HaloProperties; +import run.halo.app.infra.ThemeRootGetter; import run.halo.app.infra.utils.JsonUtils; -import run.halo.app.theme.ThemePathPolicy; /** * Tests for {@link ThemeReconciler}. @@ -57,37 +57,31 @@ class ThemeReconcilerTest { private ExtensionClient extensionClient; @Mock - private HaloProperties haloProperties; + private SystemVersionSupplier systemVersionSupplier; @Mock - private SystemVersionSupplier systemVersionSupplier; + ThemeRootGetter themeRoot; @Mock private File defaultTheme; + @InjectMocks + ThemeReconciler themeReconciler; + + @TempDir private Path tempDirectory; @BeforeEach void setUp() throws IOException { - tempDirectory = Files.createTempDirectory("halo-theme-"); defaultTheme = ResourceUtils.getFile("classpath:themes/default"); lenient().when(systemVersionSupplier.get()).thenReturn(Version.valueOf("0.0.0")); } - @AfterEach - void tearDown() throws IOException { - FileSystemUtils.deleteRecursively(tempDirectory); - } - @Test void reconcileDelete() throws IOException { Path testWorkDir = tempDirectory.resolve("reconcile-delete"); Files.createDirectory(testWorkDir); - when(haloProperties.getWorkDir()).thenReturn(testWorkDir); - - final ThemeReconciler themeReconciler = - new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier); - final ThemePathPolicy themePathPolicy = new ThemePathPolicy(testWorkDir); + when(themeRoot.get()).thenReturn(testWorkDir); Theme theme = new Theme(); Metadata metadata = new Metadata(); @@ -100,7 +94,7 @@ class ThemeReconcilerTest { themeSpec.setSettingName("theme-test-setting"); theme.setSpec(themeSpec); - Path defaultThemePath = themePathPolicy.generate(theme); + Path defaultThemePath = testWorkDir.resolve("theme-test"); // copy to temp directory FileSystemUtils.copyRecursively(defaultTheme.toPath(), defaultThemePath); @@ -130,10 +124,10 @@ class ThemeReconcilerTest { final MetadataOperator metadata = theme.getMetadata(); Path testWorkDir = tempDirectory.resolve("reconcile-delete"); - when(haloProperties.getWorkDir()).thenReturn(testWorkDir); + when(themeRoot.get()).thenReturn(testWorkDir); final ThemeReconciler themeReconciler = - new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier); + new ThemeReconciler(extensionClient, themeRoot, systemVersionSupplier); final int[] retryFlags = {0, 0}; when(extensionClient.fetch(eq(Setting.class), eq("theme-test-setting"))) @@ -169,10 +163,10 @@ class ThemeReconcilerTest { Theme theme = fakeTheme(); Path testWorkDir = tempDirectory.resolve("reconcile-delete"); - when(haloProperties.getWorkDir()).thenReturn(testWorkDir); + when(themeRoot.get()).thenReturn(testWorkDir); final ThemeReconciler themeReconciler = - new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier); + new ThemeReconciler(extensionClient, themeRoot, systemVersionSupplier); final int[] retryFlags = {0}; when(extensionClient.fetch(eq(Setting.class), eq("theme-test-setting"))) @@ -198,10 +192,10 @@ class ThemeReconcilerTest { void reconcileStatus() { when(systemVersionSupplier.get()).thenReturn(Version.valueOf("2.3.0")); Path testWorkDir = tempDirectory.resolve("reconcile-delete"); - when(haloProperties.getWorkDir()).thenReturn(testWorkDir); + when(themeRoot.get()).thenReturn(testWorkDir); final ThemeReconciler themeReconciler = - new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier); + new ThemeReconciler(extensionClient, themeRoot, systemVersionSupplier); Theme theme = fakeTheme(); theme.setStatus(null); theme.getSpec().setRequires(">2.3.0"); @@ -246,10 +240,7 @@ class ThemeReconcilerTest { void themeSettingDefaultValue() throws IOException, JSONException { Path testWorkDir = tempDirectory.resolve("reconcile-setting-value"); Files.createDirectory(testWorkDir); - when(haloProperties.getWorkDir()).thenReturn(testWorkDir); - - final ThemeReconciler themeReconciler = - new ThemeReconciler(extensionClient, haloProperties, systemVersionSupplier); + when(themeRoot.get()).thenReturn(testWorkDir); Theme theme = new Theme(); Metadata metadata = new Metadata(); diff --git a/application/src/test/java/run/halo/app/core/extension/service/DefaultRoleServiceTest.java b/application/src/test/java/run/halo/app/core/extension/service/DefaultRoleServiceTest.java index a9b622921..8db68ba58 100644 --- a/application/src/test/java/run/halo/app/core/extension/service/DefaultRoleServiceTest.java +++ b/application/src/test/java/run/halo/app/core/extension/service/DefaultRoleServiceTest.java @@ -1,9 +1,7 @@ package run.halo.app.core.extension.service; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; @@ -17,10 +15,10 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; import org.assertj.core.api.AssertionsForInterfaceTypes; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import reactor.core.publisher.Flux; @@ -28,7 +26,6 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.RoleBinding; -import run.halo.app.core.extension.TestRole; import run.halo.app.extension.Metadata; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.utils.JsonUtils; @@ -44,56 +41,9 @@ class DefaultRoleServiceTest { @Mock private ReactiveExtensionClient extensionClient; + @InjectMocks private DefaultRoleService roleService; - @BeforeEach - void setUp() { - roleService = new DefaultRoleService(extensionClient); - } - - @Test - void listDependencies() { - Role roleManage = TestRole.getRoleManage(); - Map manageAnnotations = new HashMap<>(); - manageAnnotations.put(Role.ROLE_DEPENDENCIES_ANNO, "[\"role-template-apple-view\"]"); - roleManage.getMetadata().setAnnotations(manageAnnotations); - - Role roleView = TestRole.getRoleView(); - Map viewAnnotations = new HashMap<>(); - viewAnnotations.put(Role.ROLE_DEPENDENCIES_ANNO, "[\"role-template-apple-other\"]"); - roleView.getMetadata().setAnnotations(viewAnnotations); - - Role roleOther = TestRole.getRoleOther(); - - when(extensionClient.fetch(same(Role.class), eq("role-template-apple-manage"))) - .thenReturn(Mono.just(roleManage)); - when(extensionClient.fetch(same(Role.class), eq("role-template-apple-view"))) - .thenReturn(Mono.just(roleView)); - when(extensionClient.fetch(same(Role.class), eq("role-template-apple-other"))) - .thenReturn(Mono.just(roleOther)); - - // list without cycle - List roles = roleService.listDependencies(Set.of("role-template-apple-manage")); - - verify(extensionClient, times(1)).fetch(same(Role.class), eq("role-template-apple-manage")); - verify(extensionClient, times(1)).fetch(same(Role.class), eq("role-template-apple-view")); - verify(extensionClient, times(1)).fetch(same(Role.class), eq("role-template-apple-other")); - - assertThat(roles).hasSize(3); - assertThat(roles).containsExactly(roleManage, roleView, roleOther); - - // list dependencies with a cycle relation - Map anotherAnnotations = new HashMap<>(); - anotherAnnotations.put(Role.ROLE_DEPENDENCIES_ANNO, "[\"role-template-apple-view\"]"); - roleOther.getMetadata().setAnnotations(anotherAnnotations); - when(extensionClient.fetch(same(Role.class), eq("role-template-apple-other"))) - .thenReturn(Mono.just(roleOther)); - // correct behavior is to ignore the cycle relation - List rolesFromCycle = - roleService.listDependencies(Set.of("role-template-apple-manage")); - assertThat(rolesFromCycle).hasSize(3); - } - @Nested class ListDependenciesTest { @Test diff --git a/application/src/test/java/run/halo/app/metrics/VisitLogWriterTest.java b/application/src/test/java/run/halo/app/metrics/VisitLogWriterTest.java deleted file mode 100644 index 49cfcca7d..000000000 --- a/application/src/test/java/run/halo/app/metrics/VisitLogWriterTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package run.halo.app.metrics; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.util.FileSystemUtils; -import run.halo.app.infra.properties.HaloProperties; - -/** - * Tests for {@link VisitLogWriter}. - * - * @author guqing - * @since 2.0.0 - */ -@Disabled -@Deprecated -@ExtendWith(MockitoExtension.class) -class VisitLogWriterTest { - - @Mock - private HaloProperties haloProperties; - - private VisitLogWriter visitLogWriter; - - private Path workDir; - - @BeforeEach - void setUp() throws IOException { - workDir = Files.createTempDirectory("halo-visitlog"); - when(haloProperties.getWorkDir()).thenReturn(workDir); - visitLogWriter = new VisitLogWriter(haloProperties); - } - - @AfterEach - void tearDown() throws Exception { - visitLogWriter.destroy(); - FileSystemUtils.deleteRecursively(workDir); - } - - @Test - void start() { - assertThat(visitLogWriter.isStarted()).isFalse(); - visitLogWriter.start(); - assertThat(visitLogWriter.isStarted()).isTrue(); - } -} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/migration/BackupReconcilerTest.java b/application/src/test/java/run/halo/app/migration/BackupReconcilerTest.java index 12e08331f..1a18604bf 100644 --- a/application/src/test/java/run/halo/app/migration/BackupReconcilerTest.java +++ b/application/src/test/java/run/halo/app/migration/BackupReconcilerTest.java @@ -7,7 +7,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -175,9 +174,8 @@ class BackupReconcilerTest { backup.getSpec().setFormat("zip"); when(client.fetch(Backup.class, name)).thenReturn(Optional.of(backup)); doNothing().when(client).update(backup); - Mono mono = mock(Mono.class); - when(mono.block()).thenThrow(Exceptions.propagate(new InterruptedException())); - when(migrationService.backup(backup)).thenReturn(mono); + when(migrationService.backup(backup)).thenReturn( + Mono.error(Exceptions.propagate(new InterruptedException()))); var result = reconciler.reconcile(new Reconciler.Request(name)); @@ -196,7 +194,6 @@ class BackupReconcilerTest { verify(client, times(3)).fetch(Backup.class, name); verify(client, times(3)).update(backup); verify(migrationService).backup(backup); - verify(mono).block(); } @Test @@ -206,9 +203,8 @@ class BackupReconcilerTest { backup.getSpec().setFormat("zip"); when(client.fetch(Backup.class, name)).thenReturn(Optional.of(backup)); doNothing().when(client).update(backup); - Mono mono = mock(Mono.class); - when(mono.block()).thenThrow(Exceptions.propagate(new IOException("File not found"))); - when(migrationService.backup(backup)).thenReturn(mono); + when(migrationService.backup(backup)) + .thenReturn(Mono.error(Exceptions.propagate(new IOException("File not found")))); var result = reconciler.reconcile(new Reconciler.Request(name)); @@ -227,7 +223,6 @@ class BackupReconcilerTest { verify(client, times(3)).fetch(Backup.class, name); verify(client, times(3)).update(backup); verify(migrationService).backup(backup); - verify(mono).block(); } @Test diff --git a/application/src/test/java/run/halo/app/security/LoginUtils.java b/application/src/test/java/run/halo/app/security/LoginUtils.java deleted file mode 100644 index ba64720ef..000000000 --- a/application/src/test/java/run/halo/app/security/LoginUtils.java +++ /dev/null @@ -1,23 +0,0 @@ -package run.halo.app.security; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Mono; -import run.halo.app.security.authentication.jwt.LoginAuthenticationConverter; - -public final class LoginUtils { - - public static Mono login(WebTestClient webClient, String username, String password) { - var request = new LoginAuthenticationConverter.UsernamePasswordRequest(); - request.setUsername(username); - request.setPassword(password); - return webClient.post().uri("/api/auth/token") - .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).bodyValue(request) - .exchange().expectStatus().isOk().expectHeader().contentType(MediaType.APPLICATION_JSON) - .returnResult(ResponseMap.class) - .getResponseBody() - .single() - .map(responseMap -> responseMap.get("token").toString()); - } -} diff --git a/application/src/test/java/run/halo/app/security/authentication/jwt/JwtAuthenticationTest.java b/application/src/test/java/run/halo/app/security/authentication/jwt/JwtAuthenticationTest.java deleted file mode 100644 index d80ecae70..000000000 --- a/application/src/test/java/run/halo/app/security/authentication/jwt/JwtAuthenticationTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package run.halo.app.security.authentication.jwt; - -import static org.hamcrest.Matchers.containsString; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; - -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpHeaders; -import org.springframework.security.core.userdetails.ReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Mono; -import run.halo.app.core.extension.Role; -import run.halo.app.core.extension.service.RoleService; -import run.halo.app.extension.Metadata; -import run.halo.app.infra.AnonymousUserConst; -import run.halo.app.security.LoginUtils; - -@Disabled -@SpringBootTest -@AutoConfigureWebTestClient -class JwtAuthenticationTest { - - @Autowired - WebTestClient webClient; - - @MockBean - ReactiveUserDetailsService userDetailsService; - - @MockBean - RoleService roleService; - - @BeforeEach - void setUp() { - lenient().when(roleService.getMonoRole(eq(AnonymousUserConst.Role))) - .thenReturn(Mono.empty()); - webClient = webClient.mutateWith(csrf()); - } - - @Test - void accessProtectedApiWithoutToken() { - webClient.get().uri("/api/v1/test/hello").exchange().expectStatus().isUnauthorized(); - } - - @Test - void accessProtectedApiUsingBearerToken() { - when(userDetailsService.findByUsername(anyString())).thenReturn(Mono.just( - User.withDefaultPasswordEncoder() - .username("username") - .password("password") - .roles("USER") - .build() - )); - - var role = new Role(); - var metadata = new Metadata(); - metadata.setName("USER"); - role.setMetadata(metadata); - role.setRules(List.of(new Role.PolicyRule.Builder() - .apiGroups("") - .resources("test") - .resourceNames("hello") - .build())); - when(roleService.getMonoRole("authenticated")).thenReturn(Mono.empty()); - when(roleService.getMonoRole("USER")).thenReturn(Mono.just(role)); - - final var token = LoginUtils.login(webClient, "username", "password").block(); - webClient.get().uri("/api/v1/test/hello") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) - .exchange() - .expectStatus().isForbidden() - .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, - containsString("insufficient_scope")); - } - -} diff --git a/application/src/test/java/run/halo/app/security/authentication/jwt/LoginTest.java b/application/src/test/java/run/halo/app/security/authentication/jwt/LoginTest.java deleted file mode 100644 index 270b64d30..000000000 --- a/application/src/test/java/run/halo/app/security/authentication/jwt/LoginTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package run.halo.app.security.authentication.jwt; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; - -import com.nimbusds.jwt.JWTClaimNames; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.security.core.userdetails.ReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Mono; - -@Disabled -@SpringBootTest -@AutoConfigureWebTestClient -class LoginTest { - - @Autowired - WebTestClient webClient; - - @MockBean - ReactiveUserDetailsService userDetailsService; - - @BeforeEach - void setUp(@Autowired PasswordEncoder passwordEncoder) { - when(userDetailsService.findByUsername("user")).thenReturn(Mono.just( - User.builder() - .passwordEncoder(passwordEncoder::encode) - .username("user") - .password("password") - .roles("USER") - .build() - )); - - webClient = webClient.mutateWith(csrf()); - } - - @Test - void logintWithoutLoginRequest() { - webClient.post().uri("/api/auth/token").exchange().expectStatus().isUnauthorized(); - } - - @Test - void loginWithoutPostMethod() { - webClient.get().uri("/api/auth/token").exchange().expectStatus().isUnauthorized(); - webClient.put().uri("/api/auth/token").exchange().expectStatus().isUnauthorized(); - webClient.delete().uri("/api/auth/token").exchange().expectStatus().isUnauthorized(); - webClient.patch().uri("/api/auth/token").exchange().expectStatus().isUnauthorized(); - } - - @Test - void loginWithoutApplicationJsonAcceptHeader() { - var request = new LoginAuthenticationConverter.UsernamePasswordRequest(); - request.setUsername("user"); - request.setPassword("invalid_password"); - webClient.post().uri("/api/auth/token") - .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE).bodyValue(request) - .exchange().expectStatus().isUnauthorized(); - } - - @Test - void loginWithInvalidCredential() { - when(userDetailsService.findByUsername("user")).thenReturn(Mono.empty()); - var request = new LoginAuthenticationConverter.UsernamePasswordRequest(); - request.setUsername("user"); - request.setPassword("invalid_password"); - webClient.post().uri("/api/auth/token") - .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).bodyValue(request) - .exchange().expectStatus().isBadRequest().expectHeader() - .contentType(MediaType.APPLICATION_JSON).expectBody() - .jsonPath("$.error").value(equalTo("Invalid Credentials")); - } - - @Test - void loginWithValidCredential(@Autowired ReactiveJwtDecoder jwtDecoder, - @Autowired PasswordEncoder passwordEncoder) { - when(userDetailsService.findByUsername("user")).thenReturn(Mono.just( - User.builder() - .passwordEncoder(passwordEncoder::encode) - .username("user") - .password("password") - .roles("USER") - .build() - )); - var request = new LoginAuthenticationConverter.UsernamePasswordRequest(); - request.setUsername("user"); - request.setPassword("password"); - webClient.post().uri("/api/auth/token") - .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).bodyValue(request) - .exchange().expectStatus().isOk().expectHeader().contentType(MediaType.APPLICATION_JSON) - .expectBody().jsonPath("$.token").value(token -> { - var jwt = jwtDecoder.decode(token).block(); - assertNotNull(jwt); - assertEquals("user", jwt.getClaim(JWTClaimNames.SUBJECT)); - }, String.class); - } -} diff --git a/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java b/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java index 2a8ddbea5..9ae0ef24d 100644 --- a/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java +++ b/application/src/test/java/run/halo/app/security/authorization/AuthorizationTest.java @@ -1,6 +1,6 @@ package run.halo.app.security.authorization; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -10,12 +10,9 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import static org.springframework.web.reactive.function.server.RequestPredicates.PUT; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; import static org.springframework.web.reactive.function.server.RouterFunctions.route; -import static run.halo.app.extension.GroupVersionKind.fromExtension; import java.util.ArrayList; -import java.util.List; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; @@ -24,26 +21,23 @@ import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; -import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.lang.NonNull; +import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService; import org.springframework.security.core.userdetails.ReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.Role.PolicyRule; import run.halo.app.core.extension.service.RoleService; import run.halo.app.extension.Metadata; -import run.halo.app.extension.exception.ExtensionNotFoundException; import run.halo.app.infra.AnonymousUserConst; -import run.halo.app.security.LoginUtils; -@Disabled @SpringBootTest @AutoConfigureWebTestClient @Import(AuthorizationTest.TestConfig.class) @@ -55,6 +49,9 @@ class AuthorizationTest { @MockBean ReactiveUserDetailsService userDetailsService; + @MockBean + ReactiveUserDetailsPasswordService userDetailsPasswordService; + @MockBean RoleService roleService; @@ -63,64 +60,16 @@ class AuthorizationTest { webClient = webClient.mutateWith(csrf()); } - @Test - void accessProtectedApiWithoutSufficientRole() { - when(userDetailsService.findByUsername(eq("user"))).thenReturn( - Mono.just(User.withDefaultPasswordEncoder().username("user").password("password") - .roles("invalid-role").build())); - when(roleService.getMonoRole(any())).thenReturn(Mono.empty()); - var token = LoginUtils.login(webClient, "user", "password").block(); - webClient.get().uri("/apis/fake.halo.run/v1/posts") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + token).exchange().expectStatus() - .isForbidden(); - - verify(roleService, times(1)).getMonoRole("authenticated"); - verify(roleService, times(1)).getMonoRole("invalid-role"); - } - - @Test - void accessProtectedApiWithSufficientRole() { - when(userDetailsService.findByUsername(eq("user"))).thenReturn(Mono.just( - User.withDefaultPasswordEncoder().username("user").password("password") - .roles("post.read").build())); - when(roleService.getMonoRole(eq(AnonymousUserConst.Role))) - .thenReturn(Mono.empty()); - - var role = new Role(); - role.setRules(List.of( - new PolicyRule.Builder().apiGroups("fake.halo.run").verbs("list").resources("posts") - .build())); - - when(roleService.getMonoRole("post.read")).thenReturn(Mono.just(role)); - when(roleService.getMonoRole("authenticated")).thenReturn( - Mono.error( - () -> new ExtensionNotFoundException(fromExtension(Role.class), "authenticated"))); - - var token = LoginUtils.login(webClient, "user", "password").block(); - webClient.get().uri("/apis/fake.halo.run/v1/posts") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("returned posts"); - - webClient.put().uri("/apis/fake.halo.run/v1/posts/hello-halo") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + token).exchange() - .expectStatus().isForbidden(); - - verify(roleService, times(2)).getMonoRole("authenticated"); - verify(roleService, times(2)).getMonoRole("post.read"); - } - @Test void anonymousUserAccessProtectedApi() { when(userDetailsService.findByUsername(eq(AnonymousUserConst.PRINCIPAL))) .thenReturn(Mono.empty()); - when(roleService.getMonoRole(AnonymousUserConst.Role)) - .thenReturn(Mono.empty()); + when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.empty()); + webClient.get().uri("/apis/fake.halo.run/v1/posts").exchange().expectStatus() .isUnauthorized(); - verify(roleService, times(1)).getMonoRole(AnonymousUserConst.Role); + verify(roleService).listDependenciesFlux(anySet()); } @Test @@ -137,25 +86,23 @@ class AuthorizationTest { .resources("posts") .build(); role.getRules().add(policyRule); - when(roleService.getMonoRole(AnonymousUserConst.Role)) - .thenReturn(Mono.just(role)); + when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.just(role)); webClient.get().uri("/apis/fake.halo.run/v1/posts").exchange().expectStatus() .isOk() .expectBody(String.class).isEqualTo("returned posts"); - verify(roleService, times(1)).getMonoRole(AnonymousUserConst.Role); + verify(roleService).listDependenciesFlux(anySet()); webClient.get().uri("/apis/fake.halo.run/v1/posts/hello-halo").exchange() .expectStatus() .isUnauthorized(); - verify(roleService, times(2)).getMonoRole(AnonymousUserConst.Role); + + verify(roleService, times(2)).listDependenciesFlux(anySet()); } @Test @WithMockUser(username = "user", roles = "post.read") void authenticatedUserAccessAuthenticationFreeApi() { - when(roleService.getMonoRole("authenticated")).thenReturn(Mono.empty()); - when(roleService.getMonoRole("post.read")).thenReturn(Mono.empty()); Role role = new Role(); role.setMetadata(new Metadata()); role.getMetadata().setName(AnonymousUserConst.Role); @@ -166,11 +113,13 @@ class AuthorizationTest { .resources("posts") .build(); role.getRules().add(policyRule); - when(roleService.getMonoRole(AnonymousUserConst.Role)) - .thenReturn(Mono.just(role)); + + when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.just(role)); + webClient.get().uri("/apis/fake.halo.run/v1/posts").exchange().expectStatus() .isOk() .expectBody(String.class).isEqualTo("returned posts"); + verify(roleService).listDependenciesFlux(anySet()); } @TestConfiguration diff --git a/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java b/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java new file mode 100644 index 000000000..e2f298f3f --- /dev/null +++ b/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java @@ -0,0 +1,113 @@ +package run.halo.app.security.authorization; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.mock.http.server.reactive.MockServerHttpRequest.method; +import static org.springframework.security.core.authority.AuthorityUtils.createAuthorityList; + +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpMethod; +import org.springframework.security.core.userdetails.User; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; +import run.halo.app.core.extension.Role; +import run.halo.app.core.extension.Role.PolicyRule; +import run.halo.app.core.extension.service.RoleService; +import run.halo.app.extension.Metadata; + +@ExtendWith(MockitoExtension.class) +class DefaultRuleResolverTest { + + @Mock + RoleService roleService; + + @InjectMocks + DefaultRuleResolver ruleResolver; + + @Test + void visitRules() { + when(roleService.listDependenciesFlux(Set.of("authenticated", "anonymous", "ruleReadPost"))) + .thenReturn(Flux.just(mockRole())); + var fakeUser = new User("admin", "123456", createAuthorityList("ruleReadPost")); + var cases = getRequestResolveCases(); + cases.forEach(requestResolveCase -> { + var httpMethod = HttpMethod.valueOf(requestResolveCase.method); + var request = method(httpMethod, requestResolveCase.url).build(); + var requestInfo = RequestInfoFactory.INSTANCE.newRequestInfo(request); + StepVerifier.create(ruleResolver.visitRules(fakeUser, requestInfo)) + .assertNext( + visitor -> assertEquals(requestResolveCase.expected, visitor.isAllowed())) + .verifyComplete(); + }); + + verify(roleService, times(cases.size())).listDependenciesFlux( + Set.of("authenticated", "anonymous", "ruleReadPost")); + } + + Role mockRole() { + var role = new Role(); + var rules = List.of( + new PolicyRule.Builder().apiGroups("").resources("posts").verbs("list", "get").build(), + new PolicyRule.Builder().apiGroups("").resources("categories").verbs("*").build(), + new PolicyRule.Builder().apiGroups("api.plugin.halo.run") + .resources("plugins/users") + .resourceNames("foo/bar").verbs("*").build(), + new PolicyRule.Builder().apiGroups("api.plugin.halo.run") + .resources("plugins/users") + .resourceNames("foo").verbs("*").build(), + new PolicyRule.Builder().nonResourceURLs("/healthy").verbs("get", "post", "head") + .build()); + role.setRules(rules); + var metadata = new Metadata(); + metadata.setName("ruleReadPost"); + role.setMetadata(metadata); + return role; + } + + List getRequestResolveCases() { + return List.of(new RequestResolveCase("/api/v1/tags", "GET", false), + new RequestResolveCase("/api/v1/tags/tagName", "GET", false), + + new RequestResolveCase("/api/v1/categories/aName", "GET", true), + new RequestResolveCase("/api/v1//categories", "POST", true), + new RequestResolveCase("/api/v1/categories", "DELETE", true), + new RequestResolveCase("/api/v1/posts", "GET", true), + new RequestResolveCase("/api/v1/posts/aName", "GET", true), + + new RequestResolveCase("/api/v1/posts", "DELETE", false), + new RequestResolveCase("/api/v1/posts/aName", "UPDATE", false), + + // group resource url + new RequestResolveCase("/apis/group/v1/posts", "GET", false), + + // plugin custom resource url + new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/users", "GET", + true), + new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/users/bar", + "GET", true), + new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/posts/bar", + "GET", false), + + // non resource url + new RequestResolveCase("/healthy", "GET", true), + new RequestResolveCase("/healthy", "POST", true), + new RequestResolveCase("/healthy", "HEAD", true), + new RequestResolveCase("//healthy", "GET", false), + new RequestResolveCase("/healthy/name", "GET", false), + new RequestResolveCase("/healthy1", "GET", false), + + new RequestResolveCase("//healthy//name", "GET", false), + new RequestResolveCase("/", "GET", false)); + } + + record RequestResolveCase(String url, String method, boolean expected) { + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/security/authorization/RequestInfoResolverTest.java b/application/src/test/java/run/halo/app/security/authorization/RequestInfoResolverTest.java index 498ddf3c2..af0fad243 100644 --- a/application/src/test/java/run/halo/app/security/authorization/RequestInfoResolverTest.java +++ b/application/src/test/java/run/halo/app/security/authorization/RequestInfoResolverTest.java @@ -3,33 +3,19 @@ package run.halo.app.security.authorization; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.springframework.mock.http.server.reactive.MockServerHttpRequest.method; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; -import run.halo.app.core.extension.Role; -import run.halo.app.core.extension.Role.PolicyRule; -import run.halo.app.core.extension.service.RoleService; -import run.halo.app.extension.Metadata; /** * Tests for {@link RequestInfoFactory}. * * @author guqing - * @see RbacRequestEvaluation * @see RequestInfo - * @see DefaultRuleResolver * @since 2.0.0 */ public class RequestInfoResolverTest { @@ -173,7 +159,7 @@ public class RequestInfoResolverTest { List.of(new ErrorCases("api resource has name and no subresource but post", "/api/version/themes/install"), new ErrorCases("apis resource has name and no subresource but post", - "/apis/api.halo.run/v1alpha1/themes/install")); + "/apis/api.halo.run/v1alpha1/themes/install")); for (ErrorCases errorCase : postCases) { var request = method(HttpMethod.POST, errorCase.url).build(); @@ -185,96 +171,13 @@ public class RequestInfoResolverTest { } } - @Test - public void defaultRuleResolverTest() { - var roleService = mock(RoleService.class); - var ruleResolver = new DefaultRuleResolver(roleService); - - Role role = new Role(); - List rules = List.of( - new PolicyRule.Builder().apiGroups("").resources("posts").verbs("list", "get") - .build(), - new PolicyRule.Builder().apiGroups("").resources("categories").verbs("*").build(), - new PolicyRule.Builder().apiGroups("api.plugin.halo.run").resources("plugins/users") - .resourceNames("foo/bar").verbs("*").build(), - new PolicyRule.Builder().apiGroups("api.plugin.halo.run").resources("plugins/users") - .resourceNames("foo").verbs("*").build(), - new PolicyRule.Builder().nonResourceURLs("/healthy").verbs("get", "post", "head") - .build()); - role.setRules(rules); - Metadata metadata = new Metadata(); - metadata.setName("ruleReadPost"); - role.setMetadata(metadata); - - when(roleService.getRole(anyString())).thenReturn(role); - - // list bound role names - ruleResolver.setRoleBindingService( - (Collection authorities) -> Set.of("ruleReadPost")); - - User user = new User("admin", "123456", AuthorityUtils.createAuthorityList("ruleReadPost")); - - // resolve user rules - List resolvedRules = ruleResolver.rulesFor(user); - assertThat(resolvedRules).isNotNull(); - - RbacRequestEvaluation rbacRequestEvaluation = new RbacRequestEvaluation(); - for (RequestResolveCase requestResolveCase : getRequestResolveCases()) { - var request = - method(HttpMethod.valueOf(requestResolveCase.method), - requestResolveCase.url).build(); - RequestInfo requestInfo = RequestInfoFactory.INSTANCE.newRequestInfo(request); - - AttributesRecord attributes = new AttributesRecord(user, requestInfo); - boolean allowed = rbacRequestEvaluation.rulesAllow(attributes, resolvedRules); - assertThat(allowed).isEqualTo(requestResolveCase.expected); - } - } - public record NonApiCase(String url, boolean expected) { } public record ErrorCases(String desc, String url) { } - List getRequestResolveCases() { - return List.of(new RequestResolveCase("/api/v1/tags", "GET", false), - new RequestResolveCase("/api/v1/tags/tagName", "GET", false), - new RequestResolveCase("/api/v1/categories/aName", "GET", true), - new RequestResolveCase("/api/v1//categories", "POST", true), - new RequestResolveCase("/api/v1/categories", "DELETE", true), - new RequestResolveCase("/api/v1/posts", "GET", true), - new RequestResolveCase("/api/v1/posts/aName", "GET", true), - - new RequestResolveCase("/api/v1/posts", "DELETE", false), - new RequestResolveCase("/api/v1/posts/aName", "UPDATE", false), - - // group resource url - new RequestResolveCase("/apis/group/v1/posts", "GET", false), - - // plugin custom resource url - new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/users", "GET", - true), - new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/users/bar", - "GET", true), - new RequestResolveCase("/apis/api.plugin.halo.run/v1alpha1/plugins/foo/posts/bar", - "GET", false), - - // non resource url - new RequestResolveCase("/healthy", "GET", true), - new RequestResolveCase("/healthy", "POST", true), - new RequestResolveCase("/healthy", "HEAD", true), - new RequestResolveCase("//healthy", "GET", false), - new RequestResolveCase("/healthy/name", "GET", false), - new RequestResolveCase("/healthy1", "GET", false), - - new RequestResolveCase("//healthy//name", "GET", false), - new RequestResolveCase("/", "GET", false)); - } - - public record RequestResolveCase(String url, String method, boolean expected) { - } public record SuccessCase(String method, String url, String expectedVerb, String expectedAPIPrefix, String expectedAPIGroup,