001/*******************************************************************************
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *******************************************************************************/
014package org.nuxeo.runtime.api.login;
015
016import java.util.concurrent.atomic.AtomicInteger;
017
018import javax.security.auth.login.AppConfigurationEntry;
019import javax.security.auth.login.Configuration;
020
021import org.apache.commons.logging.LogFactory;
022
023public class LoginConfiguration extends Configuration {
024
025    public static final LoginConfiguration INSTANCE = new LoginConfiguration();
026
027    protected final AtomicInteger counter = new AtomicInteger(0);
028
029    public interface Provider {
030
031        public AppConfigurationEntry[] getAppConfigurationEntry(String name);
032
033    }
034
035    protected final InheritableThreadLocal<Provider> holder = new InheritableThreadLocal<Provider>() {
036        @Override
037        protected Provider initialValue() {
038            return context.provider;
039        };
040    };
041
042    @Override
043    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
044        AppConfigurationEntry[] appConfigurationEntry = holder.get().getAppConfigurationEntry(name);
045        if (appConfigurationEntry == null) {
046            appConfigurationEntry = context.parent.getAppConfigurationEntry(name);
047        }
048        return appConfigurationEntry;
049
050    }
051
052    @Override
053    public void refresh() {
054        context.parent.refresh();
055    }
056
057    protected class InstallContext {
058        protected final Configuration parent = Configuration.getConfiguration();
059
060        protected final Provider provider;
061
062        protected final Thread thread = Thread.currentThread();
063
064        protected final Throwable stacktrace = new Throwable();
065
066        protected InstallContext(Provider provider) {
067            this.provider = provider;
068        }
069
070        @Override
071        public String toString() {
072            return "Login Installation Context [parent=" + parent + ", thread=" + thread + "]";
073        }
074
075    }
076
077    protected InstallContext context;
078
079    public void install(Provider provider) {
080        holder.set(provider);
081        int count = counter.incrementAndGet();
082        if (count == 1) {
083            context = new InstallContext(provider);
084            Configuration.setConfiguration(this);
085            LogFactory.getLog(LoginConfiguration.class).trace("installed login configuration", context.stacktrace);
086        }
087    }
088
089    public void uninstall() {
090        holder.remove();
091        int count = counter.decrementAndGet();
092        if (count == 0) {
093            LogFactory.getLog(LoginConfiguration.class).trace("uninstalled login configuration " + context.thread,
094                    context.stacktrace);
095            Configuration.setConfiguration(context.parent);
096            context = null;
097        }
098    }
099
100    /**
101     * @since 5.9.5
102     */
103    public void cleanupThisThread() {
104        holder.remove();
105    }
106
107}