diff --git a/core/src/main/java/org/fao/geonet/security/web/csrf/CookieCsrfTokenRepository.java b/core/src/main/java/org/fao/geonet/security/web/csrf/CookieCsrfTokenRepository.java new file mode 100644 index 00000000000..e3de27bba45 --- /dev/null +++ b/core/src/main/java/org/fao/geonet/security/web/csrf/CookieCsrfTokenRepository.java @@ -0,0 +1,199 @@ +/* + * Taken from https://raw.githubusercontent.com/spring-projects/spring-security/master/web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java + */ + +package org.fao.geonet.security.web.csrf; + +import java.lang.reflect.Method; +import java.util.UUID; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.DefaultCsrfToken; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.util.WebUtils; + +/** + * A {@link CsrfTokenRepository} that persists the CSRF token in a cookie named + * "XSRF-TOKEN" and reads from the header "X-XSRF-TOKEN" following the + * conventions of AngularJS. When using with AngularJS be sure to use + * {@link #withHttpOnlyFalse()}. + * + * @author Rob Winch + * @since 4.1 + */ +public final class CookieCsrfTokenRepository implements CsrfTokenRepository { + static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN"; + + static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; + + static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN"; + + private String parameterName = DEFAULT_CSRF_PARAMETER_NAME; + + private String headerName = DEFAULT_CSRF_HEADER_NAME; + + private String cookieName = DEFAULT_CSRF_COOKIE_NAME; + + private final Method setHttpOnlyMethod; + + private boolean cookieHttpOnly; + + private String cookiePath; + + public CookieCsrfTokenRepository() { + this.setHttpOnlyMethod = ReflectionUtils.findMethod(Cookie.class, "setHttpOnly", boolean.class); + if (this.setHttpOnlyMethod != null) { + this.cookieHttpOnly = true; + } + } + + @Override + public CsrfToken generateToken(HttpServletRequest request) { + return new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken()); + } + + @Override + public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { + String tokenValue = token == null ? "" : token.getToken(); + Cookie cookie = new Cookie(this.cookieName, tokenValue); + cookie.setSecure(request.isSecure()); + if (this.cookiePath != null && !this.cookiePath.isEmpty()) { + cookie.setPath(this.cookiePath); + } else { + cookie.setPath(this.getRequestContext(request)); + } + if (token == null) { + cookie.setMaxAge(0); + } else { + cookie.setMaxAge(-1); + } + if (cookieHttpOnly && setHttpOnlyMethod != null) { + ReflectionUtils.invokeMethod(setHttpOnlyMethod, cookie, Boolean.TRUE); + } + + response.addCookie(cookie); + } + + @Override + public CsrfToken loadToken(HttpServletRequest request) { + Cookie cookie = WebUtils.getCookie(request, this.cookieName); + if (cookie == null) { + return null; + } + String token = cookie.getValue(); + if (!StringUtils.hasLength(token)) { + return null; + } + return new DefaultCsrfToken(this.headerName, this.parameterName, token); + } + + /** + * Sets the name of the HTTP request parameter that should be used to provide + * a token. + * + * @param parameterName + * the name of the HTTP request parameter that should be used to + * provide a token + */ + public void setParameterName(String parameterName) { + Assert.notNull(parameterName, "parameterName is not null"); + this.parameterName = parameterName; + } + + /** + * Sets the name of the HTTP header that should be used to provide the token. + * + * @param headerName + * the name of the HTTP header that should be used to provide the + * token + */ + public void setHeaderName(String headerName) { + Assert.notNull(headerName, "headerName is not null"); + this.headerName = headerName; + } + + /** + * Sets the name of the cookie that the expected CSRF token is saved to and + * read from. + * + * @param cookieName + * the name of the cookie that the expected CSRF token is saved to + * and read from + */ + public void setCookieName(String cookieName) { + Assert.notNull(cookieName, "cookieName is not null"); + this.cookieName = cookieName; + } + + /** + * Sets the HttpOnly attribute on the cookie containing the CSRF token. The + * cookie will only be marked as HttpOnly if both cookieHttpOnly + * is true and the underlying version of Servlet is 3.0 or + * greater. Defaults to true if the underlying version of Servlet + * is 3.0 or greater. NOTE: The {@link Cookie#setHttpOnly(boolean)} was + * introduced in Servlet 3.0. + * + * @param cookieHttpOnly + * true sets the HttpOnly attribute, false + * does not set it (depending on Servlet version) + * @throws IllegalArgumentException + * if cookieHttpOnly is true and the + * underlying version of Servlet is less than 3.0 + */ + public void setCookieHttpOnly(boolean cookieHttpOnly) { + if (cookieHttpOnly && setHttpOnlyMethod == null) { + throw new IllegalArgumentException( + "Cookie will not be marked as HttpOnly because you are using a version of Servlet less than 3.0. NOTE: The Cookie#setHttpOnly(boolean) was introduced in Servlet 3.0."); + } + this.cookieHttpOnly = cookieHttpOnly; + } + + private String getRequestContext(HttpServletRequest request) { + String contextPath = request.getContextPath(); + return contextPath.length() > 0 ? contextPath : "/"; + } + + /** + * Factory method to conveniently create an instance that has + * {@link #setCookieHttpOnly(boolean)} set to false. + * + * @return an instance of CookieCsrfTokenRepository with + * {@link #setCookieHttpOnly(boolean)} set to false + */ + public static CookieCsrfTokenRepository withHttpOnlyFalse() { + CookieCsrfTokenRepository result = new CookieCsrfTokenRepository(); + result.setCookieHttpOnly(false); + return result; + } + + private String createNewToken() { + return UUID.randomUUID().toString(); + } + + /** + * Set the path that the Cookie will be created with. This will override the + * default functionality which uses the request context as the path. + * + * @param path + * the path to use + */ + public void setCookiePath(String path) { + this.cookiePath = path; + } + + /** + * Get the path that the CSRF cookie will be set to. + * + * @return the path to be used. + */ + public String getCookiePath() { + return this.cookiePath; + } +} \ No newline at end of file diff --git a/web-ui/src/main/resources/catalog/components/admin/schematron/partials/criteria-viewer.html b/web-ui/src/main/resources/catalog/components/admin/schematron/partials/criteria-viewer.html index cdcafe510c2..7ef9b608b3f 100644 --- a/web-ui/src/main/resources/catalog/components/admin/schematron/partials/criteria-viewer.html +++ b/web-ui/src/main/resources/catalog/components/admin/schematron/partials/criteria-viewer.html @@ -19,6 +19,7 @@
+
diff --git a/web-ui/src/main/resources/catalog/components/common/map/print/partials/printmap.html b/web-ui/src/main/resources/catalog/components/common/map/print/partials/printmap.html index aeb46fff45a..dfa42d6fe85 100644 --- a/web-ui/src/main/resources/catalog/components/common/map/print/partials/printmap.html +++ b/web-ui/src/main/resources/catalog/components/common/map/print/partials/printmap.html @@ -1,4 +1,5 @@ +
diff --git a/web-ui/src/main/resources/catalog/components/common/share/partials/panel.html b/web-ui/src/main/resources/catalog/components/common/share/partials/panel.html index 4828501ed65..531615a193f 100644 --- a/web-ui/src/main/resources/catalog/components/common/share/partials/panel.html +++ b/web-ui/src/main/resources/catalog/components/common/share/partials/panel.html @@ -20,6 +20,7 @@
whoCanAccess
+ diff --git a/web-ui/src/main/resources/catalog/components/contactus/partials/contactusform.html b/web-ui/src/main/resources/catalog/components/contactus/partials/contactusform.html index 1de25112b82..e99071f80d3 100644 --- a/web-ui/src/main/resources/catalog/components/contactus/partials/contactusform.html +++ b/web-ui/src/main/resources/catalog/components/contactus/partials/contactusform.html @@ -1,4 +1,5 @@ +
diff --git a/web-ui/src/main/resources/catalog/components/edit/geopublisher/partials/geopublisher.html b/web-ui/src/main/resources/catalog/components/edit/geopublisher/partials/geopublisher.html index 612b4517d1d..d41643b3ae1 100644 --- a/web-ui/src/main/resources/catalog/components/edit/geopublisher/partials/geopublisher.html +++ b/web-ui/src/main/resources/catalog/components/edit/geopublisher/partials/geopublisher.html @@ -28,6 +28,7 @@
+
@@ -44,6 +45,7 @@

