/*
* 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.tomee.catalina.realm;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.CredentialHandler;
import org.apache.catalina.Realm;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomee.catalina.TomEERuntimeException;
import org.ietf.jgss.GSSContext;
import javax.servlet.ServletSecurityElement;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class LowTypedRealm implements Realm {
private static final Class<?>[] GET_ROLES_ARGS = new Class<?>[]{Principal.class};
private static final Class<?>[] AUTHENTICATE_STRING_ARGS = new Class<?>[]{String.class};
private static final Class<?>[] SIMPLE_AUTHENTICATE_ARGS = new Class<?>[]{String.class, String.class};
private static final Class<?>[] AUTHENTICATE_ARGS = new Class<?>[]{String.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class};
private static final Class<?>[] GSCONTEXT_AUTHENTICATE = new Class<?>[]{GSSContext.class, Boolean.class};
private static final Class<?>[] X509CERT_AUTHENTICATE = new Class<?>[]{X509Certificate[].class};
private static final Class<?>[] FIND_SECURITY_CONSTRAINTS_CONSTRAINT = new Class<?>[]{HttpServletRequest.class, String.class};
private static final Class<?>[] HAS_RESOURCE_PERMISSION_CONSTRAINT = new Class<?>[]{HttpServletRequest.class, HttpServletResponse.class, Object[].class, String.class};
private static final Class<?>[] HAS_ROLE_CONSTRAINT = new Class<?>[]{Principal.class, String.class};
private static final Class<?>[] HAS_USER_DATA_PERMISSION_CONSTRAINT = new Class<?>[]{HttpServletRequest.class, HttpServletResponse.class, Object[].class};
private final Object delegate;
private final Method simpleAuthenticateMethod;
private final Method authenticateStringMethod;
private final Method authenticateMethod;
private final Method gsMethod;
private final Method findSecurityConstraintsMethod;
private final Method x509Method;
private final Method hasResourcePermissionMethod;
private final Method hasRoleConstraintMethod;
private final Method hasUserDataMethod;
private final Method getRoles;
private Container container;
public LowTypedRealm(final Object delegate) {
this.delegate = delegate;
final Class<?> clazz = delegate.getClass();
authenticateStringMethod = findMethod(clazz, AUTHENTICATE_STRING_ARGS);
simpleAuthenticateMethod = findMethod(clazz, SIMPLE_AUTHENTICATE_ARGS);
authenticateMethod = findMethod(clazz, AUTHENTICATE_ARGS);
gsMethod = findMethod(clazz, GSCONTEXT_AUTHENTICATE);
findSecurityConstraintsMethod = findMethod(clazz, FIND_SECURITY_CONSTRAINTS_CONSTRAINT);
x509Method = findMethod(clazz, X509CERT_AUTHENTICATE);
hasResourcePermissionMethod = findMethod(clazz, HAS_RESOURCE_PERMISSION_CONSTRAINT);
hasRoleConstraintMethod = findMethod(clazz, HAS_ROLE_CONSTRAINT);
hasUserDataMethod = findMethod(clazz, HAS_USER_DATA_PERMISSION_CONSTRAINT);
getRoles = findMethod(clazz, GET_ROLES_ARGS);
}
private Method findMethod(final Class<?> clazz, final Class<?>[] argTypes) {
for (final Method mtd : clazz.getMethods()) {
if (Modifier.isAbstract(mtd.getModifiers())) {
continue;
}
boolean match = true;
for (int i = 0; i < argTypes.length; i++) {
final Class<?>[] params = mtd.getParameterTypes();
if (params.length != argTypes.length) {
match = false;
break;
}
if (!argTypes[i].isAssignableFrom(params[i])) {
match = false;
break;
}
}
if (match) {
return mtd;
}
}
return null;
}
@Override
public Container getContainer() {
return container;
}
@Override
public void setContainer(final Container container) {
this.container = container;
}
@Override
public void addPropertyChangeListener(final PropertyChangeListener listener) {
// no-op
}
@Override
public void removePropertyChangeListener(final PropertyChangeListener listener) {
// no-op
}
@Override
public String[] getRoles(final Principal principal) {
return (String[]) invoke(getRoles, principal);
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public Principal authenticate(final String s) {
return (Principal) invoke(authenticateStringMethod, s);
}
@Override
public Principal authenticate(final String username, final String credentials) {
return (Principal) invoke(simpleAuthenticateMethod, username, credentials);
}
@Override
public Principal authenticate(final String username, final String digest, final String nonce,
final String nc, final String cnonce, final String qop,
final String realm, final String md5a2) {
return (Principal) invoke(authenticateMethod, username, digest, nonce, nc, cnonce, qop, realm, md5a2);
}
@Override
public Principal authenticate(final GSSContext gssContext, final boolean storeCreds) {
return (Principal) invoke(gsMethod, gssContext, storeCreds);
}
@Override
public Principal authenticate(final X509Certificate[] certs) {
return (Principal) invoke(x509Method, certs);
}
@Override
public boolean hasRole(final Wrapper wrapper, final Principal principal, final String role) {
return (Boolean) invoke(hasRoleConstraintMethod, principal, role);
}
@Override
public void backgroundProcess() {
// no-op
}
//
// next patterns can be fully reworked
//
@Override
public SecurityConstraint[] findSecurityConstraints(final Request request, final Context context) {
final Map<String, ServletSecurityElement> map = (Map<String, ServletSecurityElement>) invoke(findSecurityConstraintsMethod, request.getRequest(), context.getPath());
final List<SecurityConstraint> constraints = new ArrayList<SecurityConstraint>();
for (final Map.Entry<String, ServletSecurityElement> entry : map.entrySet()) {
constraints.addAll(Arrays.asList(SecurityConstraint.createConstraints(entry.getValue(), entry.getKey())));
}
return constraints.toArray(new SecurityConstraint[constraints.size()]);
}
@Override
public boolean hasResourcePermission(final Request request, final Response response,
final SecurityConstraint[] constraint,
final Context context) throws IOException {
return (Boolean) invoke(hasResourcePermissionMethod, request.getRequest(), response.getResponse(), constraint, context.getPath());
}
@Override
public boolean hasUserDataPermission(final Request request, final Response response, final SecurityConstraint[] constraint) throws IOException {
return (Boolean) invoke(hasUserDataMethod, request.getRequest(), response.getResponse(), constraint);
}
@Override
public CredentialHandler getCredentialHandler() {
return null;
}
@Override
public void setCredentialHandler(final CredentialHandler credentialHandler) {
// no-op: ignored, impl should handle it
}
private Object invoke(final Method method, final Object... args) {
if (method == null) {
return null;
}
try {
return method.invoke(delegate, args);
} catch (final InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
throw new TomEERuntimeException(e.getCause());
} catch (final IllegalAccessException e) {
throw new TomEERuntimeException(e);
}
}
}