This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
The following commit(s) were added to refs/heads/master by this push:
new 93e0dd81b4 FELIX-6626 : Support jakarta servlet registration
93e0dd81b4 is described below
commit 93e0dd81b4c22ec4f72d00f1437c136ce6f30ad7
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Tue Aug 22 14:55:42 2023 +0200
FELIX-6626 : Support jakarta servlet registration
---
.../felix/webconsole/AbstractWebConsolePlugin.java | 3 +-
.../internal/configuration/ConfigManager.java | 2 +-
...vletTracker.java => JakartaServiceTracker.java} | 103 ++++++++++++++++++++-
.../webconsole/internal/servlet/PluginHolder.java | 2 +-
.../felix/webconsole/servlet/AbstractServlet.java | 3 +
.../felix/webconsole/spi/SecurityProvider.java | 84 +++++++++++++++++
6 files changed, 191 insertions(+), 6 deletions(-)
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
index 557e943166..9183a28804 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -135,6 +135,7 @@ public abstract class AbstractWebConsolePlugin extends
HttpServlet {
private BundleContext bundleContext;
+ @SuppressWarnings("deprecation")
private static volatile BrandingPlugin brandingPlugin =
DefaultBrandingPlugin.getInstance();
private static volatile int logLevel;
@@ -670,7 +671,7 @@ public abstract class AbstractWebConsolePlugin extends
HttpServlet {
* @param request the HTTP request coming from the user
* @param pw the writer, where the HTML data is rendered
*/
- @SuppressWarnings({ "rawtypes" })
+ @SuppressWarnings({ "rawtypes", "deprecation" })
protected void renderTopNavigation( HttpServletRequest request,
PrintWriter pw )
{
// assume pathInfo to not be null, else this would not be called
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
index f358fce785..5233dc621a 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
@@ -411,7 +411,7 @@ public class ConfigManager extends SimpleWebConsolePlugin
implements OsgiManager
jw.object();
final ConfigAdminSupport cas = getConfigurationAdminSupport();
// check for osgi installer plugin
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "deprecation" })
final Map<String, Object> labelMap = (Map<String, Object>)
request.getAttribute(WebConsoleConstants.ATTR_LABEL_MAP);
jw.key("jsonsupport").value(
labelMap.containsKey("osgi-installer-config-printer") ); //$NON-NLS-1$
final boolean hasMetatype = cas.getMetaTypeSupport() != null;
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServiceTracker.java
similarity index 50%
rename from
webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
rename to
webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServiceTracker.java
index 7a9445a311..661b104df3 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServiceTracker.java
@@ -21,29 +21,41 @@ package org.apache.felix.webconsole.internal.servlet;
import java.io.Closeable;
import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.http.jakartawrappers.HttpServletRequestWrapper;
+import org.apache.felix.http.jakartawrappers.HttpServletResponseWrapper;
import org.apache.felix.http.javaxwrappers.ServletWrapper;
import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.felix.webconsole.WebConsoleSecurityProvider3;
import org.apache.felix.webconsole.internal.Util;
import org.apache.felix.webconsole.internal.servlet.Plugin.ServletPlugin;
import org.apache.felix.webconsole.servlet.AbstractServlet;
+import org.apache.felix.webconsole.spi.SecurityProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import jakarta.servlet.Servlet;
-public class JakartaServletTracker implements Closeable,
ServiceTrackerCustomizer<Servlet, JakartaServletTracker.JakartaServletPlugin> {
+public class JakartaServiceTracker implements Closeable,
ServiceTrackerCustomizer<Servlet, JakartaServiceTracker.JakartaServletPlugin> {
private final ServiceTracker<Servlet, JakartaServletPlugin> servletTracker;
private final PluginHolder pluginHolder;
- public JakartaServletTracker( final PluginHolder pluginHolder, final
BundleContext context ) {
+ private final JakartaSecurityProviderTracker securityProviderTracker;
+
+ public JakartaServiceTracker( final PluginHolder pluginHolder, final
BundleContext context ) {
this.pluginHolder = pluginHolder;
Filter filter = null;
try {
@@ -55,11 +67,13 @@ public class JakartaServletTracker implements Closeable,
ServiceTrackerCustomize
}
this.servletTracker = new ServiceTracker<>(context, filter, this);
servletTracker.open();
+ this.securityProviderTracker = new
JakartaSecurityProviderTracker(context);
}
@Override
public void close() {
- servletTracker.close();
+ this.servletTracker.close();
+ this.securityProviderTracker.close();
}
@Override
@@ -115,4 +129,87 @@ public class JakartaServletTracker implements Closeable,
ServiceTrackerCustomize
return null;
}
}
+
+ public static class JakartaSecurityProviderTracker implements
ServiceTrackerCustomizer<SecurityProvider,
ServiceRegistration<WebConsoleSecurityProvider3>> {
+
+ private final ServiceTracker<SecurityProvider,
ServiceRegistration<WebConsoleSecurityProvider3>> tracker;
+
+ private final BundleContext bundleContext;
+
+ public JakartaSecurityProviderTracker( final BundleContext context ) {
+ this.bundleContext = context;
+ this.tracker = new ServiceTracker<>(context,
SecurityProvider.class, this);
+ tracker.open();
+ }
+
+ public void close() {
+ tracker.close();
+ }
+
+ @Override
+ public ServiceRegistration<WebConsoleSecurityProvider3> addingService(
final ServiceReference<SecurityProvider> reference ) {
+ final SecurityProvider provider =
this.bundleContext.getService(reference);
+ if ( provider != null ) {
+ final JakartaSecurityProvider jakartaSecurityProvider = new
JakartaSecurityProvider(provider);
+ final Dictionary<String, Object> props = new Hashtable<>();
+ if (reference.getProperty(Constants.SERVICE_RANKING) != null) {
+ props.put(Constants.SERVICE_RANKING,
reference.getProperty(Constants.SERVICE_RANKING));
+ }
+ final ServiceRegistration<WebConsoleSecurityProvider3> reg =
this.bundleContext.registerService(WebConsoleSecurityProvider3.class,
jakartaSecurityProvider, props);
+ return reg;
+ }
+ return null;
+ }
+
+ @Override
+ public void modifiedService( final ServiceReference<SecurityProvider>
reference, final ServiceRegistration<WebConsoleSecurityProvider3> service ) {
+ // nothing to do
+ }
+
+ @Override
+ public void removedService( final ServiceReference<SecurityProvider>
reference, final ServiceRegistration<WebConsoleSecurityProvider3> service ) {
+ this.bundleContext.ungetService(reference);
+ try {
+ service.unregister();
+ } catch ( final IllegalStateException ise ) {
+ // ignore
+ }
+ }
+ }
+
+ public static class JakartaSecurityProvider implements
WebConsoleSecurityProvider3 {
+
+ private final SecurityProvider provider;
+
+ public JakartaSecurityProvider(final SecurityProvider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public void logout(final HttpServletRequest request, final
HttpServletResponse response) {
+
this.provider.logout((jakarta.servlet.http.HttpServletRequest)HttpServletRequestWrapper.getWrapper(request),
+
(jakarta.servlet.http.HttpServletResponse)HttpServletResponseWrapper.getWrapper(response));
+ }
+
+ @Override
+ public boolean authenticate(final HttpServletRequest request, final
HttpServletResponse response) {
+ final Object user =
this.provider.authenticate((jakarta.servlet.http.HttpServletRequest)HttpServletRequestWrapper.getWrapper(request),
+
(jakarta.servlet.http.HttpServletResponse)HttpServletResponseWrapper.getWrapper(response));
+ if (user != null) {
+
request.setAttribute(WebConsoleSecurityProvider3.USER_ATTRIBUTE, user);
+ }
+ return user != null;
+ }
+
+ @Override
+ public Object authenticate(final String username, final String
password) {
+ // no need to implement
+ return null;
+ }
+
+ @Override
+ public boolean authorize(final Object user, final String role) {
+ return this.provider.authorize(user, role);
+ }
+ }
}
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
index 86c91930c3..d8e6bcafce 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
@@ -98,7 +98,7 @@ class PluginHolder implements
ServiceTrackerCustomizer<Servlet, Plugin> {
void open() {
this.servletTracker.open();
try {
- this.jakartaTracker = new JakartaServletTracker(this,
this.getBundleContext());
+ this.jakartaTracker = new JakartaServiceTracker(this,
this.getBundleContext());
this.osgiManager.log(LogService.LOG_INFO, "Jakarta Servlet bridge
enabled");
} catch ( final Throwable t) {
// ignore
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
index 23f2f3f88b..bd7ff0d1d5 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
@@ -46,6 +46,9 @@ import jakarta.servlet.http.HttpServletResponse;
* <p>
* For html (content) requests, the web console automatically draws the header,
* footer and navigation.
+ * <p>
+ * Support for Jakarta servlets requires that the Jakarta Servlet API and the
+ * Apache Felix Http Wrappers are available in the runtime.
*
* @see ServletConstants#PLUGIN_LABEL
* @see ServletConstants#PLUGIN_TITLE
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/spi/SecurityProvider.java
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/SecurityProvider.java
new file mode 100644
index 0000000000..2546aae0ea
--- /dev/null
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/SecurityProvider.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.webconsole.spi;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * The <code>SecurityProvider</code> is a service interface allowing
+ * to use an external system to authenticate users before granting access to
the
+ * Web Console.
+ * <p>
+ * Support for Jakarta servlets requires that the Jakarta Servlet API and the
+ * Apache Felix Http Wrappers are available in the runtime.
+ *
+ * @since 1.2.0
+ */
+@ConsumerType
+public interface SecurityProvider {
+
+ /**
+ * Checks whether the authenticated user has the given role permission.
+ *
+ * @param user The object referring to the authenticated user. This is the
+ * object returned from the {@link #authenticate(HttpServletRequest,
HttpServletResponse)}
+ * method and will never be <code>null</code>.
+ * @param role The requested role
+ * @return <code>true</code> if the user is given permission for the given
+ * role.
+ */
+ boolean authorize( Object user, String role );
+
+ /**
+ * Authenticates the given request or asks the client for credentials.
+ * <p>
+ * Implementations of this method are expected to respect and implement
+ * the semantics of the <code>ServletContextHelper.handleSecurity</code>
method
+ * as specified in the OSGi HTTP Service specification.
+ * <p>
+ * If this method returns an object (non null) it is assumed the request
+ * provided valid credentials identifying the user as accepted to access
+ * the web console.
+ * <p>
+ * If this method returns {@code null} the request to the web console
+ * is terminated without any more response sent back to the client. That is
+ * the implementation is expected to have informed the client in case of
+ * non-granted access.
+ *
+ * @param request The request object
+ * @param response The response object
+ * @return An object representing the user if the request provided valid
credentials.
+ * Otherwise return {@code null}.
+ */
+ Object authenticate( HttpServletRequest request, HttpServletResponse
response );
+
+ /**
+ * This method will be called by the web console when the user clicks the
logout button. The security provider
+ * shouldn't invalidate the session, it will be invalidated after this
method exits.
+ *
+ * However the security provider must delete any cookies or objects, that
matters during the authorization process.
+ *
+ * @param request the request
+ * @param response the response
+ */
+ void logout(HttpServletRequest request, HttpServletResponse response);
+}