/* * 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.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; 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.catalina.util.LifecycleBase; import org.apache.openejb.config.sys.PropertiesAdapter; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.apache.tomee.catalina.TomEERuntimeException; import org.apache.webbeans.config.WebBeansContext; import org.apache.xbean.recipe.ObjectRecipe; import org.apache.xbean.recipe.Option; import org.ietf.jgss.GSSContext; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.security.Principal; import java.security.cert.X509Certificate; import java.util.Properties; import java.util.Set; public class LazyRealm extends LifecycleBase implements Realm { private String realmClass; private String properties; private boolean cdi; private volatile Realm delegate; private Context container; private CredentialHandler credentialHandler; private volatile boolean init; private volatile boolean start; private final PropertyChangeSupport support = new PropertyChangeSupport(this); private CreationalContext<Object> creationalContext; public void setRealmClass(final String realmClass) { this.realmClass = realmClass; } public void setProperties(final String properties) { this.properties = properties; } public void setCdi(final boolean cdi) { this.cdi = cdi; } private Realm instance() { if (delegate == null) { synchronized (this) { if (delegate == null) { final Object instance; ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (container != null && container.getLoader() != null && container.getLoader().getClassLoader() != null) { cl = container.getLoader().getClassLoader(); } final Class<?> clazz; try { clazz = cl.loadClass(realmClass); } catch (final ClassNotFoundException e) { throw new TomEERuntimeException(e); } if (!cdi) { try { final ObjectRecipe recipe = new ObjectRecipe(clazz); recipe.allow(Option.CASE_INSENSITIVE_PROPERTIES); recipe.allow(Option.IGNORE_MISSING_PROPERTIES); recipe.allow(Option.FIELD_INJECTION); recipe.allow(Option.PRIVATE_PROPERTIES); if (properties != null) { final Properties props = new PropertiesAdapter() .unmarshal(properties.trim().replaceAll("\\p{Space}*(\\p{Alnum}*)=", "\n$1=")); recipe.setAllProperties(props); } instance = recipe.create(); } catch (final Exception e) { throw new TomEERuntimeException(e); } } else { final WebBeansContext webBeansContext; try { webBeansContext = WebBeansContext.currentInstance(); if (webBeansContext == null) { return null; } } catch (final IllegalStateException ise) { return null; // too early to have a cdi bean, skip these methods - mainly init() but @PostConstruct works then } final BeanManager bm = webBeansContext.getBeanManagerImpl(); final Set<Bean<?>> beans = bm.getBeans(clazz); final Bean<?> bean = bm.resolve(beans); if (bean == null) { return null; } creationalContext = bm.createCreationalContext(null); instance = bm.getReference(bean, clazz, creationalContext); } if (instance == null) { throw new TomEERuntimeException("realm can't be retrieved from cdi"); } if (instance instanceof Realm) { delegate = (Realm) instance; delegate.setContainer(container); delegate.setCredentialHandler(credentialHandler); if (Lifecycle.class.isInstance(delegate)) { if (init) { try { final Lifecycle lifecycle = Lifecycle.class.cast(delegate); lifecycle.init(); if (start) { lifecycle.start(); } } catch (final LifecycleException e) { // no-op } } } } else { delegate = new LowTypedRealm(instance); delegate.setContainer(container); delegate.setCredentialHandler(credentialHandler); } for (final PropertyChangeListener listener : support.getPropertyChangeListeners()) { delegate.addPropertyChangeListener(listener); } } } } return delegate; } private Class<?> loadClass() { if (container != null && container.getLoader() != null && container.getLoader().getClassLoader() != null) { try { return container.getLoader().getClassLoader().loadClass(realmClass); } catch (final ClassNotFoundException e) { // no-op } } return null; } @Override protected void initInternal() throws LifecycleException { final Class<?> r = loadClass(); if (r != null && Lifecycle.class.isAssignableFrom(r) && instance() != null) { Lifecycle.class.cast(delegate).init(); } else { init = true; } } @Override protected void startInternal() throws LifecycleException { final Class<?> r = loadClass(); if (r != null && Lifecycle.class.isAssignableFrom(r) && instance() != null) { Lifecycle.class.cast(instance()).start(); } else { start = true; } setState(LifecycleState.STARTING); } @Override protected void stopInternal() throws LifecycleException { final Class<?> r = loadClass(); if (r != null && Lifecycle.class.isAssignableFrom(r) && instance() != null) { Lifecycle.class.cast(instance()).stop(); } setState(LifecycleState.STOPPING); } @Override protected void destroyInternal() throws LifecycleException { final Class<?> r = loadClass(); if (r != null && Lifecycle.class.isAssignableFrom(r) && instance() != null) { Lifecycle.class.cast(instance()).destroy(); } } @Override public Container getContainer() { if (delegate != null) { return delegate.getContainer(); } return container; } @Override public void setContainer(final Container container) { container.addLifecycleListener(new LifecycleListener() { @Override public void lifecycleEvent(final LifecycleEvent event) { if (Lifecycle.BEFORE_STOP_EVENT.equals(event.getType())) { if (creationalContext != null) { creationalContext.release(); } } } }); if (delegate != null) { delegate.setContainer(container); } else { this.container = Context.class.cast(container); } } @Override public CredentialHandler getCredentialHandler() { return credentialHandler; } @Override public void setCredentialHandler(final CredentialHandler credentialHandler) { this.credentialHandler = credentialHandler; final Class<?> r = loadClass(); if (r != null && instance() != null) { delegate.setCredentialHandler(credentialHandler); } } @Override public void addPropertyChangeListener(final PropertyChangeListener listener) { if (delegate != null) { delegate.addPropertyChangeListener(listener); } support.addPropertyChangeListener(listener); } @Override public Principal authenticate(final String s) { return instance().authenticate(s); } @Override public Principal authenticate(final String username, final String credentials) { return instance().authenticate(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 instance().authenticate(username, digest, nonce, nc, cnonce, qop, realm, md5a2); } @Override public Principal authenticate(final GSSContext gssContext, final boolean storeCreds) { return instance().authenticate(gssContext, storeCreds); } @Override public Principal authenticate(final X509Certificate[] certs) { return instance().authenticate(certs); } @Override public void backgroundProcess() { if (delegate != null) { instance().backgroundProcess(); } } @Override public SecurityConstraint[] findSecurityConstraints(final Request request, final Context context) { return instance().findSecurityConstraints(request, context); } @Override public boolean hasResourcePermission(final Request request, final Response response, final SecurityConstraint[] constraint, final Context context) throws IOException { return instance().hasResourcePermission(request, response, constraint, context); } @Override public boolean hasRole(final Wrapper wrapper, final Principal principal, final String role) { return instance().hasRole(wrapper, principal, role); } @Override public boolean hasUserDataPermission(final Request request, final Response response, final SecurityConstraint[] constraint) throws IOException { return instance().hasUserDataPermission(request, response, constraint); } @Override public void removePropertyChangeListener(final PropertyChangeListener listener) { if (delegate != null) { delegate.removePropertyChangeListener(listener); } support.removePropertyChangeListener(listener); } @Override public String[] getRoles(final Principal principal) { return instance().getRoles(principal); } @Override public boolean isAvailable() { return true; } }