Преглед изворни кода

Enable Null checking in spring-security-rsocket via JSpecify

Closes gh-16882
Rob Winch пре 1 недеља
родитељ
комит
1216ee598f
16 измењених фајлова са 176 додато и 21 уклоњено
  1. 4 0
      rsocket/spring-security-rsocket.gradle
  2. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/api/package-info.java
  3. 2 1
      rsocket/src/main/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadExchangeConverter.java
  4. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/authentication/package-info.java
  5. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/authorization/package-info.java
  6. 8 5
      rsocket/src/main/java/org/springframework/security/rsocket/core/ContextPayloadInterceptorChain.java
  7. 2 0
      rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java
  8. 3 4
      rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java
  9. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/core/package-info.java
  10. 3 2
      rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoder.java
  11. 4 2
      rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationEncoder.java
  12. 4 2
      rsocket/src/main/java/org/springframework/security/rsocket/metadata/BearerTokenAuthenticationEncoder.java
  13. 4 2
      rsocket/src/main/java/org/springframework/security/rsocket/metadata/SimpleAuthenticationEncoder.java
  14. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/metadata/package-info.java
  15. 4 3
      rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatcher.java
  16. 23 0
      rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/package-info.java

+ 4 - 0
rsocket/spring-security-rsocket.gradle

@@ -1,3 +1,7 @@
+plugins {
+	id 'security-nullability'
+}
+
 apply plugin: 'io.spring.convention.spring-module'
 
 dependencies {

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/api/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket APIs.
+ */
+@NullMarked
+package org.springframework.security.rsocket.api;
+
+import org.jspecify.annotations.NullMarked;

+ 2 - 1
rsocket/src/main/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadExchangeConverter.java

@@ -24,6 +24,7 @@ import io.netty.buffer.ByteBufAllocator;
 import io.rsocket.metadata.AuthMetadataCodec;
 import io.rsocket.metadata.WellKnownAuthType;
 import io.rsocket.metadata.WellKnownMimeType;
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 
 import org.springframework.core.codec.ByteArrayDecoder;
@@ -66,7 +67,7 @@ public class AuthenticationPayloadExchangeConverter implements PayloadExchangeAu
 			.flatMap((metadata) -> Mono.justOrEmpty(authentication(metadata)));
 	}
 
