package org.openslx.dozmod.gui.window;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Shell;
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
*
*/
public class LoginWindow extends LoginWindowLayout {
private final static Logger LOGGER = Logger.getLogger(LoginWindow.class);
// text constants
private final String NO_USERNAME = "Kein Benutzername angegeben!";
private final String NO_PASSWORD = "Kein Passwort angegeben!";
/**
* Constructor doing the setup of the logical GUI functions
* Fetches the organization list for BWIDM logins and
* adds mouse/keyboard event listeners to various widgets.
*
* @param mainShell
*/
public LoginWindow(final Shell mainShell) {
// call the constructor of the superclass
super(mainShell);
// fetch the list of the identity providers as an async Thread
// else the GUI is blocked until this is done.
idpCombo.add("Initialisiere...");
idpCombo.select(0);
idpCombo.setEnabled(false);
loginButton.setEnabled(false);
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) {
LoginWindow.LOGGER.error("Error during execution: ", e);
}
// now send the organisations back to the LoginWindow
// through populateIdpCombo()
Gui.display.asyncExec(new Runnable() {
@Override
public void run() {
if (isDisposed())
return;
populateIdpCombo(orgs);
loginButton.setEnabled(true);
}
});
}
});
// 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;
}
authButtons[savedLoginType.id].setSelection(true);
loginType = savedLoginType;
// enable the IDP combo only if we actually have items in it
idpText.setVisible(savedLoginType == LOGIN_TYPE.ECP);
idpCombo.setVisible(savedLoginType == LOGIN_TYPE.ECP);
// finally check if we had a saved username
String savedUsername = Config.getUsername();
if (savedUsername != null && !savedUsername.isEmpty()) {
usernameText.setText(savedUsername);
saveUsernameCheck.setSelection(true);
passwordText.setFocus();
} else
usernameText.setFocus();
// actions of the login button
loginButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
doSaveConfig();
doLogin();
}
});
// for save username checkbox.
saveUsernameCheck.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// clickedSaveUsernameCheck();
}
});
// selecting the "Authentifizierung über bwIDM" radio button
authButtons[0].addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
idpText.setVisible(true);
idpCombo.setEnabled(true);
idpCombo.setVisible(true);
loginType = LOGIN_TYPE.ECP;
}
});
// selecting the "Test-Zugang mit festem Benutzer" radio button
authButtons[1].addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
idpText.setVisible(false);
idpCombo.setVisible(false);
loginType = LOGIN_TYPE.TEST_ACCOUNT;
}
});
authButtons[2].setEnabled(false);
// selecting the "Direkte Verbindung zum Satteliten" radio button
authButtons[2].addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
idpText.setVisible(false);
idpCombo.setVisible(false);
loginType = LOGIN_TYPE.DIRECT_CONNECT;
}
});
// add a key listener to the password field to trigger login
// when the user presses the ENTER key.
passwordText.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
doSaveConfig();
doLogin();
}
}
});
}
public void populateIdpCombo(List<Organization> orgs) {
// safety check to see if the login window still exists
// when this function gets called by the other thread
if (this.isDisposed())
return;
// always clearup the list first
idpCombo.removeAll();
// sanity checks on orgs
if (orgs == null) {
LOGGER.error("No organizations received from the cache.");
idpCombo.add("No entries");
return;
}
// 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 check if we had a saved identity provider
String savedOrganizationId = Config.getIdentityProvider();
// add only organizations which have an ECP URL to the combobox
for (Organization o : orgs) {
if (o.getEcpUrl() == null | o.getEcpUrl().isEmpty())
continue;
idpCombo.add(o.displayName);
idpCombo.setData(o.displayName, o);
if (savedOrganizationId != null && !savedOrganizationId.isEmpty()
&& savedOrganizationId.equals(o.getOrganizationId()))
// select the organization we just added, this seems kinda bad - is there a better way?
idpCombo.select(idpCombo.getItemCount() - 1);
//idpCombo.select(idpCombo.indexOf(savedOrganizationId)); this is probably not optimal... but safer
}
// if no organization was saved, none is selected, so select the first one in the combobox
if (idpCombo.getSelectionIndex() == -1)
idpCombo.select(0);
// finally re-enable it
if (idpCombo.isVisible())
idpCombo.setEnabled(true);
}
/**
* 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.isEnabled()) {
// save username
String username = usernameText.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 = getSelectedOrganization();
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.getShell(), "No login type set, a default should be set! Ignoring...",
MessageType.ERROR, LOGGER, null);
return;
}
// here we only check for the fields
String username = usernameText.getText();
String password = passwordText.getText();
// login clicked, lets first read the fields
if (username.isEmpty()) {
Gui.showMessageBox(this.getShell(), NO_USERNAME, MessageType.ERROR, null, null);
return;
}
if (password.isEmpty()) {
Gui.showMessageBox(this.getShell(), NO_PASSWORD, MessageType.ERROR, null, null);
return;
}
// determine which organization was selected by the user.
// TODO: Needed for test accounts?
Organization selectedOrg = getSelectedOrganization();
// 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.getShell(), "IdP Error", MessageType.ERROR, null, 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.getShell(), "Invalid token from the service provider!",
MessageType.ERROR, LOGGER, t);
break;
case UNREGISTERED_ERROR:
Gui.showMessageBox(me.getShell(), "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.getShell(), "ECP Authenticator says: Invalid URL.",
MessageType.ERROR, LOGGER, t);
break;
case GENERIC_ERROR:
default:
Gui.showMessageBox(me.getShell(), "Internal error!", MessageType.ERROR, null, 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.getShell(), "Not yet implemented", MessageType.ERROR, null, null);
return;
default:
Gui.showMessageBox(this.getShell(), "No login type selected!", MessageType.ERROR, null, null);
return;
}
// Excute login
try {
authenticator.login(username, password, authenticatorCallback);
} catch (Exception e) {
Gui.showMessageBox(me.getShell(), "Authentication failed: " + e.getMessage(), MessageType.ERROR,
LOGGER, e);
return;
}
}
/**
* Functions called by doLogin is the login process succeeded.
*
* @param user user who logged in
*/
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())
MainWindow.openPopup(DisclaimerWindow.class, true, true);
getShell().dispose();
return;
} catch (Exception ex) {
e = ex;
}
Gui.showMessageBox(this.getShell(), "Login succeeded, but Satellite rejected the session token. :-(",
MessageType.ERROR, LOGGER, e);
}
/**
* @return the organization currently selected in the combobox for identity
* providers
*/
private final Organization getSelectedOrganization() {
// get the index of the selected item in the combobox
int selectionIndex = idpCombo.getSelectionIndex();
if (selectionIndex == -1) {
LOGGER.error("No identity provider is selected in the combobox. There should be a default value!");
return null;
}
return (Organization) idpCombo.getData(idpCombo.getItem(selectionIndex));
}
}