mostPopular

bestRated

+
{{md.title || md.defaultTitle}}
diff --git a/web-ui/src/main/resources/catalog/templates/admin/dashboard/statistics-search.html b/web-ui/src/main/resources/catalog/templates/admin/dashboard/statistics-search.html index a7880c51648..3ddf84d8d86 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/dashboard/statistics-search.html +++ b/web-ui/src/main/resources/catalog/templates/admin/dashboard/statistics-search.html @@ -38,6 +38,7 @@

cswStatistics

+
diff --git a/web-ui/src/main/resources/catalog/templates/admin/dashboard/status.html b/web-ui/src/main/resources/catalog/templates/admin/dashboard/status.html index d5827532468..85073962674 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/dashboard/status.html +++ b/web-ui/src/main/resources/catalog/templates/admin/dashboard/status.html @@ -75,6 +75,7 @@

{{info.name}}

data-ng-search-form="" data-runSearch="true" data-ng-show="searchResults.records.length > 0"> +
metadataWithIndexingErrors
@@ -188,6 +189,7 @@

+
{{md.title || md.defaultTitle}}
@@ -117,6 +118,7 @@
+

customizeElementSetHelp

diff --git a/web-ui/src/main/resources/catalog/templates/admin/settings/logo.html b/web-ui/src/main/resources/catalog/templates/admin/settings/logo.html index e0cb8a0a383..31865b0c377 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/settings/logo.html +++ b/web-ui/src/main/resources/catalog/templates/admin/settings/logo.html @@ -16,7 +16,7 @@ addNewLogo
- diff --git a/web-ui/src/main/resources/catalog/templates/admin/settings/mapservers.html b/web-ui/src/main/resources/catalog/templates/admin/settings/mapservers.html index 3c3f0ae22d9..b80f79782dc 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/settings/mapservers.html +++ b/web-ui/src/main/resources/catalog/templates/admin/settings/mapservers.html @@ -54,6 +54,7 @@ +
mapserverDescription
+
diff --git a/web-ui/src/main/resources/catalog/templates/admin/settings/system.html b/web-ui/src/main/resources/catalog/templates/admin/settings/system.html index dbafbc3c743..db91089a4ed 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/settings/system.html +++ b/web-ui/src/main/resources/catalog/templates/admin/settings/system.html @@ -5,6 +5,7 @@ settings
+
diff --git a/web-ui/src/main/resources/catalog/templates/admin/tools/batch.html b/web-ui/src/main/resources/catalog/templates/admin/tools/batch.html index 1a91cd053cc..617dc993d98 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/tools/batch.html +++ b/web-ui/src/main/resources/catalog/templates/admin/tools/batch.html @@ -9,6 +9,7 @@ +
@@ -148,6 +149,7 @@
configureProcess
+
diff --git a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html index db5801d3a2e..dad98ab0113 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html +++ b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html @@ -74,6 +74,7 @@
+
@@ -341,6 +342,7 @@