18d2d6836SMatthias Sohn /* 2eb67862cSThomas Wolf * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others 38d2d6836SMatthias Sohn * 48d2d6836SMatthias Sohn * This program and the accompanying materials are made available under the 58d2d6836SMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 68d2d6836SMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 78d2d6836SMatthias Sohn * 88d2d6836SMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 98d2d6836SMatthias Sohn */ 108d2d6836SMatthias Sohn package org.eclipse.jgit.junit.ssh; 118d2d6836SMatthias Sohn 128d2d6836SMatthias Sohn import static org.junit.Assert.assertEquals; 138d2d6836SMatthias Sohn import static org.junit.Assert.assertFalse; 148d2d6836SMatthias Sohn import static org.junit.Assert.assertNotEquals; 158d2d6836SMatthias Sohn import static org.junit.Assert.assertNotNull; 168d2d6836SMatthias Sohn import static org.junit.Assert.assertTrue; 178d2d6836SMatthias Sohn 18eb67862cSThomas Wolf import java.io.BufferedWriter; 198d2d6836SMatthias Sohn import java.io.File; 208d2d6836SMatthias Sohn import java.io.FileOutputStream; 218d2d6836SMatthias Sohn import java.io.IOException; 228d2d6836SMatthias Sohn import java.io.InputStream; 238d2d6836SMatthias Sohn import java.io.OutputStream; 24eb67862cSThomas Wolf import java.nio.charset.StandardCharsets; 258d2d6836SMatthias Sohn import java.nio.file.Files; 26eb67862cSThomas Wolf import java.security.KeyPair; 27eb67862cSThomas Wolf import java.security.KeyPairGenerator; 28eb67862cSThomas Wolf import java.security.PrivateKey; 298d2d6836SMatthias Sohn import java.util.ArrayList; 308d2d6836SMatthias Sohn import java.util.Arrays; 31eb67862cSThomas Wolf import java.util.Base64; 328d2d6836SMatthias Sohn import java.util.Collections; 338d2d6836SMatthias Sohn import java.util.Iterator; 348d2d6836SMatthias Sohn import java.util.List; 358d2d6836SMatthias Sohn 36eb67862cSThomas Wolf import org.apache.sshd.common.config.keys.PublicKeyEntry; 378d2d6836SMatthias Sohn import org.eclipse.jgit.api.CloneCommand; 388d2d6836SMatthias Sohn import org.eclipse.jgit.api.Git; 398d2d6836SMatthias Sohn import org.eclipse.jgit.api.PushCommand; 408d2d6836SMatthias Sohn import org.eclipse.jgit.api.ResetCommand.ResetType; 418d2d6836SMatthias Sohn import org.eclipse.jgit.errors.UnsupportedCredentialItem; 428d2d6836SMatthias Sohn import org.eclipse.jgit.junit.RepositoryTestCase; 438d2d6836SMatthias Sohn import org.eclipse.jgit.lib.Constants; 448d2d6836SMatthias Sohn import org.eclipse.jgit.lib.Repository; 458d2d6836SMatthias Sohn import org.eclipse.jgit.revwalk.RevCommit; 468d2d6836SMatthias Sohn import org.eclipse.jgit.transport.CredentialItem; 478d2d6836SMatthias Sohn import org.eclipse.jgit.transport.CredentialsProvider; 488d2d6836SMatthias Sohn import org.eclipse.jgit.transport.PushResult; 498d2d6836SMatthias Sohn import org.eclipse.jgit.transport.RemoteRefUpdate; 508d2d6836SMatthias Sohn import org.eclipse.jgit.transport.SshSessionFactory; 518d2d6836SMatthias Sohn import org.eclipse.jgit.transport.URIish; 528d2d6836SMatthias Sohn import org.eclipse.jgit.util.FS; 538d2d6836SMatthias Sohn import org.junit.After; 548d2d6836SMatthias Sohn 558d2d6836SMatthias Sohn /** 568d2d6836SMatthias Sohn * Root class for ssh tests. Sets up the ssh test server. A set of pre-computed 578d2d6836SMatthias Sohn * keys for testing is provided in the bundle and can be used in test cases via 588d2d6836SMatthias Sohn * {@link #copyTestResource(String, File)}. These test key files names have four 598d2d6836SMatthias Sohn * components, separated by a single underscore: "id", the algorithm, the bits 608d2d6836SMatthias Sohn * (if variable), and the password if the private key is encrypted. For instance 618d2d6836SMatthias Sohn * "{@code id_ecdsa_384_testpass}" is an encrypted ECDSA-384 key. The passphrase 628d2d6836SMatthias Sohn * to decrypt is "testpass". The key "{@code id_ecdsa_384}" is the same but 638d2d6836SMatthias Sohn * unencrypted. All keys were generated and encrypted via ssh-keygen. Note that 648d2d6836SMatthias Sohn * DSA and ec25519 have no "bits" component. Available keys are listed in 658d2d6836SMatthias Sohn * {@link SshTestBase#KEY_RESOURCES}. 668d2d6836SMatthias Sohn */ 678d2d6836SMatthias Sohn public abstract class SshTestHarness extends RepositoryTestCase { 688d2d6836SMatthias Sohn 698d2d6836SMatthias Sohn protected static final String TEST_USER = "testuser"; 708d2d6836SMatthias Sohn 718d2d6836SMatthias Sohn protected File sshDir; 728d2d6836SMatthias Sohn 738d2d6836SMatthias Sohn protected File privateKey1; 748d2d6836SMatthias Sohn 758d2d6836SMatthias Sohn protected File privateKey2; 768d2d6836SMatthias Sohn 778d2d6836SMatthias Sohn protected File publicKey1; 788d2d6836SMatthias Sohn 79*566e49d7SThomas Wolf protected File publicKey2; 80*566e49d7SThomas Wolf 818d2d6836SMatthias Sohn protected SshTestGitServer server; 828d2d6836SMatthias Sohn 838d2d6836SMatthias Sohn private SshSessionFactory factory; 848d2d6836SMatthias Sohn 858d2d6836SMatthias Sohn protected int testPort; 868d2d6836SMatthias Sohn 878d2d6836SMatthias Sohn protected File knownHosts; 888d2d6836SMatthias Sohn 898d2d6836SMatthias Sohn private File homeDir; 908d2d6836SMatthias Sohn 918d2d6836SMatthias Sohn @Override setUp()928d2d6836SMatthias Sohn public void setUp() throws Exception { 938d2d6836SMatthias Sohn super.setUp(); 948d2d6836SMatthias Sohn writeTrashFile("file.txt", "something"); 958d2d6836SMatthias Sohn try (Git git = new Git(db)) { 968d2d6836SMatthias Sohn git.add().addFilepattern("file.txt").call(); 978d2d6836SMatthias Sohn git.commit().setMessage("Initial commit").call(); 988d2d6836SMatthias Sohn } 998d2d6836SMatthias Sohn mockSystemReader.setProperty("user.home", 1008d2d6836SMatthias Sohn getTemporaryDirectory().getAbsolutePath()); 1018d2d6836SMatthias Sohn mockSystemReader.setProperty("HOME", 1028d2d6836SMatthias Sohn getTemporaryDirectory().getAbsolutePath()); 1038d2d6836SMatthias Sohn homeDir = FS.DETECTED.userHome(); 1048d2d6836SMatthias Sohn FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile()); 1058d2d6836SMatthias Sohn sshDir = new File(getTemporaryDirectory(), ".ssh"); 1068d2d6836SMatthias Sohn assertTrue(sshDir.mkdir()); 1078d2d6836SMatthias Sohn File serverDir = new File(getTemporaryDirectory(), "srv"); 1088d2d6836SMatthias Sohn assertTrue(serverDir.mkdir()); 1098d2d6836SMatthias Sohn // Create two key pairs. Let's not call them "id_rsa". 110eb67862cSThomas Wolf KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 111eb67862cSThomas Wolf generator.initialize(2048); 1128d2d6836SMatthias Sohn privateKey1 = new File(sshDir, "first_key"); 1138d2d6836SMatthias Sohn privateKey2 = new File(sshDir, "second_key"); 114eb67862cSThomas Wolf publicKey1 = createKeyPair(generator.generateKeyPair(), privateKey1); 115*566e49d7SThomas Wolf publicKey2 = createKeyPair(generator.generateKeyPair(), privateKey2); 116eb67862cSThomas Wolf // Create a host key 117eb67862cSThomas Wolf KeyPair hostKey = generator.generateKeyPair(); 1188d2d6836SMatthias Sohn // Start a server with our test user and the first key. 1198d2d6836SMatthias Sohn server = new SshTestGitServer(TEST_USER, publicKey1.toPath(), db, 120eb67862cSThomas Wolf hostKey); 1218d2d6836SMatthias Sohn testPort = server.start(); 1228d2d6836SMatthias Sohn assertTrue(testPort > 0); 1238d2d6836SMatthias Sohn knownHosts = new File(sshDir, "known_hosts"); 124eb67862cSThomas Wolf StringBuilder knownHostsLine = new StringBuilder(); 125eb67862cSThomas Wolf knownHostsLine.append("[localhost]:").append(testPort).append(' '); 126eb67862cSThomas Wolf PublicKeyEntry.appendPublicKeyEntry(knownHostsLine, 127eb67862cSThomas Wolf hostKey.getPublic()); 128eb67862cSThomas Wolf Files.write(knownHosts.toPath(), 129eb67862cSThomas Wolf Collections.singleton(knownHostsLine.toString())); 1308d2d6836SMatthias Sohn factory = createSessionFactory(); 1318d2d6836SMatthias Sohn SshSessionFactory.setInstance(factory); 1328d2d6836SMatthias Sohn } 1338d2d6836SMatthias Sohn createKeyPair(KeyPair newKey, File privateKeyFile)134eb67862cSThomas Wolf private static File createKeyPair(KeyPair newKey, File privateKeyFile) 135eb67862cSThomas Wolf throws Exception { 136eb67862cSThomas Wolf // Write PKCS#8 PEM unencrypted. Both JSch and sshd can read that. 137eb67862cSThomas Wolf PrivateKey privateKey = newKey.getPrivate(); 138eb67862cSThomas Wolf String format = privateKey.getFormat(); 139eb67862cSThomas Wolf if (!"PKCS#8".equalsIgnoreCase(format)) { 140eb67862cSThomas Wolf throw new IOException("Cannot write " + privateKey.getAlgorithm() 141eb67862cSThomas Wolf + " key in " + format + " format"); 142eb67862cSThomas Wolf } 143eb67862cSThomas Wolf try (BufferedWriter writer = Files.newBufferedWriter( 144eb67862cSThomas Wolf privateKeyFile.toPath(), StandardCharsets.US_ASCII)) { 145eb67862cSThomas Wolf writer.write("-----BEGIN PRIVATE KEY-----"); 146eb67862cSThomas Wolf writer.newLine(); 147eb67862cSThomas Wolf write(writer, privateKey.getEncoded(), 64); 148eb67862cSThomas Wolf writer.write("-----END PRIVATE KEY-----"); 149eb67862cSThomas Wolf writer.newLine(); 1508d2d6836SMatthias Sohn } 1518d2d6836SMatthias Sohn File publicKeyFile = new File(privateKeyFile.getParentFile(), 1528d2d6836SMatthias Sohn privateKeyFile.getName() + ".pub"); 153eb67862cSThomas Wolf StringBuilder builder = new StringBuilder(); 154eb67862cSThomas Wolf PublicKeyEntry.appendPublicKeyEntry(builder, newKey.getPublic()); 155eb67862cSThomas Wolf builder.append(' ').append(TEST_USER); 1568d2d6836SMatthias Sohn try (OutputStream out = new FileOutputStream(publicKeyFile)) { 157eb67862cSThomas Wolf out.write(builder.toString().getBytes(StandardCharsets.US_ASCII)); 1588d2d6836SMatthias Sohn } 1598d2d6836SMatthias Sohn return publicKeyFile; 1608d2d6836SMatthias Sohn } 1618d2d6836SMatthias Sohn write(BufferedWriter out, byte[] bytes, int lineLength)162eb67862cSThomas Wolf private static void write(BufferedWriter out, byte[] bytes, int lineLength) 163eb67862cSThomas Wolf throws IOException { 164eb67862cSThomas Wolf String data = Base64.getEncoder().encodeToString(bytes); 165eb67862cSThomas Wolf int last = data.length(); 166eb67862cSThomas Wolf for (int i = 0; i < last; i += lineLength) { 167eb67862cSThomas Wolf if (i + lineLength <= last) { 168eb67862cSThomas Wolf out.write(data.substring(i, i + lineLength)); 169eb67862cSThomas Wolf } else { 170eb67862cSThomas Wolf out.write(data.substring(i)); 1718d2d6836SMatthias Sohn } 172eb67862cSThomas Wolf out.newLine(); 173eb67862cSThomas Wolf } 174eb67862cSThomas Wolf Arrays.fill(bytes, (byte) 0); 1758d2d6836SMatthias Sohn } 1768d2d6836SMatthias Sohn 1778d2d6836SMatthias Sohn /** 1788d2d6836SMatthias Sohn * Creates a new known_hosts file with one entry for the given host and port 1798d2d6836SMatthias Sohn * taken from the given public key file. 1808d2d6836SMatthias Sohn * 1818d2d6836SMatthias Sohn * @param file 1828d2d6836SMatthias Sohn * to write the known_hosts file to 1838d2d6836SMatthias Sohn * @param host 1848d2d6836SMatthias Sohn * for the entry 1858d2d6836SMatthias Sohn * @param port 1868d2d6836SMatthias Sohn * for the entry 1878d2d6836SMatthias Sohn * @param publicKey 1888d2d6836SMatthias Sohn * to use 1898d2d6836SMatthias Sohn * @return the public-key part of the line 1908d2d6836SMatthias Sohn * @throws IOException 1918d2d6836SMatthias Sohn */ createKnownHostsFile(File file, String host, int port, File publicKey)1928d2d6836SMatthias Sohn protected static String createKnownHostsFile(File file, String host, 1938d2d6836SMatthias Sohn int port, File publicKey) throws IOException { 194eb67862cSThomas Wolf List<String> lines = Files.readAllLines(publicKey.toPath(), 195eb67862cSThomas Wolf StandardCharsets.UTF_8); 1968d2d6836SMatthias Sohn assertEquals("Public key has too many lines", 1, lines.size()); 1978d2d6836SMatthias Sohn String pubKey = lines.get(0); 1988d2d6836SMatthias Sohn // Strip off the comment. 1998d2d6836SMatthias Sohn String[] parts = pubKey.split("\\s+"); 2008d2d6836SMatthias Sohn assertTrue("Unexpected key content", 2018d2d6836SMatthias Sohn parts.length == 2 || parts.length == 3); 2028d2d6836SMatthias Sohn String keyPart = parts[0] + ' ' + parts[1]; 2038d2d6836SMatthias Sohn String line = '[' + host + "]:" + port + ' ' + keyPart; 2048d2d6836SMatthias Sohn Files.write(file.toPath(), Collections.singletonList(line)); 2058d2d6836SMatthias Sohn return keyPart; 2068d2d6836SMatthias Sohn } 2078d2d6836SMatthias Sohn 2088d2d6836SMatthias Sohn /** 2098d2d6836SMatthias Sohn * Checks whether there is a line for the given host and port that also 2108d2d6836SMatthias Sohn * matches the given key part in the list of lines. 2118d2d6836SMatthias Sohn * 2128d2d6836SMatthias Sohn * @param host 2138d2d6836SMatthias Sohn * to look for 2148d2d6836SMatthias Sohn * @param port 2158d2d6836SMatthias Sohn * to look for 2168d2d6836SMatthias Sohn * @param keyPart 2178d2d6836SMatthias Sohn * to look for 2188d2d6836SMatthias Sohn * @param lines 2198d2d6836SMatthias Sohn * to look in 2208d2d6836SMatthias Sohn * @return {@code true} if found, {@code false} otherwise 2218d2d6836SMatthias Sohn */ hasHostKey(String host, int port, String keyPart, List<String> lines)2228d2d6836SMatthias Sohn protected boolean hasHostKey(String host, int port, String keyPart, 2238d2d6836SMatthias Sohn List<String> lines) { 2248d2d6836SMatthias Sohn String h = '[' + host + "]:" + port; 2258d2d6836SMatthias Sohn return lines.stream() 2268d2d6836SMatthias Sohn .anyMatch(l -> l.contains(h) && l.contains(keyPart)); 2278d2d6836SMatthias Sohn } 2288d2d6836SMatthias Sohn 2298d2d6836SMatthias Sohn @After shutdownServer()2308d2d6836SMatthias Sohn public void shutdownServer() throws Exception { 2318d2d6836SMatthias Sohn if (server != null) { 2328d2d6836SMatthias Sohn server.stop(); 2338d2d6836SMatthias Sohn server = null; 2348d2d6836SMatthias Sohn } 2358d2d6836SMatthias Sohn FS.DETECTED.setUserHome(homeDir); 2368d2d6836SMatthias Sohn SshSessionFactory.setInstance(null); 2378d2d6836SMatthias Sohn factory = null; 2388d2d6836SMatthias Sohn } 2398d2d6836SMatthias Sohn createSessionFactory()2408d2d6836SMatthias Sohn protected abstract SshSessionFactory createSessionFactory(); 2418d2d6836SMatthias Sohn getSessionFactory()2428d2d6836SMatthias Sohn protected SshSessionFactory getSessionFactory() { 2438d2d6836SMatthias Sohn return factory; 2448d2d6836SMatthias Sohn } 2458d2d6836SMatthias Sohn installConfig(String... config)2468d2d6836SMatthias Sohn protected abstract void installConfig(String... config); 2478d2d6836SMatthias Sohn 2488d2d6836SMatthias Sohn /** 2498d2d6836SMatthias Sohn * Copies a test data file contained in the test bundle to the given file. 2508d2d6836SMatthias Sohn * Equivalent to {@link #copyTestResource(Class, String, File)} with 2518d2d6836SMatthias Sohn * {@code SshTestHarness.class} as first parameter. 2528d2d6836SMatthias Sohn * 2538d2d6836SMatthias Sohn * @param resourceName 2548d2d6836SMatthias Sohn * of the test resource to copy 2558d2d6836SMatthias Sohn * @param to 2568d2d6836SMatthias Sohn * file to copy the resource to 2578d2d6836SMatthias Sohn * @throws IOException 2588d2d6836SMatthias Sohn * if the resource cannot be copied 2598d2d6836SMatthias Sohn */ copyTestResource(String resourceName, File to)2608d2d6836SMatthias Sohn protected void copyTestResource(String resourceName, File to) 2618d2d6836SMatthias Sohn throws IOException { 2628d2d6836SMatthias Sohn copyTestResource(SshTestHarness.class, resourceName, to); 2638d2d6836SMatthias Sohn } 2648d2d6836SMatthias Sohn 2658d2d6836SMatthias Sohn /** 2668d2d6836SMatthias Sohn * Copies a test data file contained in the test bundle to the given file, 2678d2d6836SMatthias Sohn * using {@link Class#getResourceAsStream(String)} to get the test resource. 2688d2d6836SMatthias Sohn * 2698d2d6836SMatthias Sohn * @param loader 2708d2d6836SMatthias Sohn * {@link Class} to use to load the resource 2718d2d6836SMatthias Sohn * @param resourceName 2728d2d6836SMatthias Sohn * of the test resource to copy 2738d2d6836SMatthias Sohn * @param to 2748d2d6836SMatthias Sohn * file to copy the resource to 2758d2d6836SMatthias Sohn * @throws IOException 2768d2d6836SMatthias Sohn * if the resource cannot be copied 2778d2d6836SMatthias Sohn */ copyTestResource(Class<?> loader, String resourceName, File to)2788d2d6836SMatthias Sohn protected void copyTestResource(Class<?> loader, String resourceName, 2798d2d6836SMatthias Sohn File to) throws IOException { 2808d2d6836SMatthias Sohn try (InputStream in = loader.getResourceAsStream(resourceName)) { 2818d2d6836SMatthias Sohn Files.copy(in, to.toPath()); 2828d2d6836SMatthias Sohn } 2838d2d6836SMatthias Sohn } 2848d2d6836SMatthias Sohn cloneWith(String uri, File to, CredentialsProvider provider, String... config)2858d2d6836SMatthias Sohn protected File cloneWith(String uri, File to, CredentialsProvider provider, 2868d2d6836SMatthias Sohn String... config) throws Exception { 2878d2d6836SMatthias Sohn installConfig(config); 2888d2d6836SMatthias Sohn CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true) 2898d2d6836SMatthias Sohn .setDirectory(to).setURI(uri); 2908d2d6836SMatthias Sohn if (provider != null) { 2918d2d6836SMatthias Sohn clone.setCredentialsProvider(provider); 2928d2d6836SMatthias Sohn } 2938d2d6836SMatthias Sohn try (Git git = clone.call()) { 2948d2d6836SMatthias Sohn Repository repo = git.getRepository(); 2958d2d6836SMatthias Sohn assertNotNull(repo.resolve("master")); 2968d2d6836SMatthias Sohn assertNotEquals(db.getWorkTree(), 2978d2d6836SMatthias Sohn git.getRepository().getWorkTree()); 2988d2d6836SMatthias Sohn assertTrue(new File(git.getRepository().getWorkTree(), "file.txt") 2998d2d6836SMatthias Sohn .exists()); 3008d2d6836SMatthias Sohn return repo.getWorkTree(); 3018d2d6836SMatthias Sohn } 3028d2d6836SMatthias Sohn } 3038d2d6836SMatthias Sohn pushTo(File localClone)3048d2d6836SMatthias Sohn protected void pushTo(File localClone) throws Exception { 3058d2d6836SMatthias Sohn pushTo(null, localClone); 3068d2d6836SMatthias Sohn } 3078d2d6836SMatthias Sohn pushTo(CredentialsProvider provider, File localClone)3088d2d6836SMatthias Sohn protected void pushTo(CredentialsProvider provider, File localClone) 3098d2d6836SMatthias Sohn throws Exception { 3108d2d6836SMatthias Sohn RevCommit commit; 3118d2d6836SMatthias Sohn File newFile = null; 3128d2d6836SMatthias Sohn try (Git git = Git.open(localClone)) { 3138d2d6836SMatthias Sohn // Write a new file and modify a file. 3148d2d6836SMatthias Sohn Repository local = git.getRepository(); 3158d2d6836SMatthias Sohn newFile = File.createTempFile("new", "sshtest", 3168d2d6836SMatthias Sohn local.getWorkTree()); 3178d2d6836SMatthias Sohn write(newFile, "something new"); 3188d2d6836SMatthias Sohn File existingFile = new File(local.getWorkTree(), "file.txt"); 3198d2d6836SMatthias Sohn write(existingFile, "something else"); 3208d2d6836SMatthias Sohn git.add().addFilepattern("file.txt") 3218d2d6836SMatthias Sohn .addFilepattern(newFile.getName()) 3228d2d6836SMatthias Sohn .call(); 3238d2d6836SMatthias Sohn commit = git.commit().setMessage("Local commit").call(); 3248d2d6836SMatthias Sohn // Push 3258d2d6836SMatthias Sohn PushCommand push = git.push().setPushAll(); 3268d2d6836SMatthias Sohn if (provider != null) { 3278d2d6836SMatthias Sohn push.setCredentialsProvider(provider); 3288d2d6836SMatthias Sohn } 3298d2d6836SMatthias Sohn Iterable<PushResult> results = push.call(); 3308d2d6836SMatthias Sohn for (PushResult result : results) { 3318d2d6836SMatthias Sohn for (RemoteRefUpdate u : result.getRemoteUpdates()) { 3328d2d6836SMatthias Sohn assertEquals( 3338d2d6836SMatthias Sohn "Could not update " + u.getRemoteName() + ' ' 3348d2d6836SMatthias Sohn + u.getMessage(), 3358d2d6836SMatthias Sohn RemoteRefUpdate.Status.OK, u.getStatus()); 3368d2d6836SMatthias Sohn } 3378d2d6836SMatthias Sohn } 3388d2d6836SMatthias Sohn } 3398d2d6836SMatthias Sohn // Now check "master" in the remote repo directly: 3408d2d6836SMatthias Sohn assertEquals("Unexpected remote commit", commit, db.resolve("master")); 3418d2d6836SMatthias Sohn assertEquals("Unexpected remote commit", commit, 3428d2d6836SMatthias Sohn db.resolve(Constants.HEAD)); 3438d2d6836SMatthias Sohn File remoteFile = new File(db.getWorkTree(), newFile.getName()); 3448d2d6836SMatthias Sohn assertFalse("File should not exist on remote", remoteFile.exists()); 3458d2d6836SMatthias Sohn try (Git git = new Git(db)) { 3468d2d6836SMatthias Sohn git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call(); 3478d2d6836SMatthias Sohn } 3488d2d6836SMatthias Sohn assertTrue("File does not exist on remote", remoteFile.exists()); 3498d2d6836SMatthias Sohn checkFile(remoteFile, "something new"); 3508d2d6836SMatthias Sohn } 3518d2d6836SMatthias Sohn 3528d2d6836SMatthias Sohn protected static class TestCredentialsProvider extends CredentialsProvider { 3538d2d6836SMatthias Sohn 3548d2d6836SMatthias Sohn private final List<String> stringStore; 3558d2d6836SMatthias Sohn 3568d2d6836SMatthias Sohn private final Iterator<String> strings; 3578d2d6836SMatthias Sohn TestCredentialsProvider(String... strings)3588d2d6836SMatthias Sohn public TestCredentialsProvider(String... strings) { 3598d2d6836SMatthias Sohn if (strings == null || strings.length == 0) { 3608d2d6836SMatthias Sohn stringStore = Collections.emptyList(); 3618d2d6836SMatthias Sohn } else { 3628d2d6836SMatthias Sohn stringStore = Arrays.asList(strings); 3638d2d6836SMatthias Sohn } 3648d2d6836SMatthias Sohn this.strings = stringStore.iterator(); 3658d2d6836SMatthias Sohn } 3668d2d6836SMatthias Sohn 3678d2d6836SMatthias Sohn @Override isInteractive()3688d2d6836SMatthias Sohn public boolean isInteractive() { 3698d2d6836SMatthias Sohn return true; 3708d2d6836SMatthias Sohn } 3718d2d6836SMatthias Sohn 3728d2d6836SMatthias Sohn @Override supports(CredentialItem... items)3738d2d6836SMatthias Sohn public boolean supports(CredentialItem... items) { 3748d2d6836SMatthias Sohn return true; 3758d2d6836SMatthias Sohn } 3768d2d6836SMatthias Sohn 3778d2d6836SMatthias Sohn @Override get(URIish uri, CredentialItem... items)3788d2d6836SMatthias Sohn public boolean get(URIish uri, CredentialItem... items) 3798d2d6836SMatthias Sohn throws UnsupportedCredentialItem { 3808d2d6836SMatthias Sohn System.out.println("URI: " + uri); 3818d2d6836SMatthias Sohn for (CredentialItem item : items) { 3828d2d6836SMatthias Sohn System.out.println(item.getClass().getSimpleName() + ' ' 3838d2d6836SMatthias Sohn + item.getPromptText()); 3848d2d6836SMatthias Sohn } 3858d2d6836SMatthias Sohn logItems(uri, items); 3868d2d6836SMatthias Sohn for (CredentialItem item : items) { 3878d2d6836SMatthias Sohn if (item instanceof CredentialItem.InformationalMessage) { 3888d2d6836SMatthias Sohn continue; 3898d2d6836SMatthias Sohn } 3908d2d6836SMatthias Sohn if (item instanceof CredentialItem.YesNoType) { 3918d2d6836SMatthias Sohn ((CredentialItem.YesNoType) item).setValue(true); 3928d2d6836SMatthias Sohn } else if (item instanceof CredentialItem.CharArrayType) { 3938d2d6836SMatthias Sohn if (strings.hasNext()) { 3948d2d6836SMatthias Sohn ((CredentialItem.CharArrayType) item) 3958d2d6836SMatthias Sohn .setValue(strings.next().toCharArray()); 3968d2d6836SMatthias Sohn } else { 3978d2d6836SMatthias Sohn return false; 3988d2d6836SMatthias Sohn } 3998d2d6836SMatthias Sohn } else if (item instanceof CredentialItem.StringType) { 4008d2d6836SMatthias Sohn if (strings.hasNext()) { 4018d2d6836SMatthias Sohn ((CredentialItem.StringType) item) 4028d2d6836SMatthias Sohn .setValue(strings.next()); 4038d2d6836SMatthias Sohn } else { 4048d2d6836SMatthias Sohn return false; 4058d2d6836SMatthias Sohn } 4068d2d6836SMatthias Sohn } else { 4078d2d6836SMatthias Sohn return false; 4088d2d6836SMatthias Sohn } 4098d2d6836SMatthias Sohn } 4108d2d6836SMatthias Sohn return true; 4118d2d6836SMatthias Sohn } 4128d2d6836SMatthias Sohn 4138d2d6836SMatthias Sohn private List<LogEntry> log = new ArrayList<>(); 4148d2d6836SMatthias Sohn logItems(URIish uri, CredentialItem... items)4158d2d6836SMatthias Sohn private void logItems(URIish uri, CredentialItem... items) { 4168d2d6836SMatthias Sohn log.add(new LogEntry(uri, Arrays.asList(items))); 4178d2d6836SMatthias Sohn } 4188d2d6836SMatthias Sohn getLog()4198d2d6836SMatthias Sohn public List<LogEntry> getLog() { 4208d2d6836SMatthias Sohn return log; 4218d2d6836SMatthias Sohn } 4228d2d6836SMatthias Sohn } 4238d2d6836SMatthias Sohn 4248d2d6836SMatthias Sohn protected static class LogEntry { 4258d2d6836SMatthias Sohn 4268d2d6836SMatthias Sohn private URIish uri; 4278d2d6836SMatthias Sohn 4288d2d6836SMatthias Sohn private List<CredentialItem> items; 4298d2d6836SMatthias Sohn LogEntry(URIish uri, List<CredentialItem> items)4308d2d6836SMatthias Sohn public LogEntry(URIish uri, List<CredentialItem> items) { 4318d2d6836SMatthias Sohn this.uri = uri; 4328d2d6836SMatthias Sohn this.items = items; 4338d2d6836SMatthias Sohn } 4348d2d6836SMatthias Sohn getURIish()4358d2d6836SMatthias Sohn public URIish getURIish() { 4368d2d6836SMatthias Sohn return uri; 4378d2d6836SMatthias Sohn } 4388d2d6836SMatthias Sohn getItems()4398d2d6836SMatthias Sohn public List<CredentialItem> getItems() { 4408d2d6836SMatthias Sohn return items; 4418d2d6836SMatthias Sohn } 4428d2d6836SMatthias Sohn } 4438d2d6836SMatthias Sohn } 444