--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.shiro.filters;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends <code>BasicHttpAuthenticationFilter</code> to include ability to
+ * authenticate OAuth2 tokens, which is needed for backwards compatibility
+ * with <code>TokenAuthFilter</code>.
+ *
+ * This behavior is enabled by default for backwards compatibility. To disable
+ * OAuth2 functionality, just comment out the following line from the
+ * <code>etc/shiro.ini</code> file:
+ * <code>authcBasic = org.opendaylight.aaa.shiro.filters.ODLHttpAuthenticationFilter</code>
+ * then restart the karaf container.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ *
+ */
+public class ODLHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ODLHttpAuthenticationFilter.class);
+
+ // defined in lower-case for more efficient string comparison
+ protected static final String BEARER_SCHEME = "bearer";
+
+ public ODLHttpAuthenticationFilter() {
+ super();
+ LOG.info("Creating the ODLHttpAuthenticationFilter");
+ }
+
+ @Override
+ protected String[] getPrincipalsAndCredentials(String scheme, String encoded) {
+ final String decoded = Base64.decodeToString(encoded);
+ // attempt to decode username/password; otherwise decode as token
+ if(decoded.contains(":")) {
+ return decoded.split(":");
+ }
+ return new String[]{encoded};
+ }
+
+ @Override
+ protected boolean isLoginAttempt(String authzHeader) {
+ final String authzScheme = getAuthzScheme().toLowerCase();
+ final String authzHeaderLowerCase = authzHeader.toLowerCase();
+ return authzHeaderLowerCase.startsWith(authzScheme) || authzHeaderLowerCase.startsWith(BEARER_SCHEME);
+ }
+
+}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Strings;
+
/**
* TokenAuthRealm is an adapter between the AAA shiro subsystem and the existing
* <code>TokenAuth</code> mechanisms. Thus, one can enable use of
throw new AuthenticationException(AUTHENTICATION_SERVICE_UNAVAILABLE_MESSAGE);
}
- if (ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()) {
- Map<String, List<String>> headers = formHeaders(username,password);
-
- // iterate over <code>TokenAuth</code> implementations and attempt to
- // authentication with each one
- final List<TokenAuth> tokenAuthCollection =
- ServiceLocator.getInstance().getTokenAuthCollection();
- for (TokenAuth ta : tokenAuthCollection) {
- try {
- LOG.debug("Authentication attempt using " + ta.getClass().getName());
- Authentication auth = ta.validate(headers);
- if (auth != null) {
- LOG.debug("Authentication attempt successful");
- ServiceLocator.getInstance().getAuthenticationService().set(auth);
- this.cachedAuthenticationToken = auth;
- return new SimpleAuthenticationInfo(
- username, password.toCharArray(), getName());
+ // if the password is empty, this is an OAuth2 request, not a Basic HTTP Auth request
+ if (!Strings.isNullOrEmpty(password)) {
+ if (ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()) {
+ Map<String, List<String>> headers = formHeaders(username,password);
+ // iterate over <code>TokenAuth</code> implementations and attempt to
+ // authentication with each one
+ final List<TokenAuth> tokenAuthCollection =
+ ServiceLocator.getInstance().getTokenAuthCollection();
+ for (TokenAuth ta : tokenAuthCollection) {
+ try {
+ LOG.debug("Authentication attempt using " + ta.getClass().getName());
+ Authentication auth = ta.validate(headers);
+ if (auth != null) {
+ LOG.debug("Authentication attempt successful");
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ this.cachedAuthenticationToken = auth;
+ return new SimpleAuthenticationInfo(
+ username, password.toCharArray(), getName());
+ }
+ } catch (AuthenticationException ae) {
+ LOG.debug("Authentication attempt unsuccessful");
+ // invalidate cached token
+ cachedAuthenticationToken = null;
+ throw new AuthenticationException(UNABLE_TO_AUTHENTICATE, ae);
}
- } catch (AuthenticationException ae) {
- LOG.debug("Authentication attempt unsuccessful");
- // invalidate cached token
- cachedAuthenticationToken = null;
- throw new AuthenticationException(UNABLE_TO_AUTHENTICATE, ae);
}
}
}
+
+ // extract the authentication token and attempt validation of the token
+ final String token = extractUsername(authenticationToken);
+ final Authentication auth;
+ try {
+ auth = validate(token);
+ if (auth != null) {
+ cachedAuthenticationToken = auth;
+ return new SimpleAuthenticationInfo(auth.user(),
+ "", getName());
+ }
+ } catch(AuthenticationException e) {
+ cachedAuthenticationToken = null;
+ LOG.info("Unknown OAuth2 Token Access Request", e);
+ }
+
LOG.debug("Authentication failed: exhausted TokenAuth resources");
// invalidate cached token
cachedAuthenticationToken = null;
return null;
}
+ private Authentication validate(final String token) {
+ Authentication auth = ServiceLocator.getInstance().getTokenStore().get(token);
+ if (auth == null) {
+ throw new AuthenticationException("Could not validate the token " + token);
+ } else {
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ }
+ return auth;
+ }
+
/**
* extract the username from an <code>AuthenticationToken</code>
*