Enable Null checking in spring-security-rsocket via JSpecify
Closes gh-16882
This commit is contained in:
parent
a4a4908d71
commit
1216ee598f
|
@ -1,3 +1,7 @@
|
|||
plugins {
|
||||
id 'security-nullability'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue