diff options
16 files changed, 324 insertions, 185 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java index 430d0001..6c8b69b0 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java @@ -1,11 +1,7 @@ package org.openslx.dozmod.authentication; -import org.openslx.bwlp.thrift.iface.TAuthenticationException; -import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode; -import edu.kit.scc.dei.ecplean.ECPAuthenticationException; - /** * @author Jonathan Bauer * @@ -19,17 +15,17 @@ public interface Authenticator { * corresponding message to the user. */ interface AuthenticatorCallback { - void postLogin(ReturnCode returnCode, UserInfo user, Throwable t); + void postLogin(ReturnCode returnCode, Throwable t); } /** * Definition of the generic login method. * - * @param username The username as String. - * @param password The password as String. + * @param username The username as String + * @param password The password as String * @param callback The callback function to be called after the login - * @throws ECPAuthenticationException + * @throws Exception */ void login(String username, String password, AuthenticatorCallback callback) - throws TAuthenticationException; + throws Exception; }
\ No newline at end of file diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java index 9bbc4175..ab211386 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java @@ -1,14 +1,21 @@ package org.openslx.dozmod.authentication; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; + +import org.apache.http.ParseException; +import org.apache.http.client.ClientProtocolException; import org.apache.log4j.Logger; -import org.openslx.bwlp.thrift.iface.TAuthenticationException; -import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode; import org.openslx.dozmod.thrift.Session; +import com.google.gson.JsonSyntaxException; + /** * @author Jonathan Bauer - * + * */ public class EcpAuthenticator implements Authenticator { @@ -16,7 +23,7 @@ public class EcpAuthenticator implements Authenticator { * Logger instance for this class */ private final static Logger LOGGER = Logger.getLogger(EcpAuthenticator.class); - + private final String ecpUrl; public EcpAuthenticator(String ecpUrl) { @@ -30,49 +37,30 @@ public class EcpAuthenticator implements Authenticator { } @Override - public void login(String username, String password, - AuthenticatorCallback callback) throws TAuthenticationException { + public void login(String username, String password, AuthenticatorCallback callback) + throws TAuthorizationException, JsonSyntaxException, ClientProtocolException, ParseException, + MalformedURLException, URISyntaxException, IOException { // try to login - ReturnCode ret = null; - try { - ret = ShibbolethEcp.doLogin(this.ecpUrl, username, password); - } catch (Exception e) { - // TODO: This class should not do any GUI interaction.... - } - // if ret is still null, some exception happened, so abort. + ReturnCode ret = ShibbolethEcp.doLogin(this.ecpUrl, username, password); + if (ret == null) { - LOGGER.error("Error during the ECP authentication process."); - callback.postLogin(ReturnCode.GENERIC_ERROR, null, null); - return; + LOGGER.warn("Shibboleth doLogin returned null as ReturnCode!"); + ret = ReturnCode.GENERIC_ERROR; } - // else, we do have a valid ReturnCode? + // If login succeeded, set up session data if (ret == ReturnCode.NO_ERROR) { - final UserInfo userInfo; // we have a token? final String token = ShibbolethEcp.getResponse().token; if (token == null || token.isEmpty()) { // bad token LOGGER.error("No token received from the service provider!"); - callback.postLogin(ReturnCode.SERVICE_PROVIDER_ERROR, null, null); + callback.postLogin(ReturnCode.SERVICE_PROVIDER_ERROR, null); } // create the session for the user from the response of the ECP Session.fromEcpLogin(ShibbolethEcp.getResponse()); - - // build userInfo from the information received - userInfo = new UserInfo(Session.getUserId(), - Session.getFirstName(), - Session.getLastName(), - Session.getEMail(), - Session.getOrganizationId()); - - // send it back to the GUI - callback.postLogin(ReturnCode.NO_ERROR, userInfo, null); - } else { - // else just return the ReturnCode to the GUI - // it should then show a corresponding error message! - callback.postLogin(ret, null, null); } + callback.postLogin(ret, null); } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/ShibbolethEcp.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/ShibbolethEcp.java index e0eabb91..99c55be6 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/ShibbolethEcp.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/ShibbolethEcp.java @@ -12,8 +12,8 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; -import org.openslx.bwlp.thrift.iface.AuthenticationError; -import org.openslx.bwlp.thrift.iface.TAuthenticationException; +import org.openslx.bwlp.thrift.iface.AuthorizationError; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -33,44 +33,46 @@ public class ShibbolethEcp { * Static gson object for (de)serialization */ private static final Gson GSON = new GsonBuilder().create(); - + /** * ServiceProviderResponse Object representing the last response we received */ private static ServiceProviderResponse lastResponse = null; /** - * URL for bwLehrpool registration + * URL for bwLehrpool registration */ private static URL registrationUrl = null; + /** * Return codes */ public static enum ReturnCode { // TODO rework this... - NO_ERROR(0, "Authentication against the identity provider and request of the service provider resource worked."), + NO_ERROR(0, + "Authentication against the identity provider and request of the service provider resource worked."), IDENTITY_PROVIDER_ERROR(1, "Authentication against the identity provider failed."), UNREGISTERED_ERROR(2, "User not registered to use bwLehrpool."), SERVICE_PROVIDER_ERROR(3, "Invalid resource of the service provider."), INVALID_URL_ERROR(4, "Invalid URL received from master server."), GENERIC_ERROR(5, "Internal error."); - private final int id; - private final String msg; + private final int id; + private final String msg; - ReturnCode(int id, String msg) { - this.id = id; - this.msg = msg; - } + ReturnCode(int id, String msg) { + this.id = id; + this.msg = msg; + } - public int getId() { - return this.id; - } + public int getId() { + return this.id; + } - public String getMsg() { - return this.msg; - } + public String getMsg() { + return this.msg; + } } - + /** * Static URI to the SP. */ @@ -90,21 +92,23 @@ public class ShibbolethEcp { public static ServiceProviderResponse getResponse() { return lastResponse; } + /** * Fetches the resource - * + * * @param idpUrl - * URL of the identity provider to authenticate against, as String. + * URL of the identity provider to authenticate against, as + * String. * @param user * Username as String. * @param pass * Password as String. * @return - * true if login worked, false otherwise. - * @throws TAuthenticationException + * true if login worked, false otherwise. + * @throws TAuthorizationException */ public static ReturnCode doLogin(final String idpUrl, final String user, final String pass) - throws TAuthenticationException, URISyntaxException, ClientProtocolException, IOException, + throws TAuthorizationException, URISyntaxException, ClientProtocolException, IOException, ParseException, JsonSyntaxException, MalformedURLException { // first lets do some sanity checks @@ -127,43 +131,44 @@ public class ShibbolethEcp { // now init the authenticator for that idp and our static sp final ECPAuthenticator auth = new ECPAuthenticator(user, pass, new URI(idpUrl), BWLP_SP); - - try { + + try { auth.authenticate(); - } catch (ECPAuthenticationException e) { + } catch (ECPAuthenticationException e) { LOGGER.error("ECP Authentication Exception, see trace: ", e); - throw new TAuthenticationException(AuthenticationError.GENERIC_ERROR, e.getMessage()); - } - // here test again for the SP's URL - final HttpGet testSp = new HttpGet(BWLP_SP); - final HttpResponse response = auth.getHttpClient().execute(testSp); - - LOGGER.debug("SP request returned: " + response.getStatusLine()); - final String responseBody = EntityUtils.toString(response.getEntity()); - - lastResponse = GSON.fromJson(responseBody, ServiceProviderResponse.class); - - // TODO: here we will need to parse the answer accordingly. - // no errors, meaning everything worked fine. - if (lastResponse.status.equals("unregistered")) { + throw new TAuthorizationException(AuthorizationError.GENERIC_ERROR, e.getMessage()); + } + // here test again for the SP's URL + final HttpGet testSp = new HttpGet(BWLP_SP); + final HttpResponse response = auth.getHttpClient().execute(testSp); + + LOGGER.debug("SP request returned: " + response.getStatusLine()); + final String responseBody = EntityUtils.toString(response.getEntity()); + + lastResponse = GSON.fromJson(responseBody, ServiceProviderResponse.class); + + // TODO: here we will need to parse the answer accordingly. + // no errors, meaning everything worked fine. + if (lastResponse.status.equals("unregistered")) { registrationUrl = new URL(lastResponse.url); return ReturnCode.UNREGISTERED_ERROR; - } - // TODO the rest of the cases... - if (lastResponse.status.equals("error")) { - LOGGER.error("Server side error: " + lastResponse.error); - return ReturnCode.GENERIC_ERROR; - } - if (lastResponse.status.equals("anonymous")) { - LOGGER.error("IdP did not forward user account information to SP. Contact developper."); - return ReturnCode.GENERIC_ERROR; - } - if (lastResponse.status.equals("ok")) { - return ReturnCode.NO_ERROR; - } - // still here? then something else went wrong - return ReturnCode.GENERIC_ERROR; + } + // TODO the rest of the cases... + if (lastResponse.status.equals("error")) { + LOGGER.error("Server side error: " + lastResponse.error); + return ReturnCode.GENERIC_ERROR; + } + if (lastResponse.status.equals("anonymous")) { + LOGGER.error("IdP did not forward user account information to SP. Contact developper."); + return ReturnCode.GENERIC_ERROR; + } + if (lastResponse.status.equals("ok")) { + return ReturnCode.NO_ERROR; + } + // still here? then something else went wrong + return ReturnCode.GENERIC_ERROR; } + /** * @return Registration URL given by the SP. */ diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java index 0c83ad0b..3059f9e8 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java @@ -3,9 +3,9 @@ package org.openslx.dozmod.authentication; import org.apache.log4j.Logger; import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.SessionData; -import org.openslx.bwlp.thrift.iface.TAuthenticationException; -import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode; +import org.openslx.dozmod.thrift.Session; import org.openslx.thrifthelper.ThriftManager; /** @@ -21,35 +21,19 @@ public class TestAccountAuthenticator implements Authenticator { @Override public void login(String username, String password, AuthenticatorCallback callback) - throws TAuthenticationException { + throws TAuthorizationException, TException { SessionData authResult = null; - // try to login user - try { - authResult = ThriftManager.getMasterClient().authenticate(username, password); - } catch (TException e) { - LOGGER.error("Thrift communication error: ", e); - // TODO authenticate has to return a TAuthenticationException! - callback.postLogin(ReturnCode.GENERIC_ERROR, null, e); - return; - } + authResult = ThriftManager.getMasterClient().authenticate(username, password); // handle answer from server if (authResult != null && authResult.authToken != null) { - // TODO: Session.fromClientSessionData(authResult); - UserInfo userInfo = null; - try { - userInfo = ThriftManager.getMasterClient().getUserFromToken(authResult.authToken); - } catch (TException e) { - LOGGER.error("Thrift communication error: ", e); - // TODO authenticate has to return a TAuthenticationException! - callback.postLogin(ReturnCode.GENERIC_ERROR, null, e); - return; - } - callback.postLogin(ReturnCode.NO_ERROR, userInfo, null); + LOGGER.info(authResult); + Session.fromSessionData(authResult); + callback.postLogin(ReturnCode.NO_ERROR, null); } else { // it should then show a corresponding error message! - callback.postLogin(ReturnCode.GENERIC_ERROR, null, null); + callback.postLogin(ReturnCode.GENERIC_ERROR, null); } } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java index 8e5231ae..42fbf165 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java @@ -193,7 +193,8 @@ public class Gui { if (logger != null) logger.log(messageType.logPriority, message, exception); if (exception != null) - message += "\n\n" + exception.getClass().getSimpleName() + " (Siehe Logdatei)"; + message += "\n\n" + exception.getClass().getSimpleName() + "\n" + exception.getMessage() + "\n" + + " (Siehe Logdatei)"; MessageBox box = new MessageBox(parent, messageType.style); box.setMessage(message); box.setText(messageType.title); diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java index 8066c599..1b10ab80 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java @@ -12,8 +12,6 @@ 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.bwlp.thrift.iface.TAuthenticationException; -import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.dozmod.Config; import org.openslx.dozmod.authentication.Authenticator; import org.openslx.dozmod.authentication.Authenticator.AuthenticatorCallback; @@ -26,6 +24,7 @@ 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; @@ -251,9 +250,8 @@ public class LoginWindow extends LoginWindowLayout { 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); + 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 @@ -277,14 +275,7 @@ public class LoginWindow extends LoginWindowLayout { final LoginWindow me = this; AuthenticatorCallback authenticatorCallback = new AuthenticatorCallback() { @Override - public void postLogin(ReturnCode returnCode, UserInfo user, Throwable t) { - // TODO finish this - if (user == null) { - Gui.showMessageBox(me.getShell(), - "User information received from the masterserver is corrupt!", MessageType.ERROR, - LOGGER, null); - return; - } + public void postLogin(ReturnCode returnCode, Throwable t) { switch (returnCode) { case NO_ERROR: postSuccessfulLogin(); @@ -299,12 +290,15 @@ public class LoginWindow extends LoginWindowLayout { 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.", + 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; @@ -325,36 +319,44 @@ public class LoginWindow extends LoginWindowLayout { 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); + Gui.showMessageBox(this.getShell(), "No login type selected!", MessageType.ERROR, null, null); return; } // Excute login try { authenticator.login(username, password, authenticatorCallback); - } catch (TAuthenticationException e) { - Gui.showMessageBox(me.getShell(), "Authentication failed: " + e.getMessage(), - MessageType.ERROR, LOGGER, null); + } 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 ThriftManager.setSatelliteAddress("132.230.8.113"); - - // now read the config to see if the user already agreed to the disclaimer - if (DisclaimerWindow.shouldBeShown()) - MainWindow.openPopup(DisclaimerWindow.class, true, true); - // TODO: Add notice about VMware etc... - - getShell().dispose(); + // 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); } /** diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/Session.java b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/Session.java index c4e6bb6b..1f4cf0b0 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/Session.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/Session.java @@ -1,7 +1,12 @@ package org.openslx.dozmod.thrift; +import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.SessionData; +import org.openslx.bwlp.thrift.iface.TInvalidTokenException; +import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.dozmod.authentication.ServiceProviderResponse; +import org.openslx.thrifthelper.ThriftManager; public class Session { @@ -29,11 +34,23 @@ public class Session { masterToken = session.sessionId; satelliteToken = session.authToken; } + + public static void fromSessionData(SessionData session) throws TInvalidTokenException, TException { + // TODO: This is legacy API, switch to ClientSessionData asap + UserInfo ui = ThriftManager.getMasterClient().getUserFromToken(session.authToken); + if (userId != null && !userId.equals(ui.userId)) + throw new IllegalArgumentException("Cannot set new session data with different user id!"); + firstName = ui.firstName; + lastName = ui.lastName; + eMail = ui.eMail; + userId = ui.userId; + masterToken = session.sessionId; + satelliteToken = session.authToken; + } public static void fromEcpLogin(ServiceProviderResponse response) { - // TODO - //if (userId != null && !userId.equals(response.userId)) - // throw new IllegalArgumentException("Cannot set new session data with different user id!"); + if (userId != null && !userId.equals(response.userId)) + throw new IllegalArgumentException("Cannot set new session data with different user id!"); firstName = response.firstName; lastName = response.lastName; eMail = response.mail; diff --git a/dozentenmodulserver/setup/sat-01-schema.sql b/dozentenmodulserver/setup/sat-01-schema.sql index e07156f8..bb4a4e5c 100644 --- a/dozentenmodulserver/setup/sat-01-schema.sql +++ b/dozentenmodulserver/setup/sat-01-schema.sql @@ -114,7 +114,7 @@ CREATE TABLE IF NOT EXISTS `imageversion` ( `isvalid` tinyint(1) NOT NULL, `isprocessed` tinyint(1) NOT NULL, `mastersha1` binary(20) DEFAULT NULL, - `virtualizerconfig` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT 'Specific configuration of the virtualizer for this image. For vmware, this is basically a dump of the *.vmx.', + `virtualizerconfig` text BINARY NULL DEFAULT NULL COMMENT 'Specific configuration of the virtualizer for this image. For vmware, this is basically a dump of the *.vmx.', PRIMARY KEY (`imageversionid`), KEY `version_access` (`imagebaseid`,`isenabled`,`isvalid`,`createtime`), KEY `fk_imageversion_2_idx` (`uploaderid`), diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java index 992d5562..570cf1fa 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java @@ -55,6 +55,7 @@ public class App { ThriftManager.setMasterServerAddress("bwlp-masterserver.ruf.uni-freiburg.de"); // Load useful things from master server + //LOGGER.info(ThriftManager.getMasterClient().getUserFromToken("9ECAC1AFC02FF295292362BD165847AE")); OrganizationList.get(); OperatingSystemList.get(); diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java index fc78a5aa..3f392048 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java @@ -70,10 +70,10 @@ public class DbImage { public static ImageDetailsRead getImageDetails(UserInfo user, String imageBaseId) throws TNotFoundException, SQLException { try (MysqlConnection connection = Database.getConnection()) { - MysqlStatement stmt = connection.prepareStatement("SELECT imagebaseid, currentversionid, latestversionid," - + " displayname, description, osid, virtid, createtime, updatetime, ownerid, updaterid," - + " sharemode, istemplate," - + " canlinkdefault, candownloaddefault, caneditdefault, canadmindefault," + MysqlStatement stmt = connection.prepareStatement("SELECT i.imagebaseid, i.currentversionid, i.latestversionid," + + " i.displayname, i.description, i.osid, i.virtid, i.createtime, i.updatetime, i.ownerid, i.updaterid," + + " i.sharemode, i.istemplate," + + " i.canlinkdefault, i.candownloaddefault, i.caneditdefault, i.canadmindefault," + " perm.canlink, perm.candownload, perm.canedit, perm.canadmin" + " FROM imagebase i" + " LEFT JOIN imagepermission perm ON (i.imagebaseid = perm.imagebaseid AND perm.userid = :userid)" @@ -95,7 +95,7 @@ public class DbImage { toShareMode(rs.getString("sharemode")), rs.getByte("istemplate") != 0, defaultPermissions); return image; } catch (SQLException e) { - LOGGER.error("Query failed in DbImage.getImage()", e); + LOGGER.error("Query failed in DbImage.getImageDetails()", e); throw e; } } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbOsVirt.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbOsVirt.java index e092e944..f49c9065 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbOsVirt.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbOsVirt.java @@ -3,13 +3,16 @@ package org.openslx.bwlp.sat.database.mappers; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.log4j.Logger; import org.openslx.bwlp.sat.database.Database; import org.openslx.bwlp.sat.database.MysqlConnection; import org.openslx.bwlp.sat.database.MysqlStatement; import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.bwlp.thrift.iface.Virtualizer; public class DbOsVirt { @@ -29,20 +32,22 @@ public class DbOsVirt { } connection.commit(); } catch (SQLException e) { - LOGGER.error("Query failed in DbOsVirt.writeOsList()", e); + LOGGER.error("Query failed in DbOsVirt.storeOsList()", e); throw e; } } public static List<OperatingSystem> getOsList() throws SQLException { try (MysqlConnection connection = Database.getConnection()) { + // Query OSs MysqlStatement stmt = connection.prepareStatement("SELECT osid, displayname, architecture" + " FROM operatingsystem"); ResultSet rs = stmt.executeQuery(); List<OperatingSystem> list = new ArrayList<>(); + Map<Integer, Map<String, String>> osVirtMappings = getOsVirtMappings(connection); while (rs.next()) { - // TODO: virt mapping - list.add(new OperatingSystem(rs.getInt("osid"), rs.getString("displayname"), null, + int osId = rs.getInt("osid"); + list.add(new OperatingSystem(osId, rs.getString("displayname"), osVirtMappings.get(osId), rs.getString("architecture"))); } return list; @@ -51,5 +56,55 @@ public class DbOsVirt { throw e; } } + + private static Map<Integer, Map<String, String>> getOsVirtMappings(MysqlConnection connection) throws SQLException { + MysqlStatement stmt = connection.prepareStatement("SELECT osid, virtid, virtoskeyword FROM os_x_virt"); + ResultSet rs = stmt.executeQuery(); + Map<Integer, Map<String, String>> map = new HashMap<>(); + while (rs.next()) { + Integer osId = rs.getInt("osid"); + Map<String, String> osMap = map.get(osId); + if (osMap == null) { + osMap = new HashMap<>(); + map.put(osId, osMap); + } + osMap.put(rs.getString("virtid"), rs.getString("virtoskeyword")); + } + return map; + } + + public static void storeVirtualizerList(List<Virtualizer> list) throws SQLException { + try (MysqlConnection connection = Database.getConnection()) { + MysqlStatement stmt = connection.prepareStatement("INSERT INTO virtualizer" + + " (virtid, virtname) VALUES" + + " (:virtid, :virtname)" + + " ON DUPLICATE KEY UPDATE virtname = VALUES(virtname)"); + for (Virtualizer virt : list) { + stmt.setString("virtid", virt.virtId); + stmt.setString("displayname", virt.virtName); + stmt.executeUpdate(); + } + connection.commit(); + } catch (SQLException e) { + LOGGER.error("Query failed in DbOsVirt.storeVirtualizerList()", e); + throw e; + } + } + + public static List<Virtualizer> getVirtualizerList() throws SQLException { + try (MysqlConnection connection = Database.getConnection()) { + MysqlStatement stmt = connection.prepareStatement("SELECT virtid, virtname" + + " FROM virtualizer"); + ResultSet rs = stmt.executeQuery(); + List<Virtualizer> list = new ArrayList<>(); + while (rs.next()) { + list.add(new Virtualizer(rs.getString("virtid"), rs.getString("virtname"))); + } + return list; + } catch (SQLException e) { + LOGGER.error("Query failed in DbOsVirt.getVirtualizerList()", e); + throw e; + } + } } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/permissions/User.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/permissions/User.java index 86660ac1..34fc6cce 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/permissions/User.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/permissions/User.java @@ -41,29 +41,32 @@ public class User { * Check if given user is allowed to login to this satellite. * * @param user user to check login permission for - * @return true if user is allowed to login to this satellite + * @return null if user is allowed, {@link AuthorizationError} otherwise */ - public static boolean canLogin(UserInfo user) { + public static AuthorizationError canLogin(UserInfo user) { LocalUser localData = LocalData.getLocalUser(user); - if (localData != null) - return localData.canLogin; // User locally known, use user-specific permission + if (localData != null) { + if (localData.canLogin) + return null; // User locally known, use user-specific permission + return AuthorizationError.ACCOUNT_SUSPENDED; + } LocalOrganization local = LocalData.getLocalOrganization(user.organizationId); // User unknown, check per-organization login permission if (local == null) - return false; + return AuthorizationError.INVALID_ORGANIZATION; if (local.canLogin) - return true; + return null; // Special case: If user is not allowed to login, check if there are no allowed // organizations yet. If so, automatically allow the organization of this user. try { if (DbOrganization.getLoginAllowedOrganizations().isEmpty()) { DbOrganization.setCanLogin(user.organizationId, true); - return true; + return null; } } catch (SQLException e) { // Ignore } - return false; + return AuthorizationError.GENERIC_ERROR; } /** diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/BinaryListener.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/BinaryListener.java index 70c47edb..3e0159b6 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/BinaryListener.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/BinaryListener.java @@ -49,9 +49,9 @@ public class BinaryListener implements Runnable { final TNonblockingServerTransport serverTransport; try { serverTransport = new TNonblockingServerSocket(port); - log.fatal("Listening on port " + port + " (plain handler)"); + log.info("Listening on port " + port + " (plain handler)"); } catch (TTransportException e) { - log.fatal("Could not listen on port " + port + " (plain handler)"); + log.info("Could not listen on port " + port + " (plain handler)"); throw e; } THsHaServer.Args args = new THsHaServer.Args(serverTransport); diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java index 24a110e5..22a90de7 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java @@ -105,8 +105,8 @@ public class ServerHandler implements SatelliteServer.Iface { */ @Override - public boolean isAuthenticated(String userToken) { - return SessionManager.get(userToken) != null; + public void isAuthenticated(String userToken) throws TAuthorizationException, TInternalServerError { + SessionManager.ensureAuthenticated(userToken); } @Override diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/SessionManager.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/SessionManager.java index 1f36adfb..936b7a06 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/SessionManager.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/SessionManager.java @@ -12,6 +12,7 @@ import org.openslx.bwlp.sat.permissions.User; import org.openslx.bwlp.thrift.iface.AuthorizationError; import org.openslx.bwlp.thrift.iface.Role; import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.TInternalServerError; import org.openslx.bwlp.thrift.iface.TInvalidTokenException; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.thrifthelper.ThriftManager; @@ -80,14 +81,36 @@ public class SessionManager { } /** - * Get the user corresponding to the given token. Returns null if the token - * is not known, or the session already timed out. + * Do nothing if a user belongs to the given token and is authorized to use + * this satellite, throw an exception otherwise. + * + * @param token Token in question + * @throws TAuthorizationException + * @throws TInternalServerError + */ + public static void ensureAuthenticated(String token) throws TAuthorizationException, + TInternalServerError { + getInternal(token); + } + + /** + * Get the user corresponding to the given token. Returns <code>null</code> + * if the token is not known, or the session already timed out. * * @param token user's token * @return UserInfo for the matching user */ public static UserInfo get(String token) { + try { + return getInternal(token); + } catch (TAuthorizationException | TInternalServerError e) { + return null; + } + } + + private static UserInfo getInternal(String token) throws TAuthorizationException, TInternalServerError { Entry e = tokenManager.get(token); + LOGGER.info("Cache miss for token " + token + ", asking master"); if (e == null) return getRemote(token); // User session already cached @@ -114,25 +137,63 @@ public class SessionManager { * * @param token token of user to get * @return + * @throws TAuthorizationException if user is not allowed to use this + * satellite, this exception contains the reason + * @throws TInternalServerError if something unexpected fails */ - private static UserInfo getRemote(String token) { + private static UserInfo getRemote(String token) throws TAuthorizationException, TInternalServerError { UserInfo ui = null; try { ui = ThriftManager.getMasterClient().getUserFromToken(token); } catch (TInvalidTokenException ite) { - return null; // Token not known to master server + LOGGER.warn("Invalid token: " + token, ite); + throw new TAuthorizationException(AuthorizationError.INVALID_TOKEN, + "Your token is not known to the master server"); } catch (Exception e) { - LOGGER.warn("Could not reach master server to query for user token of a client!", e); + LOGGER.warn("Could not reach master server to query for user token (" + token + ") of a client!", + e); + throw new TInternalServerError(); } + LOGGER.info("Got user " + ui.userId + " for token " + token); + // TODO XXX HACK: Remove this once master server supplies role + if (ui.role == null) + ui.role = Role.TUTOR; // Valid reply, check if user is allowed to communicate with this satellite server - if (!User.canLogin(ui)) - return null; + AuthorizationError authError = User.canLogin(ui); + if (authError != null) { + LOGGER.info("User " + ui.userId + " cannot login: " + authError.toString()); + switch (authError) { + case ACCOUNT_SUSPENDED: + throw new TAuthorizationException(authError, + "Your account is not allowed to log in to this satellite"); + case BANNED_NETWORK: + throw new TAuthorizationException(authError, "Your IP address is banned from this satellite"); + case INVALID_CREDENTIALS: + case INVALID_KEY: + case CHALLENGE_FAILED: + throw new TAuthorizationException(authError, "Authentication error"); + case INVALID_ORGANIZATION: + throw new TAuthorizationException(authError, + "Your organization is not known to this satellite"); + case ORGANIZATION_SUSPENDED: + throw new TAuthorizationException(authError, + "Your organization is not allowed to log in to this satellite"); + case NOT_AUTHENTICATED: + case NO_PERMISSION: + throw new TAuthorizationException(authError, "No permission"); + case GENERIC_ERROR: + case INVALID_TOKEN: + default: + throw new TAuthorizationException(authError, "Internal server error"); + } + } // Is valid, insert/update db record, but ignore students if (ui.role != Role.STUDENT) { try { DbUser.writeUserOnLogin(ui); } catch (SQLException e) { - return null; + LOGGER.info("User " + ui.userId + " cannot be written to DB - rejecting."); + throw new TInternalServerError(); } } tokenManager.put(token, new Entry(ui)); diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/cache/VirtualizerList.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/cache/VirtualizerList.java index 87b85186..469096d8 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/cache/VirtualizerList.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/cache/VirtualizerList.java @@ -1,7 +1,11 @@ package org.openslx.bwlp.sat.thrift.cache; import java.util.List; +import org.apache.log4j.Logger; +import org.openslx.bwlp.sat.database.mappers.DbOsVirt; + +import java.sql.SQLException; import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.thrifthelper.ThriftManager; @@ -12,6 +16,8 @@ import org.openslx.thrifthelper.ThriftManager; */ public class VirtualizerList extends CacheBase<List<Virtualizer>> { + private static final Logger LOGGER = Logger.getLogger(VirtualizerList.class); + private static final VirtualizerList instance = new VirtualizerList(); public static List<Virtualizer> get() { @@ -20,8 +26,28 @@ public class VirtualizerList extends CacheBase<List<Virtualizer>> { @Override protected List<Virtualizer> getCallback() throws TException { - return ThriftManager.getMasterClient().getVirtualizers(); - // TODO: Store in DB + final List<Virtualizer> list; + try { + list = ThriftManager.getMasterClient().getVirtualizers(); + } catch (TException e1) { + LOGGER.warn("Could not fetch Virtualizer list from master, using local data...", e1); + try { + return DbOsVirt.getVirtualizerList(); + } catch (SQLException e) { + LOGGER.warn("Using local Virtualizer list from database also failed.", e); + } + return null; + } +// QuickTimer.scheduleOnce(new Task() { +// @Override +// public void fire() { +// try { +// DbOsVirt.storeOsList(list); +// } catch (SQLException e) { +// } +// } +// }); + return list; } } |