package org.openslx.dozmod.gui.window;
import java.awt.Component;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JList;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.Organization;
import org.openslx.dozmod.App;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.authentication.Authenticator;
import org.openslx.dozmod.authentication.Authenticator.AuthenticatorCallback;
import org.openslx.dozmod.authentication.EcpAuthenticator;
import org.openslx.dozmod.authentication.ShibbolethEcp;
import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode;
import org.openslx.dozmod.authentication.TestAccountAuthenticator;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.MainWindow;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.window.layout.LoginWindowLayout;
import org.openslx.dozmod.thrift.OrganizationCache;
import org.openslx.dozmod.thrift.Session;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import edu.kit.scc.dei.ecplean.ECPAuthenticationException;
/**
* @author Jonathan Bauer
*
*/
@SuppressWarnings("serial")
public class LoginWindow extends LoginWindowLayout {
private final static Logger LOGGER = Logger.getLogger(LoginWindow.class);
// TODO This has nothing to to with the layout
public static enum LOGIN_TYPE {
ECP(0),
TEST_ACCOUNT(1),
DIRECT_CONNECT(2);
public final int id;
private LOGIN_TYPE(final int id) {
this.id = id;
}
}
// authentication method to use for login attempts
protected LOGIN_TYPE loginType = null;
// text constants
private final String NO_USERNAME = "Kein Benutzername angegeben!";
private final String NO_PASSWORD = "Kein Passwort angegeben!";
public LoginWindow(Frame modalParent) {
// call the constructor of the superclass
super(modalParent);
// first do all listeners stuff
for (final LOGIN_TYPE type : LOGIN_TYPE.values()) {
loginTypes[type.id].setActionCommand(type.toString());
loginTypes[type.id].addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
idpLabel.setVisible(type == LOGIN_TYPE.ECP);
idpCombo.setVisible(type == LOGIN_TYPE.ECP);
loginType = type;
}
}
});
}
// check if we had saved an authentication method
String savedAuthMethod = Config.getAuthenticationMethod();
LOGIN_TYPE savedLoginType;
try {
savedLoginType = LOGIN_TYPE.valueOf(savedAuthMethod);
} catch (Exception e) {
// if no valid LOGIN_TYPE was saved, just enable the BWIDM button
savedLoginType = LOGIN_TYPE.ECP;
}
if (savedLoginType == LOGIN_TYPE.ECP) {
// disable login button til the idp list is here
loginButton.setEnabled(false);
}
// enable the corresponding button
loginTypes[savedLoginType.id].setSelected(true);
loginType = savedLoginType;
QuickTimer.scheduleOnce(new Task() {
List<Organization> orgs = null;
@Override
public void fire() {
try {
// Wait for proxy server init
App.waitForInit();
orgs = OrganizationCache.getAll();
} catch (Exception e) {
LOGGER.error("Error during execution: ", e);
}
// filter out every organisation without ecp
Iterator<Organization> iterator = orgs.iterator();
while (iterator.hasNext()) {
Organization current = iterator.next();
if (current == null | !current.isSetEcpUrl() | current.getEcpUrl().isEmpty())
iterator.remove();
}
// all fine, lets sort it
Collections.sort(orgs, new Comparator<Organization>() {
public int compare(Organization o1, Organization o2) {
return o1.getDisplayName().compareTo(o2.getDisplayName());
}
});
// now send the organisations back to the LoginWindow
// through populateIdpCombo()
Gui.asyncExec(new Runnable() {
@Override
public void run() {
populateIdpCombo(orgs);
loginButton.setEnabled(true);
}
});
}
});
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doLogin();
}
});
// add a key listener to the password field to trigger login
// when the user presses the ENTER key.
passwordField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doLogin();
}
}
});
// finally check if we had a saved username
String savedUsername = Config.getUsername();
if (savedUsername != null && !savedUsername.isEmpty()) {
usernameField.setText(savedUsername);
saveUsernameCheck.setSelected(true);
passwordField.requestFocus();
} else {
usernameField.requestFocus();
}
}
/**
* Called by the thread fetching the organization list from the cache
*
* @param orgs list of organization to show in the combo box
*/
public void populateIdpCombo(List<Organization> orgs) {
// sanity checks on orgs
if (orgs == null) {
LOGGER.error("No organizations received from the cache.");
return;
}
idpCombo.setModel(new DefaultComboBoxModel<Organization>(orgs.toArray(new Organization[orgs.size()])));
idpCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof Organization) {
Organization org = (Organization) value;
setText(org.getDisplayName());
}
return this;
}
});
// now check if we had a saved identity provider
String savedOrganizationId = Config.getIdentityProvider();
idpCombo.setSelectedItem(OrganizationCache.find(savedOrganizationId));
}
/**
* Saves various user choices to the config file.
* This gets triggered when the login button is activated.
*/
private void doSaveConfig() {
// first we need to check if the "Remember me" button is active
if (saveUsernameCheck.isSelected()) {
// save username
String username = usernameField.getText();
if (!username.isEmpty()) {
Config.setUsername(username);
}
} else {
Config.setUsername("");
}
// always save the authentication method and potentially the identity provider
Config.setAuthenticationMethod(loginType.toString());
// save the selected identity provider
Organization selectedOrg = idpCombo.getItemAt(idpCombo.getSelectedIndex());
if (selectedOrg != null) {
Config.setIdentityProvider(selectedOrg.organizationId);
}
}
/**
* Actually do the login using the username/password in the field using the
* authentication mechanism corresponding to the selected authButton
*
* @throws ECPAuthenticationException
*/
private void doLogin() {
// sanity check on loginType.
if (loginType == null) {
Gui.showMessageBox(this, "No login type set, a default should be set! Ignoring...",
MessageType.ERROR, LOGGER, null);
return;
}
// we are doing the login soon, first save the config
doSaveConfig();
// here we only check for the fields
String username = usernameField.getText();
String password = String.copyValueOf(passwordField.getPassword());
// login clicked, lets first read the fields
if (username.isEmpty()) {
Gui.showMessageBox(this, NO_USERNAME, MessageType.ERROR, LOGGER, null);
return;
}
if (password.isEmpty()) {
Gui.showMessageBox(this, NO_PASSWORD, MessageType.ERROR, LOGGER, null);
return;
}
// determine which organization was selected by the user.
Organization selectedOrg = idpCombo.getItemAt(idpCombo.getSelectedIndex());
// Setup login callback
final LoginWindow me = this;
AuthenticatorCallback authenticatorCallback = new AuthenticatorCallback() {
@Override
public void postLogin(ReturnCode returnCode, Throwable t) {
switch (returnCode) {
case NO_ERROR:
postSuccessfulLogin();
break;
case IDENTITY_PROVIDER_ERROR:
Gui.showMessageBox(me, "IdP Error", MessageType.ERROR, LOGGER, null);
break;
case SERVICE_PROVIDER_ERROR:
// here if we have t != null then we have not received a token
// if we have t, then the token is invalid.
Gui.showMessageBox(me, "Invalid token from the service provider!", MessageType.ERROR,
LOGGER, t);
break;
case UNREGISTERED_ERROR:
Gui.showMessageBox(me, "You are not registered to bwLehrpool. Please visit "
+ ShibbolethEcp.getRegistrationUrl() + " and register first.", MessageType.ERROR,
LOGGER, t);
break;
case INVALID_URL_ERROR:
Gui.showMessageBox(me, "ECP Authenticator says: Invalid URL.", MessageType.ERROR, LOGGER,
t);
break;
case GENERIC_ERROR:
default:
Gui.showMessageBox(me, "Internal error!", MessageType.ERROR, LOGGER, null);
break;
}
}
};
// now switch over the login types.
Authenticator authenticator;
switch (loginType) {
case ECP:
authenticator = new EcpAuthenticator(selectedOrg.getEcpUrl());
break;
case TEST_ACCOUNT:
authenticator = new TestAccountAuthenticator();
break;
case DIRECT_CONNECT:
Gui.showMessageBox(this, "Not yet implemented", MessageType.ERROR, LOGGER, null);
return;
default:
Gui.showMessageBox(this, "No login type selected!", MessageType.ERROR, LOGGER, null);
return;
}
// Excute login
try {
authenticator.login(username, password, authenticatorCallback);
} catch (Exception e) {
Gui.showMessageBox(this, "Authentication failed: " + e.getMessage(), MessageType.ERROR, LOGGER, e);
return;
}
}
/**
* Functions called by doLogin is the login process succeeded.
*/
private void postSuccessfulLogin() {
LOGGER.info(loginType.toString() + " succeeded.");
// TODO HACK HACK
Session.setSatelliteAddress("132.230.8.113");
ThriftManager.setSatelliteAddress(Session.getSatelliteAddress());
// Something like ThriftManager.setSatelliteAddress(Session.getSatelliteAddress()); (might need selection box)
Exception e = null;
try {
ThriftManager.getSatClient().isAuthenticated(Session.getSatelliteToken());
// now read the config to see if the user already agreed to the disclaimer
// if (DisclaimerWindow.shouldBeShown())
// VirtualizerNoticeWindow.open();
LOGGER.debug("Closing...");
dispose();
return;
} catch (Exception ex) {
e = ex;
}
Gui.showMessageBox(this, "Login succeeded, but Satellite rejected the session token. :-(",
MessageType.ERROR, LOGGER, e);
}
/**
* Opens the login window
*/
public static void open(Frame modalParent) {
LoginWindow win = new LoginWindow(modalParent);
MainWindow.centerShell(win);
win.setVisible(true);
}
}