From 23317662f2299a7ba9c4c976ce8c647e93dacb36 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 6 Nov 2020 12:15:19 +0100 Subject: [PATCH 01/25] Implement mercurial hooks over sockets --- .../hooks/DefaultHookHandlerFactory.java | 51 +++++ .../scm/repository/hooks/HgHookHandler.java | 157 ++++++++++++++ .../scm/repository/hooks/HookEnvironment.java | 60 ++++++ .../repository/hooks/HookHandlerFactory.java | 37 ++++ .../scm/repository/hooks/HookServer.java | 118 +++++++++++ .../sonia/scm/repository/hooks/Sockets.java | 56 +++++ .../repository/hooks/HgHookHandlerTest.java | 191 ++++++++++++++++++ .../scm/repository/hooks/HookServerTest.java | 135 +++++++++++++ .../org.mockito.plugins.MockMaker | 1 + 9 files changed, 806 insertions(+) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookEnvironment.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java new file mode 100644 index 0000000000..93f0915486 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.spi.HookEventFacade; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.net.Socket; + +public class DefaultHookHandlerFactory implements HookHandlerFactory { + + private final HgRepositoryHandler handler; + private final HookEventFacade hookEventFacade; + private final Provider hookEnvironment; + + @Inject + public DefaultHookHandlerFactory(HgRepositoryHandler handler, HookEventFacade hookEventFacade, Provider hookEnvironment) { + this.handler = handler; + this.hookEventFacade = hookEventFacade; + this.hookEnvironment = hookEnvironment; + } + + @Override + public HgHookHandler create(Socket socket) { + return new HgHookHandler(handler, hookEventFacade, hookEnvironment, socket); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java new file mode 100644 index 0000000000..e2398b4498 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java @@ -0,0 +1,157 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.RepositoryHookType; +import sonia.scm.repository.api.HgHookMessage; +import sonia.scm.repository.spi.HgHookContextProvider; +import sonia.scm.repository.spi.HookEventFacade; +import sonia.scm.security.BearerToken; +import sonia.scm.security.CipherUtil; + +import javax.annotation.Nonnull; +import javax.inject.Provider; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.List; + +import static java.util.Collections.singletonList; + +class HgHookHandler implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(HgHookHandler.class); + + private final HgRepositoryHandler handler; + private final HookEventFacade hookEventFacade; + private final Provider environmentProvider; + private final Socket socket; + + HgHookHandler(HgRepositoryHandler handler, HookEventFacade hookEventFacade, Provider environmentProvider, Socket socket) { + this.handler = handler; + this.hookEventFacade = hookEventFacade; + this.environmentProvider = environmentProvider; + this.socket = socket; + } + + @Override + public void run() { + try (InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream()) { + handleHookRequest(input, output); + } catch (IOException e) { + LOG.warn("failed to read hook request", e); + } finally { + close(); + } + } + + private void handleHookRequest(InputStream input, OutputStream output) throws IOException { + Request request = Sockets.read(input, Request.class); + Response response = handleHookRequest(request); + Sockets.send(output, response); + } + + private Response handleHookRequest(Request request) { + HookEnvironment environment = environmentProvider.get(); + try { + if (!environment.isAcceptAble(request.getChallenge())) { + return error("invalid hook challenge"); + } + + authenticate(request); + environment.setPending(request.getType() == RepositoryHookType.PRE_RECEIVE); + + HgHookContextProvider context = createHookContextProvider(request); + hookEventFacade.handle(request.getRepositoryId()).fireHookEvent(request.getType(), context); + + return new Response(context.getHgMessageProvider().getMessages(), false); + } catch (AuthenticationException ex) { + LOG.warn("hook authentication failed", ex); + return error("hook authentication failed"); + } catch (Exception ex) { + LOG.warn("unknown error on hook occurred", ex); + return error("unknown error"); + } finally { + environment.clearPendingState(); + } + } + + @Nonnull + private HgHookContextProvider createHookContextProvider(Request request) { + File repositoryDirectory = handler.getDirectory(request.getRepositoryId()); + return new HgHookContextProvider( + handler, repositoryDirectory, null, request.node, request.type + ); + } + + private void authenticate(Request request) { + String token = CipherUtil.getInstance().decode(request.getToken()); + BearerToken bearer = BearerToken.valueOf(token); + Subject subject = SecurityUtils.getSubject(); + subject.login(bearer); + } + + private Response error(String message) { + return new Response( + singletonList(new HgHookMessage(HgHookMessage.Severity.ERROR, message)), + true + ); + } + + private void close() { + try { + socket.close(); + } catch (IOException e) { + LOG.debug("failed to close hook socket", e); + } + } + + @Data + @AllArgsConstructor + public static class Request { + private String token; + private RepositoryHookType type; + private String repositoryId; + private String challenge; + private String node; + } + + @Data + @AllArgsConstructor + public static class Response { + private List messages; + private boolean abort; + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookEnvironment.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookEnvironment.java new file mode 100644 index 0000000000..a1d205a9d5 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookEnvironment.java @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class HookEnvironment { + + private final ThreadLocal threadEnvironment = new ThreadLocal<>(); + private final String challenge = UUID.randomUUID().toString(); + + public String getChallenge() { + return challenge; + } + + public boolean isAcceptAble(String challenge) { + return this.challenge.equals(challenge); + } + + void setPending(boolean pending) { + threadEnvironment.set(pending); + } + + void clearPendingState() { + threadEnvironment.remove(); + } + + public boolean isPending() { + Boolean threadState = threadEnvironment.get(); + if (threadState != null) { + return threadState; + } + return false; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java new file mode 100644 index 0000000000..61d3a1340d --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import com.google.inject.ImplementedBy; + +import java.net.Socket; + +@FunctionalInterface +@ImplementedBy(DefaultHookHandlerFactory.class) +interface HookHandlerFactory { + + Runnable create(Socket socket); + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java new file mode 100644 index 0000000000..0ecbe180c4 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java @@ -0,0 +1,118 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.apache.shiro.concurrent.SubjectAwareExecutorService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Singleton +public class HookServer implements AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(HookServer.class); + + private final HookHandlerFactory handlerFactory; + + private ExecutorService acceptor; + private ExecutorService workerPool; + private ServerSocket serverSocket; + + @Inject + public HookServer(HookHandlerFactory handlerFactory) { + this.handlerFactory = handlerFactory; + } + + private ExecutorService createAcceptor() { + return new SubjectAwareExecutorService(Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder().setNameFormat("HgHookAcceptor").build() + )); + } + + private ExecutorService createWorkerPool() { + return new SubjectAwareExecutorService(Executors.newCachedThreadPool( + new ThreadFactoryBuilder().setNameFormat("HgHookWorker-%d").build() + ) + ); + } + + public int start() throws IOException { + acceptor = createAcceptor(); + workerPool = createWorkerPool(); + serverSocket = createServerSocket(); + // set timeout to 2 min, to avoid blocking clients + serverSocket.setSoTimeout(2 * 60 * 1000); + + accept(); + + int port = serverSocket.getLocalPort(); + LOG.info("open hg hook server on port {}", port); + return port; + } + + private void accept() { + acceptor.submit(() -> { + while (!serverSocket.isClosed()) { + try { + Socket clientSocket = serverSocket.accept(); + LOG.trace("accept incoming hook client from {}", clientSocket.getInetAddress()); + workerPool.submit(handlerFactory.create(clientSocket)); + } catch (IOException ex) { + LOG.debug("failed to accept socket, possible closed", ex); + } + } + }); + } + + @Nonnull + private ServerSocket createServerSocket() throws IOException { + return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + } + + @Override + public void close() throws IOException { + if (serverSocket != null) { + serverSocket.close(); + } + shutdown(acceptor); + shutdown(workerPool); + } + + private void shutdown(ExecutorService acceptor) { + if (acceptor != null) { + acceptor.shutdown(); + } + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java new file mode 100644 index 0000000000..e6b748643b --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class Sockets { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private Sockets() { + } + + static void send(OutputStream out, Object object) throws IOException { + byte[] bytes = objectMapper.writeValueAsBytes(object); + out.write(bytes); + out.write('\0'); + } + + static T read(InputStream in, Class type) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int c = in.read(); + while (c != '\0') { + buffer.write(c); + c = in.read(); + } + return objectMapper.readValue(buffer.toByteArray(), type); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java new file mode 100644 index 0000000000..516b66c4a3 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java @@ -0,0 +1,191 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import com.google.inject.util.Providers; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +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.repository.HgRepositoryHandler; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHookType; +import sonia.scm.repository.api.HgHookMessage; +import sonia.scm.repository.spi.HgHookContextProvider; +import sonia.scm.repository.spi.HookEventFacade; +import sonia.scm.security.CipherUtil; + +import javax.annotation.Nonnull; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.Socket; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HgHookHandlerTest { + + @Mock + private HgRepositoryHandler repositoryHandler; + + @Mock + private HookEventFacade hookEventFacade; + + @Mock + private HookEventFacade.HookEventHandler hookEventHandler; + + @Mock + private Socket socket; + + private HookEnvironment hookEnvironment; + + private HgHookHandler handler; + + @Mock + private Subject subject; + + @BeforeEach + void setUp() { + ThreadContext.bind(subject); + + hookEnvironment = new HookEnvironment(); + DefaultHookHandlerFactory factory = new DefaultHookHandlerFactory( + repositoryHandler, hookEventFacade, Providers.of(hookEnvironment) + ); + + handler = factory.create(socket); + } + + @AfterEach + void tearDown() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldFireHook() throws IOException { + when(hookEventFacade.handle("42")).thenReturn(hookEventHandler); + + HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + HgHookHandler.Response response = send(request); + + assertSuccess(response, RepositoryHookType.POST_RECEIVE); + assertThat(hookEnvironment.isPending()).isFalse(); + } + + @Test + void shouldSetPendingStateOnPreReceiveHooks() throws IOException { + when(hookEventFacade.handle("42")).thenReturn(hookEventHandler); + + HgHookHandler.Request request = createRequest(RepositoryHookType.PRE_RECEIVE); + HgHookHandler.Response response = send(request); + + assertSuccess(response, RepositoryHookType.PRE_RECEIVE); + assertThat(hookEnvironment.isPending()).isTrue(); + } + + @Test + void shouldHandleAuthenticationFailure() throws IOException { + doThrow(IllegalStateException.class) + .when(hookEventFacade) + .handle("42"); + + HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + HgHookHandler.Response response = send(request); + + assertError(response, "unknown"); + } + + @Test + void shouldHandleUnknownFailure() throws IOException { + doThrow(AuthenticationException.class) + .when(subject) + .login(any(AuthenticationToken.class)); + + HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + HgHookHandler.Response response = send(request); + + assertError(response, "authentication"); + } + + @Test + void shouldReturnErrorWithInvalidChallenge() throws IOException { + HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE, "something-different"); + HgHookHandler.Response response = send(request); + + assertError(response, "challenge"); + } + + private void assertSuccess(HgHookHandler.Response response, RepositoryHookType type) { + assertThat(response.getMessages()).isEmpty(); + assertThat(response.isAbort()).isFalse(); + + verify(hookEventHandler).fireHookEvent(eq(type), any(HgHookContextProvider.class)); + } + + private void assertError(HgHookHandler.Response response, String message) { + assertThat(response.isAbort()).isTrue(); + assertThat(response.getMessages()).hasSize(1); + HgHookMessage hgHookMessage = response.getMessages().get(0); + assertThat(hgHookMessage.getSeverity()).isEqualTo(HgHookMessage.Severity.ERROR); + assertThat(hgHookMessage.getMessage()).contains(message); + } + + @Nonnull + private HgHookHandler.Request createRequest(RepositoryHookType type) { + return createRequest(type, hookEnvironment.getChallenge()); + } + + @Nonnull + private HgHookHandler.Request createRequest(RepositoryHookType type, String challenge) { + String secret = CipherUtil.getInstance().encode("secret"); + return new HgHookHandler.Request( + secret, type, "42", challenge, "abc" + ); + } + + private HgHookHandler.Response send(HgHookHandler.Request request) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + Sockets.send(buffer, request); + ByteArrayInputStream input = new ByteArrayInputStream(buffer.toByteArray()); + when(socket.getInputStream()).thenReturn(input); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + when(socket.getOutputStream()).thenReturn(output); + + handler.run(); + + return Sockets.read(new ByteArrayInputStream(output.toByteArray()), HgHookHandler.Response.class); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java new file mode 100644 index 0000000000..afd485c477 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java @@ -0,0 +1,135 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.mgt.DefaultSecurityManager; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class HookServerTest { + + private static final Logger LOG = LoggerFactory.getLogger(HookServerTest.class); + + @BeforeEach + void setUp() { + DefaultSecurityManager securityManager = new DefaultSecurityManager(); + ThreadContext.bind(securityManager); + Subject subject = new Subject.Builder().principals(new SimplePrincipalCollection("Tricia", "Testing")).buildSubject(); + ThreadContext.bind(subject); + } + + @AfterEach + void tearDown() { + ThreadContext.unbindSubject(); + ThreadContext.unbindSecurityManager(); + } + + @Test + void shouldStartHookServer() throws IOException { + Response response = send(new Request("Joe")); + assertThat(response.getGreeting()).isEqualTo("Hello Joe"); + assertThat(response.getGreeter()).isEqualTo("Tricia"); + } + + private Response send(Request request) throws IOException { + try (HookServer server = new HookServer(HelloHandler::new)) { + int port = server.start(); + try ( + Socket socket = new Socket(InetAddress.getLoopbackAddress(), port); + InputStream input = socket.getInputStream(); + OutputStream output = socket.getOutputStream() + ) { + Sockets.send(output, request); + return Sockets.read(input, Response.class); + } catch (IOException ex) { + throw new RuntimeException("failed", ex); + } + } + } + + public static class HelloHandler implements Runnable { + + private final Socket socket; + + private HelloHandler(Socket socket) { + this.socket = socket; + } + + @Override + public void run() { + try (InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream()) { + Request request = Sockets.read(input, Request.class); + Subject subject = SecurityUtils.getSubject(); + Sockets.send(output, new Response("Hello " + request.getName(), subject.getPrincipal().toString())); + } catch (IOException ex) { + throw new RuntimeException("failed", ex); + } finally { + try { + socket.close(); + } catch (IOException e) { + LOG.error("failed to close socket", e); + } + } + } + } + + @Data + @AllArgsConstructor + public static class Request { + + private String name; + + } + + @Data + @AllArgsConstructor + public static class Response { + + private String greeting; + private String greeter; + + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/scm-plugins/scm-hg-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline From d518af4ccc7dda0afb3bb10208eed9fae65a33c6 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 7 Nov 2020 15:52:22 +0100 Subject: [PATCH 02/25] Refactor nearly the whole scm-hg-plugin for new hook implementation --- .../scm/api/v2/resources/HgConfigDto.java | 6 +- .../scm/repository/AbstractHgHandler.java | 391 --------------- .../DefaultHgEnvironmentBuilder.java | 129 +++++ .../java/sonia/scm/repository/HgConfig.java | 27 +- .../java/sonia/scm/repository/HgContext.java | 121 ----- .../scm/repository/HgContextProvider.java | 96 ---- .../sonia/scm/repository/HgEnvironment.java | 127 ----- ...stStore.java => HgEnvironmentBuilder.java} | 27 +- .../sonia/scm/repository/HgHookManager.java | 354 -------------- .../scm/repository/HgRepositoryFactory.java | 106 +++++ .../scm/repository/HgRepositoryHandler.java | 90 +--- .../java/sonia/scm/repository/HgVersion.java | 15 +- .../scm/repository/api/HgHookMessage.java | 68 +-- ...okHandler.java => DefaultHookHandler.java} | 36 +- .../HookContextProviderFactory.java} | 67 +-- .../scm/repository/hooks/HookHandler.java | 28 ++ .../repository/hooks/HookHandlerFactory.java | 5 +- ...ookHandlerFactory.java => HookModule.java} | 31 +- .../scm/repository/hooks/HookServer.java | 39 +- .../scm/repository/spi/HgCommandContext.java | 132 +----- .../spi/HgHookChangesetProvider.java | 112 +---- .../repository/spi/HgHookContextProvider.java | 79 +--- .../spi/HgRepositoryServiceProvider.java | 38 +- .../spi/HgRepositoryServiceResolver.java | 17 +- .../scm/repository/spi/HgVersionCommand.java | 114 +++++ .../spi/SimpleHgWorkingCopyFactory.java | 20 +- .../main/java/sonia/scm/web/HgCGIServlet.java | 60 +-- .../sonia/scm/web/HgHookCallbackServlet.java | 446 ------------------ .../web/HgRepositoryEnvironmentBuilder.java | 80 ---- .../java/sonia/scm/web/HgServletModule.java | 26 +- .../src/main/java/sonia/scm/web/HgUtil.java | 155 +----- .../resources/sonia/scm/python/scmhooks.py | 109 ++--- .../HgConfigDtoToHgConfigMapperTest.java | 4 +- .../DefaultHgEnvironmentBuilderTest.java | 149 ++++++ .../EmptyHgEnvironmentBuilder.java} | 30 +- .../scm/repository/HgContextProviderTest.java | 111 ----- .../repository/HgRepositoryFactoryTest.java | 125 +++++ .../repository/HgRepositoryHandlerTest.java | 29 +- .../java/sonia/scm/repository/HgTestUtil.java | 54 +-- ...rTest.java => DefaultHookHandlerTest.java} | 83 ++-- .../HookContextProviderFactoryTest.java} | 60 +-- .../scm/repository/hooks/HookServerTest.java | 2 +- .../spi/AbstractHgCommandTestBase.java | 20 +- .../repository/spi/HgBranchCommandTest.java | 6 +- .../repository/spi/HgIncomingCommandTest.java | 17 +- .../spi/HgModificationsCommandTest.java | 27 +- .../repository/spi/HgModifyCommandTest.java | 8 +- .../repository/spi/HgOutgoingCommandTest.java | 17 +- .../repository/spi/HgVersionCommandTest.java | 137 ++++++ .../spi/IncomingOutgoingTestBase.java | 4 +- .../spi/SimpleHgWorkingCopyFactoryTest.java | 7 +- .../scm/web/HgHookCallbackServletTest.java | 60 --- .../lifecycle/modules/ScmSecurityModule.java | 4 +- 53 files changed, 1230 insertions(+), 2875 deletions(-) delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContext.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextProvider.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironment.java rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/{HgContextRequestStore.java => HgEnvironmentBuilder.java} (59%) delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/{HgHookHandler.java => DefaultHookHandler.java} (81%) rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/{spi/AbstractHgCommand.java => hooks/HookContextProviderFactory.java} (52%) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandler.java rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/{DefaultHookHandlerFactory.java => HookModule.java} (60%) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgRepositoryEnvironmentBuilder.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java rename scm-plugins/scm-hg-plugin/src/{main/java/sonia/scm/repository/HgVersionHandler.java => test/java/sonia/scm/repository/EmptyHgEnvironmentBuilder.java} (66%) delete mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgContextProviderTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/{HgHookHandlerTest.java => DefaultHookHandlerTest.java} (63%) rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/{HgEnvironmentTest.java => hooks/HookContextProviderFactoryTest.java} (52%) create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java delete mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java index b5039cbb44..464e896767 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; @@ -30,9 +30,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -@NoArgsConstructor @Getter @Setter +@NoArgsConstructor +@SuppressWarnings("java:S2160") // we don't need equals for dto public class HgConfigDto extends HalRepresentation { private boolean disabled; @@ -44,7 +45,6 @@ public class HgConfigDto extends HalRepresentation { private boolean useOptimizedBytecode; private boolean showRevisionInId; private boolean enableHttpPostArgs; - private boolean disableHookSSLValidation; @Override @SuppressWarnings("squid:S1185") // We want to have this method available in this package diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java deleted file mode 100644 index a74fdf9b21..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.SCMContext; -import sonia.scm.util.IOUtil; -import sonia.scm.util.Util; -import sonia.scm.web.HgUtil; - -import javax.xml.bind.JAXBException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * - * @author Sebastian Sdorra - */ -public class AbstractHgHandler -{ - - /** Field description */ - protected static final String ENV_ID_REVISION = "SCM_ID_REVISION"; - - /** Field description */ - protected static final String ENV_NODE = "HG_NODE"; - - /** Field description */ - protected static final String ENV_PAGE_LIMIT = "SCM_PAGE_LIMIT"; - - /** Field description */ - protected static final String ENV_PAGE_START = "SCM_PAGE_START"; - - /** Field description */ - protected static final String ENV_PATH = "SCM_PATH"; - - /** Field description */ - protected static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH"; - - /** Field description */ - protected static final String ENV_REVISION = "SCM_REVISION"; - - /** Field description */ - protected static final String ENV_REVISION_END = "SCM_REVISION_END"; - - /** Field description */ - protected static final String ENV_REVISION_START = "SCM_REVISION_START"; - - /** Field description */ - private static final String ENCODING = "UTF-8"; - - /** mercurial encoding */ - private static final String ENV_HGENCODING = "HGENCODING"; - - /** Field description */ - private static final String ENV_PENDING = "HG_PENDING"; - - /** python encoding */ - private static final String ENV_PYTHONIOENCODING = "PYTHONIOENCODING"; - - /** Field description */ - private static final String ENV_PYTHONPATH = "PYTHONPATH"; - - /** - * the logger for AbstractHgCommand - */ - private static final Logger logger = - LoggerFactory.getLogger(AbstractHgHandler.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * - * @param handler - * @param context - * @param repository - */ - protected AbstractHgHandler(HgRepositoryHandler handler, HgContext context, - Repository repository) - { - this(handler, context, repository, handler.getDirectory(repository.getId())); - } - - /** - * Constructs ... - * - * - * - * @param handler - * @param context - * @param repository - * @param repositoryDirectory - */ - protected AbstractHgHandler(HgRepositoryHandler handler, HgContext context, - Repository repository, File repositoryDirectory) - { - this.handler = handler; - this.context = context; - this.repository = repository; - this.repositoryDirectory = repositoryDirectory; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param revision - * @param path - * - * @return - */ - protected Map createEnvironment(String revision, String path) - { - Map env = new HashMap<>(); - - env.put(ENV_REVISION, HgUtil.getRevision(revision)); - env.put(ENV_PATH, Util.nonNull(path)); - - return env; - } - - /** - * Method description - * - * - * @param args - * - * @return - * - * @throws IOException - */ - protected Process createHgProcess(String... args) throws IOException - { - return createHgProcess(new HashMap(), args); - } - - /** - * Method description - * - * - * @param extraEnv - * @param args - * - * @return - * - * @throws IOException - */ - protected Process createHgProcess(Map extraEnv, - String... args) - throws IOException - { - return createProcess(extraEnv, handler.getConfig().getHgBinary(), args); - } - - /** - * Method description - * - * - * @param script - * @param extraEnv - * - * @return - * - * @throws IOException - */ - protected Process createScriptProcess(HgPythonScript script, - Map extraEnv) - throws IOException - { - return createProcess(extraEnv, handler.getConfig().getPythonBinary(), - script.getFile(SCMContext.getContext()).getAbsolutePath()); - } - - /** - * Method description - * - * - * @param errorStream - */ - protected void handleErrorStream(final InputStream errorStream) - { - if (errorStream != null) - { - new Thread(new Runnable() - { - @Override - public void run() - { - try - { - String content = IOUtil.getContent(errorStream); - - if (Util.isNotEmpty(content)) - { - logger.error(content.trim()); - } - } - catch (IOException ex) - { - logger.error("error during logging", ex); - } - } - }).start(); - } - } - - //~--- get methods ---------------------------------------------------------- - - protected T getResultFromScript(Class resultType, HgPythonScript script) throws IOException { - return getResultFromScript(resultType, script, - new HashMap()); - } - - @SuppressWarnings("unchecked") - protected T getResultFromScript(Class resultType, - HgPythonScript script, Map extraEnv) - throws IOException - { - Process p = createScriptProcess(script, extraEnv); - - handleErrorStream(p.getErrorStream()); - try (InputStream input = p.getInputStream()) { - return (T) handler.getJaxbContext().createUnmarshaller().unmarshal(input); - } catch (JAXBException ex) { - logger.error("could not parse result", ex); - - throw new InternalRepositoryException(repository, "could not parse result", ex); - } - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param extraEnv - * @param cmd - * @param args - * - * @return - * - * @throws IOException - */ - private Process createProcess(Map extraEnv, String cmd, - String... args) - throws IOException - { - HgConfig config = handler.getConfig(); - List cmdList = new ArrayList(); - - cmdList.add(cmd); - - if (Util.isNotEmpty(args)) - { - cmdList.addAll(Arrays.asList(args)); - } - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder("create process for ["); - Iterator it = cmdList.iterator(); - - while (it.hasNext()) - { - msg.append(it.next()); - - if (it.hasNext()) - { - msg.append(", "); - } - } - - msg.append("]"); - logger.debug(msg.toString()); - } - - ProcessBuilder pb = new ProcessBuilder(cmdList); - - pb.directory(repositoryDirectory); - - Map env = pb.environment(); - - // force utf-8 encoding for mercurial and python - env.put(ENV_PYTHONIOENCODING, ENCODING); - env.put(ENV_HGENCODING, ENCODING); - - //J- - env.put(ENV_ID_REVISION, - String.valueOf(handler.getConfig().isShowRevisionInId()) - ); - //J+ - - if (context.isSystemEnvironment()) - { - env.putAll(System.getenv()); - } - - if (context.isPending()) - { - if (logger.isDebugEnabled()) - { - logger.debug("enable hg pending for {}", - repositoryDirectory.getAbsolutePath()); - } - - env.put(ENV_PENDING, repositoryDirectory.getAbsolutePath()); - - if (extraEnv.containsKey(ENV_REVISION_START)) - { - env.put(ENV_NODE, extraEnv.get(ENV_REVISION_START)); - } - } - - env.put(ENV_PYTHONPATH, HgUtil.getPythonPath(config)); - env.put(ENV_REPOSITORY_PATH, repositoryDirectory.getAbsolutePath()); - env.putAll(extraEnv); - - if (logger.isTraceEnabled()) - { - StringBuilder msg = new StringBuilder("start process in directory '"); - - msg.append(repositoryDirectory.getAbsolutePath()).append( - "' with env: \n"); - - for (Map.Entry e : env.entrySet()) - { - msg.append(" ").append(e.getKey()); - msg.append(" = ").append(e.getValue()); - msg.append("\n"); - } - - logger.trace(msg.toString()); - } - - return pb.start(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - protected Repository repository; - - /** Field description */ - protected File repositoryDirectory; - - /** Field description */ - private HgContext context; - - /** Field description */ - private HgRepositoryHandler handler; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java new file mode 100644 index 0000000000..c4f37bda3b --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import sonia.scm.repository.hooks.HookEnvironment; +import sonia.scm.repository.hooks.HookServer; +import sonia.scm.security.AccessToken; +import sonia.scm.security.AccessTokenBuilderFactory; +import sonia.scm.security.CipherUtil; +import sonia.scm.web.HgUtil; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.Map; + +public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { + + @VisibleForTesting + static final String ENV_PYTHON_PATH = "PYTHONPATH"; + @VisibleForTesting + static final String ENV_HOOK_PORT = "SCM_HOOK_PORT"; + @VisibleForTesting + static final String ENV_CHALLENGE = "SCM_CHALLENGE"; + @VisibleForTesting + static final String ENV_BEARER_TOKEN = "SCM_BEARER_TOKEN"; + @VisibleForTesting + static final String ENV_REPOSITORY_NAME = "REPO_NAME"; + @VisibleForTesting + static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH"; + @VisibleForTesting + static final String ENV_REPOSITORY_ID = "SCM_REPOSITORY_ID"; + @VisibleForTesting + static final String ENV_HTTP_POST_ARGS = "SCM_HTTP_POST_ARGS"; + + private final AccessTokenBuilderFactory accessTokenBuilderFactory; + private final HgRepositoryHandler repositoryHandler; + private final HookEnvironment hookEnvironment; + private final HookServer server; + + private int hookPort = -1; + + @Inject + public DefaultHgEnvironmentBuilder( + AccessTokenBuilderFactory accessTokenBuilderFactory, HgRepositoryHandler repositoryHandler, + HookEnvironment hookEnvironment, HookServer server + ) { + this.accessTokenBuilderFactory = accessTokenBuilderFactory; + this.repositoryHandler = repositoryHandler; + this.hookEnvironment = hookEnvironment; + this.server = server; + } + + + @Override + public Map read(Repository repository) { + ImmutableMap.Builder env = ImmutableMap.builder(); + read(env, repository); + return env.build(); + } + + @Override + public Map write(Repository repository) { + ImmutableMap.Builder env = ImmutableMap.builder(); + read(env, repository); + write(env); + return env.build(); + } + + private void read(ImmutableMap.Builder env, Repository repository) { + HgConfig config = repositoryHandler.getConfig(); + env.put(ENV_PYTHON_PATH, HgUtil.getPythonPath(config)); + + File directory = repositoryHandler.getDirectory(repository.getId()); + + env.put(ENV_REPOSITORY_NAME, repository.getNamespace() + "/" + repository.getName()); + env.put(ENV_REPOSITORY_ID, repository.getId()); + env.put(ENV_REPOSITORY_PATH, directory.getAbsolutePath()); + + // enable experimental httppostargs protocol of mercurial + // Issue 970: https://goo.gl/poascp + env.put(ENV_HTTP_POST_ARGS, String.valueOf(config.isEnableHttpPostArgs())); + } + + private void write(ImmutableMap.Builder env) { + env.put(ENV_HOOK_PORT, String.valueOf(getHookPort())); + AccessToken accessToken = accessTokenBuilderFactory.create().build(); + env.put(ENV_BEARER_TOKEN, CipherUtil.getInstance().encode(accessToken.compact())); + env.put(ENV_CHALLENGE, hookEnvironment.getChallenge()); + } + + private synchronized int getHookPort() { + if (hookPort > 0) { + return hookPort; + } + try { + hookPort = server.start(); + } catch (IOException ex) { + throw new IllegalStateException("failed to start mercurial hook server"); + } + return hookPort; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java index daf3414a63..3189239dbd 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; @@ -36,20 +36,10 @@ import javax.xml.bind.annotation.XmlTransient; * @author Sebastian Sdorra */ @XmlRootElement(name = "config") -public class HgConfig extends RepositoryConfig -{ +public class HgConfig extends RepositoryConfig { public static final String PERMISSION = "hg"; - /** - * Constructs ... - * - */ - public HgConfig() {} - - //~--- get methods ---------------------------------------------------------- - - @Override @XmlTransient // Only for permission checks, don't serialize to XML public String getId() { @@ -123,10 +113,6 @@ public class HgConfig extends RepositoryConfig return useOptimizedBytecode; } - public boolean isDisableHookSSLValidation() { - return disableHookSSLValidation; - } - public boolean isEnableHttpPostArgs() { return enableHttpPostArgs; } @@ -216,10 +202,6 @@ public class HgConfig extends RepositoryConfig this.useOptimizedBytecode = useOptimizedBytecode; } - public void setDisableHookSSLValidation(boolean disableHookSSLValidation) { - this.disableHookSSLValidation = disableHookSSLValidation; - } - //~--- fields --------------------------------------------------------------- /** Field description */ @@ -242,9 +224,4 @@ public class HgConfig extends RepositoryConfig private boolean enableHttpPostArgs = false; - /** - * disable validation of ssl certificates for mercurial hook - * @see Issue 959 - */ - private boolean disableHookSSLValidation = false; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContext.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContext.java deleted file mode 100644 index 2791fff3c0..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContext.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -/** - * - * @author Sebastian Sdorra - */ -public class HgContext -{ - - /** - * Constructs ... - * - */ - public HgContext() {} - - /** - * Constructs ... - * - * - * @param pending - */ - public HgContext(boolean pending) - { - this.pending = pending; - } - - /** - * Constructs ... - * - * - * @param pending - * @param systemEnvironment - */ - public HgContext(boolean pending, boolean systemEnvironment) - { - this.pending = pending; - this.systemEnvironment = systemEnvironment; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public boolean isPending() - { - return pending; - } - - /** - * Method description - * - * - * @return - */ - public boolean isSystemEnvironment() - { - return systemEnvironment; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param pending - */ - public void setPending(boolean pending) - { - this.pending = pending; - } - - /** - * Method description - * - * - * @param systemEnvironment - */ - public void setSystemEnvironment(boolean systemEnvironment) - { - this.systemEnvironment = systemEnvironment; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private boolean pending = false; - - /** Field description */ - private boolean systemEnvironment = true; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextProvider.java deleted file mode 100644 index 6b09416db2..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - - -import com.google.common.annotations.VisibleForTesting; -import com.google.inject.OutOfScopeException; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Injection provider for {@link HgContext}. - * This provider returns an instance {@link HgContext} from request scope, if no {@link HgContext} could be found in - * request scope (mostly because the scope is not available) a new {@link HgContext} gets returned. - * - * @author Sebastian Sdorra - */ -public class HgContextProvider implements Provider -{ - - /** - * the LOG for HgContextProvider - */ - private static final Logger LOG = - LoggerFactory.getLogger(HgContextProvider.class); - - //~--- get methods ---------------------------------------------------------- - - private Provider requestStoreProvider; - - @Inject - public HgContextProvider(Provider requestStoreProvider) { - this.requestStoreProvider = requestStoreProvider; - } - - @VisibleForTesting - public HgContextProvider() { - } - - @Override - public HgContext get() { - HgContext context = fetchContextFromRequest(); - if (context != null) { - LOG.trace("return HgContext from request store"); - return context; - } - LOG.trace("could not find context in request scope, returning new instance"); - return new HgContext(); - } - - private HgContext fetchContextFromRequest() { - try { - if (requestStoreProvider != null) { - return requestStoreProvider.get().get(); - } else { - LOG.trace("no request store provider defined, could not return context from request"); - return null; - } - } catch (ProvisionException ex) { - if (ex.getCause() instanceof OutOfScopeException) { - LOG.trace("we are currently out of request scope, failed to retrieve context"); - return null; - } else { - throw ex; - } - } - } -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironment.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironment.java deleted file mode 100644 index a997bb2069..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironment.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.ProvisionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.security.AccessToken; -import sonia.scm.security.CipherUtil; -import sonia.scm.security.Xsrf; -import sonia.scm.web.HgUtil; - -import javax.servlet.http.HttpServletRequest; -import java.util.Map; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * - * @author Sebastian Sdorra - */ -public final class HgEnvironment -{ - - private static final Logger LOG = LoggerFactory.getLogger(HgEnvironment.class); - - /** Field description */ - public static final String ENV_PYTHON_PATH = "PYTHONPATH"; - - /** Field description */ - private static final String ENV_CHALLENGE = "SCM_CHALLENGE"; - - /** Field description */ - private static final String ENV_URL = "SCM_URL"; - - private static final String SCM_BEARER_TOKEN = "SCM_BEARER_TOKEN"; - - private static final String SCM_XSRF = "SCM_XSRF"; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - */ - private HgEnvironment() {} - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param environment - * @param handler - * @param hookManager - */ - public static void prepareEnvironment(Map environment, - HgRepositoryHandler handler, HgHookManager hookManager) - { - prepareEnvironment(environment, handler, hookManager, null); - } - - /** - * Method description - * - * - * @param environment - * @param handler - * @param hookManager - * @param request - */ - public static void prepareEnvironment(Map environment, - HgRepositoryHandler handler, HgHookManager hookManager, - HttpServletRequest request) - { - String hookUrl; - - if (request != null) - { - hookUrl = hookManager.createUrl(request); - } - else - { - hookUrl = hookManager.createUrl(); - } - - try { - AccessToken accessToken = hookManager.getAccessToken(); - environment.put(SCM_BEARER_TOKEN, CipherUtil.getInstance().encode(accessToken.compact())); - extractXsrfKey(environment, accessToken); - } catch (ProvisionException e) { - LOG.debug("could not create bearer token; looks like currently we are not in a request; probably you can ignore the following exception:", e); - } - environment.put(ENV_PYTHON_PATH, HgUtil.getPythonPath(handler.getConfig())); - environment.put(ENV_URL, hookUrl); - environment.put(ENV_CHALLENGE, hookManager.getChallenge()); - } - - private static void extractXsrfKey(Map environment, AccessToken accessToken) { - environment.put(SCM_XSRF, accessToken.getCustom(Xsrf.TOKEN_KEY).orElse("-")); - } -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextRequestStore.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironmentBuilder.java similarity index 59% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextRequestStore.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironmentBuilder.java index 3fa04bd1e6..3738d92d88 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgContextRequestStore.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgEnvironmentBuilder.java @@ -21,28 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; -import com.google.inject.servlet.RequestScoped; +import com.google.inject.ImplementedBy; -/** - * Holds an instance of {@link HgContext} in the request scope. - * - *

The problem seems to be that guice had multiple options for injecting HgContext. {@link HgContextProvider} - * bound via Module and {@link HgContext} bound void {@link RequestScoped} annotation. It looks like that Guice 4 - * injects randomly the one or the other, in SCMv1 (Guice 3) everything works as expected.

- * - *

To fix the problem we have created this class annotated with {@link RequestScoped}, which holds an instance - * of {@link HgContext}. This way only the {@link HgContextProvider} is used for injection.

- */ -@RequestScoped -public class HgContextRequestStore { - - private final HgContext context = new HgContext(); - - public HgContext get() { - return context; - } +import java.util.Map; +@ImplementedBy(DefaultHgEnvironmentBuilder.class) +public interface HgEnvironmentBuilder { + Map read(Repository repository); + Map write(Repository repository); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java deleted file mode 100644 index 3d333015ee..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.github.legman.Subscribe; -import com.google.common.base.MoreObjects; -import com.google.inject.Inject; -import com.google.inject.OutOfScopeException; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; -import com.google.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.config.ScmConfigurationChangedEvent; -import sonia.scm.net.ahc.AdvancedHttpClient; -import sonia.scm.security.AccessToken; -import sonia.scm.security.AccessTokenBuilderFactory; -import sonia.scm.util.HttpUtil; -import sonia.scm.util.Util; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.UUID; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -public class HgHookManager { - - @SuppressWarnings("java:S1075") // this url is fixed - private static final String URL_HOOKPATH = "/hook/hg/"; - - /** - * the logger for HgHookManager - */ - private static final Logger logger = - LoggerFactory.getLogger(HgHookManager.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * @param configuration - * @param httpServletRequestProvider - * @param httpClient - * @param accessTokenBuilderFactory - */ - @Inject - public HgHookManager(ScmConfiguration configuration, - Provider httpServletRequestProvider, - AdvancedHttpClient httpClient, AccessTokenBuilderFactory accessTokenBuilderFactory) - { - this.configuration = configuration; - this.httpServletRequestProvider = httpServletRequestProvider; - this.httpClient = httpClient; - this.accessTokenBuilderFactory = accessTokenBuilderFactory; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param config - */ - @Subscribe(async = false) - public void configChanged(ScmConfigurationChangedEvent config) - { - hookUrl = null; - } - - /** - * Method description - * - * - * @param request - * - * @return - */ - public String createUrl(HttpServletRequest request) - { - if (hookUrl == null) - { - synchronized (this) - { - if (hookUrl == null) - { - buildHookUrl(request); - - if (logger.isInfoEnabled() && Util.isNotEmpty(hookUrl)) - { - logger.info("use {} for mercurial hooks", hookUrl); - } - } - } - } - - return hookUrl; - } - - /** - * Method description - * - * - * @return - */ - public String createUrl() - { - String url = hookUrl; - - if (url == null) - { - HttpServletRequest request = getHttpServletRequest(); - - if (request != null) - { - url = createUrl(request); - } - else - { - url = createConfiguredUrl(); - logger.warn( - "created url {} without request, in some cases this could cause problems", - url); - } - } - - return url; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public String getChallenge() - { - return challenge; - } - - /** - * Method description - * - * - * @param challenge - * - * @return - */ - public boolean isAcceptAble(String challenge) - { - return this.challenge.equals(challenge); - } - - public AccessToken getAccessToken() - { - return accessTokenBuilderFactory.create().build(); - } - - private void buildHookUrl(HttpServletRequest request) { - if (configuration.isForceBaseUrl()) { - logger.debug("create hook url from configured base url because force base url is enabled"); - - hookUrl = createConfiguredUrl(); - if (!isUrlWorking(hookUrl)) { - disableHooks(); - } - } else { - logger.debug("create hook url from request"); - - hookUrl = HttpUtil.getCompleteUrl(request, URL_HOOKPATH); - if (!isUrlWorking(hookUrl)) { - logger.warn("hook url {} from request does not work, try now localhost", hookUrl); - - hookUrl = createLocalUrl(request); - if (!isUrlWorking(hookUrl)) { - logger.warn("localhost hook url {} does not work, try now from configured base url", hookUrl); - - hookUrl = createConfiguredUrl(); - if (!isUrlWorking(hookUrl)) { - disableHooks(); - } - } - } - } - } - - /** - * Method description - * - * - * @return - */ - private String createConfiguredUrl() - { - //J- - return HttpUtil.getUriWithoutEndSeperator( - MoreObjects.firstNonNull( - configuration.getBaseUrl(), - "http://localhost:8080/scm" - ) - ).concat(URL_HOOKPATH); - //J+ - } - - /** - * Method description - * - * - * @param request - * - * @return - */ - private String createLocalUrl(HttpServletRequest request) - { - StringBuilder sb = new StringBuilder(request.getScheme()); - - sb.append("://localhost:").append(request.getLocalPort()); - sb.append(request.getContextPath()).append(URL_HOOKPATH); - - return sb.toString(); - } - - /** - * Method description - * - */ - private void disableHooks() - { - if (logger.isErrorEnabled()) - { - logger.error( - "disabling mercurial hooks, because hook url {} seems not to work", - hookUrl); - } - - hookUrl = Util.EMPTY_STRING; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - private HttpServletRequest getHttpServletRequest() - { - HttpServletRequest request = null; - - try - { - request = httpServletRequestProvider.get(); - } - catch (ProvisionException | OutOfScopeException ex) - { - logger.debug("http servlet request is not available"); - } - - return request; - } - - /** - * Method description - * - * - * @param url - * - * @return - */ - private boolean isUrlWorking(String url) - { - boolean result = false; - - try - { - url = url.concat("?ping=true"); - - logger.trace("check hook url {}", url); - //J- - int sc = httpClient.get(url) - .disableHostnameValidation(true) - .disableCertificateValidation(true) - .ignoreProxySettings(true) - .disableTracing() - .request() - .getStatus(); - //J+ - result = sc == 204; - } - catch (IOException ex) - { - if (logger.isTraceEnabled()) - { - logger.trace("url test failed for url ".concat(url), ex); - } - } - - return result; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String challenge = UUID.randomUUID().toString(); - - /** Field description */ - private ScmConfiguration configuration; - - /** Field description */ - private volatile String hookUrl; - - /** Field description */ - private AdvancedHttpClient httpClient; - - /** Field description */ - private Provider httpServletRequestProvider; - - private final AccessTokenBuilderFactory accessTokenBuilderFactory; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java new file mode 100644 index 0000000000..5b17ad8bd4 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java @@ -0,0 +1,106 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import com.aragost.javahg.RepositoryConfiguration; +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.hooks.HookEnvironment; +import sonia.scm.repository.spi.javahg.HgFileviewExtension; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.File; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Map; +import java.util.function.Function; + +@Singleton +public class HgRepositoryFactory { + + private static final Logger LOG = LoggerFactory.getLogger(HgRepositoryFactory.class); + + private final HgRepositoryHandler handler; + private final HookEnvironment hookEnvironment; + private final HgEnvironmentBuilder environmentBuilder; + private final Function directoryResolver; + + @Inject + public HgRepositoryFactory(HgRepositoryHandler handler, HookEnvironment hookEnvironment, HgEnvironmentBuilder environmentBuilder) { + this( + handler, hookEnvironment, environmentBuilder, + repository -> handler.getDirectory(repository.getId()) + ); + } + + @VisibleForTesting + public HgRepositoryFactory(HgRepositoryHandler handler, HookEnvironment hookEnvironment, HgEnvironmentBuilder environmentBuilder, Function directoryResolver) { + this.handler = handler; + this.hookEnvironment = hookEnvironment; + this.environmentBuilder = environmentBuilder; + this.directoryResolver = directoryResolver; + } + + public com.aragost.javahg.Repository openForRead(Repository repository) { + return open(repository, environmentBuilder.read(repository)); + } + + public com.aragost.javahg.Repository openForWrite(Repository repository) { + return open(repository, environmentBuilder.write(repository)); + } + + private com.aragost.javahg.Repository open(Repository repository, Map environment) { + File directory = directoryResolver.apply(repository); + + RepositoryConfiguration repoConfiguration = RepositoryConfiguration.DEFAULT; + repoConfiguration.getEnvironment().putAll(environment); + repoConfiguration.addExtension(HgFileviewExtension.class); + + boolean pending = hookEnvironment.isPending(); + repoConfiguration.setEnablePendingChangesets(pending); + + Charset encoding = encoding(); + repoConfiguration.setEncoding(encoding); + + repoConfiguration.setHgBin(handler.getConfig().getHgBinary()); + + LOG.trace("open hg repository {}: encoding: {}, pending: {}", directory, encoding, pending); + + return com.aragost.javahg.Repository.open(repoConfiguration, directory); + } + + private Charset encoding() { + String charset = handler.getConfig().getEncoding(); + try { + return Charset.forName(charset); + } catch (UnsupportedCharsetException ex) { + LOG.warn("unknown charset {} in hg config, fallback to utf-8", charset); + return StandardCharsets.UTF_8; + } + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index bdc36eb54b..14cd54866d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -27,11 +27,9 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.Inject; -import com.google.inject.Provider; import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.ConfigurationException; import sonia.scm.SCMContextProvider; import sonia.scm.autoconfig.AutoConfigurator; import sonia.scm.installer.HgInstaller; @@ -43,14 +41,14 @@ import sonia.scm.io.INISection; import sonia.scm.plugin.Extension; import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HgRepositoryServiceProvider; +import sonia.scm.repository.spi.HgVersionCommand; import sonia.scm.repository.spi.HgWorkingCopyFactory; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.IOUtil; import sonia.scm.util.SystemUtil; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -63,14 +61,15 @@ import java.util.Optional; public class HgRepositoryHandler extends AbstractSimpleRepositoryHandler { - public static final String PATH_HOOK = ".hook-1.8"; public static final String RESOURCE_VERSION = "sonia/scm/version/scm-hg-plugin"; public static final String TYPE_DISPLAYNAME = "Mercurial"; public static final String TYPE_NAME = "hg"; - public static final RepositoryType TYPE = new RepositoryType(TYPE_NAME, + public static final RepositoryType TYPE = new RepositoryType( + TYPE_NAME, TYPE_DISPLAYNAME, HgRepositoryServiceProvider.COMMANDS, - HgRepositoryServiceProvider.FEATURES); + HgRepositoryServiceProvider.FEATURES + ); private static final Logger logger = LoggerFactory.getLogger(HgRepositoryHandler.class); @@ -78,28 +77,14 @@ public class HgRepositoryHandler private static final String CONFIG_SECTION_SCMM = "scmm"; private static final String CONFIG_KEY_REPOSITORY_ID = "repositoryid"; - private final Provider hgContextProvider; - private final HgWorkingCopyFactory workingCopyFactory; - private final JAXBContext jaxbContext; - @Inject public HgRepositoryHandler(ConfigurationStoreFactory storeFactory, - Provider hgContextProvider, RepositoryLocationResolver repositoryLocationResolver, PluginLoader pluginLoader, HgWorkingCopyFactory workingCopyFactory) { super(storeFactory, repositoryLocationResolver, pluginLoader); - this.hgContextProvider = hgContextProvider; this.workingCopyFactory = workingCopyFactory; - - try { - this.jaxbContext = JAXBContext.newInstance(BrowserResult.class, - BlameResult.class, Changeset.class, ChangesetPagingResult.class, - HgVersion.class); - } catch (JAXBException ex) { - throw new ConfigurationException("could not create jaxbcontext", ex); - } } public void doAutoConfiguration(HgConfig autoConfig) { @@ -107,8 +92,7 @@ public class HgRepositoryHandler try { if (logger.isDebugEnabled()) { - logger.debug("installing mercurial with {}", - installer.getClass().getName()); + logger.debug("installing mercurial with {}", installer.getClass().getName()); } installer.install(baseDirectory, autoConfig); @@ -154,16 +138,6 @@ public class HgRepositoryHandler } } - public HgContext getHgContext() { - HgContext context = hgContextProvider.get(); - - if (context == null) { - context = new HgContext(); - } - - return context; - } - @Override public ImportHandler getImportHandler() { return new HgImportHandler(this); @@ -176,28 +150,14 @@ public class HgRepositoryHandler @Override public String getVersionInformation() { - String version = getStringFromResource(RESOURCE_VERSION, - DEFAULT_VERSION_INFORMATION); + return getVersionInformation(new HgVersionCommand(getConfig())); + } - try { - HgVersion hgVersion = new HgVersionHandler(this, hgContextProvider.get(), - baseDirectory).getVersion(); - - if (hgVersion != null) { - if (logger.isDebugEnabled()) { - logger.debug("mercurial/python informations: {}", hgVersion); - } - - version = MessageFormat.format(version, hgVersion.getPython(), - hgVersion.getMercurial()); - } else if (logger.isWarnEnabled()) { - logger.warn("could not retrieve version informations"); - } - } catch (Exception ex) { - logger.error("could not read version informations", ex); - } - - return version; + String getVersionInformation(HgVersionCommand command) { + String version = getStringFromResource(RESOURCE_VERSION, DEFAULT_VERSION_INFORMATION); + HgVersion hgVersion = command.get(); + logger.debug("mercurial/python informations: {}", hgVersion); + return MessageFormat.format(version, hgVersion.getPython(), hgVersion.getMercurial()); } @Override @@ -253,28 +213,24 @@ public class HgRepositoryHandler logger.debug("write python script {}", script.getName()); } - InputStream content = null; - OutputStream output = null; - - try { - content = HgRepositoryHandler.class.getResourceAsStream( - script.getResourcePath()); - output = new FileOutputStream(script.getFile(context)); + try (InputStream content = input(script); OutputStream output = output(context, script)) { IOUtil.copy(content, output); } catch (IOException ex) { logger.error("could not write script", ex); - } finally { - IOUtil.close(content); - IOUtil.close(output); } } } + private InputStream input(HgPythonScript script) { + return HgRepositoryHandler.class.getResourceAsStream(script.getResourcePath()); + } + + private OutputStream output(SCMContextProvider context, HgPythonScript script) throws FileNotFoundException { + return new FileOutputStream(script.getFile(context)); + } + public HgWorkingCopyFactory getWorkingCopyFactory() { return workingCopyFactory; } - public JAXBContext getJaxbContext() { - return jaxbContext; - } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersion.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersion.java index 36bd6abfa3..a517c2fe08 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersion.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersion.java @@ -24,10 +24,8 @@ package sonia.scm.repository; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import lombok.AllArgsConstructor; +import lombok.Data; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -37,13 +35,14 @@ import javax.xml.bind.annotation.XmlRootElement; * * @author Sebastian Sdorra */ +@Data +@AllArgsConstructor @XmlRootElement(name = "version") @XmlAccessorType(XmlAccessType.FIELD) -@EqualsAndHashCode -@Getter -@Setter -@ToString public class HgVersion { + + public static final String UNKNOWN = "x.y.z (unknown)"; + private String mercurial; private String python; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookMessage.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookMessage.java index ea1671d7ab..0cd78ac932 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookMessage.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookMessage.java @@ -21,75 +21,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.api; //~--- JDK imports ------------------------------------------------------------ +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + import java.io.Serializable; /** * * @author Sebastian Sdorra */ -public final class HgHookMessage implements Serializable -{ +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public final class HgHookMessage implements Serializable { - /** Field description */ private static final long serialVersionUID = 1804492842452344326L; - //~--- constant enums ------------------------------------------------------- - - /** - * Enum description - * - */ - public static enum Severity { NOTE, ERROR; } - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param severity - * @param message - */ - public HgHookMessage(Severity severity, String message) - { - this.severity = severity; - this.message = message; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public String getMessage() - { - return message; - } - - /** - * Method description - * - * - * @return - */ - public Severity getSeverity() - { - return severity; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ + private Severity severity; private String message; - /** Field description */ - private Severity severity; + public enum Severity { NOTE, ERROR } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java similarity index 81% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index e2398b4498..f4d8ba2a08 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HgHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -24,6 +24,7 @@ package sonia.scm.repository.hooks; +import com.google.inject.assistedinject.Assisted; import lombok.AllArgsConstructor; import lombok.Data; import org.apache.shiro.SecurityUtils; @@ -31,7 +32,7 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; import sonia.scm.repository.spi.HgHookContextProvider; @@ -39,9 +40,7 @@ import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.security.BearerToken; import sonia.scm.security.CipherUtil; -import javax.annotation.Nonnull; -import javax.inject.Provider; -import java.io.File; +import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -50,19 +49,20 @@ import java.util.List; import static java.util.Collections.singletonList; -class HgHookHandler implements Runnable { +class DefaultHookHandler implements HookHandler { - private static final Logger LOG = LoggerFactory.getLogger(HgHookHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(DefaultHookHandler.class); - private final HgRepositoryHandler handler; private final HookEventFacade hookEventFacade; - private final Provider environmentProvider; + private final HookEnvironment environment; + private final HookContextProviderFactory hookContextProviderFactory; private final Socket socket; - HgHookHandler(HgRepositoryHandler handler, HookEventFacade hookEventFacade, Provider environmentProvider, Socket socket) { - this.handler = handler; + @Inject + public DefaultHookHandler(HookContextProviderFactory hookContextProviderFactory, HookEventFacade hookEventFacade, HookEnvironment environment, @Assisted Socket socket) { + this.hookContextProviderFactory = hookContextProviderFactory; this.hookEventFacade = hookEventFacade; - this.environmentProvider = environmentProvider; + this.environment = environment; this.socket = socket; } @@ -84,7 +84,6 @@ class HgHookHandler implements Runnable { } private Response handleHookRequest(Request request) { - HookEnvironment environment = environmentProvider.get(); try { if (!environment.isAcceptAble(request.getChallenge())) { return error("invalid hook challenge"); @@ -93,13 +92,16 @@ class HgHookHandler implements Runnable { authenticate(request); environment.setPending(request.getType() == RepositoryHookType.PRE_RECEIVE); - HgHookContextProvider context = createHookContextProvider(request); + HgHookContextProvider context = hookContextProviderFactory.create(request.getRepositoryId(), request.getNode()); hookEventFacade.handle(request.getRepositoryId()).fireHookEvent(request.getType(), context); return new Response(context.getHgMessageProvider().getMessages(), false); } catch (AuthenticationException ex) { LOG.warn("hook authentication failed", ex); return error("hook authentication failed"); + } catch (NotFoundException ex) { + LOG.warn("could not find repository with id {}", request.getRepositoryId(), ex); + return error("repository not found"); } catch (Exception ex) { LOG.warn("unknown error on hook occurred", ex); return error("unknown error"); @@ -108,14 +110,6 @@ class HgHookHandler implements Runnable { } } - @Nonnull - private HgHookContextProvider createHookContextProvider(Request request) { - File repositoryDirectory = handler.getDirectory(request.getRepositoryId()); - return new HgHookContextProvider( - handler, repositoryDirectory, null, request.node, request.type - ); - } - private void authenticate(Request request) { String token = CipherUtil.getInstance().decode(request.getToken()); BearerToken bearer = BearerToken.valueOf(token); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/AbstractHgCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java similarity index 52% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/AbstractHgCommand.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java index 259996e2ed..04f88189ce 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/AbstractHgCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java @@ -21,60 +21,37 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -package sonia.scm.repository.spi; -//~--- non-JDK imports -------------------------------------------------------- +package sonia.scm.repository.hooks; -import sonia.scm.repository.AbstractHgHandler; -import sonia.scm.repository.HgContext; +import sonia.scm.NotFoundException; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.spi.HgHookContextProvider; -//~--- JDK imports ------------------------------------------------------------ +import javax.inject.Inject; -import java.io.File; +public class HookContextProviderFactory { -import java.util.Map; + private final RepositoryManager repositoryManager; + private final HgRepositoryHandler repositoryHandler; + private final HgRepositoryFactory repositoryFactory; -/** - * - * @author Sebastian Sdorra - */ -public class AbstractHgCommand extends AbstractHgHandler -{ - - /** - * Constructs ... - * - * - * @param handler - * @param context - * @param repository - * @param repositoryDirectory - */ - protected AbstractHgCommand(HgRepositoryHandler handler, HgContext context, - Repository repository, File repositoryDirectory) - { - super(handler, context, repository, repositoryDirectory); + @Inject + public HookContextProviderFactory(RepositoryManager repositoryManager, HgRepositoryHandler repositoryHandler, HgRepositoryFactory repositoryFactory) { + this.repositoryManager = repositoryManager; + this.repositoryHandler = repositoryHandler; + this.repositoryFactory = repositoryFactory; } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param revision - * @param path - * - * @param request - * - * @return - */ - protected Map createEnvironment(FileBaseCommandRequest request) - { - return createEnvironment(request.getRevision(), request.getPath()); + HgHookContextProvider create(String repositoryId, String node) { + Repository repository = repositoryManager.get(repositoryId); + if (repository == null) { + throw new NotFoundException(Repository.class, repositoryId); + } + return new HgHookContextProvider(repositoryHandler, repositoryFactory, repository, node); } + } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandler.java new file mode 100644 index 0000000000..f2f7e7ccdd --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandler.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +public interface HookHandler extends Runnable { +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java index 61d3a1340d..4eaf205bdd 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookHandlerFactory.java @@ -24,14 +24,11 @@ package sonia.scm.repository.hooks; -import com.google.inject.ImplementedBy; - import java.net.Socket; @FunctionalInterface -@ImplementedBy(DefaultHookHandlerFactory.class) interface HookHandlerFactory { - Runnable create(Socket socket); + HookHandler create(Socket socket); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookModule.java similarity index 60% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookModule.java index 93f0915486..2eed226a09 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandlerFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookModule.java @@ -24,28 +24,17 @@ package sonia.scm.repository.hooks; -import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.spi.HookEventFacade; - -import javax.inject.Inject; -import javax.inject.Provider; -import java.net.Socket; - -public class DefaultHookHandlerFactory implements HookHandlerFactory { - - private final HgRepositoryHandler handler; - private final HookEventFacade hookEventFacade; - private final Provider hookEnvironment; - - @Inject - public DefaultHookHandlerFactory(HgRepositoryHandler handler, HookEventFacade hookEventFacade, Provider hookEnvironment) { - this.handler = handler; - this.hookEventFacade = hookEventFacade; - this.hookEnvironment = hookEnvironment; - } +import com.google.inject.AbstractModule; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import sonia.scm.plugin.Extension; +@Extension +public class HookModule extends AbstractModule { @Override - public HgHookHandler create(Socket socket) { - return new HgHookHandler(handler, hookEventFacade, hookEnvironment, socket); + protected void configure() { + install(new FactoryModuleBuilder() + .implement(HookHandler.class, DefaultHookHandler.class) + .build(HookHandlerFactory.class) + ); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java index 0ecbe180c4..40fb4a68e3 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java @@ -25,7 +25,9 @@ package sonia.scm.repository.hooks; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.apache.shiro.concurrent.SubjectAwareExecutorService; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.util.ThreadContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +40,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; @Singleton public class HookServer implements AutoCloseable { @@ -56,18 +59,24 @@ public class HookServer implements AutoCloseable { } private ExecutorService createAcceptor() { - return new SubjectAwareExecutorService(Executors.newSingleThreadExecutor( - new ThreadFactoryBuilder().setNameFormat("HgHookAcceptor").build() - )); + return Executors.newSingleThreadExecutor( + createThreadFactory("HgHookAcceptor") + ); } private ExecutorService createWorkerPool() { - return new SubjectAwareExecutorService(Executors.newCachedThreadPool( - new ThreadFactoryBuilder().setNameFormat("HgHookWorker-%d").build() - ) + return Executors.newCachedThreadPool( + createThreadFactory("HgHookWorker-%d") ); } + @Nonnull + private ThreadFactory createThreadFactory(String hgHookAcceptor) { + return new ThreadFactoryBuilder() + .setNameFormat(hgHookAcceptor) + .build(); + } + public int start() throws IOException { acceptor = createAcceptor(); workerPool = createWorkerPool(); @@ -83,12 +92,14 @@ public class HookServer implements AutoCloseable { } private void accept() { + SecurityManager securityManager = SecurityUtils.getSecurityManager(); acceptor.submit(() -> { while (!serverSocket.isClosed()) { try { Socket clientSocket = serverSocket.accept(); LOG.trace("accept incoming hook client from {}", clientSocket.getInetAddress()); - workerPool.submit(handlerFactory.create(clientSocket)); + HookHandler hookHandler = handlerFactory.create(clientSocket); + workerPool.submit(associateSecurityManager(securityManager, hookHandler)); } catch (IOException ex) { LOG.debug("failed to accept socket, possible closed", ex); } @@ -96,6 +107,18 @@ public class HookServer implements AutoCloseable { }); } + private Runnable associateSecurityManager(SecurityManager securityManager, HookHandler hookHandler) { + return () -> { + ThreadContext.bind(securityManager); + try { + hookHandler.run(); + } finally { + ThreadContext.unbindSubject(); + ThreadContext.unbindSecurityManager(); + } + }; + } + @Nonnull private ServerSocket createServerSocket() throws IOException { return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java index 39181d3d67..5169d3c389 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java @@ -27,18 +27,12 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.aragost.javahg.Repository; -import com.google.common.base.Strings; import sonia.scm.repository.HgConfig; -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryProvider; -import sonia.scm.web.HgUtil; import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.function.BiConsumer; //~--- JDK imports ------------------------------------------------------------ @@ -46,105 +40,32 @@ import java.util.function.BiConsumer; * * @author Sebastian Sdorra */ -public class HgCommandContext implements Closeable, RepositoryProvider -{ +public class HgCommandContext implements Closeable, RepositoryProvider { - /** Field description */ - private static final String PROPERTY_ENCODING = "hg.encoding"; + private final HgRepositoryHandler handler; + private final HgRepositoryFactory factory; + private final sonia.scm.repository.Repository scmRepository; - //~--- constructors --------------------------------------------------------- + private Repository repository; - /** - * Constructs ... - * - * - * @param hookManager - * @param handler - * @param repository - * @param directory - */ - public HgCommandContext(HgHookManager hookManager, - HgRepositoryHandler handler, sonia.scm.repository.Repository repository, - File directory) - { - this(hookManager, handler, repository, directory, - handler.getHgContext().isPending()); - } - - /** - * Constructs ... - * - * - * @param hookManager - * @param handler - * @param repository - * @param directory - * @param pending - */ - public HgCommandContext(HgHookManager hookManager, - HgRepositoryHandler handler, sonia.scm.repository.Repository repository, - File directory, boolean pending) - { - this.hookManager = hookManager; + public HgCommandContext(HgRepositoryHandler handler, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository) { this.handler = handler; - this.directory = directory; - this.scmRepository = repository; - this.encoding = repository.getProperty(PROPERTY_ENCODING); - this.pending = pending; - - if (Strings.isNullOrEmpty(encoding)) - { - encoding = handler.getConfig().getEncoding(); - } + this.factory = factory; + this.scmRepository = scmRepository; } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @throws IOException - */ - @Override - public void close() throws IOException - { - if (repository != null) - { - repository.close(); + public Repository open() { + if (repository == null) { + repository = factory.openForRead(scmRepository); } - } - - /** - * Method description - * - * - * @return - */ - public Repository open() - { - if (repository == null) - { - repository = HgUtil.open(handler, hookManager, directory, encoding, pending); - } - return repository; } - public Repository openWithSpecialEnvironment(BiConsumer> prepareEnvironment) - { - return HgUtil.open(handler, directory, encoding, - pending, environment -> prepareEnvironment.accept(scmRepository, environment)); + public Repository openForWrite() { + return factory.openForWrite(scmRepository); } - //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ public HgConfig getConfig() { return handler.getConfig(); @@ -159,25 +80,12 @@ public class HgCommandContext implements Closeable, RepositoryProvider return getScmRepository(); } - //~--- fields --------------------------------------------------------------- - /** Field description */ - private File directory; + @Override + public void close() { + if (repository != null) { + repository.close(); + } + } - /** Field description */ - private String encoding; - - /** Field description */ - private HgRepositoryHandler handler; - - /** Field description */ - private HgHookManager hookManager; - - /** Field description */ - private boolean pending; - - /** Field description */ - private Repository repository; - - private final sonia.scm.repository.Repository scmRepository; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java index 10fe43bcf7..98bddd8c7c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java @@ -21,85 +21,56 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -package sonia.scm.repository.spi; -//~--- non-JDK imports -------------------------------------------------------- +package sonia.scm.repository.spi; import com.aragost.javahg.Repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.spi.javahg.HgLogChangesetCommand; import sonia.scm.web.HgUtil; -import java.io.File; - -//~--- JDK imports ------------------------------------------------------------ - /** * * @author Sebastian Sdorra */ -public class HgHookChangesetProvider implements HookChangesetProvider -{ +public class HgHookChangesetProvider implements HookChangesetProvider { - /** - * the logger for HgHookChangesetProvider - */ - private static final Logger logger = - LoggerFactory.getLogger(HgHookChangesetProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(HgHookChangesetProvider.class); - //~--- constructors --------------------------------------------------------- + private final HgRepositoryHandler handler; + private final HgRepositoryFactory factory; + private final sonia.scm.repository.Repository scmRepository; + private final String startRev; - public HgHookChangesetProvider(HgRepositoryHandler handler, - File repositoryDirectory, HgHookManager hookManager, String startRev, - RepositoryHookType type) - { + private HookChangesetResponse response; + + public HgHookChangesetProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository, String startRev) { this.handler = handler; - this.repositoryDirectory = repositoryDirectory; - this.hookManager = hookManager; + this.factory = factory; + this.scmRepository = scmRepository; this.startRev = startRev; - this.type = type; } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param request - * - * @return - */ @Override - public synchronized HookChangesetResponse handleRequest(HookChangesetRequest request) - { - if (response == null) - { + public synchronized HookChangesetResponse handleRequest(HookChangesetRequest request) { + if (response == null) { Repository repository = null; - try - { - repository = open(); + try { + repository = factory.openForRead(scmRepository); - HgLogChangesetCommand cmd = HgLogChangesetCommand.on(repository, - handler.getConfig()); + HgLogChangesetCommand cmd = HgLogChangesetCommand.on(repository, handler.getConfig()); response = new HookChangesetResponse( - cmd.rev(startRev.concat(":").concat(HgUtil.REVISION_TIP)).execute()); - } - catch (Exception ex) - { - logger.error("could not retrieve changesets", ex); - } - finally - { - if (repository != null) - { + cmd.rev(startRev.concat(":").concat(HgUtil.REVISION_TIP)).execute() + ); + } catch (Exception ex) { + LOG.error("could not retrieve changesets", ex); + } finally { + if (repository != null) { repository.close(); } } @@ -108,39 +79,4 @@ public class HgHookChangesetProvider implements HookChangesetProvider return response; } - /** - * Method description - * - * - * @return - */ - private Repository open() - { - // use HG_PENDING only for pre receive hooks - boolean pending = type == RepositoryHookType.PRE_RECEIVE; - - // TODO get repository encoding - return HgUtil.open(handler, hookManager, repositoryDirectory, null, - pending); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private HgRepositoryHandler handler; - - /** Field description */ - private HgHookManager hookManager; - - /** Field description */ - private File repositoryDirectory; - - /** Field description */ - private HookChangesetResponse response; - - /** Field description */ - private String startRev; - - /** Field description */ - private RepositoryHookType type; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java index 1f72d56e24..0282729baa 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.RepositoryHookType; +import sonia.scm.repository.Repository; import sonia.scm.repository.api.HgHookBranchProvider; import sonia.scm.repository.api.HgHookMessageProvider; import sonia.scm.repository.api.HgHookTagProvider; @@ -37,7 +37,6 @@ import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.api.HookMessageProvider; import sonia.scm.repository.api.HookTagProvider; -import java.io.File; import java.util.EnumSet; import java.util.Set; @@ -45,55 +44,40 @@ import java.util.Set; /** * Mercurial implementation of {@link HookContextProvider}. - * + * * @author Sebastian Sdorra */ -public class HgHookContextProvider extends HookContextProvider -{ +public class HgHookContextProvider extends HookContextProvider { - private static final Set SUPPORTED_FEATURES = - EnumSet.of(HookFeature.CHANGESET_PROVIDER, HookFeature.MESSAGE_PROVIDER, - HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER); + private static final Set SUPPORTED_FEATURES = EnumSet.of( + HookFeature.CHANGESET_PROVIDER, + HookFeature.MESSAGE_PROVIDER, + HookFeature.BRANCH_PROVIDER, + HookFeature.TAG_PROVIDER + ); - //~--- constructors --------------------------------------------------------- + private final HgHookChangesetProvider hookChangesetProvider; + private HgHookMessageProvider hgMessageProvider; + private HgHookBranchProvider hookBranchProvider; + private HgHookTagProvider hookTagProvider; - /** - * Constructs a new instance. - * - * @param handler mercurial repository handler - * @param repositoryDirectory the directory of the changed repository - * @param hookManager mercurial hook manager - * @param startRev start revision - * @param type type of hook - */ - public HgHookContextProvider(HgRepositoryHandler handler, - File repositoryDirectory, HgHookManager hookManager, String startRev, - RepositoryHookType type) - { - this.hookChangesetProvider = new HgHookChangesetProvider(handler, repositoryDirectory, hookManager, startRev, type); + public HgHookContextProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, Repository repository, String startRev) { + this.hookChangesetProvider = new HgHookChangesetProvider(handler, factory, repository, startRev); } - //~--- get methods ---------------------------------------------------------- - @Override - public HookBranchProvider getBranchProvider() - { - if (hookBranchProvider == null) - { + public HookBranchProvider getBranchProvider() { + if (hookBranchProvider == null) { hookBranchProvider = new HgHookBranchProvider(hookChangesetProvider); } - return hookBranchProvider; } @Override - public HookTagProvider getTagProvider() - { - if (hookTagProvider == null) - { + public HookTagProvider getTagProvider() { + if (hookTagProvider == null) { hookTagProvider = new HgHookTagProvider(hookChangesetProvider); } - return hookTagProvider; } @@ -102,14 +86,11 @@ public class HgHookContextProvider extends HookContextProvider { return hookChangesetProvider; } - - public HgHookMessageProvider getHgMessageProvider() - { - if (hgMessageProvider == null) - { + + public HgHookMessageProvider getHgMessageProvider() { + if (hgMessageProvider == null) { hgMessageProvider = new HgHookMessageProvider(); } - return hgMessageProvider; } @@ -119,21 +100,9 @@ public class HgHookContextProvider extends HookContextProvider return SUPPORTED_FEATURES; } - //~--- methods -------------------------------------------------------------- - @Override protected HookMessageProvider createMessageProvider() { return getHgMessageProvider(); } - - //~--- fields --------------------------------------------------------------- - - private final HgHookChangesetProvider hookChangesetProvider; - - private HgHookMessageProvider hgMessageProvider; - - private HgHookBranchProvider hookBranchProvider; - - private HgHookTagProvider hookTagProvider; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index ce64b06982..0188078b77 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -26,13 +26,12 @@ package sonia.scm.repository.spi; import com.google.common.io.Closeables; import sonia.scm.repository.Feature; -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; import sonia.scm.repository.api.Command; import sonia.scm.repository.api.CommandNotSupportedException; -import java.io.File; import java.io.IOException; import java.util.EnumSet; import java.util.Set; @@ -41,11 +40,8 @@ import java.util.Set; * * @author Sebastian Sdorra */ -public class HgRepositoryServiceProvider extends RepositoryServiceProvider -{ +public class HgRepositoryServiceProvider extends RepositoryServiceProvider { - /** Field description */ - //J- public static final Set COMMANDS = EnumSet.of( Command.BLAME, Command.BROWSE, @@ -61,25 +57,19 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider Command.PULL, Command.MODIFY ); - //J+ - /** Field description */ - public static final Set FEATURES = - EnumSet.of(Feature.COMBINED_DEFAULT_BRANCH); + public static final Set FEATURES = EnumSet.of(Feature.COMBINED_DEFAULT_BRANCH); - //~--- constructors --------------------------------------------------------- + private final HgRepositoryHandler handler; + private final HgCommandContext context; - HgRepositoryServiceProvider(HgRepositoryHandler handler, - HgHookManager hookManager, Repository repository) - { + HgRepositoryServiceProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, Repository repository) { this.handler = handler; - this.repositoryDirectory = handler.getDirectory(repository.getId()); - this.context = new HgCommandContext(hookManager, handler, repository, - repositoryDirectory); + this.context = new HgCommandContext(handler, factory, repository); } - //~--- methods -------------------------------------------------------------- + /** * Method description * @@ -91,9 +81,9 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider { Closeables.close(context, true); } - //~--- get methods ---------------------------------------------------------- + /** * Method description * @@ -271,14 +261,4 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider return new HgTagsCommand(context); } - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private HgCommandContext context; - - /** Field description */ - private HgRepositoryHandler handler; - - /** Field description */ - private File repositoryDirectory; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java index 4af799f30a..87a5d6d6fb 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; import com.google.inject.Inject; import sonia.scm.plugin.Extension; -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; @@ -35,18 +35,15 @@ import sonia.scm.repository.Repository; * @author Sebastian Sdorra */ @Extension -public class HgRepositoryServiceResolver implements RepositoryServiceResolver -{ +public class HgRepositoryServiceResolver implements RepositoryServiceResolver { private final HgRepositoryHandler handler; - private final HgHookManager hookManager; + private final HgRepositoryFactory factory; @Inject - public HgRepositoryServiceResolver(HgRepositoryHandler handler, - HgHookManager hookManager) - { + public HgRepositoryServiceResolver(HgRepositoryHandler handler, HgRepositoryFactory factory) { this.handler = handler; - this.hookManager = hookManager; + this.factory = factory; } @Override @@ -54,7 +51,7 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver HgRepositoryServiceProvider provider = null; if (HgRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { - provider = new HgRepositoryServiceProvider(handler, hookManager, repository); + provider = new HgRepositoryServiceProvider(handler, factory, repository); } return provider; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java new file mode 100644 index 0000000000..7ea5b97dde --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java @@ -0,0 +1,114 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.spi; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.ByteStreams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgVersion; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HgVersionCommand { + + private static final Logger LOG = LoggerFactory.getLogger(HgVersionCommand.class); + + @VisibleForTesting + static final String[] HG_ARGS = { + "version", "--template", "{ver}" + }; + + @VisibleForTesting + static final String[] PYTHON_ARGS = { + "-c", "import sys; print(sys.version)" + }; + + private final HgConfig config; + private final ProcessExecutor executor; + + public HgVersionCommand(HgConfig config) { + this(config, command -> new ProcessBuilder(command).start()); + } + + HgVersionCommand(HgConfig config, ProcessExecutor executor) { + this.config = config; + this.executor = executor; + } + + public HgVersion get() { + return new HgVersion(getHgVersion(), getPythonVersion()); + } + + @Nonnull + private String getPythonVersion() { + try { + String content = exec(config.getPythonBinary(), PYTHON_ARGS); + int index = content.indexOf(' '); + if (index > 0) { + return content.substring(0, index); + } + } catch (IOException ex) { + LOG.warn("failed to get python version", ex); + } + return HgVersion.UNKNOWN; + } + + @Nonnull + private String getHgVersion() { + try { + return exec(config.getHgBinary(), HG_ARGS).trim(); + } catch (IOException ex) { + LOG.warn("failed to get mercurial version", ex); + return HgVersion.UNKNOWN; + } + } + + @SuppressWarnings("UnstableApiUsage") + private String exec(String command, String[] args) throws IOException { + List cmd = new ArrayList<>(); + cmd.add(command); + cmd.addAll(Arrays.asList(args)); + + Process process = executor.execute(cmd); + byte[] bytes = ByteStreams.toByteArray(process.getInputStream()); + int exitCode = process.exitValue(); + if (exitCode != 0) { + throw new IOException("process ends with exit code " + exitCode); + } + return new String(bytes, StandardCharsets.UTF_8); + } + + @FunctionalInterface + interface ProcessExecutor { + Process execute(List command) throws IOException; + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java index 4e4c4cf80c..c11e15ee4c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java @@ -36,27 +36,21 @@ import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.work.SimpleWorkingCopyFactory; import sonia.scm.repository.work.WorkingCopyPool; import sonia.scm.util.IOUtil; -import sonia.scm.web.HgRepositoryEnvironmentBuilder; import javax.inject.Inject; -import javax.inject.Provider; import java.io.File; import java.io.IOException; -import java.util.Map; -import java.util.function.BiConsumer; public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory implements HgWorkingCopyFactory { - private final Provider hgRepositoryEnvironmentBuilder; - @Inject - public SimpleHgWorkingCopyFactory(Provider hgRepositoryEnvironmentBuilder, WorkingCopyPool workdirProvider) { + public SimpleHgWorkingCopyFactory(WorkingCopyPool workdirProvider) { super(workdirProvider); - this.hgRepositoryEnvironmentBuilder = hgRepositoryEnvironmentBuilder; } + @Override public ParentAndClone initialize(HgCommandContext context, File target, String initialBranch) { - Repository centralRepository = openCentral(context); + Repository centralRepository = context.openForWrite(); CloneCommand cloneCommand = CloneCommandFlags.on(centralRepository); if (initialBranch != null) { cloneCommand.updaterev(initialBranch); @@ -76,7 +70,7 @@ public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory reclaim(HgCommandContext context, File target, String initialBranch) throws ReclaimFailedException { - Repository centralRepository = openCentral(context); + Repository centralRepository = context.openForWrite(); try { BaseRepository clone = Repository.open(target); for (String unknown : StatusCommand.on(clone).execute().getUnknown()) { @@ -89,12 +83,6 @@ public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory> repositoryMapBiConsumer = - (repository, environment) -> hgRepositoryEnvironmentBuilder.get().buildFor(repository, null, environment); - return context.openWithSpecialEnvironment(repositoryMapBiConsumer); - } - private void delete(File directory, String unknownFile) throws IOException { IOUtil.delete(new File(directory, unknownFile)); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java index 9762431d05..90fc52a77c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; import com.google.common.base.Stopwatch; @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgEnvironmentBuilder; import sonia.scm.repository.HgPythonScript; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; @@ -42,29 +43,21 @@ import sonia.scm.web.cgi.CGIExecutor; import sonia.scm.web.cgi.CGIExecutorFactory; import sonia.scm.web.cgi.EnvList; -//~--- JDK imports ------------------------------------------------------------ - -import java.io.File; -import java.io.IOException; - -import java.util.Enumeration; - import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import java.io.File; +import java.io.IOException; + +//~--- JDK imports ------------------------------------------------------------ /** * * @author Sebastian Sdorra */ @Singleton -public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet -{ - - /** Field description */ - public static final String ENV_SESSION_PREFIX = "SCM_"; +public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet { /** Field description */ private static final long serialVersionUID = -3492811300905099810L; @@ -80,13 +73,13 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet ScmConfiguration configuration, HgRepositoryHandler handler, RepositoryRequestListenerUtil requestListenerUtil, - HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder) + HgEnvironmentBuilder environmentBuilder) { this.cgiExecutorFactory = cgiExecutorFactory; this.configuration = configuration; this.handler = handler; this.requestListenerUtil = requestListenerUtil; - this.hgRepositoryEnvironmentBuilder = hgRepositoryEnvironmentBuilder; + this.environmentBuilder = environmentBuilder; this.exceptionHandler = new HgCGIExceptionHandler(); this.command = HgPythonScript.HGWEB.getFile(SCMContext.getContext()); } @@ -108,11 +101,7 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet { handleRequest(request, response, repository); } - catch (ServletException ex) - { - exceptionHandler.handleException(request, response, ex); - } - catch (IOException ex) + catch (ServletException | IOException ex) { exceptionHandler.handleException(request, response, ex); } @@ -146,29 +135,6 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet } } - /** - * Method description - * - * - * @param env - * @param session - */ - @SuppressWarnings("unchecked") - private void passSessionAttributes(EnvList env, HttpSession session) - { - Enumeration enm = session.getAttributeNames(); - - while (enm.hasMoreElements()) - { - String key = enm.nextElement(); - - if (key.startsWith(ENV_SESSION_PREFIX)) - { - env.set(key, session.getAttribute(key).toString()); - } - } - } - /** * Method description * @@ -192,7 +158,9 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet executor.setExceptionHandler(exceptionHandler); executor.setStatusCodeHandler(exceptionHandler); executor.setContentLengthWorkaround(true); - hgRepositoryEnvironmentBuilder.buildFor(repository, request, executor.getEnvironment().asMutableMap()); + + EnvList env = executor.getEnvironment(); + environmentBuilder.write(repository).forEach(env::set); String interpreter = getInterpreter(); @@ -248,5 +216,5 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet /** Field description */ private final RepositoryRequestListenerUtil requestListenerUtil; - private final HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder; + private final HgEnvironmentBuilder environmentBuilder; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java deleted file mode 100644 index 5b0609cc72..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.web; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.io.Closeables; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.subject.Subject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.NotFoundException; -import sonia.scm.repository.HgContext; -import sonia.scm.repository.HgHookManager; -import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.RepositoryHookType; -import sonia.scm.repository.api.HgHookMessage; -import sonia.scm.repository.api.HgHookMessage.Severity; -import sonia.scm.repository.spi.HgHookContextProvider; -import sonia.scm.repository.spi.HookEventFacade; -import sonia.scm.security.BearerToken; -import sonia.scm.security.CipherUtil; -import sonia.scm.util.HttpUtil; -import sonia.scm.util.Util; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -public class HgHookCallbackServlet extends HttpServlet -{ - - /** Field description */ - public static final String HGHOOK_POST_RECEIVE = "changegroup"; - - /** Field description */ - public static final String HGHOOK_PRE_RECEIVE = "pretxnchangegroup"; - - /** Field description */ - public static final String PARAM_REPOSITORYID = "repositoryId"; - - /** Field description */ - private static final String PARAM_CHALLENGE = "challenge"; - - /** Field description */ - private static final String PARAM_TOKEN = "token"; - - /** Field description */ - private static final String PARAM_NODE = "node"; - - /** Field description */ - private static final String PARAM_PING = "ping"; - - /** Field description */ - private static final Pattern REGEX_URL = - Pattern.compile("^/hook/hg/([^/]+)$"); - - /** the logger for HgHookCallbackServlet */ - private static final Logger logger = - LoggerFactory.getLogger(HgHookCallbackServlet.class); - - /** Field description */ - private static final long serialVersionUID = 3531596724828189353L; - - //~--- constructors --------------------------------------------------------- - - @Inject - public HgHookCallbackServlet(HookEventFacade hookEventFacade, - HgRepositoryHandler handler, HgHookManager hookManager, - Provider contextProvider) - { - this.hookEventFacade = hookEventFacade; - this.handler = handler; - this.hookManager = hookManager; - this.contextProvider = contextProvider; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param request - * @param response - * - * @throws IOException - * @throws ServletException - */ - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - { - String ping = request.getParameter(PARAM_PING); - - if (Util.isNotEmpty(ping) && Boolean.parseBoolean(ping)) - { - response.setStatus(HttpServletResponse.SC_NO_CONTENT); - } - else - { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - } - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - handlePostRequest(request, response); - } catch (IOException ex) { - logger.warn("error in hook callback execution, sending internal server error", ex); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - - private void handlePostRequest(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String strippedURI = HttpUtil.getStrippedURI(request); - Matcher m = REGEX_URL.matcher(strippedURI); - - if (m.matches()) - { - String repositoryId = getRepositoryId(request); - String type = m.group(1); - String challenge = request.getParameter(PARAM_CHALLENGE); - - if (Util.isNotEmpty(challenge)) - { - String node = request.getParameter(PARAM_NODE); - - if (Util.isNotEmpty(node)) - { - String token = request.getParameter(PARAM_TOKEN); - - if (Util.isNotEmpty(token)) - { - authenticate(token); - } - - hookCallback(response, type, repositoryId, challenge, node); - } - else if (logger.isDebugEnabled()) - { - logger.debug("node parameter not found"); - } - } - else if (logger.isDebugEnabled()) - { - logger.debug("challenge parameter not found"); - } - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("url does not match"); - } - - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - - private void authenticate(String token) - { - try - { - token = CipherUtil.getInstance().decode(token); - - if (Util.isNotEmpty(token)) - { - Subject subject = SecurityUtils.getSubject(); - - AuthenticationToken accessToken = createToken(token); - - //J- - subject.login(accessToken); - } - } - catch (Exception ex) - { - logger.error("could not authenticate user", ex); - } - } - - private AuthenticationToken createToken(String tokenString) - { - return BearerToken.valueOf(tokenString); - } - - private void fireHook(HttpServletResponse response, String repositoryId, String node, RepositoryHookType type) - throws IOException - { - HgHookContextProvider context = null; - - try - { - if (type == RepositoryHookType.PRE_RECEIVE) - { - contextProvider.get().setPending(true); - } - - File repositoryDirectory = handler.getDirectory(repositoryId); - context = new HgHookContextProvider(handler, repositoryDirectory, hookManager, - node, type); - - hookEventFacade.handle(repositoryId).fireHookEvent(type, context); - - printMessages(response, context); - } - catch (NotFoundException ex) - { - logger.error(ex.getMessage()); - - logger.trace("repository not found", ex); - - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - catch (Exception ex) - { - sendError(response, context, ex); - } - } - - private void hookCallback(HttpServletResponse response, String typeName, String repositoryId, String challenge, String node) throws IOException { - if (hookManager.isAcceptAble(challenge)) - { - RepositoryHookType type = null; - - if (HGHOOK_PRE_RECEIVE.equals(typeName)) - { - type = RepositoryHookType.PRE_RECEIVE; - } - else if (HGHOOK_POST_RECEIVE.equals(typeName)) - { - type = RepositoryHookType.POST_RECEIVE; - } - - if (type != null) - { - fireHook(response, repositoryId, node, type); - } - else - { - if (logger.isWarnEnabled()) - { - logger.warn("unknown hook type {}", typeName); - } - - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - else - { - if (logger.isWarnEnabled()) - { - logger.warn("hg hook challenge is not accept able"); - } - - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - - /** - * Method description - * - * - * @param writer - * @param msg - */ - private void printMessage(PrintWriter writer, HgHookMessage msg) - { - writer.append('_'); - - if (msg.getSeverity() == Severity.ERROR) - { - writer.append("e[SCM] Error: "); - } - else - { - writer.append("n[SCM] "); - } - - writer.println(msg.getMessage()); - } - - /** - * Method description - * - * - * @param response - * @param context - * - * @throws IOException - */ - private void printMessages(HttpServletResponse response, - HgHookContextProvider context) - throws IOException - { - List msgs = context.getHgMessageProvider().getMessages(); - - if (Util.isNotEmpty(msgs)) - { - PrintWriter writer = null; - - try - { - writer = response.getWriter(); - - printMessages(writer, msgs); - } - finally - { - Closeables.close(writer, false); - } - } - } - - /** - * Method description - * - * - * @param writer - * @param msgs - */ - private void printMessages(PrintWriter writer, List msgs) - { - for (HgHookMessage msg : msgs) - { - printMessage(writer, msg); - } - } - - /** - * Method description - * - * - * @param response - * @param context - * @param ex - * - * @throws IOException - */ - private void sendError(HttpServletResponse response, - HgHookContextProvider context, Exception ex) - throws IOException - { - logger.warn("hook ended with exception", ex); - response.setStatus(HttpServletResponse.SC_CONFLICT); - - String msg = ex.getMessage(); - List msgs = null; - - if (context != null) - { - msgs = context.getHgMessageProvider().getMessages(); - } - - if (!Strings.isNullOrEmpty(msg) || Util.isNotEmpty(msgs)) - { - PrintWriter writer = null; - - try - { - writer = response.getWriter(); - - if (Util.isNotEmpty(msgs)) - { - printMessages(writer, msgs); - } - - if (!Strings.isNullOrEmpty(msg)) - { - printMessage(writer, new HgHookMessage(Severity.ERROR, msg)); - } - } - finally - { - Closeables.close(writer, true); - } - } - } - - //~--- get methods ---------------------------------------------------------- - - private String getRepositoryId(HttpServletRequest request) - { - String id = request.getParameter(PARAM_REPOSITORYID); - Preconditions.checkArgument(!Strings.isNullOrEmpty(id), "repository id not found in request"); - return id; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final Provider contextProvider; - - /** Field description */ - private final HgRepositoryHandler handler; - - /** Field description */ - private final HookEventFacade hookEventFacade; - - /** Field description */ - private final HgHookManager hookManager; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgRepositoryEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgRepositoryEnvironmentBuilder.java deleted file mode 100644 index 6b626b5c33..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgRepositoryEnvironmentBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.web; - -import sonia.scm.repository.HgEnvironment; -import sonia.scm.repository.HgHookManager; -import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.Repository; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import java.io.File; -import java.util.Map; - -public class HgRepositoryEnvironmentBuilder { - - private static final String ENV_REPOSITORY_NAME = "REPO_NAME"; - private static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH"; - private static final String ENV_REPOSITORY_ID = "SCM_REPOSITORY_ID"; - private static final String ENV_PYTHON_HTTPS_VERIFY = "PYTHONHTTPSVERIFY"; - private static final String ENV_HTTP_POST_ARGS = "SCM_HTTP_POST_ARGS"; - - private final HgRepositoryHandler handler; - private final HgHookManager hookManager; - - @Inject - public HgRepositoryEnvironmentBuilder(HgRepositoryHandler handler, HgHookManager hookManager) { - this.handler = handler; - this.hookManager = hookManager; - } - - public void buildFor(Repository repository, HttpServletRequest request, Map environment) { - File directory = handler.getDirectory(repository.getId()); - - environment.put(ENV_REPOSITORY_NAME, repository.getNamespace() + "/" + repository.getName()); - environment.put(ENV_REPOSITORY_ID, repository.getId()); - environment.put(ENV_REPOSITORY_PATH, - directory.getAbsolutePath()); - - // add hook environment - if (handler.getConfig().isDisableHookSSLValidation()) { - // disable ssl validation - // Issue 959: https://goo.gl/zH5eY8 - environment.put(ENV_PYTHON_HTTPS_VERIFY, "0"); - } - - // enable experimental httppostargs protocol of mercurial - // Issue 970: https://goo.gl/poascp - environment.put(ENV_HTTP_POST_ARGS, String.valueOf(handler.getConfig().isEnableHttpPostArgs())); - - HgEnvironment.prepareEnvironment( - environment, - handler, - hookManager, - request - ); - } -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index 5271751faa..64a27990a8 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -34,9 +34,6 @@ import sonia.scm.api.v2.resources.HgConfigPackagesToDtoMapper; import sonia.scm.api.v2.resources.HgConfigToHgConfigDtoMapper; import sonia.scm.installer.HgPackageReader; import sonia.scm.plugin.Extension; -import sonia.scm.repository.HgContext; -import sonia.scm.repository.HgContextProvider; -import sonia.scm.repository.HgHookManager; import sonia.scm.repository.spi.HgWorkingCopyFactory; import sonia.scm.repository.spi.SimpleHgWorkingCopyFactory; @@ -45,26 +42,10 @@ import sonia.scm.repository.spi.SimpleHgWorkingCopyFactory; * @author Sebastian Sdorra */ @Extension -public class HgServletModule extends ServletModule -{ +public class HgServletModule extends ServletModule { - /** Field description */ - public static final String MAPPING_HG = "/hg/*"; - - /** Field description */ - public static final String MAPPING_HOOK = "/hook/hg/*"; - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - */ @Override - protected void configureServlets() - { - bind(HgContext.class).toProvider(HgContextProvider.class); - bind(HgHookManager.class); + protected void configureServlets() { bind(HgPackageReader.class); bind(HgConfigDtoToHgConfigMapper.class).to(Mappers.getMapper(HgConfigDtoToHgConfigMapper.class).getClass()); @@ -72,9 +53,6 @@ public class HgServletModule extends ServletModule bind(HgConfigPackagesToDtoMapper.class).to(Mappers.getMapper(HgConfigPackagesToDtoMapper.class).getClass()); bind(HgConfigInstallationsToDtoMapper.class); - // bind servlets - serve(MAPPING_HOOK).with(HgHookCallbackServlet.class); - bind(HgWorkingCopyFactory.class).to(SimpleHgWorkingCopyFactory.class); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java index 234580ed6f..3ba797aff2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java @@ -21,189 +21,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; -//~--- non-JDK imports -------------------------------------------------------- - -import com.aragost.javahg.Repository; -import com.aragost.javahg.RepositoryConfiguration; - -import com.google.common.base.Strings; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sonia.scm.SCMContext; import sonia.scm.repository.HgConfig; -import sonia.scm.repository.HgEnvironment; -import sonia.scm.repository.HgHookManager; import sonia.scm.repository.HgPythonScript; -import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.repository.spi.javahg.HgFileviewExtension; -import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; -//~--- JDK imports ------------------------------------------------------------ - import java.io.File; -import java.nio.charset.Charset; -import java.util.Map; -import java.util.function.Consumer; - -import javax.servlet.http.HttpServletRequest; - /** * * @author Sebastian Sdorra */ -public final class HgUtil -{ +public final class HgUtil { - /** Field description */ public static final String REVISION_TIP = "tip"; - /** Field description */ - private static final String USERAGENT_HG = "mercurial/"; - - /** - * the logger for HgUtil - */ - private static final Logger logger = LoggerFactory.getLogger(HgUtil.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - */ private HgUtil() {} - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param handler - * @param hookManager - * @param directory - * @param encoding - * @param pending - * - * @return - */ - public static Repository open(HgRepositoryHandler handler, - HgHookManager hookManager, File directory, String encoding, boolean pending) - { - return open( - handler, - directory, - encoding, - pending, - environment -> HgEnvironment.prepareEnvironment(environment, handler, hookManager) - ); - } - - public static Repository open(HgRepositoryHandler handler, - File directory, String encoding, boolean pending, - Consumer> prepareEnvironment) - { - String enc = encoding; - - if (Strings.isNullOrEmpty(enc)) - { - enc = handler.getConfig().getEncoding(); - } - - RepositoryConfiguration repoConfiguration = RepositoryConfiguration.DEFAULT; - - prepareEnvironment.accept(repoConfiguration.getEnvironment()); - - repoConfiguration.addExtension(HgFileviewExtension.class); - repoConfiguration.setEnablePendingChangesets(pending); - - try - { - Charset charset = Charset.forName(enc); - - logger.trace("set encoding {} for mercurial", enc); - - repoConfiguration.setEncoding(charset); - } - catch (IllegalArgumentException ex) - { - logger.error("could not set encoding for mercurial", ex); - } - - repoConfiguration.setHgBin(handler.getConfig().getHgBinary()); - - logger.debug("open hg repository {}: encoding: {}, pending: {}", directory, enc, pending); - - return Repository.open(repoConfiguration, directory); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param config - * - * @return - */ - public static String getPythonPath(HgConfig config) - { + public static String getPythonPath(HgConfig config) { String pythonPath = Util.EMPTY_STRING; - if (config != null) - { + if (config != null) { pythonPath = Util.nonNull(config.getPythonPath()); } - if (Util.isNotEmpty(pythonPath)) - { + if (Util.isNotEmpty(pythonPath)) { pythonPath = pythonPath.concat(File.pathSeparator); } - //J- pythonPath = pythonPath.concat( HgPythonScript.getScriptDirectory( SCMContext.getContext() ).getAbsolutePath() ); - //J+ return pythonPath; } - /** - * Method description - * - * - * @param revision - * - * @return - */ - public static String getRevision(String revision) - { - return Util.isEmpty(revision) - ? REVISION_TIP - : revision; + public static String getRevision(String revision) { + return Util.isEmpty(revision) ? REVISION_TIP : revision; } - /** - * Returns true if the request comes from a mercurial client. - * - * - * @param request servlet request - * - * @return true if the client is mercurial - */ - public static boolean isHgClient(HttpServletRequest request) - { - return HttpUtil.userAgentStartsWith(request, USERAGENT_HG); - } } diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index eb7718bb87..f6166fab1a 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -29,87 +29,40 @@ # changegroup.scm = python:scmhooks.callback # -import os, sys - -client = None - -# compatibility layer between python 2 and 3 urllib implementations -if sys.version_info[0] < 3: - import urllib, urllib2 - # create type alias for url error - URLError = urllib2.URLError - - class Python2Client: - def post(self, url, values): - data = urllib.urlencode(values) - # open url but ignore proxy settings - proxy_handler = urllib2.ProxyHandler({}) - opener = urllib2.build_opener(proxy_handler) - req = urllib2.Request(url, data) - req.add_header("X-XSRF-Token", xsrf) - return opener.open(req) - - client = Python2Client() -else: - import urllib.parse, urllib.request, urllib.error - # create type alias for url error - URLError = urllib.error.URLError - - class Python3Client: - def post(self, url, values): - data = urllib.parse.urlencode(values) - # open url but ignore proxy settings - proxy_handler = urllib.request.ProxyHandler({}) - opener = urllib.request.build_opener(proxy_handler) - req = urllib.request.Request(url, data.encode()) - req.add_header("X-XSRF-Token", xsrf) - return opener.open(req) - - client = Python3Client() +import os, sys, json, socket # read environment -baseUrl = os.environ['SCM_URL'] +port = os.environ['SCM_HOOK_PORT'] challenge = os.environ['SCM_CHALLENGE'] token = os.environ['SCM_BEARER_TOKEN'] -xsrf = os.environ['SCM_XSRF'] repositoryId = os.environ['SCM_REPOSITORY_ID'] -def printMessages(ui, msgs): - for raw in msgs: - line = raw - if hasattr(line, "encode"): - line = line.encode() - if line.startswith(b"_e") or line.startswith(b"_n"): - line = line[2:] - ui.warn(b'%s\n' % line.rstrip()) +def print_messages(ui, messages): + for message in messages: + ui.warn(b'%s: %s\n' % message['severity'], message['message']) -def callHookUrl(ui, repo, hooktype, node): +def fire_hook(ui, repo, hooktype, node): abort = True try: - url = baseUrl + hooktype.decode("utf-8") - ui.debug( b"send scm-hook to " + url.encode() + b" and " + node + b"\n" ) - values = {'node': node.decode("utf-8"), 'challenge': challenge, 'token': token, 'repositoryPath': repo.root, 'repositoryId': repositoryId} - conn = client.post(url, values) - if 200 <= conn.code < 300: - ui.debug( b"scm-hook " + hooktype + b" success with status code " + str(conn.code).encode() + b"\n" ) - printMessages(ui, conn) - abort = False - else: - ui.warn( b"ERROR: scm-hook failed with error code " + str(conn.code).encode() + b"\n" ) - except URLError as e: - msg = None - # some URLErrors have no read method - if hasattr(e, "read"): - msg = e.read() - elif hasattr(e, "code"): - msg = "scm-hook failed with error code " + e.code + "\n" - else: - msg = str(e) - if len(msg) > 0: - printMessages(ui, msg.splitlines(True)) - else: - ui.warn( b"ERROR: scm-hook failed with an unknown error\n" ) - ui.traceback() + values = {'token': token, 'type': hooktype, 'repositoryId': repositoryId, 'challenge': challenge, 'node': node.decode('utf8') } + ui.debug( b"send scm-hook for " + node + b"\n" ) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(("127.0.0.1", int(port))) + s.sendall(json.dumps(values).encode('utf-8')) + s.sendall(b'\0') + + received = [] + received = s.recv(1) + while b != b'\0': + received.append(b) + received = s.recv(1) + + message = b''.join(bytes).decode('utf-8') + response = json.loads(message) + + abort = response['abort'] + print_messages(ui, response['messages']) + except ValueError: ui.warn( b"scm-hook failed with an exception\n" ) ui.traceback() @@ -118,8 +71,8 @@ def callHookUrl(ui, repo, hooktype, node): def callback(ui, repo, hooktype, node=None): abort = True if node != None: - if len(baseUrl) > 0: - abort = callHookUrl(ui, repo, hooktype, node) + if len(port) > 0: + abort = fire_hook(ui, repo, hooktype, node) else: ui.warn(b"ERROR: scm-manager hooks are disabled, please check your configuration and the scm-manager log for details\n") abort = False @@ -127,7 +80,7 @@ def callback(ui, repo, hooktype, node=None): ui.warn(b"changeset node is not available") return abort -def preHook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): +def pre_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): # older mercurial versions if pending != None: pending() @@ -146,7 +99,7 @@ def preHook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): ui.debug(b"mercurial does not support currenttransation") # do nothing - return callback(ui, repo, hooktype, node) + return callback(ui, repo, "PRE_RECEIVE", node) -def postHook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): - return callback(ui, repo, hooktype, node) +def post_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): + return callback(ui, repo, "POST_RECEIVE", node) diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java index 028276cd62..7779380638 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import org.junit.Test; @@ -52,7 +52,6 @@ public class HgConfigDtoToHgConfigMapperTest { assertEquals("/etc/", config.getPythonPath()); assertTrue(config.isShowRevisionInId()); assertTrue(config.isUseOptimizedBytecode()); - assertTrue(config.isDisableHookSSLValidation()); assertTrue(config.isEnableHttpPostArgs()); } @@ -65,7 +64,6 @@ public class HgConfigDtoToHgConfigMapperTest { configDto.setPythonPath("/etc/"); configDto.setShowRevisionInId(true); configDto.setUseOptimizedBytecode(true); - configDto.setDisableHookSSLValidation(true); configDto.setEnableHttpPostArgs(true); return configDto; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java new file mode 100644 index 0000000000..53ae99e631 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java @@ -0,0 +1,149 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.SCMContext; +import sonia.scm.repository.hooks.HookEnvironment; +import sonia.scm.repository.hooks.HookServer; +import sonia.scm.security.AccessToken; +import sonia.scm.security.AccessTokenBuilderFactory; +import sonia.scm.security.CipherUtil; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static sonia.scm.repository.DefaultHgEnvironmentBuilder.*; + + +@ExtendWith(MockitoExtension.class) +class DefaultHgEnvironmentBuilderTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private AccessTokenBuilderFactory accessTokenBuilderFactory; + + @Mock + private HgRepositoryHandler repositoryHandler; + + @Mock + private HookEnvironment hookEnvironment; + + @Mock + private HookServer server; + + @InjectMocks + private DefaultHgEnvironmentBuilder builder; + + private Path directory; + + @BeforeEach + void setBaseDir(@TempDir Path directory) { + this.directory = directory; + TempSCMContextProvider context = (TempSCMContextProvider) SCMContext.getContext(); + context.setBaseDirectory(directory.resolve("home").toFile()); + } + + @Test + void shouldReturnReadEnvironment() { + Repository heartOfGold = prepareForRead("/usr/lib/python", "42"); + + Map env = builder.read(heartOfGold); + assertReadEnv(env, "/usr/lib/python", "42"); + } + + @Test + void shouldReturnWriteEnvironment() throws IOException { + Repository heartOfGold = prepareForWrite("/opt/python", "21"); + + Map env = builder.write(heartOfGold); + assertReadEnv(env, "/opt/python", "21"); + + String bearer = CipherUtil.getInstance().decode(env.get(ENV_BEARER_TOKEN)); + assertThat(bearer).isEqualTo("secretAC"); + assertThat(env) + .containsEntry(ENV_CHALLENGE, "challenge") + .containsEntry(ENV_HOOK_PORT, "2042"); + } + + @Test + void shouldThrowIllegalStateIfServerCouldNotBeStarted() throws IOException { + when(server.start()).thenThrow(new IOException("failed to start")); + Repository repository = prepareForRead("/usr", "42"); + assertThrows(IllegalStateException.class, () -> builder.write(repository)); + } + + private Repository prepareForWrite(String pythonPath, String id) throws IOException { + Repository heartOfGold = prepareForRead(pythonPath, id); + applyAccessToken("secretAC"); + when(server.start()).thenReturn(2042); + when(hookEnvironment.getChallenge()).thenReturn("challenge"); + return heartOfGold; + } + + private void applyAccessToken(String compact) { + AccessToken accessToken = mock(AccessToken.class); + when(accessTokenBuilderFactory.create().build()).thenReturn(accessToken); + when(accessToken.compact()).thenReturn(compact); + } + + + private void assertReadEnv(Map env, String pythonPath, String repositoryId) { + assertThat(env) + .containsEntry(ENV_REPOSITORY_ID, repositoryId) + .containsEntry(ENV_REPOSITORY_NAME, "hitchhiker/HeartOfGold") + .containsEntry(ENV_HTTP_POST_ARGS, "false") + .containsEntry(ENV_REPOSITORY_PATH, directory.resolve("repo").toAbsolutePath().toString()) + .containsEntry(ENV_PYTHON_PATH, pythonPath + File.pathSeparator + directory.resolve("home/lib/python")); + } + + @Nonnull + private Repository prepareForRead(String pythonPath, String id) { + when(repositoryHandler.getDirectory(id)).thenReturn(directory.resolve("repo").toFile()); + + HgConfig config = new HgConfig(); + config.setPythonPath(pythonPath); + when(repositoryHandler.getConfig()).thenReturn(config); + + Repository heartOfGold = RepositoryTestData.createHeartOfGold(); + heartOfGold.setId(id); + + return heartOfGold; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersionHandler.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/EmptyHgEnvironmentBuilder.java similarity index 66% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersionHandler.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/EmptyHgEnvironmentBuilder.java index 7ee499741f..140802ce7f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVersionHandler.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/EmptyHgEnvironmentBuilder.java @@ -21,30 +21,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; -//~--- JDK imports ------------------------------------------------------------ +import java.util.Collections; +import java.util.Map; -import java.io.File; -import java.io.IOException; - -/** - * - * @author Sebastian Sdorra - */ -public class HgVersionHandler extends AbstractHgHandler -{ - - public HgVersionHandler(HgRepositoryHandler handler, HgContext context, - File directory) - { - super(handler, context, null, directory); +public class EmptyHgEnvironmentBuilder implements HgEnvironmentBuilder { + @Override + public Map read(Repository repository) { + return Collections.emptyMap(); } - //~--- get methods ---------------------------------------------------------- - - public HgVersion getVersion() throws IOException { - return getResultFromScript(HgVersion.class, HgPythonScript.VERSION); + @Override + public Map write(Repository repository) { + return Collections.emptyMap(); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgContextProviderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgContextProviderTest.java deleted file mode 100644 index a65e4cd8e2..0000000000 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgContextProviderTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.repository; - -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.OutOfScopeException; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; -import com.google.inject.Scope; -import com.google.inject.servlet.RequestScoped; -import com.google.inject.util.Providers; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class HgContextProviderTest { - - @Mock - private Scope scope; - - @Test - void shouldThrowNonOutOfScopeProvisionExceptions() { - Provider provider = () -> { - throw new RuntimeException("something different"); - }; - - when(scope.scope(any(Key.class), any(Provider.class))).thenReturn(provider); - - Injector injector = Guice.createInjector(new HgContextModule(scope)); - - assertThrows(ProvisionException.class, () -> injector.getInstance(HgContext.class)); - } - - @Test - void shouldCreateANewInstanceIfOutOfRequestScope() { - Provider provider = () -> { - throw new OutOfScopeException("no request"); - }; - when(scope.scope(any(Key.class), any(Provider.class))).thenReturn(provider); - - Injector injector = Guice.createInjector(new HgContextModule(scope)); - - HgContext contextOne = injector.getInstance(HgContext.class); - HgContext contextTwo = injector.getInstance(HgContext.class); - - assertThat(contextOne).isNotSameAs(contextTwo); - } - - @Test - void shouldInjectFromRequestScope() { - HgContextRequestStore requestStore = new HgContextRequestStore(); - Provider provider = Providers.of(requestStore); - - when(scope.scope(any(Key.class), any(Provider.class))).thenReturn(provider); - - Injector injector = Guice.createInjector(new HgContextModule(scope)); - - HgContext contextOne = injector.getInstance(HgContext.class); - HgContext contextTwo = injector.getInstance(HgContext.class); - - assertThat(contextOne).isSameAs(contextTwo); - } - - private static class HgContextModule extends AbstractModule { - - private Scope scope; - - private HgContextModule(Scope scope) { - this.scope = scope; - } - - @Override - protected void configure() { - bindScope(RequestScoped.class, scope); - bind(HgContextRequestStore.class); - bind(HgContext.class).toProvider(HgContextProvider.class); - } - } -} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java new file mode 100644 index 0000000000..ffeb42f613 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java @@ -0,0 +1,125 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import com.aragost.javahg.Repository; +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.hooks.HookEnvironment; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HgRepositoryFactoryTest { + + private HgRepositoryHandler handler; + + @Mock + private HookEnvironment hookEnvironment; + + @Mock + private HgEnvironmentBuilder environmentBuilder; + + private HgRepositoryFactory factory; + + private sonia.scm.repository.Repository heartOfGold; + + @BeforeEach + void setUpFactory(@TempDir Path directory) { + handler = HgTestUtil.createHandler(directory.toFile()); + assumeTrue(handler.isConfigured()); + + factory = new HgRepositoryFactory(handler, hookEnvironment, environmentBuilder); + heartOfGold = createRepository(); + } + + @Test + void shouldOpenRepositoryForRead() { + Repository repository = factory.openForRead(heartOfGold); + + assertThat(repository).isNotNull(); + verify(environmentBuilder).read(heartOfGold); + } + + @Test + void shouldOpenRepositoryForWrite() { + Repository repository = factory.openForWrite(heartOfGold); + + assertThat(repository).isNotNull(); + verify(environmentBuilder).write(heartOfGold); + } + + @Test + void shouldFallbackToUTF8OnUnknownEncoding() { + handler.getConfig().setEncoding("unknown"); + + Repository repository = factory.openForRead(heartOfGold); + + assertThat(repository.getBaseRepository().getConfiguration().getEncoding()).isEqualTo(StandardCharsets.UTF_8); + } + + @Test + void shouldSetPendingChangesetState() { + when(hookEnvironment.isPending()).thenReturn(true); + + Repository repository = factory.openForRead(heartOfGold); + + assertThat(repository.getBaseRepository().getConfiguration().isEnablePendingChangesets()) + .isTrue(); + } + + @Test + void shouldPassEnvironment() { + when(environmentBuilder.read(heartOfGold)).thenReturn(ImmutableMap.of("spaceship", "heartOfGold")); + + Repository repository = factory.openForRead(heartOfGold); + + assertThat(repository.getBaseRepository().getConfiguration().getEnvironment()) + .containsEntry("spaceship", "heartOfGold"); + } + + @Nonnull + private sonia.scm.repository.Repository createRepository() { + sonia.scm.repository.Repository heartOfGold = RepositoryTestData.createHeartOfGold("hg"); + heartOfGold.setId("42"); + + handler.create(heartOfGold); + return heartOfGold; + } + + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java index e91659e841..7d0dc6f6db 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- @@ -32,13 +32,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.plugin.PluginLoader; +import sonia.scm.repository.spi.HgVersionCommand; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; //~--- JDK imports ------------------------------------------------------------ @@ -52,9 +56,6 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Mock private ConfigurationStoreFactory factory; - @Mock - private com.google.inject.Provider provider; - @Override protected void checkDirectory(File directory) { File hgDirectory = new File(directory, ".hg"); @@ -70,7 +71,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Override protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) { - HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver, null, null); + HgRepositoryHandler handler = new HgRepositoryHandler(factory, locationResolver, null, null); handler.init(contextProvider); HgTestUtil.checkForSkip(handler); @@ -80,7 +81,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { - HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null, null); + HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, locationResolver, null, null); HgConfig hgConfig = new HgConfig(); hgConfig.setHgBinary("hg"); @@ -91,4 +92,20 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { File path = repositoryHandler.getDirectory(repository.getId()); assertEquals(repoPath.toString() + File.separator + RepositoryDirectoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath()); } + + @Test + public void shouldReturnVersionInformation() { + PluginLoader pluginLoader = mock(PluginLoader.class); + when(pluginLoader.getUberClassLoader()).thenReturn(HgRepositoryHandler.class.getClassLoader()); + + HgVersionCommand versionCommand = mock(HgVersionCommand.class); + when(versionCommand.get()).thenReturn(new HgVersion("5.2.0", "3.7.2")); + + HgRepositoryHandler handler = new HgRepositoryHandler( + factory, locationResolver, pluginLoader, null + ); + + String versionInformation = handler.getVersionInformation(versionCommand); + assertThat(versionInformation).startsWith("scm-hg-version/").endsWith("python/3.7.2 mercurial/5.2.0"); + } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index 72d276b2b6..48b8e4c506 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- @@ -29,16 +29,13 @@ package sonia.scm.repository; import org.junit.Assume; import sonia.scm.SCMContext; import sonia.scm.TempDirRepositoryLocationResolver; -import sonia.scm.security.AccessToken; +import sonia.scm.repository.hooks.HookEnvironment; import sonia.scm.store.InMemoryConfigurationStoreFactory; -import javax.servlet.http.HttpServletRequest; import java.io.File; -import java.nio.file.Path; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; //~--- JDK imports ------------------------------------------------------------ @@ -80,49 +77,26 @@ public final class HgTestUtil } } - /** - * Method description - * - * - * @param directory - * - * @return - */ - public static HgRepositoryHandler createHandler(File directory) { - TempSCMContextProvider context = - (TempSCMContextProvider) SCMContext.getContext(); + public static HgRepositoryHandler createHandler(File directory) { + TempSCMContextProvider context = (TempSCMContextProvider) SCMContext.getContext(); context.setBaseDirectory(directory); - RepositoryDAO repoDao = mock(RepositoryDAO.class); - RepositoryLocationResolver repositoryLocationResolver = new TempDirRepositoryLocationResolver(directory); - HgRepositoryHandler handler = - new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null); + HgRepositoryHandler handler = new HgRepositoryHandler( + new InMemoryConfigurationStoreFactory(), + repositoryLocationResolver, + null, + null + ); handler.init(context); return handler; } - /** - * Method description - * - * - * @return - */ - public static HgHookManager createHookManager() - { - HgHookManager hookManager = mock(HgHookManager.class); - - when(hookManager.getChallenge()).thenReturn("challenge"); - when(hookManager.createUrl()).thenReturn( - "http://localhost:8081/scm/hook/hg/"); - when(hookManager.createUrl(any(HttpServletRequest.class))).thenReturn( - "http://localhost:8081/scm/hook/hg/"); - AccessToken accessToken = mock(AccessToken.class); - when(accessToken.compact()).thenReturn(""); - when(hookManager.getAccessToken()).thenReturn(accessToken); - - return hookManager; + public static HgRepositoryFactory createFactory(HgRepositoryHandler handler, File directory) { + return new HgRepositoryFactory( + handler, new HookEnvironment(), new EmptyHgEnvironmentBuilder(), repository -> directory + ); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java similarity index 63% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 516b66c4a3..45a9bc5aa5 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HgHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -24,7 +24,6 @@ package sonia.scm.repository.hooks; -import com.google.inject.util.Providers; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; @@ -35,10 +34,10 @@ 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.repository.HgRepositoryHandler; -import sonia.scm.repository.Repository; +import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; +import sonia.scm.repository.api.HgHookMessageProvider; import sonia.scm.repository.spi.HgHookContextProvider; import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.security.CipherUtil; @@ -48,6 +47,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; +import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -55,10 +55,13 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) -class HgHookHandlerTest { +class DefaultHookHandlerTest { @Mock - private HgRepositoryHandler repositoryHandler; + private HookContextProviderFactory hookContextProviderFactory; + + @Mock + private HgHookContextProvider contextProvider; @Mock private HookEventFacade hookEventFacade; @@ -71,7 +74,7 @@ class HgHookHandlerTest { private HookEnvironment hookEnvironment; - private HgHookHandler handler; + private DefaultHookHandler handler; @Mock private Subject subject; @@ -81,11 +84,13 @@ class HgHookHandlerTest { ThreadContext.bind(subject); hookEnvironment = new HookEnvironment(); - DefaultHookHandlerFactory factory = new DefaultHookHandlerFactory( - repositoryHandler, hookEventFacade, Providers.of(hookEnvironment) - ); - handler = factory.create(socket); + handler = new DefaultHookHandler(hookContextProviderFactory, hookEventFacade, hookEnvironment, socket); + } + + private void mockMessageProvider() { + when(hookContextProviderFactory.create("42", "abc")).thenReturn(contextProvider); + when(contextProvider.getHgMessageProvider()).thenReturn(new HgHookMessageProvider()); } @AfterEach @@ -95,10 +100,11 @@ class HgHookHandlerTest { @Test void shouldFireHook() throws IOException { + mockMessageProvider(); when(hookEventFacade.handle("42")).thenReturn(hookEventHandler); - HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); - HgHookHandler.Response response = send(request); + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); assertSuccess(response, RepositoryHookType.POST_RECEIVE); assertThat(hookEnvironment.isPending()).isFalse(); @@ -106,13 +112,22 @@ class HgHookHandlerTest { @Test void shouldSetPendingStateOnPreReceiveHooks() throws IOException { + mockMessageProvider(); when(hookEventFacade.handle("42")).thenReturn(hookEventHandler); - HgHookHandler.Request request = createRequest(RepositoryHookType.PRE_RECEIVE); - HgHookHandler.Response response = send(request); + // we have to capture the pending state, when the hook is fired + // because the state is cleared before the method ends + AtomicReference ref = new AtomicReference<>(Boolean.FALSE); + doAnswer(ic -> { + ref.set(hookEnvironment.isPending()); + return null; + }).when(hookEventHandler).fireHookEvent(RepositoryHookType.PRE_RECEIVE, contextProvider); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.PRE_RECEIVE); + DefaultHookHandler.Response response = send(request); assertSuccess(response, RepositoryHookType.PRE_RECEIVE); - assertThat(hookEnvironment.isPending()).isTrue(); + assertThat(ref.get()).isTrue(); } @Test @@ -121,8 +136,8 @@ class HgHookHandlerTest { .when(hookEventFacade) .handle("42"); - HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); - HgHookHandler.Response response = send(request); + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); assertError(response, "unknown"); } @@ -133,28 +148,40 @@ class HgHookHandlerTest { .when(subject) .login(any(AuthenticationToken.class)); - HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); - HgHookHandler.Response response = send(request); + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); assertError(response, "authentication"); } + @Test + void shouldHandleNotFoundException() throws IOException { + doThrow(NotFoundException.class) + .when(hookEventFacade) + .handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); + + assertError(response, "not found"); + } + @Test void shouldReturnErrorWithInvalidChallenge() throws IOException { - HgHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE, "something-different"); - HgHookHandler.Response response = send(request); + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE, "something-different"); + DefaultHookHandler.Response response = send(request); assertError(response, "challenge"); } - private void assertSuccess(HgHookHandler.Response response, RepositoryHookType type) { + private void assertSuccess(DefaultHookHandler.Response response, RepositoryHookType type) { assertThat(response.getMessages()).isEmpty(); assertThat(response.isAbort()).isFalse(); verify(hookEventHandler).fireHookEvent(eq(type), any(HgHookContextProvider.class)); } - private void assertError(HgHookHandler.Response response, String message) { + private void assertError(DefaultHookHandler.Response response, String message) { assertThat(response.isAbort()).isTrue(); assertThat(response.getMessages()).hasSize(1); HgHookMessage hgHookMessage = response.getMessages().get(0); @@ -163,19 +190,19 @@ class HgHookHandlerTest { } @Nonnull - private HgHookHandler.Request createRequest(RepositoryHookType type) { + private DefaultHookHandler.Request createRequest(RepositoryHookType type) { return createRequest(type, hookEnvironment.getChallenge()); } @Nonnull - private HgHookHandler.Request createRequest(RepositoryHookType type, String challenge) { + private DefaultHookHandler.Request createRequest(RepositoryHookType type, String challenge) { String secret = CipherUtil.getInstance().encode("secret"); - return new HgHookHandler.Request( + return new DefaultHookHandler.Request( secret, type, "42", challenge, "abc" ); } - private HgHookHandler.Response send(HgHookHandler.Request request) throws IOException { + private DefaultHookHandler.Response send(DefaultHookHandler.Request request) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Sockets.send(buffer, request); ByteArrayInputStream input = new ByteArrayInputStream(buffer.toByteArray()); @@ -185,7 +212,7 @@ class HgHookHandlerTest { handler.run(); - return Sockets.read(new ByteArrayInputStream(output.toByteArray()), HgHookHandler.Response.class); + return Sockets.read(new ByteArrayInputStream(output.toByteArray()), DefaultHookHandler.Response.class); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgEnvironmentTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookContextProviderFactoryTest.java similarity index 52% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgEnvironmentTest.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookContextProviderFactoryTest.java index b290eb229a..b5b74509ee 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgEnvironmentTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookContextProviderFactoryTest.java @@ -21,58 +21,50 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -package sonia.scm.repository; +package sonia.scm.repository.hooks; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.security.AccessToken; -import sonia.scm.security.Xsrf; +import sonia.scm.NotFoundException; +import sonia.scm.repository.HgRepositoryFactory; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryTestData; +import sonia.scm.repository.spi.HgHookContextProvider; -import java.util.HashMap; -import java.util.Map; - -import static java.util.Optional.empty; -import static java.util.Optional.of; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -class HgEnvironmentTest { +class HookContextProviderFactoryTest { @Mock - HgRepositoryHandler handler; + private RepositoryManager repositoryManager; + @Mock - HgHookManager hookManager; + private HgRepositoryHandler repositoryHandler; + + @Mock + private HgRepositoryFactory repositoryFactory; + + @InjectMocks + private HookContextProviderFactory factory; @Test - void shouldExtractXsrfTokenWhenSet() { - AccessToken accessToken = mock(AccessToken.class); - when(accessToken.compact()).thenReturn(""); - when(accessToken.getCustom(Xsrf.TOKEN_KEY)).thenReturn(of("XSRF Token")); - when(hookManager.getAccessToken()).thenReturn(accessToken); - - Map environment = new HashMap<>(); - HgEnvironment.prepareEnvironment(environment, handler, hookManager); - - assertThat(environment).contains(entry("SCM_XSRF", "XSRF Token")); + void shouldCreateHookContextProvider() { + when(repositoryManager.get("42")).thenReturn(RepositoryTestData.create42Puzzle()); + HgHookContextProvider provider = factory.create("42", "xyz"); + assertThat(provider).isNotNull(); } @Test - void shouldIgnoreXsrfWhenNotSetButStillContainDummy() { - AccessToken accessToken = mock(AccessToken.class); - when(accessToken.compact()).thenReturn(""); - when(accessToken.getCustom(Xsrf.TOKEN_KEY)).thenReturn(empty()); - when(hookManager.getAccessToken()).thenReturn(accessToken); - - Map environment = new HashMap<>(); - HgEnvironment.prepareEnvironment(environment, handler, hookManager); - - assertThat(environment).containsKeys("SCM_XSRF"); + void shouldThrowNotFoundExceptionWithoutRepository() { + assertThrows(NotFoundException.class, () -> factory.create("42", "xyz")); } + } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java index afd485c477..95467edc25 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java @@ -89,7 +89,7 @@ class HookServerTest { } } - public static class HelloHandler implements Runnable { + public static class HelloHandler implements HookHandler { private final Socket socket; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java index f026169f87..d115123c52 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- @@ -29,13 +29,16 @@ package sonia.scm.repository.spi; import org.junit.After; import org.junit.Before; +import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.RepositoryTestData; +import sonia.scm.repository.hooks.HookEnvironment; import sonia.scm.util.MockUtil; //~--- JDK imports ------------------------------------------------------------ +import java.io.File; import java.io.IOException; /** @@ -49,31 +52,22 @@ public class AbstractHgCommandTestBase extends ZippedRepositoryTestBase * Method description * * - * @throws IOException */ @After - public void close() throws IOException - { + public void close() { if (cmdContext != null) { cmdContext.close(); } } - /** - * Method description - * - * - * @throws IOException - */ @Before public void initHgHandler() throws IOException { this.handler = HgTestUtil.createHandler(tempFolder.newFolder()); - HgTestUtil.checkForSkip(handler); - cmdContext = new HgCommandContext(HgTestUtil.createHookManager(), handler, - RepositoryTestData.createHeartOfGold(), repositoryDirectory); + HgRepositoryFactory factory = HgTestUtil.createFactory(handler, repositoryDirectory); + cmdContext = new HgCommandContext(handler, factory, RepositoryTestData.createHeartOfGold()); } //~--- set methods ---------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java index f3a632fa59..241d416d2c 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgBranchCommandTest.java @@ -29,12 +29,10 @@ import com.google.inject.util.Providers; import org.junit.Before; import org.junit.Test; import sonia.scm.repository.Branch; -import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.api.BranchRequest; import sonia.scm.repository.work.NoneCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; -import sonia.scm.web.HgRepositoryEnvironmentBuilder; import java.util.List; @@ -47,10 +45,8 @@ public class HgBranchCommandTest extends AbstractHgCommandTestBase { @Before public void initWorkingCopyFactory() { - HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder = - new HgRepositoryEnvironmentBuilder(handler, HgTestUtil.createHookManager()); - workingCopyFactory = new SimpleHgWorkingCopyFactory(Providers.of(hgRepositoryEnvironmentBuilder), new NoneCachingWorkingCopyPool(new WorkdirProvider())) { + workingCopyFactory = new SimpleHgWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())) { @Override public void configure(PullCommand pullCommand) { // we do not want to configure http hooks in this unit test diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java index a128e23075..9d04255027 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- @@ -110,17 +110,10 @@ public class HgIncomingCommandTest extends IncomingOutgoingTestBase cmd.getIncomingChangesets(request); } - /** - * Method description - * - * - * @return - */ - private HgIncomingCommand createIncomingCommand() - { + private HgIncomingCommand createIncomingCommand() { return new HgIncomingCommand( - new HgCommandContext( - HgTestUtil.createHookManager(), handler, incomingRepository, - incomingDirectory), handler); + new HgCommandContext(handler, HgTestUtil.createFactory(handler, incomingDirectory), incomingRepository), + handler + ); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java index 914a063790..2e645c04c7 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java @@ -40,12 +40,11 @@ import static org.assertj.core.api.Assertions.assertThat; public class HgModificationsCommandTest extends IncomingOutgoingTestBase { - private HgModificationsCommand outgoingModificationsCommand; @Before public void init() { - HgCommandContext outgoingContext = new HgCommandContext(HgTestUtil.createHookManager(), handler, outgoingRepository, outgoingDirectory); + HgCommandContext outgoingContext = new HgCommandContext(handler, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository); outgoingModificationsCommand = new HgModificationsCommand(outgoingContext); } @@ -116,10 +115,10 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { assertThat(modifications).isNotNull(); assertThat(modifications.getAdded()) .as("added files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getModified()) .as("modified files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRemoved()) .as("removed files modifications") .hasSize(1) @@ -136,10 +135,10 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { assertThat(modifications).isNotNull(); assertThat(modifications.getAdded()) .as("added files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getModified()) .as("modified files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRemoved()) .as("removed files modifications") .isEmpty(); @@ -161,10 +160,10 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { assertThat(modifications).isNotNull(); assertThat(modifications.getAdded()) .as("added files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getModified()) .as("modified files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRemoved()) .as("removed files modifications") .isEmpty(); @@ -189,7 +188,7 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { assertThat(modifications).isNotNull(); assertThat(modifications.getAdded()) .as("added files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getModified()) .as("modified files modifications") .hasSize(1) @@ -197,10 +196,10 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { .containsOnly(file); assertThat(modifications.getRemoved()) .as("removed files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRenamed()) .as("renamed files modifications") - .hasSize(0); + .isEmpty(); }; } @@ -214,13 +213,13 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { .containsOnly(addedFile); assertThat(modifications.getModified()) .as("modified files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRemoved()) .as("removed files modifications") - .hasSize(0); + .isEmpty(); assertThat(modifications.getRenamed()) .as("renamed files modifications") - .hasSize(0); + .isEmpty(); }; } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java index d55005f675..0ccc7d94c6 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModifyCommandTest.java @@ -24,7 +24,6 @@ package sonia.scm.repository.spi; -import com.google.inject.util.Providers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,12 +31,9 @@ import org.junit.rules.TemporaryFolder; import sonia.scm.AlreadyExistsException; import sonia.scm.NoChangesMadeException; import sonia.scm.NotFoundException; -import sonia.scm.repository.HgHookManager; -import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.Person; import sonia.scm.repository.work.NoneCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; -import sonia.scm.web.HgRepositoryEnvironmentBuilder; import java.io.File; import java.io.FileOutputStream; @@ -54,9 +50,7 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase { @Before public void initHgModifyCommand() { - HgHookManager hookManager = HgTestUtil.createHookManager(); - HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); - SimpleHgWorkingCopyFactory workingCopyFactory = new SimpleHgWorkingCopyFactory(Providers.of(environmentBuilder), new NoneCachingWorkingCopyPool(new WorkdirProvider())) { + SimpleHgWorkingCopyFactory workingCopyFactory = new SimpleHgWorkingCopyFactory(new NoneCachingWorkingCopyPool(new WorkdirProvider())) { @Override public void configure(com.aragost.javahg.commands.PullCommand pullCommand) { // we do not want to configure http hooks in this unit test diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java index 192b425e6c..426eff8537 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- @@ -106,17 +106,10 @@ public class HgOutgoingCommandTest extends IncomingOutgoingTestBase System.out.println(cpr.getChangesets()); } - /** - * Method description - * - * - * @return - */ - private HgOutgoingCommand createOutgoingCommand() - { + private HgOutgoingCommand createOutgoingCommand() { return new HgOutgoingCommand( - new HgCommandContext( - HgTestUtil.createHookManager(), handler, outgoingRepository, - outgoingDirectory), handler); + new HgCommandContext(handler, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository), + handler + ); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java new file mode 100644 index 0000000000..6c020efcc0 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java @@ -0,0 +1,137 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.spi; + +import com.google.common.base.Joiner; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgVersion; + +import javax.annotation.Nonnull; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HgVersionCommandTest { + + private static final String PYTHON_OUTPUT = String.join("\n", + "3.9.0 (default, Oct 27 2020, 14:15:17)", + "[Clang 12.0.0 (clang-1200.0.32.21)]" + ); + + private Map outputs; + + @BeforeEach + void setUpOutputs() { + outputs = new HashMap<>(); + } + + @Test + void shouldReturnHgVersion() { + command("/usr/local/bin/hg", HgVersionCommand.HG_ARGS, "5.5.2", 0); + command("/opt/python/bin/python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0); + + HgVersion hgVersion = getVersion("/usr/local/bin/hg", "/opt/python/bin/python"); + assertThat(hgVersion.getMercurial()).isEqualTo("5.5.2"); + assertThat(hgVersion.getPython()).isEqualTo("3.9.0"); + } + + @Test + void shouldReturnUnknownMercurialVersionOnNonZeroExitCode() { + command("hg", HgVersionCommand.HG_ARGS, "", 1); + command("python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0); + + HgVersion hgVersion = getVersion("hg", "python"); + assertThat(hgVersion.getMercurial()).isEqualTo(HgVersion.UNKNOWN); + assertThat(hgVersion.getPython()).isEqualTo("3.9.0"); + } + + @Test + void shouldReturnUnknownPythonVersionOnNonZeroExitCode() { + command("hg", HgVersionCommand.HG_ARGS, "4.4.2", 0); + command("python", HgVersionCommand.PYTHON_ARGS, "", 1); + + HgVersion hgVersion = getVersion("hg", "python"); + assertThat(hgVersion.getMercurial()).isEqualTo("4.4.2"); + assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN); + } + + @Test + void shouldReturnUnknownForInvalidPythonOutput() { + command("hg", HgVersionCommand.HG_ARGS, "1.0.0", 0); + command("python", HgVersionCommand.PYTHON_ARGS, "abcdef", 0); + + HgVersion hgVersion = getVersion("hg", "python"); + assertThat(hgVersion.getMercurial()).isEqualTo("1.0.0"); + assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN); + } + + @Test + void shouldReturnUnknownForIOException() { + HgVersionCommand command = new HgVersionCommand(new HgConfig(), cmd -> { + throw new IOException("failed"); + }); + + HgVersion hgVersion = command.get(); + assertThat(hgVersion.getMercurial()).isEqualTo(HgVersion.UNKNOWN); + assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN); + } + + private Process command(String command, String[] args, String content, int exitValue) { + Process process = mock(Process.class); + when(process.getInputStream()).thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + when(process.exitValue()).thenReturn(exitValue); + + List cmdLine = new ArrayList<>(); + cmdLine.add(command); + cmdLine.addAll(Arrays.asList(args)); + + outputs.put(Joiner.on(' ').join(cmdLine), process); + + return process; + } + + @Nonnull + private HgVersion getVersion(String hg, String python) { + HgConfig config = new HgConfig(); + config.setHgBinary(hg); + config.setPythonBinary(python); + return new HgVersionCommand(config, command -> outputs.get(Joiner.on(' ').join(command))).get(); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java index 6162351148..a5b103ed93 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- @@ -39,7 +39,6 @@ import org.junit.Rule; import org.junit.rules.TemporaryFolder; import sonia.scm.AbstractTestBase; import sonia.scm.repository.HgConfig; -import sonia.scm.repository.HgContext; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgTestUtil; import sonia.scm.user.User; @@ -88,7 +87,6 @@ public abstract class IncomingOutgoingTestBase extends AbstractTestBase when(handler.getDirectory(outgoingRepository.getId())).thenReturn( outgoingDirectory); when(handler.getConfig()).thenReturn(temp.getConfig()); - when(handler.getHgContext()).thenReturn(new HgContext()); } //~--- set methods ---------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java index 8a8164e072..d429371a04 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java @@ -30,12 +30,11 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgEnvironmentBuilder; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.work.SimpleCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; import sonia.scm.repository.work.WorkingCopy; -import sonia.scm.web.HgRepositoryEnvironmentBuilder; import java.io.File; import java.io.IOException; @@ -57,9 +56,7 @@ public class SimpleHgWorkingCopyFactoryTest extends AbstractHgCommandTestBase { @Before public void bindScmProtocol() throws IOException { workdirProvider = new WorkdirProvider(temporaryFolder.newFolder()); - HgHookManager hookManager = HgTestUtil.createHookManager(); - HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); - workingCopyFactory = new SimpleHgWorkingCopyFactory(Providers.of(environmentBuilder), new SimpleCachingWorkingCopyPool(workdirProvider)) { + workingCopyFactory = new SimpleHgWorkingCopyFactory(new SimpleCachingWorkingCopyPool(workdirProvider)) { @Override public void configure(com.aragost.javahg.commands.PullCommand pullCommand) { // we do not want to configure http hooks in this unit test diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java deleted file mode 100644 index b7dd2346c5..0000000000 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.web; - -import org.junit.Test; -import sonia.scm.repository.HgRepositoryHandler; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static sonia.scm.web.HgHookCallbackServlet.PARAM_REPOSITORYID; - -public class HgHookCallbackServletTest { - - @Test - public void shouldExtractCorrectRepositoryId() throws ServletException, IOException { - HgRepositoryHandler handler = mock(HgRepositoryHandler.class); - HgHookCallbackServlet servlet = new HgHookCallbackServlet(null, handler, null, null); - HttpServletRequest request = mock(HttpServletRequest.class); - HttpServletResponse response = mock(HttpServletResponse.class); - - when(request.getContextPath()).thenReturn("http://example.com/scm"); - when(request.getRequestURI()).thenReturn("http://example.com/scm/hook/hg/pretxnchangegroup"); - String path = "/tmp/hg/12345"; - when(request.getParameter(PARAM_REPOSITORYID)).thenReturn(path); - - servlet.doPost(request, response); - - verify(response, never()).sendError(anyInt()); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java index 985c53ddbd..6a7375f925 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java @@ -114,14 +114,14 @@ public class ScmSecurityModule extends ShiroWebModule } // bind constant - bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to( - "/index.html"); + bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/index.html"); // disable access to mustache resources addFilterChain("/**.mustache", filterConfig(ROLES, "nobody")); // disable session addFilterChain("/**", NO_SESSION_CREATION); + bindConstant().annotatedWith(Names.named("shiro.sessionStorageEnabled")).to(false); } /** From d86b2f70c33506fdb7d3baa7afd68dcedb1785d2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 8 Nov 2020 12:23:15 +0100 Subject: [PATCH 03/25] Disable xsrf for mercurial hook tokens --- .../scm/repository/DefaultHgEnvironmentBuilder.java | 12 ++++++++++-- .../repository/DefaultHgEnvironmentBuilderTest.java | 3 ++- .../sonia/scm/security/JwtAccessTokenBuilder.java | 10 +++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java index c4f37bda3b..215025da43 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java @@ -32,6 +32,7 @@ import sonia.scm.repository.hooks.HookServer; import sonia.scm.security.AccessToken; import sonia.scm.security.AccessTokenBuilderFactory; import sonia.scm.security.CipherUtil; +import sonia.scm.security.Xsrf; import sonia.scm.web.HgUtil; import javax.inject.Inject; @@ -109,11 +110,18 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { private void write(ImmutableMap.Builder env) { env.put(ENV_HOOK_PORT, String.valueOf(getHookPort())); - AccessToken accessToken = accessTokenBuilderFactory.create().build(); - env.put(ENV_BEARER_TOKEN, CipherUtil.getInstance().encode(accessToken.compact())); + env.put(ENV_BEARER_TOKEN, accessToken()); env.put(ENV_CHALLENGE, hookEnvironment.getChallenge()); } + private String accessToken() { + AccessToken accessToken = accessTokenBuilderFactory.create() + // disable xsrf protection, because we can not access the http servlet request for verification + .custom(Xsrf.TOKEN_KEY, null) + .build(); + return CipherUtil.getInstance().encode(accessToken.compact()); + } + private synchronized int getHookPort() { if (hookPort > 0) { return hookPort; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java index 53ae99e631..c28bb21e78 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java @@ -38,6 +38,7 @@ import sonia.scm.repository.hooks.HookServer; import sonia.scm.security.AccessToken; import sonia.scm.security.AccessTokenBuilderFactory; import sonia.scm.security.CipherUtil; +import sonia.scm.security.Xsrf; import javax.annotation.Nonnull; import java.io.File; @@ -118,7 +119,7 @@ class DefaultHgEnvironmentBuilderTest { private void applyAccessToken(String compact) { AccessToken accessToken = mock(AccessToken.class); - when(accessTokenBuilderFactory.create().build()).thenReturn(accessToken); + when(accessTokenBuilderFactory.create().custom(Xsrf.TOKEN_KEY, null).build()).thenReturn(accessToken); when(accessToken.compact()).thenReturn(compact); } diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java index 2455a01eba..c0d8521220 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java +++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java @@ -30,6 +30,7 @@ import com.google.common.collect.Maps; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.subject.Subject; @@ -87,8 +88,6 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { @Override public JwtAccessTokenBuilder custom(String key, Object value) { - Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "null or empty value not allowed"); - Preconditions.checkArgument(value != null, "null or empty value not allowed"); this.custom.put(key, value); return this; } @@ -183,8 +182,8 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { if (refreshableFor > 0) { - long refreshExpiration = refreshableForUnit.toMillis(refreshableFor); - claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, new Date(now.toEpochMilli() + refreshExpiration).getTime()); + long re = refreshableForUnit.toMillis(refreshableFor); + claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, new Date(now.toEpochMilli() + re).getTime()); } else if (refreshExpiration != null) { claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, Date.from(refreshExpiration)); } @@ -198,10 +197,11 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { claims.setIssuer(issuer); } + // sign token and create compact version String compact = Jwts.builder() .setClaims(claims) - .signWith(SignatureAlgorithm.HS256, key.getBytes()) + .signWith(Keys.hmacShaKeyFor(key.getBytes()), SignatureAlgorithm.HS256) .compact(); return new JwtAccessToken(claims, compact); From 36aa05986f4123a312c31d48c7d4b2b400e58f2c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 8 Nov 2020 12:25:25 +0100 Subject: [PATCH 04/25] Disable shiro session storage We use JWT for session management, so we can disable shiro session management and this allows usage of SecurityManager outside of a http request. --- .../scm/lifecycle/modules/ScmSecurityModule.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java index 6a7375f925..291bb7c047 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/ScmSecurityModule.java @@ -36,6 +36,9 @@ import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.authz.permission.PermissionResolver; import org.apache.shiro.crypto.hash.DefaultHashService; import org.apache.shiro.guice.web.ShiroWebModule; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.mgt.SubjectDAO; import org.apache.shiro.realm.Realm; import org.slf4j.Logger; @@ -120,8 +123,18 @@ public class ScmSecurityModule extends ShiroWebModule addFilterChain("/**.mustache", filterConfig(ROLES, "nobody")); // disable session + disableSession(); + } + + private void disableSession() { addFilterChain("/**", NO_SESSION_CREATION); bindConstant().annotatedWith(Names.named("shiro.sessionStorageEnabled")).to(false); + + DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator(); + sessionStorageEvaluator.setSessionStorageEnabled(false); + subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator); + bind(SubjectDAO.class).toInstance(subjectDAO); } /** From e774c77b2733059ecc7126b55c4806d5a90ba82c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 8 Nov 2020 12:26:38 +0100 Subject: [PATCH 05/25] Rename mercurial hooks to match python coding specs --- .../repository/spi/SimpleHgWorkingCopyFactory.java | 4 ++-- .../src/main/resources/sonia/scm/python/hgweb.py | 4 ++-- .../main/resources/sonia/scm/python/scmhooks.py | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java index c11e15ee4c..8c01c369ff 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java @@ -99,7 +99,7 @@ public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory Date: Tue, 10 Nov 2020 09:57:53 +0100 Subject: [PATCH 06/25] Improve logging for mercurial hooks --- .../repository/hooks/DefaultHookHandler.java | 5 +++++ .../scm/repository/hooks/HookServer.java | 2 ++ .../sonia/scm/repository/hooks/Sockets.java | 1 + .../scm/repository/spi/HgModifyCommand.java | 22 +++++++++++++++---- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index f4d8ba2a08..b7ebefca3b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -68,11 +68,13 @@ class DefaultHookHandler implements HookHandler { @Override public void run() { + LOG.trace("start handling hook protocol"); try (InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream()) { handleHookRequest(input, output); } catch (IOException e) { LOG.warn("failed to read hook request", e); } finally { + LOG.trace("close client socket"); close(); } } @@ -84,8 +86,10 @@ class DefaultHookHandler implements HookHandler { } private Response handleHookRequest(Request request) { + LOG.trace("process {} hook for node {}", request.getType(), request.getNode()); try { if (!environment.isAcceptAble(request.getChallenge())) { + LOG.warn("received hook with invalid challenge: {}", request.getChallenge()); return error("invalid hook challenge"); } @@ -111,6 +115,7 @@ class DefaultHookHandler implements HookHandler { } private void authenticate(Request request) { + LOG.trace("authenticate hook request"); String token = CipherUtil.getInstance().decode(request.getToken()); BearerToken bearer = BearerToken.valueOf(token); Subject subject = SecurityUtils.getSubject(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java index 40fb4a68e3..ac27323018 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java @@ -96,6 +96,7 @@ public class HookServer implements AutoCloseable { acceptor.submit(() -> { while (!serverSocket.isClosed()) { try { + LOG.trace("wait for next hook connection"); Socket clientSocket = serverSocket.accept(); LOG.trace("accept incoming hook client from {}", clientSocket.getInetAddress()); HookHandler hookHandler = handlerFactory.create(clientSocket); @@ -104,6 +105,7 @@ public class HookServer implements AutoCloseable { LOG.debug("failed to accept socket, possible closed", ex); } } + LOG.warn("ServerSocket is closed"); }); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java index e6b748643b..9791e99c31 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java @@ -42,6 +42,7 @@ class Sockets { byte[] bytes = objectMapper.writeValueAsBytes(object); out.write(bytes); out.write('\0'); + out.flush(); } static T read(InputStream in, Class type) throws IOException { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java index 012592c7a4..10e444b03a 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgModifyCommand.java @@ -31,6 +31,8 @@ import com.aragost.javahg.commands.ExecutionException; import com.aragost.javahg.commands.PullCommand; import com.aragost.javahg.commands.RemoveCommand; import com.aragost.javahg.commands.StatusCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.NoChangesMadeException; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.work.WorkingCopy; @@ -40,9 +42,12 @@ import java.io.IOException; import java.nio.file.Path; import java.util.List; +@SuppressWarnings("java:S3252") // it is ok for javahg classes to access static method of subtype public class HgModifyCommand implements ModifyCommand { - private HgCommandContext context; + private static final Logger LOG = LoggerFactory.getLogger(HgModifyCommand.class); + + private final HgCommandContext context; private final HgWorkingCopyFactory workingCopyFactory; public HgModifyCommand(HgCommandContext context, HgWorkingCopyFactory workingCopyFactory) { @@ -52,7 +57,6 @@ public class HgModifyCommand implements ModifyCommand { @Override public String execute(ModifyCommandRequest request) { - try (WorkingCopy workingCopy = workingCopyFactory.createWorkingCopy(context, request.getBranch())) { Repository workingRepository = workingCopy.getWorkingRepository(); request.getRequests().forEach( @@ -97,12 +101,21 @@ public class HgModifyCommand implements ModifyCommand { } } ); + if (StatusCommand.on(workingRepository).lines().isEmpty()) { throw new NoChangesMadeException(context.getScmRepository()); } - CommitCommand.on(workingRepository).user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())).message(request.getCommitMessage()).execute(); + + LOG.trace("commit changes in working copy"); + CommitCommand.on(workingRepository) + .user(String.format("%s <%s>", request.getAuthor().getName(), request.getAuthor().getMail())) + .message(request.getCommitMessage()).execute(); + List execute = pullModifyChangesToCentralRepository(request, workingCopy); - return execute.get(0).getNode(); + + String node = execute.get(0).getNode(); + LOG.debug("successfully pulled changes from working copy, new node {}", node); + return node; } catch (ExecutionException e) { throwInternalRepositoryException("could not execute command on repository", e); return null; @@ -110,6 +123,7 @@ public class HgModifyCommand implements ModifyCommand { } private List pullModifyChangesToCentralRepository(ModifyCommandRequest request, WorkingCopy workingCopy) { + LOG.trace("pull changes from working copy"); try { com.aragost.javahg.commands.PullCommand pullCommand = PullCommand.on(workingCopy.getCentralRepository()); workingCopyFactory.configure(pullCommand); From 51182945242b6af9fc13e407db41bd098560a9ac Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 09:58:55 +0100 Subject: [PATCH 07/25] Fix multiple accessor threads --- .../java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java index 215025da43..597a56af15 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java @@ -36,10 +36,12 @@ import sonia.scm.security.Xsrf; import sonia.scm.web.HgUtil; import javax.inject.Inject; +import javax.inject.Singleton; import java.io.File; import java.io.IOException; import java.util.Map; +@Singleton public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { @VisibleForTesting From e038eebde653f690c849204a281a195329be205f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 10:01:16 +0100 Subject: [PATCH 08/25] Improve connection handling of mercurial hook python client --- .../resources/sonia/scm/python/scmhooks.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 9704fe156e..8e89bb1f06 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -44,19 +44,20 @@ def print_messages(ui, messages): def fire_hook(ui, repo, hooktype, node): abort = True + ui.debug( b"send scm-hook for " + node + b"\n" ) + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: values = {'token': token, 'type': hooktype, 'repositoryId': repositoryId, 'challenge': challenge, 'node': node.decode('utf8') } - ui.debug( b"send scm-hook for " + node + b"\n" ) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect(("127.0.0.1", int(port))) - s.sendall(json.dumps(values).encode('utf-8')) - s.sendall(b'\0') + + connection.connect(("127.0.0.1", int(port))) + connection.send(json.dumps(values).encode('utf-8')) + connection.sendall(b'\0') received = [] - byte = s.recv(1) + byte = connection.recv(1) while byte != b'\0': received.append(byte) - byte = s.recv(1) + byte = connection.recv(1) message = b''.join(received).decode('utf-8') response = json.loads(message) @@ -67,6 +68,8 @@ def fire_hook(ui, repo, hooktype, node): except ValueError: ui.warn( b"scm-hook failed with an exception\n" ) ui.traceback() + finally: + connection.close() return abort def callback(ui, repo, hooktype, node=None): @@ -103,3 +106,4 @@ def pre_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs) def post_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): return callback(ui, repo, "POST_RECEIVE", node) + From 70969b5161c73b8f032abb49fdabf53a3c05c749 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 10:07:15 +0100 Subject: [PATCH 09/25] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 695fa92aa3..a464d82ad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Send mercurial hook callbacks over separate tcp socket instead of http ([#1415](https://github.com/scm-manager/scm-manager/pull/1415)) + ### Fixed - Error on repository initialization with least-privilege user ([#1414](https://github.com/scm-manager/scm-manager/pull/1414)) From 8b4f92fa5c76e1c5d8807fb330b27df9c09ad619 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 10:14:40 +0100 Subject: [PATCH 10/25] Remove version script because it is not longer required --- .../sonia/scm/repository/HgPythonScript.java | 66 +++---------------- .../resources/sonia/scm/python/version.py | 45 ------------- 2 files changed, 8 insertions(+), 103 deletions(-) delete mode 100644 scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/version.py diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgPythonScript.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgPythonScript.java index a7c5ef6069..0e4b05b101 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgPythonScript.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgPythonScript.java @@ -38,80 +38,30 @@ import java.io.File; */ public enum HgPythonScript { - HOOK("scmhooks.py"), HGWEB("hgweb.py"), VERSION("version.py"); + HOOK("scmhooks.py"), HGWEB("hgweb.py"); - /** Field description */ - private static final String BASE_DIRECTORY = - "lib".concat(File.separator).concat("python"); - - /** Field description */ + private static final String BASE_DIRECTORY = "lib".concat(File.separator).concat("python"); private static final String BASE_RESOURCE = "/sonia/scm/python/"; - //~--- constructors --------------------------------------------------------- + private final String name; - /** - * Constructs ... - * - * - * @param name - */ - private HgPythonScript(String name) - { + HgPythonScript(String name) { this.name = name; } - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param context - * - * @return - */ - public static File getScriptDirectory(SCMContextProvider context) - { + public static File getScriptDirectory(SCMContextProvider context) { return new File(context.getBaseDirectory(), BASE_DIRECTORY); } - /** - * Method description - * - * - * @param context - * - * @return - */ - public File getFile(SCMContextProvider context) - { + public File getFile(SCMContextProvider context) { return new File(getScriptDirectory(context), name); } - /** - * Method description - * - * - * @return - */ - public String getName() - { + public String getName() { return name; } - /** - * Method description - * - * - * @return - */ - public String getResourcePath() - { + public String getResourcePath() { return BASE_RESOURCE.concat(name); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String name; } diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/version.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/version.py deleted file mode 100644 index 25c35fe8a2..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/version.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# MIT License -# -# Copyright (c) 2020-present Cloudogu GmbH and Contributors -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -import sys -from mercurial import util -from xml.dom.minidom import Document - -pyVersion = sys.version_info -pyVersion = str(pyVersion.major) + "." + str(pyVersion.minor) + "." + str(pyVersion.micro) -hgVersion = util.version() - -doc = Document() -root = doc.createElement('version') - -pyNode = doc.createElement('python') -pyNode.appendChild(doc.createTextNode(pyVersion)) -root.appendChild(pyNode) - -hgNode = doc.createElement('mercurial') -hgNode.appendChild(doc.createTextNode(hgVersion)) -root.appendChild(hgNode) - -doc.appendChild(root) -doc.writexml(sys.stdout, encoding='UTF-8') \ No newline at end of file From 3fe678e4a0a9da71afa3fbdca90e39aa65bb398e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 10:18:03 +0100 Subject: [PATCH 11/25] Remove disableHookSSLValidation field from mercurial settings form --- scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.tsx | 2 -- .../scm-hg-plugin/src/main/resources/locales/de/plugins.json | 2 -- .../scm-hg-plugin/src/main/resources/locales/en/plugins.json | 2 -- 3 files changed, 6 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.tsx b/scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.tsx index 6b72cb9477..bbcf11c012 100644 --- a/scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.tsx +++ b/scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.tsx @@ -33,7 +33,6 @@ type Configuration = { encoding: string; useOptimizedBytecode: boolean; showRevisionInId: boolean; - disableHookSSLValidation: boolean; enableHttpPostArgs: boolean; _links: Links; }; @@ -139,7 +138,6 @@ class HgConfigurationForm extends React.Component { {this.checkbox("showRevisionInId")}
- {this.checkbox("disableHookSSLValidation")} {this.checkbox("enableHttpPostArgs")}
diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json b/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json index 6a4a639812..2d4b08cd2d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json +++ b/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json @@ -25,8 +25,6 @@ "showRevisionInIdHelpText": "Die Revision als Teil der Node ID anzeigen.", "enableHttpPostArgs": "HttpPostArgs Protocol aktivieren", "enableHttpPostArgsHelpText": "Aktiviert das experimentelle HttpPostArgs Protokoll von Mercurial. Das HttpPostArgs Protokoll verwendet den Post Request Body anstatt des HTTP Headers um Meta Informationen zu versenden. Dieses Vorgehen reduziert die Header Größe der Mercurial Requests. HttpPostArgs wird seit Mercurial 3.8 unterstützt.", - "disableHookSSLValidation": "SSL Validierung für Hooks deaktivieren", - "disableHookSSLValidationHelpText": "Deaktiviert die Validierung von SSL Zertifikaten für den Mercurial Hook, der die Repositoryänderungen wieder zurück an den SCM-Manager leitet. Diese Option sollte nur benutzt werden, wenn der SCM-Manager ein selbstsigniertes Zertifikat verwendet.", "disabled": "Deaktiviert", "disabledHelpText": "Aktiviert oder deaktiviert das Mercurial Plugin.", "required": "Dieser Konfigurationswert wird benötigt" diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json b/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json index 87f4d80ac4..9082862174 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json +++ b/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json @@ -25,8 +25,6 @@ "showRevisionInIdHelpText": "Show revision as part of the node id.", "enableHttpPostArgs": "Enable HttpPostArgs Protocol", "enableHttpPostArgsHelpText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8.", - "disableHookSSLValidation": "Disable SSL Validation on Hooks", - "disableHookSSLValidationHelpText": "Disables the validation of ssl certificates for the mercurial hook, which forwards the repository changes back to scm-manager. This option should only be used, if SCM-Manager uses a self signed certificate.", "disabled": "Disabled", "disabledHelpText": "Enable or disable the Mercurial plugin.", "required": "This configuration value is required" From 84aa8b4bde6ccaa8aaca6cd29dd5875045a84ee5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Nov 2020 11:03:00 +0100 Subject: [PATCH 12/25] Update pull request link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a464d82ad0..83c05e5423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changed -- Send mercurial hook callbacks over separate tcp socket instead of http ([#1415](https://github.com/scm-manager/scm-manager/pull/1415)) +- Send mercurial hook callbacks over separate tcp socket instead of http ([#1416](https://github.com/scm-manager/scm-manager/pull/1416)) ### Fixed - Error on repository initialization with least-privilege user ([#1414](https://github.com/scm-manager/scm-manager/pull/1414)) From c2df6d2372ea73aed5851db8c3022d8dde4192ac Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 18 Nov 2020 10:44:28 +0100 Subject: [PATCH 13/25] Update storybook components to be compatible with jest 26 --- scm-ui/ui-components/package.json | 9 +- yarn.lock | 2052 +++++++++++++++++------------ 2 files changed, 1219 insertions(+), 842 deletions(-) diff --git a/scm-ui/ui-components/package.json b/scm-ui/ui-components/package.json index c78531db56..8990acba03 100644 --- a/scm-ui/ui-components/package.json +++ b/scm-ui/ui-components/package.json @@ -19,9 +19,9 @@ }, "devDependencies": { "@scm-manager/ui-tests": "^2.10.0-SNAPSHOT", - "@storybook/addon-actions": "^5.2.3", - "@storybook/addon-storyshots": "^5.2.3", - "@storybook/react": "^5.2.3", + "@storybook/addon-actions": "^6.0.28", + "@storybook/addon-storyshots": "^6.0.28", + "@storybook/react": "^6.0.28", "@types/classnames": "^2.2.9", "@types/css": "^0.0.31", "@types/enzyme": "^3.10.3", @@ -35,7 +35,6 @@ "@types/react-select": "^2.0.19", "@types/react-syntax-highlighter": "^11.0.1", "@types/refractor": "^3.0.0", - "@types/storybook__addon-storyshots": "^5.1.1", "@types/styled-components": "^5.1.0", "@types/to-camel-case": "^1.0.0", "css": "^3.0.0", @@ -45,7 +44,7 @@ "i18next-fetch-backend": "^2.2.0", "raf": "^3.4.0", "react-test-renderer": "^16.10.2", - "storybook-addon-i18next": "^1.2.1", + "storybook-addon-i18next": "^1.3.0", "to-camel-case": "^1.0.0", "typescript": "^3.7.2", "worker-plugin": "^3.2.0" diff --git a/yarn.lock b/yarn.lock index 9b752f6242..bb5ab73e0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" - "@babel/code-frame@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -32,7 +25,12 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.6.3", "@babel/core@^7.7.5": +"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.5.tgz#f56db0c4bb1bbbf221b4e81345aab4141e7cb0e9" + integrity sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg== + +"@babel/core@^7.1.0", "@babel/core@^7.6.3", "@babel/core@^7.7.5": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== @@ -54,6 +52,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.12.3": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.11.5", "@babel/generator@^7.11.6", "@babel/generator@^7.4.0": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" @@ -63,6 +83,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.12.1", "@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -87,6 +116,15 @@ "@babel/helper-module-imports" "^7.10.4" "@babel/types" "^7.11.5" +"@babel/helper-builder-react-jsx-experimental@^7.12.1": + version "7.12.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz#55fc1ead5242caa0ca2875dcb8eed6d311e50f48" + integrity sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-module-imports" "^7.12.1" + "@babel/types" "^7.12.1" + "@babel/helper-builder-react-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" @@ -106,6 +144,16 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/helper-compilation-targets@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831" + integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== + dependencies: + "@babel/compat-data" "^7.12.5" + "@babel/helper-validator-option" "^7.12.1" + browserslist "^4.14.5" + semver "^5.5.0" + "@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" @@ -118,6 +166,17 @@ "@babel/helper-replace-supers" "^7.10.4" "@babel/helper-split-export-declaration" "^7.10.4" +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/helper-create-regexp-features-plugin@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" @@ -127,6 +186,15 @@ "@babel/helper-regex" "^7.10.4" regexpu-core "^4.7.0" +"@babel/helper-create-regexp-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz#18b1302d4677f9dc4740fe8c9ed96680e29d37e8" + integrity sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.1" + "@babel/helper-define-map@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" @@ -173,6 +241,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-member-expression-to-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz#fba0f2fcff3fba00e6ecb664bb5e6e26e2d6165c" + integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" @@ -180,6 +255,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-module-imports@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + "@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" @@ -193,6 +275,21 @@ "@babel/types" "^7.11.0" lodash "^4.17.19" +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + "@babel/helper-optimise-call-expression@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" @@ -222,6 +319,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-remap-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" + integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/types" "^7.12.1" + "@babel/helper-replace-supers@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" @@ -232,6 +338,16 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-replace-supers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz#f009a17543bbbbce16b06206ae73b63d3fca68d9" + integrity sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + "@babel/helper-simple-access@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" @@ -240,6 +356,13 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-skip-transparent-expression-wrappers@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" @@ -247,6 +370,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" @@ -259,6 +389,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-option@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" + integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== + "@babel/helper-wrap-function@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" @@ -278,7 +413,16 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4", "@babel/highlight@^7.8.3": +"@babel/helpers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== @@ -292,6 +436,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.12.3", "@babel/parser@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" + integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -301,7 +450,16 @@ "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.5.5", "@babel/plugin-proposal-class-properties@^7.7.0", "@babel/plugin-proposal-class-properties@^7.8.3": +"@babel/plugin-proposal-async-generator-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" + integrity sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.5.5", "@babel/plugin-proposal-class-properties@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== @@ -309,6 +467,14 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-decorators@^7.8.3": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" @@ -326,6 +492,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" +"@babel/plugin-proposal-dynamic-import@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc" + integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-proposal-export-default-from@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.10.4.tgz#08f66eef0067cbf6a7bc036977dcdccecaf0c6c5" @@ -342,6 +516,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-proposal-export-namespace-from@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz#8b9b8f376b2d88f5dd774e4d24a5cc2e3679b6d4" + integrity sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-proposal-json-strings@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" @@ -350,6 +532,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-json-strings@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz#d45423b517714eedd5621a9dfdc03fa9f4eb241c" + integrity sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-proposal-logical-assignment-operators@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" @@ -358,6 +548,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751" + integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" @@ -366,6 +564,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c" + integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-proposal-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" @@ -374,7 +580,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.9.6": +"@babel/plugin-proposal-numeric-separator@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz#b1ce757156d40ed79d59d467cb2b154a5c4149ba" + integrity sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.9.6": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== @@ -383,6 +597,15 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.10.4" +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" @@ -391,6 +614,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" +"@babel/plugin-proposal-optional-catch-binding@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz#ccc2421af64d3aae50b558a71cede929a5ab2942" + integrity sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-proposal-optional-chaining@^7.10.1", "@babel/plugin-proposal-optional-chaining@^7.11.0", "@babel/plugin-proposal-optional-chaining@^7.6.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" @@ -400,6 +631,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" +"@babel/plugin-proposal-optional-chaining@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz#cce122203fc8a32794296fc377c6dedaf4363797" + integrity sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-proposal-private-methods@^7.10.4", "@babel/plugin-proposal-private-methods@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" @@ -408,6 +648,14 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-private-methods@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389" + integrity sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex@^7.10.4", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" @@ -416,6 +664,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-unicode-property-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz#2a183958d417765b9eae334f47758e5d6a82e072" + integrity sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -437,6 +693,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators@^7.10.4", "@babel/plugin-syntax-decorators@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c" @@ -444,7 +707,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-dynamic-import@^7", "@babel/plugin-syntax-dynamic-import@^7.2.0", "@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7", "@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -493,6 +756,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-jsx@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -542,7 +812,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-top-level-await@^7.12.1", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0" integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== @@ -563,6 +833,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-arrow-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" + integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-async-to-generator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" @@ -572,6 +849,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-remap-async-to-generator" "^7.10.4" +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" + integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" @@ -579,6 +865,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoped-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9" + integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-block-scoping@^7.10.4", "@babel/plugin-transform-block-scoping@^7.8.3": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" @@ -586,6 +879,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoping@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz#f0ee727874b42a208a48a586b84c3d222c2bbef1" + integrity sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-classes@^7.10.4", "@babel/plugin-transform-classes@^7.9.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" @@ -600,6 +900,20 @@ "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6" + integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" @@ -607,6 +921,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-computed-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852" + integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-destructuring@^7.10.4", "@babel/plugin-transform-destructuring@^7.9.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" @@ -614,6 +935,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" @@ -622,6 +950,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-dotall-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz#a1d16c14862817b6409c0a678d6f9373ca9cd975" + integrity sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-duplicate-keys@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" @@ -629,6 +965,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-duplicate-keys@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz#745661baba295ac06e686822797a69fbaa2ca228" + integrity sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" @@ -637,6 +980,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-exponentiation-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz#b0f2ed356ba1be1428ecaf128ff8a24f02830ae0" + integrity sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-flow-strip-types@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.4.tgz#c497957f09e86e3df7296271e9eb642876bf7788" @@ -652,6 +1003,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-for-of@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa" + integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-function-name@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" @@ -660,6 +1018,14 @@ "@babel/helper-function-name" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-function-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667" + integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-literals@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" @@ -667,6 +1033,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57" + integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" @@ -674,6 +1047,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-member-expression-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad" + integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-modules-amd@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" @@ -683,6 +1063,15 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-amd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz#3154300b026185666eebb0c0ed7f8415fefcf6f9" + integrity sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" @@ -693,6 +1082,16 @@ "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" @@ -703,6 +1102,17 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz#663fea620d593c93f214a464cd399bf6dc683086" + integrity sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-identifier" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-umd@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" @@ -711,6 +1121,14 @@ "@babel/helper-module-transforms" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-modules-umd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz#eb5a218d6b1c68f3d6217b8fa2cc82fec6547902" + integrity sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" @@ -718,6 +1136,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.10.4" +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz#b407f5c96be0d9f5f88467497fa82b30ac3e8753" + integrity sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/plugin-transform-new-target@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" @@ -725,6 +1150,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-new-target@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz#80073f02ee1bb2d365c3416490e085c95759dec0" + integrity sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-object-super@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" @@ -733,6 +1165,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-replace-supers" "^7.10.4" +"@babel/plugin-transform-object-super@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e" + integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/plugin-transform-parameters@^7.10.4", "@babel/plugin-transform-parameters@^7.9.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" @@ -741,6 +1181,13 @@ "@babel/helper-get-function-arity" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-property-literals@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" @@ -748,10 +1195,17 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-constant-elements@^7.0.0", "@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.10.4.tgz#0f485260bf1c29012bb973e7e404749eaac12c9e" - integrity sha512-cYmQBW1pXrqBte1raMkAulXmi7rjg3VI6ZLg9QIic8Hq7BtYXaWuZSxsr2siOMI6SWwpxjWfnwhTUrd7JlAV7g== +"@babel/plugin-transform-property-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd" + integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-constant-elements@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz#4471f0851feec3231cc9aaa0dccde39947c1ac1e" + integrity sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA== dependencies: "@babel/helper-plugin-utils" "^7.10.4" @@ -762,6 +1216,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-react-display-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d" + integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-jsx-development@^7.10.4": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.11.5.tgz#e1439e6a57ee3d43e9f54ace363fb29cefe5d7b6" @@ -771,6 +1232,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx-development@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.5.tgz#677de5b96da310430d6cfb7fee16a1603afa3d56" + integrity sha512-1JJusg3iPgsZDthyWiCr3KQiGs31ikU/mSf2N2dSYEAO0GEImmVUbWf0VoSDGDFTAn5Dj4DUiR6SdIXHY7tELA== + dependencies: + "@babel/helper-builder-react-jsx-experimental" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/plugin-transform-react-jsx-self@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369" @@ -779,6 +1249,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx-self@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz#ef43cbca2a14f1bd17807dbe4376ff89d714cf28" + integrity sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-jsx-source@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4" @@ -787,6 +1264,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx-source@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz#d07de6863f468da0809edcf79a1aa8ce2a82a26b" + integrity sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" @@ -797,6 +1281,16 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.5.tgz#39ede0e30159770561b6963be143e40af3bde00c" + integrity sha512-2xkcPqqrYiOQgSlM/iwto1paPijjsDbUynN13tI6bosDz/jOW3CRzYguIE8wKX32h+msbBM22Dv5fwrFkUOZjQ== + dependencies: + "@babel/helper-builder-react-jsx" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/plugin-transform-react-pure-annotations@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" @@ -805,6 +1299,14 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-react-pure-annotations@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42" + integrity sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-regenerator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" @@ -812,6 +1314,13 @@ dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-regenerator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz#5f0a28d842f6462281f06a964e88ba8d7ab49753" + integrity sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng== + dependencies: + regenerator-transform "^0.14.2" + "@babel/plugin-transform-reserved-words@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" @@ -819,6 +1328,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-reserved-words@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz#6fdfc8cc7edcc42b36a7c12188c6787c873adcd8" + integrity sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-shorthand-properties@^7.10.4", "@babel/plugin-transform-shorthand-properties@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" @@ -826,6 +1342,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-shorthand-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3" + integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-spread@^7.11.0", "@babel/plugin-transform-spread@^7.8.3": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" @@ -834,6 +1357,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" +"@babel/plugin-transform-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e" + integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-transform-sticky-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" @@ -842,6 +1373,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-regex" "^7.10.4" +"@babel/plugin-transform-sticky-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz#5c24cf50de396d30e99afc8d1c700e8bce0f5caf" + integrity sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + "@babel/plugin-transform-template-literals@^7.10.4", "@babel/plugin-transform-template-literals@^7.8.3": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" @@ -850,6 +1389,13 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-template-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843" + integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typeof-symbol@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" @@ -857,6 +1403,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typeof-symbol@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz#9ca6be343d42512fbc2e68236a82ae64bc7af78a" + integrity sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript@^7.10.4": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" @@ -873,6 +1426,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-escapes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709" + integrity sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-unicode-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" @@ -881,6 +1441,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz#cc9661f61390db5c65e3febaccefd5c6ac3faecb" + integrity sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/polyfill@^7.6.0": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.11.5.tgz#df550b2ec53abbc2ed599367ec59e64c7a707bb5" @@ -889,7 +1457,79 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.6.3", "@babel/preset-env@^7.9.6": +"@babel/preset-env@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" + integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== + dependencies: + "@babel/compat-data" "^7.12.1" + "@babel/helper-compilation-targets" "^7.12.1" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-proposal-async-generator-functions" "^7.12.1" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-dynamic-import" "^7.12.1" + "@babel/plugin-proposal-export-namespace-from" "^7.12.1" + "@babel/plugin-proposal-json-strings" "^7.12.1" + "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.12.1" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-computed-properties" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-dotall-regex" "^7.12.1" + "@babel/plugin-transform-duplicate-keys" "^7.12.1" + "@babel/plugin-transform-exponentiation-operator" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-function-name" "^7.12.1" + "@babel/plugin-transform-literals" "^7.12.1" + "@babel/plugin-transform-member-expression-literals" "^7.12.1" + "@babel/plugin-transform-modules-amd" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-modules-systemjs" "^7.12.1" + "@babel/plugin-transform-modules-umd" "^7.12.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" + "@babel/plugin-transform-new-target" "^7.12.1" + "@babel/plugin-transform-object-super" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-property-literals" "^7.12.1" + "@babel/plugin-transform-regenerator" "^7.12.1" + "@babel/plugin-transform-reserved-words" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-unicode-escapes" "^7.12.1" + "@babel/plugin-transform-unicode-regex" "^7.12.1" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.12.1" + core-js-compat "^3.6.2" + semver "^5.5.0" + +"@babel/preset-env@^7.6.3", "@babel/preset-env@^7.9.6": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA== @@ -995,6 +1635,19 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" +"@babel/preset-react@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.5.tgz#d45625f65d53612078a43867c5c6750e78772c56" + integrity sha512-jcs++VPrgyFehkMezHtezS2BpnUlR7tQFAyesJn1vGTO9aTFZrgIQrA5YydlTwxbcjMwkFY6i04flCigRRr3GA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-display-name" "^7.12.1" + "@babel/plugin-transform-react-jsx" "^7.12.5" + "@babel/plugin-transform-react-jsx-development" "^7.12.5" + "@babel/plugin-transform-react-jsx-self" "^7.12.1" + "@babel/plugin-transform-react-jsx-source" "^7.12.1" + "@babel/plugin-transform-react-pure-annotations" "^7.12.1" + "@babel/preset-typescript@^7.6.0", "@babel/preset-typescript@^7.9.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" @@ -1022,7 +1675,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1053,6 +1706,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095" + integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.5" + "@babel/types" "^7.12.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.7.0": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" @@ -1062,6 +1730,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.1", "@babel/types@^7.12.5", "@babel/types@^7.12.6": + version "7.12.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" + integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -2789,35 +3466,39 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@storybook/addon-actions@^5.2.3": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.3.21.tgz#16eed3eb24996adfcbf70bd476a261324d6de593" - integrity sha512-6SAF/j8UBZaAbRz/rYUlcCXda+c4LQvvNlbVJc9GHjNNNMJQQVc3/EU+M7PyFz6uDUxudAW1+AFchGk04ACJ2g== +"@storybook/addon-actions@^6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.0.28.tgz#86c0353b597c2a41f6ac7efa7c0d5aeed6a456b2" + integrity sha512-DrApIt/rMCBrynJsFbmXPba2BCAj+yOybCsr0cqNNMkaZytAnoeDEqseCdWUtNXSgDnprAQ0HIrS2TIf4twh8w== dependencies: - "@storybook/addons" "5.3.21" - "@storybook/api" "5.3.21" - "@storybook/client-api" "5.3.21" - "@storybook/components" "5.3.21" - "@storybook/core-events" "5.3.21" - "@storybook/theming" "5.3.21" + "@storybook/addons" "6.0.28" + "@storybook/api" "6.0.28" + "@storybook/client-api" "6.0.28" + "@storybook/components" "6.0.28" + "@storybook/core-events" "6.0.28" + "@storybook/theming" "6.0.28" core-js "^3.0.1" - fast-deep-equal "^2.0.1" + fast-deep-equal "^3.1.1" global "^4.3.2" - polished "^3.3.1" + lodash "^4.17.15" + polished "^3.4.4" prop-types "^15.7.2" react "^16.8.3" - react-inspector "^4.0.0" - uuid "^3.3.2" + react-inspector "^5.0.1" + regenerator-runtime "^0.13.3" + ts-dedent "^1.1.1" + util-deprecate "^1.0.2" + uuid "^8.0.0" -"@storybook/addon-storyshots@*": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.0.22.tgz#96e201be5f108c347a9b129cb6a43a26480f593f" - integrity sha512-l4h5OTwbORwdi2GxAOzCF3avmGwbxdJFmXgagzq74iHooCE/H7FF3VfY1vu3ZXWz5LTqe5INNJyfhwHxocntpg== +"@storybook/addon-storyshots@^6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.0.28.tgz#689dbe3e848dac7cdc878bcb5e29b213ab356deb" + integrity sha512-5tOKVlCJHVzDesYCwSBW6oOHmmOli2Lo2Rup6ygMq6ZYQUfwyrtA352WzZpW9MKH2VryxKrd0jNrcxVVMDDHFA== dependencies: "@jest/transform" "^26.0.0" - "@storybook/addons" "6.0.22" - "@storybook/client-api" "6.0.22" - "@storybook/core" "6.0.22" + "@storybook/addons" "6.0.28" + "@storybook/client-api" "6.0.28" + "@storybook/core" "6.0.28" "@types/glob" "^7.1.1" "@types/jest" "^25.1.1" "@types/jest-specific-snapshot" "^0.5.3" @@ -2831,28 +3512,22 @@ regenerator-runtime "^0.13.3" ts-dedent "^1.1.1" -"@storybook/addon-storyshots@^5.2.3": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-5.3.21.tgz#f9536145642cf590b73151a0187671991a7c0057" - integrity sha512-uYXlDNZ9l1w1eOh8G1RT+/FOH33qLw00CnhArsWpy+pnckiL7SKvHaZcQPjkFliatDUr1jUHpLBea78B1fKV1g== +"@storybook/addons@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.0.28.tgz#8c7ef3229706e2dc32d40ec9158431a3ffee3e5b" + integrity sha512-3Brf9w/u2sw0huarcO1iLFBmGt7KtApRxX4bFcsRiPPFxliDrmb1sAsd37UbKp8qBSw1U6FByjuTJQ7xn/TDhg== dependencies: - "@jest/transform" "^24.9.0" - "@storybook/addons" "5.3.21" - "@storybook/client-api" "5.3.21" - "@storybook/core" "5.3.21" - "@types/glob" "^7.1.1" - "@types/jest" "^24.0.16" - "@types/jest-specific-snapshot" "^0.5.3" - babel-plugin-require-context-hook "^1.0.0" + "@storybook/api" "6.0.28" + "@storybook/channels" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/core-events" "6.0.28" + "@storybook/router" "6.0.28" + "@storybook/theming" "6.0.28" core-js "^3.0.1" - glob "^7.1.3" global "^4.3.2" - jest-specific-snapshot "^2.0.0" - read-pkg-up "^7.0.0" regenerator-runtime "^0.13.3" - ts-dedent "^1.1.0" -"@storybook/addons@5.3.21", "@storybook/addons@^5.3.17": +"@storybook/addons@^5.3.17": version "5.3.21" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.3.21.tgz#ee312c738c33e8c34dc11777ef93522c3c36e56a" integrity sha512-Ji/21WADTLVbTbiKcZ64BcL0Es+h1Afxx3kNmGJqPSTUYroCwIFCT9mUzCqU6G+YyWaISAmTii5UJkTwMkChwA== @@ -2865,21 +3540,6 @@ global "^4.3.2" util-deprecate "^1.0.2" -"@storybook/addons@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.0.22.tgz#90958365dcd16cd1f71dcf1f7497c6554293b6a5" - integrity sha512-D7GfOZ16DAyIUoNXY/aisKlXxHlk61XDIAvN102n/GGrmiNQhCKO2cuwjrmpqQGIXW/+QAsc0YUUAptEKpw9vw== - dependencies: - "@storybook/api" "6.0.22" - "@storybook/channels" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/core-events" "6.0.22" - "@storybook/router" "6.0.22" - "@storybook/theming" "6.0.22" - core-js "^3.0.1" - global "^4.3.2" - regenerator-runtime "^0.13.3" - "@storybook/api@5.3.21": version "5.3.21" resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.3.21.tgz#8f1772de53b65e1a65d2f0257463d621a8617c58" @@ -2906,19 +3566,19 @@ telejson "^3.2.0" util-deprecate "^1.0.2" -"@storybook/api@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.0.22.tgz#ef6bbb4f06036cf09bb355fc5fd41d16ead37e23" - integrity sha512-GfGRXAe0h5cFTwJUJ7XqhaaE4+aXk/f+QCWfuUQkipUsGhGL+KLY80OU5cqC7LDB2nbhZ2bKUaLCzXu1Qsw5pw== +"@storybook/api@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.0.28.tgz#ec87494e982240e2ccc2390f79d3100cc5a8f4da" + integrity sha512-6dWjz5HSM3v8c+J0Z/lGYGujs4ZKSt1lJUWaH/MPvuoeDiVm+1jKLwWTAMlMnOP9o8xgNTuQtEbQkNG2D66oQA== dependencies: "@reach/router" "^1.3.3" - "@storybook/channels" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/core-events" "6.0.22" + "@storybook/channels" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/core-events" "6.0.28" "@storybook/csf" "0.0.1" - "@storybook/router" "6.0.22" + "@storybook/router" "6.0.28" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.22" + "@storybook/theming" "6.0.28" "@types/reach__router" "^1.3.5" core-js "^3.0.1" fast-deep-equal "^3.1.1" @@ -2932,25 +3592,14 @@ ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@5.3.21": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.3.21.tgz#9c08bf1c108ff973dbca18e582680d25178db1d4" - integrity sha512-CfoP7aEbZtJ35R9zeujMRdIwprETUi+Ve+y84DhXYQ2uJ0rR3vO4zHLZnxMMyJ5VnYOfuO042uch07+EKBz40Q== +"@storybook/channel-postmessage@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.0.28.tgz#82028e368d440d49e3c4ad4e6d0166d044f0a35b" + integrity sha512-HnLKXPIwZo+JvuG1aYfGX+BoDVpmYbZBlFDSSKroBdCWZRxJJ7OQIOjBWfnf9RxOJqtwEZfVylgtmIUr9ult3Q== dependencies: - "@storybook/channels" "5.3.21" - "@storybook/client-logger" "5.3.21" - core-js "^3.0.1" - global "^4.3.2" - telejson "^3.2.0" - -"@storybook/channel-postmessage@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.0.22.tgz#0a928d25fe3b87340e5670e897dc8fa4ee6ca6df" - integrity sha512-Upa2rG9H65MPdVxT9pNeDL9VlX5VeP7bpvR/TTEf2cRCiq6SC93pAs45XPWBcD8Jhq3p5+uFDARKReb2iF49+w== - dependencies: - "@storybook/channels" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/core-events" "6.0.22" + "@storybook/channels" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/core-events" "6.0.28" core-js "^3.0.1" global "^4.3.2" qs "^6.6.0" @@ -2963,48 +3612,25 @@ dependencies: core-js "^3.0.1" -"@storybook/channels@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.0.22.tgz#4cdfee7c1581462ec872b310917003c9e4dc7224" - integrity sha512-d/RlPFDq9NXA/Y3CVDsSVsWgvYiiiifxQN9hz5+y3T6MnRJPEfAPWYkbv+wLixWbDF2ULzjQHp4zcfTm6T7A4w== +"@storybook/channels@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.0.28.tgz#9d086db8ae9ee0464fa743fefd155a2e810eb4b7" + integrity sha512-Ow1fR0GGdlfAQlagOrXHs7QQlCQ0xTKoznQgUFv1KxKw/bMwMtTJcpCYGSAuDzCzypCIR4EHtIHZxJsd/A2ieg== dependencies: core-js "^3.0.1" ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/client-api@5.3.21": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.3.21.tgz#5b218a28f24219c32ab4b92a6af2a3e452fb8089" - integrity sha512-vS4DfA2Avvl7JNQymO4e3RUNoTWIGVfZJ70Irnd6PTAZNojbCXTYuigDavrmyf83F3g5rQpwmSAPjuoi/X/FRA== +"@storybook/client-api@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.0.28.tgz#789e1dd85e5bfed8580035bbefe1871359bac8fc" + integrity sha512-KZ6cw6MU8exXyahHrpNAG5gmSioCXw6I5k0IOH/xzkJpN/9IMbYSbACvxbSKHeCIZorhcWNVWJjth8Kc6dD4Hg== dependencies: - "@storybook/addons" "5.3.21" - "@storybook/channel-postmessage" "5.3.21" - "@storybook/channels" "5.3.21" - "@storybook/client-logger" "5.3.21" - "@storybook/core-events" "5.3.21" - "@storybook/csf" "0.0.1" - "@types/webpack-env" "^1.15.0" - core-js "^3.0.1" - eventemitter3 "^4.0.0" - global "^4.3.2" - is-plain-object "^3.0.0" - lodash "^4.17.15" - memoizerific "^1.11.3" - qs "^6.6.0" - stable "^0.1.8" - ts-dedent "^1.1.0" - util-deprecate "^1.0.2" - -"@storybook/client-api@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.0.22.tgz#b6079d376b49eb23b69661474446ed402bdef235" - integrity sha512-GP9m1LW3C79EJxTGToCvBZDEApMRCl9tVXGfB9yEB0dIFC9jTwsPfpwjnhh2Imp9xJjszahSqxkhv4rAZ8C44Q== - dependencies: - "@storybook/addons" "6.0.22" - "@storybook/channel-postmessage" "6.0.22" - "@storybook/channels" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/core-events" "6.0.22" + "@storybook/addons" "6.0.28" + "@storybook/channel-postmessage" "6.0.28" + "@storybook/channels" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/core-events" "6.0.28" "@storybook/csf" "0.0.1" "@types/qs" "^6.9.0" "@types/webpack-env" "^1.15.2" @@ -3025,15 +3651,43 @@ dependencies: core-js "^3.0.1" -"@storybook/client-logger@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.0.22.tgz#4e4b2c40b708b500611d5e207099a4e46e825590" - integrity sha512-AQD2Zz7BIIwrP0/sNZMXgP/BEZo5qK1YPDl2mPppSJdFocVCYDlc6HgYPZZHtPvD5BVWAENg2NQoGBOivuMl3g== +"@storybook/client-logger@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.0.28.tgz#f71d0ad3314facfdce4a5d2bebd0f08dc0289109" + integrity sha512-FOgeRQknrlm5PTgfY0THPrcrDyxu4ImuFOn+VWQW60mf9ShJQl45BEgm4bm9hbblYYnVHtnskWUOJfxqhHvgjg== dependencies: core-js "^3.0.1" global "^4.3.2" -"@storybook/components@5.3.21", "@storybook/components@^5.3.17": +"@storybook/components@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.0.28.tgz#aca24419a3941e0f5a20dd349f2b13614542b3da" + integrity sha512-rp18w+xC5/ATQThnUR6Rv2cg/DAokbV88uZLPCGHX8HsPY2jFPLyCsWnNiIHBF/SfUCjIlljChlX3bzGk+SQUA== + dependencies: + "@storybook/client-logger" "6.0.28" + "@storybook/csf" "0.0.1" + "@storybook/theming" "6.0.28" + "@types/overlayscrollbars" "^1.9.0" + "@types/react-color" "^3.0.1" + "@types/react-syntax-highlighter" "11.0.4" + core-js "^3.0.1" + fast-deep-equal "^3.1.1" + global "^4.3.2" + lodash "^4.17.15" + markdown-to-jsx "^6.11.4" + memoizerific "^1.11.3" + overlayscrollbars "^1.10.2" + polished "^3.4.4" + popper.js "^1.14.7" + react "^16.8.3" + react-color "^2.17.0" + react-dom "^16.8.3" + react-popper-tooltip "^2.11.0" + react-syntax-highlighter "^12.2.1" + react-textarea-autosize "^8.1.1" + ts-dedent "^1.1.1" + +"@storybook/components@^5.3.17": version "5.3.21" resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.3.21.tgz#17ee371a2455c6e807c3d3135a9266e63ad7651a" integrity sha512-42QQk6qZl6wrtajP8yNCfmNS2t8Iod5QY+4V/l6iNnnT9O+j6cWOlnO+ZyvjNv0Xm0zIOt+VyVjdkKh8FUjQmA== @@ -3060,34 +3714,6 @@ simplebar-react "^1.0.0-alpha.6" ts-dedent "^1.1.0" -"@storybook/components@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.0.22.tgz#07b0804fb9b39787967be88d435540adddce328d" - integrity sha512-sc7O4djNLajyJdVY4dUSO73L/+VM8IyzYKK9c5kSw4pN+l6M3EUBi4Zt/jdQc+WxSBmmriSe7aBOKrOSxBBSiA== - dependencies: - "@storybook/client-logger" "6.0.22" - "@storybook/csf" "0.0.1" - "@storybook/theming" "6.0.22" - "@types/overlayscrollbars" "^1.9.0" - "@types/react-color" "^3.0.1" - "@types/react-syntax-highlighter" "11.0.4" - core-js "^3.0.1" - fast-deep-equal "^3.1.1" - global "^4.3.2" - lodash "^4.17.15" - markdown-to-jsx "^6.11.4" - memoizerific "^1.11.3" - overlayscrollbars "^1.10.2" - polished "^3.4.4" - popper.js "^1.14.7" - react "^16.8.3" - react-color "^2.17.0" - react-dom "^16.8.3" - react-popper-tooltip "^2.11.0" - react-syntax-highlighter "^12.2.1" - react-textarea-autosize "^8.1.1" - ts-dedent "^1.1.1" - "@storybook/core-events@5.3.21", "@storybook/core-events@^5.3.17": version "5.3.21" resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.3.21.tgz#41d81c3f107302a032545fc86ff344230c04b9e9" @@ -3095,96 +3721,17 @@ dependencies: core-js "^3.0.1" -"@storybook/core-events@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.0.22.tgz#1bbdef9d50cea628f6f761117b6ddb9927caebf8" - integrity sha512-XQplzZwC9o4OQbKPjBruIOSFGto6qtmIAuh94NaHB6Hpv8YpsDwy1fXxEr990fj/5bOXmL4YV3x1AD6fOK/1sA== +"@storybook/core-events@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.0.28.tgz#f9d0925cf11e696fdd3602ae581d29568f352822" + integrity sha512-YT691sQEyoTabXZGCeCXulIO31aXfiWpvE7vW7t3F/uo/Xv6aiNcY/Fzy1vRNcbgCAf3EWsBtzb1eh0FCJkyuA== dependencies: core-js "^3.0.1" -"@storybook/core@5.3.21": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.3.21.tgz#da963166ea24601f318266a3aa6bbc06fc8fb175" - integrity sha512-plD47WIsn/JoyRJDOpmH7N7mEMo/jiA8ZlOitLW55zYvzUn8UrVpRFpMYo91OJxiCT6JFoaEh3XtNdhbgUwnPA== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.7.0" - "@babel/plugin-proposal-object-rest-spread" "^7.6.2" - "@babel/plugin-syntax-dynamic-import" "^7.2.0" - "@babel/plugin-transform-react-constant-elements" "^7.2.0" - "@babel/preset-env" "^7.4.5" - "@storybook/addons" "5.3.21" - "@storybook/channel-postmessage" "5.3.21" - "@storybook/client-api" "5.3.21" - "@storybook/client-logger" "5.3.21" - "@storybook/core-events" "5.3.21" - "@storybook/csf" "0.0.1" - "@storybook/node-logger" "5.3.21" - "@storybook/router" "5.3.21" - "@storybook/theming" "5.3.21" - "@storybook/ui" "5.3.21" - airbnb-js-shims "^2.2.1" - ansi-to-html "^0.6.11" - autoprefixer "^9.7.2" - babel-plugin-add-react-displayname "^0.0.5" - babel-plugin-emotion "^10.0.20" - babel-plugin-macros "^2.7.0" - babel-preset-minify "^0.5.0 || 0.6.0-alpha.5" - boxen "^4.1.0" - case-sensitive-paths-webpack-plugin "^2.2.0" - chalk "^3.0.0" - cli-table3 "0.5.1" - commander "^4.0.1" - core-js "^3.0.1" - corejs-upgrade-webpack-plugin "^2.2.0" - css-loader "^3.0.0" - detect-port "^1.3.0" - dotenv-webpack "^1.7.0" - ejs "^2.7.4" - express "^4.17.0" - file-loader "^4.2.0" - file-system-cache "^1.0.5" - find-cache-dir "^3.0.0" - find-up "^4.1.0" - fs-extra "^8.0.1" - glob-base "^0.3.0" - global "^4.3.2" - html-webpack-plugin "^4.0.0-beta.2" - inquirer "^7.0.0" - interpret "^2.0.0" - ip "^1.1.5" - json5 "^2.1.1" - lazy-universal-dotenv "^3.0.1" - micromatch "^4.0.2" - node-fetch "^2.6.0" - open "^7.0.0" - pnp-webpack-plugin "1.5.0" - postcss-flexbugs-fixes "^4.1.0" - postcss-loader "^3.0.0" - pretty-hrtime "^1.0.3" - qs "^6.6.0" - raw-loader "^3.1.0" - react-dev-utils "^9.0.0" - regenerator-runtime "^0.13.3" - resolve "^1.11.0" - resolve-from "^5.0.0" - semver "^6.0.0" - serve-favicon "^2.5.0" - shelljs "^0.8.3" - style-loader "^1.0.0" - terser-webpack-plugin "^2.1.2" - ts-dedent "^1.1.0" - unfetch "^4.1.0" - url-loader "^2.0.1" - util-deprecate "^1.0.2" - webpack "^4.33.0" - webpack-dev-middleware "^3.7.0" - webpack-hot-middleware "^2.25.0" - webpack-virtual-modules "^0.2.0" - -"@storybook/core@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.0.22.tgz#3ea911f486bbf5918976f0101627057af58d2f34" - integrity sha512-VgzybAKw5Jd5HzpVukvKLj2ScZ8bzJAvhoFAab3zegNyk1bK+qUK8vYDWP5dzaINvW63zA/D5kyjfZP8T9EofQ== +"@storybook/core@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.0.28.tgz#82ca8ef2700971c5abceea3750fc2d1081034c9d" + integrity sha512-NIEeU4NXl6vK6NVIisR90QYWomTexXEmF9cFcr02DcxKwFTNixSfBuquDFt6o7OTLNgmUQ5r2y+fXlQ4vDTG4w== dependencies: "@babel/plugin-proposal-class-properties" "^7.8.3" "@babel/plugin-proposal-decorators" "^7.8.3" @@ -3207,20 +3754,20 @@ "@babel/preset-react" "^7.8.3" "@babel/preset-typescript" "^7.9.0" "@babel/register" "^7.10.5" - "@storybook/addons" "6.0.22" - "@storybook/api" "6.0.22" - "@storybook/channel-postmessage" "6.0.22" - "@storybook/channels" "6.0.22" - "@storybook/client-api" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/components" "6.0.22" - "@storybook/core-events" "6.0.22" + "@storybook/addons" "6.0.28" + "@storybook/api" "6.0.28" + "@storybook/channel-postmessage" "6.0.28" + "@storybook/channels" "6.0.28" + "@storybook/client-api" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/components" "6.0.28" + "@storybook/core-events" "6.0.28" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.0.22" - "@storybook/router" "6.0.22" + "@storybook/node-logger" "6.0.28" + "@storybook/router" "6.0.28" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.22" - "@storybook/ui" "6.0.22" + "@storybook/theming" "6.0.28" + "@storybook/ui" "6.0.28" "@types/glob-base" "^0.3.0" "@types/micromatch" "^4.0.1" "@types/node-fetch" "^2.5.4" @@ -3291,22 +3838,10 @@ dependencies: lodash "^4.17.15" -"@storybook/node-logger@5.3.21": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-5.3.21.tgz#f11d45042bd57dc69e9037d8f374d9fd0aad8071" - integrity sha512-8xibncy873JXePCK5MC0qem1MKtWI1Lc4hv6rwURSwYpZtkO7yElay3XAFGUSfz8qFJkoDBmMTxBR3fp4Dln7g== - dependencies: - "@types/npmlog" "^4.1.2" - chalk "^3.0.0" - core-js "^3.0.1" - npmlog "^4.1.2" - pretty-hrtime "^1.0.3" - regenerator-runtime "^0.13.3" - -"@storybook/node-logger@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.0.22.tgz#a95bb605309baf6bd768fa57dff77760f41c7f10" - integrity sha512-H5j0zjMmg6o+wQgiY1GWlgz6cciHJN5vw7/B/hUksMHOwc+30nrGa89dDouj2ze1vJfiY3AaOMrsgtuMYFXaHQ== +"@storybook/node-logger@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.0.28.tgz#b1e51d00c8acaa44b91e4cef33ab3275cad93b14" + integrity sha512-foGRKU9n6tRcN+Os2XJvsWMQQoUubUjuX6/pIyh+rGVOfOxbK56logE/UDOAsdibGMiof9MOldNeGmYS51vPCg== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.0.0" @@ -3314,32 +3849,31 @@ npmlog "^4.1.2" pretty-hrtime "^1.0.3" -"@storybook/react@^5.2.3": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-5.3.21.tgz#f7f364d3d3adc70746a12cf8b6614952f41d4bd0" - integrity sha512-A50F8dDZxyLGa/dE3q0Zxt7T5r9UbomoSclqw7oJTO9GI76QOu7GfsoWrEL2gTEDAmqXreLVQqGuTLQhBz0rlA== +"@storybook/react@^6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.0.28.tgz#b7b482342b995645770906833799a0b1e155f9b4" + integrity sha512-a0X5htjhqlBYQbuQlllTHD8VBeiEUs0UMU3lnJv79B2B2BiIaXK0w5ptyQyiOznP5OzSLnjZEQwmiefepFpr3g== dependencies: - "@babel/plugin-transform-react-constant-elements" "^7.6.3" "@babel/preset-flow" "^7.0.0" "@babel/preset-react" "^7.0.0" - "@storybook/addons" "5.3.21" - "@storybook/core" "5.3.21" - "@storybook/node-logger" "5.3.21" - "@svgr/webpack" "^4.0.3" - "@types/webpack-env" "^1.15.0" + "@storybook/addons" "6.0.28" + "@storybook/core" "6.0.28" + "@storybook/node-logger" "6.0.28" + "@storybook/semver" "^7.3.2" + "@svgr/webpack" "^5.4.0" + "@types/webpack-env" "^1.15.2" babel-plugin-add-react-displayname "^0.0.5" babel-plugin-named-asset-import "^0.3.1" - babel-plugin-react-docgen "^4.0.0" + babel-plugin-react-docgen "^4.2.1" core-js "^3.0.1" global "^4.3.2" lodash "^4.17.15" - mini-css-extract-plugin "^0.7.0" prop-types "^15.7.2" - react-dev-utils "^9.0.0" + react-dev-utils "^10.0.0" + react-docgen-typescript-plugin "^0.5.2" regenerator-runtime "^0.13.3" - semver "^6.0.0" - ts-dedent "^1.1.0" - webpack "^4.33.0" + ts-dedent "^1.1.1" + webpack "^4.43.0" "@storybook/router@5.3.21": version "5.3.21" @@ -3356,10 +3890,10 @@ qs "^6.6.0" util-deprecate "^1.0.2" -"@storybook/router@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.0.22.tgz#90dc8eb5c766b85b555cc103ac6197d7c11700be" - integrity sha512-Gu3PmWXaDDhDqTY/S8/ag2OCdTb0S+aD/QkXvQzSht5gt5d8M2tQxBlhXDVFNhYGRz7zQtjRmTxqT/3YX9tjrg== +"@storybook/router@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.0.28.tgz#e95d8c3bcc18688c17a5f582b2612014400fed58" + integrity sha512-omp2LRq3LYc7A89PM0WpJnioedzCme3jJbJXRR7tFva4N+aP6JGaFTJZZdk2NHXHxerGfWG0Cs9G6HNAw9nN1A== dependencies: "@reach/router" "^1.3.3" "@types/reach__router" "^1.3.5" @@ -3394,15 +3928,15 @@ resolve-from "^5.0.0" ts-dedent "^1.1.0" -"@storybook/theming@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.0.22.tgz#c50877d19c9807cc35655d78f8b5c866b861b853" - integrity sha512-aR11z70vq0G+F61PIJHW1Kt1lmA2vYxGWF1TL6rsECXNt4fN+X9ig082G0Uhag0mV/FJZdKhhpv360paJFYF2g== +"@storybook/theming@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.0.28.tgz#8403333c68c44729eb677ef7c934aaa06e269f33" + integrity sha512-dcZXDkO1LYcnWUejAzvjl3OPBnAB1m2+fzmRx0dBrgm2O+fcNXTadQ6SXZYKaSz37lS+aGtYG7I9nurwhXMMXA== dependencies: "@emotion/core" "^10.0.20" "@emotion/is-prop-valid" "^0.8.6" "@emotion/styled" "^10.0.17" - "@storybook/client-logger" "6.0.22" + "@storybook/client-logger" "6.0.28" core-js "^3.0.1" deep-object-diff "^1.1.0" emotion-theming "^10.0.19" @@ -3412,61 +3946,21 @@ resolve-from "^5.0.0" ts-dedent "^1.1.1" -"@storybook/ui@5.3.21": - version "5.3.21" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.3.21.tgz#b42568e03353b47aaab1b6449311f38858585f81" - integrity sha512-OUf8JYY9LN+XfzLSZE6KtboITGDL6C8Z0W9QOXM5LJwFLv4PkANK/f9qsB5vVHFm7vhoO96butFzs6SjTKhxkw== +"@storybook/ui@6.0.28": + version "6.0.28" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.0.28.tgz#9b07d11c648b26d9fe20db0436bfbb65634a1930" + integrity sha512-LGYW+hGiRu+5QRrZXamEt7aoHWQMfYWAaSO8hJldD5lfIXUXcoWUm0Mx7CRpUhw22LzmidrwZijyxlTnuTRZyw== dependencies: "@emotion/core" "^10.0.20" - "@storybook/addons" "5.3.21" - "@storybook/api" "5.3.21" - "@storybook/channels" "5.3.21" - "@storybook/client-logger" "5.3.21" - "@storybook/components" "5.3.21" - "@storybook/core-events" "5.3.21" - "@storybook/router" "5.3.21" - "@storybook/theming" "5.3.21" - copy-to-clipboard "^3.0.8" - core-js "^3.0.1" - core-js-pure "^3.0.1" - emotion-theming "^10.0.19" - fast-deep-equal "^2.0.1" - fuse.js "^3.4.6" - global "^4.3.2" - lodash "^4.17.15" - markdown-to-jsx "^6.11.4" - memoizerific "^1.11.3" - polished "^3.3.1" - prop-types "^15.7.2" - qs "^6.6.0" - react "^16.8.3" - react-dom "^16.8.3" - react-draggable "^4.0.3" - react-helmet-async "^1.0.2" - react-hotkeys "2.0.0" - react-sizeme "^2.6.7" - regenerator-runtime "^0.13.2" - resolve-from "^5.0.0" - semver "^6.0.0" - store2 "^2.7.1" - telejson "^3.2.0" - util-deprecate "^1.0.2" - -"@storybook/ui@6.0.22": - version "6.0.22" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.0.22.tgz#f8aa93c66e66e99010d98a7344adf1c7a9839224" - integrity sha512-iueyQ3EnLHhbV6xWQWMoN1aenEh3jLAXFmabxrf1s/l0JKn0u6qr7BHZcu3VZJ4EJCEsh6wDFNWjaUbTpfDU5g== - dependencies: - "@emotion/core" "^10.0.20" - "@storybook/addons" "6.0.22" - "@storybook/api" "6.0.22" - "@storybook/channels" "6.0.22" - "@storybook/client-logger" "6.0.22" - "@storybook/components" "6.0.22" - "@storybook/core-events" "6.0.22" - "@storybook/router" "6.0.22" + "@storybook/addons" "6.0.28" + "@storybook/api" "6.0.28" + "@storybook/channels" "6.0.28" + "@storybook/client-logger" "6.0.28" + "@storybook/components" "6.0.28" + "@storybook/core-events" "6.0.28" + "@storybook/router" "6.0.28" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.22" + "@storybook/theming" "6.0.28" "@types/markdown-to-jsx" "^6.11.0" copy-to-clipboard "^3.0.8" core-js "^3.0.1" @@ -3489,108 +3983,108 @@ resolve-from "^5.0.0" store2 "^2.7.1" -"@svgr/babel-plugin-add-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" - integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig== +"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" + integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== -"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc" - integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ== +"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" + integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== -"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7" - integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w== +"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" + integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== -"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" - integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== +"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" + integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== -"@svgr/babel-plugin-svg-dynamic-title@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz#2cdedd747e5b1b29ed4c241e46256aac8110dd93" - integrity sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w== +"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" + integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== -"@svgr/babel-plugin-svg-em-dimensions@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391" - integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w== +"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" + integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== -"@svgr/babel-plugin-transform-react-native-svg@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717" - integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw== +"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" + integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== -"@svgr/babel-plugin-transform-svg-component@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" - integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== +"@svgr/babel-plugin-transform-svg-component@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" + integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== -"@svgr/babel-preset@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.3.tgz#a75d8c2f202ac0e5774e6bfc165d028b39a1316c" - integrity sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A== +"@svgr/babel-preset@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" + integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig== dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" - "@svgr/babel-plugin-svg-dynamic-title" "^4.3.3" - "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" - "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" - "@svgr/babel-plugin-transform-svg-component" "^4.2.0" + "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" + "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" + "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" + "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" + "@svgr/babel-plugin-transform-svg-component" "^5.5.0" -"@svgr/core@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293" - integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== +"@svgr/core@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" + integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== dependencies: - "@svgr/plugin-jsx" "^4.3.3" - camelcase "^5.3.1" - cosmiconfig "^5.2.1" + "@svgr/plugin-jsx" "^5.5.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.0" -"@svgr/hast-util-to-babel-ast@^4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz#1d5a082f7b929ef8f1f578950238f630e14532b8" - integrity sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg== +"@svgr/hast-util-to-babel-ast@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" + integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ== dependencies: - "@babel/types" "^7.4.4" + "@babel/types" "^7.12.6" -"@svgr/plugin-jsx@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa" - integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w== +"@svgr/plugin-jsx@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" + integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== dependencies: - "@babel/core" "^7.4.5" - "@svgr/babel-preset" "^4.3.3" - "@svgr/hast-util-to-babel-ast" "^4.3.2" - svg-parser "^2.0.0" + "@babel/core" "^7.12.3" + "@svgr/babel-preset" "^5.5.0" + "@svgr/hast-util-to-babel-ast" "^5.5.0" + svg-parser "^2.0.2" -"@svgr/plugin-svgo@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32" - integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w== +"@svgr/plugin-svgo@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" + integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== dependencies: - cosmiconfig "^5.2.1" - merge-deep "^3.0.2" + cosmiconfig "^7.0.0" + deepmerge "^4.2.2" svgo "^1.2.2" -"@svgr/webpack@^4.0.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.3.3.tgz#13cc2423bf3dff2d494f16b17eb7eacb86895017" - integrity sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg== +"@svgr/webpack@^5.4.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" + integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== dependencies: - "@babel/core" "^7.4.5" - "@babel/plugin-transform-react-constant-elements" "^7.0.0" - "@babel/preset-env" "^7.4.5" - "@babel/preset-react" "^7.0.0" - "@svgr/core" "^4.3.3" - "@svgr/plugin-jsx" "^4.3.3" - "@svgr/plugin-svgo" "^4.3.1" - loader-utils "^1.2.3" + "@babel/core" "^7.12.3" + "@babel/plugin-transform-react-constant-elements" "^7.12.1" + "@babel/preset-env" "^7.12.1" + "@babel/preset-react" "^7.12.5" + "@svgr/core" "^5.5.0" + "@svgr/plugin-jsx" "^5.5.0" + "@svgr/plugin-svgo" "^5.5.0" + loader-utils "^2.0.0" "@types/anymatch@*": version "1.3.1" @@ -3791,7 +4285,7 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/jest@^24.0.16", "@types/jest@^24.0.19": +"@types/jest@^24.0.19": version "24.9.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" integrity sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q== @@ -4071,13 +4565,6 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== -"@types/storybook__addon-storyshots@^5.1.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@types/storybook__addon-storyshots/-/storybook__addon-storyshots-5.3.1.tgz#3bf921bcf34265d239c7417e4bbd3d66aa3d41d3" - integrity sha512-gTH1kziacyF86FieOMV86OhTu6zhV9a3gzF1Pqxm1k3/SKIScML7YgBQS0xwSV0riAlcRkXZNqjSVdci5TiUdA== - dependencies: - "@storybook/addon-storyshots" "*" - "@types/styled-components@^5.1.0": version "5.1.3" resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.3.tgz#6fab3d9c8f7d9a15cbb89d379d850c985002f363" @@ -4115,7 +4602,7 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== -"@types/webpack-env@^1.15.0", "@types/webpack-env@^1.15.2": +"@types/webpack-env@^1.15.2": version "1.15.3" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.15.3.tgz#fb602cd4c2f0b7c0fb857e922075fdf677d25d84" integrity sha512-5oiXqR7kwDGZ6+gmzIO2lTC+QsriNuQXZDWNYRV3l2XRN/zmPgnC21DLSx2D05zvD8vnXW6qUg7JnXZ4I6qLVQ== @@ -4348,6 +4835,18 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@webpack-contrib/schema-utils@^1.0.0-beta.0": + version "1.0.0-beta.0" + resolved "https://registry.yarnpkg.com/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65" + integrity sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg== + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chalk "^2.3.2" + strip-ansi "^4.0.0" + text-table "^0.2.0" + webpack-log "^1.1.2" + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -4847,11 +5346,6 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= -ast-types@0.11.3: - version "0.11.3" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" - integrity sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA== - ast-types@^0.13.2: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -4859,6 +5353,13 @@ ast-types@^0.13.2: dependencies: tslib "^2.0.1" +ast-types@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -5156,7 +5657,7 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.7.0, babel-plugin-macros@^2.8.0: +babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -5246,14 +5747,14 @@ babel-plugin-named-asset-import@^0.3.1: resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz#c9750a1b38d85112c9e166bf3ef7c5dbc605f4be" integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA== -babel-plugin-react-docgen@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.0.tgz#4f425692f0ca06c73a1462274d370a3ac0637b46" - integrity sha512-B3tjZwKskcia9TsqkND+9OTjl/F5A5OBvRJ6Ktg34CONoxm+kB3CJ52wk5TjbszX9gqCPcAuc0GgkhT0CLuT/Q== +babel-plugin-react-docgen@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b" + integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ== dependencies: + ast-types "^0.14.2" lodash "^4.17.15" react-docgen "^5.0.0" - recast "^0.14.7" babel-plugin-reflow@^0.2.7: version "0.2.7" @@ -5723,15 +6224,6 @@ browserslist@4.10.0: node-releases "^1.1.52" pkg-up "^3.1.0" -browserslist@4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" - integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== - dependencies: - caniuse-lite "^1.0.30000989" - electron-to-chromium "^1.3.247" - node-releases "^1.1.29" - browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: version "4.14.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015" @@ -5742,6 +6234,17 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: escalade "^3.1.0" node-releases "^1.1.61" +browserslist@^4.14.5: + version "4.14.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.7.tgz#c071c1b3622c1c2e790799a37bb09473a4351cb6" + integrity sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ== + dependencies: + caniuse-lite "^1.0.30001157" + colorette "^1.2.1" + electron-to-chromium "^1.3.591" + escalade "^3.1.1" + node-releases "^1.1.66" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -5849,30 +6352,6 @@ cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" - integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== - dependencies: - chownr "^1.1.2" - figgy-pudding "^3.5.1" - fs-minipass "^2.0.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.0.0" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - p-map "^3.0.0" - promise-inflight "^1.0.1" - rimraf "^2.7.1" - ssri "^7.0.0" - unique-filename "^1.1.1" - cacache@^15.0.5: version "15.0.5" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" @@ -5999,6 +6478,11 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== +camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + camelize@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" @@ -6019,11 +6503,16 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135: version "1.0.30001141" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001141.tgz#214a196d81aa938b268fb0cb6d8fab23fdf14378" integrity sha512-EHfInJHoQTmlMdVZrEc5gmwPc0zyN/hVufmGHPbVNQwlk7tJfCmQ2ysRZMY2MeleBivALUTyyxXnQjK18XrVpA== +caniuse-lite@^1.0.30001157: + version "1.0.30001159" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001159.tgz#bebde28f893fa9594dadcaa7d6b8e2aa0299df20" + integrity sha512-w9Ph56jOsS8RL20K9cLND3u/+5WASWdhC/PPrf+V3/HsM3uHOavWOR1Xzakbv4Puo/srmPHudkmCRWM7Aq+/UA== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -6041,7 +6530,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -6134,7 +6623,7 @@ cheerio@^1.0.0-rc.3: optionalDependencies: fsevents "~2.1.2" -chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -6241,16 +6730,6 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-table3@0.5.1, cli-table3@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" - integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== - dependencies: - object-assign "^4.1.0" - string-width "^2.1.1" - optionalDependencies: - colors "^1.1.2" - cli-table3@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" @@ -6261,6 +6740,16 @@ cli-table3@0.6.0: optionalDependencies: colors "^1.1.2" +cli-table3@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -6314,17 +6803,6 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-deep@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" - integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= - dependencies: - for-own "^0.1.3" - is-plain-object "^2.0.1" - kind-of "^3.0.2" - lazy-cache "^1.0.3" - shallow-clone "^0.1.2" - clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -6456,7 +6934,7 @@ commander@^3.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== -commander@^4.0.1, commander@^4.1.1: +commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -6746,15 +7224,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -corejs-upgrade-webpack-plugin@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/corejs-upgrade-webpack-plugin/-/corejs-upgrade-webpack-plugin-2.2.0.tgz#503293bf1fdcb104918eb40d0294e4776ad6923a" - integrity sha512-J0QMp9GNoiw91Kj/dkIQFZeiCXgXoja/Wlht1SPybxerBWh4NCmb0pOgCv61lrlQZETwvVVfAFAA3IqoEO9aqQ== - dependencies: - resolve-from "^5.0.0" - webpack "^4.38.0" - -cosmiconfig@^5.0.0, cosmiconfig@^5.1.0, cosmiconfig@^5.2.1: +cosmiconfig@^5.0.0, cosmiconfig@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -6838,7 +7308,16 @@ create-react-context@0.3.0, create-react-context@^0.3.0: gud "^1.0.0" warning "^4.0.3" -cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -6849,15 +7328,6 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" - integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -6902,7 +7372,7 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@^3.0.0, css-loader@^3.2.0, css-loader@^3.5.3: +css-loader@^3.2.0, css-loader@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== @@ -7159,6 +7629,14 @@ cypress@^4.12.0: url "^0.11.0" yauzl "^2.10.0" +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + damerau-levenshtein@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" @@ -7740,11 +8218,6 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== - ejs@^3.1.2: version "3.1.5" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.5.tgz#aed723844dc20acb4b170cd9ab1017e476a0d93b" @@ -7752,11 +8225,16 @@ ejs@^3.1.2: dependencies: jake "^10.6.1" -electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.571: +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.571: version "1.3.576" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.576.tgz#2e70234484e03d7c7e90310d7d79fd3775379c34" integrity sha512-uSEI0XZ//5ic+0NdOqlxp0liCD44ck20OAGyLMSymIWTEAtHKVJi6JM18acOnRgUgX7Q65QqnI+sNncNvIy8ew== +electron-to-chromium@^1.3.591: + version "1.3.598" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.598.tgz#8f757018902ab6190323a8c5f6124d854893a35b" + integrity sha512-G5Ztk23/ubLYVPxPXnB1uu105uzIPd4xB/D8ld8x1GaSC9+vU9NZL16nYZya8H77/7CCKKN7dArzJL3pBs8N7A== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -7848,6 +8326,15 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +endent@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/endent/-/endent-2.0.1.tgz#fb18383a3f37ae3213a5d9f6c4a880d1061eb4c5" + integrity sha512-mADztvcC+vCk4XEZaCz6xIPO2NHQuprv5CAEjuVAu6aZwqAj7nVNlMyl1goPFYqCCpS2OJV9jwpumJLkotZrNw== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.4" + enhanced-resolve@^4.1.1, enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" @@ -8059,11 +8546,29 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + es5-shim@^4.5.13: version "4.5.14" resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.14.tgz#90009e1019d0ea327447cb523deaff8fe45697ef" integrity sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg== +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -8081,26 +8586,39 @@ es6-shim@^0.35.5: resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab" integrity sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg== +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escalade@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + escodegen@^1.14.1, escodegen@^1.9.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -8311,7 +8829,7 @@ espree@^7.3.0: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.3.0" -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -8527,6 +9045,13 @@ express@^4.17.0, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -8617,6 +9142,11 @@ fast-glob@^2.0.2, fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -8745,11 +9275,6 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" -filesize@3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - filesize@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" @@ -8794,7 +9319,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.0.0, find-cache-dir@^3.3.1: +find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== @@ -8808,13 +9333,6 @@ find-root@^1.1.0: resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -8838,6 +9356,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-versions@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -8895,42 +9420,16 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== -for-in@^0.1.3: - version "0.1.8" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" - integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= - -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -fork-ts-checker-webpack-plugin@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz#ce1d77190b44d81a761b10b6284a373795e41f0c" - integrity sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA== - dependencies: - babel-code-frame "^6.22.0" - chalk "^2.4.1" - chokidar "^2.0.4" - micromatch "^3.1.10" - minimatch "^3.0.4" - semver "^5.6.0" - tapable "^1.0.0" - worker-rpc "^0.1.0" - fork-ts-checker-webpack-plugin@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" @@ -9017,7 +9516,7 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^8.0.1, fs-extra@^8.1.0: +fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -9102,7 +9601,7 @@ functions-have-names@^1.2.0: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91" integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA== -fuse.js@^3.4.6, fuse.js@^3.6.1: +fuse.js@^3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== @@ -9744,7 +10243,7 @@ html-to-react@^1.3.4: lodash.camelcase "^4.3.0" ramda "^0.27" -html-webpack-plugin@^4.0.0-beta.2, html-webpack-plugin@^4.2.1: +html-webpack-plugin@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz#625097650886b97ea5dae331c320e3238f6c121c" integrity sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw== @@ -10097,25 +10596,6 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" -inquirer@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" - integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - inquirer@7.0.4: version "7.0.4" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" @@ -10293,7 +10773,7 @@ is-boolean-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== -is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5: +is-buffer@^1.1.4, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -10381,7 +10861,7 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== -is-dom@^1.0.9: +is-dom@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== @@ -10555,18 +11035,13 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-plain-object@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" - integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -11714,7 +12189,7 @@ jest-serializer@^26.6.2: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^24.1.0, jest-snapshot@^24.9.0: +jest-snapshot@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== @@ -11776,13 +12251,6 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-specific-snapshot@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jest-specific-snapshot/-/jest-specific-snapshot-2.0.0.tgz#425fe524b25df154aa39f97fa6fe9726faaac273" - integrity sha512-aXaNqBg/svwEpY5iQEzEHc5I85cUBKgfeVka9KmpznxLnatpjiqjr7QLb/BYNYlsrZjZzgRHTjQJ+Svx+dbdvg== - dependencies: - jest-snapshot "^24.1.0" - jest-specific-snapshot@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jest-specific-snapshot/-/jest-specific-snapshot-4.0.0.tgz#a52a2e223e7576e610dbeaf341207c557ac20554" @@ -11915,14 +12383,6 @@ jest-worker@^24.6.0, jest-worker@^24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^25.4.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1" - integrity sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - jest-worker@^26.2.1, jest-worker@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" @@ -12164,13 +12624,6 @@ killable@^1.0.1: resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= - dependencies: - is-buffer "^1.0.2" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -12232,16 +12685,6 @@ lazy-ass@^1.6.0: resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" - integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - lazy-universal-dotenv@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" @@ -12608,6 +13051,13 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" +log-symbols@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -12646,6 +13096,14 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== +loglevelnext@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" + integrity sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A== + dependencies: + es6-symbol "^3.1.1" + object.assign "^4.1.0" + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -12922,15 +13380,6 @@ meow@^7.0.0: type-fest "^0.13.1" yargs-parser "^18.1.3" -merge-deep@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" - integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== - dependencies: - arr-union "^3.1.0" - clone-deep "^0.2.4" - kind-of "^3.0.2" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -13058,16 +13507,6 @@ mini-css-extract-plugin@^0.12.0: schema-utils "^1.0.0" webpack-sources "^1.1.0" -mini-css-extract-plugin@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0" - integrity sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ== - dependencies: - loader-utils "^1.1.0" - normalize-url "1.9.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -13182,14 +13621,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mixin-object@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" - integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= - dependencies: - for-in "^0.1.3" - is-extendable "^0.1.1" - mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -13353,6 +13784,11 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -13471,11 +13907,16 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^1.1.29, node-releases@^1.1.52, node-releases@^1.1.61: +node-releases@^1.1.52, node-releases@^1.1.61: version "1.1.61" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e" integrity sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g== +node-releases@^1.1.66: + version "1.1.67" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12" + integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg== + nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -13731,6 +14172,11 @@ object.values@^1.1.0, object.values@^1.1.1: function-bind "^1.1.1" has "^1.0.3" +objectorarray@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.4.tgz#d69b2f0ff7dc2701903d308bb85882f4ddb49483" + integrity sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w== + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -13779,14 +14225,7 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@^6.3.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - -open@^7.0.0, open@^7.0.2, open@^7.0.3: +open@^7.0.2, open@^7.0.3: version "7.3.0" resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== @@ -13910,7 +14349,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.3.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -13957,13 +14396,6 @@ p-map@^2.0.0, p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -14342,13 +14774,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -14368,13 +14793,6 @@ pn@^1.1.0: resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -pnp-webpack-plugin@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz#62a1cd3068f46d564bb33c56eb250e4d586676eb" - integrity sha512-jd9olUr9D7do+RN8Wspzhpxhgp1n6Vd0NtQ4SFkmIACZoEL1nkyAdW9Ygrinjec0vgDcWjscFQQ1gDW8rsfKTg== - dependencies: - ts-pnp "^1.1.2" - pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -14858,11 +15276,6 @@ prismjs@1.22.0, prismjs@^1.21.0, prismjs@^1.8.4, prismjs@~1.22.0: optionalDependencies: clipboard "^2.0.0" -private@~0.1.5: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -14935,7 +15348,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -15165,14 +15578,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" - integrity sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA== - dependencies: - loader-utils "^1.1.0" - schema-utils "^2.0.1" - raw-loader@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" @@ -15235,42 +15640,37 @@ react-dev-utils@^10.0.0: strip-ansi "6.0.0" text-table "0.2.0" -react-dev-utils@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" - integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== - dependencies: - "@babel/code-frame" "7.5.5" - address "1.1.2" - browserslist "4.7.0" - chalk "2.4.2" - cross-spawn "6.0.5" - detect-port-alt "1.1.6" - escape-string-regexp "1.0.5" - filesize "3.6.1" - find-up "3.0.0" - fork-ts-checker-webpack-plugin "1.5.0" - global-modules "2.0.0" - globby "8.0.2" - gzip-size "5.1.1" - immer "1.10.0" - inquirer "6.5.0" - is-root "2.1.0" - loader-utils "1.2.3" - open "^6.3.0" - pkg-up "2.0.0" - react-error-overlay "^6.0.3" - recursive-readdir "2.2.2" - shell-quote "1.7.2" - sockjs-client "1.4.0" - strip-ansi "5.2.0" - text-table "0.2.0" - react-diff-view@^2.4.1: version "2.4.6" resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-2.4.6.tgz#5091ea1c326673fb5fced8b471ecbf1ab84c98fb" integrity sha512-kiF9RyRSefPa86xSqJDFesekYFqN6rhrMQbUwH8WZuBKHVDZmT5dobaiba7WPQOywOUTyJAm54kE0G4UDP3fxA== +react-docgen-typescript-loader@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/react-docgen-typescript-loader/-/react-docgen-typescript-loader-3.7.2.tgz#45cb2305652c0602767242a8700ad1ebd66bbbbd" + integrity sha512-fNzUayyUGzSyoOl7E89VaPKJk9dpvdSgyXg81cUkwy0u+NBvkzQG3FC5WBIlXda0k/iaxS+PWi+OC+tUiGxzPA== + dependencies: + "@webpack-contrib/schema-utils" "^1.0.0-beta.0" + loader-utils "^1.2.3" + react-docgen-typescript "^1.15.0" + +react-docgen-typescript-plugin@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-0.5.2.tgz#2b294d75ef3145c36303da82be5d447cb67dc0dc" + integrity sha512-NQfWyWLmzUnedkiN2nPDb6Nkm68ik6fqbC3UvgjqYSeZsbKijXUA4bmV6aU7qICOXdop9PevPdjEgJuAN0nNVQ== + dependencies: + debug "^4.1.1" + endent "^2.0.1" + micromatch "^4.0.2" + react-docgen-typescript "^1.20.1" + react-docgen-typescript-loader "^3.7.2" + tslib "^2.0.0" + +react-docgen-typescript@^1.15.0, react-docgen-typescript@^1.20.1: + version "1.20.5" + resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.20.5.tgz#fb8d78a707243498436c2952bd3f6f488a68d4f3" + integrity sha512-AbLGMtn76bn7SYBJSSaKJrZ0lgNRRR3qL60PucM5M4v/AXyC8221cKBXW5Pyt9TfDRfe+LDnPNlg7TibxX0ovA== + react-docgen@^5.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.3.0.tgz#9aabde5e69f1993c8ba839fd9a86696504654589" @@ -15303,7 +15703,7 @@ react-draggable@^4.0.3: classnames "^2.2.5" prop-types "^15.6.0" -react-error-overlay@^6.0.3, react-error-overlay@^6.0.7: +react-error-overlay@^6.0.7: version "6.0.7" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== @@ -15366,14 +15766,14 @@ react-input-autosize@^2.2.1: dependencies: prop-types "^15.5.8" -react-inspector@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-4.0.1.tgz#0f888f78ff7daccbc7be5d452b20c96dc6d5fbb8" - integrity sha512-xSiM6CE79JBqSj8Fzd9dWBHv57tLTH7OM57GP3VrE5crzVF3D5Khce9w1Xcw75OAbvrA0Mi2vBneR1OajKmXFg== +react-inspector@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.0.tgz#45a325e15f33e595be5356ca2d3ceffb7d6b8c3a" + integrity sha512-JAwswiengIcxi4X/Ssb8nf6suOuQsyit8Fxo04+iPKTnPNY3XIOuagjMZSzpJDDKkYcc/ARlySOYZZv626WUvA== dependencies: - "@babel/runtime" "^7.6.3" - is-dom "^1.0.9" - prop-types "^15.6.1" + "@babel/runtime" "^7.0.0" + is-dom "^1.0.0" + prop-types "^15.0.0" react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.13.1" @@ -15747,16 +16147,6 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" -recast@^0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" - integrity sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A== - dependencies: - ast-types "0.11.3" - esprima "~4.0.0" - private "~0.1.5" - source-map "~0.6.1" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -15873,7 +16263,7 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== @@ -15906,7 +16296,7 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== -regexpu-core@^4.7.0: +regexpu-core@^4.7.0, regexpu-core@^4.7.1: version "4.7.1" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== @@ -16115,7 +16505,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -16186,7 +16576,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -16329,7 +16719,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0, schema-utils@^2.7.1: +schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0, schema-utils@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -16490,16 +16880,6 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" - integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= - dependencies: - is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" - mixin-object "^2.0.1" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -16897,14 +17277,6 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -ssri@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" - integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== - dependencies: - figgy-pudding "^3.5.1" - minipass "^3.1.1" - ssri@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" @@ -16962,7 +17334,7 @@ store2@^2.7.1: resolved "https://registry.yarnpkg.com/store2/-/store2-2.12.0.tgz#e1f1b7e1a59b6083b2596a8d067f6ee88fd4d3cf" integrity sha512-7t+/wpKLanLzSnQPX8WAcuLCCeuSHoWdQuh9SB3xD0kNOM38DNf+0Oa+wmvxmYueRzkmh6IcdKFtvTa+ecgPDw== -storybook-addon-i18next@^1.2.1: +storybook-addon-i18next@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/storybook-addon-i18next/-/storybook-addon-i18next-1.3.0.tgz#0577c12e9f4ac64aeafa093542031518ed6f59e6" integrity sha512-CyZOr+6cqE73/r2kjlFm+hpDOoVDivZSJtt6czWL2wxUJCwWPdMfHQvDDrAI7YyE0ppyefUu9pesghzY2f3clg== @@ -17143,13 +17515,6 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-ansi@6.0.0, strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -17171,6 +17536,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -17308,7 +17680,7 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" -svg-parser@^2.0.0: +svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== @@ -17468,21 +17840,6 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser-webpack-plugin@^2.1.2: - version "2.3.8" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz#894764a19b0743f2f704e7c2a848c5283a696724" - integrity sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w== - dependencies: - cacache "^13.0.1" - find-cache-dir "^3.3.1" - jest-worker "^25.4.0" - p-limit "^2.3.0" - schema-utils "^2.6.6" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.6.12" - webpack-sources "^1.4.3" - terser-webpack-plugin@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.1.0.tgz#91e6d39571460ed240c0cf69d295bcf30ebf98cb" @@ -17498,7 +17855,7 @@ terser-webpack-plugin@^3.0.0: terser "^4.8.0" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.6.12, terser@^4.6.3, terser@^4.8.0: +terser@^4.1.2, terser@^4.6.3, terser@^4.8.0: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== @@ -17801,7 +18158,7 @@ ts-essentials@^2.0.3: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== -ts-pnp@^1.1.2, ts-pnp@^1.1.6: +ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== @@ -17821,6 +18178,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== +tslib@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + tslib@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" @@ -17902,6 +18264,16 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + typed-styles@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" @@ -18119,15 +18491,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@^2.0.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.3.0.tgz#e0e2ef658f003efb8ca41b0f3ffbf76bab88658b" - integrity sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog== - dependencies: - loader-utils "^1.2.3" - mime "^2.4.4" - schema-utils "^2.5.0" - url-loader@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.0.tgz#c7d6b0d6b0fccd51ab3ffc58a78d32b8d89a7be2" @@ -18244,11 +18607,16 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.0.1, uuid@^3.3.2, uuid@^3.4.0: +uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.0.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== + uuid@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" @@ -18500,6 +18868,16 @@ webpack-hot-middleware@^2.25.0: querystring "^0.2.0" strip-ansi "^3.0.0" +webpack-log@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" + integrity sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA== + dependencies: + chalk "^2.1.0" + log-symbols "^2.1.0" + loglevelnext "^1.0.1" + uuid "^3.1.0" + webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -18516,14 +18894,14 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack-virtual-modules@^0.2.0, webpack-virtual-modules@^0.2.2: +webpack-virtual-modules@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz#20863dc3cb6bb2104729fff951fbe14b18bd0299" integrity sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA== dependencies: debug "^3.0.0" -webpack@^4.33.0, webpack@^4.38.0, webpack@^4.41.5, webpack@^4.43.0: +webpack@^4.41.5, webpack@^4.43.0: version "4.44.2" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== From 5311cd527bb343cc0cd51340e4e0697e174bbc8e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 18 Nov 2020 11:10:09 +0100 Subject: [PATCH 14/25] Fix test for jest 26 --- .../components/buttons/DeletePermissionButton.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-ui/ui-webapp/src/repos/permissions/components/buttons/DeletePermissionButton.test.tsx b/scm-ui/ui-webapp/src/repos/permissions/components/buttons/DeletePermissionButton.test.tsx index 2e34e07160..f8f6d9ddea 100644 --- a/scm-ui/ui-webapp/src/repos/permissions/components/buttons/DeletePermissionButton.test.tsx +++ b/scm-ui/ui-webapp/src/repos/permissions/components/buttons/DeletePermissionButton.test.tsx @@ -34,7 +34,7 @@ import DeletePermissionButton from "./DeletePermissionButton"; jest.mock("@scm-manager/ui-components", () => ({ ConfirmAlert: (({ children }) =>
{children}
) as FC, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton + DeleteButton: jest.requireActual("@scm-manager/ui-components").DeleteButton })); describe("DeletePermissionButton", () => { From b6c5a253cbbe851bccde16ceffd16910e0537d52 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 19 Nov 2020 09:26:36 +0100 Subject: [PATCH 15/25] Fixed missing messages on exceptions during hook --- .../repository/hooks/DefaultHookHandler.java | 21 ++++++++-- .../resources/sonia/scm/python/scmhooks.py | 5 ++- .../hooks/DefaultHookHandlerTest.java | 40 ++++++++++++++++--- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index b7ebefca3b..2b2e4132c2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -40,11 +40,13 @@ import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.security.BearerToken; import sonia.scm.security.CipherUtil; +import javax.annotation.Nonnull; import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.ArrayList; import java.util.List; import static java.util.Collections.singletonList; @@ -87,6 +89,9 @@ class DefaultHookHandler implements HookHandler { private Response handleHookRequest(Request request) { LOG.trace("process {} hook for node {}", request.getType(), request.getNode()); + + HgHookContextProvider context = hookContextProviderFactory.create(request.getRepositoryId(), request.getNode()); + try { if (!environment.isAcceptAble(request.getChallenge())) { LOG.warn("received hook with invalid challenge: {}", request.getChallenge()); @@ -96,7 +101,6 @@ class DefaultHookHandler implements HookHandler { authenticate(request); environment.setPending(request.getType() == RepositoryHookType.PRE_RECEIVE); - HgHookContextProvider context = hookContextProviderFactory.create(request.getRepositoryId(), request.getNode()); hookEventFacade.handle(request.getRepositoryId()).fireHookEvent(request.getType(), context); return new Response(context.getHgMessageProvider().getMessages(), false); @@ -108,7 +112,7 @@ class DefaultHookHandler implements HookHandler { return error("repository not found"); } catch (Exception ex) { LOG.warn("unknown error on hook occurred", ex); - return error("unknown error"); + return error(context, ex); } finally { environment.clearPendingState(); } @@ -122,13 +126,24 @@ class DefaultHookHandler implements HookHandler { subject.login(bearer); } + private Response error(HgHookContextProvider context, Exception ex) { + List messages = new ArrayList<>(context.getHgMessageProvider().getMessages()); + messages.add(createErrorMessage(ex.getMessage())); + return new Response(messages, true); + } + private Response error(String message) { return new Response( - singletonList(new HgHookMessage(HgHookMessage.Severity.ERROR, message)), + singletonList(createErrorMessage(message)), true ); } + @Nonnull + private HgHookMessage createErrorMessage(String message) { + return new HgHookMessage(HgHookMessage.Severity.ERROR, message); + } + private void close() { try { socket.close(); diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 8e89bb1f06..72c5f7b528 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -39,7 +39,10 @@ repositoryId = os.environ['SCM_REPOSITORY_ID'] def print_messages(ui, messages): for message in messages: - msg = "%s: %s\n" % (message['severity'], message['message']) + msg = "[SCM]" + if message['severity'] == "ERROR": + msg += " Error" + msg += ": " + message['message'] + "\n" ui.warn(msg.encode('utf-8')) def fire_hook(ui, repo, hooktype, node): diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 45a9bc5aa5..0a430727d6 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -47,7 +47,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -89,8 +91,12 @@ class DefaultHookHandlerTest { } private void mockMessageProvider() { + mockMessageProvider(new HgHookMessageProvider()); + } + + private void mockMessageProvider(HgHookMessageProvider messageProvider) { when(hookContextProviderFactory.create("42", "abc")).thenReturn(contextProvider); - when(contextProvider.getHgMessageProvider()).thenReturn(new HgHookMessageProvider()); + when(contextProvider.getHgMessageProvider()).thenReturn(messageProvider); } @AfterEach @@ -131,19 +137,43 @@ class DefaultHookHandlerTest { } @Test - void shouldHandleAuthenticationFailure() throws IOException { - doThrow(IllegalStateException.class) + void shouldHandleUnknownFailure() throws IOException { + mockMessageProvider(); + + doThrow(new IllegalStateException("Something went wrong")) .when(hookEventFacade) .handle("42"); DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); DefaultHookHandler.Response response = send(request); - assertError(response, "unknown"); + assertError(response, "Something went wrong"); } @Test - void shouldHandleUnknownFailure() throws IOException { + void shouldSendMessagesOnException() throws IOException { + HgHookMessageProvider messageProvider = new HgHookMessageProvider(); + messageProvider.sendMessage("Some note"); + messageProvider.sendMessage("Some error"); + mockMessageProvider(messageProvider); + + doThrow(new IllegalStateException("Abort it")) + .when(hookEventFacade) + .handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); + + List received = response.getMessages() + .stream() + .map(HgHookMessage::getMessage) + .collect(Collectors.toList()); + + assertThat(received).containsExactly("Some note", "Some error", "Abort it"); + } + + @Test + void shouldHandleAuthenticationFailure() throws IOException { doThrow(AuthenticationException.class) .when(subject) .login(any(AuthenticationToken.class)); From 982743e82766ae17b19fb32cac995455277c26fb Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 19 Nov 2020 10:21:17 +0100 Subject: [PATCH 16/25] Fix review findings --- .../repository/hooks/DefaultHookHandler.java | 4 ++ .../scm/repository/hooks/HookServer.java | 54 +++++++++++-------- .../hooks/DefaultHookHandlerTest.java | 28 ++++++++++ 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index 2b2e4132c2..2d035bf32f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -32,6 +32,7 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.ExceptionWithContext; import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; @@ -110,6 +111,9 @@ class DefaultHookHandler implements HookHandler { } catch (NotFoundException ex) { LOG.warn("could not find repository with id {}", request.getRepositoryId(), ex); return error("repository not found"); + } catch (ExceptionWithContext ex) { + LOG.debug("scm exception on hook occurred", ex); + return error(context, ex); } catch (Exception ex) { LOG.warn("unknown error on hook occurred", ex); return error(context, ex); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java index ac27323018..5b025e981c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java @@ -58,25 +58,6 @@ public class HookServer implements AutoCloseable { this.handlerFactory = handlerFactory; } - private ExecutorService createAcceptor() { - return Executors.newSingleThreadExecutor( - createThreadFactory("HgHookAcceptor") - ); - } - - private ExecutorService createWorkerPool() { - return Executors.newCachedThreadPool( - createThreadFactory("HgHookWorker-%d") - ); - } - - @Nonnull - private ThreadFactory createThreadFactory(String hgHookAcceptor) { - return new ThreadFactoryBuilder() - .setNameFormat(hgHookAcceptor) - .build(); - } - public int start() throws IOException { acceptor = createAcceptor(); workerPool = createWorkerPool(); @@ -126,15 +107,42 @@ public class HookServer implements AutoCloseable { return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); } + private ExecutorService createAcceptor() { + return Executors.newSingleThreadExecutor( + createThreadFactory("HgHookAcceptor") + ); + } + + private ExecutorService createWorkerPool() { + return Executors.newCachedThreadPool( + createThreadFactory("HgHookWorker-%d") + ); + } + + @Nonnull + private ThreadFactory createThreadFactory(String hgHookAcceptor) { + return new ThreadFactoryBuilder() + .setNameFormat(hgHookAcceptor) + .build(); + } + @Override - public void close() throws IOException { - if (serverSocket != null) { - serverSocket.close(); - } + public void close() { + closeSocket(); shutdown(acceptor); shutdown(workerPool); } + private void closeSocket() { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException ex) { + LOG.warn("failed to close server socket", ex); + } + } + } + private void shutdown(ExecutorService acceptor) { if (acceptor != null) { acceptor.shutdown(); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 0a430727d6..fcdbc4792c 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -34,6 +34,7 @@ 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.ExceptionWithContext; import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; @@ -47,6 +48,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -150,6 +152,20 @@ class DefaultHookHandlerTest { assertError(response, "Something went wrong"); } + @Test + void shouldHandleExceptionWithContext() throws IOException { + mockMessageProvider(); + + doThrow(new TestingException("Exception with Context")) + .when(hookEventFacade) + .handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); + + assertError(response, "Exception with Context"); + } + @Test void shouldSendMessagesOnException() throws IOException { HgHookMessageProvider messageProvider = new HgHookMessageProvider(); @@ -245,4 +261,16 @@ class DefaultHookHandlerTest { return Sockets.read(new ByteArrayInputStream(output.toByteArray()), DefaultHookHandler.Response.class); } + private static class TestingException extends ExceptionWithContext { + + private TestingException(String message) { + super(Collections.emptyList(), message); + } + + @Override + public String getCode() { + return "42"; + } + } + } From 29faa5ec6cfc41f72ca0aea58a3871f762ecb849 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 19 Nov 2020 10:21:17 +0100 Subject: [PATCH 17/25] Fix review findings --- .../repository/hooks/DefaultHookHandler.java | 4 + .../scm/repository/hooks/HookServer.java | 116 +++++++++------- .../hooks/DefaultHookHandlerTest.java | 28 ++++ .../scm/security/JwtAccessTokenBuilder.java | 13 +- .../security/JwtAccessTokenBuilderTest.java | 131 +++++++++++++----- 5 files changed, 199 insertions(+), 93 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index 2b2e4132c2..2d035bf32f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -32,6 +32,7 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.ExceptionWithContext; import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; @@ -110,6 +111,9 @@ class DefaultHookHandler implements HookHandler { } catch (NotFoundException ex) { LOG.warn("could not find repository with id {}", request.getRepositoryId(), ex); return error("repository not found"); + } catch (ExceptionWithContext ex) { + LOG.debug("scm exception on hook occurred", ex); + return error(context, ex); } catch (Exception ex) { LOG.warn("unknown error on hook occurred", ex); return error(context, ex); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java index ac27323018..90a15c6fb4 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookServer.java @@ -52,12 +52,63 @@ public class HookServer implements AutoCloseable { private ExecutorService acceptor; private ExecutorService workerPool; private ServerSocket serverSocket; + private SecurityManager securityManager; @Inject public HookServer(HookHandlerFactory handlerFactory) { this.handlerFactory = handlerFactory; } + public int start() throws IOException { + securityManager = SecurityUtils.getSecurityManager(); + + acceptor = createAcceptor(); + workerPool = createWorkerPool(); + serverSocket = createServerSocket(); + // set timeout to 2 min, to avoid blocking clients + serverSocket.setSoTimeout(2 * 60 * 1000); + + accept(); + + int port = serverSocket.getLocalPort(); + LOG.info("open hg hook server on port {}", port); + return port; + } + + private void accept() { + acceptor.submit(() -> { + while (!serverSocket.isClosed()) { + try { + LOG.trace("wait for next hook connection"); + Socket clientSocket = serverSocket.accept(); + LOG.trace("accept incoming hook client from {}", clientSocket.getInetAddress()); + HookHandler hookHandler = handlerFactory.create(clientSocket); + workerPool.submit(associateSecurityManager(hookHandler)); + } catch (IOException ex) { + LOG.debug("failed to accept socket, possible closed", ex); + } + } + LOG.warn("ServerSocket is closed"); + }); + } + + private Runnable associateSecurityManager(HookHandler hookHandler) { + return () -> { + ThreadContext.bind(securityManager); + try { + hookHandler.run(); + } finally { + ThreadContext.unbindSubject(); + ThreadContext.unbindSecurityManager(); + } + }; + } + + @Nonnull + private ServerSocket createServerSocket() throws IOException { + return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + } + private ExecutorService createAcceptor() { return Executors.newSingleThreadExecutor( createThreadFactory("HgHookAcceptor") @@ -77,64 +128,23 @@ public class HookServer implements AutoCloseable { .build(); } - public int start() throws IOException { - acceptor = createAcceptor(); - workerPool = createWorkerPool(); - serverSocket = createServerSocket(); - // set timeout to 2 min, to avoid blocking clients - serverSocket.setSoTimeout(2 * 60 * 1000); - - accept(); - - int port = serverSocket.getLocalPort(); - LOG.info("open hg hook server on port {}", port); - return port; - } - - private void accept() { - SecurityManager securityManager = SecurityUtils.getSecurityManager(); - acceptor.submit(() -> { - while (!serverSocket.isClosed()) { - try { - LOG.trace("wait for next hook connection"); - Socket clientSocket = serverSocket.accept(); - LOG.trace("accept incoming hook client from {}", clientSocket.getInetAddress()); - HookHandler hookHandler = handlerFactory.create(clientSocket); - workerPool.submit(associateSecurityManager(securityManager, hookHandler)); - } catch (IOException ex) { - LOG.debug("failed to accept socket, possible closed", ex); - } - } - LOG.warn("ServerSocket is closed"); - }); - } - - private Runnable associateSecurityManager(SecurityManager securityManager, HookHandler hookHandler) { - return () -> { - ThreadContext.bind(securityManager); - try { - hookHandler.run(); - } finally { - ThreadContext.unbindSubject(); - ThreadContext.unbindSecurityManager(); - } - }; - } - - @Nonnull - private ServerSocket createServerSocket() throws IOException { - return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); - } - @Override - public void close() throws IOException { - if (serverSocket != null) { - serverSocket.close(); - } + public void close() { + closeSocket(); shutdown(acceptor); shutdown(workerPool); } + private void closeSocket() { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException ex) { + LOG.warn("failed to close server socket", ex); + } + } + } + private void shutdown(ExecutorService acceptor) { if (acceptor != null) { acceptor.shutdown(); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 0a430727d6..fcdbc4792c 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -34,6 +34,7 @@ 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.ExceptionWithContext; import sonia.scm.NotFoundException; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; @@ -47,6 +48,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -150,6 +152,20 @@ class DefaultHookHandlerTest { assertError(response, "Something went wrong"); } + @Test + void shouldHandleExceptionWithContext() throws IOException { + mockMessageProvider(); + + doThrow(new TestingException("Exception with Context")) + .when(hookEventFacade) + .handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); + + assertError(response, "Exception with Context"); + } + @Test void shouldSendMessagesOnException() throws IOException { HgHookMessageProvider messageProvider = new HgHookMessageProvider(); @@ -245,4 +261,16 @@ class DefaultHookHandlerTest { return Sockets.read(new ByteArrayInputStream(output.toByteArray()), DefaultHookHandler.Response.class); } + private static class TestingException extends ExceptionWithContext { + + private TestingException(String message) { + super(Collections.emptyList(), message); + } + + @Override + public String getCode() { + return "42"; + } + } + } diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java index c0d8521220..4e43f45e68 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java +++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessTokenBuilder.java @@ -24,6 +24,7 @@ package sonia.scm.security; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Maps; @@ -57,6 +58,12 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { */ private static final Logger LOG = LoggerFactory.getLogger(JwtAccessTokenBuilder.class); + @VisibleForTesting + static final long DEFAULT_REFRESHABLE = 12L; + + @VisibleForTesting + static final TimeUnit DEFAULT_REFRESHABLE_UNIT = TimeUnit.HOURS; + private final KeyGenerator keyGenerator; private final SecureKeyResolver keyResolver; private final Clock clock; @@ -65,8 +72,8 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { private String issuer; private long expiresIn = 1; private TimeUnit expiresInUnit = TimeUnit.HOURS; - private long refreshableFor = 12; - private TimeUnit refreshableForUnit = TimeUnit.HOURS; + private long refreshableFor = DEFAULT_REFRESHABLE; + private TimeUnit refreshableForUnit = DEFAULT_REFRESHABLE_UNIT; private Instant refreshExpiration; private String parentKeyId; private Scope scope = Scope.empty(); @@ -183,7 +190,7 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder { if (refreshableFor > 0) { long re = refreshableForUnit.toMillis(refreshableFor); - claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, new Date(now.toEpochMilli() + re).getTime()); + claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, Date.from(now.plusMillis(re))); } else if (refreshExpiration != null) { claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, Date.from(refreshExpiration)); } diff --git a/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java b/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java index 3f36a4e341..66ec8a0a6e 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java @@ -39,15 +39,17 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.Clock; +import java.time.Instant; import java.util.Collection; +import java.util.Date; import java.util.Set; import java.util.concurrent.TimeUnit; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.*; import static sonia.scm.security.SecureKeyTestUtil.createSecureKey; /** @@ -85,52 +87,107 @@ class JwtAccessTokenBuilderTest { ThreadContext.unbindSubject(); } - /** - * Prepare mocks and set up object under test. - */ @BeforeEach - void setUpObjectUnderTest() { + void setUpDependencies() { lenient().when(keyGenerator.createKey()).thenReturn("42"); lenient().when(secureKeyResolver.getSecureKey(anyString())).thenReturn(createSecureKey()); enrichers = Sets.newHashSet(); factory = new JwtAccessTokenBuilderFactory(keyGenerator, secureKeyResolver, enrichers); } - /** - * Tests {@link JwtAccessTokenBuilder#build()}. - */ - @Test - void testBuild() { - JwtAccessToken token = factory.create().subject("dent") - .issuer("https://www.scm-manager.org") - .expiresIn(1, TimeUnit.MINUTES) - .custom("a", "b") - .scope(Scope.valueOf("repo:*")) - .build(); + @Nested + class SimpleTests { - // assert claims - assertClaims(token); + /** + * Prepare mocks and set up object under test. + */ + @BeforeEach + void setUpObjectUnderTest() { + factory = new JwtAccessTokenBuilderFactory(keyGenerator, secureKeyResolver, enrichers); + } + + /** + * Tests {@link JwtAccessTokenBuilder#build()}. + */ + @Test + void testBuild() { + JwtAccessToken token = factory.create().subject("dent") + .issuer("https://www.scm-manager.org") + .expiresIn(1, TimeUnit.MINUTES) + .custom("a", "b") + .scope(Scope.valueOf("repo:*")) + .build(); + + // assert claims + assertClaims(token); + + // reparse and assert again + String compact = token.compact(); + assertThat(compact).isNotEmpty(); + Claims claims = Jwts.parser() + .setSigningKey(secureKeyResolver.getSecureKey("dent").getBytes()) + .parseClaimsJws(compact) + .getBody(); + assertClaims(new JwtAccessToken(claims, compact)); + } + + private void assertClaims(JwtAccessToken token) { + assertThat(token.getId()).isNotEmpty(); + assertThat(token.getIssuedAt()).isNotNull(); + assertThat(token.getExpiration()).isNotNull(); + assertThat(token.getExpiration().getTime() > token.getIssuedAt().getTime()).isTrue(); + assertThat(token.getSubject()).isEqualTo("dent"); + assertThat(token.getIssuer()).isNotEmpty(); + assertThat(token.getIssuer()).get().isEqualTo("https://www.scm-manager.org"); + assertThat(token.getCustom("a")).get().isEqualTo("b"); + assertThat(token.getScope()).hasToString("[\"repo:*\"]"); + } - // reparse and assert again - String compact = token.compact(); - assertThat(compact).isNotEmpty(); - Claims claims = Jwts.parser() - .setSigningKey(secureKeyResolver.getSecureKey("dent").getBytes()) - .parseClaimsJws(compact) - .getBody(); - assertClaims(new JwtAccessToken(claims, compact)); } - private void assertClaims(JwtAccessToken token) { - assertThat(token.getId()).isNotEmpty(); - assertThat(token.getIssuedAt()).isNotNull(); - assertThat(token.getExpiration()).isNotNull(); - assertThat(token.getExpiration().getTime() > token.getIssuedAt().getTime()).isTrue(); - assertThat(token.getSubject()).isEqualTo("dent"); - assertThat(token.getIssuer()).isNotEmpty(); - assertThat(token.getIssuer()).get().isEqualTo("https://www.scm-manager.org"); - assertThat(token.getCustom("a")).get().isEqualTo("b"); - assertThat(token.getScope()).hasToString("[\"repo:*\"]"); + @Nested + class ClockTests { + + @Mock + private Clock clock; + + @BeforeEach + void setUpObjectUnderTest() { + factory = new JwtAccessTokenBuilderFactory(keyGenerator, secureKeyResolver, enrichers, clock); + } + + @Test + void shouldSetRefreshExpiration() { + Instant now = Instant.now(); + when(clock.instant()).thenReturn(now); + + JwtAccessToken token = factory.create() + .subject("dent") + .refreshableFor(2, TimeUnit.SECONDS) + .build(); + + assertThat(token.getRefreshExpiration()).isPresent(); + Date date = token.getRefreshExpiration().get(); + + assertThat(date).hasSameTimeAs(Date.from(now.plusSeconds(2L))); + } + + @Test + void shouldSetDefaultRefreshExpiration() { + Instant now = Instant.now(); + when(clock.instant()).thenReturn(now); + + JwtAccessToken token = factory.create() + .subject("dent") + .build(); + + assertThat(token.getRefreshExpiration()).isPresent(); + Date date = token.getRefreshExpiration().get(); + + long defaultRefresh = JwtAccessTokenBuilder.DEFAULT_REFRESHABLE_UNIT.toMillis(JwtAccessTokenBuilder.DEFAULT_REFRESHABLE); + assertThat(date).hasSameTimeAs(Date.from(now.plusMillis(defaultRefresh))); + } + } @Nested From 1311061c82666f2a2213b52f637580c77b82c017 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 19 Nov 2020 14:30:39 +0100 Subject: [PATCH 18/25] Pass transaction id from request to mercurial hooks --- pom.xml | 2 +- scm-core/pom.xml | 6 ++ .../main/java/sonia/scm/TransactionId.java | 71 +++++++++++++++++++ .../java/sonia/scm/TransactionIdTest.java | 42 +++++++++++ scm-plugins/scm-hg-plugin/pom.xml | 14 ++++ .../DefaultHgEnvironmentBuilder.java | 4 ++ .../repository/hooks/DefaultHookHandler.java | 4 ++ .../resources/sonia/scm/python/scmhooks.py | 3 +- .../DefaultHgEnvironmentBuilderTest.java | 9 +++ .../hooks/DefaultHookHandlerTest.java | 20 +++++- .../main/java/sonia/scm/filter/MDCFilter.java | 10 ++- .../java/sonia/scm/filter/MDCFilterTest.java | 37 +++++----- 12 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/TransactionId.java create mode 100644 scm-core/src/test/java/sonia/scm/TransactionIdTest.java diff --git a/pom.xml b/pom.xml index b8192de3a8..46c0b2db13 100644 --- a/pom.xml +++ b/pom.xml @@ -506,7 +506,7 @@ - jakarta.xml.bind + jakarta.xml.bind jakarta.xml.bind-api ${jaxb.version} diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 85a539c471..6ff4d7a7b0 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -250,6 +250,12 @@ test + + ch.qos.logback + logback-classic + test + + diff --git a/scm-core/src/main/java/sonia/scm/TransactionId.java b/scm-core/src/main/java/sonia/scm/TransactionId.java new file mode 100644 index 0000000000..68169ba7d4 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/TransactionId.java @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm; + +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.MDC; + +import java.util.Optional; + +/** + * Id of the current transaction. + * The transaction id is mainly used for logging and debugging. + * + * @since 2.10.0 + */ +public final class TransactionId { + + @VisibleForTesting + public static final String KEY = "transaction_id"; + + private TransactionId() { + } + + /** + * Binds the given transaction id to the current thread. + * + * @param transactionId transaction id + */ + public static void set(String transactionId) { + MDC.put(KEY, transactionId); + } + + /** + * Returns an optional transaction id. + * If there is no transaction id bound to the thread, the method will return an empty optional. + * + * @return optional transaction id + */ + public static Optional get() { + return Optional.ofNullable(MDC.get(KEY)); + } + + /** + * Removes a bound transaction id from the current thread. + */ + public static void clear() { + MDC.remove(KEY); + } +} diff --git a/scm-core/src/test/java/sonia/scm/TransactionIdTest.java b/scm-core/src/test/java/sonia/scm/TransactionIdTest.java new file mode 100644 index 0000000000..053f2ce86c --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/TransactionIdTest.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TransactionIdTest { + + @Test + void shouldSetGetAndClear() { + TransactionId.set("42"); + + assertThat(TransactionId.get()).contains("42"); + TransactionId.clear(); + assertThat(TransactionId.get()).isEmpty(); + } + +} diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index a6f125a6f9..3b280b4519 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -50,9 +50,23 @@ com.google.guava guava + + org.slf4j + slf4j-simple + + + org.slf4j + slf4j-nop + + + ch.qos.logback + logback-classic + test + + diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java index 597a56af15..1888f0aeae 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java @@ -27,6 +27,7 @@ package sonia.scm.repository; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; +import sonia.scm.TransactionId; import sonia.scm.repository.hooks.HookEnvironment; import sonia.scm.repository.hooks.HookServer; import sonia.scm.security.AccessToken; @@ -60,6 +61,8 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { static final String ENV_REPOSITORY_ID = "SCM_REPOSITORY_ID"; @VisibleForTesting static final String ENV_HTTP_POST_ARGS = "SCM_HTTP_POST_ARGS"; + @VisibleForTesting + static final String ENV_TRANSACTION_ID = "SCM_TRANSACTION_ID"; private final AccessTokenBuilderFactory accessTokenBuilderFactory; private final HgRepositoryHandler repositoryHandler; @@ -114,6 +117,7 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { env.put(ENV_HOOK_PORT, String.valueOf(getHookPort())); env.put(ENV_BEARER_TOKEN, accessToken()); env.put(ENV_CHALLENGE, hookEnvironment.getChallenge()); + TransactionId.get().ifPresent(transactionId -> env.put(ENV_TRANSACTION_ID, transactionId)); } private String accessToken() { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index 2d035bf32f..9ec95a5dcc 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -34,6 +34,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ExceptionWithContext; import sonia.scm.NotFoundException; +import sonia.scm.TransactionId; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; import sonia.scm.repository.spi.HgHookContextProvider; @@ -90,6 +91,7 @@ class DefaultHookHandler implements HookHandler { private Response handleHookRequest(Request request) { LOG.trace("process {} hook for node {}", request.getType(), request.getNode()); + TransactionId.set(request.getTransactionId()); HgHookContextProvider context = hookContextProviderFactory.create(request.getRepositoryId(), request.getNode()); @@ -119,6 +121,7 @@ class DefaultHookHandler implements HookHandler { return error(context, ex); } finally { environment.clearPendingState(); + TransactionId.clear(); } } @@ -161,6 +164,7 @@ class DefaultHookHandler implements HookHandler { public static class Request { private String token; private RepositoryHookType type; + private String transactionId; private String repositoryId; private String challenge; private String node; diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 72c5f7b528..1fdbc07b15 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -36,6 +36,7 @@ port = os.environ['SCM_HOOK_PORT'] challenge = os.environ['SCM_CHALLENGE'] token = os.environ['SCM_BEARER_TOKEN'] repositoryId = os.environ['SCM_REPOSITORY_ID'] +transactionId = os.environ['SCM_TRANSACTION_ID'] def print_messages(ui, messages): for message in messages: @@ -50,7 +51,7 @@ def fire_hook(ui, repo, hooktype, node): ui.debug( b"send scm-hook for " + node + b"\n" ) connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - values = {'token': token, 'type': hooktype, 'repositoryId': repositoryId, 'challenge': challenge, 'node': node.decode('utf8') } + values = {'token': token, 'type': hooktype, 'repositoryId': repositoryId, 'transactionId': transactionId, 'challenge': challenge, 'node': node.decode('utf8') } connection.connect(("127.0.0.1", int(port))) connection.send(json.dumps(values).encode('utf-8')) diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java index c28bb21e78..7bf3a3c5bf 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java @@ -33,6 +33,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.SCMContext; +import sonia.scm.TransactionId; import sonia.scm.repository.hooks.HookEnvironment; import sonia.scm.repository.hooks.HookServer; import sonia.scm.security.AccessToken; @@ -102,6 +103,14 @@ class DefaultHgEnvironmentBuilderTest { .containsEntry(ENV_HOOK_PORT, "2042"); } + @Test + void shouldSetTransactionId() throws IOException { + TransactionId.set("ti42"); + Repository heartOfGold = prepareForWrite("/opt/python", "21"); + Map env = builder.write(heartOfGold); + assertThat(env).containsEntry(ENV_TRANSACTION_ID, "ti42"); + } + @Test void shouldThrowIllegalStateIfServerCouldNotBeStarted() throws IOException { when(server.start()).thenThrow(new IOException("failed to start")); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index fcdbc4792c..541e0620ef 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -36,6 +36,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.ExceptionWithContext; import sonia.scm.NotFoundException; +import sonia.scm.TransactionId; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.api.HgHookMessage; import sonia.scm.repository.api.HgHookMessageProvider; @@ -188,6 +189,23 @@ class DefaultHookHandlerTest { assertThat(received).containsExactly("Some note", "Some error", "Abort it"); } + @Test + void shouldSetAndClearTransactionId() throws IOException { + mockMessageProvider(); + + AtomicReference ref = new AtomicReference<>(); + doAnswer(ic -> { + TransactionId.get().ifPresent(ref::set); + return null; + }).when(hookEventFacade).handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + send(request); + + assertThat(ref).hasValue("ti21"); + assertThat(TransactionId.get()).isEmpty(); + } + @Test void shouldHandleAuthenticationFailure() throws IOException { doThrow(AuthenticationException.class) @@ -244,7 +262,7 @@ class DefaultHookHandlerTest { private DefaultHookHandler.Request createRequest(RepositoryHookType type, String challenge) { String secret = CipherUtil.getInstance().encode("secret"); return new DefaultHookHandler.Request( - secret, type, "42", challenge, "abc" + secret, type, "ti21", "42", challenge, "abc" ); } diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index 6272e04fd8..d254546ff0 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.filter; //~--- non-JDK imports -------------------------------------------------------- @@ -34,6 +34,7 @@ import org.apache.shiro.subject.Subject; import org.slf4j.MDC; import sonia.scm.SCMContext; +import sonia.scm.TransactionId; import sonia.scm.security.DefaultKeyGenerator; import sonia.scm.web.filter.HttpFilter; @@ -72,9 +73,6 @@ public class MDCFilter extends HttpFilter @VisibleForTesting static final String MDC_USERNAME = "username"; - @VisibleForTesting - static final String MDC_TRANSACTION_ID = "transaction_id"; - //~--- methods -------------------------------------------------------------- /** @@ -98,7 +96,7 @@ public class MDCFilter extends HttpFilter MDC.put(MDC_CLIENT_HOST, request.getRemoteHost()); MDC.put(MDC_REQUEST_METHOD, request.getMethod()); MDC.put(MDC_REQUEST_URI, request.getRequestURI()); - MDC.put(MDC_TRANSACTION_ID, TRANSACTION_KEY_GENERATOR.createKey()); + TransactionId.set(TRANSACTION_KEY_GENERATOR.createKey()); try { @@ -111,7 +109,7 @@ public class MDCFilter extends HttpFilter MDC.remove(MDC_CLIENT_HOST); MDC.remove(MDC_REQUEST_METHOD); MDC.remove(MDC_REQUEST_URI); - MDC.remove(MDC_TRANSACTION_ID); + TransactionId.clear(); } } diff --git a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java index db12e17c1d..935285dbcf 100644 --- a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.filter; import com.github.sdorra.shiro.ShiroRule; @@ -34,6 +34,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.slf4j.MDC; import sonia.scm.AbstractTestBase; import sonia.scm.SCMContext; +import sonia.scm.TransactionId; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -50,28 +51,28 @@ import static org.mockito.Mockito.when; /** * Unit tests for {@link MDCFilter}. - * + * * @author Sebastian Sdorra */ @RunWith(MockitoJUnitRunner.Silent.class) public class MDCFilterTest extends AbstractTestBase { - + @Rule public ShiroRule shiro = new ShiroRule(); - + @Mock private HttpServletRequest request; - + @Mock private HttpServletResponse response; - + private final MDCFilter filter = new MDCFilter(); /** * Tests {@link MDCFilter#doFilter(HttpServletRequest, HttpServletResponse, FilterChain)}. - * + * * @throws IOException - * @throws ServletException + * @throws ServletException */ @Test @SubjectAware( @@ -85,44 +86,44 @@ public class MDCFilterTest extends AbstractTestBase { when(request.getRemoteAddr()).thenReturn("127.0.0.1"); when(request.getRemoteHost()).thenReturn("localhost"); when(request.getMethod()).thenReturn("GET"); - + MDCCapturingFilterChain chain = new MDCCapturingFilterChain(); filter.doFilter(request, response, chain); - + assertNotNull(chain.ctx); assertEquals("trillian", chain.ctx.get(MDCFilter.MDC_USERNAME)); assertEquals("api/v1/repositories", chain.ctx.get(MDCFilter.MDC_REQUEST_URI)); assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIENT_IP)); assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIENT_HOST)); assertEquals("GET", chain.ctx.get(MDCFilter.MDC_REQUEST_METHOD)); - assertNotNull(chain.ctx.get(MDCFilter.MDC_TRANSACTION_ID)); + assertNotNull(chain.ctx.get(TransactionId.KEY)); } - + /** * Tests {@link MDCFilter#doFilter(HttpServletRequest, HttpServletResponse, FilterChain)} as anonymous user. - * + * * @throws IOException - * @throws ServletException + * @throws ServletException */ @Test @SubjectAware public void testDoFilterAsAnonymous() throws IOException, ServletException { MDCCapturingFilterChain chain = new MDCCapturingFilterChain(); filter.doFilter(request, response, chain); - + assertNotNull(chain.ctx); assertEquals(SCMContext.USER_ANONYMOUS, chain.ctx.get(MDCFilter.MDC_USERNAME)); } - + private static class MDCCapturingFilterChain implements FilterChain { private Map ctx; - + @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { this.ctx = MDC.getCopyOfContextMap(); } - + } } From 959dfb8f451eecfc6e2c5311102f72a5a3ce3933 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 19 Nov 2020 14:49:29 +0100 Subject: [PATCH 19/25] Fix compiler error --- .../main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java index 743cd2cfaf..7106c7c85a 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java @@ -39,7 +39,5 @@ interface UpdateHgConfigDto { boolean isShowRevisionInId(); - boolean isDisableHookSSLValidation(); - boolean isEnableHttpPostArgs(); } From 73b2c4adc2cbd11351dbde13e876ea450897915c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 21 Nov 2020 09:22:46 +0100 Subject: [PATCH 20/25] Do not expose internal exception messages --- .../repository/hooks/DefaultHookHandler.java | 8 ++--- .../hooks/DefaultHookHandlerTest.java | 36 +++++++++++++++---- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index 9ec95a5dcc..fce7d5dfae 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -115,10 +115,10 @@ class DefaultHookHandler implements HookHandler { return error("repository not found"); } catch (ExceptionWithContext ex) { LOG.debug("scm exception on hook occurred", ex); - return error(context, ex); + return error(context, ex.getMessage()); } catch (Exception ex) { LOG.warn("unknown error on hook occurred", ex); - return error(context, ex); + return error(context, "unknown error"); } finally { environment.clearPendingState(); TransactionId.clear(); @@ -133,9 +133,9 @@ class DefaultHookHandler implements HookHandler { subject.login(bearer); } - private Response error(HgHookContextProvider context, Exception ex) { + private Response error(HgHookContextProvider context, String message) { List messages = new ArrayList<>(context.getHgMessageProvider().getMessages()); - messages.add(createErrorMessage(ex.getMessage())); + messages.add(createErrorMessage(message)); return new Response(messages, true); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 541e0620ef..1be67c4c44 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -150,7 +150,7 @@ class DefaultHookHandlerTest { DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); DefaultHookHandler.Response response = send(request); - assertError(response, "Something went wrong"); + assertError(response, "unknown error"); } @Test @@ -168,11 +168,8 @@ class DefaultHookHandlerTest { } @Test - void shouldSendMessagesOnException() throws IOException { - HgHookMessageProvider messageProvider = new HgHookMessageProvider(); - messageProvider.sendMessage("Some note"); - messageProvider.sendMessage("Some error"); - mockMessageProvider(messageProvider); + void shouldSendMessagesOnUnknownException() throws IOException { + mockMessageProviderWithMessages(); doThrow(new IllegalStateException("Abort it")) .when(hookEventFacade) @@ -181,12 +178,37 @@ class DefaultHookHandlerTest { DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); DefaultHookHandler.Response response = send(request); + assertMessages(response, "unknown error"); + } + + @Test + void shouldSendMessagesOnExceptionWithContext() throws IOException { + mockMessageProviderWithMessages(); + + doThrow(new TestingException("Exception with Context")) + .when(hookEventFacade) + .handle("42"); + + DefaultHookHandler.Request request = createRequest(RepositoryHookType.POST_RECEIVE); + DefaultHookHandler.Response response = send(request); + + assertMessages(response, "Exception with Context"); + } + + private void assertMessages(DefaultHookHandler.Response response, String errorMessage) { List received = response.getMessages() .stream() .map(HgHookMessage::getMessage) .collect(Collectors.toList()); - assertThat(received).containsExactly("Some note", "Some error", "Abort it"); + assertThat(received).containsExactly("Some note", "Some error", errorMessage); + } + + private void mockMessageProviderWithMessages() { + HgHookMessageProvider messageProvider = new HgHookMessageProvider(); + messageProvider.sendMessage("Some note"); + messageProvider.sendMessage("Some error"); + mockMessageProvider(messageProvider); } @Test From c0ae910d237f9e3c50a75abc01b82b0505c3c588 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 22 Nov 2020 13:35:45 +0100 Subject: [PATCH 21/25] Implement more robust socket hook protocol --- .../repository/hooks/DefaultHookHandler.java | 33 ++++--- .../sonia/scm/repository/hooks/Sockets.java | 60 ++++++++++-- .../resources/sonia/scm/python/scmhooks.py | 26 ++--- .../hooks/DefaultHookHandlerTest.java | 4 +- .../scm/repository/hooks/HookServerTest.java | 4 +- .../scm/repository/hooks/SocketsTest.java | 94 +++++++++++++++++++ 6 files changed, 185 insertions(+), 36 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java index fce7d5dfae..c189299d72 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/DefaultHookHandler.java @@ -79,37 +79,47 @@ class DefaultHookHandler implements HookHandler { LOG.warn("failed to read hook request", e); } finally { LOG.trace("close client socket"); + TransactionId.clear(); close(); } } private void handleHookRequest(InputStream input, OutputStream output) throws IOException { - Request request = Sockets.read(input, Request.class); + Request request = Sockets.receive(input, Request.class); + TransactionId.set(request.getTransactionId()); Response response = handleHookRequest(request); Sockets.send(output, response); } private Response handleHookRequest(Request request) { LOG.trace("process {} hook for node {}", request.getType(), request.getNode()); - TransactionId.set(request.getTransactionId()); + if (!environment.isAcceptAble(request.getChallenge())) { + LOG.warn("received hook with invalid challenge: {}", request.getChallenge()); + return error("invalid hook challenge"); + } + + try { + authenticate(request); + + return fireHook(request); + } catch (AuthenticationException ex) { + LOG.warn("hook authentication failed", ex); + return error("hook authentication failed"); + } + } + + @Nonnull + private Response fireHook(Request request) { HgHookContextProvider context = hookContextProviderFactory.create(request.getRepositoryId(), request.getNode()); try { - if (!environment.isAcceptAble(request.getChallenge())) { - LOG.warn("received hook with invalid challenge: {}", request.getChallenge()); - return error("invalid hook challenge"); - } - - authenticate(request); environment.setPending(request.getType() == RepositoryHookType.PRE_RECEIVE); hookEventFacade.handle(request.getRepositoryId()).fireHookEvent(request.getType(), context); return new Response(context.getHgMessageProvider().getMessages(), false); - } catch (AuthenticationException ex) { - LOG.warn("hook authentication failed", ex); - return error("hook authentication failed"); + } catch (NotFoundException ex) { LOG.warn("could not find repository with id {}", request.getRepositoryId(), ex); return error("repository not found"); @@ -121,7 +131,6 @@ class DefaultHookHandler implements HookHandler { return error(context, "unknown error"); } finally { environment.clearPendingState(); - TransactionId.clear(); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java index 9791e99c31..76fe3f9f88 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java @@ -25,14 +25,20 @@ package sonia.scm.repository.hooks; import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.io.ByteArrayOutputStream; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; class Sockets { + private static final Logger LOG = LoggerFactory.getLogger(Sockets.class); + + private static final int READ_LIMIT = 8192; + private static final ObjectMapper objectMapper = new ObjectMapper(); private Sockets() { @@ -40,18 +46,54 @@ class Sockets { static void send(OutputStream out, Object object) throws IOException { byte[] bytes = objectMapper.writeValueAsBytes(object); + LOG.trace("send message length of {} to socket", bytes.length); + writeInt(out, bytes.length); + LOG.trace("send message to socket"); out.write(bytes); - out.write('\0'); + LOG.trace("flush socket"); out.flush(); } - static T read(InputStream in, Class type) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int c = in.read(); - while (c != '\0') { - buffer.write(c); - c = in.read(); + static T receive(InputStream in, Class type) throws IOException { + LOG.trace("read {} from socket", type); + int length = readInt(in); + LOG.trace("read message length of {} from socket", length); + if (length > READ_LIMIT) { + String message = String.format("received length of %d, which exceeds the limit of %d", length, READ_LIMIT); + throw new IOException(message); } - return objectMapper.readValue(buffer.toByteArray(), type); + byte[] data = read(in, length); + LOG.trace("convert message to {}", type); + return objectMapper.readValue(data, type); } + + private static void writeInt(OutputStream out, int value) throws IOException { + out.write((value >>> 24) & 0xFF); + out.write((value >>> 16) & 0xFF); + out.write((value >>> 8) & 0xFF); + out.write(value & 0xFF); + } + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + + if ((b1 | b2 | b3 | b4) < 0) { + throw new EOFException("failed to read int from socket"); + } + + return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4); + } + + private static byte[] read(InputStream in, int length) throws IOException { + byte[] buffer = new byte[length]; + int read = in.read(buffer); + if (read < length) { + throw new EOFException("failed to read bytes from socket"); + } + return buffer; + } + } diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 1fdbc07b15..1e337317e7 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -29,7 +29,7 @@ # changegroup.scm = python:scmhooks.callback # -import os, sys, json, socket +import os, sys, json, socket, struct # read environment port = os.environ['SCM_HOOK_PORT'] @@ -54,17 +54,19 @@ def fire_hook(ui, repo, hooktype, node): values = {'token': token, 'type': hooktype, 'repositoryId': repositoryId, 'transactionId': transactionId, 'challenge': challenge, 'node': node.decode('utf8') } connection.connect(("127.0.0.1", int(port))) - connection.send(json.dumps(values).encode('utf-8')) - connection.sendall(b'\0') - received = [] - byte = connection.recv(1) - while byte != b'\0': - received.append(byte) - byte = connection.recv(1) + data = json.dumps(values).encode('utf-8') + connection.send(struct.pack('>i', len(data))) + connection.sendall(data) - message = b''.join(received).decode('utf-8') - response = json.loads(message) + d = connection.recv(4, socket.MSG_WAITALL) + length = struct.unpack('>i', bytearray(d))[0] + if length > 8192: + ui.warn( b"scm-hook received message with exceeds the limit of 8192\n" ) + return True + + d = connection.recv(length, socket.MSG_WAITALL) + response = json.loads(d.decode("utf-8")) abort = response['abort'] print_messages(ui, response['messages']) @@ -94,7 +96,7 @@ def pre_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs) # newer mercurial version # we have to make in-memory changes visible to external process - # this does not happen automatically, because mercurial treat our hooks as internal hooks + # this does not happen automatically, because mercurial treat our hooks as internal hook # see hook.py at mercurial sources _exthook try: if repo is not None: @@ -103,7 +105,7 @@ def pre_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs) if tr and not tr.writepending(): ui.warn(b"no pending write transaction found") except AttributeError: - ui.debug(b"mercurial does not support currenttransation") + ui.debug(b"mercurial does not support currenttransaction") # do nothing return callback(ui, repo, "PRE_RECEIVE", node) diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java index 1be67c4c44..eed006b6ce 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/DefaultHookHandlerTest.java @@ -291,14 +291,16 @@ class DefaultHookHandlerTest { private DefaultHookHandler.Response send(DefaultHookHandler.Request request) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Sockets.send(buffer, request); + ByteArrayInputStream input = new ByteArrayInputStream(buffer.toByteArray()); when(socket.getInputStream()).thenReturn(input); + ByteArrayOutputStream output = new ByteArrayOutputStream(); when(socket.getOutputStream()).thenReturn(output); handler.run(); - return Sockets.read(new ByteArrayInputStream(output.toByteArray()), DefaultHookHandler.Response.class); + return Sockets.receive(new ByteArrayInputStream(output.toByteArray()), DefaultHookHandler.Response.class); } private static class TestingException extends ExceptionWithContext { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java index 95467edc25..ccffd36b04 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/HookServerTest.java @@ -82,7 +82,7 @@ class HookServerTest { OutputStream output = socket.getOutputStream() ) { Sockets.send(output, request); - return Sockets.read(input, Response.class); + return Sockets.receive(input, Response.class); } catch (IOException ex) { throw new RuntimeException("failed", ex); } @@ -100,7 +100,7 @@ class HookServerTest { @Override public void run() { try (InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream()) { - Request request = Sockets.read(input, Request.class); + Request request = Sockets.receive(input, Request.class); Subject subject = SecurityUtils.getSubject(); Sockets.send(output, new Response("Hello " + request.getName(), subject.getPrincipal().toString())); } catch (IOException ex) { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java new file mode 100644 index 0000000000..f20c2e93db --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.hooks; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SocketsTest { + + @Test + void shouldSendAndReceive() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Sockets.send(output, new TestValue("awesome")); + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + TestValue value = Sockets.receive(input, TestValue.class); + assertThat(value.value).isEqualTo("awesome"); + } + + @Test + void shouldFailWithTooFewBytesForLength() { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write((512 >>> 24) & 0xFF); + output.write((512 >>> 16) & 0xFF); + + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + IOException ex = assertThrows(IOException.class, () -> Sockets.receive(input, TestValue.class)); + assertThat(ex.getMessage()).contains("int"); + } + + @Test + void shouldFailWithTooFewBytesForData() { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write((16 >>> 24) & 0xFF); + output.write((16 >>> 16) & 0xFF); + output.write((16 >>> 8) & 0xFF); + output.write(16 & 0xFF); + + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + IOException ex = assertThrows(IOException.class, () -> Sockets.receive(input, TestValue.class)); + assertThat(ex.getMessage()).contains("bytes"); + } + + @Test + void shouldFailIfLimitIsExceeded() { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write((9216 >>> 24) & 0xFF); + output.write((9216 >>> 16) & 0xFF); + output.write((9216 >>> 8) & 0xFF); + output.write(9216 & 0xFF); + + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + IOException ex = assertThrows(IOException.class, () -> Sockets.receive(input, TestValue.class)); + assertThat(ex.getMessage()).contains("9216"); + } + + @Data + @AllArgsConstructor + public static class TestValue { + + private String value; + + } + +} From 7357d83b73008599a2407e26588c5f3edfbc9447 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 27 Nov 2020 08:10:18 +0100 Subject: [PATCH 22/25] Use DataInputStream and DataOutputStream instead of manual reading and writing --- .../sonia/scm/repository/hooks/Sockets.java | 50 ++++++------------- .../scm/repository/hooks/SocketsTest.java | 26 ++++++++-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java index 76fe3f9f88..4a0389a33c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/Sockets.java @@ -28,7 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.EOFException; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -47,53 +48,34 @@ class Sockets { static void send(OutputStream out, Object object) throws IOException { byte[] bytes = objectMapper.writeValueAsBytes(object); LOG.trace("send message length of {} to socket", bytes.length); - writeInt(out, bytes.length); + + DataOutputStream dataOutputStream = new DataOutputStream(out); + dataOutputStream.writeInt(bytes.length); + LOG.trace("send message to socket"); - out.write(bytes); + dataOutputStream.write(bytes); + LOG.trace("flush socket"); out.flush(); } static T receive(InputStream in, Class type) throws IOException { LOG.trace("read {} from socket", type); - int length = readInt(in); + + DataInputStream dataInputStream = new DataInputStream(in); + + int length = dataInputStream.readInt(); LOG.trace("read message length of {} from socket", length); if (length > READ_LIMIT) { String message = String.format("received length of %d, which exceeds the limit of %d", length, READ_LIMIT); throw new IOException(message); } - byte[] data = read(in, length); + + byte[] data = new byte[length]; + dataInputStream.readFully(data); + LOG.trace("convert message to {}", type); return objectMapper.readValue(data, type); } - private static void writeInt(OutputStream out, int value) throws IOException { - out.write((value >>> 24) & 0xFF); - out.write((value >>> 16) & 0xFF); - out.write((value >>> 8) & 0xFF); - out.write(value & 0xFF); - } - - private static int readInt(InputStream in) throws IOException { - int b1 = in.read(); - int b2 = in.read(); - int b3 = in.read(); - int b4 = in.read(); - - if ((b1 | b2 | b3 | b4) < 0) { - throw new EOFException("failed to read int from socket"); - } - - return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4); - } - - private static byte[] read(InputStream in, int length) throws IOException { - byte[] buffer = new byte[length]; - int read = in.read(buffer); - if (read < length) { - throw new EOFException("failed to read bytes from socket"); - } - return buffer; - } - } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java index f20c2e93db..bb5b48a420 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/hooks/SocketsTest.java @@ -30,8 +30,12 @@ import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.EOFException; import java.io.IOException; +import java.io.InputStream; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Stream.generate; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -53,8 +57,7 @@ class SocketsTest { output.write((512 >>> 16) & 0xFF); ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - IOException ex = assertThrows(IOException.class, () -> Sockets.receive(input, TestValue.class)); - assertThat(ex.getMessage()).contains("int"); + assertThrows(EOFException.class, () -> Sockets.receive(input, TestValue.class)); } @Test @@ -66,8 +69,7 @@ class SocketsTest { output.write(16 & 0xFF); ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - IOException ex = assertThrows(IOException.class, () -> Sockets.receive(input, TestValue.class)); - assertThat(ex.getMessage()).contains("bytes"); + assertThrows(EOFException.class, () -> Sockets.receive(input, TestValue.class)); } @Test @@ -83,6 +85,22 @@ class SocketsTest { assertThat(ex.getMessage()).contains("9216"); } + @Test + void shouldSendAndReceiveWithChunks() throws IOException { + String stringValue = generate(() -> "a").limit(1024).collect(joining()); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Sockets.send(output, new TestValue(stringValue)); + InputStream input = new ByteArrayInputStream(output.toByteArray()) { + @Override + public synchronized int read(byte[] b, int off, int len) { + return super.read(b, off, Math.min(8, len)); + } + }; + TestValue value = Sockets.receive(input, TestValue.class); + assertThat(value.value).hasSize(1024); + } + @Data @AllArgsConstructor public static class TestValue { From 3e19f28cc042161367d092bdd05d6cc407d2cd1b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 27 Nov 2020 08:50:22 +0100 Subject: [PATCH 23/25] Do not use socket.MSG_WAITALL --- .../main/resources/sonia/scm/python/scmhooks.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 1e337317e7..7c4ca796f5 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -46,6 +46,17 @@ def print_messages(ui, messages): msg += ": " + message['message'] + "\n" ui.warn(msg.encode('utf-8')) +def read_bytes(connection, length): + received = bytearray() + while len(received) < length: + buffer = connection.recv(length - len(received)) + received = received + buffer + return received + +def read_int(connection): + data = read_bytes(connection, 4) + return struct.unpack('>i', bytearray(data))[0] + def fire_hook(ui, repo, hooktype, node): abort = True ui.debug( b"send scm-hook for " + node + b"\n" ) @@ -59,13 +70,12 @@ def fire_hook(ui, repo, hooktype, node): connection.send(struct.pack('>i', len(data))) connection.sendall(data) - d = connection.recv(4, socket.MSG_WAITALL) - length = struct.unpack('>i', bytearray(d))[0] + length = read_int(connection) if length > 8192: ui.warn( b"scm-hook received message with exceeds the limit of 8192\n" ) return True - d = connection.recv(length, socket.MSG_WAITALL) + d = read_bytes(connection, length) response = json.loads(d.decode("utf-8")) abort = response['abort'] @@ -112,4 +122,3 @@ def pre_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs) def post_hook(ui, repo, hooktype, node=None, source=None, pending=None, **kwargs): return callback(ui, repo, "POST_RECEIVE", node) - From 136cdbb6b0bbce15a109ec9a7af2fd920ae9add4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 27 Nov 2020 10:46:37 +0100 Subject: [PATCH 24/25] Fix broken HgVersionCommand Use waitFor instead of exitValue to wait for process to finish --- .../sonia/scm/repository/spi/HgVersionCommand.java | 12 +++++++++--- .../scm/repository/spi/HgVersionCommandTest.java | 12 ++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java index 7ea5b97dde..2d4e44f5a7 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java @@ -78,6 +78,9 @@ public class HgVersionCommand { } } catch (IOException ex) { LOG.warn("failed to get python version", ex); + } catch (InterruptedException ex) { + LOG.warn("failed to get python version", ex); + Thread.currentThread().interrupt(); } return HgVersion.UNKNOWN; } @@ -88,19 +91,22 @@ public class HgVersionCommand { return exec(config.getHgBinary(), HG_ARGS).trim(); } catch (IOException ex) { LOG.warn("failed to get mercurial version", ex); - return HgVersion.UNKNOWN; + } catch (InterruptedException ex) { + LOG.warn("failed to get mercurial version", ex); + Thread.currentThread().interrupt(); } + return HgVersion.UNKNOWN; } @SuppressWarnings("UnstableApiUsage") - private String exec(String command, String[] args) throws IOException { + private String exec(String command, String[] args) throws IOException, InterruptedException { List cmd = new ArrayList<>(); cmd.add(command); cmd.addAll(Arrays.asList(args)); Process process = executor.execute(cmd); byte[] bytes = ByteStreams.toByteArray(process.getInputStream()); - int exitCode = process.exitValue(); + int exitCode = process.waitFor(); if (exitCode != 0) { throw new IOException("process ends with exit code " + exitCode); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java index 6c020efcc0..404e004fc2 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java @@ -62,7 +62,7 @@ class HgVersionCommandTest { } @Test - void shouldReturnHgVersion() { + void shouldReturnHgVersion() throws InterruptedException { command("/usr/local/bin/hg", HgVersionCommand.HG_ARGS, "5.5.2", 0); command("/opt/python/bin/python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0); @@ -72,7 +72,7 @@ class HgVersionCommandTest { } @Test - void shouldReturnUnknownMercurialVersionOnNonZeroExitCode() { + void shouldReturnUnknownMercurialVersionOnNonZeroExitCode() throws InterruptedException { command("hg", HgVersionCommand.HG_ARGS, "", 1); command("python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0); @@ -82,7 +82,7 @@ class HgVersionCommandTest { } @Test - void shouldReturnUnknownPythonVersionOnNonZeroExitCode() { + void shouldReturnUnknownPythonVersionOnNonZeroExitCode() throws InterruptedException { command("hg", HgVersionCommand.HG_ARGS, "4.4.2", 0); command("python", HgVersionCommand.PYTHON_ARGS, "", 1); @@ -92,7 +92,7 @@ class HgVersionCommandTest { } @Test - void shouldReturnUnknownForInvalidPythonOutput() { + void shouldReturnUnknownForInvalidPythonOutput() throws InterruptedException { command("hg", HgVersionCommand.HG_ARGS, "1.0.0", 0); command("python", HgVersionCommand.PYTHON_ARGS, "abcdef", 0); @@ -112,10 +112,10 @@ class HgVersionCommandTest { assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN); } - private Process command(String command, String[] args, String content, int exitValue) { + private Process command(String command, String[] args, String content, int exitValue) throws InterruptedException { Process process = mock(Process.class); when(process.getInputStream()).thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); - when(process.exitValue()).thenReturn(exitValue); + when(process.waitFor()).thenReturn(exitValue); List cmdLine = new ArrayList<>(); cmdLine.add(command); From d7321009357704b791ad14440de35838269a9beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 27 Nov 2020 13:11:42 +0100 Subject: [PATCH 25/25] Fix typo --- .../src/main/resources/sonia/scm/python/scmhooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index 7c4ca796f5..492b6f53ba 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -72,7 +72,7 @@ def fire_hook(ui, repo, hooktype, node): length = read_int(connection) if length > 8192: - ui.warn( b"scm-hook received message with exceeds the limit of 8192\n" ) + ui.warn( b"scm-hook received message which exceeds the limit of 8192\n" ) return True d = read_bytes(connection, length)