You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2020/12/12 13:36:14 UTC

[felix-dev] branch master updated: FELIX-6370 : Provide a User interface for checks within plugins

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 00be08c  FELIX-6370 : Provide a User interface for checks within plugins
00be08c is described below

commit 00be08cb16669f34d763eb38b9fe11677504dbfe
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sat Dec 12 14:36:19 2020 +0100

    FELIX-6370 : Provide a User interface for checks within plugins
---
 webconsole/changelog.txt                           |   1 +
 webconsole/pom.xml                                 |  21 +++-
 .../java/org/apache/felix/webconsole/User.java     |  53 +++++++++
 .../webconsole/WebConsoleSecurityProvider.java     |   2 +-
 .../felix/webconsole/bundleinfo/package-info.java  |  21 ++++
 .../apache/felix/webconsole/i18n/package-info.java |  21 ++++
 .../webconsole/internal/servlet/OsgiManager.java   | 121 +++++++++------------
 .../internal/servlet/OsgiManagerHttpContext.java   |  80 ++++++++------
 .../org/apache/felix/webconsole/package-info.java  |  21 ++++
 .../servlet/OsgiManagerHttpContextTest.java        |   4 +-
 .../internal/servlet/OsgiManagerTest.java          |   5 +-
 11 files changed, 241 insertions(+), 109 deletions(-)

diff --git a/webconsole/changelog.txt b/webconsole/changelog.txt
index 5330860..46194e1 100644
--- a/webconsole/changelog.txt
+++ b/webconsole/changelog.txt
@@ -3,6 +3,7 @@ Changes in 4.5.6
 ** Improvement
     * [FELIX-6366] - Update to jQuery 3.5.1 and jQuery migrate 3.3.0
     * [FELIX-6363] - Simplify updating of OSGi configurations through REST API
+    * [FELIX-6370] - Provide a User interface for checks within plugins
 ** Bug
     * [FELIX-6341] - ConfigAdmin - deleting a configuration logs a string that should be translated
     * [FELIX-6328] - Web Console (All In One) imports javax.portlet via fileupload
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 065b29b..e4b272f 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -39,9 +39,9 @@
     
     <properties>
         <webconsole.exports>
-            org.apache.felix.webconsole;version=3.3.0;provide:=true,
-            org.apache.felix.webconsole.bundleinfo;version=1.0.0;provide:=true,
-            org.apache.felix.webconsole.i18n;version=1.0.0;provide:=true
+            org.apache.felix.webconsole;provide:=true,
+            org.apache.felix.webconsole.bundleinfo;provide:=true,
+            org.apache.felix.webconsole.i18n;provide:=true
         </webconsole.exports>
     </properties>
 
@@ -145,6 +145,14 @@
                         </_removeheaders>
                     </instructions>
                 </configuration>
+                <executions>
+                    <execution>
+                        <id>baseline</id>
+                        <goals>
+                            <goal>baseline</goal>
+                        </goals>
+                    </execution>
+                </executions>
             </plugin>
             <plugin>
                 <artifactId>maven-javadoc-plugin</artifactId>
