/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Bruce Chapman, Yahoo! Inc.
*
* 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 hudson.scm;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.ClassicPluginStrategy;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Launcher.LocalLauncher;
import hudson.Proc;
import hudson.model.AbstractProject;
import hudson.slaves.DumbSlave;
import hudson.model.Cause;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParametersAction;
import hudson.model.Result;
import hudson.model.StringParameterValue;
import hudson.model.TaskListener;
import hudson.scm.SubversionSCM.ModuleLocation;
import static hudson.scm.SubversionSCM.compareSVNAuthentications;
import hudson.scm.SubversionSCM.DescriptorImpl;
import hudson.scm.browsers.Sventon;
import hudson.util.FormValidation;
import hudson.util.NullStream;
import hudson.util.StreamTaskListener;
import org.dom4j.Document;
import org.dom4j.io.DOMReader;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Email;
import org.jvnet.hudson.test.HudsonHomeLoader.CopyExisting;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.Url;
import org.jvnet.hudson.test.recipes.PresetData;
import static org.jvnet.hudson.test.recipes.PresetData.DataSet.ANONYMOUS_READONLY;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
import org.tmatesoft.svn.core.auth.SVNSSHAuthentication;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
import org.tmatesoft.svn.core.auth.SVNSSLAuthentication;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
/**
* @author Kohsuke Kawaguchi
*/
public class SubversionSCMTest extends HudsonTestCase {
private static final int LOG_LIMIT = 1000;
private DescriptorImpl descriptor;
// in some tests we play authentication games with this repo
String realm = "<https://hudson.dev.java.net:443> CollabNet Subversion Repository";
String kind = ISVNAuthenticationManager.PASSWORD;
SVNURL repo;
@Override
protected void setUp() throws Exception {
super.setUp();
descriptor = hudson.getDescriptorByType(DescriptorImpl.class);
repo = SVNURL.parseURIDecoded("https://hudson.dev.java.net/svn/hudson");
}
/**
* Sets guest credentials to access java.net Subversion repo.
*/
protected void setJavaNetCredential() throws SVNException, IOException {
// set the credential to access svn.dev.java.net
descriptor.postCredential(null,"https://svn.dev.java.net/svn/hudson/","guest","",null,new PrintWriter(new NullStream()));
}
@PresetData(ANONYMOUS_READONLY)
@Bug(2380)
public void testTaggingPermission() throws Exception {
// create a build
FreeStyleProject p = createFreeStyleProject();
p.setScm(loadSvnRepo());
FreeStyleBuild b = p.scheduleBuild2(0, new Cause.UserCause()).get();
System.out.println(b.getLog(LOG_LIMIT));
assertBuildStatus(Result.SUCCESS,b);
SubversionTagAction action = b.getAction(SubversionTagAction.class);
assertFalse(b.hasPermission(action.getPermission()));
WebClient wc = new WebClient();
HtmlPage html = wc.getPage(b);
// make sure there's no link to the 'tag this build'
Document dom = new DOMReader().read(html);
assertNull(dom.selectSingleNode("//A[text()='Tag this build']"));
for( HtmlAnchor a : html.getAnchors() )
assertFalse(a.getHrefAttribute().contains("/tagBuild/"));
// and no tag form on tagBuild page
html = wc.getPage(b,"tagBuild/");
try {
html.getFormByName("tag");
fail("should not have been found");
} catch (ElementNotFoundException e) {
}
// and that tagging would fail
try {
wc.getPage(b,"tagBuild/submit?name0=test&Submit=Tag");
fail("should have been denied");
} catch (FailingHttpStatusCodeException e) {
// make sure the request is denied
assertEquals(e.getResponse().getStatusCode(),403);
}
// now login as alice and make sure that the tagging would succeed
wc = new WebClient();
wc.login("alice","alice");
html = wc.getPage(b,"tagBuild/");
HtmlForm form = html.getFormByName("tag");
submit(form);
}
/**
* Loads a test Subversion repository into a temporary directory, and creates {@link SubversionSCM} for it.
*/
private SubversionSCM loadSvnRepo() throws Exception {
return new SubversionSCM("file://" + new CopyExisting(getClass().getResource("/svn-repo.zip")).allocate().toURI().toURL().getPath() + "trunk/a","a");
}
@Email("http://www.nabble.com/Hudson-1.266-and-1.267%3A-Subversion-authentication-broken--td21156950.html")
public void testHttpsCheckOut() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject();
p.setScm(new SubversionSCM("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/trivial-ant"));
FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserCause()).get());
assertTrue(b.getWorkspace().child("build.xml").exists());
}
@Email("http://www.nabble.com/Hudson-1.266-and-1.267%3A-Subversion-authentication-broken--td21156950.html")
public void testHttpCheckOut() throws Exception {
FreeStyleProject p = createFreeStyleProject();
p.setScm(new SubversionSCM("http://svn.codehaus.org/plexus/tags/JASF_INIT/plexus-avalon-components/jasf/"));
FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserCause()).get());
assertTrue(b.getWorkspace().child("maven.xml").exists());
}
@Url("http://hudson.pastebin.com/m3ea34eea")
public void testRemoteCheckOut() throws Exception {
setJavaNetCredential();
DumbSlave s = createSlave();
FreeStyleProject p = createFreeStyleProject();
p.setAssignedLabel(s.getSelfLabel());
p.setScm(new SubversionSCM("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/trivial-ant"));
FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserCause()).get());
assertTrue(b.getWorkspace().child("build.xml").exists());
b = assertBuildStatusSuccess(p.scheduleBuild2(0).get());
}
/**
* Tests the "URL@REV" format in SVN URL.
*/
@Bug(262)
public void testRevisionedCheckout() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject();
p.setScm(new SubversionSCM("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/trivial-ant@13000"));
FreeStyleBuild b = p.scheduleBuild2(0, new Cause.UserCause()).get();
System.out.println(b.getLog(LOG_LIMIT));
assertTrue(b.getLog(LOG_LIMIT).contains("At revision 13000"));
assertBuildStatus(Result.SUCCESS,b);
b = p.scheduleBuild2(0, new Cause.UserCause()).get();
System.out.println(b.getLog(LOG_LIMIT));
assertTrue(b.getLog(LOG_LIMIT).contains("At revision 13000"));
assertBuildStatus(Result.SUCCESS,b);
}
/**
* Tests a checkout with RevisionParameterAction
*/
public void testRevisionParameter() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject();
String url = "https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/trivial-ant";
p.setScm(new SubversionSCM(url));
FreeStyleBuild b = p.scheduleBuild2(0, new Cause.UserCause(),
new RevisionParameterAction(new SubversionSCM.SvnInfo(url, 13000))).get();
System.out.println(b.getLog(LOG_LIMIT));
assertTrue(b.getLog(LOG_LIMIT).contains("At revision 13000"));
assertBuildStatus(Result.SUCCESS,b);
}
/**
* {@link SubversionSCM#pollChanges(AbstractProject , Launcher , FilePath, TaskListener)} should notice
* if the workspace and the current configuration is inconsistent and schedule a new build.
*/
@Email("http://www.nabble.com/Proper-way-to-switch---relocate-SVN-tree---tt21173306.html")
public void testPollingAfterRelocation() throws Exception {
// fetch the current workspace
FreeStyleProject p = createFreeStyleProject();
p.setScm(loadSvnRepo());
p.scheduleBuild2(0, new Cause.UserCause()).get();
// as a baseline, this shouldn't detect any change
TaskListener listener = createTaskListener();
assertFalse(p.pollSCMChanges(listener));
// now switch the repository to a new one.
// this time the polling should indicate that we need a new build
p.setScm(loadSvnRepo());
assertTrue(p.pollSCMChanges(listener));
// build it once again to switch
p.scheduleBuild2(0, new Cause.UserCause()).get();
// then no more change should be detected
assertFalse(p.pollSCMChanges(listener));
}
public void testURLWithVariable() throws Exception {
FreeStyleProject p = createFreeStyleProject();
String url = "http://svn.codehaus.org/plexus/tags/JASF_INIT/plexus-avalon-components/jasf/";
p.setScm(new SubversionSCM("$REPO" + url.substring(10)));
String var = url.substring(0, 10);
FreeStyleBuild b = p.scheduleBuild2(0, new Cause.UserCause(),
new ParametersAction(new StringParameterValue("REPO", var))).get();
System.out.println(b.getLog(LOG_LIMIT));
assertBuildStatus(Result.SUCCESS,b);
assertTrue(b.getWorkspace().child("maven.xml").exists());
}
/**
* Test that multiple repository URLs are all polled.
*/
@Bug(3168)
public void testPollMultipleRepositories() throws Exception {
// fetch the current workspaces
FreeStyleProject p = createFreeStyleProject();
String svnBase = "file://" + new CopyExisting(getClass().getResource("/svn-repo.zip")).allocate().toURI().toURL().getPath();
SubversionSCM scm = new SubversionSCM(
Arrays.asList(new ModuleLocation(svnBase + "trunk", null), new ModuleLocation(svnBase + "branches", null)),
false, false, null, null, null, null, null);
p.setScm(scm);
FreeStyleBuild build = p.scheduleBuild2(0, new Cause.UserCause()).get();
// as a baseline, this shouldn't detect any change
TaskListener listener = createTaskListener();
assertFalse(p.pollSCMChanges(listener));
createCommit(scm,"branches/foo");
assertTrue("any change in any of the repository should be detected", p.pollSCMChanges(listener));
assertFalse("no change since the last polling",p.pollSCMChanges(listener));
createCommit(scm,"trunk/foo");
assertTrue("another change in the repo should be detected separately", p.pollSCMChanges(listener));
}
public void testConfigRoundtrip() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject();
SubversionSCM scm = new SubversionSCM(
Arrays.asList(
new ModuleLocation("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/testSubversionExclusion", "c"),
new ModuleLocation("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/testSubversionExclusion", "d")),
true,new Sventon(new URL("http://www.sun.com/"),"test"),"exclude","user","revprop","excludeMessage");
p.setScm(scm);
submit(new WebClient().getPage(p,"configure").getFormByName("config"));
verify(scm,(SubversionSCM)p.getScm());
scm = new SubversionSCM(
Arrays.asList(
new ModuleLocation("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/testSubversionExclusion", "c")),
false,null,"","","","");
p.setScm(scm);
submit(new WebClient().getPage(p,"configure").getFormByName("config"));
verify(scm,(SubversionSCM)p.getScm());
}
@Bug(5684)
public void testDoCheckExcludedUsers() throws Exception {
String[] validUsernames = new String[] {
"DOMAIN\\user",
"user",
"us_er",
"user123",
"User",
"", // this one is ignored
"DOmain12\\User34"};
for (String validUsername : validUsernames) {
assertEquals(
"User " + validUsername + " isn't OK (but it's valid).",
FormValidation.Kind.OK,
new SubversionSCM.DescriptorImpl().doCheckExcludedUsers(validUsername).kind);
}
String[] invalidUsernames = new String[] {
"\\user",
"DOMAIN\\",
"DOMAIN@user",
"DOMAIN.user" };
for (String invalidUsername : invalidUsernames) {
assertEquals(
"User " + invalidUsername + " isn't ERROR (but it's not valid).",
FormValidation.Kind.ERROR,
new SubversionSCM.DescriptorImpl().doCheckExcludedUsers(invalidUsername).kind);
}
}
private void verify(SubversionSCM lhs, SubversionSCM rhs) {
ModuleLocation[] ll = lhs.getLocations();
ModuleLocation[] rl = rhs.getLocations();
assertEquals(ll.length, rl.length);
for(int i=0; i<ll.length; i++) {
assertEquals(ll[i].getLocalDir(), rl[i].getLocalDir());
assertEquals(ll[i].remote, rl[i].remote);
}
assertEquals(lhs.isUseUpdate(), rhs.isUseUpdate());
assertNullEquals(lhs.getExcludedRegions(), rhs.getExcludedRegions());
assertNullEquals(lhs.getExcludedUsers(), rhs.getExcludedUsers());
assertNullEquals(lhs.getExcludedRevprop(), rhs.getExcludedRevprop());
assertNullEquals(lhs.getExcludedCommitMessages(), rhs.getExcludedCommitMessages());
assertNullEquals(lhs.getIncludedRegions(), rhs.getIncludedRegions());
}
private void assertNullEquals (String left, String right) {
if (left == null)
left = "";
if (right == null)
right = "";
assertEquals(left, right);
}
public void test1() {
check("http://foobar/");
check("https://foobar/");
check("file://foobar/");
check("svn://foobar/");
check("svn+ssh://foobar/");
}
public void test2() {
String[] r = "abc\\ def ghi".split("(?<!\\\\)[ \\r\\n]+");
for (int i = 0; i < r.length; i++) {
r[i] = r[i].replaceAll("\\\\ "," ");
}
System.out.println(Arrays.asList(r));
assertEquals(r.length,2);
}
private void check(String url) {
assertTrue(SubversionSCM.URL_PATTERN.matcher(url).matches());
}
/**
* Makes sure that Subversion doesn't check out workspace in 1.6
*/
@Email("http://www.nabble.com/SVN-1.6-td24081571.html")
public void testWorkspaceVersion() throws Exception {
FreeStyleProject p = createFreeStyleProject();
p.setScm(loadSvnRepo());
FreeStyleBuild b = p.scheduleBuild2(0).get();
SVNClientManager wc = SubversionSCM.createSvnClientManager((AbstractProject)null);
SVNStatus st = wc.getStatusClient().doStatus(new File(b.getWorkspace().getRemote()+"/a"), false);
int wcf = st.getWorkingCopyFormat();
System.out.println(wcf);
assertEquals(SVNAdminAreaFactory.WC_FORMAT_14,wcf);
}
private static String readFileAsString(File file)
throws java.io.IOException{
StringBuffer fileData = new StringBuffer(1000);
BufferedReader reader = new BufferedReader(new FileReader(file));
char[] buf = new char[1024];
int numRead=0;
while((numRead=reader.read(buf)) != -1){
fileData.append(buf, 0, numRead);
}
reader.close();
return fileData.toString();
}
/**
* Makes sure the symbolic link is checked out correctly. There seems to be
*/
@Bug(3904)
public void testSymbolicLinkCheckout() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject();
p.setScm(new SubversionSCM("https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/issue-3904"));
FreeStyleBuild b = p.scheduleBuild2(0, new Cause.UserCause()).get();
File source = new File(b.getWorkspace().getRemote() + "/readme.txt");
File linked = new File(b.getWorkspace().getRemote() + "/linked.txt");
assertEquals("Files '" + source + "' and '" + linked + "' are not identical from user view.", readFileAsString(source), readFileAsString(linked));
}
public void testExcludeByUser() throws Exception {
setJavaNetCredential();
FreeStyleProject p = createFreeStyleProject( "testExcludeByUser" );
p.setScm(new SubversionSCM(
Arrays.asList( new ModuleLocation( "https://svn.dev.java.net/svn/hudson/trunk/hudson/test-projects/testSubversionExclusions@19438", null )),
true, null, "", "dty", "", "")
);
// Do a build to force the creation of the workspace. This works around
// pollChanges returning true when the workspace does not exist.
p.scheduleBuild2(0).get();
boolean foundChanges = p.pollSCMChanges(createTaskListener());
assertFalse("Polling found changes that should have been ignored", foundChanges);
}
/**
* Test excluded regions
*/
@Bug(6030)
public void testExcludedRegions() throws Exception {
// SLAVE_DEBUG_PORT = 8001;
File repo = new CopyExisting(getClass().getResource("HUDSON-6030.zip")).allocate();
SubversionSCM scm = new SubversionSCM(ModuleLocation.parse(new String[]{"file://" + repo.getPath()},
new String[]{"."}),
true, false, null, ".*/bar", "", "", "", "");
FreeStyleProject p = createFreeStyleProject("testExcludedRegions");
p.setScm(scm);
assertBuildStatusSuccess(p.scheduleBuild2(0).get());
// initial polling on the slave for the code path that doesn't find any change
assertFalse(p.pollSCMChanges(createTaskListener()));
createCommit(scm, "bar");
// polling on the slave for the code path that does have a change but should be excluded.
assertFalse("Polling found changes that should have been ignored",
p.pollSCMChanges(createTaskListener()));
createCommit(scm, "foo");
// polling on the slave for the code path that doesn't find any change
assertTrue("Polling didn't find a change it should have found.",
p.pollSCMChanges(createTaskListener()));
}
/**
* Test included regions
*/
@Bug(6030)
public void testIncludedRegions() throws Exception {
// SLAVE_DEBUG_PORT = 8001;
File repo = new CopyExisting(getClass().getResource("HUDSON-6030.zip")).allocate();
SubversionSCM scm = new SubversionSCM(ModuleLocation.parse(new String[]{"file://" + repo.getPath()},
new String[]{"."}),
true, false, null, "", "", "", "", ".*/foo");
FreeStyleProject p = createFreeStyleProject("testExcludedRegions");
p.setScm(scm);
assertBuildStatusSuccess(p.scheduleBuild2(0).get());
// initial polling on the slave for the code path that doesn't find any change
assertFalse(p.pollSCMChanges(createTaskListener()));
createCommit(scm, "bar");
// polling on the slave for the code path that does have a change but should be excluded.
assertFalse("Polling found changes that should have been ignored",
p.pollSCMChanges(createTaskListener()));
createCommit(scm, "foo");
// polling on the slave for the code path that doesn't find any change
assertTrue("Polling didn't find a change it should have found.",
p.pollSCMChanges(createTaskListener()));
}
/**
* Do the polling on the slave and make sure it works.
*/
@Bug(4299)
public void testPolling() throws Exception {
// SLAVE_DEBUG_PORT = 8001;
File repo = new CopyExisting(getClass().getResource("two-revisions.zip")).allocate();
SubversionSCM scm = new SubversionSCM("file://" + repo.getPath());
FreeStyleProject p = createFreeStyleProject();
p.setScm(scm);
p.setAssignedLabel(createSlave().getSelfLabel());
assertBuildStatusSuccess(p.scheduleBuild2(0).get());
// initial polling on the slave for the code path that doesn't find any change
assertFalse(p.pollSCMChanges(new StreamTaskListener(System.out, Charset.defaultCharset())));
createCommit(scm, "foo");
// polling on the slave for the code path that doesn't find any change
assertTrue(p.pollSCMChanges(new StreamTaskListener(System.out, Charset.defaultCharset())));
}
/**
* Manufactures commits by adding files in the given names.
*/
private void createCommit(SubversionSCM scm, String... paths) throws Exception {
FreeStyleProject forCommit = createFreeStyleProject();
forCommit.setScm(scm);
forCommit.setAssignedLabel(hudson.getSelfLabel());
FreeStyleBuild b = assertBuildStatusSuccess(forCommit.scheduleBuild2(0).get());
SVNClientManager svnm = SubversionSCM.createSvnClientManager((AbstractProject)null);
List<File> added = new ArrayList<File>();
for (String path : paths) {
FilePath newFile = b.getWorkspace().child(path);
added.add(new File(newFile.getRemote()));
if (!newFile.exists()) {
newFile.touch(System.currentTimeMillis());
svnm.getWCClient().doAdd(new File(newFile.getRemote()),false,false,false, SVNDepth.INFINITY, false,false);
} else
newFile.write("random content","UTF-8");
}
SVNCommitClient cc = svnm.getCommitClient();
cc.doCommit(added.toArray(new File[added.size()]),false,"added",null,null,false,false,SVNDepth.EMPTY);
}
public void testCompareSVNAuthentications() throws Exception {
assertFalse(compareSVNAuthentications(new SVNUserNameAuthentication("me",true),new SVNSSHAuthentication("me","me",22,true)));
// same object should compare equal
_idem(new SVNUserNameAuthentication("me",true));
_idem(new SVNSSHAuthentication("me","pass",22,true));
_idem(new SVNSSHAuthentication("me",new File("./some.key"),null,23,false));
_idem(new SVNSSHAuthentication("me","key".toCharArray(),"phrase",0,false));
_idem(new SVNPasswordAuthentication("me","pass",true));
_idem(new SVNSSLAuthentication("certificate".getBytes(),null,true));
// make sure two Files and char[]s compare the same
assertTrue(compareSVNAuthentications(
new SVNSSHAuthentication("me",new File("./some.key"),null,23,false),
new SVNSSHAuthentication("me",new File("./some.key"),null,23,false)));
assertTrue(compareSVNAuthentications(
new SVNSSHAuthentication("me","key".toCharArray(),"phrase",0,false),
new SVNSSHAuthentication("me","key".toCharArray(),"phrase",0,false)));
// negative cases
assertFalse(compareSVNAuthentications(
new SVNSSHAuthentication("me",new File("./some1.key"),null,23,false),
new SVNSSHAuthentication("me",new File("./some2.key"),null,23,false)));
assertFalse(compareSVNAuthentications(
new SVNSSHAuthentication("me","key".toCharArray(),"phrase",0,false),
new SVNSSHAuthentication("yo","key".toCharArray(),"phrase",0,false)));
}
private void _idem(SVNAuthentication a) {
assertTrue(compareSVNAuthentications(a,a));
}
/**
* Make sure that a failed credential doesn't result in an infinite loop
*/
@Bug(2909)
public void testInfiniteLoop() throws Exception {
// creates a purely in memory auth manager
ISVNAuthenticationManager m = createInMemoryManager();
// double check that it really knows nothing about the fake repo
try {
m.getFirstAuthentication(kind, realm, repo);
fail();
} catch (SVNCancelException e) {
// yep
}
// let Hudson have the credential
descriptor.postCredential(null,repo.toDecodedString(),"guest","",null,new PrintWriter(System.out));
// emulate the call flow where the credential fails
List<SVNAuthentication> attempted = new ArrayList<SVNAuthentication>();
SVNAuthentication a = m.getFirstAuthentication(kind, realm, repo);
assertNotNull(a);
attempted.add(a);
for (int i=0; i<10; i++) {
m.acknowledgeAuthentication(false,kind,realm,SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED),a);
try {
a = m.getNextAuthentication(kind,realm,repo);
assertNotNull(a);
attempted.add(a);
} catch (SVNCancelException e) {
// make sure we've tried our fake credential
for (SVNAuthentication aa : attempted) {
if (aa instanceof SVNPasswordAuthentication) {
SVNPasswordAuthentication pa = (SVNPasswordAuthentication) aa;
if(pa.getUserName().equals("guest") && pa.getPassword().equals(""))
return; // yep
}
}
fail("Hudson didn't try authentication");
}
}
fail("Looks like we went into an infinite loop");
}
/**
* Even if the default providers remember bogus passwords, Hudson should still attempt what it knows.
*/
@Bug(3936)
public void test3936() throws Exception {
// creates a purely in memory auth manager
ISVNAuthenticationManager m = createInMemoryManager();
// double check that it really knows nothing about the fake repo
try {
m.getFirstAuthentication(kind, realm, repo);
fail();
} catch (SVNCancelException e) {
// yep
}
// teach a bogus credential and have SVNKit store it.
SVNPasswordAuthentication bogus = new SVNPasswordAuthentication("bogus", "bogus", true);
m.acknowledgeAuthentication(true,kind,realm,null, bogus);
assertTrue(compareSVNAuthentications(m.getFirstAuthentication(kind, realm, repo),bogus));
try {
attemptAccess(m);
fail("SVNKit shouldn't yet know how to access");
} catch (SVNCancelException e) {
}
// make sure the failure didn't clean up the cache,
// since what we want to test here is Hudson trying to supply its credential, despite the failed cache
assertTrue(compareSVNAuthentications(m.getFirstAuthentication(kind, realm, repo),bogus));
// now let Hudson have the real credential
// can we now access the repo?
descriptor.postCredential(null,repo.toDecodedString(),"guest","",null,new PrintWriter(System.out));
attemptAccess(m);
}
private void attemptAccess(ISVNAuthenticationManager m) throws SVNException {
SVNRepository repository = SVNRepositoryFactory.create(repo);
repository.setAuthenticationManager(m);
repository.testConnection();
}
private ISVNAuthenticationManager createInMemoryManager() {
ISVNAuthenticationManager m = SVNWCUtil.createDefaultAuthenticationManager(hudson.root,null,null,false);
m.setAuthenticationProvider(descriptor.createAuthenticationProvider(null));
return m;
}
@Bug(1379)
public void testMultipleCredentialsPerRepo() throws Exception {
Proc p = runSvnServe(getClass().getResource("HUDSON-1379.zip"));
try {
FreeStyleProject b = createFreeStyleProject();
b.setScm(new SubversionSCM("svn://localhost/bob"));
FreeStyleProject c = createFreeStyleProject();
c.setScm(new SubversionSCM("svn://localhost/charlie"));
// should fail without a credential
assertBuildStatus(Result.FAILURE,b.scheduleBuild2(0).get());
descriptor.postCredential(b,"svn://localhost/bob","bob","bob",null,new PrintWriter(System.out));
buildAndAssertSuccess(b);
assertBuildStatus(Result.FAILURE,c.scheduleBuild2(0).get());
descriptor.postCredential(c,"svn://localhost/charlie","charlie","charlie",null,new PrintWriter(System.out));
buildAndAssertSuccess(c);
// b should still build fine.
buildAndAssertSuccess(b);
} finally {
p.kill();
}
}
@Bug(1379)
public void testSuperUserForAllRepos() throws Exception {
Proc p = runSvnServe(getClass().getResource("HUDSON-1379.zip"));
try {
FreeStyleProject b = createFreeStyleProject();
b.setScm(new SubversionSCM("svn://localhost/bob"));
FreeStyleProject c = createFreeStyleProject();
c.setScm(new SubversionSCM("svn://localhost/charlie"));
// should fail without a credential
assertBuildStatus(Result.FAILURE,b.scheduleBuild2(0).get());
assertBuildStatus(Result.FAILURE,c.scheduleBuild2(0).get());
// but with the super user credential both should work now
descriptor.postCredential(b,"svn://localhost/bob","alice","alice",null,new PrintWriter(System.out));
buildAndAssertSuccess(b);
buildAndAssertSuccess(c);
} finally {
p.kill();
}
}
private Proc runSvnServe(URL zip) throws Exception {
return runSvnServe(new CopyExisting(zip).allocate());
}
/**
* Runs svnserve to serve the specified directory as a subversion repository.
*/
private Proc runSvnServe(File repo) throws Exception {
LocalLauncher launcher = new LocalLauncher(new StreamTaskListener(System.out));
try {
launcher.launch().cmds("svnserve","--help").start().join();
} catch (IOException e) {
// if we fail to launch svnserve, skip the test
return null;
}
return launcher.launch().cmds(
"svnserve","-d","--foreground","-r",repo.getAbsolutePath()).pwd(repo).start();
}
static {
ClassicPluginStrategy.useAntClassLoader = true;
}
}