Skip to content

Commit 1216ee5

Browse files
committed
Enable Null checking in spring-security-rsocket via JSpecify
Closes gh-16882
1 parent a4a4908 commit 1216ee5

16 files changed

+176
-21
lines changed

rsocket/spring-security-rsocket.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
plugins {
2+
id 'security-nullability'
3+
}
4+
15
apply plugin: 'io.spring.convention.spring-module'
26

37
dependencies {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
/**
18+
* Spring Security RSocket APIs.
19+
*/
20+
@NullMarked
21+
package org.springframework.security.rsocket.api;
22+
23+
import org.jspecify.annotations.NullMarked;

rsocket/src/main/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadExchangeConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.rsocket.metadata.AuthMetadataCodec;
2525
import io.rsocket.metadata.WellKnownAuthType;
2626
import io.rsocket.metadata.WellKnownMimeType;
27+
import org.jspecify.annotations.Nullable;
2728
import reactor.core.publisher.Mono;
2829

2930
import org.springframework.core.codec.ByteArrayDecoder;
@@ -66,7 +67,7 @@ public Mono<Authentication> convert(PayloadExchange exchange) {
6667
.flatMap((metadata) -> Mono.justOrEmpty(authentication(metadata)));
6768
}
6869

69-
private Authentication authentication(Map<String, Object> metadata) {
70+
private @Nullable Authentication authentication(Map<String, Object> metadata) {
7071
byte[] authenticationMetadata = (byte[]) metadata.get("authentication");
7172
if (authenticationMetadata == null) {
7273
return null;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
/**
18+
* Spring Security RSocket Authentication integration.
19+
*/
20+
@NullMarked
21+
package org.springframework.security.rsocket.authentication;
22+
23+
import org.jspecify.annotations.NullMarked;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
/**
18+
* Spring Security RSocket authorization integration.
19+
*/
20+
@NullMarked
21+
package org.springframework.security.rsocket.authorization;
22+
23+
import org.jspecify.annotations.NullMarked;

rsocket/src/main/java/org/springframework/security/rsocket/core/ContextPayloadInterceptorChain.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020
import java.util.ListIterator;
2121

22+
import org.jspecify.annotations.Nullable;
2223
import reactor.core.publisher.Mono;
2324
import reactor.util.context.Context;
2425

@@ -41,11 +42,11 @@
4142
*/
4243
class ContextPayloadInterceptorChain implements PayloadInterceptorChain {
4344

44-
private final PayloadInterceptor currentInterceptor;
45+
private final @Nullable PayloadInterceptor currentInterceptor;
4546

46-
private final ContextPayloadInterceptorChain next;
47+
private final @Nullable ContextPayloadInterceptorChain next;
4748

48-
private Context context;
49+
private @Nullable Context context;
4950

5051
ContextPayloadInterceptorChain(List<PayloadInterceptor> interceptors) {
5152
if (interceptors == null) {
@@ -68,18 +69,20 @@ private static ContextPayloadInterceptorChain init(List<PayloadInterceptor> inte
6869
return interceptor;
6970
}
7071

71-
private ContextPayloadInterceptorChain(PayloadInterceptor currentInterceptor, ContextPayloadInterceptorChain next) {
72+
private ContextPayloadInterceptorChain(@Nullable PayloadInterceptor currentInterceptor,
73+
@Nullable ContextPayloadInterceptorChain next) {
7274
this.currentInterceptor = currentInterceptor;
7375
this.next = next;
7476
}
7577

7678
@Override
79+
@SuppressWarnings("NullAway") // Dataflow analysis limitation
7780
public Mono<Void> next(PayloadExchange exchange) {
7881
return Mono.defer(() -> shouldIntercept() ? this.currentInterceptor.intercept(exchange, this.next)
7982
: Mono.deferContextual(Mono::just).cast(Context.class).doOnNext((c) -> this.context = c).then());
8083
}
8184

82-
Context getContext() {
85+
@Nullable Context getContext() {
8386
if (this.next == null) {
8487
return this.context;
8588
}

rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import org.springframework.security.rsocket.api.PayloadExchangeType;
3030
import org.springframework.security.rsocket.api.PayloadInterceptor;
31+
import org.springframework.util.Assert;
3132
import org.springframework.util.MimeType;
3233

3334
/**
@@ -91,6 +92,7 @@ public Flux<Payload> requestStream(Payload payload) {
9192
public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
9293
return Flux.from(payloads).switchOnFirst((signal, innerFlux) -> {
9394
Payload firstPayload = signal.get();
95+
Assert.notNull(firstPayload, "payload cannot be null");
9496
return intercept(PayloadExchangeType.REQUEST_CHANNEL, firstPayload)
9597
.flatMapMany((context) -> innerFlux.index()
9698
.concatMap((tuple) -> justOrIntercept(tuple.getT1(), tuple.getT2()))

rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
import io.rsocket.RSocket;
2424
import io.rsocket.SocketAcceptor;
2525
import io.rsocket.metadata.WellKnownMimeType;
26+
import org.jspecify.annotations.Nullable;
2627
import reactor.core.publisher.Mono;
2728
import reactor.util.context.Context;
2829

29-
import org.springframework.lang.Nullable;
3030
import org.springframework.security.rsocket.api.PayloadExchangeType;
3131
import org.springframework.security.rsocket.api.PayloadInterceptor;
3232
import org.springframework.util.Assert;
@@ -44,8 +44,7 @@ class PayloadSocketAcceptor implements SocketAcceptor {
4444

4545
private final List<PayloadInterceptor> interceptors;
4646

47-
@Nullable
48-
private MimeType defaultDataMimeType;
47+
private @Nullable MimeType defaultDataMimeType;
4948

5049
private MimeType defaultMetadataMimeType = MimeTypeUtils
5150
.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());
@@ -85,7 +84,7 @@ private Mono<Context> intercept(Payload payload, MimeType dataMimeType, MimeType
8584
});
8685
}
8786

88-
private MimeType parseMimeType(String str, MimeType defaultMimeType) {
87+
private @Nullable MimeType parseMimeType(String str, @Nullable MimeType defaultMimeType) {
8988
return StringUtils.hasText(str) ? MimeTypeUtils.parseMimeType(str) : defaultMimeType;
9089
}
9190

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
/**
18+
* Spring Security RSocket core integration.
19+
*/
20+
@NullMarked
21+
package org.springframework.security.rsocket.core;
22+
23+
import org.jspecify.annotations.NullMarked;

rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Map;
2020

21+
import org.jspecify.annotations.Nullable;
2122
import org.reactivestreams.Publisher;
2223
import reactor.core.publisher.Flux;
2324
import reactor.core.publisher.Mono;
@@ -44,7 +45,7 @@ public BasicAuthenticationDecoder() {
4445

4546
@Override
4647
public Flux<UsernamePasswordMetadata> decode(Publisher<DataBuffer> input, ResolvableType elementType,
47-
MimeType mimeType, Map<String, Object> hints) {
48+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
4849
return Flux.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {
4950
byte[] sizeBytes = new byte[4];
5051
byteBuffer.get(sizeBytes);
@@ -61,7 +62,7 @@ public Flux<UsernamePasswordMetadata> decode(Publisher<DataBuffer> input, Resolv
6162

6263
@Override
6364
public Mono<UsernamePasswordMetadata> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
64-
MimeType mimeType, Map<String, Object> hints) {
65+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
6566
return Mono.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {
6667
int usernameSize = byteBuffer.getInt();
6768
byte[] usernameBytes = new byte[usernameSize];

0 commit comments

Comments
 (0)