-
Notifications
You must be signed in to change notification settings - Fork 0
Features/thymeleaf module #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve | ||
| * Copyright (C) 2013-2025 SteVe Community Team | ||
| * All Rights Reserved. | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU 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 General Public License | ||
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| */ | ||
| package de.rwth.idsg.steve; | ||
|
|
||
| import org.eclipse.jetty.ee10.webapp.WebAppContext; | ||
|
|
||
| public interface JettyCustomizer { | ||
| void configure(WebAppContext ctx); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -48,6 +48,14 @@ public static class Paths { | |||||||||||||||||||||||||
| private final String routerEndpointPath; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private final @Nullable String contextPath; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public String getLocation() { | ||||||||||||||||||||||||||
| return contextPath + managerMapping + "/home"; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle nullable contextPath and slash normalization in getLocation(). Current code risks "null..." and double slashes. - public String getLocation() {
- return contextPath + managerMapping + "/home";
- }
+ public String getLocation() {
+ String ctx = (contextPath == null || "/".equals(contextPath)) ? "" : contextPath;
+ if (!managerMapping.startsWith("/")) {
+ // Ensure managerMapping is absolute
+ ctx = ctx + "/";
+ }
+ String base = (ctx.endsWith("/")) ? ctx.substring(0, ctx.length() - 1) : ctx;
+ return base + managerMapping + "/home";
+ }Add unit tests for contextPath = null, "", and "/foo".
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| public String getWsPathInfix() { | ||||||||||||||||||||||||||
| return routerEndpointPath + websocketMapping; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+56
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Normalize join for WebSocket path infix. Avoid missing or duplicate slashes. - public String getWsPathInfix() {
- return routerEndpointPath + websocketMapping;
- }
+ public String getWsPathInfix() {
+ String left = routerEndpointPath.endsWith("/") ? routerEndpointPath.substring(0, routerEndpointPath.length()-1) : routerEndpointPath;
+ String right = websocketMapping.startsWith("/") ? websocketMapping : "/" + websocketMapping;
+ return left + right;
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private final Paths paths; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| package de.rwth.idsg.steve.config; | ||
|
|
||
| import com.google.common.collect.Lists; | ||
| import de.rwth.idsg.steve.SteveConfiguration; | ||
| import de.rwth.idsg.steve.ocpp.ws.OcppWebSocketHandshakeHandler; | ||
| import de.rwth.idsg.steve.ocpp.ws.ocpp12.Ocpp12WebSocketEndpoint; | ||
| import de.rwth.idsg.steve.ocpp.ws.ocpp15.Ocpp15WebSocketEndpoint; | ||
|
|
@@ -45,13 +46,13 @@ | |
| @RequiredArgsConstructor | ||
| public class OcppWebSocketConfiguration implements WebSocketConfigurer { | ||
|
|
||
| public static final String PATH_INFIX = "/websocket/CentralSystemService/"; | ||
| public static final Duration PING_INTERVAL = Duration.ofMinutes(15); | ||
| public static final Duration IDLE_TIMEOUT = Duration.ofHours(2); | ||
| public static final int MAX_MSG_SIZE = 8_388_608; // 8 MB for max message size | ||
| public static final Duration WS_PING_INTERVAL = Duration.ofMinutes(15); | ||
| public static final Duration WS_IDLE_TIMEOUT = Duration.ofHours(2); | ||
| public static final int WS_MAX_MSG_SIZE = 8_388_608; // 8 MB for max message size | ||
|
|
||
| private final ChargePointRegistrationService chargePointRegistrationService; | ||
| private final ChargeBoxIdValidator chargeBoxIdValidator; | ||
| private final SteveConfiguration config; | ||
|
|
||
| private final Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint; | ||
| private final Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint; | ||
|
|
@@ -60,13 +61,16 @@ public class OcppWebSocketConfiguration implements WebSocketConfigurer { | |
| @Override | ||
| public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { | ||
|
|
||
| OcppWebSocketHandshakeHandler handshakeHandler = new OcppWebSocketHandshakeHandler( | ||
| var handshakeHandler = new OcppWebSocketHandshakeHandler( | ||
| chargeBoxIdValidator, | ||
| new DefaultHandshakeHandler(), | ||
| Lists.newArrayList(ocpp16WebSocketEndpoint, ocpp15WebSocketEndpoint, ocpp12WebSocketEndpoint), | ||
| chargePointRegistrationService); | ||
| chargePointRegistrationService, | ||
| config.getPaths().getWsPathInfix() + "/"); | ||
|
|
||
| registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), PATH_INFIX + "*") | ||
| registry.addHandler( | ||
| handshakeHandler.getDummyWebSocketHandler(), | ||
| config.getPaths().getWsPathInfix() + "/*") | ||
| .setHandshakeHandler(handshakeHandler) | ||
| .setAllowedOrigins("*"); | ||
| } | ||
|
Comment on lines
+71
to
76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Restrict WebSocket allowed origins. setAllowedOrigins("*") enables any origin. Prefer explicit origins or config-driven patterns. - .setAllowedOrigins("*");
+ // Prefer patterns or a configured list
+ .setAllowedOriginPatterns(config.getSecurity().getAllowedWsOrigins());If no such config exists, introduce one or restrict to known UI origins.
🤖 Prompt for AI Agents |
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,7 +19,6 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package de.rwth.idsg.steve.ocpp.ws; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.google.common.base.Strings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import de.rwth.idsg.steve.config.OcppWebSocketConfiguration; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import de.rwth.idsg.steve.service.ChargePointRegistrationService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import de.rwth.idsg.steve.web.validation.ChargeBoxIdValidator; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -51,6 +50,7 @@ public class OcppWebSocketHandshakeHandler implements HandshakeHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final DefaultHandshakeHandler delegate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final List<AbstractWebSocketEndpoint> endpoints; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final ChargePointRegistrationService chargePointRegistrationService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final String wsPathInfix; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * We need some WebSocketHandler just for Spring to register it for the path. We will not use it for the actual | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -73,7 +73,7 @@ public boolean doHandshake( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. Check the chargeBoxId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var chargeBoxId = getLastBitFromUrl(request.getURI().getPath()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var chargeBoxId = getLastBitFromUrl(wsPathInfix, request.getURI().getPath()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var isValid = chargeBoxIdValidator.isValid(chargeBoxId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isValid) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error("ChargeBoxId '{}' violates the configured pattern.", chargeBoxId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -133,17 +133,15 @@ public boolean doHandshake( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static String getLastBitFromUrl(final String input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static String getLastBitFromUrl(String path, String input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Strings.isNullOrEmpty(input)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var substring = OcppWebSocketConfiguration.PATH_INFIX; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var index = input.indexOf(substring); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var index = input.indexOf(path); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (index == -1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return input.substring(index + substring.length()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return input.substring(index + path.length()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+136
to
146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getLastBitFromUrl returns more than the last segment when extra path parts exist Ensure only the next segment after the infix is returned (the chargeBoxId), ignoring trailing segments. - public static String getLastBitFromUrl(String path, String input) {
+ public static String getLastBitFromUrl(String path, String input) {
if (Strings.isNullOrEmpty(input)) {
return "";
}
-
- var index = input.indexOf(path);
- if (index == -1) {
- return "";
- }
- return input.substring(index + path.length());
+ int start = input.indexOf(path);
+ if (start == -1) {
+ return "";
+ }
+ int from = start + path.length();
+ // Skip any leading slash just in case
+ if (from < input.length() && input.charAt(from) == '/') {
+ from++;
+ }
+ int next = input.indexOf('/', from);
+ return next == -1 ? input.substring(from) : input.substring(from, next);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Don’t pin a logging backend in core; make logback non-transitive
Core libraries should depend on slf4j-api only. Make logback optional so downstream runnable modules decide the backend.
Apply:
🏁 Script executed:
Length of output: 1065
🏁 Script executed:
Length of output: 210
Mark logback-classic as optional in steve-core/pom.xml
Replace the direct logback binding so core only brings in slf4j-api by default:
📝 Committable suggestion
🤖 Prompt for AI Agents