FELIX-3006 : Please create a logout button for the web console screen

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1662638 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 46394c1..cd7f6fb 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -41,7 +41,7 @@
         <org.json.version>20070829</org.json.version>
         <org.json.version.osgi>0.0.0.${org.json.version}</org.json.version.osgi>
         <webconsole.exports>
-            org.apache.felix.webconsole;version=3.1.3;provide:=true,
+            org.apache.felix.webconsole;version=3.2.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
         </webconsole.exports>
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 e571565..9d52489 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -737,6 +737,9 @@
             SortedMap categoryMap = sortMenuCategoryMap( menuMap, appRoot );
             pw.println( "<ul id=\"navmenu\">" );
             renderSubmenu( categoryMap, appRoot, pw, 0 );
+            pw.println("<li class=\"logoutButton navMenuItem-0\">");
+            pw.println("<a href=\"" + appRoot + "/logout\">${logout}</a>");
+            pw.println("</li>");
             pw.println( "</ul>" );
         }
     }
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 477559b..3f63db6 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,8 @@
 import org.apache.felix.webconsole.BrandingPlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
+import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
+import org.apache.felix.webconsole.WebConsoleSecurityProvider3;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
 import org.apache.felix.webconsole.internal.core.BundlesServlet;
@@ -168,6 +170,10 @@
 
     static final String DEFAULT_HTTP_SERVICE_SELECTOR = ""; //$NON-NLS-1$
 
+    private static final String HEADER_AUTHORIZATION = "Authorization"; //$NON-NLS-1$
+
+    private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$
+
     /**
      * The default value for the {@link #PROP_MANAGER_ROOT} configuration
      * property (value is "/system/console").
@@ -514,10 +520,16 @@
             }
             path = path.concat(holder.getDefaultPluginLabel());
             response.sendRedirect(path);
+            response.setContentLength(0);
             return;
         }
 
-        int slash = pathInfo.indexOf("/", 1);
+        if (pathInfo.equals("/logout")) { //$NON-NLS-1$
+            logout(request, response);
+            return;
+        }
+
+        int slash = pathInfo.indexOf("/", 1); //$NON-NLS-1$
         if (slash < 2)
         {
             slash = pathInfo.length();
@@ -568,6 +580,63 @@
         }
     }
 
+    private final void logout(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        // check if special logout cookie is set, this is used to prevent
+        // from an endless loop with basic auth
+        Cookie[] cookies = request.getCookies();
+        boolean found = false;
+        if ( cookies != null )
+        {
+            for(int i=0;i<cookies.length;i++)
+            {
+                if ( cookies[i].getName().equals("logout") ) //$NON-NLS-1$
+                {
+                    found = true;
+                    break;
+                }
+            }
+        }
+        if ( found )
+        {
+            // redirect to main page
+            String url = request.getRequestURI();
+            final int lastSlash = url.lastIndexOf('/');
+            final Cookie c = new Cookie("logout", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+            c.setMaxAge(0);
+            response.addCookie(c);
+            response.sendRedirect(url.substring(0, lastSlash));
+            return;
+        }
+        Object securityProvider = securityProviderTracker.getService();
+        if (securityProvider instanceof WebConsoleSecurityProvider3)
+        {
+            ((WebConsoleSecurityProvider3) securityProvider).logout(request, response);
+        }
+        else
+        {
+            // if the security provider doesn't support logout, we try to
+            // logout the default basic authentication mechanism
+            // See https://issues.apache.org/jira/browse/FELIX-3006
+
+            // 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();
+                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$
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            }
+        }
+
+        // clean-up
+        request.removeAttribute(HttpContext.REMOTE_USER);
+        request.removeAttribute(HttpContext.AUTHORIZATION);
+        request.removeAttribute(WebConsoleSecurityProvider2.USER_ATTRIBUTE);
+    }
+
     private final AbstractWebConsolePlugin getConsolePlugin(final String label)
     {
         // backwards compatibility for the former "install" action which is
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index c5a1cea..3383e15 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -46,6 +46,7 @@
 reset=Reset
 delete=Delete
 refresh=Refresh
+logout=Log out
 
 # VMStat plugin
 vmstat.pluginTitle=System Information
diff --git a/webconsole/src/main/resources/res/ui/webconsole.css b/webconsole/src/main/resources/res/ui/webconsole.css
index 0ddc954..a2fc654 100644
--- a/webconsole/src/main/resources/res/ui/webconsole.css
+++ b/webconsole/src/main/resources/res/ui/webconsole.css
@@ -189,8 +189,11 @@
 	list-style: none
 }
 /* language selection element */
-#langSelect { position: absolute;	top: 5px; right: 5px }
+#langSelect { position: absolute; top: 5px; right: 5px }
 #langSelect img   { display: block; padding: 2px 0 }
+/* logout element */
+.logoutButton { float: right; text-decoration: none }
+
 
 .filterBox      { float: left; margin-left: 1em }
 .servicesFilter { width: 400px; }