Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
ruibaby committed May 24, 2024
2 parents ee6a045 + 69c3a63 commit 39c3ba0
Show file tree
Hide file tree
Showing 35 changed files with 478 additions and 377 deletions.
7 changes: 7 additions & 0 deletions api-docs/openapi/v3_0/aggregated.json
Original file line number Diff line number Diff line change
Expand Up @@ -3109,6 +3109,13 @@
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "async",
"schema": {
"type": "boolean"
}
}
],
"responses": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public class Post extends AbstractExtension {

public static final String STATS_ANNO = "content.halo.run/stats";

/**
* <p>The key of the label that indicates that the post is scheduled to be published.</p>
* <p>Can be used to query posts that are scheduled to be published.</p>
*/
public static final String SCHEDULING_PUBLISH_LABEL = "content.halo.run/scheduling-publish";

public static final String DELETED_LABEL = "content.halo.run/deleted";
public static final String PUBLISHED_LABEL = "content.halo.run/published";
public static final String OWNER_LABEL = "content.halo.run/owner";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,11 @@
import run.halo.app.infra.AnonymousUserConst;
import run.halo.app.infra.properties.HaloProperties;
import run.halo.app.security.DefaultUserDetailService;
import run.halo.app.security.authentication.CryptoService;
import run.halo.app.security.authentication.SecurityConfigurer;
import run.halo.app.security.authentication.login.CryptoService;
import run.halo.app.security.authentication.impl.RsaKeyService;
import run.halo.app.security.authentication.login.PublicKeyRouteBuilder;
import run.halo.app.security.authentication.login.RsaKeyScheduledGenerator;
import run.halo.app.security.authentication.login.impl.RsaKeyService;
import run.halo.app.security.authentication.pat.PatAuthenticationManager;
import run.halo.app.security.authentication.pat.PatJwkSupplier;
import run.halo.app.security.authentication.pat.PatServerWebExchangeMatcher;
import run.halo.app.security.authentication.twofactor.TwoFactorAuthorizationManager;
import run.halo.app.security.authorization.RequestInfoAuthorizationManager;
Expand All @@ -58,7 +56,7 @@ SecurityWebFilterChain filterChain(ServerHttpSecurity http,
ObjectProvider<SecurityConfigurer> securityConfigurers,
ServerSecurityContextRepository securityContextRepository,
ReactiveExtensionClient client,
PatJwkSupplier patJwkSupplier,
CryptoService cryptoService,
HaloProperties haloProperties) {

http.securityMatcher(pathMatchers("/**"))
Expand All @@ -85,7 +83,7 @@ SecurityWebFilterChain filterChain(ServerHttpSecurity http,
.oauth2ResourceServer(oauth2 -> {
var authManagerResolver = builder().add(
new PatServerWebExchangeMatcher(),
new PatAuthenticationManager(client, patJwkSupplier)
new PatAuthenticationManager(client, cryptoService)
)
// TODO Add other authentication mangers here. e.g.: JwtAuthenticationManager.
.build();
Expand Down Expand Up @@ -149,8 +147,4 @@ CryptoService cryptoService(HaloProperties haloProperties) {
return new RsaKeyService(haloProperties.getWorkDir().resolve("keys"));
}

@Bean
RsaKeyScheduledGenerator rsaKeyScheduledGenerator(CryptoService cryptoService) {
return new RsaKeyScheduledGenerator(cryptoService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
import static org.springdoc.core.fn.builders.content.Builder.contentBuilder;
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
import static run.halo.app.extension.MetadataUtil.nullSafeLabels;

import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Duration;
import java.util.Objects;
import java.util.function.Predicate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springdoc.core.fn.builders.schema.Builder;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
import org.springframework.dao.OptimisticLockingFailureException;
Expand Down Expand Up @@ -199,6 +202,11 @@ public RouterFunction<ServerResponse> endpoint() {
.description("Head snapshot name of content.")
.in(ParameterIn.QUERY)
.required(false))
.parameter(parameterBuilder()
.name("async")
.in(ParameterIn.QUERY)
.implementation(Boolean.class)
.required(false))
.response(responseBuilder()
.implementation(Post.class))
)
Expand Down Expand Up @@ -319,6 +327,7 @@ Mono<ServerResponse> publishPost(ServerRequest request) {
boolean asyncPublish = request.queryParam("async")
.map(Boolean::parseBoolean)
.orElse(false);

return Mono.defer(() -> client.get(Post.class, name)
.doOnNext(post -> {
var spec = post.getSpec();
Expand All @@ -327,7 +336,6 @@ Mono<ServerResponse> publishPost(ServerRequest request) {
if (spec.getHeadSnapshot() == null) {
spec.setHeadSnapshot(spec.getBaseSnapshot());
}
// TODO Provide release snapshot query param to control
spec.setReleaseSnapshot(spec.getHeadSnapshot());
})
.flatMap(client::update)
Expand All @@ -342,12 +350,17 @@ Mono<ServerResponse> publishPost(ServerRequest request) {
}

private Mono<Post> awaitPostPublished(String postName) {
Predicate<Post> schedulePublish = post -> {
var labels = nullSafeLabels(post);
return BooleanUtils.TRUE.equals(labels.get(Post.SCHEDULING_PUBLISH_LABEL));
};
return Mono.defer(() -> client.get(Post.class, postName)
.filter(post -> {
var releasedSnapshot = MetadataUtil.nullSafeAnnotations(post)
.get(Post.LAST_RELEASED_SNAPSHOT_ANNO);
var expectReleaseSnapshot = post.getSpec().getReleaseSnapshot();
return Objects.equals(releasedSnapshot, expectReleaseSnapshot);
return Objects.equals(releasedSnapshot, expectReleaseSnapshot)
|| schedulePublish.test(post);
})
.switchIfEmpty(Mono.error(
() -> new RetryException("Retry to check post publish status"))))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package run.halo.app.core.extension.reconciler;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.lang3.BooleanUtils.TRUE;
import static org.apache.commons.lang3.BooleanUtils.isFalse;
import static org.apache.commons.lang3.BooleanUtils.isTrue;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static run.halo.app.extension.ExtensionUtil.addFinalizers;
import static run.halo.app.extension.ExtensionUtil.removeFinalizers;
import static run.halo.app.extension.MetadataUtil.nullSafeAnnotations;
import static run.halo.app.extension.MetadataUtil.nullSafeLabels;
import static run.halo.app.extension.index.query.QueryFactory.equal;

import com.google.common.hash.Hashing;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -46,6 +51,7 @@
import run.halo.app.extension.controller.Controller;
import run.halo.app.extension.controller.ControllerBuilder;
import run.halo.app.extension.controller.Reconciler;
import run.halo.app.extension.controller.RequeueException;
import run.halo.app.extension.index.query.QueryFactory;
import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.infra.Condition;
Expand Down Expand Up @@ -96,26 +102,13 @@ public Result reconcile(Request request) {
events.forEach(eventPublisher::publishEvent);
return;
}

addFinalizers(post.getMetadata(), Set.of(FINALIZER_NAME));

subscribeNewCommentNotification(post);
populateLabels(post);

var labels = post.getMetadata().getLabels();
if (labels == null) {
labels = new HashMap<>();
post.getMetadata().setLabels(labels);
}

var annotations = post.getMetadata().getAnnotations();
if (annotations == null) {
annotations = new HashMap<>();
post.getMetadata().setAnnotations(annotations);
}
schedulePublishIfNecessary(post);

if (!annotations.containsKey(Post.PUBLISHED_LABEL)) {
labels.put(Post.PUBLISHED_LABEL, BooleanUtils.FALSE);
}
subscribeNewCommentNotification(post);

var status = post.getStatus();
if (status == null) {
Expand All @@ -131,44 +124,20 @@ public Result reconcile(Request request) {
var configSha256sum = Hashing.sha256().hashString(post.getSpec().toString(), UTF_8)
.toString();

var annotations = nullSafeAnnotations(post);
var oldConfigChecksum = annotations.get(Constant.CHECKSUM_CONFIG_ANNO);
if (!Objects.equals(oldConfigChecksum, configSha256sum)) {
// if the checksum doesn't match
events.add(new PostUpdatedEvent(this, post.getMetadata().getName()));
annotations.put(Constant.CHECKSUM_CONFIG_ANNO, configSha256sum);
}

var expectDelete = defaultIfNull(post.getSpec().getDeleted(), false);
var expectPublish = defaultIfNull(post.getSpec().getPublish(), false);

if (expectDelete || !expectPublish) {
if (shouldUnPublish(post)) {
unPublishPost(post, events);
} else {
publishPost(post, events);
}

labels.put(Post.DELETED_LABEL, expectDelete.toString());

var expectVisible = defaultIfNull(post.getSpec().getVisible(), VisibleEnum.PUBLIC);
var oldVisible = VisibleEnum.from(labels.get(Post.VISIBLE_LABEL));
if (!Objects.equals(oldVisible, expectVisible)) {
eventPublisher.publishEvent(
new PostVisibleChangedEvent(request.name(), oldVisible, expectVisible));
}
labels.put(Post.VISIBLE_LABEL, expectVisible.toString());

var ownerName = post.getSpec().getOwner();
if (StringUtils.isNotBlank(ownerName)) {
labels.put(Post.OWNER_LABEL, ownerName);
}

var publishTime = post.getSpec().getPublishTime();
if (publishTime != null) {
labels.put(Post.ARCHIVE_YEAR_LABEL, HaloUtils.getYearText(publishTime));
labels.put(Post.ARCHIVE_MONTH_LABEL, HaloUtils.getMonthText(publishTime));
labels.put(Post.ARCHIVE_DAY_LABEL, HaloUtils.getDayText(publishTime));
}

var permalinkPattern = postPermalinkPolicy.pattern();
annotations.put(Constant.PERMALINK_PATTERN_ANNO, permalinkPattern);

Expand All @@ -195,7 +164,6 @@ public Result reconcile(Request request) {
status.setExcerpt(excerpt.getRaw());
}


var ref = Ref.of(post);
// handle contributors
var headSnapshot = post.getSpec().getHeadSnapshot();
Expand Down Expand Up @@ -227,19 +195,75 @@ public Result reconcile(Request request) {
return Result.doNotRetry();
}

private void populateLabels(Post post) {
var labels = nullSafeLabels(post);
labels.put(Post.DELETED_LABEL, String.valueOf(isTrue(post.getSpec().getDeleted())));

var expectVisible = defaultIfNull(post.getSpec().getVisible(), VisibleEnum.PUBLIC);
var oldVisible = VisibleEnum.from(labels.get(Post.VISIBLE_LABEL));
if (!Objects.equals(oldVisible, expectVisible)) {
var postName = post.getMetadata().getName();
eventPublisher.publishEvent(
new PostVisibleChangedEvent(postName, oldVisible, expectVisible));
}
labels.put(Post.VISIBLE_LABEL, expectVisible.toString());

var ownerName = post.getSpec().getOwner();
if (StringUtils.isNotBlank(ownerName)) {
labels.put(Post.OWNER_LABEL, ownerName);
}

var publishTime = post.getSpec().getPublishTime();
if (publishTime != null) {
labels.put(Post.ARCHIVE_YEAR_LABEL, HaloUtils.getYearText(publishTime));
labels.put(Post.ARCHIVE_MONTH_LABEL, HaloUtils.getMonthText(publishTime));
labels.put(Post.ARCHIVE_DAY_LABEL, HaloUtils.getDayText(publishTime));
}

if (!labels.containsKey(Post.PUBLISHED_LABEL)) {
labels.put(Post.PUBLISHED_LABEL, BooleanUtils.FALSE);
}
}

private static boolean shouldUnPublish(Post post) {
return isTrue(post.getSpec().getDeleted()) || isFalse(post.getSpec().getPublish());
}

@Override
public Controller setupWith(ControllerBuilder builder) {
return builder
.extension(new Post())
.onAddMatcher(DefaultExtensionMatcher.builder(client, Post.GVK)
.fieldSelector(FieldSelector.of(
equal(Post.REQUIRE_SYNC_ON_STARTUP_INDEX_NAME, BooleanUtils.TRUE))
equal(Post.REQUIRE_SYNC_ON_STARTUP_INDEX_NAME, TRUE))
)
.build()
)
.build();
}

void schedulePublishIfNecessary(Post post) {
var labels = nullSafeLabels(post);
// ensure the label is removed
labels.remove(Post.SCHEDULING_PUBLISH_LABEL);

final var now = Instant.now();
var publishTime = post.getSpec().getPublishTime();
if (post.isPublished() || publishTime == null) {
return;
}

// expect to publish in the future
if (isTrue(post.getSpec().getPublish()) && publishTime.isAfter(now)) {
labels.put(Post.SCHEDULING_PUBLISH_LABEL, TRUE);
// update post changes before requeue
client.update(post);

throw new RequeueException(Result.requeue(Duration.between(now, publishTime)),
"Requeue for scheduled publish.");
}
}

void subscribeNewCommentNotification(Post post) {
var subscriber = new Subscription.Subscriber();
subscriber.setName(post.getSpec().getOwner());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package run.halo.app.security.authentication.login;
package run.halo.app.security.authentication;

import com.nimbusds.jose.jwk.JWK;
import reactor.core.publisher.Mono;

public interface CryptoService {

/**
* Generates key pair.
*/
Mono<Void> generateKeys();

/**
* Decrypts message with Base64 format.
*
Expand All @@ -24,4 +20,18 @@ public interface CryptoService {
*/
Mono<byte[]> readPublicKey();

/**
* Gets key ID of private key.
*
* @return key ID of private key.
*/
String getKeyId();

/**
* Gets JSON Web Keys.
*
* @return JSON Web Keys
*/
JWK getJwk();

}
Loading

0 comments on commit 39c3ba0

Please sign in to comment.