Skip to content
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

fix(web2): Fixed whiteboard services unregistration and context managing #5737

Merged
merged 9 commits into from
Feb 21, 2025
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*******************************************************************************
* Copyright (c) 2023, 2025 Eurotech and/or its affiliates and others
*
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
*
* SPDX-License-Identifier: EPL-2.0
*
*
* Contributors:
* Eurotech
*******************************************************************************/
Expand All @@ -19,10 +19,6 @@
import java.util.Optional;
import java.util.Set;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import org.eclipse.kura.audit.AuditConstants;
import org.eclipse.kura.audit.AuditContext;
import org.eclipse.kura.internal.rest.auth.dto.AuthenticationInfoDTO;
Expand All @@ -42,6 +38,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
Expand Down
1 change: 1 addition & 0 deletions kura/org.eclipse.kura.web2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
<style>OBF</style> <!-- DETAILED https://support.google.com/code/answer/55203?topic=10212 -->
<failOnError>true</failOnError>
<extraJvmArgs>-Xmx2048M -Xss1024k -XX:+UseParallelGC</extraJvmArgs>
<!-- gwt-maven-plugin supports languages feature only up to java 11-->
<sourceLevel>auto</sourceLevel>
<localWorkers>2</localWorkers>
<runTarget>denali.html</runTarget>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventProperties;
import org.osgi.service.http.NamespaceException;
import org.osgi.service.servlet.context.ServletContextHelper;
import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
import org.slf4j.Logger;
Expand All @@ -96,9 +95,8 @@

