FELIX-2639 Add new WebConsoleSecurityProvider2 interface and
increase package export version to 3.1.2; merge former
SecurityProvider proxy into OsgiManagerHttpContext; update JavaDoc
of WebConsoleSecurityProvider interface
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1022015 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 8663c7a..dfba529 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -101,7 +101,7 @@
org.apache.felix.webconsole.internal.OsgiManagerActivator
</Bundle-Activator>
<Export-Package>
- org.apache.felix.webconsole;version=3.1
+ org.apache.felix.webconsole;version=3.1.2
</Export-Package>
<Private-Package>
!org.apache.felix.webconsole,
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 00bec5f..fa62d5e 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider.java
@@ -18,24 +18,41 @@
*/
package org.apache.felix.webconsole;
+
/**
* The <code>WebConsoleSecurityProvider</code> is a service interface allowing
- * to use an external system to authenticate users before granting access to
- * the Web Console.
+ * to use an external system to authenticate users before granting access to the
+ * Web Console.
*
- * @since Web Console 3.0.2
+ * @since 3.1.0; Web Console Bundle 3.1.0
*/
-public interface WebConsoleSecurityProvider {
+public interface WebConsoleSecurityProvider
+{
/**
- * Check if the user with the specified password exists and return an
- * object identifying the user, else null
+ * Authenticates the user with the given user name and password.
+ *
+ * @param username The name of the user presented by the client
+ * @param password The password presented by the client
+ * @return Some object representing the authenticated user indicating general
+ * access to be granted to the web console. If the user cannot be
+ * authenticated (e.g. unknown user name or wrong password) or the
+ * user must not be allowed access to the web console at all
+ * <code>null</code> must be returned from this method.
*/
- public Object authenticate(String username, String password);
+ public Object authenticate( String username, String password );
+
/**
- * Check that the authenticated user has the given role permission
+ * Checks whether bthe 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)}
+ * 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.
*/
- public boolean authorize(Object user, String role);
+ public boolean authorize( Object user, String role );
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider2.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider2.java
new file mode 100644
index 0000000..9f91465
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider2.java
@@ -0,0 +1,74 @@
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * The <code>WebConsoleSecurityProvider2</code> extends the
+ * {@link WebConsoleSecurityProvider} interface allowing for full control of
+ * the authentication process to access the Web Console.
+ * <p>
+ * If a registered {@link WebConsoleSecurityProvider} service implements this
+ * interface the {@link #authenticate(HttpServletRequest, HttpServletResponse)}
+ * method is called instead of the
+ * {@link WebConsoleSecurityProvider#authenticate(String, String)} method.
+ *
+ * @since 3.1.2; Web Console Bundle 3.1.4
+ */
+public interface WebConsoleSecurityProvider2 extends WebConsoleSecurityProvider
+{
+
+ /**
+ * The name of the request attribute providing the object representing the
+ * authenticated user. This object is used to call the
+ * {@link WebConsoleSecurityProvider#authorize(Object, String)} to
+ * authorize access for certain roles.
+ */
+ String USER_ATTRIBUTE = "org.apache.felix.webconsole.user";
+
+
+ /**
+ * 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>HttpContext.handleSecurity</code> method
+ * as specified in the OSGi HTTP Service specification.
+ * <p>
+ * If this method returns <code>true</code> it is assumed the request
+ * provided valid credentials identifying the user as accepted to access
+ * the web console. In addition, the {@link #USER_ATTRIBUTE} request
+ * attribute must be set to a non-<code>null</code> object reference
+ * identifying the authenticated user.
+ * <p>
+ * If this method returns <code>false</code> 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 <code>true</code> If the request provided valid credentials.
+ */
+ public boolean authenticate( HttpServletRequest request, HttpServletResponse response );
+
+}
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 cfd0eca..8a5d285 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
@@ -347,6 +347,13 @@
configurationListener = null;
}
+ // stop tracking security provider
+ if ( securityProviderTracker != null )
+ {
+ securityProviderTracker.close();
+ securityProviderTracker = null;
+ }
+
this.bundleContext = null;
}
@@ -708,7 +715,8 @@
// register the servlet and resources
try
{
- HttpContext httpContext = new OsgiManagerHttpContext( httpService, realm, new SecurityProvider( securityProviderTracker, userId, password ) );
+ HttpContext httpContext = new OsgiManagerHttpContext( httpService, securityProviderTracker, userId,
+ password, realm );
Dictionary servletConfig = toStringConfig( config );
@@ -950,7 +958,7 @@
}
return stringConfig;
}
-
+
private Map langMap;
private final Map getLangMap()
@@ -977,33 +985,4 @@
return langMap = map;
}
- static class SecurityProvider implements WebConsoleSecurityProvider {
-
- final ServiceTracker tracker;
- final String username;
- final String password;
-
- SecurityProvider( ServiceTracker tracker, String username, String password ) {
- this.tracker = tracker;
- this.username = username;
- this.password = password;
- }
-
- public Object authenticate(String username, String password) {
- WebConsoleSecurityProvider provider = (WebConsoleSecurityProvider) tracker.getService();
- if (provider != null) {
- return provider.authenticate(username, password);
- }
- if (this.username.equals(username) && this.password.equals(password)) {
- return username;
- }
- return null;
- }
-
- public boolean authorize(Object user, String role) {
- // no op: authorize everything
- return true;
- }
- }
-
}
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 d930810..f9635ff 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
@@ -25,8 +25,10 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.webconsole.WebConsoleSecurityProvider;
+import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
+import org.osgi.util.tracker.ServiceTracker;
final class OsgiManagerHttpContext implements HttpContext
@@ -38,18 +40,25 @@
private static final String AUTHENTICATION_SCHEME_BASIC = "Basic";
- String realm;
-
- WebConsoleSecurityProvider securityProvider;
-
private final HttpContext base;
+ private final ServiceTracker tracker;
- OsgiManagerHttpContext( HttpService httpService, String realm, WebConsoleSecurityProvider securityProvider)
+ private final String username;
+
+ private final String password;
+
+ private final String realm;
+
+
+ OsgiManagerHttpContext( HttpService httpService, final ServiceTracker tracker, final String username,
+ final String password, final String realm )
{
- this.base = httpService.createDefaultHttpContext();
+ this.tracker = tracker;
+ this.username = username;
+ this.password = password;
this.realm = realm;
- this.securityProvider = securityProvider;
+ this.base = httpService.createDefaultHttpContext();
}
@@ -87,11 +96,12 @@
*/
public boolean handleSecurity( HttpServletRequest request, HttpServletResponse response )
{
+ Object provider = tracker.getService();
- // don't care for authentication if no user name is configured
- if ( this.securityProvider == null )
+ // check whether the security provider can fully handle the request
+ if ( provider instanceof WebConsoleSecurityProvider2 )
{
- return true;
+ return ( ( WebConsoleSecurityProvider2 ) provider ).authenticate( request, response );
}
// Return immediately if the header is missing
@@ -110,27 +120,30 @@
String authInfo = authHeader.substring( blank ).trim();
// Check whether authorization type matches
- if ( authType.equalsIgnoreCase( AUTHENTICATION_SCHEME_BASIC ))
+ if ( authType.equalsIgnoreCase( AUTHENTICATION_SCHEME_BASIC ) )
{
try
{
String srcString = base64Decode( authInfo );
- int i = srcString.indexOf(':');
- String username = srcString.substring(0, i);
- String password = srcString.substring(i + 1);
+ int i = srcString.indexOf( ':' );
+ String username = srcString.substring( 0, i );
+ String password = srcString.substring( i + 1 );
// authenticate
- Object id = securityProvider.authenticate( username, password );
- if (id != null) {
+ if ( authenticate( provider, username, password ) )
+ {
// as per the spec, set attributes
- request.setAttribute( HttpContext.AUTHENTICATION_TYPE, "" );
+ request.setAttribute( HttpContext.AUTHENTICATION_TYPE, HttpServletRequest.BASIC_AUTH );
request.setAttribute( HttpContext.REMOTE_USER, username );
+ // set web console user attribute
+ request.setAttribute( WebConsoleSecurityProvider2.USER_ATTRIBUTE, username );
+
// succeed
return true;
}
}
- catch (Exception e)
+ catch ( Exception e )
{
// Ignore
}
@@ -143,7 +156,7 @@
{
response.setHeader( HEADER_WWW_AUTHENTICATE, AUTHENTICATION_SCHEME_BASIC + " realm=\"" + this.realm + "\"" );
response.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
- response.setContentLength(0);
+ response.setContentLength( 0 );
response.flushBuffer();
}
catch ( IOException ioe )
@@ -155,7 +168,28 @@
return false;
}
- public static String base64Decode( String srcString )
+
+ 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 String base64Decode( String srcString )
{
byte[] transformed = Base64.decodeBase64( srcString );
try
@@ -168,4 +202,18 @@
}
}
+
+ private boolean authenticate( Object provider, String username, String password )
+ {
+ if ( provider != null )
+ {
+ return ( ( WebConsoleSecurityProvider ) provider ).authenticate( username, password ) != null;
+ }
+ if ( this.username.equals( username ) && this.password.equals( password ) )
+ {
+ return true;
+ }
+ return false;
+ }
+
}
\ No newline at end of file