blob: 0794b6acce1646a9a64b9e32f98771a0143d8173 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.server.srvext.auth;
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 org.eclipse.statet.rj.RjException;
import org.eclipse.statet.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= "org.eclipse.statet.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]);
}
}
}