blob: a79fabef3deb296dd4826039ada9c71ed29c3f3d [file] [log] [blame]
/**
* 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.pulsar.broker.authentication;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import javax.naming.AuthenticationException;
import javax.net.ssl.SSLSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.pulsar.broker.ServiceConfiguration;
import org.apache.pulsar.common.api.AuthData;
/**
* An authentication provider wraps a list of auth providers.
*/
@Slf4j
public class AuthenticationProviderList implements AuthenticationProvider {
private interface AuthProcessor<T, P> {
T apply(P process) throws AuthenticationException;
}
static <T, P> T applyAuthProcessor(List<P> processors, AuthProcessor<T, P> authFunc)
throws AuthenticationException {
AuthenticationException authenticationException = null;
for (P ap : processors) {
try {
return authFunc.apply(ap);
} catch (AuthenticationException ae) {
if (log.isDebugEnabled()) {
log.debug("Authentication failed for auth provider " + ap.getClass() + ": ", ae);
}
// Store the exception so we can throw it later instead of a generic one
authenticationException = ae;
}
}
if (null == authenticationException) {
throw new AuthenticationException("Authentication required");
} else {
throw authenticationException;
}
}
private static class AuthenticationListState implements AuthenticationState {
private final List<AuthenticationState> states;
private AuthenticationState authState;
AuthenticationListState(List<AuthenticationState> states) {
this.states = states;
this.authState = states.get(0);
}
private AuthenticationState getAuthState() throws AuthenticationException {
if (authState != null) {
return authState;
} else {
throw new AuthenticationException("Authentication state is not initialized");
}
}
@Override
public String getAuthRole() throws AuthenticationException {
return getAuthState().getAuthRole();
}
@Override
public AuthData authenticate(AuthData authData) throws AuthenticationException {
return applyAuthProcessor(
states,
as -> {
AuthData ad = as.authenticate(authData);
AuthenticationListState.this.authState = as;
return ad;
}
);
}
@Override
public AuthenticationDataSource getAuthDataSource() {
return authState.getAuthDataSource();
}
@Override
public boolean isComplete() {
return authState.isComplete();
}
@Override
public long getStateId() {
if (null != authState) {
return authState.getStateId();
} else {
return states.get(0).getStateId();
}
}
@Override
public boolean isExpired() {
return authState.isExpired();
}
@Override
public AuthData refreshAuthentication() throws AuthenticationException {
return getAuthState().refreshAuthentication();
}
}
private final List<AuthenticationProvider> providers;
public AuthenticationProviderList(List<AuthenticationProvider> providers) {
this.providers = providers;
}
public List<AuthenticationProvider> getProviders() {
return providers;
}
@Override
public void initialize(ServiceConfiguration config) throws IOException {
for (AuthenticationProvider ap : providers) {
ap.initialize(config);
}
}
@Override
public String getAuthMethodName() {
return providers.get(0).getAuthMethodName();
}
@Override
public String authenticate(AuthenticationDataSource authData) throws AuthenticationException {
return applyAuthProcessor(
providers,
provider -> provider.authenticate(authData)
);
}
@Override
public AuthenticationState newAuthState(AuthData authData, SocketAddress remoteAddress, SSLSession sslSession)
throws AuthenticationException {
final List<AuthenticationState> states = new ArrayList<>(providers.size());
AuthenticationException authenticationException = null;
try {
applyAuthProcessor(
providers,
provider -> {
AuthenticationState state = provider.newAuthState(authData, remoteAddress, sslSession);
states.add(state);
return state;
}
);
} catch (AuthenticationException ae) {
authenticationException = ae;
}
if (states.isEmpty()) {
log.debug("Failed to initialize a new auth state from {}", remoteAddress, authenticationException);
if (authenticationException != null) {
throw authenticationException;
} else {
throw new AuthenticationException("Failed to initialize a new auth state from " + remoteAddress);
}
} else {
return new AuthenticationListState(states);
}
}
@Override
public boolean authenticateHttpRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
Boolean authenticated = applyAuthProcessor(
providers,
provider -> {
try {
return provider.authenticateHttpRequest(request, response);
} catch (Exception e) {
if (e instanceof AuthenticationException) {
throw (AuthenticationException) e;
} else {
throw new AuthenticationException("Failed to authentication http request");
}
}
}
);
return authenticated.booleanValue();
}
@Override
public void close() throws IOException {
for (AuthenticationProvider provider : providers) {
provider.close();
}
}
}