Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.condation.cms.server.filter.InitRequestContextFilter;
import com.condation.cms.server.filter.PreviewFilter;
import com.condation.cms.server.handler.StaticFileHandler;
import com.condation.cms.server.handler.WellKnownHandler;
import com.condation.cms.server.handler.auth.JettyAuthenticationHandler;
import com.condation.cms.server.handler.content.JettyContentHandler;
import com.condation.cms.server.handler.content.JettyTaxonomyHandler;
Expand All @@ -57,6 +58,7 @@
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import java.util.ArrayList;
import java.util.List;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -132,4 +134,19 @@ public ResourceHandler assetsHandler (@Named("assets") Path assetBase, ServerPro
public StaticFileHandler publicHandler (@Named("public") Path publicBase, ServerProperties serverProperties) throws IOException {
return new StaticFileHandler(List.of(publicBase));
}

@Provides
@Singleton
public WellKnownHandler wellKnownHandler (@Named("public") Path publicBase, Theme theme) throws IOException {

List<Path> paths = new ArrayList<>();
paths.add(publicBase);
paths.add(theme.publicPath());

if (theme.getParentTheme() != null) {
paths.add(theme.getParentTheme().publicPath());
}

return new WellKnownHandler(List.of(publicBase));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.condation.cms.api.Constants;
import com.condation.cms.api.feature.features.IsPreviewFeature;
import com.condation.cms.api.request.RequestContext;
import com.condation.cms.api.utils.RequestUtil;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;

Expand All @@ -37,4 +38,15 @@ protected boolean isPreview(Request request) {
var requestContext = (RequestContext) request.getAttribute(Constants.REQUEST_CONTEXT_ATTRIBUTE_NAME);
return requestContext.has(IsPreviewFeature.class);
}

protected String getRelativePath(Request request) {
String path = request.getHttpURI().getPath();
String contextPath = RequestUtil.getContextPath(request);

if (!contextPath.endsWith("/")) {
contextPath += "/";
}

return path.replaceFirst("^" + contextPath, "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,7 @@ public void failed(Throwable x) {
}
}

private String getRelativePath(Request request) {
String path = request.getHttpURI().getPath();
String contextPath = RequestUtil.getContextPath(request);

if (!contextPath.endsWith("/")) {
contextPath += "/";
}

return path.replaceFirst("^" + contextPath, "");
}

private String guessContentType(Path path) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.condation.cms.server.handler;

/*-
* #%L
* CMS Server
* %%
* Copyright (C) 2023 - 2026 CondationCMS
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import com.condation.cms.api.utils.PathUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;

@Slf4j
public class WellKnownHandler extends AbstractHandler {

private final List<Path> bases;

public WellKnownHandler(List<Path> bases) {
this.bases = bases;
}

@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {

String relativePath = getRelativePath(request);

if (!relativePath.startsWith(".well-known/")) {
return false;
}

if (relativePath.contains("..")) {
response.setStatus(400);
callback.succeeded();
return true;
}

for (Path base : bases) {
Path requested = base.resolve(relativePath).normalize();

if (!Files.exists(requested)
|| !Files.isRegularFile(requested)
|| !PathUtil.isChild(base, requested)) {
continue;
}

return serveFile(request, response, callback, requested);
}
// no found
response.setStatus(404);
callback.succeeded();

return true;
}

private boolean serveFile(Request request, Response response, Callback callback, Path requested) {

try {
response.setStatus(200);
// HEAD support
if ("HEAD".equalsIgnoreCase(request.getMethod())) {
callback.succeeded();
return true;
}

long size = Files.size(requested);
response.getHeaders().put("Content-Type", guessContentType(requested));
response.getHeaders().put("Content-Length", String.valueOf(size));

var in = Files.newInputStream(requested);

Content.copy(Content.Source.from(in), response, new Callback() {
@Override
public void succeeded() {
try {
in.close();
} catch (IOException ignore) {
}
callback.succeeded();
}

@Override
public void failed(Throwable x) {
try {
in.close();
} catch (IOException ignore) {
}
callback.failed(x);
}
});

return true;

} catch (IOException e) {
log.debug("Error serving .well-known file {}", requested, e);
callback.failed(e);
return true;
}
}

private String guessContentType(Path path) {
try {
String type = Files.probeContentType(path);
if (type != null) {
return type;
}

String p = path.toString();
if (p.endsWith(".json")) {
return "application/json";
}
if (p.endsWith(".txt")) {
return "text/plain";
}

return "application/octet-stream";
} catch (IOException e) {
return "application/octet-stream";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import com.condation.cms.server.annotations.Eager;
import com.condation.cms.server.filter.metrics.PipelineRequestMetricsFilter;
import com.condation.cms.server.filter.metrics.RequestMetricsFilter;
import com.condation.cms.server.handler.WellKnownHandler;
import io.micrometer.core.instrument.MeterRegistry;

/**
Expand Down Expand Up @@ -269,6 +270,7 @@ public Handler buildHttpHandler() {
var defaultHandlerSequence = new Handler.Sequence(
authHandler,
publicHandlerSequence,
injector.getInstance(WellKnownHandler.class),
initContextHandler,
uiPreviewFilter,
routesHandler,
Expand Down
1 change: 1 addition & 0 deletions test-server/hosts/demo/public/.well-known/security.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CondationCMS is secure