summaryrefslogtreecommitdiffstats
path: root/dozentenmodul/src
diff options
context:
space:
mode:
authorSimon Rettberg2023-02-23 15:44:18 +0100
committerSimon Rettberg2023-02-23 15:44:18 +0100
commit998df88dac37f5932fa7dd40877c947bf234806a (patch)
tree509c761e377605d8d4fa9b8c68ea6e88eae13d35 /dozentenmodul/src
parent[client] Use branded updateserver url for download link (diff)
downloadtutor-module-998df88dac37f5932fa7dd40877c947bf234806a.tar.gz
tutor-module-998df88dac37f5932fa7dd40877c947bf234806a.tar.xz
tutor-module-998df88dac37f5932fa7dd40877c947bf234806a.zip
[client] Add authentication via external browser
Diffstat (limited to 'dozentenmodul/src')
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java5
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/authentication/BrowserAuthenticator.java99
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java4
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java4
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java66
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LoginWindowLayout.java64
-rw-r--r--dozentenmodul/src/main/properties/i18n/window.properties3
-rw-r--r--dozentenmodul/src/main/properties/i18n/window_de_DE.properties1
-rw-r--r--dozentenmodul/src/main/properties/i18n/window_layout.properties5
-rw-r--r--dozentenmodul/src/main/properties/i18n/window_layout_de_DE.properties5
10 files changed, 224 insertions, 32 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 733aab01..c654ed8b 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/Authenticator.java
@@ -42,4 +42,9 @@ public interface Authenticator {
* @throws Exception
*/
void login(String username, String password, AuthenticatorCallback callback) throws Exception;
+
+ /**
+ * Cancel any running login attempt.
+ */
+ void cancel();
} \ No newline at end of file
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/BrowserAuthenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/BrowserAuthenticator.java
new file mode 100644
index 00000000..f6587edf
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/BrowserAuthenticator.java
@@ -0,0 +1,99 @@
+package org.openslx.dozmod.authentication;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.hc.client5.http.ClientProtocolException;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.thrift.TException;
+import org.openslx.bwlp.thrift.iface.ClientSessionData;
+import org.openslx.bwlp.thrift.iface.TAuthorizationException;
+import org.openslx.bwlp.thrift.iface.TNotFoundException;
+import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode;
+import org.openslx.dozmod.gui.control.QLabel;
+import org.openslx.dozmod.gui.helper.I18n;
+import org.openslx.dozmod.util.DesktopEnvironment;
+import org.openslx.thrifthelper.ThriftManager;
+import org.openslx.util.Util;
+
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * Authenticator that relies on opening an external browser/website which should
+ * eventually lead to the provided token yielding a valid session.
+ */
+public class BrowserAuthenticator implements Authenticator {
+
+ /**
+ * Logger instance for this class
+ */
+ private final static Logger LOGGER = LogManager.getLogger(BrowserAuthenticator.class);
+
+ private static final String[] ANIMATION = { " -", " \\", " |", " /" };
+
+ private volatile boolean cancelled = false;
+
+ private final QLabel lblError;
+
+ public BrowserAuthenticator(QLabel lblError) {
+ this.lblError = lblError;
+ }
+
+ @Override
+ public void login(String authUrl, String accessToken, AuthenticatorCallback callback)
+ throws JsonSyntaxException, ClientProtocolException, ParseException,
+ MalformedURLException, URISyntaxException, IOException {
+ // try to login
+ DesktopEnvironment.openWebpageUri(new URI(authUrl));
+ Exception errEx = null;
+ ReturnCode ret = ReturnCode.GENERIC_ERROR;
+ AuthenticationData data = null;
+ String prefix = I18n.WINDOW.getString("Login.Label.error.continueBrowser");
+
+ // Try for ~10 minutes
+ for (int i = 1; i < 600 && !cancelled; ++i) {
+ Util.sleep(600 + i * 5);
+ lblError.setText(prefix + ANIMATION[i % ANIMATION.length]);
+ try {
+ ClientSessionData sess = ThriftManager.getMasterClient()
+ .getSessionFromAccessCode(accessToken);
+ if (sess != null) {
+ data = new AuthenticationData(sess.authToken, sess.sessionId, sess.satellites);
+ ret = ReturnCode.NO_ERROR;
+ break;
+ }
+
+ } catch (TNotFoundException nf) {
+ continue; // Keep waiting
+ } catch (TAuthorizationException ex) {
+ errEx = ex;
+ break;
+ } catch (TException e) {
+ LOGGER.warn("Cannot get login state for browser-based login", e);
+ }
+ }
+ if (cancelled)
+ return;
+ if (data == null && errEx == null) {
+ // Loop timed out
+ errEx = new TException("Timeout, try again");
+ }
+
+ callback.postLogin(ret, data, errEx);
+ }
+
+ @Override
+ public void cancel() {
+ cancelled = true;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ cancel();
+ }
+}
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 bed848f2..f17cfe96 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java
@@ -87,4 +87,8 @@ public class EcpAuthenticator implements Authenticator {
}
callback.postLogin(ret, data, errEx);
}
+
+ @Override
+ public void cancel() {
+ }
}
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 bc08dc39..c56a2fdb 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java
@@ -36,4 +36,8 @@ public class TestAccountAuthenticator implements Authenticator {
callback.postLogin(ReturnCode.GENERIC_ERROR, null, null);
}
}
+
+ @Override
+ public void cancel() {
+ }
}
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 eab1c4f9..0f2e1f35 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
@@ -3,6 +3,7 @@ package org.openslx.dozmod.gui.window;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
@@ -11,6 +12,7 @@ import java.awt.event.WindowEvent;
import java.io.File;
import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
@@ -26,8 +28,10 @@ import org.apache.thrift.TException;
import org.openslx.bwlp.thrift.iface.Organization;
import org.openslx.bwlp.thrift.iface.Satellite;
import org.openslx.dozmod.App;
+import org.openslx.dozmod.Branding;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.authentication.Authenticator;
+import org.openslx.dozmod.authentication.BrowserAuthenticator;
import org.openslx.dozmod.authentication.Authenticator.AuthenticationData;
import org.openslx.dozmod.authentication.Authenticator.AuthenticatorCallback;
import org.openslx.dozmod.authentication.EcpAuthenticator;
@@ -69,7 +73,8 @@ public class LoginWindow extends LoginWindowLayout {
public static enum LoginType {
ECP(0),
TEST_ACCOUNT(1),
- DIRECT_CONNECT(2);
+ DIRECT_CONNECT(2),
+ EXTERNAL_BROWSER(3);
public final int id;
@@ -80,6 +85,9 @@ public class LoginWindow extends LoginWindowLayout {
// authentication method to use for login attempts
protected LoginType loginType = null;
+
+ // Currently running authentication
+ protected Authenticator currentAuthenticator;
private boolean forceCustomSatellite = false;
@@ -99,10 +107,25 @@ public class LoginWindow extends LoginWindowLayout {
rdoLoginType[type.id].addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
- if (e.getStateChange() == ItemEvent.SELECTED) {
- cboOrganization.setEnabled(cboOrganization.getModel().getSize() != 0 && type == LoginType.ECP);
- loginType = type;
- btnOpenRegistration.setEnabled(type == LoginType.ECP);
+ if (e.getStateChange() != ItemEvent.SELECTED)
+ return;
+ cboOrganization.setEnabled(cboOrganization.getModel().getSize() != 0 && type == LoginType.ECP);
+ loginType = type;
+ btnOpenRegistration.setEnabled(type == LoginType.ECP || type == LoginType.EXTERNAL_BROWSER);
+ boolean browser = (type == LoginType.EXTERNAL_BROWSER);
+ cboOrganization.setVisible(!browser);
+ txtUsername.setVisible(!browser);
+ txtPassword.setVisible(!browser);
+ lblOrganization.setVisible(!browser);
+ lblUsername.setVisible(!browser);
+ lblPassword.setVisible(!browser);
+ txtUrl.setVisible(browser);
+ lblError.setText("-");
+ lblError.setVisible(browser);
+ pnlLoginForm.doLayout();
+ if (currentAuthenticator != null) {
+ currentAuthenticator.cancel();
+ currentAuthenticator = null;
}
}
});
@@ -183,6 +206,9 @@ public class LoginWindow extends LoginWindowLayout {
// make enter key activate login
pnlLoginForm.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "login");
+ pnlLoginForm.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
+ KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK), "login");
+
pnlLoginForm.getActionMap().put("login", new AbstractAction() {
/**
*
@@ -317,8 +343,19 @@ public class LoginWindow extends LoginWindowLayout {
return;
}
// here we only check for the fields
- String username = txtUsername.getText();
- final String password = String.copyValueOf(txtPassword.getPassword());
+ String username;
+ final String password;
+ // SPECIAL CASE - browser based
+ // username is the full URL including token, password is just the token
+ if (loginType == LoginType.EXTERNAL_BROWSER) {
+ password = UUID.randomUUID().toString();
+ username = "https://" + Branding.getMasterServerAddress() + "/webif/shib/?do=SuiteLogin&accessToken=" + password;
+ lblError.setText(I18n.WINDOW.getString("Login.Label.error.continueBrowser"));
+ txtUrl.setText(username);
+ } else {
+ username = txtUsername.getText();
+ password = String.copyValueOf(txtPassword.getPassword());
+ }
// login clicked, lets first read the fields
if (username.isEmpty()) {
Gui.showMessageBox(this, I18n.WINDOW.getString("Login.Message.error.noUsername"), MessageType.ERROR, LOGGER, null);
@@ -340,6 +377,9 @@ public class LoginWindow extends LoginWindowLayout {
final AuthenticatorCallback authenticatorCallback = new AuthenticatorCallback() {
@Override
public void postLogin(ReturnCode returnCode, final AuthenticationData data, Throwable t) {
+ if (t != null) {
+ lblError.setText(t.getMessage());
+ }
switch (returnCode) {
case NO_ERROR:
Gui.asyncExec(new Runnable() {
@@ -384,13 +424,15 @@ public class LoginWindow extends LoginWindowLayout {
};
// now switch over the login types.
- final Authenticator authenticator;
switch (loginType) {
case ECP:
- authenticator = new EcpAuthenticator(selectedOrg.getEcpUrl());
+ currentAuthenticator = new EcpAuthenticator(selectedOrg.getEcpUrl());
break;
case TEST_ACCOUNT:
- authenticator = new TestAccountAuthenticator();
+ currentAuthenticator = new TestAccountAuthenticator();
+ break;
+ case EXTERNAL_BROWSER:
+ currentAuthenticator = new BrowserAuthenticator(lblError);
break;
case DIRECT_CONNECT:
Gui.showMessageBox(this, I18n.WINDOW.getString("Login.Message.error.loginTypeDirectConnect"),
@@ -410,7 +452,7 @@ public class LoginWindow extends LoginWindowLayout {
// Execute login
App.waitForInit();
try {
- authenticator.login(finalUsername, password, authenticatorCallback);
+ currentAuthenticator.login(finalUsername, password, authenticatorCallback);
return;
} catch (TException e) {
ThriftError.showMessage(LoginWindow.this, LOGGER, e,
@@ -418,6 +460,8 @@ public class LoginWindow extends LoginWindowLayout {
} catch (Exception e) {
Gui.showMessageBox(LoginWindow.this, I18n.WINDOW.getString("Login.Message.error.loginFailed"),
MessageType.ERROR, LOGGER, e);
+ } finally {
+ currentAuthenticator = null;
}
enableLogin(true);
}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LoginWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LoginWindowLayout.java
index 55b525d9..c65e7a53 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LoginWindowLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LoginWindowLayout.java
@@ -56,7 +56,7 @@ public abstract class LoginWindowLayout extends JDialog {
protected LOGIN_TYPE loginType = null;
// login type panel
- protected final JRadioButton[] rdoLoginType = new JRadioButton[3];
+ protected final JRadioButton[] rdoLoginType = new JRadioButton[4];
// login form panel
protected final JComboBox<Organization> cboOrganization;
@@ -68,7 +68,14 @@ public abstract class LoginWindowLayout extends JDialog {
protected final JPanel pnlLoginType;
protected final JPanel pnlLoginForm;
protected final JPanel pnlAdvanced;
-
+
+ protected final JTextField txtUrl;
+ protected final QLabel lblError;
+
+ protected final QLabel lblOrganization;
+ protected final QLabel lblUsername;
+ protected final QLabel lblPassword;
+
// advanced panel
protected final JButton btnSettings;
protected final JButton btnLogDir;
@@ -90,13 +97,18 @@ public abstract class LoginWindowLayout extends JDialog {
grid.add(new QLabel(getScaledLogo()), 2);
grid.nextRow();
- rdoLoginType[0] = new JRadioButton(
- I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.0", Branding.getMasterServerIdm()));
- rdoLoginType[1] = new JRadioButton(I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.1"));
- rdoLoginType[2] = new JRadioButton(I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.2"));
+ rdoLoginType[0] = new JRadioButton(I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.0",
+ Branding.getMasterServerIdm()));
+ rdoLoginType[1] = new JRadioButton(
+ I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.1"));
+ rdoLoginType[2] = new JRadioButton(
+ I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.2"));
+ rdoLoginType[3] = new JRadioButton(I18n.WINDOW_LAYOUT.getString("Login.RadioButton.loginType.text.3",
+ Branding.getMasterServerIdm()));
btnSettings = new JButton(I18n.WINDOW_LAYOUT.getString("Login.Button.settings.text"));
btnLogDir = new JButton(I18n.WINDOW_LAYOUT.getString("Login.Button.logDir.text"));
+ lblOrganization = new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.organization.text"));
cboOrganization = new ComboBox<Organization>(new ComboBoxRenderer<Organization>() {
@Override
public String renderItem(Organization item) {
@@ -110,8 +122,17 @@ public abstract class LoginWindowLayout extends JDialog {
return I18n.WINDOW_LAYOUT.getString("Login.ComboBox.organization.emptyText");
}
}, Organization.class);
+ lblUsername = new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.username.text"));
txtUsername = new JTextField();
+ lblPassword = new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.password.text"));
txtPassword = new JPasswordField();
+ // Browser
+ txtUrl = new JTextField();
+ txtUrl.setEditable(false);
+ lblError = new QLabel();
+ txtUrl.setVisible(false);
+ lblError.setVisible(false);
+ //
btnLogin = new JButton(I18n.WINDOW_LAYOUT.getString("Login.Button.login.text"));
chkSaveUsername = new JCheckBox(I18n.WINDOW_LAYOUT.getString("Login.CheckBox.saveUsername.text"));
btnOpenRegistration = new JButton(I18n.WINDOW_LAYOUT.getString("Login.Button.openRegistration.text"));
@@ -119,12 +140,12 @@ public abstract class LoginWindowLayout extends JDialog {
pnlLoginType = makeLoginTypePanel();
grid.add(pnlLoginType).expand(0.25, 1).fill(true, true);
pnlLoginForm = makeLoginFormPanel();
- grid.add(pnlLoginForm,1,2).expand(0.75, 1).fill(true, true);
+ grid.add(pnlLoginForm, 1, 2).expand(0.75, 1).fill(true, true);
grid.nextRow();
-
+
pnlAdvanced = makeAdvancedPanel();
grid.add(pnlAdvanced).expand(true, true).fill(true, true);
-
+
grid.nextRow();
pnlActivity = new JPanel();
pnlActivity.setLayout(new BoxLayout(pnlActivity, BoxLayout.PAGE_AXIS));
@@ -144,26 +165,36 @@ public abstract class LoginWindowLayout extends JDialog {
I18n.WINDOW_LAYOUT.getString("Login.TitledBorder.loginFormPanel.title")));
GridManager grid = new GridManager(loginFormPanel, 4);
- grid.add(new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.organization.text")));
+ grid.add(lblOrganization);
grid.add(cboOrganization, 3).expand(true, false).fill(true, false);
grid.nextRow();
// label + field for username
- grid.add(new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.username.text")));
+ grid.add(lblUsername);
grid.add(txtUsername, 3).expand(true, false).fill(true, false);
grid.nextRow();
// label + field for password
- grid.add(new QLabel(I18n.WINDOW_LAYOUT.getString("Login.Label.password.text")));
+ grid.add(lblPassword);
grid.add(txtPassword, 3).expand(true, false).fill(true, false);
grid.nextRow();
+ // --- Browser-based login
+
+ grid.add(txtUrl, 4).expand(true, false).fill(true, false);
+ grid.nextRow();
+ grid.add(lblError, 4).expand(true, false).fill(true, false);
+ grid.nextRow();
+
+ grid.add(Box.createVerticalGlue(), 4).expand(false, true);
+ grid.nextRow();
+
grid.add(Box.createGlue());
grid.add(chkSaveUsername).expand(true, false);
grid.add(btnOpenRegistration);
grid.add(btnLogin);
grid.nextRow();
- grid.finish(true);
+ grid.finish(false);
return loginFormPanel;
}
@@ -180,7 +211,7 @@ public abstract class LoginWindowLayout extends JDialog {
return loginTypePanel;
}
-
+
private JPanel makeAdvancedPanel() {
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.LINE_AXIS));
@@ -208,8 +239,9 @@ public abstract class LoginWindowLayout extends JDialog {
} else {
scaling = scaleY / 2;
}
- image = new ImageIcon(image.getImage().getScaledInstance((int) (image.getIconWidth() * scaling),
- (int) (image.getIconHeight() * scaling), 0));
+ image = new ImageIcon(image.getImage()
+ .getScaledInstance((int) (image.getIconWidth() * scaling),
+ (int) (image.getIconHeight() * scaling), 0));
return image;
} catch (Exception e) {
LOGGER.warn("Cannot load image", e);
diff --git a/dozentenmodul/src/main/properties/i18n/window.properties b/dozentenmodul/src/main/properties/i18n/window.properties
index 180db3fc..b348034b 100644
--- a/dozentenmodul/src/main/properties/i18n/window.properties
+++ b/dozentenmodul/src/main/properties/i18n/window.properties
@@ -128,6 +128,7 @@ Login.Message.error.authMasterServer=The master server has rejected the login at
Login.Message.error.loginTypeDirectConnect=Not yet implemented
Login.Message.error.loginTypeDefault=No login type selected!
Login.Message.error.loginFailed=Login failed
+Login.Label.error.continueBrowser=Continue via browser...
# SatelliteListWindow
SatelliteList.Message.error.noSatellite=No satellite selected
@@ -143,4 +144,4 @@ VirtConfigEditor.Message.yesNo.safeClose=Your changes in this window will be los
VirtDropDownConfigEditor.Message.warning.initializeComboBoxes=You saved an incorrect entry \n\
during the last configuration. \nThe VM will not start!
VirtDropDownConfigEditor.Message.yesNo.safeClose=Do you really want to cancel?\n\
- Your changes will be discarded. \ No newline at end of file
+ Your changes will be discarded.
diff --git a/dozentenmodul/src/main/properties/i18n/window_de_DE.properties b/dozentenmodul/src/main/properties/i18n/window_de_DE.properties
index 97c72ab8..f3a43a8f 100644
--- a/dozentenmodul/src/main/properties/i18n/window_de_DE.properties
+++ b/dozentenmodul/src/main/properties/i18n/window_de_DE.properties
@@ -128,6 +128,7 @@ Login.Message.error.authMasterServer=Der Masterserver hat den Loginversuch mit d
Login.Message.error.loginTypeDirectConnect=Noch nicht implementiert
Login.Message.error.loginTypeDefault=Keine Authentifizierungsart ausgewählt!
Login.Message.error.loginFailed=Anmeldung fehlgeschlagen
+Login.Label.error.continueBrowser=Im Browser fortfahren...
# SatelliteListWindow
SatelliteList.Message.error.noSatellite=Kein Satellitenserver ausgewählt
diff --git a/dozentenmodul/src/main/properties/i18n/window_layout.properties b/dozentenmodul/src/main/properties/i18n/window_layout.properties
index 982ce0b5..04a659b2 100644
--- a/dozentenmodul/src/main/properties/i18n/window_layout.properties
+++ b/dozentenmodul/src/main/properties/i18n/window_layout.properties
@@ -174,10 +174,11 @@ Login.Dialog.title={0} - Login
Login.RadioButton.loginType.text.0=Authentication via {0}
Login.RadioButton.loginType.text.1=Test access with fixed user
Login.RadioButton.loginType.text.2=Direct access to the satellite
+Login.RadioButton.loginType.text.3={0} via system browser
Login.Button.settings.text=Settings
Login.Button.logDir.text=Log directory
Login.Button.login.text=Login
-Login.CheckBox.saveUsername.text=Remember username
+Login.CheckBox.saveUsername.text=Stay logged in
Login.Button.openRegistration.text=Register
Login.TitledBorder.loginFormPanel.title=Login data
Login.Label.organization.text=Identity Provider
@@ -241,4 +242,4 @@ VirtDropDownConfigEditor.Label.E0VirtDev.text=Network interface card
VirtDropDownConfigEditor.Label.maxUSBSpeed.text=USB
VirtDropDownConfigEditor.Button.more.text=Expert mode
VirtDropDownConfigEditor.Button.cancel.text=Cancel
-VirtDropDownConfigEditor.Button.save.text=Save \ No newline at end of file
+VirtDropDownConfigEditor.Button.save.text=Save
diff --git a/dozentenmodul/src/main/properties/i18n/window_layout_de_DE.properties b/dozentenmodul/src/main/properties/i18n/window_layout_de_DE.properties
index c26066b7..b91e9c25 100644
--- a/dozentenmodul/src/main/properties/i18n/window_layout_de_DE.properties
+++ b/dozentenmodul/src/main/properties/i18n/window_layout_de_DE.properties
@@ -174,10 +174,11 @@ Login.Dialog.title={0} - Login
Login.RadioButton.loginType.text.0=Authentifizierung über {0}
Login.RadioButton.loginType.text.1=Test-Zugang mit festem Benutzer
Login.RadioButton.loginType.text.2=Direkter Zugang zum Satelliten
+Login.RadioButton.loginType.text.3={0} via System-Browser
Login.Button.settings.text=Einstellungen
Login.Button.logDir.text=Logverzeichnis
Login.Button.login.text=Login
-Login.CheckBox.saveUsername.text=Benutzername speichern
+Login.CheckBox.saveUsername.text=Eingeloggt bleiben
Login.Button.openRegistration.text=Registrieren
Login.TitledBorder.loginFormPanel.title=Zugangsdaten
Login.Label.organization.text=Identity Provider
@@ -241,4 +242,4 @@ VirtDropDownConfigEditor.Label.E0VirtDev.text=Netzwerkkarte
VirtDropDownConfigEditor.Label.maxUSBSpeed.text=USB
VirtDropDownConfigEditor.Button.more.text=Expertenmodus
VirtDropDownConfigEditor.Button.cancel.text=Abbrechen
-VirtDropDownConfigEditor.Button.save.text=Speichern \ No newline at end of file
+VirtDropDownConfigEditor.Button.save.text=Speichern