mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-23 10:48:51 +00:00
Add Disabling Anonymous Authentication in RSocketSecurity
Closes: gh-17132 Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com> 1 Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com> 1 Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
This commit is contained in:
parent
3278f3a410
commit
559b73b39f
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present 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 org.springframework.security.config.annotation.rsocket;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.rsocket.core.RSocketServer;
|
||||||
|
import io.rsocket.exceptions.RejectedSetupException;
|
||||||
|
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||||
|
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||||
|
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||||
|
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.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||||
|
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
|
||||||
|
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||||
|
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrey Litvitski
|
||||||
|
*/
|
||||||
|
@ContextConfiguration
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
public class AnonymousAuthenticationITests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RSocketMessageHandler handler;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
SecuritySocketAcceptorInterceptor interceptor;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ServerController controller;
|
||||||
|
|
||||||
|
private CloseableChannel server;
|
||||||
|
|
||||||
|
private RSocketRequester requester;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
// @formatter:off
|
||||||
|
this.server = RSocketServer.create()
|
||||||
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
|
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
|
||||||
|
)
|
||||||
|
.acceptor(this.handler.responder())
|
||||||
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
.block();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void dispose() {
|
||||||
|
this.requester.rsocket().dispose();
|
||||||
|
this.server.dispose();
|
||||||
|
this.controller.payloads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWhenAnonymousDisabledThenRespondsWithForbidden() {
|
||||||
|
this.requester = RSocketRequester.builder()
|
||||||
|
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||||
|
.connectTcp("localhost", this.server.address().getPort())
|
||||||
|
.block();
|
||||||
|
String data = "andrew";
|
||||||
|
assertThatExceptionOfType(RejectedSetupException.class).isThrownBy(
|
||||||
|
() -> this.requester.route("secure.retrieve-mono").data(data).retrieveMono(String.class).block());
|
||||||
|
assertThat(this.controller.payloads).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRSocketSecurity
|
||||||
|
static class Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ServerController controller() {
|
||||||
|
return new ServerController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RSocketMessageHandler messageHandler() {
|
||||||
|
return new RSocketMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
|
||||||
|
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
|
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,
|
||||||
|
exchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);
|
||||||
|
rsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));
|
||||||
|
rsocket.anonymousAuthentication((anonymousAuthentication) -> anonymousAuthentication.disable());
|
||||||
|
return rsocket.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
static class ServerController {
|
||||||
|
|
||||||
|
private List<String> payloads = new ArrayList<>();
|
||||||
|
|
||||||
|
@MessageMapping("**")
|
||||||
|
String retrieveMono(String payload) {
|
||||||
|
add(payload);
|
||||||
|
return "Hi " + payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(String p) {
|
||||||
|
this.payloads.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -109,6 +109,7 @@ import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMat
|
|||||||
* @author Manuel Tejeda
|
* @author Manuel Tejeda
|
||||||
* @author Ebert Toribio
|
* @author Ebert Toribio
|
||||||
* @author Ngoc Nhan
|
* @author Ngoc Nhan
|
||||||
|
* @author Andrey Litvitski
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class RSocketSecurity {
|
public class RSocketSecurity {
|
||||||
@ -119,6 +120,8 @@ public class RSocketSecurity {
|
|||||||
|
|
||||||
private SimpleAuthenticationSpec simpleAuthSpec;
|
private SimpleAuthenticationSpec simpleAuthSpec;
|
||||||
|
|
||||||
|
private AnonymousAuthenticationSpec anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
|
||||||
|
|
||||||
private JwtSpec jwtSpec;
|
private JwtSpec jwtSpec;
|
||||||
|
|
||||||
private AuthorizePayloadsSpec authorizePayload;
|
private AuthorizePayloadsSpec authorizePayload;
|
||||||
@ -164,6 +167,19 @@ public class RSocketSecurity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds anonymous authentication
|
||||||
|
* @param anonymous a customizer
|
||||||
|
* @return this instance
|
||||||
|
*/
|
||||||
|
public RSocketSecurity anonymousAuthentication(Customizer<AnonymousAuthenticationSpec> anonymous) {
|
||||||
|
if (this.anonymousAuthSpec == null) {
|
||||||
|
this.anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
|
||||||
|
}
|
||||||
|
anonymous.customize(this.anonymousAuthSpec);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
|
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
|
||||||
* @param basic
|
* @param basic
|
||||||
@ -214,7 +230,9 @@ public class RSocketSecurity {
|
|||||||
if (this.jwtSpec != null) {
|
if (this.jwtSpec != null) {
|
||||||
result.addAll(this.jwtSpec.build());
|
result.addAll(this.jwtSpec.build());
|
||||||
}
|
}
|
||||||
result.add(anonymous());
|
if (this.anonymousAuthSpec != null) {
|
||||||
|
result.add(this.anonymousAuthSpec.build());
|
||||||
|
}
|
||||||
if (this.authorizePayload != null) {
|
if (this.authorizePayload != null) {
|
||||||
result.add(this.authorizePayload.build());
|
result.add(this.authorizePayload.build());
|
||||||
}
|
}
|
||||||
@ -222,12 +240,6 @@ public class RSocketSecurity {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnonymousPayloadInterceptor anonymous() {
|
|
||||||
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
|
|
||||||
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T getBean(Class<T> beanClass) {
|
private <T> T getBean(Class<T> beanClass) {
|
||||||
if (this.context == null) {
|
if (this.context == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -283,6 +295,26 @@ public class RSocketSecurity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class AnonymousAuthenticationSpec {
|
||||||
|
|
||||||
|
private RSocketSecurity parent;
|
||||||
|
|
||||||
|
private AnonymousAuthenticationSpec(RSocketSecurity parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AnonymousPayloadInterceptor build() {
|
||||||
|
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
|
||||||
|
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disable() {
|
||||||
|
this.parent.anonymousAuthSpec = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public final class BasicAuthenticationSpec {
|
public final class BasicAuthenticationSpec {
|
||||||
|
|
||||||
private ReactiveAuthenticationManager authenticationManager;
|
private ReactiveAuthenticationManager authenticationManager;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user