diff --git a/.gitignore b/.gitignore index 798c009..6970922 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ logs *~ .secret build +**/*.key **/*.crt **/*.pkcs8 **/*.gz diff --git a/build.gradle b/build.gradle index 684c597..348f93b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,10 @@ plugins { id "pmd" id 'maven-publish' id 'signing' - id "io.github.gradle-nexus.publish-plugin" version "1.3.0" - id "com.github.spotbugs" version "5.0.14" - id "org.cyclonedx.bom" version "1.7.2" + id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1" + id "com.github.spotbugs" version "6.0.0-beta.3" + id "org.cyclonedx.bom" version "1.7.4" + id "org.xbib.gradle.plugin.asciidoctor" version "3.0.0" } wrapper { @@ -30,14 +31,14 @@ ext { } subprojects { - apply from: rootProject.file('gradle/ide/idea.gradle') + //apply from: rootProject.file('gradle/ide/idea.gradle') apply from: rootProject.file('gradle/repositories/maven.gradle') apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/test/junit5.gradle') apply from: rootProject.file('gradle/publish/maven.gradle') apply from: rootProject.file('gradle/quality/checkstyle.gradle') apply from: rootProject.file('gradle/quality/pmd.gradle') - apply from: rootProject.file('gradle/quality/spotbugs.gradle') + //apply from: rootProject.file('gradle/quality/spotbugs.gradle') } apply from: rootProject.file('gradle/publish/sonatype.gradle') apply from: rootProject.file('gradle/publish/forgejo.gradle') diff --git a/files-ftp-fs/src/test/java/org/xbib/io/ftp/fs/FTPFileSystemTest.java b/files-ftp-fs/src/test/java/org/xbib/io/ftp/fs/FTPFileSystemTest.java index 9da42ef..2351cc3 100644 --- a/files-ftp-fs/src/test/java/org/xbib/io/ftp/fs/FTPFileSystemTest.java +++ b/files-ftp-fs/src/test/java/org/xbib/io/ftp/fs/FTPFileSystemTest.java @@ -49,11 +49,11 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyCollectionOf; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -330,7 +330,7 @@ public class FTPFileSystemTest extends AbstractFTPFileSystemTest { try (OutputStream input = getFileSystem().newOutputStream(createPath("/foo/bar"), options)) { // don't do anything with the stream, there's a separate test for that } finally { - verify(getExceptionFactory()).createNewOutputStreamException(eq("/foo/bar"), eq(553), anyString(), anyCollectionOf(OpenOption.class)); + verify(getExceptionFactory()).createNewOutputStreamException(eq("/foo/bar"), eq(553), anyString(), anyList()); assertSame(foo, getFileSystemEntry("/foo")); assertSame(bar, getFileSystemEntry("/foo/bar")); } @@ -347,7 +347,7 @@ public class FTPFileSystemTest extends AbstractFTPFileSystemTest { try (OutputStream input = getFileSystem().newOutputStream(createPath("/foo/bar"), options)) { // don't do anything with the stream, there's a separate test for that } finally { - verify(getExceptionFactory()).createNewOutputStreamException(eq("/foo/bar"), eq(553), anyString(), anyCollectionOf(OpenOption.class)); + verify(getExceptionFactory()).createNewOutputStreamException(eq("/foo/bar"), eq(553), anyString(), anyList()); assertSame(foo, getFileSystemEntry("/foo")); assertSame(bar, getFileSystemEntry("/foo/bar")); } @@ -427,8 +427,7 @@ public class FTPFileSystemTest extends AbstractFTPFileSystemTest { try (OutputStream input = getFileSystem().newOutputStream(createPath("/foo"), options)) { // don't do anything with the stream, there's a separate test for that } finally { - verify(getExceptionFactory(), never()).createNewOutputStreamException(anyString(), anyInt(), anyString(), - anyCollectionOf(OpenOption.class)); + verify(getExceptionFactory(), never()).createNewOutputStreamException(anyString(), anyInt(), anyString(), anyList()); assertSame(foo, getFileSystemEntry("/foo")); assertEquals(0, getChildCount("/foo")); } @@ -443,8 +442,7 @@ public class FTPFileSystemTest extends AbstractFTPFileSystemTest { try (OutputStream input = getFileSystem().newOutputStream(createPath("/foo"), options)) { // don't do anything with the stream, there's a separate test for that } finally { - verify(getExceptionFactory(), never()).createNewOutputStreamException(anyString(), anyInt(), anyString(), - anyCollectionOf(OpenOption.class)); + verify(getExceptionFactory(), never()).createNewOutputStreamException(anyString(), anyInt(), anyString(), anyList()); assertSame(foo, getFileSystemEntry("/foo")); assertEquals(0, getChildCount("/foo")); } @@ -693,7 +691,7 @@ public class FTPFileSystemTest extends AbstractFTPFileSystemTest { try { getFileSystem().copy(createPath("/foo/bar"), createPath("/baz/bar"), options); } finally { - verify(getExceptionFactory()).createNewOutputStreamException(eq("/baz/bar"), eq(553), anyString(), anyCollectionOf(OpenOption.class)); + verify(getExceptionFactory()).createNewOutputStreamException(eq("/baz/bar"), eq(553), anyString(), anyList()); assertSame(foo, getFileSystemEntry("/foo")); assertSame(bar, getFileSystemEntry("/foo/bar")); assertNull(getFileSystemEntry("/baz")); diff --git a/files-ftp/src/main/java/org/xbib/io/ftp/client/FTPClient.java b/files-ftp/src/main/java/org/xbib/io/ftp/client/FTPClient.java index 648e6e2..06b8e29 100644 --- a/files-ftp/src/main/java/org/xbib/io/ftp/client/FTPClient.java +++ b/files-ftp/src/main/java/org/xbib/io/ftp/client/FTPClient.java @@ -2755,7 +2755,7 @@ public class FTPClient extends FTP implements Configurable { * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface - * {@link }FTPFileEntryParser} + * {@link FTPFileEntryParser} * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. diff --git a/files-sftp-fs/src/main/java/module-info.java b/files-sftp-fs/src/main/java/module-info.java index f0d578f..3fdcbf1 100644 --- a/files-sftp-fs/src/main/java/module-info.java +++ b/files-sftp-fs/src/main/java/module-info.java @@ -7,7 +7,7 @@ module org.xbib.files.sftp.fs { exports org.apache.sshd.fs; exports org.apache.sshd.fs.spi; requires org.xbib.files; - requires transitive org.xbib.files.sftp; + requires org.xbib.files.sftp; provides FileSystemProvider with SftpFileSystemProvider; provides FileServiceProvider with SFTPFileServiceProvider; requires java.logging; diff --git a/files-sftp-fs/src/main/java/org/apache/sshd/fs/SftpFileSystemProvider.java b/files-sftp-fs/src/main/java/org/apache/sshd/fs/SftpFileSystemProvider.java index c5857a9..d4c0d68 100644 --- a/files-sftp-fs/src/main/java/org/apache/sshd/fs/SftpFileSystemProvider.java +++ b/files-sftp-fs/src/main/java/org/apache/sshd/fs/SftpFileSystemProvider.java @@ -83,6 +83,7 @@ import org.apache.sshd.common.auth.BasicCredentialsImpl; import org.apache.sshd.common.auth.BasicCredentialsProvider; import org.apache.sshd.common.auth.MutableBasicCredentials; import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider; import org.apache.sshd.common.keyprovider.KeyIdentityProvider; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.NumberUtils; @@ -197,12 +198,13 @@ public class SftpFileSystemProvider extends FileSystemProvider { if (port <= 0) { port = SshConstants.DEFAULT_PORT; } - Object o = env.get("username"); String username = o instanceof String ? (String) o : o != null ? o.toString() : null; o = env.get("password"); char[] password = o instanceof char[] ? (char[]) o : o instanceof String ? ((String)o).toCharArray() : null; - + if (env.containsKey("key")) { + clientInstance.setKeyIdentityProvider(new ClassLoadableResourceKeyPairProvider(env.get("key").toString())); + } boolean disableServerKeys = "true".equals(env.get("disableServerKeys")); if (disableServerKeys) { clientInstance.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE); @@ -280,7 +282,7 @@ public class SftpFileSystemProvider extends FileSystemProvider { if (session != null) { try { session.close(); - } catch (IOException t) { + } catch (Exception t) { e.addSuppressed(t); } } diff --git a/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/FileServiceProviderTest.java b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/FileServiceProviderTest.java index 442b665..49a1038 100644 --- a/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/FileServiceProviderTest.java +++ b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/FileServiceProviderTest.java @@ -4,23 +4,11 @@ import org.junit.jupiter.api.Test; import org.xbib.files.FileService; import java.io.IOException; -import java.security.Provider; -import java.security.Security; import java.util.Map; -import java.util.ServiceLoader; import java.util.logging.Logger; public class FileServiceProviderTest { - static { - // load bouncy castle provider (and other security providers) - for (Provider p : ServiceLoader.load(Provider.class)) { - if (Security.getProvider(p.getName()) == null) { - Security.addProvider(p); - } - } - } - @Test public void testSFTP() throws IOException { Map env = Map.of("username", "joerg"); diff --git a/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpClientTest.java b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpClientTest.java new file mode 100644 index 0000000..d3b72bf --- /dev/null +++ b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpClientTest.java @@ -0,0 +1,22 @@ +package org.apache.sshd.fs.test; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; +import java.util.logging.Logger; +import org.junit.jupiter.api.Test; +import org.xbib.files.FileService; + +public class SftpClientTest { + + @Test + public void testSFTP() throws IOException { + String targetURLString = "sftp://fernleihe-test.hbz-nrw.de:22"; + String targetIdString = "malva"; + URI uri = URI.create(targetURLString); + FileService fs = FileService.newInstance(targetURLString, + Map.of("username", targetIdString, + "key", uri.getHost() + "/" + targetIdString + ".key")); + fs.list(".").forEach(p -> Logger.getAnonymousLogger().info(p.toString())); + } +} diff --git a/files-sftp/src/main/java/org/apache/sshd/client/SshClient.java b/files-sftp/src/main/java/org/apache/sshd/client/SshClient.java index 0743024..6156106 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/SshClient.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/SshClient.java @@ -75,7 +75,6 @@ import org.apache.sshd.common.Factory; import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.ServiceFactory; import org.apache.sshd.common.config.keys.FilePasswordProvider; -import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.PublicKeyEntry; import org.apache.sshd.common.future.SshFutureListener; import org.apache.sshd.common.helpers.AbstractFactoryManager; @@ -732,7 +731,6 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } else if (id instanceof KeyPair) { KeyPair kp = (KeyPair) id; session.addPublicKeyIdentity(kp); - } else { } } } diff --git a/files-sftp/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java b/files-sftp/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java index a0d6eca..429d3a9 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java @@ -34,7 +34,7 @@ import org.apache.sshd.common.util.GenericUtils; public interface PasswordIdentityProvider { /** - * An "empty" implementation of {@link PasswordIdentityProvider} that returns and empty group of passwords + * An "empty" implementation of {@link PasswordIdentityProvider} that returns and empty group of passwords */ PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() { @Override diff --git a/files-sftp/src/main/java/org/apache/sshd/client/impl/SimpleSftpClientImpl.java b/files-sftp/src/main/java/org/apache/sshd/client/impl/SimpleSftpClientImpl.java index 7f9378e..b6b649b 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/impl/SimpleSftpClientImpl.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/impl/SimpleSftpClientImpl.java @@ -20,6 +20,7 @@ package org.apache.sshd.client.impl; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.SocketAddress; import java.security.KeyPair; @@ -82,12 +83,16 @@ public class SimpleSftpClientImpl implements SimpleSftpClient { SimpleClient client = getClient(); ClientSession session = sessionProvider.apply(client); try { - SftpClient sftp = createSftpClient(session); - session = null; // disable auto-close at finally block - return sftp; + SftpClient sftpClient = createSftpClient(session); + session = null; + return sftpClient; } finally { if (session != null) { - session.close(); + try { + session.close(); + } catch (Exception e) { + throw new IOException(e); + } } } } diff --git a/files-sftp/src/main/java/org/apache/sshd/client/session/ClientProxyConnector.java b/files-sftp/src/main/java/org/apache/sshd/client/session/ClientProxyConnector.java index a765e67..21534a7 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/session/ClientProxyConnector.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/session/ClientProxyConnector.java @@ -23,8 +23,8 @@ import org.apache.sshd.common.CoreModuleProperties; /** * Provides a way to implement proxied connections where some metadata about the client is sent before the actual - * SSH protocol is executed - e.g., the PROXY - * protocol. The implementor should use the {@code IoSession#write(Buffer)} method to send any packets with the + * SSH protocol is executed - e.g., the http://www.haproxy.org/download/1.6/doc/proxy-protocol.txt PROXY + * protocol. The implementor should use the {@code IoSession#write(Buffer)} method to send any packets with the * meta-data. * * @author Apache MINA SSHD Project diff --git a/files-sftp/src/main/java/org/apache/sshd/client/session/ClientSession.java b/files-sftp/src/main/java/org/apache/sshd/client/session/ClientSession.java index b1a76a9..ee76687 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/session/ClientSession.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/session/ClientSession.java @@ -18,13 +18,8 @@ */ package org.apache.sshd.client.session; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.SocketAddress; -import java.net.SocketTimeoutException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.security.PublicKey; import java.time.Duration; diff --git a/files-sftp/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java b/files-sftp/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java index e32bf28..e3281c8 100644 --- a/files-sftp/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java +++ b/files-sftp/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java @@ -125,8 +125,8 @@ public abstract class AbstractSimpleClientSessionCreator extends AbstractSimpleC if (session != null) { try { session.close(); - } catch (IOException e) { - err = GenericUtils.accumulateException(err, e); + } catch (Exception e) { + err = GenericUtils.accumulateException(err, new IOException(e)); } } } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/Closeable.java b/files-sftp/src/main/java/org/apache/sshd/common/Closeable.java index e6880a7..5c6957c 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/Closeable.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/Closeable.java @@ -33,7 +33,7 @@ import org.apache.sshd.common.future.SshFutureListener; * * @author Apache MINA SSHD Project */ -public interface Closeable extends Channel { +public interface Closeable extends AutoCloseable { /** * Close this resource asynchronously and return a future. Resources support two closing modes: a graceful mode @@ -76,13 +76,12 @@ public interface Closeable extends Channel { */ boolean isClosing(); - @Override default boolean isOpen() { return !(isClosed() || isClosing()); } @Override - default void close() throws IOException { + default void close() throws Exception { Closeable.close(this); } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/CoreModuleProperties.java b/files-sftp/src/main/java/org/apache/sshd/common/CoreModuleProperties.java index abf3c5e..09ce116 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/CoreModuleProperties.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/CoreModuleProperties.java @@ -44,9 +44,6 @@ public final class CoreModuleProperties { public static final Property PROXY_AUTH_CHANNEL_TYPE = Property.string("ssh-agent-factory-proxy-auth-channel-type", "auth-agent-req@openssh.com"); - /** - * See {@link org.apache.sshd.agent.local.ProxyAgentFactory#getChannelForwardingFactories} - */ public static final Property PREFER_UNIX_AGENT = Property.bool("ssh-prefer-unix-agent", OsUtils.isUNIX()); @@ -147,8 +144,6 @@ public final class CoreModuleProperties { /** * Whether to ignore invalid identities files when pre-initializing the client session - * - * @see ClientIdentityLoader#isValidLocation(NamedResource) */ public static final Property IGNORE_INVALID_IDENTITIES = Property.bool("ignore-invalid-identities", true); @@ -579,105 +574,84 @@ public final class CoreModuleProperties { public static final Property MODULI_URL = Property.string("moduli-url"); - /** - * See {@link org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator}. - */ public static final Property KB_SERVER_INTERACTIVE_NAME = Property.string("kb-server-interactive-name", "Password authentication"); - /** - * See {@link org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator}. - */ public static final Property KB_SERVER_INTERACTIVE_INSTRUCTION = Property.string("kb-server-interactive-instruction", ""); - /** - * See {@link org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator}. - */ public static final Property KB_SERVER_INTERACTIVE_LANG = Property.string("kb-server-interactive-language", "en-US"); - /** - * See {@link org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator}. - */ public static final Property KB_SERVER_INTERACTIVE_PROMPT = Property.string("kb-server-interactive-prompt", "Password: "); - /** - * See {@link org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator}. - */ public static final Property KB_SERVER_INTERACTIVE_ECHO_PROMPT = Property.bool("kb-server-interactive-echo-prompt", false); /** - * Maximum amount of extended (a.k.a. STDERR) data allowed to be accumulated until a {@link ChannelDataReceiver} for + * Maximum amount of extended (a.k.a. STDERR) data allowed to be accumulated until a ChannelDataReceiver for * the data is registered */ public static final Property MAX_EXTDATA_BUFSIZE = Property.integer("channel-session-max-extdata-bufsize", 0); - /** - * See {@link org.apache.sshd.server.kex.DHGEXServer}. - */ public static final Property PROP_DHGEX_SERVER_MIN_KEY = Property.integer("dhgex-server-min"); - /** - * See {@link org.apache.sshd.server.kex.DHGEXServer}. - */ public static final Property PROP_DHGEX_SERVER_MAX_KEY = Property.integer("dhgex-server-max"); /** - * Value used by the {@link org.apache.sshd.server.shell.InvertedShellWrapper} to control the "busy-wait" + * Value used to control the "busy-wait" * sleep time (millis) on the pumping loop if nothing was pumped - must be positive. */ public static final Property PUMP_SLEEP_TIME = Property.duration("inverted-shell-wrapper-pump-sleep", Duration.ofMillis(1)); /** - * Value used by the {@link org.apache.sshd.server.shell.InvertedShellWrapper} to control copy buffer size. + * Value used to control copy buffer size. */ public static final Property BUFFER_SIZE = Property.integer("inverted-shell-wrapper-buffer-size", IoUtils.DEFAULT_COPY_SIZE); /** - * Configuration value for the {@link org.apache.sshd.server.x11.X11ForwardSupport} to control the channel open + * Configuration value to control the channel open * timeout. */ public static final Property X11_OPEN_TIMEOUT = Property.duration("x11-fwd-open-timeout", Duration.ofSeconds(30L)); /** - * Configuration value for the {@link org.apache.sshd.server.x11.X11ForwardSupport} to control from which X11 + * Configuration value to control from which X11 * display number to start looking for a free value. */ public static final Property X11_DISPLAY_OFFSET = Property.integer("x11-fwd-display-offset", 10); /** - * Configuration value for the {@link org.apache.sshd.server.x11.X11ForwardSupport} to control up to which (but not + * Configuration value to control up to which (but not * including) X11 display number to look or a free value. */ public static final Property X11_MAX_DISPLAYS = Property.integer("x11-fwd-max-display", 1000); /** - * Configuration value for the {@link org.apache.sshd.server.x11.X11ForwardSupport} to control the base port number + * Configuration value to control the base port number * for the X11 display number socket binding. */ public static final Property X11_BASE_PORT = Property.integer("x11-fwd-base-port", 6000); /** - * Configuration value for the {@link org.apache.sshd.server.x11.X11ForwardSupport} to control the host used to bind + * Configuration value to control the host used to bind * to for the X11 display when looking for a free port. */ public static final Property X11_BIND_HOST = Property.string("x11-fwd-bind-host", SshdSocketAddress.LOCALHOST_IPV4); /** - * Configuration value for the {@link org.apache.sshd.server.forward.TcpipServerChannel} to control the higher - * theshold for the data to be buffered waiting to be sent. If the buffered data size reaches this value, the + * Configuration value to control the higher + * threshold for the data to be buffered waiting to be sent. If the buffered data size reaches this value, the * session will pause reading until the data length goes below the * {@link #TCPIP_SERVER_CHANNEL_BUFFER_SIZE_THRESHOLD_LOW} threshold. */ diff --git a/files-sftp/src/main/java/org/apache/sshd/common/SftpModuleProperties.java b/files-sftp/src/main/java/org/apache/sshd/common/SftpModuleProperties.java index b5b7bff..dfceaa1 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/SftpModuleProperties.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/SftpModuleProperties.java @@ -23,9 +23,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.time.Duration; -import org.apache.sshd.common.Property; -import org.apache.sshd.common.PropertyResolver; -import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.io.IoUtils; @@ -41,8 +38,6 @@ public final class SftpModuleProperties { * Used to indicate the {@link Charset} (or its name) for decoding referenced files/folders names - extracted from * the client session when 1st initialized. * - * @see SftpClient#getNameDecodingCharset() - * @see SftpClient#setNameDecodingCharset(Charset) */ public static final Property NAME_DECODING_CHARSET = Property.charset("sftp-name-decoding-charset", StandardCharsets.UTF_8); @@ -54,45 +49,26 @@ public final class SftpModuleProperties { public static final Property SFTP_CHANNEL_OPEN_TIMEOUT = Property.duration("sftp-channel-open-timeout", Duration.ofSeconds(15L)); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystem}. - */ public static final Property POOL_SIZE = Property.integer("sftp-fs-pool-size", 8); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystemProvider}. - */ public static final Property READ_BUFFER_SIZE = Property.integer("sftp-fs-read-buffer-size"); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystemProvider}. - */ public static final Property WRITE_BUFFER_SIZE = Property.integer("sftp-fs-write-buffer-size"); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystemProvider}. - */ public static final Property CONNECT_TIME = Property.duration("sftp-fs-connect-time", Duration.ofSeconds(15L)); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystemProvider}. - */ public static final Property AUTH_TIME = Property.duration("sftp-fs-auth-time", Duration.ofSeconds(15L)); - /** - * See {@link org.apache.sshd.sftp.client.fs.SftpFileSystemProvider}. - */ public static final Property NAME_DECODER_CHARSET = Property.charset("sftp-fs-name-decoder-charset", StandardCharsets.UTF_8); /** * Property used to avoid large buffers when - * {@link org.apache.sshd.sftp.client.impl.AbstractSftpClient#write(SftpClient.Handle, long, byte[], int, int)} is * invoked with a large buffer size. */ public static final Property WRITE_CHUNK_SIZE @@ -122,25 +98,22 @@ public final class SftpModuleProperties { /** * Allows controlling reports of which client extensions are supported (and reported via "support" and * "support2" server extensions) as a comma-separate list of names. Note: requires overriding the - * {@link AbstractSftpSubsystemHelper#executeExtendedCommand(Buffer, int, String)} command accordingly. If empty + * command accordingly. If empty * string is set then no server extensions are reported * - * @see AbstractSftpSubsystemHelper#DEFAULT_SUPPORTED_CLIENT_EXTENSIONS */ public static final Property CLIENT_EXTENSIONS = Property.string("sftp-client-extensions"); /** * Comma-separated list of which {@code OpenSSH} extensions are reported and what version is reported for each - - * format: {@code name=version}. If empty value set, then no such extensions are reported. Otherwise, the - * {@link AbstractSftpSubsystemHelper#DEFAULT_OPEN_SSH_EXTENSIONS} are used + * format: {@code name=version}. If empty value set, then no such extensions are reported. */ public static final Property OPENSSH_EXTENSIONS = Property.string("sftp-openssh-extensions"); /** - * Comma separate list of {@code SSH_ACL_CAP_xxx} names - where name can be without the prefix. If not defined then - * {@link AbstractSftpSubsystemHelper#DEFAULT_ACL_SUPPORTED_MASK} is used + * Comma separate list of {@code SSH_ACL_CAP_xxx} names - where name can be without the prefix. */ public static final Property ACL_SUPPORTED_MASK = Property.string("sftp-acl-supported-mask"); @@ -152,7 +125,7 @@ public final class SftpModuleProperties { = Property.string("sftp-newline", IoUtils.EOL); /** - * Force the use of a max. packet length for {@link AbstractSftpSubsystemHelper#doRead(Buffer, int)} protection + * Force the use of a max. packet length for protection * against malicious packets */ public static final Property MAX_READDATA_PACKET_LENGTH @@ -187,9 +160,6 @@ public final class SftpModuleProperties { /** * Max. rounds to attempt to create a unique file handle - if all handles already in use after these many rounds, * then an exception is thrown - * - * @see SftpSubsystem#generateFileHandle(Path) - * @see #DEFAULT_FILE_HANDLE_ROUNDS */ public static final Property MAX_FILE_HANDLE_RAND_ROUNDS = Property.validating( @@ -200,8 +170,7 @@ public final class SftpModuleProperties { }); /** - * Maximum amount of data allocated for listing the contents of a directory in any single invocation of - * {@link SftpSubsystem#doReadDir(Buffer, int)} + * Maximum amount of data allocated for listing the contents of a directory in any single invocation */ public static final Property MAX_READDIR_DATA_SIZE = Property.integer("sftp-max-readdir-data-size", 16 * 1024); diff --git a/files-sftp/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/files-sftp/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java index 8c0fcb6..c8a695c 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java @@ -36,7 +36,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.IntUnaryOperator; -import org.apache.sshd.common.AttributeRepository; import org.apache.sshd.common.Closeable; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.PropertyResolver; @@ -100,7 +99,7 @@ public abstract class AbstractChannel extends AbstractInnerCloseable implements private int id = -1; private int recipient = -1; private Session sessionInstance; - private CloseableExecutorService executor; + private final CloseableExecutorService executor; private final List> requestHandlers = new CopyOnWriteArrayList<>(); private final Window localWindow; diff --git a/files-sftp/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/files-sftp/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java index 87040f2..c11b8df 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java @@ -704,7 +704,7 @@ public final class KeyUtils { * @param expected The expected fingerprint if {@code null} or empty then returns a failure with the default * fingerprint. * @param key the {@link PublicKey} - if {@code null} then returns null. - * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, + * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, * {@code null} if no key. * @see #getDefaultFingerPrintFactory() * @see #checkFingerPrint(String, Factory, PublicKey) @@ -718,7 +718,7 @@ public final class KeyUtils { * fingerprint. * @param f The {@link Factory} to be used to generate the default {@link Digest} for the key * @param key the {@link PublicKey} - if {@code null} then returns null. - * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, + * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, * {@code null} if no key. */ public static SimpleImmutableEntry checkFingerPrint( @@ -731,7 +731,7 @@ public final class KeyUtils { * fingerprint. * @param d The {@link Digest} to be used to generate the default fingerprint for the key * @param key the {@link PublicKey} - if {@code null} then returns null. - * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, + * @return SimpleImmutableEntry - key is success indicator, value is actual fingerprint, * {@code null} if no key. */ public static SimpleImmutableEntry checkFingerPrint(String expected, Digest d, PublicKey key) { diff --git a/files-sftp/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/files-sftp/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java index 9f27d4b..ad32fc2 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java @@ -47,6 +47,7 @@ import org.apache.sshd.common.util.security.SecurityUtils; * @author Apache MINA SSHD Project */ public abstract class AbstractResourceKeyPairProvider extends AbstractKeyPairProvider { + private FilePasswordProvider passwordFinder; /* * NOTE: the map is case insensitive even for Linux, as it is (very) bad practice to have 2 key files that differ diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/OsUtils.java b/files-sftp/src/main/java/org/apache/sshd/common/util/OsUtils.java index 54ca86b..659d318 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/OsUtils.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/OsUtils.java @@ -165,7 +165,7 @@ public final class OsUtils { } /** - * Remove {@code Windows} domain and/or group prefix as well as "(User);" suffix + * Remove {@code Windows} domain and/or group prefix as well as "(User);" suffix * * @param user The original username - ignored if {@code null}/empty * @return The canonical user - unchanged if {@code Unix} O/S diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java b/files-sftp/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java index 68b48c1..6a15390 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java @@ -63,13 +63,13 @@ public interface BufferPublicKeyParser { OpenSSHCertPublicKeyParser.INSTANCE)); /** - * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss" + * @param keyType The key type - e.g., ssh-rsa, ssh-dss * @return {@code true} if this key type is supported by the parser */ boolean isKeyTypeSupported(String keyType); /** - * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss" + * @param keyType The key type - e.g., ssh-rsa, ssh-dss * @param buffer The {@link Buffer} containing the encoded raw public key * @return The decoded {@link PublicKey} * @throws GeneralSecurityException If failed to generate the key diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/Builder.java b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/Builder.java index 84a899e..2978594 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/Builder.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/Builder.java @@ -78,10 +78,6 @@ public final class Builder implements ObjectBuilder { return this; } - public Builder sequential(Object id, Iterable closeables) { - return close(new SequentialCloseable(id, lock, closeables)); - } - public Builder parallel(Closeable... closeables) { if (closeables.length == 1) { close(closeables[0]); diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java index fc8e3f4..73447af 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java @@ -18,6 +18,8 @@ */ package org.apache.sshd.common.util.closeable; +import java.io.IOException; +import java.io.UncheckedIOException; import org.apache.sshd.common.Closeable; /** @@ -25,4 +27,12 @@ import org.apache.sshd.common.Closeable; */ public abstract class IoBaseCloseable implements Closeable { + @Override + public void close() { + try { + Closeable.close(this); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java index 89681c4..889f651 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java @@ -42,7 +42,7 @@ public class SequentialCloseable extends SimpleCloseable { @Override protected void doClose(boolean immediately) { Iterator iterator = closeables.iterator(); - SshFutureListener listener = new SshFutureListener() { + SshFutureListener listener = new SshFutureListener<>() { @SuppressWarnings("synthetic-access") @Override public void operationComplete(CloseFuture previousFuture) { diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java b/files-sftp/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java index fc52c82..7e3957e 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java @@ -76,7 +76,7 @@ public class DERWriter extends FilterOutputStream { } /** - * The integer is always considered to be positive, so if the first byte is < 0, we pad with a zero to make it + * The integer is always considered to be positive, so if the first byte is < 0, we pad with a zero to make it * positive * * @param bytes {@link BigInteger} bytes @@ -87,7 +87,7 @@ public class DERWriter extends FilterOutputStream { } /** - * The integer is always considered to be positive, so if the first byte is < 0, we pad with a zero to make it + * The integer is always considered to be positive, so if the first byte is < 0, we pad with a zero to make it * positive * * @param bytes {@link BigInteger} bytes diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java index 2a687e1..e6536c0 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java @@ -111,7 +111,7 @@ public final class SecurityUtils { /** * The min. key size value used for testing whether Diffie-Hellman Group Exchange is supported or not. According to * RFC 4419 section 3: "Servers and clients SHOULD support - * groups with a modulus length of k bits, where 1024 <= k <= 8192". + * groups with a modulus length of k bits, where 1024 <= k <= 8192". * * Note: this has been amended by RFC 8270 */ @@ -384,7 +384,6 @@ public final class SecurityUtils { if (REGISTRATION_STATE_HOLDER.get()) { return; } - String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS, GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ',')); boolean bouncyCastleRegistered = false; @@ -475,13 +474,11 @@ public final class SecurityUtils { if (parser == null) { throw new NoSuchProviderException("No registered key-pair resource parser"); } - Collection ids = parser.loadKeyPairs(session, resourceKey, provider, inputStream); int numLoaded = GenericUtils.size(ids); if (numLoaded <= 0) { return null; } - return ids; } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java b/files-sftp/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java index 0dd51f3..ce3b7a7 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java @@ -19,16 +19,27 @@ package org.apache.sshd.common.util.threads; +import java.io.IOException; +import java.io.UncheckedIOException; import java.time.Duration; import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; - import org.apache.sshd.common.Closeable; public interface CloseableExecutorService extends ExecutorService, Closeable { - default boolean awaitTermination(Duration timeout) throws InterruptedException { + + default boolean awaitTermination(Duration timeout) throws InterruptedException { Objects.requireNonNull(timeout, "No timeout specified"); return awaitTermination(timeout.toMillis(), TimeUnit.MILLISECONDS); } + + @Override + default void close() { + try { + Closeable.close(this); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/files-webdav/src/main/java/org/xbib/io/webdav/common/DavPropertyNameSet.java b/files-webdav/src/main/java/org/xbib/io/webdav/common/DavPropertyNameSet.java index e426454..b5e8b7d 100644 --- a/files-webdav/src/main/java/org/xbib/io/webdav/common/DavPropertyNameSet.java +++ b/files-webdav/src/main/java/org/xbib/io/webdav/common/DavPropertyNameSet.java @@ -120,7 +120,6 @@ public class DavPropertyNameSet extends PropContainer * in order to successfully add the given entry. * @return true if contentEntry is an instance of DavPropertyName * that could be added to this set. False otherwise. - * @see PropContainer#addContent(Object) */ @Override public boolean addContent(PropEntry contentEntry) { diff --git a/gradle.properties b/gradle.properties index 10f0f70..d88b328 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = files -version = 4.0.0 +version = 4.1.0 org.gradle.warning.mode = ALL diff --git a/gradle/compile/java.gradle b/gradle/compile/java.gradle index 046e29f..f6a043d 100644 --- a/gradle/compile/java.gradle +++ b/gradle/compile/java.gradle @@ -2,21 +2,14 @@ apply plugin: 'java-library' java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } modularity.inferModulePath.set(true) withSourcesJar() withJavadocJar() } -compileJava { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -compileTestJava { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - jar { manifest { attributes('Implementation-Version': project.version) @@ -24,7 +17,9 @@ jar { } tasks.withType(JavaCompile) { - options.compilerArgs.add('-Xlint:all,-exports') + options.fork = true + options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US'] + options.compilerArgs.add('-Xlint:all') options.encoding = 'UTF-8' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a7..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8707e8b..8838ba9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cb..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -130,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/settings.gradle b/settings.gradle index 622a7dd..9b8d76d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,10 +15,10 @@ pluginManagement { dependencyResolutionManagement { versionCatalogs { libs { - version('gradle', '8.1.1') + version('gradle', '8.4') version('groovy', '4.0.12') - version('junit', '5.9.3') - version('net', '3.2.0') + version('junit', '5.10.0') + version('net', '4.0.0') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') @@ -27,8 +27,8 @@ dependencyResolutionManagement { library('junit4', 'junit', 'junit').version('4.13.2') library('net-security', 'org.xbib', 'net-security').versionRef('net') library('mockftpserver', 'org.mockftpserver', 'MockFtpServer').version('2.7.1') - library('mockito-core', 'org.mockito', 'mockito-core').version('3.7.7') - library('mockito-junit-jupiter', 'org.mockito', 'mockito-junit-jupiter').version('3.7.7') + library('mockito-core', 'org.mockito', 'mockito-core').version('5.6.0') + library('mockito-junit-jupiter', 'org.mockito', 'mockito-junit-jupiter').version('5.6.0') } } }