/*=============================================================================#
# Copyright (c) 2009-2016 Stephan Wahlbrink (WalWare.de) and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of either (per the licensee's choosing)
# - the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html, or
# - the GNU Lesser General Public License v2.1 or newer
# which accompanies this distribution, and is available at
# http://www.gnu.org/licenses/lgpl.html
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.rj.server.srvstdext;
import java.io.IOException;
import java.util.HashMap;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import de.walware.rj.RjException;
import de.walware.rj.server.srvext.ServerAuthMethod;
/**
* Draft for a JAAS auth method.
* Missing role/principal handling.
*/
public abstract class JaasAuthMethod extends ServerAuthMethod implements CallbackHandler, Runnable {
private static String JAAS_NAME = "de.walware.rj.server";
private class JaasConfig extends Configuration {
private final AppConfigurationEntry entry;
JaasConfig(final String clazz) {
this.entry = new AppConfigurationEntry(clazz, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, new HashMap());
}
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(final String name) {
return null;
}
@Override
public void refresh() {
}
}
private static final int STARTED = 1;
private static final int CALLBACK = 2;
private static final int PERFORM = 3;
private static final int LOGGED_IN = 4;
private static final int CANCEL = -1;
private static final int FAILED = -2;
private Configuration configuration;
private LoginContext context;
private Callback[] pendingLoginCallback;
private int pendingLogin;
private String pendingLoginMsg;
private final Thread loginThread;
protected JaasAuthMethod(final String id) {
super(id, true);
this.loginThread = new Thread(this);
}
@Override
public void doInit(final String arg) throws RjException {
this.configuration = Configuration.getConfiguration();
if (this.configuration.getAppConfigurationEntry(JAAS_NAME) == null) {
this.configuration = new JaasConfig(arg);
}
try {
this.context = new LoginContext(JAAS_NAME, new Subject(), this, this.configuration);
}
catch (final LoginException e) {
e.printStackTrace();
}
}
@Override
protected Callback[] doCreateLogin() throws RjException {
cancel();
this.loginThread.start();
synchronized (this) {
while (this.pendingLogin >= 0 && this.pendingLogin < CALLBACK) {
notifyAll();
try {
wait();
}
catch (final InterruptedException e) {}
}
}
if (this.pendingLogin == FAILED) {
throw new SecurityException("failed to init login", null);
}
return this.pendingLoginCallback;
}
@Override
protected String doPerformLogin(final Callback[] callbacks) throws LoginException, RjException {
this.pendingLoginCallback = callbacks;
this.pendingLogin = PERFORM;
synchronized (this) {
while (this.loginThread.isAlive()) {
notifyAll();
try {
wait();
}
catch (final InterruptedException e) {}
}
}
if (this.pendingLogin != LOGGED_IN) {
if (this.pendingLoginMsg != null) {
throw new LoginException(this.pendingLoginMsg);
}
else {
throw new SecurityException("", null);
}
}
return null;
}
private void cancel() {
this.pendingLoginCallback = null;
this.pendingLoginMsg = null;
this.pendingLogin = CANCEL;
synchronized (this) {
while (this.loginThread.isAlive()) {
notifyAll();
try {
wait();
}
catch (final InterruptedException e) {}
}
}
this.pendingLogin = 0;
}
@Override
public void run() {
this.pendingLogin = STARTED;
try {
this.context.login();
this.pendingLogin = LOGGED_IN;
this.context.logout();
}
catch (final LoginException e) {
this.pendingLogin = FAILED;
this.pendingLoginMsg = e.getLocalizedMessage();
}
catch (final Throwable e) {
this.pendingLogin = FAILED;
}
finally {
synchronized (this) {
notifyAll();
}
}
}
@Override
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
if (this.pendingLogin == STARTED) {
this.pendingLogin = CALLBACK;
this.pendingLoginCallback = callbacks;
synchronized (this) {
while (this.pendingLogin == CALLBACK) {
notifyAll();
try {
wait();
}
catch (final InterruptedException e) {}
}
}
if (this.pendingLogin != PERFORM) {
throw new IOException();
}
copyAnswer(this.pendingLoginCallback, callbacks);
}
else if (callbacks.length > 0){
this.pendingLogin = FAILED;
throw new UnsupportedCallbackException(callbacks[0]);
}
}
}