-	private Authentication authentication(Map<String, Object> metadata) {
+	private @Nullable Authentication authentication(Map<String, Object> metadata) {
 		byte[] authenticationMetadata = (byte[]) metadata.get("authentication");
 		if (authenticationMetadata == null) {
 			return null;

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/authentication/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket Authentication integration.
+ */
+@NullMarked
+package org.springframework.security.rsocket.authentication;
+
+import org.jspecify.annotations.NullMarked;

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/authorization/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket authorization integration.
+ */
+@NullMarked
+package org.springframework.security.rsocket.authorization;
+
+import org.jspecify.annotations.NullMarked;

+ 8 - 5
rsocket/src/main/java/org/springframework/security/rsocket/core/ContextPayloadInterceptorChain.java

@@ -19,6 +19,7 @@ package org.springframework.security.rsocket.core;
 import java.util.List;
 import java.util.ListIterator;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 import reactor.util.context.Context;
 
@@ -41,11 +42,11 @@ import org.springframework.security.rsocket.api.PayloadInterceptorChain;
  */
 class ContextPayloadInterceptorChain implements PayloadInterceptorChain {
 
-	private final PayloadInterceptor currentInterceptor;
+	private final @Nullable PayloadInterceptor currentInterceptor;
 
-	private final ContextPayloadInterceptorChain next;
+	private final @Nullable ContextPayloadInterceptorChain next;
 
-	private Context context;
+	private @Nullable Context context;
 
 	ContextPayloadInterceptorChain(List<PayloadInterceptor> interceptors) {
 		if (interceptors == null) {
@@ -68,18 +69,20 @@ class ContextPayloadInterceptorChain implements PayloadInterceptorChain {
 		return interceptor;
 	}
 
-	private ContextPayloadInterceptorChain(PayloadInterceptor currentInterceptor, ContextPayloadInterceptorChain next) {
+	private ContextPayloadInterceptorChain(@Nullable PayloadInterceptor currentInterceptor,
+			@Nullable ContextPayloadInterceptorChain next) {
 		this.currentInterceptor = currentInterceptor;
 		this.next = next;
 	}
 
 	@Override
+	@SuppressWarnings("NullAway") // Dataflow analysis limitation
 	public Mono<Void> next(PayloadExchange exchange) {
 		return Mono.defer(() -> shouldIntercept() ? this.currentInterceptor.intercept(exchange, this.next)
 				: Mono.deferContextual(Mono::just).cast(Context.class).doOnNext((c) -> this.context = c).then());
 	}
 
-	Context getContext() {
+	@Nullable Context getContext() {
 		if (this.next == null) {
 			return this.context;
 		}

+ 2 - 0
rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java

@@ -28,6 +28,7 @@ import reactor.util.context.Context;
 
 import org.springframework.security.rsocket.api.PayloadExchangeType;
 import org.springframework.security.rsocket.api.PayloadInterceptor;
+import org.springframework.util.Assert;
 import org.springframework.util.MimeType;
 
 /**
@@ -91,6 +92,7 @@ class PayloadInterceptorRSocket extends RSocketProxy {
 	public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
 		return Flux.from(payloads).switchOnFirst((signal, innerFlux) -> {
 			Payload firstPayload = signal.get();
+			Assert.notNull(firstPayload, "payload cannot be null");
 			return intercept(PayloadExchangeType.REQUEST_CHANNEL, firstPayload)
 				.flatMapMany((context) -> innerFlux.index()
 					.concatMap((tuple) -> justOrIntercept(tuple.getT1(), tuple.getT2()))

+ 3 - 4
rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java

@@ -23,10 +23,10 @@ import io.rsocket.Payload;
 import io.rsocket.RSocket;
 import io.rsocket.SocketAcceptor;
 import io.rsocket.metadata.WellKnownMimeType;
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 import reactor.util.context.Context;
 
-import org.springframework.lang.Nullable;
 import org.springframework.security.rsocket.api.PayloadExchangeType;
 import org.springframework.security.rsocket.api.PayloadInterceptor;
 import org.springframework.util.Assert;
@@ -44,8 +44,7 @@ class PayloadSocketAcceptor implements SocketAcceptor {
 
 	private final List<PayloadInterceptor> interceptors;
 
-	@Nullable
-	private MimeType defaultDataMimeType;
+	private @Nullable MimeType defaultDataMimeType;
 
 	private MimeType defaultMetadataMimeType = MimeTypeUtils
 		.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());
@@ -85,7 +84,7 @@ class PayloadSocketAcceptor implements SocketAcceptor {
 		});
 	}
 
-	private MimeType parseMimeType(String str, MimeType defaultMimeType) {
+	private @Nullable MimeType parseMimeType(String str, @Nullable MimeType defaultMimeType) {
 		return StringUtils.hasText(str) ? MimeTypeUtils.parseMimeType(str) : defaultMimeType;
 	}
 

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/core/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket core integration.
+ */
+@NullMarked
+package org.springframework.security.rsocket.core;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 2
rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoder.java

@@ -18,6 +18,7 @@ package org.springframework.security.rsocket.metadata;
 
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -44,7 +45,7 @@ public class BasicAuthenticationDecoder extends AbstractDecoder<UsernamePassword
 
 	@Override
 	public Flux<UsernamePasswordMetadata> decode(Publisher<DataBuffer> input, ResolvableType elementType,
-			MimeType mimeType, Map<String, Object> hints) {
+			@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
 		return Flux.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {
 			byte[] sizeBytes = new byte[4];
 			byteBuffer.get(sizeBytes);
@@ -61,7 +62,7 @@ public class BasicAuthenticationDecoder extends AbstractDecoder<UsernamePassword
 
 	@Override
 	public Mono<UsernamePasswordMetadata> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
-			MimeType mimeType, Map<String, Object> hints) {
+			@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
 		return Mono.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {
 			int usernameSize = byteBuffer.getInt();
 			byte[] usernameBytes = new byte[usernameSize];

+ 4 - 2
rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationEncoder.java

@@ -20,6 +20,7 @@ import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Flux;
 
@@ -47,14 +48,15 @@ public class BasicAuthenticationEncoder extends AbstractEncoder<UsernamePassword
 
 	@Override
 	public Flux<DataBuffer> encode(Publisher<? extends UsernamePasswordMetadata> inputStream,
-			DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
+			DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,
+			@Nullable Map<String, Object> hints) {
 		return Flux.from(inputStream)
 			.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));
 	}
 
 	@Override
 	public DataBuffer encodeValue(UsernamePasswordMetadata credentials, DataBufferFactory bufferFactory,
-			ResolvableType valueType, MimeType mimeType, Map<String, Object> hints) {
+			ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
 		String username = credentials.getUsername();
 		String password = credentials.getPassword();
 		byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);

+ 4 - 2
rsocket/src/main/java/org/springframework/security/rsocket/metadata/BearerTokenAuthenticationEncoder.java

@@ -21,6 +21,7 @@ import java.util.Map;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.rsocket.metadata.AuthMetadataCodec;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Flux;
 
@@ -53,14 +54,15 @@ public class BearerTokenAuthenticationEncoder extends AbstractEncoder<BearerToke
 
 	@Override
 	public Flux<DataBuffer> encode(Publisher<? extends BearerTokenMetadata> inputStream,
-			DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
+			DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,
+			@Nullable Map<String, Object> hints) {
 		return Flux.from(inputStream)
 			.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));
 	}
 
 	@Override
 	public DataBuffer encodeValue(BearerTokenMetadata credentials, DataBufferFactory bufferFactory,
-			ResolvableType valueType, MimeType mimeType, Map<String, Object> hints) {
+			ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
 		String token = credentials.getToken();
 		NettyDataBufferFactory factory = nettyFactory(bufferFactory);
 		ByteBufAllocator allocator = factory.getByteBufAllocator();

+ 4 - 2
rsocket/src/main/java/org/springframework/security/rsocket/metadata/SimpleAuthenticationEncoder.java

@@ -21,6 +21,7 @@ import java.util.Map;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.rsocket.metadata.AuthMetadataCodec;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Flux;
 
@@ -53,14 +54,15 @@ public class SimpleAuthenticationEncoder extends AbstractEncoder<UsernamePasswor
 
 	@Override
 	public Flux<DataBuffer> encode(Publisher<? extends UsernamePasswordMetadata> inputStream,
-			DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
+			DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,
+			@Nullable Map<String, Object> hints) {
 		return Flux.from(inputStream)
 			.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));
 	}
 
 	@Override
 	public DataBuffer encodeValue(UsernamePasswordMetadata credentials, DataBufferFactory bufferFactory,
-			ResolvableType valueType, MimeType mimeType, Map<String, Object> hints) {
+			ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
 		String username = credentials.getUsername();
 		String password = credentials.getPassword();
 		NettyDataBufferFactory factory = nettyFactory(bufferFactory);

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/metadata/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket metadata integration.
+ */
+@NullMarked
+package org.springframework.security.rsocket.metadata;
+
+import org.jspecify.annotations.NullMarked;

+ 4 - 3
rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatcher.java

@@ -20,6 +20,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 
 import org.springframework.security.rsocket.api.PayloadExchange;
@@ -46,9 +47,9 @@ public interface PayloadExchangeMatcher {
 
 		private final boolean match;
 
-		private final Map<String, Object> variables;
+		private final @Nullable Map<String, Object> variables;
 
-		private MatchResult(boolean match, Map<String, Object> variables) {
+		private MatchResult(boolean match, @Nullable Map<String, Object> variables) {
 			this.match = match;
 			this.variables = variables;
 		}
@@ -61,7 +62,7 @@ public interface PayloadExchangeMatcher {
 		 * Gets potential variables and their values
 		 * @return
 		 */
-		public Map<String, Object> getVariables() {
+		public @Nullable Map<String, Object> getVariables() {
 			return this.variables;
 		}
 

+ 23 - 0
rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/package-info.java

@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Spring Security RSocket matching APIs.
+ */
+@NullMarked
+package org.springframework.security.rsocket.util.matcher;
+
+import org.jspecify.annotations.NullMarked;