/* * Copyright 2015 Groupon, Inc * Copyright 2015 The Billing Project, LLC * * The Billing Project 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.shiro.authc.pam; import java.util.Collection; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.realm.Realm; import org.killbill.billing.server.security.FirstSuccessfulStrategyWith540; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // See https://issues.apache.org/jira/browse/SHIRO-540 public class ModularRealmAuthenticatorWith540 extends ModularRealmAuthenticator { private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class); public ModularRealmAuthenticatorWith540(final ModularRealmAuthenticator delegate) { setRealms(delegate.getRealms()); setAuthenticationStrategy(delegate.getAuthenticationStrategy()); } /** * Performs the multi-realm authentication attempt by calling back to a {@link AuthenticationStrategy} object * as each realm is consulted for {@code AuthenticationInfo} for the specified {@code token}. * * @param realms the multiple realms configured on this Authenticator instance. * @param token the submitted AuthenticationToken representing the subject's (user's) log-in principals and credentials. * @return an aggregated AuthenticationInfo instance representing account data across all the successfully * consulted realms. */ protected AuthenticationInfo doMultiRealmAuthentication(final Collection<Realm> realms, final AuthenticationToken token) { final AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } for (final Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; try { info = realm.getAuthenticationInfo(token); } catch (final Throwable throwable) { t = throwable; if (log.isDebugEnabled()) { final String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, t); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); if (strategy instanceof FirstSuccessfulStrategyWith540) { // check if we should check the next realm, or just stop here. if (!((FirstSuccessfulStrategyWith540) strategy).continueAfterAttempt(info, aggregate, t)) { log.trace("Will not consult any other realms for authentication, last realm [{}].", realm); break; } } } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; } }