mirror of https://github.com/halo-dev/halo
Prevent data conflicts caused by database case sensitivity as possible (#7371)
#### What type of PR is this? /kind improvement /area core /milestone 2.20.x #### What this PR does / why we need it: This PR use secure-strong SecureRandom to generate unpredictable metadata name. Meanwhile, the length of generate name suffix is increased to `8` and lower-case is to prevent data conflicts caused by database case sensitivity as possible. Another improvement is using bounded-elastic thread to run the method `secureString()#nextAlphanumeric` because the method contains blocking operation, which might cause system block. #### Does this PR introduce a user-facing change? ```release-note None ```pull/7376/head
parent
a94b74cb38
commit
05177544bd
|
@ -1,6 +1,6 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
import static org.apache.commons.lang3.RandomStringUtils.secureStrong;
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -31,6 +31,7 @@ import org.springframework.transaction.ReactiveTransactionManager;
|
|||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import reactor.util.retry.Retry;
|
||||
import run.halo.app.extension.availability.IndexBuildState;
|
||||
import run.halo.app.extension.exception.ExtensionNotFoundException;
|
||||
|
@ -44,6 +45,8 @@ import run.halo.app.extension.store.ReactiveExtensionStoreClient;
|
|||
@Component
|
||||
public class ReactiveExtensionClientImpl implements ReactiveExtensionClient {
|
||||
|
||||
public static final int GENERATE_NAME_RANDOM_LENGTH = 8;
|
||||
|
||||
private final ReactiveExtensionStoreClient client;
|
||||
|
||||
private final ExtensionConverter converter;
|
||||
|
@ -218,27 +221,36 @@ public class ReactiveExtensionClientImpl implements ReactiveExtensionClient {
|
|||
|
||||
@Override
|
||||
public <E extends Extension> Mono<E> create(E extension) {
|
||||
checkClientWritable(extension);
|
||||
return Mono.just(extension)
|
||||
.doOnNext(ext -> {
|
||||
var metadata = extension.getMetadata();
|
||||
// those fields should be managed by halo.
|
||||
metadata.setCreationTimestamp(Instant.now());
|
||||
metadata.setDeletionTimestamp(null);
|
||||
metadata.setVersion(null);
|
||||
return Mono.fromCallable(
|
||||
() -> {
|
||||
checkClientWritable(extension);
|
||||
var metadata = extension.getMetadata();
|
||||
// those fields should be managed by halo.
|
||||
metadata.setCreationTimestamp(Instant.now());
|
||||
metadata.setDeletionTimestamp(null);
|
||||
metadata.setVersion(null);
|
||||
|
||||
if (!hasText(metadata.getName())) {
|
||||
if (!hasText(metadata.getGenerateName())) {
|
||||
throw new IllegalArgumentException(
|
||||
"The metadata.generateName must not be blank when metadata.name is "
|
||||
+ "blank");
|
||||
if (!hasText(metadata.getName())) {
|
||||
if (!hasText(metadata.getGenerateName())) {
|
||||
throw new IllegalArgumentException(
|
||||
"The metadata.generateName must not be blank when metadata.name is "
|
||||
+ "blank");
|
||||
}
|
||||
|
||||
// generate name with random text
|
||||
// use secureStrong() to make sure the generated name is unpredictable.
|
||||
metadata.setName(metadata.getGenerateName() + secureStrong()
|
||||
.nextAlphanumeric(GENERATE_NAME_RANDOM_LENGTH)
|
||||
// Prevent data conflicts caused by database case sensitivity
|
||||
.toLowerCase()
|
||||
);
|
||||
}
|
||||
// generate name with random text
|
||||
metadata.setName(metadata.getGenerateName() + randomAlphabetic(5));
|
||||
}
|
||||
extension.setMetadata(metadata);
|
||||
})
|
||||
.map(converter::convertTo)
|
||||
extension.setMetadata(metadata);
|
||||
return converter.convertTo(extension);
|
||||
})
|
||||
// the method secureStrong() may invoke blocking SecureRandom, so we need to subscribe
|
||||
// on boundedElastic thread pool.
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMap(extStore -> doCreate(extension, extStore.getName(), extStore.getData())
|
||||
.doOnNext(created -> watchers.onAdd(convertToRealExtension(created)))
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue