Skip to content

Commit 7414684

Browse files
committed
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>
1 parent ca557a9 commit 7414684

File tree

3 files changed

+180
-8
lines changed

3 files changed

+180
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.rsocket;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import io.rsocket.core.RSocketServer;
23+
import io.rsocket.exceptions.RejectedSetupException;
24+
import io.rsocket.frame.decoder.PayloadDecoder;
25+
import io.rsocket.transport.netty.server.CloseableChannel;
26+
import io.rsocket.transport.netty.server.TcpServerTransport;
27+
import org.junit.jupiter.api.AfterEach;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.extension.ExtendWith;
31+
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.messaging.handler.annotation.MessageMapping;
36+
import org.springframework.messaging.rsocket.RSocketRequester;
37+
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
38+
import org.springframework.security.authentication.AuthenticationTrustResolver;
39+
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
40+
import org.springframework.security.authorization.AuthorizationDecision;
41+
import org.springframework.security.authorization.ReactiveAuthorizationManager;
42+
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
43+
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
44+
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
45+
import org.springframework.stereotype.Controller;
46+
import org.springframework.test.context.ContextConfiguration;
47+
import org.springframework.test.context.junit.jupiter.SpringExtension;
48+
49+
import static org.assertj.core.api.Assertions.assertThat;
50+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
51+
52+
/**
53+
* @author Andrey Litvitski
54+
*/
55+
@ContextConfiguration
56+
@ExtendWith(SpringExtension.class)
57+
public class AnonymousAuthenticationITests {
58+
59+
@Autowired
60+
RSocketMessageHandler handler;
61+
62+
@Autowired
63+
SecuritySocketAcceptorInterceptor interceptor;
64+
65+
@Autowired
66+
ServerController controller;
67+
68+
private CloseableChannel server;
69+
70+
private RSocketRequester requester;
71+
72+
@BeforeEach
73+
public void setup() {
74+
// @formatter:off
75+
this.server = RSocketServer.create()
76+
.payloadDecoder(PayloadDecoder.ZERO_COPY)
77+
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
78+
)
79+
.acceptor(this.handler.responder())
80+
.bind(TcpServerTransport.create("localhost", 0))
81+
.block();
82+
// @formatter:on
83+
}
84+
85+
@AfterEach
86+
public void dispose() {
87+
this.requester.rsocket().dispose();
88+
this.server.dispose();
89+
this.controller.payloads.clear();
90+
}
91+
92+
@Test
93+
public void requestWhenAnonymousDisabledThenRespondsWithForbidden() {
94+
this.requester = RSocketRequester.builder()
95+
.rsocketStrategies(this.handler.getRSocketStrategies())
96+
.connectTcp("localhost", this.server.address().getPort())
97+
.block();
98+
String data = "andrew";
99+
assertThatExceptionOfType(RejectedSetupException.class).isThrownBy(
100+
() -> this.requester.route("secure.retrieve-mono").data(data).retrieveMono(String.class).block());
101+
assertThat(this.controller.payloads).isEmpty();
102+
}
103+
104+
@Configuration
105+
@EnableRSocketSecurity
106+
static class Config {
107+
108+
@Bean
109+
ServerController controller() {
110+
return new ServerController();
111+
}
112+
113+
@Bean
114+
RSocketMessageHandler messageHandler() {
115+
return new RSocketMessageHandler();
116+
}
117+
118+
@Bean
119+
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
120+
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
121+
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,
122+
exchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);
123+
rsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));
124+
return rsocket.build();
125+
}
126+
127+
}
128+
129+
@Controller
130+
static class ServerController {
131+
132+
private List<String> payloads = new ArrayList<>();
133+
134+
@MessageMapping("**")
135+
String retrieveMono(String payload) {
136+
add(payload);
137+
return "Hi " + payload;
138+
}
139+
140+
private void add(String p) {
141+
this.payloads.add(p);
142+
}
143+
144+
}
145+
146+
}

config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
268268
.route("secure.*").authenticated()
269269
.anyExchange().permitAll()
270270
)
271-
.basicAuthentication(Customizer.withDefaults());
271+
.basicAuthentication(Customizer.withDefaults())
272+
.anonymousAuthentication(Customizer.withDefaults());
272273
// @formatter:on
273274
return rsocket.build();
274275
}

config/src/main/java/org/springframework/security/config/annotation/rsocket/RSocketSecurity.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
* @author Manuel Tejeda
110110
* @author Ebert Toribio
111111
* @author Ngoc Nhan
112+
* @author Andrey Litvitski
112113
* @since 5.2
113114
*/
114115
public class RSocketSecurity {
@@ -119,6 +120,8 @@ public class RSocketSecurity {
119120

120121
private SimpleAuthenticationSpec simpleAuthSpec;
121122

123+
private AnonymousAuthenticationSpec anonymousAuthSpec;
124+
122125
private JwtSpec jwtSpec;
123126

124127
private AuthorizePayloadsSpec authorizePayload;
@@ -164,6 +167,19 @@ public RSocketSecurity simpleAuthentication(Customizer<SimpleAuthenticationSpec>
164167
return this;
165168
}
166169

170+
/**
171+
* Adds anonymous authentication
172+
* @param anonymous a customizer
173+
* @return this instance
174+
*/
175+
public RSocketSecurity anonymousAuthentication(Customizer<AnonymousAuthenticationSpec> anonymous) {
176+
if (this.anonymousAuthSpec == null) {
177+
this.anonymousAuthSpec = new AnonymousAuthenticationSpec();
178+
}
179+
anonymous.customize(this.anonymousAuthSpec);
180+
return this;
181+
}
182+
167183
/**
168184
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
169185
* @param basic
@@ -214,20 +230,16 @@ private List<PayloadInterceptor> payloadInterceptors() {
214230
if (this.jwtSpec != null) {
215231
result.addAll(this.jwtSpec.build());
216232
}
217-
result.add(anonymous());
233+
if (this.anonymousAuthSpec != null) {
234+
result.add(this.anonymousAuthSpec.build());
235+
}
218236
if (this.authorizePayload != null) {
219237
result.add(this.authorizePayload.build());
220238
}
221239
AnnotationAwareOrderComparator.sort(result);
222240
return result;
223241
}
224242

225-
private AnonymousPayloadInterceptor anonymous() {
226-
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
227-
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
228-
return result;
229-
}
230-
231243
private <T> T getBean(Class<T> beanClass) {
232244
if (this.context == null) {
233245
return null;
@@ -283,6 +295,19 @@ protected AuthenticationPayloadInterceptor build() {
283295

284296
}
285297

298+
public final class AnonymousAuthenticationSpec {
299+
300+
private AnonymousAuthenticationSpec() {
301+
}
302+
303+
protected AnonymousPayloadInterceptor build() {
304+
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
305+
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
306+
return result;
307+
}
308+
309+
}
310+
286311
public final class BasicAuthenticationSpec {
287312

288313
private ReactiveAuthenticationManager authenticationManager;

0 commit comments

Comments
 (0)