diff --git a/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java b/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java index 511438694..096d19cfd 100644 --- a/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java +++ b/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java @@ -3,6 +3,7 @@ package run.halo.app.security.jackson2; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.security.jackson2.SecurityJackson2Modules; +import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; import run.halo.app.security.authentication.login.HaloUser; import run.halo.app.security.authentication.oauth2.HaloOAuth2AuthenticationToken; import run.halo.app.security.authentication.twofactor.TwoFactorAuthentication; @@ -28,6 +29,9 @@ public class HaloSecurityJackson2Module extends SimpleModule { context.setMixInAnnotations( HaloOAuth2AuthenticationToken.class, HaloOAuth2AuthenticationTokenMixin.class ); + context.setMixInAnnotations( + SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class + ); } } diff --git a/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java b/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java new file mode 100644 index 000000000..cbf440c11 --- /dev/null +++ b/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package run.halo.app.security.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; +import org.springframework.security.web.jackson2.WebServletJackson2Module; + +/** + * Jackson mixin class to serialize/deserialize {@link SwitchUserGrantedAuthority}. + * This class is copied from repository spring-projects/spring-security. + * + * @author Markus Heiden + * @see WebServletJackson2Module + * @see org.springframework.security.jackson2.SecurityJackson2Modules + * @since 6.3 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE +) +@JsonIgnoreProperties(ignoreUnknown = true) +abstract class SwitchUserGrantedAuthorityMixIn { + + @JsonCreator + SwitchUserGrantedAuthorityMixIn( + @JsonProperty("role") String role, + @JsonProperty("source") Authentication source + ) { + } + +} diff --git a/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java b/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java index a548c25c6..6ab6d07fd 100644 --- a/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java +++ b/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -18,6 +19,7 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; import run.halo.app.security.authentication.login.HaloUser; import run.halo.app.security.authentication.oauth2.HaloOAuth2AuthenticationToken; import run.halo.app.security.authentication.twofactor.TwoFactorAuthentication; @@ -61,6 +63,23 @@ class HaloSecurityJacksonModuleTest { }); } + @Test + void shouldReadSwitchUserGrantedAuthority() throws JsonProcessingException { + codecAssert(haloUser -> { + var authentication = UsernamePasswordAuthenticationToken.authenticated( + haloUser.getUsername(), haloUser.getPassword(), haloUser.getAuthorities() + ); + var switchUserGrantedAuthority = + new SwitchUserGrantedAuthority("ADMIN", authentication); + var extendedAuthorities = new ArrayList<>(authentication.getAuthorities()); + extendedAuthorities.add(switchUserGrantedAuthority); + authentication = UsernamePasswordAuthenticationToken.authenticated( + authentication.getPrincipal(), authentication.getCredentials(), extendedAuthorities + ); + return authentication; + }); + } + void codecAssert(Function authenticationConverter) throws JsonProcessingException { var userDetails = User.withUsername("faker")