From 920d491f402d18884d79d48ed1ecde0cd52df309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Wed, 15 Jan 2025 15:04:44 +0100 Subject: [PATCH] Static pages / add support for mail links --- .../java/org/fao/geonet/utils/EmailUtil.java | 43 +++++++++++++++++++ .../org/fao/geonet/utils/EmailUtilTest.java | 43 +++++++++++++++++++ .../java/org/fao/geonet/domain/page/Page.java | 2 +- .../org/fao/geonet/api/pages/PagesAPI.java | 31 +++++++++---- .../validation/UserRegisterDtoValidator.java | 7 ++- .../pages/GnStaticPagesDirective.js | 8 +++- .../components/pages/partials/menu-page.html | 2 +- .../catalog/js/admin/StaticPagesController.js | 9 +++- .../main/resources/catalog/locales/en-v4.json | 1 + .../admin/settings/static-pages.html | 8 ++-- 10 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 common/src/main/java/org/fao/geonet/utils/EmailUtil.java create mode 100644 common/src/test/java/org/fao/geonet/utils/EmailUtilTest.java diff --git a/common/src/main/java/org/fao/geonet/utils/EmailUtil.java b/common/src/main/java/org/fao/geonet/utils/EmailUtil.java new file mode 100644 index 000000000000..b66fc183fbe9 --- /dev/null +++ b/common/src/main/java/org/fao/geonet/utils/EmailUtil.java @@ -0,0 +1,43 @@ +//============================================================================= +//=== Copyright (C) 2001-2025 Food and Agriculture Organization of the +//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) +//=== and United Nations Environment Programme (UNEP) +//=== +//=== This library is free software; you can redistribute it and/or +//=== modify it under the terms of the GNU Lesser General Public +//=== License as published by the Free Software Foundation; either +//=== version 2.1 of the License, or (at your option) any later version. +//=== +//=== This library is distributed in the hope that it will be useful, +//=== but WITHOUT ANY WARRANTY; without even the implied warranty of +//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//=== Lesser General Public License for more details. +//=== +//=== You should have received a copy of the GNU Lesser General Public +//=== License along with this library; if not, write to the Free Software +//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +//=== +//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, +//=== Rome - Italy. email: geonetwork@osgeo.org +//============================================================================== + +package org.fao.geonet.utils; + +public class EmailUtil { + private static final String OWASP_EMAIL_REGEX = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; + private static final java.util.regex.Pattern OWASP_EMAIL_PATTERN = java.util.regex.Pattern.compile(OWASP_EMAIL_REGEX); + + private EmailUtil() { + throw new UnsupportedOperationException(); + } + + /** + * Checks if a string contains a valid email address format. + * + * @param emailAddress Value to validate. + * @return true if the value contains a valid email address format, otherwise false. + */ + public static boolean isValidEmailAddress(String emailAddress) { + return OWASP_EMAIL_PATTERN.matcher(emailAddress).matches(); + } +} diff --git a/common/src/test/java/org/fao/geonet/utils/EmailUtilTest.java b/common/src/test/java/org/fao/geonet/utils/EmailUtilTest.java new file mode 100644 index 000000000000..41e45f7724bb --- /dev/null +++ b/common/src/test/java/org/fao/geonet/utils/EmailUtilTest.java @@ -0,0 +1,43 @@ +//============================================================================= +//=== Copyright (C) 2001-2025 Food and Agriculture Organization of the +//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) +//=== and United Nations Environment Programme (UNEP) +//=== +//=== This library is free software; you can redistribute it and/or +//=== modify it under the terms of the GNU Lesser General Public +//=== License as published by the Free Software Foundation; either +//=== version 2.1 of the License, or (at your option) any later version. +//=== +//=== This library is distributed in the hope that it will be useful, +//=== but WITHOUT ANY WARRANTY; without even the implied warranty of +//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//=== Lesser General Public License for more details. +//=== +//=== You should have received a copy of the GNU Lesser General Public +//=== License along with this library; if not, write to the Free Software +//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +//=== +//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, +//=== Rome - Italy. email: geonetwork@osgeo.org +//============================================================================== + +package org.fao.geonet.utils; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EmailUtilTest { + @Test + public void testEmailAddress() { + assertEquals(true, EmailUtil.isValidEmailAddress("test@domain.com")); + + assertEquals(true, EmailUtil.isValidEmailAddress("test.user@domain.com")); + + assertEquals(true, EmailUtil.isValidEmailAddress("test.user@domain.subdomain.com")); + + assertEquals(false, EmailUtil.isValidEmailAddress("test.user")); + + assertEquals(false, EmailUtil.isValidEmailAddress("test.user@domain")); + } +} diff --git a/domain/src/main/java/org/fao/geonet/domain/page/Page.java b/domain/src/main/java/org/fao/geonet/domain/page/Page.java index 563e58663fa3..49387077f3eb 100644 --- a/domain/src/main/java/org/fao/geonet/domain/page/Page.java +++ b/domain/src/main/java/org/fao/geonet/domain/page/Page.java @@ -90,7 +90,7 @@ public enum PageStatus { } public enum PageFormat { - LINK, HTML, HTMLPAGE, TEXT; + LINK, HTML, HTMLPAGE, TEXT, EMAILLINK; } // These are the sections where is shown the link to the Page object diff --git a/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java b/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java index 14da2d9e93c3..f5de7d082188 100644 --- a/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java +++ b/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java @@ -45,6 +45,7 @@ import org.fao.geonet.repository.UserGroupRepository; import org.fao.geonet.repository.page.PageRepository; import org.fao.geonet.repository.specification.UserGroupSpecs; +import org.fao.geonet.utils.EmailUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -58,6 +59,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.validation.constraints.Email; import javax.validation.constraints.NotNull; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -178,7 +180,11 @@ private ResponseEntity createPage(PageProperties pageProperties, } if (!StringUtils.isBlank(link)) { - format = Page.PageFormat.LINK; + if (EmailUtil.isValidEmailAddress(link)) { + format = Page.PageFormat.EMAILLINK; + } else { + format = Page.PageFormat.LINK; + } } checkMandatoryContent(data, link, content); @@ -499,8 +505,8 @@ public Page.PageStatus[] getPageStatus() { } private void checkCorrectFormat(final MultipartFile data, final Page.PageFormat format) { - if (Page.PageFormat.LINK.equals(format) && data != null && !data.isEmpty()) { - throw new IllegalArgumentException("Wrong format. Cannot set format to LINK and upload a file."); + if ((Page.PageFormat.LINK.equals(format) || Page.PageFormat.EMAILLINK.equals(format)) && data != null && !data.isEmpty()) { + throw new IllegalArgumentException("Wrong format. Cannot set format to LINK or EMAILLINK and upload a file."); } } @@ -619,15 +625,24 @@ private void fillContent(final MultipartFile data, page.setData(content.getBytes()); } else if (page.getData() == null) { // Check the link, unless it refers to a file uploaded to the page, that contains the original file name. - if (StringUtils.isNotBlank(link) && !UrlUtils.isValidRedirectUrl(link)) { - throw new IllegalArgumentException("The link provided is not valid"); - } else { - page.setLink(link); + // The link should be a valid URL or mailto address + if (page.getFormat() == Page.PageFormat.LINK) { + if (StringUtils.isNotBlank(link) && !UrlUtils.isValidRedirectUrl(link)) { + throw new IllegalArgumentException("The link provided is not valid"); + } else { + page.setLink(link); + } + } else if (page.getFormat() == Page.PageFormat.EMAILLINK) { + if (StringUtils.isNotBlank(link) && !EmailUtil.isValidEmailAddress(link)) { + throw new IllegalArgumentException("The link provided is not valid"); + } else { + page.setLink(link); + } } } - } + /** * Check is the user is in designated group to access the static page when page permission level is set to GROUP * @param us Current User Session diff --git a/services/src/main/java/org/fao/geonet/api/users/validation/UserRegisterDtoValidator.java b/services/src/main/java/org/fao/geonet/api/users/validation/UserRegisterDtoValidator.java index f22d7cc821f3..3d291b2d8c7d 100644 --- a/services/src/main/java/org/fao/geonet/api/users/validation/UserRegisterDtoValidator.java +++ b/services/src/main/java/org/fao/geonet/api/users/validation/UserRegisterDtoValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2024 Food and Agriculture Organization of the + * Copyright (C) 2001-2025 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -27,6 +27,7 @@ import org.fao.geonet.api.users.model.UserRegisterDto; import org.fao.geonet.constants.Params; import org.fao.geonet.repository.UserRepository; +import org.fao.geonet.utils.EmailUtil; import org.springframework.util.StringUtils; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; @@ -37,8 +38,6 @@ * */ public class UserRegisterDtoValidator implements Validator { - private static final String OWASP_EMAIL_REGEX = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; - private static final java.util.regex.Pattern OWASP_EMAIL_PATTERN = java.util.regex.Pattern.compile(OWASP_EMAIL_REGEX); @Override public boolean supports(Class clazz) { @@ -55,7 +54,7 @@ public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "field.required", Params.EMAIL + " is required"); - if (StringUtils.hasLength(userRegisterDto.getEmail()) && !OWASP_EMAIL_PATTERN.matcher(userRegisterDto.getEmail()).matches()) { + if (StringUtils.hasLength(userRegisterDto.getEmail()) && !EmailUtil.isValidEmailAddress(userRegisterDto.getEmail())) { errors.rejectValue("email", "field.notvalid", "Email address is not valid"); } diff --git a/web-ui/src/main/resources/catalog/components/pages/GnStaticPagesDirective.js b/web-ui/src/main/resources/catalog/components/pages/GnStaticPagesDirective.js index 2fc26e928fce..b98f64a89fc6 100644 --- a/web-ui/src/main/resources/catalog/components/pages/GnStaticPagesDirective.js +++ b/web-ui/src/main/resources/catalog/components/pages/GnStaticPagesDirective.js @@ -145,7 +145,13 @@ $scope.page = $scope.pagesMenu[0]; $scope.isSubmenu = $scope.page.type === "submenu"; $scope.isExternalLink = - $scope.page.format == "LINK" || $scope.page.format == "HTMLPAGE"; + $scope.page.format === "LINK" || + $scope.page.format === "EMAILLINK" || + $scope.page.format === "HTMLPAGE"; + + if ($scope.page.format === "EMAILLINK") { + $scope.page.link = "mailto:" + $scope.page.link; + } } }, function (response) { diff --git a/web-ui/src/main/resources/catalog/components/pages/partials/menu-page.html b/web-ui/src/main/resources/catalog/components/pages/partials/menu-page.html index 2f86ca3658ef..c42c22051739 100644 --- a/web-ui/src/main/resources/catalog/components/pages/partials/menu-page.html +++ b/web-ui/src/main/resources/catalog/components/pages/partials/menu-page.html @@ -19,7 +19,7 @@ data-ng-repeat="page in page.pages" class="gn-menuitem-xs" role="menuitem" - data-ng-init="submenuExternalLink = (page.format == 'LINK' || page.format == 'HTMLPAGE');" + data-ng-init="submenuExternalLink = (page.format == 'LINK' || page.format == 'EMAILLINK' || page.format == 'HTMLPAGE');" > -
+
@@ -186,7 +186,7 @@ type="text" name="link" class="form-control" - data-ng-required="staticPageSelected.format === 'LINK'" + data-ng-required="isLinkFormat(staticPageSelected.format)" data-ng-model="staticPageSelected.link" />
@@ -194,7 +194,7 @@
@@ -260,7 +260,7 @@
-
+