@@ -331,6 +339,13 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <version>1.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>servlet-api</artifactId>
             <version>2.4</version>
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/User.java b/webconsole/src/main/java/org/apache/felix/webconsole/User.java
new file mode 100644
index 0000000..0e56790
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/User.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Representation of a user.
+ * The user object can be used by plugins to {@link #authorize(String)} the user.
+ * @since 3.4.0
+ */
+@ProviderType
+public interface User {
+    
+    /**
+     * The name of the request attribute providing an object of this class
+     */
+    String USER_ATTRIBUTE = User.class.getName();
+
+    /**
+     * Return the user object.
+     * This method might return {@code null} if no web console security provider is configured and
+     * access to the console is allowed without authentication.
+     * This is the same value as the request attribute from {@link WebConsoleSecurityProvider2#USER_ATTRIBUTE}.
+     * @return The user object or {@code null}
+     */
+    Object getUserObject();
+
+
+    /**
+     * Checks whether the user has the given role permission.
+     *
+     * @param role The requested role
+     * @return {@code true} if the user is given permission for the given role.
+     */
+    boolean authorize( String role );
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java
index fa62d5e..39b0e07 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java
@@ -44,7 +44,7 @@ public interface WebConsoleSecurityProvider
 
 
     /**
-     * Checks whether bthe authenticated user has the given role permission.
+     * 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(String, String)}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/package-info.java b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/package-info.java
new file mode 100644
index 0000000..46d6e34
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.felix.webconsole.bundleinfo;
+
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/i18n/package-info.java b/webconsole/src/main/java/org/apache/felix/webconsole/i18n/package-info.java
new file mode 100644
index 0000000..59cd5c3
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/i18n/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.felix.webconsole.i18n;
+
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
index 28a46c6..548f53e 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
@@ -53,6 +53,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.felix.webconsole.AbstractWebConsolePlugin;
 import org.apache.felix.webconsole.BrandingPlugin;
+import org.apache.felix.webconsole.User;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
@@ -219,13 +220,13 @@ public class OsgiManager extends GenericServlet
 
     private ServiceTracker brandingTracker;
 
-    private ServiceTracker securityProviderTracker;
+    private ServiceTracker<WebConsoleSecurityProvider, WebConsoleSecurityProvider> securityProviderTracker;
 
     private ServiceRegistration configurationListener;
 
     // list of OsgiManagerPlugin instances activated during init. All these
     // instances will have to be deactivated during destroy
-    private List osgiManagerPlugins = new ArrayList();
+    private List<OsgiManagerPlugin> osgiManagerPlugins = new ArrayList<>();
 
     private String webManagerRoot;
 
@@ -236,15 +237,15 @@ public class OsgiManager extends GenericServlet
     private boolean httpResourcesRegistered;
 
     // default configuration from framework properties
-    private HashMap defaultConfiguration;
+    private Map<String, Object> defaultConfiguration;
 
     // configuration from Configuration Admin
-    private HashMap configuration;
+    private volatile Map<String, Object> configuration;
 
     // See https://issues.apache.org/jira/browse/FELIX-2267
     private Locale configuredLocale;
 
-    private Set enabledPlugins;
+    private Set<String> enabledPlugins;
 
     final ConcurrentSkipListSet<String> registeredSecurityProviders = new ConcurrentSkipListSet<String>();
 
@@ -277,13 +278,14 @@ public class OsgiManager extends GenericServlet
 
             try
             {
-                Class pluginClass = classLoader.loadClass(pluginClassName);
-                Object plugin = pluginClass.newInstance();
+                Class<?> pluginClass = classLoader.loadClass(pluginClassName);
+                Object plugin = pluginClass.getDeclaredConstructor(null).newInstance(null);
 
                 if (plugin instanceof OsgiManagerPlugin)
                 {
-                    ((OsgiManagerPlugin) plugin).activate(bundleContext);
-                    osgiManagerPlugins.add(plugin);
+                    final OsgiManagerPlugin p = (OsgiManagerPlugin)plugin;
+                    p.activate(bundleContext);
+                    osgiManagerPlugins.add(p);
                 }
                 if (plugin instanceof BrandingPlugin)
                 {
@@ -332,13 +334,12 @@ public class OsgiManager extends GenericServlet
         this.requiredSecurityProviders = splitCommaSeparatedString(bundleContext.getProperty(FRAMEWORK_PROP_SECURITY_PROVIDERS));
 
         // add support for pluggable security
-        securityProviderTracker = new ServiceTracker(bundleContext,
-            WebConsoleSecurityProvider.class.getName(),
-            new UpdateDependenciesStateCustomizer());
+        securityProviderTracker = new ServiceTracker<>(bundleContext, WebConsoleSecurityProvider.class,
+                                          new UpdateDependenciesStateCustomizer());
         securityProviderTracker.open();
 
         // load the default configuration from the framework
-        this.defaultConfiguration = new HashMap();
+        this.defaultConfiguration = new HashMap<>();
         this.defaultConfiguration.put( PROP_MANAGER_ROOT,
             ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_MANAGER_ROOT, DEFAULT_MANAGER_ROOT ) );
         this.defaultConfiguration.put( PROP_REALM,
@@ -348,7 +349,7 @@ public class OsgiManager extends GenericServlet
         this.defaultConfiguration.put( PROP_PASSWORD,
             ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_PASSWORD, DEFAULT_PASSWORD ) );
         this.defaultConfiguration.put( PROP_LOG_LEVEL,
-            new Integer( ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_LOG_LEVEL, DEFAULT_LOG_LEVEL ) ) );
+            ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_LOG_LEVEL, DEFAULT_LOG_LEVEL ) );
         this.defaultConfiguration.put( PROP_LOCALE,
             ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_LOCALE, null ) );
 
@@ -386,7 +387,7 @@ public class OsgiManager extends GenericServlet
                 {
                     // do nothing
                 }
-            }, new Hashtable()
+            }, new Hashtable<String, Object>()
             {
                 {
                     put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" ); //$NON-NLS-1$
@@ -431,10 +432,10 @@ public class OsgiManager extends GenericServlet
         }
 
         // deactivate any remaining plugins
-        for (Iterator pi = osgiManagerPlugins.iterator(); pi.hasNext();)
+        for (Iterator<OsgiManagerPlugin> pi = osgiManagerPlugins.iterator(); pi.hasNext();)
         {
-            Object plugin = pi.next();
-            ((OsgiManagerPlugin) plugin).deactivate();
+            OsgiManagerPlugin plugin = pi.next();
+            plugin.deactivate();
         }
 
         // simply remove all operations, we should not be used anymore
@@ -487,7 +488,7 @@ public class OsgiManager extends GenericServlet
         // don't really expect to be called within a non-HTTP environment
         try
         {
-            AccessController.doPrivileged(new PrivilegedExceptionAction()
+            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
             {
                 public Object run() throws Exception
                 {
@@ -648,7 +649,7 @@ public class OsgiManager extends GenericServlet
             // check for basic authentication
             String auth = request.getHeader(HEADER_AUTHORIZATION); //$NON-NLS-1$
             if (null != auth && auth.toLowerCase().startsWith("basic ")) { //$NON-NLS-1$
-                Map config = getConfiguration();
+                Map<String, Object> config = getConfiguration();
                 String realm = ConfigurationUtil.getProperty(config, PROP_REALM, DEFAULT_REALM);
                 response.setHeader(HEADER_WWW_AUTHENTICATE, "Basic realm=\"" +  realm + "\""); //$NON-NLS-1$ //$NON-NLS-2$
                 response.addCookie(new Cookie("logout", "true")); //$NON-NLS-1$ //$NON-NLS-2$
@@ -660,6 +661,7 @@ public class OsgiManager extends GenericServlet
         request.removeAttribute(HttpContext.REMOTE_USER);
         request.removeAttribute(HttpContext.AUTHORIZATION);
         request.removeAttribute(WebConsoleSecurityProvider2.USER_ATTRIBUTE);
+        request.removeAttribute(User.USER_ATTRIBUTE);
     }
 
     private final AbstractWebConsolePlugin getConsolePlugin(final String label)
@@ -840,11 +842,8 @@ public class OsgiManager extends GenericServlet
         return new FilteringResponseWrapper(response, resourceBundle, request);
     }
 
-    private static class HttpServiceTracker extends ServiceTracker
+    private static class HttpServiceTracker extends ServiceTracker<HttpService, HttpService>
     {
-
-        private static final String HTTP_SERVICE = "org.osgi.service.http.HttpService"; //$NON-NLS-1$
-
         private final OsgiManager osgiManager;
 
         private final String httpServiceSelector;
@@ -858,7 +857,7 @@ public class OsgiManager extends GenericServlet
                 try
                 {
                     final String filterString = "(&(" + Constants.OBJECTCLASS + "=" //$NON-NLS-1$ //$NON-NLS-2$
-                        + HTTP_SERVICE + ")(" + httpServiceSelector + "))"; //$NON-NLS-1$ //$NON-NLS-2$
+                        + HttpService.class.getName() + ")(" + httpServiceSelector + "))"; //$NON-NLS-1$ //$NON-NLS-2$
                     Filter filter = osgiManager.getBundleContext().createFilter(
                         filterString);
                     return new HttpServiceTracker(osgiManager, httpServiceSelector,
@@ -876,7 +875,7 @@ public class OsgiManager extends GenericServlet
 
         private HttpServiceTracker(final OsgiManager osgiManager)
         {
-            super(osgiManager.getBundleContext(), HTTP_SERVICE, null);
+            super(osgiManager.getBundleContext(), HttpService.class, null);
             this.osgiManager = osgiManager;
             this.httpServiceSelector = null;
         }
@@ -897,50 +896,38 @@ public class OsgiManager extends GenericServlet
             return httpServiceSelector == null;
         }
 
-        public Object addingService(ServiceReference reference)
+        public HttpService addingService(ServiceReference<HttpService> reference)
         {
-            Object service = super.addingService(reference);
-            if (service instanceof HttpService)
-            {
-                osgiManager.bindHttpService((HttpService) service);
-            }
+            HttpService service = super.addingService(reference);
+            osgiManager.bindHttpService(service);
             return service;
         }
 
-        public void removedService(ServiceReference reference, Object service)
+        public void removedService(ServiceReference<HttpService> reference, HttpService service)
         {
-            if (service instanceof HttpService)
-            {
-                osgiManager.unbindHttpService((HttpService) service);
-            }
+            osgiManager.unbindHttpService(service);
 
             super.removedService(reference, service);
         }
     }
 
-    private static class BrandingServiceTracker extends ServiceTracker
+    private static class BrandingServiceTracker extends ServiceTracker<BrandingPlugin, BrandingPlugin>
     {
         BrandingServiceTracker(OsgiManager osgiManager)
         {
-            super(osgiManager.getBundleContext(), BrandingPlugin.class.getName(), null);
+            super(osgiManager.getBundleContext(), BrandingPlugin.class, null);
         }
 
-        public Object addingService(ServiceReference reference)
+        public BrandingPlugin addingService(ServiceReference<BrandingPlugin> reference)
         {
-            Object plugin = super.addingService(reference);
-            if (plugin instanceof BrandingPlugin)
-            {
-                AbstractWebConsolePlugin.setBrandingPlugin((BrandingPlugin) plugin);
-            }
+            BrandingPlugin plugin = super.addingService(reference);
+            AbstractWebConsolePlugin.setBrandingPlugin( plugin);
             return plugin;
         }
 
-        public void removedService(ServiceReference reference, Object service)
+        public void removedService(ServiceReference<BrandingPlugin> reference, BrandingPlugin service)
         {
-            if (service instanceof BrandingPlugin)
-            {
-                AbstractWebConsolePlugin.setBrandingPlugin(null);
-            }
+            AbstractWebConsolePlugin.setBrandingPlugin(null);
             super.removedService(reference, service);
         }
 
@@ -961,7 +948,7 @@ public class OsgiManager extends GenericServlet
     }
 
     synchronized void registerHttpService() {
-        Map config = getConfiguration();
+        Map<String, Object> config = getConfiguration();
 
         // get authentication details
         String realm = ConfigurationUtil.getProperty(config, PROP_REALM, DEFAULT_REALM);
@@ -974,7 +961,7 @@ public class OsgiManager extends GenericServlet
             HttpContext httpContext = new OsgiManagerHttpContext(bundleContext, httpService,
                 securityProviderTracker, userId, password, realm);
 
-            Dictionary servletConfig = toStringConfig(config);
+            Dictionary<String, String> servletConfig = toStringConfig(config);
 
             if (!httpServletRegistered) {
                 // register this servlet and take note of this
@@ -1045,27 +1032,27 @@ public class OsgiManager extends GenericServlet
     }
 
 
-    private Map getConfiguration()
+    private Map<String, Object> getConfiguration()
     {
         return configuration;
     }
 
 
-    Map getDefaultConfiguration()
+    Map<String, Object> getDefaultConfiguration()
     {
         return defaultConfiguration;
     }
 
 
-    synchronized void updateConfiguration( Dictionary osgiConfig )
+    synchronized void updateConfiguration( Dictionary<String, Object> osgiConfig )
     {
-        HashMap config = new HashMap( this.defaultConfiguration );
+        Map<String, Object> config = new HashMap<String, Object>( this.defaultConfiguration );
 
         if ( osgiConfig != null )
         {
-            for ( Enumeration keys = osgiConfig.keys(); keys.hasMoreElements(); )
+            for ( Enumeration<String> keys = osgiConfig.keys(); keys.hasMoreElements(); )
             {
-                final Object key = keys.nextElement();
+                final String key = keys.nextElement();
                 config.put( key, osgiConfig.get( key ) );
             }
         }
@@ -1105,7 +1092,7 @@ public class OsgiManager extends GenericServlet
 
         // get enabled plugins
         String[] plugins = ConfigurationUtil.getStringArrayProperty(config, PROP_ENABLED_PLUGINS);
-        enabledPlugins = null == plugins ? null : new HashSet(Arrays.asList(plugins));
+        enabledPlugins = null == plugins ? null : new HashSet<String>(Arrays.asList(plugins));
         // check for moved config manager class (see FELIX-4074)
         if ( enabledPlugins != null )
         {
@@ -1180,12 +1167,12 @@ public class OsgiManager extends GenericServlet
     }
 
 
-    private Dictionary toStringConfig( Map config )
+    private Dictionary<String, String> toStringConfig( Map<String, Object> config )
     {
-        Dictionary stringConfig = new Hashtable();
-        for ( Iterator ei = config.entrySet().iterator(); ei.hasNext(); )
+        Dictionary<String, String> stringConfig = new Hashtable<>();
+        for ( Iterator<Map.Entry<String, Object>> ei = config.entrySet().iterator(); ei.hasNext(); )
         {
-            Entry entry = ( Entry ) ei.next();
+            Entry<String, Object> entry = ei.next();
             stringConfig.put( entry.getKey(), String.valueOf( entry.getValue() ) );
         }
         return stringConfig;
@@ -1233,9 +1220,9 @@ public class OsgiManager extends GenericServlet
         return langMap = map;
     }
 
-    class UpdateDependenciesStateCustomizer implements ServiceTrackerCustomizer {
+    class UpdateDependenciesStateCustomizer implements ServiceTrackerCustomizer<WebConsoleSecurityProvider, WebConsoleSecurityProvider> {
         @Override
-        public Object addingService(ServiceReference reference) {
+        public WebConsoleSecurityProvider addingService(ServiceReference<WebConsoleSecurityProvider> reference) {
             Object nameObj = reference.getProperty(SECURITY_PROVIDER_PROPERTY_NAME);
             if (nameObj instanceof String) {
                 String name = (String) nameObj;
@@ -1246,13 +1233,13 @@ public class OsgiManager extends GenericServlet
         }
 
         @Override
-        public void modifiedService(ServiceReference reference, Object service) {
+        public void modifiedService(ServiceReference<WebConsoleSecurityProvider> reference, WebConsoleSecurityProvider service) {
             removedService(reference, service);
             addingService(reference);
         }
 
         @Override
-        public void removedService(ServiceReference reference, Object service) {
+        public void removedService(ServiceReference<WebConsoleSecurityProvider> reference, WebConsoleSecurityProvider service) {
             Object nameObj = reference.getProperty(SECURITY_PROVIDER_PROPERTY_NAME);
             if (nameObj instanceof String) {
                 String name = (String) nameObj;
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
index 03cf735..c7d472f 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
@@ -23,6 +23,7 @@ import java.net.URL;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.webconsole.User;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
 import org.osgi.framework.BundleContext;
@@ -44,7 +45,7 @@ final class OsgiManagerHttpContext implements HttpContext
 
     private final HttpContext base;
 
-    private final ServiceTracker tracker;
+    private final ServiceTracker<WebConsoleSecurityProvider, WebConsoleSecurityProvider> tracker;
 
     private final String username;
 
@@ -54,7 +55,7 @@ final class OsgiManagerHttpContext implements HttpContext
 
 
     OsgiManagerHttpContext(final BundleContext bundleContext,
-        final HttpService httpService, final ServiceTracker tracker, final String username,
+        final HttpService httpService, final ServiceTracker<WebConsoleSecurityProvider, WebConsoleSecurityProvider> tracker, final String username,
         final String password, final String realm )
     {
         this.bundleContext = bundleContext;
@@ -95,19 +96,52 @@ final class OsgiManagerHttpContext implements HttpContext
      *            <code>Authorization</code> header.
      * @param response The HTTP response used to send the authentication request
      *            if authentication is required but not satisfied.
-     * @return <code>true</code> if authentication is required and not
-     *         satisfied by the request.
+     * @return {@code} true if authentication is required and not satisfied by the request.
      */
-    public boolean handleSecurity( HttpServletRequest request, HttpServletResponse response )
-    {
-        Object provider = tracker.getService();
+    public boolean handleSecurity( final HttpServletRequest request, final HttpServletResponse response ) {
+        final WebConsoleSecurityProvider provider = tracker.getService();
 
         // check whether the security provider can fully handle the request
-        if ( provider instanceof WebConsoleSecurityProvider2 )
-        {
-            return ( ( WebConsoleSecurityProvider2 ) provider ).authenticate( request, response );
+        final boolean result;
+        if ( provider instanceof WebConsoleSecurityProvider2 ) {
+            result = ( ( WebConsoleSecurityProvider2 ) provider ).authenticate( request, response );
+        } else {
+            result = handleSecurity(provider, request, response);
+        }
+
+        if ( result ) {
+            request.setAttribute(User.USER_ATTRIBUTE, new User(){
+
+				@Override
+				public boolean authorize(String role) {
+                    final Object user = this.getUserObject();
+                    if ( user == null ) {
+                        // no user object in request, deny
+                        return false;
+                    }
+					if ( provider == null ) {
+                        // no provider, allow (compatibility)
+                        return true;
+                    }
+					return provider.authorize(this.getUserObject(), role);
+				}
+
+				@Override
+				public Object getUserObject() {
+					return request.getAttribute(WebConsoleSecurityProvider2.USER_ATTRIBUTE);
+				}
+                
+            });
         }
+        return result;
+    }
 
+    /**
+     * Handle security with an optional web console security provider
+     */
+    private boolean handleSecurity( final WebConsoleSecurityProvider provider, 
+        final HttpServletRequest request,
+        final HttpServletResponse response) {
         // Return immediately if the header is missing
         String authHeader = request.getHeader( HEADER_AUTHORIZATION );
         if ( authHeader != null && authHeader.length() > 0 )
@@ -170,27 +204,6 @@ final class OsgiManagerHttpContext implements HttpContext
         return false;
     }
 
-
-    public boolean authorize( final HttpServletRequest request, String role )
-    {
-        Object user = request.getAttribute( WebConsoleSecurityProvider2.USER_ATTRIBUTE );
-        if ( user != null )
-        {
-            WebConsoleSecurityProvider provider = ( WebConsoleSecurityProvider ) tracker.getService();
-            if ( provider != null )
-            {
-                return provider.authorize( user, role );
-            }
-
-            // no provider, grant access (backwards compatibility)
-            return true;
-        }
-
-        // missing user in the request, deny access
-        return false;
-    }
-
-
     private static byte[][] base64Decode( String srcString )
     {
         byte[] transformed = Base64.decodeBase64( srcString );
@@ -225,11 +238,11 @@ final class OsgiManagerHttpContext implements HttpContext
     }
 
 
-    private boolean authenticate( Object provider, String username, byte[] password )
+    private boolean authenticate( WebConsoleSecurityProvider provider, String username, byte[] password )
     {
         if ( provider != null )
         {
-            return ( ( WebConsoleSecurityProvider ) provider ).authenticate( username, toString( password ) ) != null;
+            return provider.authenticate( username, toString( password ) ) != null;
         }
         if ( this.username.equals( username ) && this.password.matches( password ) )
         {
@@ -240,5 +253,4 @@ final class OsgiManagerHttpContext implements HttpContext
         }
         return false;
     }
-
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/package-info.java b/webconsole/src/main/java/org/apache/felix/webconsole/package-info.java
new file mode 100644
index 0000000..beefc07
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@org.osgi.annotation.versioning.Version("3.4.0")
+package org.apache.felix.webconsole;
+
diff --git a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
index 2664100..0b49dbd 100644
--- a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
+++ b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
@@ -36,7 +36,7 @@ public class OsgiManagerHttpContextTest {
         OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(bc, svc, null, "foo", "bar", "blah");
 
         Method authenticateMethod = OsgiManagerHttpContext.class.getDeclaredMethod(
-                "authenticate", new Class [] {Object.class, String.class, byte[].class});
+                "authenticate", new Class [] {WebConsoleSecurityProvider.class, String.class, byte[].class});
         authenticateMethod.setAccessible(true);
 
         assertEquals(true, authenticateMethod.invoke(ctx, null, "foo", "bar".getBytes()));
@@ -57,7 +57,7 @@ public class OsgiManagerHttpContextTest {
         OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(bc, svc, null, "foo", "bar", "blah");
 
         Method authenticateMethod = OsgiManagerHttpContext.class.getDeclaredMethod(
-                "authenticate", new Class [] {Object.class, String.class, byte[].class});
+                "authenticate", new Class [] {WebConsoleSecurityProvider.class, String.class, byte[].class});
         authenticateMethod.setAccessible(true);
 
         assertEquals("A required security provider is configured, logging in using "
diff --git a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
index 49f6af7..7c52482 100644
--- a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
+++ b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
@@ -36,6 +36,7 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
@@ -134,13 +135,13 @@ public class OsgiManagerTest {
         final List<String> invocations = new ArrayList<String>();
         ServiceTrackerCustomizer stc = mgr.new UpdateDependenciesStateCustomizer() {
             @Override
-            public Object addingService(ServiceReference reference) {
+            public WebConsoleSecurityProvider addingService(ServiceReference<WebConsoleSecurityProvider> reference) {
                 invocations.add("added:" + reference);
                 return null;
             }
 
             @Override
-            public void removedService(ServiceReference reference, Object service) {
+            public void removedService(ServiceReference<WebConsoleSecurityProvider> reference, WebConsoleSecurityProvider service) {
                 invocations.add("removed:" + reference);
             }
         };