public class Console implements SelfConfiguringComponent {

private static final String SESSION_CONTEXT_NAME = "sessionContext";

private static final String RESOURCE_CONTEXT_NAME = "resourceContext";
private static final String SESSION_CONTEXT_NAME_PREFIX = "sessionContext-";
private static final String RESOURCE_CONTEXT_NAME_PREFIX = "resourceContext-";

private static final String SESSION = "/session";

Expand All @@ -117,6 +115,8 @@ public class Console implements SelfConfiguringComponent {
private static final String PASSWORD_AUTH_PATH = LOGIN_MODULE_PATH + "/password";
private static final String CERT_AUTH_PATH = LOGIN_MODULE_PATH + "/cert";

private static final String XSRF_PATH = "/xsrf";

private static final Logger logger = LoggerFactory.getLogger(Console.class);

private String appRoot;
Expand All @@ -138,6 +138,9 @@ public class Console implements SelfConfiguringComponent {
private final Set<ServiceRegistration<ResourcesService>> resources = new CopyOnWriteArraySet<>();
private final Set<ServiceRegistration<Servlet>> servlets = new CopyOnWriteArraySet<>();

private final Set<String> authenticationPaths = new HashSet<>(
Arrays.asList(AUTH_PATH, PASSWORD_AUTH_PATH, CERT_AUTH_PATH));

private BundleContext bundleContext;

private static Console instance;
Expand Down Expand Up @@ -268,14 +271,11 @@ private void doUpdate(ConsoleOptions options) {
setAppRoot(options.getAppRoot());
setSessionMaxInactiveInterval(options.getSessionMaxInactivityInterval());

try {
initHTTPService();
} catch (NamespaceException e) {
logger.warn("Error Registering Web Resources", e);
}
initResourcesAndServlets();

}

protected void deactivate(BundleContext context) {
protected void deactivate() {
logger.info("deactivate...");

unregisterAll();
Expand All @@ -289,13 +289,16 @@ protected void deactivate(BundleContext context) {

private synchronized void unregisterAll() {

this.contexts.forEach(ServiceRegistration::unregister);
this.wiresBlinkService.stop();
this.eventService.stop();

this.resources.forEach(ServiceRegistration::unregister);
this.servlets.forEach(ServiceRegistration::unregister);
this.contexts.forEach(ServiceRegistration::unregister);

this.wiresBlinkService.stop();

this.eventService.stop();
this.contexts.clear();
this.resources.clear();
this.servlets.clear();

}

Expand Down Expand Up @@ -409,8 +412,6 @@ private void updateAuditContext(final HttpSession session) {

}

final Set<String> authenticationPaths = new HashSet<>(Arrays.asList(AUTH_PATH, PASSWORD_AUTH_PATH, CERT_AUTH_PATH));

private SecurityHandler createSessionHandlerChain() {

final Set<String> eventPaths = new HashSet<>(Arrays.asList(DENALI_MODULE_PATH + EVENT_PATH, "/sse"));
Expand All @@ -429,7 +430,7 @@ private SecurityHandler createSessionHandlerChain() {
final RoutingSecurityHandler routingHandler = new RoutingSecurityHandler(
defaultHandler.sendErrorOnFailure(401));

// exception on authentication paths, allow access without authenticaton but
// exception on authentication paths, allow access without authentication but
// create a session
routingHandler.addRouteHandler(this.authenticationPaths::contains,
chain(baseHandler, new CreateSessionSecurityHandler()));
Expand All @@ -446,92 +447,94 @@ private SecurityHandler createSessionHandlerChain() {
// exception on login session and xsrf path, like default but without locked
// session checking
routingHandler.addRouteHandler(
Arrays.asList(LOGIN_MODULE_PATH + SESSION, LOGIN_MODULE_PATH + "/xsrf")::contains,
Arrays.asList(LOGIN_MODULE_PATH + SESSION, LOGIN_MODULE_PATH + XSRF_PATH)::contains,
chain(baseHandler, sessionAuthHandler, sessionExpirationHandler));

return routingHandler;
}

private synchronized void initHTTPService() throws NamespaceException {
private synchronized void initResourcesAndServlets() {

this.eventService = new GwtEventServiceImpl();
this.wiresBlinkService = new WiresBlinkServlet();

ServletContextHelper resourceContextHelper = new HttpServletContextHelper(new BaseSecurityHandler());
ServletContextHelper sessionContextHelper = new HttpServletContextHelper(createSessionHandlerChain());

registerContextHelper(RESOURCE_CONTEXT_NAME, "/", resourceContextHelper, 5);
registerContextHelper(SESSION_CONTEXT_NAME, "/", sessionContextHelper, 10);
String resourceContextName = RESOURCE_CONTEXT_NAME_PREFIX + System.nanoTime();
String sessionContextName = SESSION_CONTEXT_NAME_PREFIX + System.nanoTime();

registerContextHelper(resourceContextName, "/", resourceContextHelper, 5);
registerContextHelper(sessionContextName, "/", sessionContextHelper, 10);

registerResources(ADMIN_ROOT + "/*", "www", new AdminResources(), RESOURCE_CONTEXT_NAME);
registerResources(AUTH_PATH, "www/auth.html", new AuthorizationResources(), SESSION_CONTEXT_NAME);
registerResources(CONSOLE_PATH, "www/denali.html", new ConsoleResources(), SESSION_CONTEXT_NAME);
registerResources(ADMIN_ROOT + "/*", "www", new AdminResources(), resourceContextName);
registerResources(AUTH_PATH, "www/auth.html", new AuthorizationResources(), sessionContextName);
registerResources(CONSOLE_PATH, "www/denali.html", new ConsoleResources(), sessionContextName);

registerServlet("gwtLoginInfoService", LOGIN_MODULE_PATH + "/loginInfo", new GwtLoginInfoServiceImpl(),
RESOURCE_CONTEXT_NAME);
registerServlet("redirectServlet", "/", new RedirectServlet("/"::equals, this.appRoot), RESOURCE_CONTEXT_NAME);
resourceContextName);
registerServlet("redirectServlet", "/", new RedirectServlet("/"::equals, this.appRoot), resourceContextName);

registerServlet("notFoundAuthResourceServlet", AUTH_RESOURCE_PATH, new SendStatusServlet(404),
RESOURCE_CONTEXT_NAME);
resourceContextName);
registerServlet("notFoundConsoleResourceServlet", CONSOLE_RESOURCE_PATH, new SendStatusServlet(404),
RESOURCE_CONTEXT_NAME);
resourceContextName);

registerServlet("gwtPasswordAuthenticationService", PASSWORD_AUTH_PATH,
new GwtPasswordAuthenticationServiceImpl(this.userManager, CONSOLE_PATH), SESSION_CONTEXT_NAME);
new GwtPasswordAuthenticationServiceImpl(this.userManager, CONSOLE_PATH), sessionContextName);
registerServlet("sslAuthenticationServlet", CERT_AUTH_PATH,
new SslAuthenticationServlet(CONSOLE_PATH, this.userManager), SESSION_CONTEXT_NAME);
new SslAuthenticationServlet(CONSOLE_PATH, this.userManager), sessionContextName);

registerServlet("gwtKeystoreServiceImpl", DENALI_MODULE_PATH + "/keystore", new GwtKeystoreServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("gwtSslManagerServiceImpl", DENALI_MODULE_PATH + "/ssl", new GwtSslManagerServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);

registerServlet("denaliSessionService", DENALI_MODULE_PATH + SESSION,
new GwtSessionServiceImpl(this.userManager), SESSION_CONTEXT_NAME);
new GwtSessionServiceImpl(this.userManager), sessionContextName);

registerServlet("loginSessionService", LOGIN_MODULE_PATH + SESSION, new GwtSessionServiceImpl(this.userManager),
SESSION_CONTEXT_NAME);
registerServlet("xsrfLoginServlet", LOGIN_MODULE_PATH + "/xsrf", new GwtSecurityTokenServiceImpl(),
SESSION_CONTEXT_NAME);
registerServlet("xsrfDenaliServlet", DENALI_MODULE_PATH + "/xsrf", new GwtSecurityTokenServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("xsrfLoginServlet", LOGIN_MODULE_PATH + XSRF_PATH, new GwtSecurityTokenServiceImpl(),
sessionContextName);
registerServlet("xsrfDenaliServlet", DENALI_MODULE_PATH + XSRF_PATH, new GwtSecurityTokenServiceImpl(),
sessionContextName);
registerServlet("statusService", DENALI_MODULE_PATH + "/status", new GwtStatusServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("deviceService", DENALI_MODULE_PATH + "/device", new GwtDeviceServiceImpl(),
SESSION_CONTEXT_NAME);
registerServlet("logService", DENALI_MODULE_PATH + "/logservice", new GwtLogServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("logService", DENALI_MODULE_PATH + "/logservice", new GwtLogServiceImpl(), sessionContextName);
registerServlet("networkService", DENALI_MODULE_PATH + "/network", new GwtNetworkServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("componentService", DENALI_MODULE_PATH + "/component", new GwtComponentServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("packageService", DENALI_MODULE_PATH + "/package",
new GwtPackageServiceImpl(this.sslManagerService::get), SESSION_CONTEXT_NAME);
new GwtPackageServiceImpl(this.sslManagerService::get), sessionContextName);
registerServlet("snapshotServiceImpl", DENALI_MODULE_PATH + "/snapshot", new GwtSnapshotServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("certificateService", DENALI_MODULE_PATH + "/certificate", new GwtCertificatesServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("securityService", DENALI_MODULE_PATH + "/security", new GwtSecurityServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("usersService", DENALI_MODULE_PATH + "/users", new GwtUserServiceImpl(this.userManager),
SESSION_CONTEXT_NAME);
registerServlet("fileServlet", DENALI_MODULE_PATH + "/file/*", new FileServlet(), SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("fileServlet", DENALI_MODULE_PATH + "/file/*", new FileServlet(), sessionContextName);
registerServlet("deviceSnapshotsServlet", DENALI_MODULE_PATH + "/device_snapshots",
new DeviceSnapshotsServlet(), SESSION_CONTEXT_NAME);
new DeviceSnapshotsServlet(), sessionContextName);
registerServlet("channelServlet", DENALI_MODULE_PATH + "/assetsUpDownload", new ChannelServlet(),
SESSION_CONTEXT_NAME);
registerServlet("logServlet", DENALI_MODULE_PATH + "/log", new LogServlet(), SESSION_CONTEXT_NAME);
registerServlet("skinServlet", DENALI_MODULE_PATH + "/skin/*", new SkinServlet(), RESOURCE_CONTEXT_NAME);
sessionContextName);
registerServlet("logServlet", DENALI_MODULE_PATH + "/log", new LogServlet(), sessionContextName);
registerServlet("skinServlet", DENALI_MODULE_PATH + "/skin/*", new SkinServlet(), resourceContextName);
registerServlet("cloudServices", DENALI_MODULE_PATH + "/cloudservices", new GwtCloudConnectionServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("wireGraphService", DENALI_MODULE_PATH + "/wires", new GwtWireGraphServiceImpl(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("wiresSnapshotServlet", DENALI_MODULE_PATH + "/wiresSnapshot", new WiresSnapshotServlet(),
SESSION_CONTEXT_NAME);
sessionContextName);
registerServlet("driverAndAssetService", DENALI_MODULE_PATH + "/assetservices",
new GwtDriverAndAssetServiceImpl(), SESSION_CONTEXT_NAME);
registerServlet("wiresBlinkService", ADMIN_ROOT + "/sse", this.wiresBlinkService, SESSION_CONTEXT_NAME);
registerServlet("eventService", DENALI_MODULE_PATH + EVENT_PATH, this.eventService, SESSION_CONTEXT_NAME);
new GwtDriverAndAssetServiceImpl(), sessionContextName);
registerServlet("wiresBlinkService", ADMIN_ROOT + "/sse", this.wiresBlinkService, sessionContextName);
registerServlet("eventService", DENALI_MODULE_PATH + EVENT_PATH, this.eventService, sessionContextName);

this.eventService.start();
}
Expand All @@ -548,37 +551,51 @@ private void registerContextHelper(String contextName, String contextPath, Servl
.registerService(ServletContextHelper.class, contextHelper, new Hashtable<>(props));

this.contexts.add(contextService);

logger.debug("registered context helper with id: {} , name: {} , path {}", //
contextService.getReference().getProperty(Constants.SERVICE_ID), contextName, contextPath);
}

private void registerResources(String pattern, String prefix, ResourcesService resourcesService,
String contextHelperName) {
Map<String, Object> props = new HashMap<>();

String contextHelperSelector = "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "="
+ contextHelperName + ")";

props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, pattern);
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, prefix);
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
"(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + contextHelperName + ")");
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, contextHelperSelector);

ServiceRegistration<ResourcesService> resourcesS = this.bundleContext.registerService(ResourcesService.class,
resourcesService, new Hashtable<>(props));

this.resources.add(resourcesS);

logger.debug("registered resource with id: {} , pattern: {} , prefix {} , contextHelper {}", //
resourcesS.getReference().getProperty(Constants.SERVICE_ID), pattern, prefix, contextHelperSelector);
}

private void registerServlet(String servletName, String servletPattern, HttpServlet servlet,
String contextHelperName) {

Map<String, String> props = new HashMap<>();

String contextHelperSelector = "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "="
+ contextHelperName + ")";

props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, servletName);
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, servletPattern);
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
"(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + contextHelperName + ")");
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, contextHelperSelector);

ServiceRegistration<Servlet> servletService = this.bundleContext.registerService(Servlet.class, servlet,
new Hashtable<>(props));

this.servlets.add(servletService);

logger.debug("registered servlet with id: {} , name: {} , pattern {} , contextHelper {}", //
servletService.getReference().getProperty(Constants.SERVICE_ID), servletName, servletPattern,
contextHelperSelector);
}

public interface ResourcesService {
Expand Down Expand Up @@ -609,4 +626,5 @@ public UserManager getUserManager() {
public ComponentConfiguration getConfiguration() throws KuraException {
return consoleOptions.getConfiguration();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
import java.util.Map;
import java.util.Optional;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.eclipse.kura.audit.AuditContext;
import org.eclipse.kura.audit.AuditContext.Scope;
import org.eclipse.kura.web.Console;
Expand All @@ -37,9 +33,13 @@
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class OsgiRemoteServiceServlet extends KuraRemoteServiceServlet {

private final Logger auditLogger = LoggerFactory.getLogger("AuditLogger");
private static final Logger auditLogger = LoggerFactory.getLogger("AuditLogger");

private final Optional<RequiredPermissions> servicePermissionRequirements;
private final Map<Method, RequiredPermissions> methodPermissionRequirements = new HashMap<>();
Expand Down Expand Up @@ -100,13 +100,13 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
* alternative approach.
*
* @param request
* the HTTP request being serviced
* the HTTP request being serviced
* @param moduleBaseURL
* as specified in the incoming payload
* as specified in the incoming payload
* @param strongName
* a strong name that uniquely identifies a serialization
* policy
* file
* a strong name that uniquely identifies a serialization
* policy
* file
* @return a {@link SerializationPolicy} for the given module base URL and
* strong name, or <code>null</code> if there is none
*/
Expand Down
Loading