package hudson.tasks; import com.google.common.collect.Sets; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Item; import hudson.model.Result; import hudson.model.TaskListener; import hudson.model.User; import hudson.security.ACL; import hudson.util.StreamTaskListener; import java.io.StringWriter; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Set; import jenkins.model.Jenkins; import jenkins.plugins.mailer.tasks.i18n.Messages; import org.acegisecurity.Authentication; import org.acegisecurity.userdetails.UsernameNotFoundException; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.jvnet.hudson.test.Issue; import static org.mockito.Mockito.*; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; /** * Test case for the {@link MailSender} * * See also {@link MailerTest} for more tests for the mailer. * * @author Christoph Kutzinski */ @RunWith(PowerMockRunner.class) @PrepareForTest(Jenkins.class) @PowerMockIgnore("javax.security.auth.Subject") // otherwise as in https://groups.google.com/d/msg/jenkinsci-dev/n5sdCxrccSk/7K4yTTc7XG4J mock(ACL.class) in Java 8 fails with: java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.acegisecurity.Authentication$$EnhancerByMockitoWithCGLIB$$31bf4863.implies(Ljavax/security/auth/Subject;)Z" the class loader (instance of org/powermock/core/classloader/MockClassLoader) of the current class, org/acegisecurity/Authentication$$EnhancerByMockitoWithCGLIB$$31bf4863, and the class loader (instance of <bootloader>) for interface java/security/Principal have different Class objects for the type javax/security/auth/Subject used in the signature @SuppressWarnings("rawtypes") public class MailSenderTest { /** * Tests that all culprits from the previous builds upstream build (exclusive) * until the current builds upstream build (inclusive) are contained in the recipients * list. */ @SuppressWarnings("unchecked") @Test public void testIncludeUpstreamCulprits() throws Exception { final Jenkins jenkins = PowerMockito.mock(Jenkins.class); PowerMockito.when(jenkins.isUseSecurity()).thenReturn(false); PowerMockito.mockStatic(Jenkins.class); PowerMockito.doReturn(jenkins).when(Jenkins.class, "getActiveInstance"); AbstractProject upstreamProject = mock(AbstractProject.class); AbstractBuild previousBuildUpstreamBuild = mock(AbstractBuild.class); when(previousBuildUpstreamBuild.toString()).thenReturn("previousBuildUpstreamBuild"); AbstractBuild upstreamBuildBetweenPreviousAndCurrent = mock(AbstractBuild.class); when(upstreamBuildBetweenPreviousAndCurrent.toString()).thenReturn("upstreamBuildBetweenPreviousAndCurrent"); AbstractBuild upstreamBuild = mock(AbstractBuild.class); when(upstreamBuild.toString()).thenReturn("upstreamBuild"); createPreviousNextRelationShip(previousBuildUpstreamBuild, upstreamBuildBetweenPreviousAndCurrent, upstreamBuild); User user1 = mock(User.class); when(user1.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("this.one.should.not.be.included@example.com")); Set<User> badGuys1 = Sets.newHashSet(user1); when(previousBuildUpstreamBuild.getCulprits()).thenReturn(badGuys1); User user2 = mock(User.class); when(user2.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("this.one.must.be.included@example.com")); Set<User> badGuys2 = Sets.newHashSet(user2); when(upstreamBuildBetweenPreviousAndCurrent.getCulprits()).thenReturn(badGuys2); User user3 = mock(User.class); when(user3.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("this.one.must.be.included.too@example.com")); Set<User> badGuys3 = Sets.newHashSet(user3); when(upstreamBuild.getCulprits()).thenReturn(badGuys3); AbstractBuild previousBuild = mock(AbstractBuild.class); when(previousBuild.getResult()).thenReturn(Result.SUCCESS); when(previousBuild.getUpstreamRelationshipBuild(upstreamProject)).thenReturn(previousBuildUpstreamBuild); when(previousBuild.toString()).thenReturn("previousBuild"); AbstractBuild build = mock(AbstractBuild.class); when(build.getResult()).thenReturn(Result.FAILURE); when(build.getUpstreamRelationshipBuild(upstreamProject)).thenReturn(upstreamBuild); when(build.toString()).thenReturn("build"); createPreviousNextRelationShip(previousBuild, build); BuildListener listener = mock(BuildListener.class); when(listener.getLogger()).thenReturn(System.out); Collection<AbstractProject> upstreamProjects = Collections.singleton(upstreamProject); MailSender sender = new MailSender("", false, false, "UTF-8", upstreamProjects); String emailList = sender.getCulpritsOfEmailList(upstreamProject, build, listener); assertFalse(emailList.contains("this.one.should.not.be.included@example.com")); assertTrue(emailList.contains("this.one.must.be.included@example.com")); assertTrue(emailList.contains("this.one.must.be.included.too@example.com")); } /** * Creates a previous/next relationship between the builds in the given order. */ private static void createPreviousNextRelationShip(AbstractBuild... builds) { int max = builds.length - 1; for (int i = 0; i < builds.length; i++) { if (i < max) { when(builds[i].getNextBuild()).thenReturn(builds[i+1]); } } for (int i = builds.length - 1; i >= 0; i--) { if (i >= 1) { when(builds[i].getPreviousBuild()).thenReturn(builds[i-1]); } } } @Issue("SECURITY-372") @Test public void forbiddenMail() throws Exception { final Jenkins jenkins = PowerMockito.mock(Jenkins.class); PowerMockito.when(jenkins.isUseSecurity()).thenReturn(true); PowerMockito.mockStatic(Jenkins.class); PowerMockito.doReturn(jenkins).when(Jenkins.class, "getActiveInstance"); ACL acl = mock(ACL.class); User authorizedU = mock(User.class); when(authorizedU.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("authorized@mycorp")); Authentication authorized = mock(Authentication.class); when(authorizedU.impersonate()).thenReturn(authorized); when(acl.hasPermission(authorized, Item.READ)).thenReturn(true); User unauthorizedU = mock(User.class); when(unauthorizedU.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("unauthorized@mycorp")); Authentication unauthorized = mock(Authentication.class); when(unauthorizedU.impersonate()).thenReturn(unauthorized); when(acl.hasPermission(unauthorized, Item.READ)).thenReturn(false); User externalU = mock(User.class); when(externalU.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty("someone@nowhere.net")); when(externalU.impersonate()).thenThrow(new UsernameNotFoundException("")); AbstractBuild<?, ?> build = mock(AbstractBuild.class); when(build.getCulprits()).thenReturn(Sets.newLinkedHashSet(Arrays.asList(authorizedU, unauthorizedU, externalU))); when(build.getACL()).thenReturn(acl); when(build.getFullDisplayName()).thenReturn("prj #1"); StringWriter sw = new StringWriter(); TaskListener listener = new StreamTaskListener(sw); assertEquals("authorized@mycorp", new MailSender("", false, true).getUserEmailList(listener, build)); listener.getLogger().flush(); assertThat(sw.toString(), containsString(Messages.MailSender_user_without_read("unauthorized@mycorp", "prj #1"))); assertThat(sw.toString(), containsString(Messages.MailSender_unknown_user("someone@nowhere.net"))); MailSender.SEND_TO_USERS_WITHOUT_READ = true; try { sw = new StringWriter(); listener = new StreamTaskListener(sw); assertEquals("authorized@mycorp,unauthorized@mycorp", new MailSender("", false, true).getUserEmailList(listener, build)); listener.getLogger().flush(); assertThat(sw.toString(), containsString(Messages.MailSender_warning_user_without_read("unauthorized@mycorp", "prj #1"))); assertThat(sw.toString(), containsString(Messages.MailSender_unknown_user("someone@nowhere.net"))); MailSender.SEND_TO_UNKNOWN_USERS = true; try { sw = new StringWriter(); listener = new StreamTaskListener(sw); assertEquals("authorized@mycorp,unauthorized@mycorp,someone@nowhere.net", new MailSender("", false, true).getUserEmailList(listener, build)); listener.getLogger().flush(); assertThat(sw.toString(), containsString(Messages.MailSender_warning_user_without_read("unauthorized@mycorp", "prj #1"))); assertThat(sw.toString(), containsString(Messages.MailSender_warning_unknown_user("someone@nowhere.net"))); } finally { MailSender.SEND_TO_UNKNOWN_USERS = false; } } finally { MailSender.SEND_TO_USERS_WITHOUT_READ = false; } } }