diff --git a/scm-plugins/scm-git-plugin/package.json b/scm-plugins/scm-git-plugin/package.json index 8d5ac3165b..3b66c4f2c1 100644 --- a/scm-plugins/scm-git-plugin/package.json +++ b/scm-plugins/scm-git-plugin/package.json @@ -12,6 +12,6 @@ "@scm-manager/ui-extensions": "^0.1.2" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25" + "@scm-manager/ui-bundler": "^0.0.26" } } diff --git a/scm-plugins/scm-git-plugin/yarn.lock b/scm-plugins/scm-git-plugin/yarn.lock index 6438262437..a45145d157 100644 --- a/scm-plugins/scm-git-plugin/yarn.lock +++ b/scm-plugins/scm-git-plugin/yarn.lock @@ -707,9 +707,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-plugins/scm-hg-plugin/package.json b/scm-plugins/scm-hg-plugin/package.json index 8d3926f0dd..d723751912 100644 --- a/scm-plugins/scm-hg-plugin/package.json +++ b/scm-plugins/scm-hg-plugin/package.json @@ -9,6 +9,6 @@ "@scm-manager/ui-extensions": "^0.1.2" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25" + "@scm-manager/ui-bundler": "^0.0.26" } } diff --git a/scm-plugins/scm-hg-plugin/yarn.lock b/scm-plugins/scm-hg-plugin/yarn.lock index 47334b6e36..c1e19efede 100644 --- a/scm-plugins/scm-hg-plugin/yarn.lock +++ b/scm-plugins/scm-hg-plugin/yarn.lock @@ -641,9 +641,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-plugins/scm-svn-plugin/package.json b/scm-plugins/scm-svn-plugin/package.json index 8bbf204e2f..10439dba66 100644 --- a/scm-plugins/scm-svn-plugin/package.json +++ b/scm-plugins/scm-svn-plugin/package.json @@ -9,6 +9,6 @@ "@scm-manager/ui-extensions": "^0.1.2" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25" + "@scm-manager/ui-bundler": "^0.0.26" } } diff --git a/scm-plugins/scm-svn-plugin/yarn.lock b/scm-plugins/scm-svn-plugin/yarn.lock index 47334b6e36..c1e19efede 100644 --- a/scm-plugins/scm-svn-plugin/yarn.lock +++ b/scm-plugins/scm-svn-plugin/yarn.lock @@ -641,9 +641,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-ui-components/packages/ui-components/package.json b/scm-ui-components/packages/ui-components/package.json index 80b83b0265..8eaebe3632 100644 --- a/scm-ui-components/packages/ui-components/package.json +++ b/scm-ui-components/packages/ui-components/package.json @@ -14,7 +14,7 @@ "eslint-fix": "eslint src --fix" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25", + "@scm-manager/ui-bundler": "^0.0.26", "create-index": "^2.3.0", "enzyme": "^3.5.0", "enzyme-adapter-react-16": "^1.3.1", diff --git a/scm-ui-components/packages/ui-components/src/DateFromNow.js b/scm-ui-components/packages/ui-components/src/DateFromNow.js index be15bcdad2..3c14f02d0c 100644 --- a/scm-ui-components/packages/ui-components/src/DateFromNow.js +++ b/scm-ui-components/packages/ui-components/src/DateFromNow.js @@ -4,6 +4,10 @@ import moment from "moment"; import { translate } from "react-i18next"; import injectSheet from "react-jss"; +// fix german locale +// https://momentjscom.readthedocs.io/en/latest/moment/00-use-it/07-browserify/ +import "moment/locale/de"; + const styles = { date: { borderBottom: "1px dotted rgba(219, 219, 219)", diff --git a/scm-ui-components/packages/ui-components/src/apiclient.js b/scm-ui-components/packages/ui-components/src/apiclient.js index 77ebe8cf4e..551bb3e2eb 100644 --- a/scm-ui-components/packages/ui-components/src/apiclient.js +++ b/scm-ui-components/packages/ui-components/src/apiclient.js @@ -6,7 +6,9 @@ import type { BackendErrorContent } from "./errors"; const fetchOptions: RequestOptions = { credentials: "same-origin", headers: { - Cache: "no-cache" + Cache: "no-cache", + // identify the request as ajax request + "X-Requested-With": "XMLHttpRequest" } }; diff --git a/scm-ui-components/packages/ui-components/yarn.lock b/scm-ui-components/packages/ui-components/yarn.lock index 1a4c28c9c2..9736de8a78 100644 --- a/scm-ui-components/packages/ui-components/yarn.lock +++ b/scm-ui-components/packages/ui-components/yarn.lock @@ -687,9 +687,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-ui-components/packages/ui-types/package.json b/scm-ui-components/packages/ui-types/package.json index 11029490bb..c6a7c880dc 100644 --- a/scm-ui-components/packages/ui-types/package.json +++ b/scm-ui-components/packages/ui-types/package.json @@ -14,7 +14,7 @@ "check": "flow check" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25" + "@scm-manager/ui-bundler": "^0.0.26" }, "browserify": { "transform": [ diff --git a/scm-ui-components/packages/ui-types/yarn.lock b/scm-ui-components/packages/ui-types/yarn.lock index d0ef9f7a86..2d47639005 100644 --- a/scm-ui-components/packages/ui-types/yarn.lock +++ b/scm-ui-components/packages/ui-types/yarn.lock @@ -707,9 +707,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-ui/package.json b/scm-ui/package.json index 531b3400a9..6844ea3ec7 100644 --- a/scm-ui/package.json +++ b/scm-ui/package.json @@ -52,7 +52,7 @@ "pre-commit": "jest && flow && eslint src" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.25", + "@scm-manager/ui-bundler": "^0.0.26", "concat": "^1.0.3", "copyfiles": "^2.0.0", "enzyme": "^3.3.0", diff --git a/scm-ui/yarn.lock b/scm-ui/yarn.lock index 5c38cb2dba..5b6ec89ef3 100644 --- a/scm-ui/yarn.lock +++ b/scm-ui/yarn.lock @@ -698,9 +698,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.25.tgz#1f65b3ff0ae81559a114c6a8d8cf43856cc6e166" +"@scm-manager/ui-bundler@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java index e58945a346..973b3af4cb 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java @@ -1,7 +1,6 @@ package sonia.scm.web.filter; import sonia.scm.Priority; -import sonia.scm.PushStateDispatcher; import sonia.scm.config.ScmConfiguration; import sonia.scm.filter.Filters; import sonia.scm.filter.WebElement; @@ -12,6 +11,8 @@ import sonia.scm.web.WebTokenGenerator; import sonia.scm.web.protocol.HttpProtocolServlet; import javax.inject.Inject; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -21,25 +22,23 @@ import java.util.Set; @WebElement(value = HttpProtocolServlet.PATTERN) public class HttpProtocolServletAuthenticationFilter extends AuthenticationFilter { - private final PushStateDispatcher dispatcher; private final UserAgentParser userAgentParser; @Inject public HttpProtocolServletAuthenticationFilter( ScmConfiguration configuration, Set tokenGenerators, - PushStateDispatcher dispatcher, UserAgentParser userAgentParser) { super(configuration, tokenGenerators); - this.dispatcher = dispatcher; this.userAgentParser = userAgentParser; } @Override - protected void sendUnauthorizedError(HttpServletRequest request, HttpServletResponse response) throws IOException { + protected void handleUnauthorized(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { UserAgent userAgent = userAgentParser.parse(request); if (userAgent.isBrowser()) { - dispatcher.dispatch(request, response, request.getRequestURI()); + // we can proceed the filter chain because the HttpProtocolServlet will render the ui if the client is a browser + chain.doFilter(request, response); } else { HttpUtil.sendUnauthorized(request, response); } diff --git a/scm-webapp/src/test/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilterTest.java b/scm-webapp/src/test/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilterTest.java new file mode 100644 index 0000000000..ff493e2b84 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilterTest.java @@ -0,0 +1,72 @@ +package sonia.scm.web.filter; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.util.HttpUtil; +import sonia.scm.web.UserAgent; +import sonia.scm.web.UserAgentParser; +import sonia.scm.web.WebTokenGenerator; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HttpProtocolServletAuthenticationFilterTest { + + private ScmConfiguration configuration = new ScmConfiguration(); + + private Set tokenGenerators = Collections.emptySet(); + + @Mock + private UserAgentParser userAgentParser; + + private HttpProtocolServletAuthenticationFilter authenticationFilter; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private FilterChain filterChain; + + private UserAgent nonBrowser = UserAgent.builder("i'm not a browser").browser(false).build(); + private UserAgent browser = UserAgent.builder("i am a browser").browser(true).build(); + + @BeforeEach + void setUpObjectUnderTest() { + authenticationFilter = new HttpProtocolServletAuthenticationFilter(configuration, tokenGenerators, userAgentParser); + } + + @Test + void shouldSendUnauthorized() throws IOException, ServletException { + when(userAgentParser.parse(request)).thenReturn(nonBrowser); + + authenticationFilter.handleUnauthorized(request, response, filterChain); + + verify(response).sendError(HttpServletResponse.SC_UNAUTHORIZED, HttpUtil.STATUS_UNAUTHORIZED_MESSAGE); + } + + @Test + void shouldCallFilterChain() throws IOException, ServletException { + when(userAgentParser.parse(request)).thenReturn(browser); + + authenticationFilter.handleUnauthorized(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + } + +}