/* * The MIT License * * Copyright 2012 Jesse Glick. * * 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.cli; import edu.umd.cs.findbugs.annotations.SuppressWarnings; import static hudson.cli.CLICommandInvoker.Matcher.*; import hudson.model.AbstractItem; import hudson.model.FreeStyleProject; import hudson.model.Item; import hudson.model.Job; import hudson.model.User; import hudson.security.ACL; import hudson.security.AuthorizationStrategy; import hudson.security.SparseACL; import java.util.Collection; import java.util.Collections; import jenkins.model.Jenkins; import org.acegisecurity.acls.sid.PrincipalSid; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.MockFolder; @SuppressWarnings("DM_DEFAULT_ENCODING") public class CopyJobCommandTest { @Rule public JenkinsRule j = new JenkinsRule(); private CLICommand copyJobCommand; private CLICommandInvoker command; @Before public void setUp() { copyJobCommand = new CopyJobCommand(); command = new CLICommandInvoker(j, copyJobCommand); } @Test public void copyBetweenFolders() throws Exception { MockFolder dir1 = j.createFolder("dir1"); MockFolder dir2 = j.createFolder("dir2"); FreeStyleProject p = dir1.createProject(FreeStyleProject.class, "p1"); CLICommandInvoker.Result result = command.invokeWithArgs("dir1/p1", "dir2/p2"); assertThat(result, succeededSilently()); assertNotNull(j.jenkins.getItemByFullName("dir2/p2")); // TODO test copying from/to root, or into nonexistent folder } @Issue("JENKINS-22262") @Test public void folderPermissions() throws Exception { final MockFolder d1 = j.createFolder("d1"); final FreeStyleProject p = d1.createProject(FreeStyleProject.class, "p"); final MockFolder d2 = j.createFolder("d2"); // alice has no real permissions. bob has READ on everything but no more. charlie has CREATE on d2 but not EXTENDED_READ on p. debbie has both. final SparseACL rootACL = new SparseACL(null); rootACL.add(new PrincipalSid("alice"), Jenkins.READ, true); rootACL.add(new PrincipalSid("bob"), Jenkins.READ, true); rootACL.add(new PrincipalSid("charlie"), Jenkins.READ, true); rootACL.add(new PrincipalSid("debbie"), Jenkins.READ, true); final SparseACL d1ACL = new SparseACL(null); d1ACL.add(new PrincipalSid("bob"), Item.READ, true); d1ACL.add(new PrincipalSid("charlie"), Item.READ, true); d1ACL.add(new PrincipalSid("debbie"), Item.READ, true); final SparseACL pACL = new SparseACL(null); pACL.add(new PrincipalSid("bob"), Item.READ, true); pACL.add(new PrincipalSid("charlie"), Item.READ, true); pACL.add(new PrincipalSid("debbie"), Item.READ, true); pACL.add(new PrincipalSid("debbie"), Item.EXTENDED_READ, true); final SparseACL d2ACL = new SparseACL(null); d2ACL.add(new PrincipalSid("bob"), Item.READ, true); d2ACL.add(new PrincipalSid("charlie"), Item.READ, true); d2ACL.add(new PrincipalSid("charlie"), Item.CREATE, true); d2ACL.add(new PrincipalSid("debbie"), Item.READ, true); d2ACL.add(new PrincipalSid("debbie"), Item.CREATE, true); j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); j.jenkins.setAuthorizationStrategy(new AuthorizationStrategy() { @Override public ACL getRootACL() { return rootACL; } @Override public ACL getACL(Job<?, ?> project) { if (project == p) { return pACL; } else { throw new AssertionError(project); } } @Override public ACL getACL(AbstractItem item) { if (item == d1) { return d1ACL; } else if (item == d2) { return d2ACL; } else { throw new AssertionError(item); } } @Override public Collection<String> getGroups() { return Collections.emptySet(); } }); copyJobCommand.setTransportAuth(User.get("alice").impersonate()); assertThat(command.invokeWithArgs("d1/p", "d2/p"), failedWith(3)); copyJobCommand.setTransportAuth(User.get("bob").impersonate()); assertThat(command.invokeWithArgs("d1/p", "d2/p"), failedWith(6)); copyJobCommand.setTransportAuth(User.get("charlie").impersonate()); assertThat(command.invokeWithArgs("d1/p", "d2/p"), failedWith(6)); copyJobCommand.setTransportAuth(User.get("debbie").impersonate()); assertThat(command.invokeWithArgs("d1/p", "d2/p"), succeededSilently()); assertNotNull(d2.getItem("p")); } // hold off build until saved only makes sense on the UI with config screen shown after copying; // expect the CLI copy command to leave the job buildable @Test public void copiedJobIsBuildable() throws Exception { FreeStyleProject p1 = j.createFreeStyleProject(); String copiedProjectName = "p2"; CLICommandInvoker.Result result = command.invokeWithArgs(p1.getName(), copiedProjectName); assertThat(result, succeededSilently()); FreeStyleProject p2 = (FreeStyleProject)j.jenkins.getItem(copiedProjectName); assertNotNull(p2); assertTrue(p2.isBuildable()); } }