You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2022/12/26 05:35:51 UTC
[openmeetings] branch master updated: [OPENMEETINGS-2755] 2 factor auth is added
This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/master by this push:
new a75f9b7cc [OPENMEETINGS-2755] 2 factor auth is added
a75f9b7cc is described below
commit a75f9b7ccf2c5c235d84258935687685bb0c4260
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Mon Dec 26 12:35:40 2022 +0700
[OPENMEETINGS-2755] 2 factor auth is added
---
.../db/dao/basic/ConfigurationDao.java | 8 +
.../apache/openmeetings/db/entity/user/User.java | 24 +++
.../installation/ImportInitvalues.java | 17 +-
.../apache/openmeetings/mediaserver/KStream.java | 2 -
.../service/calendar/caldav/IcalUtils.java | 2 +-
.../openmeetings/util/OpenmeetingsVariables.java | 10 +
.../apache/openmeetings/util/TestStoredFile.java | 6 +-
openmeetings-web/pom.xml | 8 +
.../src/main/front/room/src/user-list.js | 4 +-
.../src/main/front/settings/src/WebRtcPeer.js | 4 +-
openmeetings-web/src/main/front/wb/src/wb-area.js | 1 -
.../openmeetings/web/admin/backup/BackupPanel.java | 4 +-
.../web/admin/configurations/ConfigsPanel.html | 14 +-
.../web/admin/groups/GroupUsersPanel.java | 4 +-
.../openmeetings/web/admin/labels/LangPanel.java | 4 +-
.../openmeetings/web/admin/oauth/OAuthForm.java | 4 +-
.../openmeetings/web/admin/rooms/RoomForm.java | 8 +-
.../openmeetings/web/admin/users/UserForm.html | 6 +
.../openmeetings/web/admin/users/UserForm.java | 19 +-
.../web/app/Application.properties.xml | 10 +
.../web/app/Application_ar.properties.xml | 10 +
.../web/app/Application_bg.properties.xml | 10 +
.../web/app/Application_bn.properties.xml | 10 +
.../web/app/Application_ca.properties.xml | 10 +
.../web/app/Application_cs.properties.xml | 10 +
.../web/app/Application_da.properties.xml | 10 +
.../web/app/Application_de.properties.xml | 10 +
.../web/app/Application_el.properties.xml | 10 +
.../web/app/Application_es.properties.xml | 10 +
.../web/app/Application_fa.properties.xml | 10 +
.../web/app/Application_fi.properties.xml | 10 +
.../web/app/Application_fr.properties.xml | 10 +
.../web/app/Application_gl.properties.xml | 10 +
.../web/app/Application_he.properties.xml | 10 +
.../web/app/Application_hi.properties.xml | 10 +
.../web/app/Application_hu.properties.xml | 10 +
.../web/app/Application_id.properties.xml | 10 +
.../web/app/Application_it.properties.xml | 10 +
.../web/app/Application_ja.properties.xml | 10 +
.../web/app/Application_ko.properties.xml | 10 +
.../web/app/Application_ku.properties.xml | 10 +
.../web/app/Application_lo.properties.xml | 10 +
.../web/app/Application_nl.properties.xml | 10 +
.../web/app/Application_pl.properties.xml | 10 +
.../web/app/Application_pt.properties.xml | 10 +
.../web/app/Application_pt_BR.properties.xml | 10 +
.../web/app/Application_ru.properties.xml | 10 +
.../web/app/Application_sk.properties.xml | 10 +
.../web/app/Application_sv.properties.xml | 10 +
.../web/app/Application_ta.properties.xml | 10 +
.../web/app/Application_th.properties.xml | 10 +
.../web/app/Application_tk.properties.xml | 10 +
.../web/app/Application_tr.properties.xml | 10 +
.../web/app/Application_uk.properties.xml | 10 +
.../web/app/Application_ur.properties.xml | 10 +
.../web/app/Application_zh_CN.properties.xml | 10 +
.../web/app/Application_zh_TW.properties.xml | 10 +
.../apache/openmeetings/web/app/OtpManager.java | 108 +++++++++++
.../apache/openmeetings/web/common/Captcha.java | 4 +-
.../openmeetings/web/common/CommunityUserForm.html | 6 +-
.../web/common/UploadableImagePanel.html | 2 +-
.../web/common/UploadableImagePanel.java | 4 +-
.../common/datetime/AbstractOmDateTimePicker.java | 16 +-
.../web/common/tree/FileTreePanel.java | 4 +-
.../openmeetings/web/common/upload/UploadForm.java | 2 +-
.../apache/openmeetings/web/pages/BasePage.java | 4 +-
.../apache/openmeetings/web/pages/ResetPage.java | 2 +-
.../web/pages/auth/ForgetPasswordDialog.java | 16 +-
.../openmeetings/web/pages/auth/OtpDialog.html | 51 +++++
.../openmeetings/web/pages/auth/OtpDialog.java | 215 +++++++++++++++++++++
.../web/pages/auth/RegisterDialog.java | 27 ++-
.../web/pages/auth/ResetPasswordDialog.java | 6 +-
.../openmeetings/web/pages/auth/SignInDialog.java | 113 +++++++----
.../openmeetings/web/pages/auth/SignInPage.html | 1 +
.../openmeetings/web/pages/auth/SignInPage.java | 10 +-
.../openmeetings/web/room/IconTextModal.java | 8 +-
.../openmeetings/web/room/menu/RoomMenuPanel.java | 4 +-
.../web/room/poll/PollResultsDialog.java | 6 +-
.../web/user/profile/ChangePasswordDialog.html | 32 +--
.../web/user/profile/ChangePasswordDialog.java | 12 +-
.../web/user/profile/EditProfileForm.html | 1 +
.../web/user/profile/EditProfileForm.java | 60 ++++--
.../web/user/profile/EditProfilePanel.html | 3 +-
.../web/user/profile/EditProfilePanel.java | 9 +-
.../web/user/profile/MessagesContactsPanel.java | 6 +-
...ngePasswordDialog.html => ToggleOtpDialog.html} | 30 +--
.../web/user/profile/ToggleOtpDialog.java | 129 +++++++++++++
.../openmeetings/web/user/rooms/RoomListPanel.java | 4 +-
.../webapp/WEB-INF/classes/openmeetings.properties | 10 +
openmeetings-web/src/main/webapp/css/raw-admin.css | 2 +-
.../src/main/webapp/css/raw-calendar.css | 4 +-
.../src/main/webapp/css/raw-general.css | 6 +-
openmeetings-web/src/main/webapp/css/raw-wb.css | 2 +-
.../webservice/AbstractWebServiceTest.java | 1 -
.../webservice/TestRecordingService.java | 1 -
pom.xml | 26 ++-
src/license/license-template.ftl | 2 +-
97 files changed, 1265 insertions(+), 213 deletions(-)
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
index 02c13e2cb..b2e8c25c8 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
@@ -326,6 +326,9 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> {
case CONFIG_THEME:
reloadTheme();
break;
+ case CONFIG_OTP_ENABLED:
+ reloadOtpEnabled();
+ break;
default:
break;
}
@@ -475,6 +478,10 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> {
app.updateTheme();
}
+ private void reloadOtpEnabled() {
+ setOtpEnabled(getBool(CONFIG_OTP_ENABLED, false));
+ }
+
public void reinit() {
reloadMaxUpload();
reloadCrypt();
@@ -503,6 +510,7 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> {
reloadAppointmentSettings();
reloadRecordingEnabled();
reloadTheme();
+ reloadOtpEnabled();
updateCsp();
}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/user/User.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/user/User.java
index 7c7882cc8..ed3365a82 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/user/User.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/user/User.java
@@ -373,6 +373,14 @@ public class User extends HistoricalEntity {
@XmlJavaTypeAdapter(LongAdapter.class)
private Long domainId; // LDAP config id for LDAP, OAuth server id for OAuth
+ @Column(name = "otp_secret")
+ @XmlElement(name = "otpSecret", required = false)
+ private String otpSecret;
+
+ @Column(name = "otp_recovery", length=350)
+ @XmlElement(name = "otpRecovery", required = false)
+ private String otpRecoveryCodes;
+
@Override
public Long getId() {
return id;
@@ -658,6 +666,22 @@ public class User extends HistoricalEntity {
this.domainId = domainId;
}
+ public String getOtpSecret() {
+ return otpSecret;
+ }
+
+ public void setOtpSecret(String otpSecret) {
+ this.otpSecret = otpSecret;
+ }
+
+ public String getOtpRecoveryCodes() {
+ return otpRecoveryCodes;
+ }
+
+ public void setOtpRecoveryCodes(String otpRecoveryCodes) {
+ this.otpRecoveryCodes = otpRecoveryCodes;
+ }
+
@Override
public String toString() {
return "User [id=" + id + ", firstname=" + firstname
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
index d94f9e4c4..14d4a4e6a 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
@@ -106,6 +106,7 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_TIM
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_TLS;
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_USER;
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_THEME;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_OTP_ENABLED;
import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_APP_NAME;
import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_CSP_DATA;
import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_CSP_FONT;
@@ -294,8 +295,12 @@ public class ImportInitvalues {
// additional settings
// ***************************************
- addCfg(list, CONFIG_SCREENSHARING_QUALITY, "1", Configuration.Type.NUMBER,
- "Default selection in ScreenSharing Quality:\n 0 - bigger frame rate, no resize\n 1 - no resize\n 2 - size == 1/2 of selected area\n 3 - size == 3/8 of selected area", VER_3_0_3);
+ addCfg(list, CONFIG_SCREENSHARING_QUALITY, "1", Configuration.Type.NUMBER, """
+ Default selection in ScreenSharing Quality:
+ 0 - bigger frame rate, no resize
+ 1 - no resize
+ 2 - size == 1/2 of selected area
+ 3 - size == 3/8 of selected area""", VER_3_0_3);
addCfg(list, CONFIG_SCREENSHARING_FPS, "10", Configuration.Type.NUMBER, "Default selection in ScreenSharing FPS", VER_3_0_3);
addCfg(list, CONFIG_SCREENSHARING_FPS_SHOW, String.valueOf(true), Configuration.Type.BOOL, "Is screensharing FPS should be displayed or not", VER_3_0_3);
@@ -345,9 +350,9 @@ public class ImportInitvalues {
+ ", admin/group, admin/room, admin/config, admin/lang, admin/ldap, admin/oauth2, admin/backup, admin/email", "2.1.x");
// oauth2 params
- addCfg(list, CONFIG_IGNORE_BAD_SSL, String.valueOf(false), Configuration.Type.BOOL,
- "Set \"yes\" or \"no\" to enable/disable ssl certifications checking for OAuth2\n"
- + "WARNING: it is not secure", VER_3_0);
+ addCfg(list, CONFIG_IGNORE_BAD_SSL, String.valueOf(false), Configuration.Type.BOOL, """
+ Set "yes" or "no" to enable/disable ssl certifications checking for OAuth2
+ WARNING: it is not secure to ignore bad SSL""", VER_3_0);
addCfg(list, CONFIG_REDIRECT_URL_FOR_EXTERNAL, "", Configuration.Type.STRING,
"Users entered the room via invitationHash or secureHash will be redirected to this URL on connection lost", VER_3_0);
@@ -401,6 +406,8 @@ public class ImportInitvalues {
addCfg(list, CONFIG_THEME, getTheme(), Configuration.Type.STRING, "UI theme, possible values are Cerulean, Cosmo, Cyborg, Darkly, Flatly, "
+ "Journal, Litera, Lumen, Lux, Materia, Minty, Pulse, Sandstone, Simplex, Sketchy, Slate, Solar, Spacelab, Superhero, "
+ "United, Yeti", "6.1.0");
+
+ addCfg(list, CONFIG_OTP_ENABLED, String.valueOf(false), Configuration.Type.BOOL, "Whether or not Time-based One Time Passwords are enabled", "6.3.0");
return list;
}
diff --git a/openmeetings-mediaserver/src/main/java/org/apache/openmeetings/mediaserver/KStream.java b/openmeetings-mediaserver/src/main/java/org/apache/openmeetings/mediaserver/KStream.java
index 97f7da622..b6bddfc35 100644
--- a/openmeetings-mediaserver/src/main/java/org/apache/openmeetings/mediaserver/KStream.java
+++ b/openmeetings-mediaserver/src/main/java/org/apache/openmeetings/mediaserver/KStream.java
@@ -115,8 +115,6 @@ public class KStream extends AbstractStream implements ISipCallbacks {
streamType = sd.getType();
this.connectedSince = new Date();
Injector.get().inject(this);
- //TODO Min/MaxVideoSendBandwidth
- //TODO Min/Max Audio/Video RecvBandwidth
}
public void startBroadcast(final StreamDesc sd, final String sdpOffer, Runnable then) {
diff --git a/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java b/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
index 2372d78a8..0b8bfb807 100644
--- a/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
+++ b/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
@@ -348,7 +348,7 @@ public class IcalUtils {
* @param amount Amount to be Added
* @return New Date
*/
- public Date addTimetoDate(Date date, int field, int amount) { //FIXME TODO
+ public Date addTimetoDate(Date date, int field, int amount) {
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(date);
c.add(field, amount);
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
index e28c3b3dd..1ec5ed310 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
@@ -121,6 +121,7 @@ public class OpenmeetingsVariables {
public static final String CONFIG_CSP_ENABLED = "header.csp.enabled";
public static final String CONFIG_RECORDING_ENABLED = "recording.enabled";
public static final String CONFIG_THEME = "ui.theme";
+ public static final String CONFIG_OTP_ENABLED = "otp.enabled";
public static final int RECENT_ROOMS_COUNT = 5;
public static final int USER_LOGIN_MINIMUM_LENGTH = 4;
@@ -188,6 +189,7 @@ public class OpenmeetingsVariables {
private static int appointmentPreStartMinutes = 5;
private static boolean recordingsEnabled = true;
private static String theme = "Sandstone";
+ private static boolean otpEnabled = false;
private OpenmeetingsVariables() {}
@@ -640,4 +642,12 @@ public class OpenmeetingsVariables {
public static void setTheme(String inTheme) {
theme = inTheme;
}
+
+ public static boolean isOtpEnabled() {
+ return otpEnabled;
+ }
+
+ public static void setOtpEnabled(boolean enabled) {
+ otpEnabled = enabled;
+ }
}
diff --git a/openmeetings-util/src/test/java/org/apache/openmeetings/util/TestStoredFile.java b/openmeetings-util/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
index 8e8614e5b..9fb802aee 100644
--- a/openmeetings-util/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
+++ b/openmeetings-util/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
@@ -29,7 +29,7 @@ import org.junit.jupiter.api.Test;
class TestStoredFile {
@Test
void testAudio() {
- final String[] exts = {"aif", "aifc", "aiff", "au", "mp3", "flac", "wav"}; //TODO enlarge
+ final String[] exts = {"aif", "aifc", "aiff", "au", "mp3", "flac", "wav"};
for (String ext : exts) {
StoredFile sf = new StoredFile("test", ext, (InputStream)null);
assertTrue(sf.isVideo(), String.format("Files of type '%s' should be treated as Video", ext));
@@ -39,7 +39,7 @@ class TestStoredFile {
@Test
void testVideo() {
- final String[] exts = {"avi", "mov", "flv", "mp4"}; //TODO enlarge
+ final String[] exts = {"avi", "mov", "flv", "mp4"};
for (String ext : exts) {
StoredFile sf = new StoredFile("test", ext, (InputStream)null);
assertTrue(sf.isVideo(), String.format("Files of type '%s' should be treated as Video", ext));
@@ -58,7 +58,7 @@ class TestStoredFile {
"wpg", // Word Perfect Graphics
"bmp", "ico", // Microsoft Icon
"tga", // Truevision Targa
- "jpg", "jpeg"}; //TODO enlarge
+ "jpg", "jpeg"};
for (String ext : exts) {
StoredFile sf = new StoredFile("test", ext, (InputStream)null);
assertTrue(sf.isImage(), String.format("Files of type '%s' should be treated as Image", ext));
diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index 6799e47e8..2c94d67a3 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -573,6 +573,14 @@
<groupId>org.webjars</groupId>
<artifactId>jquery-ui-touch-punch</artifactId>
</dependency>
+ <dependency>
+ <groupId>dev.samstevens.totp</groupId>
+ <artifactId>totp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ </dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.apache.openmeetings</groupId>
diff --git a/openmeetings-web/src/main/front/room/src/user-list.js b/openmeetings-web/src/main/front/room/src/user-list.js
index e56eed39b..75306a260 100644
--- a/openmeetings-web/src/main/front/room/src/user-list.js
+++ b/openmeetings-web/src/main/front/room/src/user-list.js
@@ -78,8 +78,8 @@ function __rightVideoIcon(c, elem) {
function __rightOtherIcons(c, elem) {
__rightIcon(c, elem, ['PRESENTER'], '.right.presenter', () => !options.interview && $('.wb-area').is(':visible'));
__rightIcon(c, elem, ['WHITEBOARD', 'PRESENTER'], '.right.wb', () => !options.interview && $('.wb-area').is(':visible'));
- __rightIcon(c, elem, ['SHARE'], '.right.screen-share', () => true); //FIXME TODO getRoomPanel().screenShareAllowed()
- __rightIcon(c, elem, ['REMOTE_CONTROL'], '.right.remote-control', () => true); //FIXME TODO getRoomPanel().screenShareAllowed()
+ __rightIcon(c, elem, ['SHARE'], '.right.screen-share', () => true);
+ __rightIcon(c, elem, ['REMOTE_CONTROL'], '.right.remote-control', () => true);
__rightIcon(c, elem, ['MODERATOR'], '.right.moderator', () => true);
}
function __setStatus(c, le) {
diff --git a/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js b/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js
index 903065988..5d1b84751 100644
--- a/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js
+++ b/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js
@@ -230,13 +230,13 @@ class WebRtcPeer {
OmUtil.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`);
- // FIXME: Firefox implements degradationPreference on each individual encoding!
+ // Firefox implements degradationPreference on each individual encoding!
// (set it on every element of the sendParams.encodings array)
needSetParams = true;
}
- // FIXME: Check that the simulcast encodings were applied.
+ // Check that the simulcast encodings were applied.
// Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings`
// so the only way to enable simulcast is with `RTCRtpSender.setParameters()`.
//
diff --git a/openmeetings-web/src/main/front/wb/src/wb-area.js b/openmeetings-web/src/main/front/wb/src/wb-area.js
index e8705db7d..3132def91 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-area.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-area.js
@@ -243,7 +243,6 @@ module.exports = class DrawWbArea extends WbAreaBase {
this.wsinit();
_doInit(callback);
};
- //FIXME TODO self.getWb = _getWb;
this.setRole = (_role) => {
if (!_inited) {
return;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
index 6b7de1d72..62041df81 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
@@ -57,7 +57,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.progress.UpdatableProgressBar;
import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.BackgroundColorBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
/**
* Panel component to manage Backup Import/Export
*
@@ -186,7 +186,7 @@ public class BackupPanel extends AdminBasePanel {
target.add(feedback);
}
};
- download.setIconType(FontAwesome5IconType.file_download_s);
+ download.setIconType(FontAwesome6IconType.file_arrow_down_s);
progressBar = new UpdatableProgressBar("progress", new Model<>(0), BackgroundColorBehavior.Color.Info, true) {
private static final long serialVersionUID = 1L;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/configurations/ConfigsPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/configurations/ConfigsPanel.html
index 808102b73..35e9d7a4a 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/configurations/ConfigsPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/configurations/ConfigsPanel.html
@@ -73,7 +73,7 @@
</div>
<div wicket:id="boolean-box" class="row">
<label wicket:for="valueB" class="form-label col-3 text-right"><wicket:message key="271" /></label>
- <div class="onoffswitch">
+ <div class="onoffswitch col-8 p-0">
<input type="checkbox" class="onoff-checkbox" wicket:id="valueB"/>
<label class="onoff-label clickable" wicket:for="valueB"></label>
</div>
@@ -86,12 +86,16 @@
</div>
</div>
<div class="formelement row">
- <label class="form-labelcol-3 text-right"><wicket:message key="268" /></label>
- <span wicket:id="updated"/>
+ <label class="form-label col-3 text-right"><wicket:message key="268" /></label>
+ <div class="col-8 p-0">
+ <span wicket:id="updated"/>
+ </div>
</div>
<div class="formelement row">
- <label class="form-labelcol-3 text-right"><wicket:message key="269" /></label>
- <span wicket:id="user.login"/>
+ <label class="form-label col-3 text-right"><wicket:message key="269" /></label>
+ <div class="col-8 p-0">
+ <span wicket:id="user.login"/>
+ </div>
</div>
<div class="formelement row">
<label class="form-label col-3 text-right" wicket:for="comment"><wicket:message key="196" /></label>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
index 572ad9b82..cd295b95f 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
@@ -44,7 +44,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.badge.BootstrapBadge;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.BackgroundColorBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class GroupUsersPanel extends Panel {
private static final long serialVersionUID = 1L;
@@ -92,7 +92,7 @@ public class GroupUsersPanel extends Panel {
target.add(GroupUsersPanel.this);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(del);
item.add(new BootstrapBadge("new", new ResourceModel("lbl.new"), BackgroundColorBehavior.Color.Warning).setVisible((grpUser.getId() == null)));
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
index 471fdeddb..046938763 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
@@ -65,7 +65,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxButt
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
/**
* Language Editor, add/insert/update Label and add/delete language contains several Forms and one list
@@ -242,7 +242,7 @@ public class LangPanel extends AdminBasePanel {
target.add(listContainer);
}
};
- langForm.add(delLngBtn.setIconType(FontAwesome5IconType.times_s)
+ langForm.add(delLngBtn.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833"))));
super.onInitialize();
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
index adb89d7c2..d3c28f4c1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
@@ -55,7 +55,7 @@ import org.apache.wicket.util.string.Strings;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxButton;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class OAuthForm extends AdminBaseForm<OAuthServer> {
private static final long serialVersionUID = 1L;
@@ -79,7 +79,7 @@ public class OAuthForm extends AdminBaseForm<OAuthServer> {
target.add(attrsContainer);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(new Label("key", Model.of(entry.getKey())))
.add(new Label("value", Model.of(entry.getValue())))
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
index a55b78fab..32db3737d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
@@ -84,7 +84,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxButt
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.BackgroundColorBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class RoomForm extends AdminBaseForm<Room> {
private static final long serialVersionUID = 1L;
@@ -110,7 +110,7 @@ public class RoomForm extends AdminBaseForm<Room> {
updateClients(target);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(new Label("clientId", "" + c.getUserId()))
.add(new Label("clientLogin", "" + c.getUser().getLogin()))
@@ -317,7 +317,7 @@ public class RoomForm extends AdminBaseForm<Room> {
target.add(moderatorContainer);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(new CheckBox("superModerator", new PropertyModel<>(moderator, "superModerator")))
.add(new Label("userId", String.valueOf(moderator.getUser().getId())))
@@ -403,7 +403,7 @@ public class RoomForm extends AdminBaseForm<Room> {
target.add(filesContainer);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(new Label("name", new PropertyModel<>(rf.getFile(), "name")))
.add(new Label("wbIdx", new PropertyModel<>(rf, "wbIdx")))
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.html
index a314ed2e1..969858c5e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.html
@@ -42,6 +42,12 @@
<input type="password" wicket:id="password" class="form-control"/>
</div>
</div>
+ <div class="formelement row">
+ <label wicket:for="otp-enabled" class="form-check-label col-3 text-right"><wicket:message key="otp.enabled" /></label>
+ <div class="col-8 p-0 form-check">
+ <input type="checkbox" class="formcheckbox form-check-input ms-0" wicket:id="otp-enabled" />
+ </div>
+ </div>
<form wicket:id="general"></form>
<div class="formelement row">
<label wicket:for="type" class="form-label col-3 text-right"><wicket:message key="45" /></label>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
index 1e6a08c1e..afabc5061 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
@@ -25,6 +25,7 @@ import static org.apache.openmeetings.db.util.AuthLevelUtil.hasAdminLevel;
import static org.apache.openmeetings.db.util.AuthLevelUtil.hasGroupAdminLevel;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinLoginLength;
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSendRegisterEmail;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.isOtpEnabled;
import static org.apache.openmeetings.web.app.WebSession.getRights;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
import static org.apache.wicket.validation.validator.StringValidator.minimumLength;
@@ -56,6 +57,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
@@ -96,6 +98,7 @@ public class UserForm extends AdminBaseForm<User> {
private final DropDownChoice<Long> domainId = new DropDownChoice<>("domainId");
private final PasswordDialog adminPass;
private final UploadableProfileImagePanel avatar = new UploadableProfileImagePanel("avatar", null);
+ private final CheckBox otpEnabled = new CheckBox("otp-enabled", Model.of(false));
@SpringBean
private UserDao userDao;
@SpringBean
@@ -121,6 +124,7 @@ public class UserForm extends AdminBaseForm<User> {
mainContainer.add(generalForm = new GeneralUserForm("general", getModel(), true));
mainContainer.add(password.setResetPassword(false).setLabel(new ResourceModel("110")).setRequired(false)
.add(passValidator = new StrongPasswordValidator(getModelObject())));
+ mainContainer.add(otpEnabled.setLabel(new ResourceModel("otp.enabled")));
login.setLabel(new ResourceModel("108"));
mainContainer.add(login.add(minimumLength(getMinLoginLength())));
@@ -180,16 +184,19 @@ public class UserForm extends AdminBaseForm<User> {
@Override
protected void onModelChanged() {
super.onModelChanged();
- boolean nd = !getModelObject().isDeleted();
- boolean isNew = getModelObject().getId() == null;
+ User u = getModelObject();
+ boolean nd = !u.isDeleted();
+ boolean isNew = u.getId() == null;
mainContainer.setEnabled(nd);
+ otpEnabled.setModelObject(u.getOtpSecret() != null)
+ .setEnabled(isOtpEnabled() && u.getOtpSecret() != null); // admin can only disable OTP
setSaveVisible(nd);
setDelVisible(nd && !isNew);
setRestoreVisible(!nd);
setPurgeVisible(!isNew);
password.setModelObject(null);
- generalForm.updateModelObject(getModelObject(), true);
- passValidator.setUser(getModelObject());
+ generalForm.updateModelObject(u, true);
+ passValidator.setUser(u);
}
@Override
@@ -255,6 +262,10 @@ public class UserForm extends AdminBaseForm<User> {
if (isNew && DISPLAY_NAME_NA.equals(u.getDisplayName())) {
u.resetDisplayName();
}
+ if (Boolean.FALSE.equals(otpEnabled.getModelObject())) {
+ u.setOtpSecret(null);
+ u.setOtpRecoveryCodes(null);
+ }
try {
u = userDao.update(u, pass, getUserId());
} catch (Exception e) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
index dd9f98409..81ac794cd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
index cfbc96ead..3b769f636 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
@@ -881,6 +881,16 @@ see https://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="network.test.upl.time"><![CDATA[وقت التحميل]]></entry>
<entry key="notification.chat.message"><![CDATA[لديك رسالة (رسائل) دردشة جديدة]]></entry>
<entry key="notification.room.activity"><![CDATA[لديك أنشطة جديدة ، يرجى تحديد "الأنشطة والعمل"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[استنساخ]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[عددي من 1 الى 10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[نعم / لا]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
index d0bfc4e4d..2ab090e4e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Числов 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Да/Не]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bn.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bn.properties.xml
index 943964a62..d235dfa61 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bn.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bn.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
index e56508a89..012844388 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Valor numèric [1-10]]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Sí/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
index fd533655d..83e7bcc3e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Čísla 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ano/Ne]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
index 3fca24f1b..6a27b279e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numerisk 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ja/Nej]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
index 03ec631ec..45a4e9af2 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
@@ -887,6 +887,16 @@ Bitte <tt>openmeetings.log</tt> prüfen und die OpenMeetings-Entwickeler kontakt
<entry key="network.test.upl.time"><![CDATA[Upload Zeit]]></entry>
<entry key="notification.chat.message"><![CDATA[Sie haben neue Chat-Nachrichten]]></entry>
<entry key="notification.room.activity"><![CDATA[Es gibt neue Aktivitäten. Bitte sehen Sie unter "Aktivitäten & Aktionen" nach.]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clonen]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numerisch 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ja/Nein]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
index aab8a8f18..2ba1a175e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Αριθμός 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ναι/Όχι]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
index e1c57ece6..8a05180f3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
@@ -883,6 +883,16 @@ por favor revise <tt> openmeetings.log </tt> y contacte a los desarrolladores de
<entry key="network.test.upl.time"><![CDATA[Tiempo de subida]]></entry>
<entry key="notification.chat.message"><![CDATA[Usted tiene nuevo mensaje(s) de chat]]></entry>
<entry key="notification.room.activity"><![CDATA[Usted tiene nuevas actividades, marque "Actividades&Acción"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clonar]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Valor numérico [1-10]]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Sí/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
index f5dd65af0..7c2c547e9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
@@ -878,6 +878,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[زمان بارگذاری]]></entry>
<entry key="notification.chat.message"><![CDATA[پیام (های) گپ جدید دارید]]></entry>
<entry key="notification.room.activity"><![CDATA[شما فعالیت های جدیدی دارید ، لطفاً "فعالیتها و اقدام" را بررسی کنید]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[اعداد 10-1]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[خير/بله]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
index 65e7ec05b..d236119b0 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeerinen 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Kyllä/Ei]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
index b73c3708c..dbd90d5ee 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
@@ -883,6 +883,16 @@ allez sur <tt>openmeetings.log</tt> et contactez les développeurs d'OpenMeeting
<entry key="network.test.upl.time"><![CDATA[Temps de téléchargement]]></entry>
<entry key="notification.chat.message"><![CDATA[Vous avez un nouveau message]]></entry>
<entry key="notification.room.activity"><![CDATA[Vous avez des nouvelles notifications, regardez dans "Notifications & Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Cloner]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numérique 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Oui/Non]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
index bba5f5613..bd1c7fe53 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Valor numérico 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Sí/Non]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
index 135b38c93..13dc739f4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[יש לך הודעות צ'אט חדשות]]></entry>
<entry key="notification.room.activity"><![CDATA[יש לך פעילויות חדשות, אנא בדוק "פעילויות ופעולה"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hi.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hi.properties.xml
index 72a73f9fc..5d459c155 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hi.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hi.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[राशि (१-१०)]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
index ee2f98b16..789e40ea9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
@@ -870,6 +870,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Szám 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Igen/Nem]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
index 4090525a7..220afc69d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Angka dari 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ya/Tidak]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
index 7d0e8aa99..a6f767806 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
@@ -883,6 +883,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Tempo Upload]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numerico 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Si/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
index 12796dec5..b842ca3a1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
@@ -882,6 +882,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[アップロード時間]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[1~10の数字]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[はい/いいえ]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
index 5d7edd53f..76c1efa5d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[숫자 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[네/아니오]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ku.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ku.properties.xml
index a1dafe489..2e3bac866 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ku.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ku.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[Peyama weyên nû yên sohbetê hene]]></entry>
<entry key="notification.room.activity"><![CDATA[Çalakiyên we yên nû hene, ji kerema xwe "Çalakî & Çalakî" bigerin]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_lo.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_lo.properties.xml
index 90f758bc5..5ae55fe09 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_lo.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_lo.properties.xml
@@ -881,6 +881,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
index 8cc24031e..c26188aec 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Getal 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ja/Nee]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
index 2cdfd524a..ef5555785 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
@@ -881,6 +881,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Czas wysyłania]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Sklonuj]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Liczba 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Tak/Nie]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
index 04fe17abb..019ca2670 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numérico 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Sim/Não]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
index a85a08612..4f90f84d5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
@@ -881,6 +881,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numérico 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Sim/Não]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
index f67309add..4c5ff7652 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
@@ -882,6 +882,16 @@ see https://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="network.test.upl.time"><![CDATA[Время загрузки]]></entry>
<entry key="notification.chat.message"><![CDATA[У Вас есть новые сообщения в чате]]></entry>
<entry key="notification.room.activity"><![CDATA[У Вас новый запрос, пожалуйста проверьте "Активность и действия"]]></entry>
+ <entry key="otp.disable"><![CDATA[Выключить одноразовые коды]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Вы уверены что хотите отключить вход по одноразовому коду? Уровень безопасности будет понижен.]]></entry>
+ <entry key="otp.enable"><![CDATA[Включить одноразовые коды]]></entry>
+ <entry key="otp.enabled"><![CDATA[Проверка одноразового кода включена]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Пожалуйста сохраните эти одноразовые пароли в секретном месте, ими можно будет воспользоваться если Вы потеряете доступ к своему мобильному.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Неверный запасной код]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Сохранённый код]]></entry>
+ <entry key="otp.invalid"><![CDATA[Неверный код]]></entry>
+ <entry key="otp.label"><![CDATA[Одноразовый код]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Пожалуйста откройте приложение Google Authenticator и просканируйте этот код.]]></entry>
<entry key="poll.clone"><![CDATA[Клонировать]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Число 1 - 10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Да/Нет]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
index be7995299..9f07850bd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Čísla 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Áno/Nie]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
index 246f18898..346962bf4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
@@ -888,6 +888,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Uppladningstid]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Klona]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numerisk 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Ja/Nej]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ta.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ta.properties.xml
index 86d319261..d73953d50 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ta.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ta.properties.xml
@@ -892,6 +892,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
index 5e217632a..438b1d916 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[เลข 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[ใช่/ไม่]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tk.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tk.properties.xml
index f8197519a..894acb1c0 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tk.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tk.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
index eb84ca12a..21ab30137 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Sayısal 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Evet/Hayır]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
index 6e6fc2cd8..9a796ebfb 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
@@ -881,6 +881,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[В числовому порядку 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Так/Ні]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ur.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ur.properties.xml
index dd9f98409..81ac794cd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ur.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ur.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[Numeric 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[Yes/No]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
index 4bdce2246..6428f69d2 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
@@ -882,6 +882,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[数字 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[是/否]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
index e154396c7..64c126d22 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
@@ -890,6 +890,16 @@ please check <tt>openmeetings.log</tt> and contact OpenMeetings developers]]></e
<entry key="network.test.upl.time"><![CDATA[Upload time]]></entry>
<entry key="notification.chat.message"><![CDATA[You have new chat message(s)]]></entry>
<entry key="notification.room.activity"><![CDATA[You have new activities, please check "Activities&Action"]]></entry>
+ <entry key="otp.disable"><![CDATA[Disable One Time Password]]></entry>
+ <entry key="otp.disable.confirm"><![CDATA[Are you sure you want to disable OTP? Security level will be lowered.]]></entry>
+ <entry key="otp.enable"><![CDATA[Enable One Time Password]]></entry>
+ <entry key="otp.enabled"><![CDATA[One Time Password enabled]]></entry>
+ <entry key="otp.fallback.desc"><![CDATA[Please save these one-time codes in secret place, they can be used in case you will loose access to your mobile device.]]></entry>
+ <entry key="otp.fallback.invalid"><![CDATA[Wrong Recovery code]]></entry>
+ <entry key="otp.fallback.label"><![CDATA[Recovery code]]></entry>
+ <entry key="otp.invalid"><![CDATA[Wrong One Time Password]]></entry>
+ <entry key="otp.label"><![CDATA[One Time Password]]></entry>
+ <entry key="otp.qr.desc"><![CDATA[Please open Google Authenticator and scan this QR code]]></entry>
<entry key="poll.clone"><![CDATA[Clone]]></entry>
<entry key="poll.type.NUMERIC"><![CDATA[數字的 1-10]]></entry>
<entry key="poll.type.YES_NO"><![CDATA[是/否]]></entry>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/OtpManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/OtpManager.java
new file mode 100644
index 000000000..8715cde17
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/OtpManager.java
@@ -0,0 +1,108 @@
+/*
+
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") + you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openmeetings.web.app;
+
+import static dev.samstevens.totp.util.Utils.getDataUriForImage;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.getApplicationName;
+
+import java.net.UnknownHostException;
+
+import javax.annotation.PostConstruct;
+
+import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
+
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import dev.samstevens.totp.code.CodeGenerator;
+import dev.samstevens.totp.code.CodeVerifier;
+import dev.samstevens.totp.code.DefaultCodeGenerator;
+import dev.samstevens.totp.code.DefaultCodeVerifier;
+import dev.samstevens.totp.code.HashingAlgorithm;
+import dev.samstevens.totp.exceptions.QrGenerationException;
+import dev.samstevens.totp.qr.QrData;
+import dev.samstevens.totp.qr.QrGenerator;
+import dev.samstevens.totp.qr.ZxingPngQrGenerator;
+import dev.samstevens.totp.recovery.RecoveryCodeGenerator;
+import dev.samstevens.totp.secret.DefaultSecretGenerator;
+import dev.samstevens.totp.secret.SecretGenerator;
+import dev.samstevens.totp.time.NtpTimeProvider;
+
+@Service
+public class OtpManager {
+ private static final Logger log = LoggerFactory.getLogger(OtpManager.class);
+ // these properties are hardcoded into Google Authenticator :(
+ private static final int digits = 6;
+ private static final int period = 30;
+ private static final HashingAlgorithm alg = HashingAlgorithm.SHA1;
+
+ private final SecretGenerator secretGenerator = new DefaultSecretGenerator(128);
+ private CodeGenerator codeGenerator;
+ private CodeVerifier codeVerifier;
+ @Value("${otp.issuer}")
+ private String issuer = "";
+ @Value("${otp.ntp.server}")
+ private String ntpServer = "pool.ntp.org";
+ @Value("${otp.ntp.timeout}")
+ private int ntpTimeout = 6;
+
+ @PostConstruct
+ public void init() throws UnknownHostException {
+ codeGenerator = new DefaultCodeGenerator(alg, digits);
+ final DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, new NtpTimeProvider(ntpServer, ntpTimeout));
+ verifier.setTimePeriod(period);
+ codeVerifier = verifier;
+ }
+
+ public String generateSecret() {
+ return secretGenerator.generate();
+ }
+
+ public String getQr(String userEmail, String secret) {
+ QrData data = new QrData.Builder()
+ .label(userEmail)
+ .secret(secret)
+ .issuer(Strings.isEmpty(issuer) ? getApplicationName() : issuer)
+ .algorithm(alg)
+ .digits(digits)
+ .period(period)
+ .build();
+ QrGenerator generator = new ZxingPngQrGenerator();
+ try {
+ byte[] imageData = generator.generate(data);
+ return getDataUriForImage(imageData, PNG_MIME_TYPE);
+ } catch (QrGenerationException e) {
+ log.error("Unexpected exception while generating QR", e);
+ }
+ return "";
+ }
+
+ public String[] getRecoveryCodes() {
+ RecoveryCodeGenerator recoveryCodes = new RecoveryCodeGenerator();
+ return recoveryCodes.generateCodes(16);
+ }
+
+ public boolean verify(String secret, String code) {
+ return codeVerifier.isValidCode(secret, code);
+ }
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/Captcha.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/Captcha.java
index eec5dac68..e5a6d3827 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/Captcha.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/Captcha.java
@@ -41,7 +41,7 @@ import org.apache.wicket.validation.ValidationError;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class Captcha extends Panel {
private static final long serialVersionUID = 1L;
@@ -105,7 +105,7 @@ public class Captcha extends Panel {
@Override
protected Icon newIcon(String markupId) {
- return new Icon(markupId, FontAwesome5IconType.sync_s);
+ return new Icon(markupId, FontAwesome6IconType.rotate_s);
}
});
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/CommunityUserForm.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/CommunityUserForm.html
index bdb2c885a..8aad69dab 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/CommunityUserForm.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/CommunityUserForm.html
@@ -24,21 +24,21 @@
<div wicket:id="community_settings">
<div>
<div class="d-inline-block col-3"></div>
- <div class="d-inline-block col-8">
+ <div class="d-inline-block col-8 form-check">
<input wicket:id="everybody" type="radio" class="form-check-input me-2"/>
<label wicket:for="everybody" class="form-check-label"><wicket:message key="1160"/></label>
</div>
</div>
<div>
<div class="d-inline-block col-3"></div>
- <div class="d-inline-block col-8">
+ <div class="d-inline-block col-8 form-check">
<input wicket:id="contact" type="radio" class="form-check-input me-2"/>
<label wicket:for="contact" class="form-check-label"><wicket:message key="1168"/></label>
</div>
</div>
<div>
<div class="d-inline-block col-3"></div>
- <div class="d-inline-block col-8">
+ <div class="d-inline-block col-8 form-check">
<input wicket:id="nobody" type="radio" class="form-check-input me-2"/>
<label wicket:for="nobody" class="form-check-label"><wicket:message key="1169"/></label>
</div>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.html
index 5730cc680..568fa706a 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.html
@@ -21,7 +21,7 @@
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
<wicket:extend>
- <button type="button" class="btn btn-xs btn-outline-secondary remove" wicket:id="remove">
+ <button type="button" class="btn btn-xs btn-outline-secondary remove" wicket:id="remove" wicket:message="title:80">
<span aria-hidden="true">×</span>
</button>
<form wicket:id="form" class="img-upload">
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
index 959f212a6..c75aed681 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
@@ -44,7 +44,7 @@ import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public abstract class UploadableImagePanel extends ImagePanel {
private static final long serialVersionUID = 1L;
@@ -90,7 +90,7 @@ public abstract class UploadableImagePanel extends ImagePanel {
}
};
add(remove
- .setIconType(FontAwesome5IconType.times_s)
+ .setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelConfirm(this, getString("833"))));
fileUploadField.add(new AjaxFormSubmitBehavior(form, "change") {
private static final long serialVersionUID = 1L;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java
index 8659b455d..431851fbc 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java
@@ -40,7 +40,7 @@ import org.apache.wicket.request.resource.ResourceReference;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.AbstractDateTimePickerWithIcon;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerConfig;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerIconConfig;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public abstract class AbstractOmDateTimePicker<T extends Serializable> extends FormComponentPanel<T> {
private static final long serialVersionUID = 1L;
@@ -66,13 +66,13 @@ public abstract class AbstractOmDateTimePicker<T extends Serializable> extends F
.useLocale(WebSession.get().getLocale().toLanguageTag())
.withFormat(patch(format))
.withKeepInvalid(true)
- .with(new DatetimePickerIconConfig().useDateIcon(FontAwesome5IconType.calendar_s)
- .useTimeIcon(FontAwesome5IconType.clock_s).useUpIcon(FontAwesome5IconType.arrow_up_s)
- .useDownIcon(FontAwesome5IconType.arrow_down_s)
- .usePreviousIcon(FontAwesome5IconType.arrow_left_s)
- .useNextIcon(FontAwesome5IconType.arrow_right_s)
- .useTodayIcon(FontAwesome5IconType.calendar_check_s).useClearIcon(FontAwesome5IconType.eraser_s)
- .useCloseIcon(FontAwesome5IconType.times_s));
+ .with(new DatetimePickerIconConfig().useDateIcon(FontAwesome6IconType.calendar_s)
+ .useTimeIcon(FontAwesome6IconType.clock_s).useUpIcon(FontAwesome6IconType.arrow_up_s)
+ .useDownIcon(FontAwesome6IconType.arrow_down_s)
+ .usePreviousIcon(FontAwesome6IconType.arrow_left_s)
+ .useNextIcon(FontAwesome6IconType.arrow_right_s)
+ .useTodayIcon(FontAwesome6IconType.calendar_check_s).useClearIcon(FontAwesome6IconType.eraser_s)
+ .useCloseIcon(FontAwesome6IconType.xmark_s));
picker = new AbstractDateTimePickerWithIcon<>("picker", new Model<>(getModelObject()), config) {
private static final long serialVersionUID = 1L;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
index 9c4afcde8..5fe0f2a10 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
@@ -85,7 +85,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.dropdown.SplitButton;
import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public abstract class FileTreePanel extends Panel {
private static final long serialVersionUID = 1L;
@@ -266,7 +266,7 @@ public abstract class FileTreePanel extends Panel {
public void onClick(AjaxRequestTarget target) {
onDownlownClick(target, ext);
}
- }.setIconType(FontAwesome5IconType.download_s);
+ }.setIconType(FontAwesome6IconType.download_s);
}
@Override
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java
index e67e6de61..55737ef63 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java
@@ -71,7 +71,7 @@ public abstract class UploadForm extends Panel {
// set max upload size in form as info text
Long maxBytes = getMaxUploadSize();
double megaBytes = maxBytes.doubleValue() / 1024 / 1024;
- DecimalFormat formatter = new DecimalFormat("#,###.00"); //FIXME TODO locale based format
+ DecimalFormat formatter = new DecimalFormat("#,###.00");
form.add(new Label("MaxUploadSize", formatter.format(megaBytes)));
form.add(new Label("btn-label", new ResourceModel(buttonLabelKey())));
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
index 6e4890f33..00501de1f 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
@@ -53,7 +53,7 @@ import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.Strings;
import org.wicketstuff.urlfragment.AsyncUrlFragmentAwarePage;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5CssReference;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6CssReference;
public abstract class BasePage extends AsyncUrlFragmentAwarePage {
private static final long serialVersionUID = 1L;
@@ -144,7 +144,7 @@ public abstract class BasePage extends AsyncUrlFragmentAwarePage {
.append(getGaCode()).append("', ").append(isMainPage()).append(");");
response.render(OnDomReadyHeaderItem.forScript(script));
}
- response.render(CssHeaderItem.forReference(FontAwesome5CssReference.instance()));
+ response.render(CssHeaderItem.forReference(FontAwesome6CssReference.instance()));
response.render(new FilteredHeaderItem(CssHeaderItem.forUrl("css/custom.css"), CUSTOM_CSS_FILTER));
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/ResetPage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/ResetPage.java
index b0630b997..84c1d647b 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/ResetPage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/ResetPage.java
@@ -50,7 +50,7 @@ public class ResetPage extends BaseNotInitedPage {
if (resetHash != null) {
User user = userDao.getUserByHash(resetHash);
if (user != null) {
- add(new ResetPasswordDialog("resetPassword", user, resetInfo));
+ add(new ResetPasswordDialog("resetPassword", user));
add(resetInfo.header(new ResourceModel("325"))
.addButton(OmModalCloseButton.of("54"))
.setUseCloseHandler(true));
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
index baf1e8440..fe26e94ce 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
@@ -72,8 +72,6 @@ public class ForgetPasswordDialog extends Modal<String> {
private final WebMarkupContainer label = new WebMarkupContainer("label");
private final Captcha captcha = new Captcha("captcha");
private ForgetPasswordForm form = new ForgetPasswordForm("form");
- private SignInDialog s;
- private final Modal<String> forgetInfoDialog;
private boolean wasReset = false;
@SpringBean
@@ -86,9 +84,8 @@ public class ForgetPasswordDialog extends Modal<String> {
, LOGIN
}
- public ForgetPasswordDialog(String id, Modal<String> forgetInfoDialog) {
+ public ForgetPasswordDialog(String id) {
super(id);
- this.forgetInfoDialog = forgetInfoDialog;
}
@Override
@@ -131,16 +128,15 @@ public class ForgetPasswordDialog extends Modal<String> {
@Override
public void onClose(IPartialPageRequestHandler handler) {
if (wasReset) {
- forgetInfoDialog.show(handler);
+ @SuppressWarnings("unchecked")
+ Modal<String> forgetInfo = (Modal<String>)getPage().get("forgetInfo");
+ forgetInfo.show(handler);
} else {
- s.show(handler);
+ SignInDialog signin = (SignInDialog)getPage().get("signin");
+ signin.show(handler);
}
}
- public void setSignInDialog(SignInDialog s) {
- this.s = s;
- }
-
private class ForgetPasswordForm extends Form<String> {
private static final long serialVersionUID = 1L;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.html
new file mode 100644
index 000000000..a7af19d91
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.html
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<!DOCTYPE html>
+<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
+<wicket:extend>
+ <form wicket:id="form" class="signin-forget">
+ <div class="row mb-1 ms-2 me-2 g-0" wicket:id="type">
+ <div class="col-6">
+ <div class="form-check form-check-inline">
+ <label class="form-check-label" wicket:for="type-otp"><wicket:message key="otp.label" /></label>
+ <input class="form-check-input" type="radio" wicket:id="type-otp"/>
+ </div>
+ </div>
+ <div class="col-5">
+ <div class="form-check form-check-inline">
+ <label class="form-check-label" wicket:for="type-fallback"><wicket:message key="otp.fallback.label" /></label>
+ <input class="form-check-input" type="radio" wicket:id="type-fallback"/>
+ </div>
+ </div>
+ </div>
+ <div class="input-group mb-1 g-0" wicket:id="otp-block">
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
+ <input wicket:id="otp" class="form-control auto-focus" type="number" value="" maxlength="6"/>
+ </div>
+ <div class="input-group mb-1 g-0" wicket:id="fallback-block">
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
+ <input wicket:id="fallback" class="form-control auto-focus" type="text" value=""/>
+ </div>
+ <div wicket:id="feedback"></div>
+ <input type="submit" wicket:id="submit" hidden="hidden"/>
+ </form>
+</wicket:extend>
+</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.java
new file mode 100644
index 000000000..682229d8b
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/OtpDialog.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") + you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openmeetings.web.pages.auth;
+
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.entity.user.User.Type;
+import org.apache.openmeetings.web.app.OtpManager;
+import org.apache.openmeetings.web.app.WebSession;
+import org.apache.openmeetings.web.common.OmModalCloseButton;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.Radio;
+import org.apache.wicket.markup.html.form.RadioGroup;
+import org.apache.wicket.markup.html.form.RequiredTextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.apache.wicket.util.string.Strings;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
+import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.spinner.SpinnerAjaxButton;
+
+public class OtpDialog extends Modal<User> {
+ private static final long serialVersionUID = 1L;
+ private static final String TYPE_OTP = "type-otp";
+ private static final String TYPE_FALLBACK = "type-fallback";
+ private final NotificationPanel feedback = new NotificationPanel("feedback");
+ private final OtpForm form = new OtpForm("form");
+ private final RadioGroup<String> radioGroup = new RadioGroup<>("type", Model.of(TYPE_OTP));
+ @SpringBean
+ private OtpManager otpManager;
+ @SpringBean
+ private UserDao userDao;
+
+ public OtpDialog(String id, IModel<User> model) {
+ super(id, model);
+ }
+
+ @Override
+ protected void onInitialize() {
+ header(new ResourceModel("otp.label"));
+ setUseCloseHandler(true);
+
+ addButton(new SpinnerAjaxButton(BUTTON_MARKUP_ID, new ResourceModel("54"), form, Buttons.Type.Outline_Primary)); // OK
+ addButton(OmModalCloseButton.of());
+
+ add(form);
+ super.onInitialize();
+ }
+
+ @Override
+ public Modal<User> show(IPartialPageRequestHandler handler) {
+ form.reset(handler);
+ return super.show(handler);
+ }
+
+ private SignInDialog getSignin() {
+ return (SignInDialog)getPage().get("signin");
+ }
+
+ @Override
+ public void onClose(IPartialPageRequestHandler handler) {
+ getSignin().show(handler);
+ }
+
+ private class OtpForm extends Form<String> {
+ private static final long serialVersionUID = 1L;
+ private final WebMarkupContainer otpBlock = new WebMarkupContainer("otp-block");
+ private final WebMarkupContainer fallbackBlock = new WebMarkupContainer("fallback-block");
+ private final RequiredTextField<String> otpField = new RequiredTextField<>("otp", Model.of("")) {
+ private static final long serialVersionUID = 1L;
+
+ protected String[] getInputTypes() {
+ return new String[]{"number"};
+ };
+ };
+ private final RequiredTextField<String> fallbackField = new RequiredTextField<>("fallback", Model.of(""));
+
+ public OtpForm(String id) {
+ super(id);
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ add(feedback.setOutputMarkupId(true));
+ add(otpBlock.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
+ otpBlock.add(otpField.setLabel(new ResourceModel("otp.label")));
+ add(fallbackBlock.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
+ fallbackBlock.add(fallbackField.setLabel(new ResourceModel("otp.fallback.label")));
+ add(radioGroup.add(new Radio<>(TYPE_OTP, Model.of(TYPE_OTP)))
+ .add(new Radio<>(TYPE_FALLBACK, Model.of(TYPE_FALLBACK)))
+ .setOutputMarkupId(true));
+ radioGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ updateForm(target);
+ }
+ });
+ add(new AjaxButton("submit") { //FAKE button so "submit-on-enter" works as expected
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target) {
+ OtpForm.this.onSubmit(target);
+ }
+
+ @Override
+ protected void onError(AjaxRequestTarget target) {
+ OtpForm.this.onError(target);
+ }
+ });
+ }
+
+ @Override
+ protected void onError() {
+ RequestCycle.get().find(AjaxRequestTarget.class).ifPresent(this::onError);
+ }
+
+ private void onError(AjaxRequestTarget target) {
+ SignInDialog.penalty();
+ target.add(feedback);
+ }
+
+ @Override
+ protected void onSubmit() {
+ RequestCycle.get().find(AjaxRequestTarget.class).ifPresent(this::onSubmit);
+ }
+
+ @Override
+ protected void onValidate() {
+ final User u = OtpDialog.this.getModelObject();
+ String otp = otpField.getConvertedInput();
+ try {
+ Integer.valueOf(otp);
+ if (otpManager.verify(u.getOtpSecret(), otp)) {
+ return;
+ } else {
+ error(getString("otp.invalid"));
+ }
+ } catch (NumberFormatException e) {
+ // fallback
+ String fallback = fallbackField.getConvertedInput();
+ final String[] codes = Optional.of(u.getOtpRecoveryCodes())
+ .map(str -> str.split(" "))
+ .orElse(new String[]{});
+ if (Stream.of(codes).anyMatch(str -> str.equalsIgnoreCase(fallback))) {
+ // match found
+ // let's remove recovery code
+ u.setOtpRecoveryCodes(Stream.of(codes)
+ .filter(str -> !Strings.isEmpty(str))
+ .filter(str -> !str.equalsIgnoreCase(fallback))
+ .collect(Collectors.joining(" "))
+ );
+ userDao.update(u, u.getId());
+ return;
+ }
+ error(getString("otp.fallback.invalid"));
+ }
+ }
+
+ private void onSubmit(AjaxRequestTarget target) {
+ final User u = OtpDialog.this.getModelObject();
+ WebSession ws = WebSession.get();
+ ws.signIn(u);
+ getSignin().finalStep(true, Type.USER, target);
+ }
+
+ private void updateForm(IPartialPageRequestHandler handler) {
+ final boolean isOtp = TYPE_OTP.equals(radioGroup.getModelObject());
+ otpBlock.setVisible(isOtp);
+ fallbackBlock.setVisible(!isOtp);
+ handler.add(otpBlock, fallbackBlock);
+ }
+
+ private void reset(IPartialPageRequestHandler handler) {
+ radioGroup.setModelObject(TYPE_OTP);
+ otpField.setModelObject("");
+ fallbackField.setModelObject("");
+ handler.add(radioGroup);
+ updateForm(handler);
+ }
+ }
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/RegisterDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/RegisterDialog.java
index f026a54a8..0ec636d04 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/RegisterDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/RegisterDialog.java
@@ -65,7 +65,6 @@ public class RegisterDialog extends Modal<String> {
private final NotificationPanel feedback = new NotificationPanel("feedback");
private final IModel<String> tzModel = Model.of(WebSession.get().getClientTZCode());
private final RegisterForm form = new RegisterForm("form");
- private SignInDialog s;
private Captcha captcha;
private String firstName;
private String lastName;
@@ -76,15 +75,13 @@ public class RegisterDialog extends Modal<String> {
private Long lang;
private boolean wasRegistered = false;
- private final Modal<String> registerInfo;
@SpringBean
private IUserManager userManager;
@SpringBean
private UserDao userDao;
- public RegisterDialog(String id, Modal<String> registerInfo) {
+ public RegisterDialog(String id) {
super(id);
- this.registerInfo = registerInfo;
}
@Override
@@ -100,10 +97,6 @@ public class RegisterDialog extends Modal<String> {
super.onInitialize();
}
- public void setSignInDialog(SignInDialog s) {
- this.s = s;
- }
-
public void setClientTimeZone() {
tzModel.setObject(WebSession.get().getClientTZCode());
}
@@ -130,6 +123,7 @@ public class RegisterDialog extends Modal<String> {
if (sendConfirmation && sendEmailAtRegister) {
messageCode = "warn.notverified";
}
+ Modal<String> registerInfo = getRegisterInfo();
registerInfo.setModelObject(getString(messageCode));
handler.add(registerInfo.get("content"));
reset(handler);
@@ -140,7 +134,8 @@ public class RegisterDialog extends Modal<String> {
@Override
public void onClose(IPartialPageRequestHandler handler) {
if (!wasRegistered) {
- s.show(handler);
+ SignInDialog signin = (SignInDialog)getPage().get("signin");
+ signin.show(handler);
}
}
@@ -150,6 +145,11 @@ public class RegisterDialog extends Modal<String> {
super.onDetach();
}
+ @SuppressWarnings("unchecked")
+ private Modal<String> getRegisterInfo() {
+ return (Modal<String>)getPage().get("registerInfo");
+ }
+
class RegisterForm extends StatelessForm<Void> {
private static final long serialVersionUID = 1L;
private PasswordTextField confirmPassword;
@@ -219,13 +219,7 @@ public class RegisterDialog extends Modal<String> {
error(getString("error.login.inuse"));
}
if (hasErrorMessage()) {
- // add random timeout
- try {
- Thread.sleep((long)(10 * Math.random() * 1000));
- } catch (InterruptedException e) {
- log.error("Unexpected exception while sleeting", e);
- Thread.currentThread().interrupt();
- }
+ SignInDialog.penalty();
}
}
@@ -244,6 +238,7 @@ public class RegisterDialog extends Modal<String> {
}
private void onSubmit(AjaxRequestTarget target) {
+ Modal<String> registerInfo = getRegisterInfo();
wasRegistered = true;
try {
Object o = userManager.registerUser(login, password, lastName
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ResetPasswordDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ResetPasswordDialog.java
index b25bf8edc..dff43f077 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ResetPasswordDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ResetPasswordDialog.java
@@ -42,15 +42,13 @@ public class ResetPasswordDialog extends Modal<String> {
private final NotificationPanel feedback = new NotificationPanel("feedback");
private PasswordTextField password;
private final User user;
- private final Modal<String> resetInfo;
@SpringBean
private UserDao userDao;
- public ResetPasswordDialog(String id, final User user, Modal<String> resetInfo) {
+ public ResetPasswordDialog(String id, final User user) {
super(id);
this.user = user;
- this.resetInfo = resetInfo;
}
@Override
@@ -132,6 +130,8 @@ public class ResetPasswordDialog extends Modal<String> {
try {
userDao.resetPassword(user, password.getConvertedInput());
ResetPasswordDialog.this.close(target);
+ @SuppressWarnings("unchecked")
+ Modal<String> resetInfo = (Modal<String>)getPage().get("resetInfo");
resetInfo.show(target);
} catch (Exception e) {
error(e.getMessage());
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
index 8765fcd44..06b9320a1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
@@ -19,6 +19,7 @@
package org.apache.openmeetings.web.pages.auth;
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LDAP_ID;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.isOtpEnabled;
import static org.apache.openmeetings.web.app.Application.getAuthenticationStrategy;
import static org.apache.openmeetings.web.app.UserManager.showAuth;
import static org.apache.openmeetings.web.pages.HashPage.APP;
@@ -29,8 +30,10 @@ import java.util.List;
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
import org.apache.openmeetings.db.dao.server.LdapConfigDao;
import org.apache.openmeetings.db.dao.server.OAuth2Dao;
+import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.server.LdapConfig;
import org.apache.openmeetings.db.entity.server.OAuthServer;
+import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.entity.user.User.Type;
import org.apache.openmeetings.util.OmException;
import org.apache.openmeetings.util.OpenmeetingsVariables;
@@ -82,8 +85,6 @@ public class SignInDialog extends Modal<String> {
private final PasswordTextField passField = new PasswordTextField("pass", Model.of(""));
private final RequiredTextField<String> loginField = new RequiredTextField<>("login", Model.of(""));
private boolean rememberMe = false;
- private RegisterDialog register;
- private ForgetPasswordDialog f;
private LdapConfig domain;
private NotificationPanel feedback = new NotificationPanel("feedback");
@SpringBean
@@ -92,6 +93,8 @@ public class SignInDialog extends Modal<String> {
private LdapConfigDao ldapDao;
@SpringBean
private OAuth2Dao oauthDao;
+ @SpringBean
+ private UserDao userDao;
public SignInDialog(String id) {
super(id);
@@ -112,11 +115,14 @@ public class SignInDialog extends Modal<String> {
public void onClick(AjaxRequestTarget target) {
SignInDialog.this.close(target);
+
+ RegisterDialog register = (RegisterDialog)getPage().get("register");
register.setClientTimeZone();
register.show(target);
}
}.setVisible(OpenmeetingsVariables.isAllowRegisterFrontend()));
+
super.onInitialize();
}
@@ -125,14 +131,6 @@ public class SignInDialog extends Modal<String> {
return super.createHeaderCloseButton(id).setVisible(false);
}
- public void setRegisterDialog(RegisterDialog r) {
- this.register = r;
- }
-
- public void setForgetPasswordDialog(ForgetPasswordDialog f) {
- this.f = f;
- }
-
class SignInForm extends StatelessForm<String> {
private static final long serialVersionUID = 1L;
private final WebMarkupContainer credentials = new WebMarkupContainer("credentials");
@@ -170,7 +168,9 @@ public class SignInDialog extends Modal<String> {
@Override
public void onClick(AjaxRequestTarget target) {
SignInDialog.this.close(target);
- f.show(target);
+
+ ForgetPasswordDialog forget = (ForgetPasswordDialog)getPage().get("forget");
+ forget.show(target);
}
});
add(new WebMarkupContainer("netTest").add(AttributeModifier.append("href"
@@ -241,41 +241,86 @@ public class SignInDialog extends Modal<String> {
RequestCycle.get().find(AjaxRequestTarget.class).ifPresent(this::onSubmit);
}
+ private void processOtp(final String login, AjaxRequestTarget target) {
+ boolean signedIn = false;
+ boolean checkOtp = false;
+ final String password = passField.getModelObject();
+ User u = null;
+ try {
+ u = userDao.login(login, password);
+ signedIn = u != null;
+ checkOtp = signedIn && u.getOtpSecret() != null;
+ } catch (OmException e) {
+ error(getString(e.getKey()));
+ target.add(feedback);
+ }
+ if (signedIn) {
+ if (checkOtp) {
+ OtpDialog otp = (OtpDialog)getPage().get("otpDialog");
+ otp.setModelObject(u);
+ SignInDialog.this.close(target);
+ otp.show(target);
+ return;
+ }
+ WebSession ws = WebSession.get();
+ ws.signIn(u);
+ }
+ finalStep(signedIn, Type.USER, target);
+ }
+
protected void onSubmit(AjaxRequestTarget target) {
- final String login = String.format(domain.getAddDomainToUserName() ? "%s@%s" : "%s"
- , loginField.getModelObject(), domain.getDomain());
+ final String login = getLogin();
final String password = passField.getModelObject();
- OmAuthenticationStrategy strategy = getAuthenticationStrategy();
WebSession ws = WebSession.get();
Type type = domain.getId() > 0 ? Type.LDAP : Type.USER;
- boolean signIn = false;
+ if (isOtpEnabled() && Type.USER == type) {
+ processOtp(login, target);
+ return;
+ }
+ boolean signedIn = false;
try {
- signIn = ws.signIn(login, password, type, domain.getId());
+ signedIn = ws.signIn(login, password, type, domain.getId());
} catch (OmException e) {
error(getString(e.getKey()));
target.add(feedback);
}
- if (signIn) {
- setResponsePage(Application.get().getHomePage());
- if (rememberMe) {
- strategy.save(login, password, type, domain.getId());
- } else {
- strategy.remove();
- }
+ finalStep(signedIn, type, target);
+ }
+
+ }
+
+ private String getLogin() {
+ return String.format(domain.getAddDomainToUserName() ? "%s@%s" : "%s", loginField.getModelObject(), domain.getDomain());
+ }
+
+ // package private for OTP-Dialog
+ void finalStep(boolean signedIn, final Type type, AjaxRequestTarget target) {
+ OmAuthenticationStrategy strategy = getAuthenticationStrategy();
+ if (signedIn) {
+ setResponsePage(Application.get().getHomePage());
+ if (rememberMe) {
+ final String login = getLogin();
+ strategy.save(login, passField.getModelObject(), type, domain.getId());
} else {
- if (!hasErrorMessage()) {
- error(getString("error.bad.credentials"));
- target.add(feedback);
- }
- // add random timeout
- try {
- Thread.sleep(6 + (long)(10 * Math.random() * 1000));
- } catch (InterruptedException e) {
- log.error("Unexpected exception while sleeping", e);
- Thread.currentThread().interrupt();
- }
strategy.remove();
}
+ } else {
+ if (!hasErrorMessage()) {
+ error(getString("error.bad.credentials"));
+ target.add(feedback);
+ }
+ penalty();
+ strategy.remove();
+ }
+ }
+
+ public static void penalty() {
+ // add random timeout
+ try {
+ Thread.sleep(6 + (long)(10 * Math.random() * 1000));
+ } catch (InterruptedException e) {
+ log.error("Unexpected exception while sleeping", e);
+ Thread.currentThread().interrupt();
}
}
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.html
index d381fce30..ec55be940 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.html
@@ -27,5 +27,6 @@
<div wicket:id="forget" id="forget-dialog"></div>
<div wicket:id="forgetInfo"></div>
<div wicket:id="kick"></div>
+ <div wicket:id="otpDialog" id="otp-dialog"></div>
</wicket:extend>
</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
index 3d15e66fc..71130f459 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
@@ -84,7 +84,7 @@ public class SignInPage extends BaseInitedPage {
signin.show(handler);
}
};
- private final ForgetPasswordDialog forget = new ForgetPasswordDialog("forget", forgetInfoDialog);
+ private final ForgetPasswordDialog forget = new ForgetPasswordDialog("forget");
private final Modal<String> registerInfoDialog = new TextContentModal("registerInfo", Model.of("")) {
private static final long serialVersionUID = 1L;
@@ -107,7 +107,8 @@ public class SignInPage extends BaseInitedPage {
signin.show(handler);
}
};
- RegisterDialog r = new RegisterDialog("register", registerInfoDialog);
+ RegisterDialog r = new RegisterDialog("register");
+ private final OtpDialog otpDialog = new OtpDialog("otpDialog", Model.of());
@SpringBean
private ConfigurationDao cfgDao;
@SpringBean
@@ -168,10 +169,6 @@ public class SignInPage extends BaseInitedPage {
super.onInitialize();
signin = new SignInDialog("signin");
- signin.setRegisterDialog(r);
- signin.setForgetPasswordDialog(forget);
- r.setSignInDialog(signin);
- forget.setSignInDialog(signin);
add(signin.setVisible(!WebSession.get().isKickedByAdmin()),
r.setVisible(allowRegister()), forget, kick.setVisible(WebSession.get().isKickedByAdmin()));
add(forgetInfoDialog
@@ -184,6 +181,7 @@ public class SignInPage extends BaseInitedPage {
.addButton(OmModalCloseButton.of("54"))
.setUseCloseHandler(true)
);
+ add(otpDialog);
}
@Override
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/IconTextModal.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/IconTextModal.java
index c7349e9ae..4103e8ec6 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/IconTextModal.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/IconTextModal.java
@@ -26,8 +26,8 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon;
import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType;
import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.ColorBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconTypeBuilder;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconTypeBuilder.FontAwesome5Solid;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconTypeBuilder;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconTypeBuilder.FontAwesome6Solid;
public class IconTextModal extends Modal<String> {
private static final long serialVersionUID = 1L;
@@ -69,8 +69,8 @@ public class IconTextModal extends Modal<String> {
public IconTextModal withErrorIcon(ColorBehavior.Color color) {
add(new ColorBehavior(color));
- return withIcon(FontAwesome5IconTypeBuilder.on(FontAwesome5Solid.exclamation_triangle)
- .size(FontAwesome5IconTypeBuilder.Size.three)
+ return withIcon(FontAwesome6IconTypeBuilder.on(FontAwesome6Solid.triangle_exclamation)
+ .size(FontAwesome6IconTypeBuilder.Size.three)
.build());
}
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
index 4c0829b1c..bba3616c5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
@@ -69,7 +69,7 @@ import org.apache.openmeetings.mediaserver.StreamProcessor;
import com.github.openjson.JSONObject;
import de.agilecoders.wicket.core.markup.html.bootstrap.navbar.INavbarComponent;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class RoomMenuPanel extends Panel {
private static final long serialVersionUID = 1L;
@@ -142,7 +142,7 @@ public class RoomMenuPanel extends Panel {
@Override
protected void onInitialize() {
- exitMenuItem = new OmMenuItem(getString("308"), getString("309"), FontAwesome5IconType.sign_out_alt_s) {
+ exitMenuItem = new OmMenuItem(getString("308"), getString("309"), FontAwesome6IconType.right_from_bracket_s) {
private static final long serialVersionUID = 1L;
@Override
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/PollResultsDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/PollResultsDialog.java
index d650d145d..08ada8e24 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/PollResultsDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/PollResultsDialog.java
@@ -64,7 +64,7 @@ import org.wicketstuff.jqplot.lib.elements.RendererOptions;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class PollResultsDialog extends Modal<RoomPoll> {
private static final long serialVersionUID = 1L;
@@ -118,7 +118,7 @@ public class PollResultsDialog extends Modal<RoomPoll> {
close(target);
}
});
- close.setIconType(FontAwesome5IconType.times_s).add(newOkCancelDangerConfirm(this, getString("1419")));
+ close.setIconType(FontAwesome6IconType.xmark_s).add(newOkCancelDangerConfirm(this, getString("1419")));
close.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
addButton(delete = new BootstrapAjaxLink<>(BUTTON_MARKUP_ID, null, Buttons.Type.Outline_Danger, new ResourceModel("1420")) {
private static final long serialVersionUID = 1L;
@@ -132,7 +132,7 @@ public class PollResultsDialog extends Modal<RoomPoll> {
close(target);
}
});
- delete.setIconType(FontAwesome5IconType.trash_s).add(newOkCancelDangerConfirm(this, getString("1421")));
+ delete.setIconType(FontAwesome6IconType.trash_s).add(newOkCancelDangerConfirm(this, getString("1421")));
delete.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true);
addButton(clone = new BootstrapAjaxLink<>(BUTTON_MARKUP_ID, null, Buttons.Type.Outline_Danger, new ResourceModel("poll.clone")) {
private static final long serialVersionUID = 1L;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html
index de8363365..8c0cb5712 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html
@@ -23,20 +23,24 @@
<wicket:extend>
<form wicket:id="form">
<div wicket:id="feedback" class="error"></div>
- <table>
- <tr>
- <td class="desc"><label wicket:for="current" class="form-label"><wicket:message key="current.password" /></label></td>
- <td><input wicket:id="current" type="password" value="" class="form-control"/></td>
- </tr>
- <tr>
- <td class="desc"><label wicket:for="pass" class="form-label"><wicket:message key="328" /></label></td>
- <td><input wicket:id="pass" type="password" value="" class="form-control"/></td>
- </tr>
- <tr>
- <td class="desc"><label wicket:for="pass2" class="form-label"><wicket:message key="116" /></label></td>
- <td><input wicket:id="pass2" type="password" value="" class="form-control"/></td>
- </tr>
- </table>
+ <div class="formelement row">
+ <label class="form-label col-4 text-right" wicket:for="current"><wicket:message key="current.password"/></label>
+ <div class="col-7 p-0">
+ <input type="password" wicket:id="current" class="form-control"/>
+ </div>
+ </div>
+ <div class="formelement row">
+ <label class="form-label col-4 text-right" wicket:for="pass"><wicket:message key="328"/></label>
+ <div class="col-7 p-0">
+ <input type="password" wicket:id="pass" class="form-control"/>
+ </div>
+ </div>
+ <div class="formelement row">
+ <label class="form-label col-4 text-right" wicket:for="pass2"><wicket:message key="116"/></label>
+ <div class="col-7 p-0">
+ <input type="password" wicket:id="pass2" class="form-control"/>
+ </div>
+ </div>
</form>
</wicket:extend>
</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.java
index bb9e82cf3..f2280c5e5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.java
@@ -23,6 +23,7 @@ import static org.apache.openmeetings.web.app.WebSession.getUserId;
import org.apache.openmeetings.core.util.StrongPasswordValidator;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.web.common.OmModalCloseButton;
+import org.apache.openmeetings.web.pages.auth.SignInDialog;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.html.form.Form;
@@ -31,8 +32,6 @@ import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.string.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
@@ -41,7 +40,6 @@ import de.agilecoders.wicket.extensions.markup.html.bootstrap.spinner.SpinnerAja
public class ChangePasswordDialog extends Modal<String> {
private static final long serialVersionUID = 1L;
- private static final Logger log = LoggerFactory.getLogger(ChangePasswordDialog.class);
private final PasswordTextField current = new PasswordTextField("current", Model.of((String)null));
private final PasswordTextField pass = new PasswordTextField("pass", Model.of((String)null));
private final PasswordTextField pass2 = new PasswordTextField("pass2", Model.of((String)null));
@@ -53,13 +51,7 @@ public class ChangePasswordDialog extends Modal<String> {
String p = current.getConvertedInput();
if (!Strings.isEmpty(p) && !userDao.verifyPassword(getUserId(), p)) {
error(getString("231"));
- // add random timeout
- try {
- Thread.sleep(6 + (long)(10 * Math.random() * 1000));
- } catch (InterruptedException e) {
- log.error("Unexpected exception while sleeping", e);
- Thread.currentThread().interrupt();
- }
+ SignInDialog.penalty();
}
String p1 = pass.getConvertedInput();
if (!Strings.isEmpty(p1) && !p1.equals(pass2.getConvertedInput())) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.html
index b06f9a9a1..3254db6c5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.html
@@ -30,6 +30,7 @@
<wicket:message key="143" />
</legend>
<button type="button" wicket:id="changePwd" id="changePwd"></button>
+ <button type="button" wicket:id="toggleOtp" id="changeOtp"></button>
<div class="formelement row" wicket:enclosure="passwd">
<label class="form-label col-3 text-right" wicket:for="passwd"><wicket:message key="current.password" /></label>
<div class="col-8 p-0">
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.java
index 8077360c6..2ac5b1372 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfileForm.java
@@ -19,6 +19,8 @@
package org.apache.openmeetings.web.user.profile;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmationHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.isOtpEnabled;
import java.time.Duration;
@@ -31,8 +33,10 @@ import org.apache.openmeetings.web.common.FormActionsPanel;
import org.apache.openmeetings.web.common.GeneralUserForm;
import org.apache.openmeetings.web.common.UploadableProfileImagePanel;
import org.apache.openmeetings.web.pages.PrivacyPage;
+import org.apache.openmeetings.web.pages.auth.SignInDialog;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormValidatingBehavior;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.form.Form;
@@ -45,29 +49,27 @@ import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.string.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class EditProfileForm extends Form<User> {
private static final long serialVersionUID = 1L;
- private static final Logger log = LoggerFactory.getLogger(EditProfileForm.class);
private final PasswordTextField passwd = new PasswordTextField("passwd", new Model<>());
private final GeneralUserForm userForm;
- private final ChangePasswordDialog chPwdDlg;
private boolean checkPassword;
private FormActionsPanel<User> actions;
+ private BootstrapAjaxLink<String> toggleOtp;
@SpringBean
private UserDao userDao;
- public EditProfileForm(String id, final ChangePasswordDialog chPwdDlg) {
+ public EditProfileForm(String id) {
super(id);
setModel(new CompoundPropertyModel<>(userDao.get(getUserId())));
userForm = new GeneralUserForm("general", getModel(), false);
- this.chPwdDlg = chPwdDlg;
this.checkPassword = User.Type.OAUTH != getModelObject().getType();
}
@@ -118,9 +120,30 @@ public class EditProfileForm extends Form<User> {
@Override
public void onClick(AjaxRequestTarget target) {
- chPwdDlg.show(target);
+ ChangePasswordDialog dlg = (ChangePasswordDialog)findParent(EditProfilePanel.class).get("changePasswdDlg");
+ dlg.show(target);
}
}.setVisible(checkPassword));
+ toggleOtp = new BootstrapAjaxLink<>("toggleOtp", null, Buttons.Type.Outline_Danger, Model.of("")) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ User u = EditProfileForm.this.getModelObject();
+ if (u.getOtpSecret() == null) {
+ ToggleOtpDialog dlg = (ToggleOtpDialog)findParent(EditProfilePanel.class).get("toggleOtpDlg");
+ dlg.setModel(EditProfileForm.this.getModel());
+ dlg.show(target);
+ } else {
+ u.setOtpSecret(null);
+ u.setOtpRecoveryCodes(null);
+ updateOtpButton(false, target);
+ }
+ }
+ };
+ add(toggleOtp.setOutputMarkupId(true).setVisible(isOtpEnabled() && checkPassword));
+ updateOtpButton(getModelObject().getOtpSecret() != null, null);
+
add(userForm);
add(new UploadableProfileImagePanel("img", getUserId()));
add(new CommunityUserForm("comunity", getModel()));
@@ -143,13 +166,7 @@ public class EditProfileForm extends Form<User> {
String p = passwd.getConvertedInput();
if (!Strings.isEmpty(p) && !userDao.verifyPassword(getModelObject().getId(), p)) {
error(getString("231"));
- // add random timeout
- try {
- Thread.sleep(6 + (long)(10 * Math.random() * 1000));
- } catch (InterruptedException e) {
- log.error("Unexpected exception while sleeping", e);
- Thread.currentThread().interrupt();
- }
+ SignInDialog.penalty();
}
}
super.onValidate();
@@ -165,4 +182,19 @@ public class EditProfileForm extends Form<User> {
super.renderHead(response);
response.render(OnDomReadyHeaderItem.forScript("$('.profile-edit-form .my-info').off().click(function() {showUserInfo(" + getUserId() + ");});"));
}
+
+ // package private for ToggleOtpDialog
+ void updateOtpButton(boolean enabled, IPartialPageRequestHandler handler) {
+ if (enabled) {
+ toggleOtp.add(newOkCancelDangerConfirm(this, getString("otp.disable.confirm")));
+ } else {
+ toggleOtp.getBehaviors(ConfirmationBehavior.class).stream().forEach(b -> toggleOtp.remove(b));
+ }
+ toggleOtp.setLabel(new ResourceModel(enabled ? "otp.disable" : "otp.enable"));
+ toggleOtp.setIconType(enabled ? FontAwesome6IconType.square_check_r : FontAwesome6IconType.square_r);
+ toggleOtp.setType(enabled ? Buttons.Type.Outline_Danger : Buttons.Type.Primary);
+ if (handler != null) {
+ handler.add(toggleOtp);
+ }
+ }
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.html
index eb27de1cb..ab12ef068 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.html
@@ -22,6 +22,7 @@
<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
<wicket:panel>
<form wicket:id="form" class="adminForm profile-panel container"/>
- <div wicket:id="changePasswd"></div>
+ <div wicket:id="changePasswdDlg"></div>
+ <div wicket:id="toggleOtpDlg"></div>
</wicket:panel>
</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.java
index c2ece82f2..296ec6c6e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/EditProfilePanel.java
@@ -26,8 +26,13 @@ public class EditProfilePanel extends UserBasePanel {
public EditProfilePanel(String id) {
super(id);
setOutputMarkupId(true);
+ }
- final ChangePasswordDialog chPwdDlg = new ChangePasswordDialog("changePasswd");
- add(chPwdDlg, new EditProfileForm("form", chPwdDlg));
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ add(new ChangePasswordDialog("changePasswdDlg"));
+ add(new ToggleOtpDialog("toggleOtpDlg"));
+ add(new EditProfileForm("form"));
}
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
index 9be682bb9..b2014fd64 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
@@ -80,7 +80,7 @@ import org.apache.wicket.spring.injection.annot.SpringBean;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class MessagesContactsPanel extends UserBasePanel {
private static final long serialVersionUID = 1L;
@@ -227,7 +227,7 @@ public class MessagesContactsPanel extends UserBasePanel {
target.add(folders, moveDropDown);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(del);
item.add(AjaxEventBehavior.onEvent(EVT_CLICK, target -> selectFolder(item, item.getModelObject().getId(), target)));
@@ -471,7 +471,7 @@ public class MessagesContactsPanel extends UserBasePanel {
updateContacts(target);
}
};
- del.setIconType(FontAwesome5IconType.times_s)
+ del.setIconType(FontAwesome6IconType.xmark_s)
.add(newOkCancelDangerConfirm(this, getString("833")));
item.add(del.setVisible(!uc.isPending()));
}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.html
similarity index 60%
copy from openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html
copy to openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.html
index de8363365..ed67296a7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ChangePasswordDialog.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.html
@@ -22,21 +22,23 @@
<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
<wicket:extend>
<form wicket:id="form">
+ <div class="formelement row pb-2">
+ <label class="form-label col-4 text-right" wicket:for="current"><wicket:message key="current.password"/></label>
+ <div class="col-7 p-0">
+ <input type="password" wicket:id="current" class="form-control"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-6">
+ <div><wicket:message key="otp.qr.desc"/></div>
+ <img class="col-12" wicket:id="qr"/>
+ </div>
+ <div class="col-6">
+ <div><wicket:message key="otp.fallback.desc"/></div>
+ <textarea class="col-12" wicket:id="codes" readonly="readonly" rows="5"></textarea>
+ </div>
+ </div>
<div wicket:id="feedback" class="error"></div>
- <table>
- <tr>
- <td class="desc"><label wicket:for="current" class="form-label"><wicket:message key="current.password" /></label></td>
- <td><input wicket:id="current" type="password" value="" class="form-control"/></td>
- </tr>
- <tr>
- <td class="desc"><label wicket:for="pass" class="form-label"><wicket:message key="328" /></label></td>
- <td><input wicket:id="pass" type="password" value="" class="form-control"/></td>
- </tr>
- <tr>
- <td class="desc"><label wicket:for="pass2" class="form-label"><wicket:message key="116" /></label></td>
- <td><input wicket:id="pass2" type="password" value="" class="form-control"/></td>
- </tr>
- </table>
</form>
</wicket:extend>
</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.java
new file mode 100644
index 000000000..72df0279a
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/ToggleOtpDialog.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") + you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openmeetings.web.user.profile;
+
+import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_SRC;
+
+import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.OtpManager;
+import org.apache.openmeetings.web.common.OmModalCloseButton;
+import org.apache.openmeetings.web.pages.auth.SignInDialog;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.apache.wicket.util.string.Strings;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
+import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.spinner.SpinnerAjaxButton;
+
+public class ToggleOtpDialog extends Modal<User> {
+ private final NotificationPanel feedback = new NotificationPanel("feedback");
+ private final PasswordTextField current = new PasswordTextField("current", Model.of((String)null));
+ private final WebMarkupContainer qr = new WebMarkupContainer("qr");
+ private final TextArea<String> codesArea = new TextArea<>("codes", Model.of(""));
+ private String secret;
+ private String[] codes;
+
+ @SpringBean
+ private OtpManager otpManager;
+ @SpringBean
+ private UserDao userDao;
+
+ public ToggleOtpDialog(String id) {
+ super(id);
+ }
+
+ @Override
+ protected void onInitialize() {
+ header(new ResourceModel("otp.enable"));
+ setUseCloseHandler(true);
+
+ final Form<String> form = new Form<>("form") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onValidate() {
+ final User u = ToggleOtpDialog.this.getModelObject();
+ String p = current.getConvertedInput();
+ if (!Strings.isEmpty(p) && !userDao.verifyPassword(u.getId(), p)) {
+ error(getString("231"));
+ SignInDialog.penalty();
+ }
+ super.onValidate();
+ }
+ };
+ add(form.add(current.setLabel(new ResourceModel("current.password")).setOutputMarkupId(true))
+ .add(qr.setOutputMarkupId(true))
+ .add(codesArea.setOutputMarkupId(true))
+ .add(feedback.setOutputMarkupId(true)));
+
+ addButton(new SpinnerAjaxButton(BUTTON_MARKUP_ID, new ResourceModel("otp.enable"), form, Buttons.Type.Outline_Primary) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onError(AjaxRequestTarget target) {
+ target.add(feedback);
+ }
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target) {
+ final User u = ToggleOtpDialog.this.getModelObject();
+ u.setOtpSecret(secret);
+ u.setOtpRecoveryCodes(String.join(" ", codes));
+ EditProfileForm editForm = (EditProfileForm)findParent(EditProfilePanel.class).get("form");
+ editForm.updateOtpButton(true, target);
+ userDao.update(u, u.getId());
+ ToggleOtpDialog.this.close(target);
+ }
+ });
+ addButton(OmModalCloseButton.of());
+ super.onInitialize();
+ }
+
+ @Override
+ public void onClose(IPartialPageRequestHandler handler) {
+ secret = null;
+ current.setModelObject(null);
+ qr.add(AttributeModifier.remove(PARAM_SRC));
+ codesArea.setModelObject("");
+ handler.add(current, qr, codesArea);
+ }
+
+ @Override
+ public Modal<User> show(IPartialPageRequestHandler target) {
+ secret = otpManager.generateSecret();
+ codes = otpManager.getRecoveryCodes();
+ final User u = getModelObject();
+ current.setModelObject(null);
+ qr.add(AttributeModifier.replace(PARAM_SRC, otpManager.getQr(u.getAddress().getEmail(), secret)));
+ codesArea.setModelObject(String.join("\n", codes));
+ target.add(current, qr, codesArea);
+ return super.show(target);
+ }
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
index 3576fc8e9..0402ecdee 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
@@ -46,7 +46,7 @@ import org.apache.wicket.spring.injection.annot.SpringBean;
import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.CssClassNameAppender;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType;
public class RoomListPanel extends Panel {
private static final long serialVersionUID = 1L;
@@ -85,7 +85,7 @@ public class RoomListPanel extends Panel {
private static final long serialVersionUID = 1L;
{
- setIconType(FontAwesome5IconType.sync_alt_s);
+ setIconType(FontAwesome6IconType.rotate_s);
}
@Override
diff --git a/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties b/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties
index 54796a37b..681f3baa7 100644
--- a/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties
+++ b/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties
@@ -78,3 +78,13 @@ sip.ws.local.host=
sip.ws.remote.port=8088
sip.ws.remote.user=omsip_user
sip.ws.remote.password=12345
+
+################## Time-based One Time Password ##################
+## Please NOTE these values need to be changed BEFORE users will set-up OTP for themselves
+## otherwise they can't login
+
+# NOTE Config->application.name will be used if blank
+otp.issuer=
+otp.ntp.server=pool.ntp.org
+## milliseconds
+otp.ntp.timeout=3000
diff --git a/openmeetings-web/src/main/webapp/css/raw-admin.css b/openmeetings-web/src/main/webapp/css/raw-admin.css
index 2691a269b..745c12a6b 100644
--- a/openmeetings-web/src/main/webapp/css/raw-admin.css
+++ b/openmeetings-web/src/main/webapp/css/raw-admin.css
@@ -82,7 +82,7 @@
margin: 0;
}
.onoff-label::before {
- font-family: 'Font Awesome 5 Free';
+ font-family: 'Font Awesome 6 Free';
font-weight: 900;
font-size: 2.2em;
}
diff --git a/openmeetings-web/src/main/webapp/css/raw-calendar.css b/openmeetings-web/src/main/webapp/css/raw-calendar.css
index d4c729397..a57978afe 100644
--- a/openmeetings-web/src/main/webapp/css/raw-calendar.css
+++ b/openmeetings-web/src/main/webapp/css/raw-calendar.css
@@ -1,6 +1,6 @@
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
#contents #calendar .fc-gotoBtn-button::before {
- font-family: 'Font Awesome 5 Free';
+ font-family: 'Font Awesome 6 Free';
font-weight: 900;
content: "\f133";
font-size: 1em;
@@ -31,7 +31,7 @@
#wrapper-panel-frame .main-form, #calendar {
height: 100%;
}
-/* FIXME TODO bootstrap override */
+/* bootstrap override */
.table-bordered {
border: 1px solid #dee2e6;
}
diff --git a/openmeetings-web/src/main/webapp/css/raw-general.css b/openmeetings-web/src/main/webapp/css/raw-general.css
index 652dda0a3..e4d2da485 100644
--- a/openmeetings-web/src/main/webapp/css/raw-general.css
+++ b/openmeetings-web/src/main/webapp/css/raw-general.css
@@ -218,7 +218,7 @@ html, body {
height: 34px;
}
.om-icon::before {
- font-family: 'Font Awesome 5 Free';
+ font-family: 'Font Awesome 6 Free';
font-weight: 900;
color: var(--bs-secondary);
font-size: 1.2em;
@@ -576,7 +576,7 @@ select.messages.selector {
height: 100%;
}
.dragbox .dragbox-header .dragbox-toggle, .dragbox .dragbox-header .dragbox-actions .icon, .sort-icon a {
- font-family: "Font Awesome 5 Free";
+ font-family: "Font Awesome 6 Free";
font-weight: 900;
display: inline-block;
font-style: normal;
@@ -743,7 +743,7 @@ select.messages.selector {
z-index: calc(var(--chat-zindex) + 2);
}
.popover.confirmation.show {
- z-index: 3000; /* FIXME TODO move this to variables */
+ z-index: 3000;
}
.installer {
overflow-y: auto;
diff --git a/openmeetings-web/src/main/webapp/css/raw-wb.css b/openmeetings-web/src/main/webapp/css/raw-wb.css
index e234e7a73..f62803098 100644
--- a/openmeetings-web/src/main/webapp/css/raw-wb.css
+++ b/openmeetings-web/src/main/webapp/css/raw-wb.css
@@ -30,7 +30,7 @@ html[dir="rtl"] .room-block .sb-wb .wb-block {
background-position: center;
}
.room-block .sb-wb .wb-block.droppable-hover .wb-drop-area::before {
- font-family: 'Font Awesome 5 Free';
+ font-family: 'Font Awesome 6 Free';
font-weight: 400;
font-size: 20em;
content: '\f358';
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
index 8082d9694..a9c15588d 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
@@ -136,7 +136,6 @@ public abstract class AbstractWebServiceTest {
assertEquals(r.getName(), room1.getName(), "Room with same Name should be returned");
assertEquals(r.getExternalType(), room1.getExternalType(), "Room with same ExternalType should be returned");
assertEquals(r.getExternalId(), room1.getExternalId(), "Room with same ExternalId should be returned");
- //TODO check other fields
return new CallResult<>(sid, room1);
}
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
index e23738012..e3f7ffe21 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
@@ -62,7 +62,6 @@ class TestRecordingService extends AbstractWebServiceTest {
boolean found = false;
for (RecordingDTO rdo : recs) {
if (r.getId().equals(rdo.getId())) {
- //TODO check room, user
found = true;
break;
}
diff --git a/pom.xml b/pom.xml
index 4b7587740..3ac6bfda6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,26 +96,27 @@
<site.basedir>${project.basedir}</site.basedir>
<src.pack.skip>false</src.pack.skip>
<h2.version>2.1.214</h2.version>
- <commons-lang3.version>3.12.0</commons-lang3.version>
<jakarta.mail.version>2.0.1</jakarta.mail.version>
<openjpa.version>3.2.2</openjpa.version>
<asterisk-java.version>3.37.0</asterisk-java.version>
+ <commons-lang3.version>3.12.0</commons-lang3.version>
<commons-dbcp.version>2.9.0</commons-dbcp.version>
<commons-pool2.version>2.11.1</commons-pool2.version>
<commons-cli.version>1.5.0</commons-cli.version>
- <dom4j.version>2.1.3</dom4j.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-io.version>2.11.0</commons-io.version>
+ <commons-collections4.version>4.4</commons-collections4.version>
+ <commons-text.version>1.10.0</commons-text.version>
+ <commons-net.version>3.9.0</commons-net.version>
+ <dom4j.version>2.1.3</dom4j.version>
<postgresql.version>42.5.1</postgresql.version>
<mysql.version>8.0.31</mysql.version>
<mssql.version>11.2.1.jre17</mssql.version>
<ojdbc.version>19.17.0.0</ojdbc.version>
- <commons-collections4.version>4.4</commons-collections4.version>
<xstream.version>1.4.19</xstream.version>
<api-all.version>2.1.2</api-all.version>
<caldav4j.version>1.0.5</caldav4j.version>
<tika-parsers.version>2.6.0</tika-parsers.version>
- <commons-text.version>1.10.0</commons-text.version>
<slf4j.version>2.0.6</slf4j.version>
<logback.version>1.4.5</logback.version>
<jetty.version>9.4.50.v20221201</jetty.version>
@@ -135,6 +136,7 @@
<bytebuddy.version>1.12.20</bytebuddy.version>
<annotation-api.version>1.3.2</annotation-api.version>
<jsr305.version>3.0.2</jsr305.version>
+ <totp.version>1.7.1</totp.version>
<!-- Exclude all generated code -->
<sonar.exclusions>file:**/generated-sources/**, file:**/jquery-ui.css, file:**/cssemoticons.js, file:**/bootstrap-confirmation.js</sonar.exclusions>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
@@ -1022,6 +1024,18 @@
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
+ <!-- OTP block -->
+ <dependency>
+ <groupId>dev.samstevens.totp</groupId>
+ <artifactId>totp</artifactId>
+ <version>${totp.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ <version>${commons-net.version}</version>
+ </dependency>
+ <!-- OTP block -->
</dependencies>
</dependencyManagement>
<dependencies>
@@ -1215,7 +1229,7 @@
<property name="fileExtensions" value="java"/>
</module>
<module name="SuppressWithPlainTextCommentFilter">
- <property name="offCommentFormat" value="[=+](\s+)&quot;&quot;&quot;"/>
+ <property name="offCommentFormat" value="(\s+)&quot;&quot;&quot;"/>
<property name="onCommentFormat" value="^\s+.*&quot;&quot;&quot;;"/>
</module>
<module name="TreeWalker">
@@ -1244,7 +1258,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
- <version>10.3.2</version> <!-- FIXME TODO: should be removed after checkstyle plugin will be updated -->
+ <version>10.5.0</version> <!-- FIXME TODO: should be removed after checkstyle plugin will be updated -->
</dependency>
</dependencies>
</plugin>
diff --git a/src/license/license-template.ftl b/src/license/license-template.ftl
index 4e3e209be..2d29ab44c 100644
--- a/src/license/license-template.ftl
+++ b/src/license/license-template.ftl
@@ -49,7 +49,7 @@
<#function licensesKey licenses>
<#local result = "">
<#list licenses?sort as license>
- <#if license?contains("Apache License, Version 2.0")><#return "Apache License Version 2.0"></#if><#-- FIXME TODO overriding mapping-->
+ <#if license?contains("Apache License, Version 2.0")><#return "Apache License Version 2.0"></#if>
<#if license?lower_case?contains("apache")><#return license></#if>
<#if license?contains("Eclipse Public License")><#return license></#if>
<#if license?contains("Common Development and Distribution License")><#return license></#if>