package hudson.plugins.collabnet.auth; import com.collabnet.ce.webservices.CTFList; import com.collabnet.ce.webservices.CTFProject; import com.collabnet.ce.webservices.CTFRole; import com.collabnet.ce.webservices.CollabNetApp; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlInput; import com.gargoylesoftware.htmlunit.html.HtmlPage; import hudson.model.FreeStyleProject; import hudson.plugins.collabnet.TestParam; import hudson.plugins.collabnet.auth.CNProjectACL.CollabNetRoles; import hudson.plugins.collabnet.util.Util; import hudson.plugins.promoted_builds.JobPropertyImpl; import hudson.plugins.promoted_builds.PromotionProcess; import hudson.plugins.promoted_builds.conditions.ManualCondition; import hudson.security.ACL; import org.acegisecurity.context.SecurityContextHolder; import java.rmi.RemoteException; /** * Test authorization for a Hudson job associated with a CN project. */ public class ProjectAuthTest extends AbstractSecurityTestCase { @TestParam private final String build_user = "hudsonBuild"; @TestParam private final String config_user = "hudsonConfig"; @TestParam private final String delete_user = "hudsonDelete"; @TestParam private final String promote_user = "hudsonPromote"; @Override protected void setUp() throws Exception { super.setUp(); setGlobalConnectionFactory(); installAuthorizationStrategy(); CollabNetApp cna = connect(); CTFProject p = cna.getProjectByTitle(teamforge_project); CTFList<CTFRole> existing = p.getRoles(); CollabNetRole promote = null; for (CollabNetRole role: CNProjectACL.CollabNetRoles.getAllRoles()) { if (existing.byTitle(role.getName())==null) p.createRole(role.getName(), role.getDescription()); if (role.getName().equals("Hudson Promote")) promote = role; } for (String name : new String[]{read_user,build_user,config_user,delete_user,promote_user}) { createUserIfNotExist(cna,name); p.addMember(name); } existing = p.getRoles(); grant(existing, CollabNetRoles.HUDSON_BUILD_ROLE, build_user); grant(existing, CollabNetRoles.HUDSON_CONFIGURE_ROLE, config_user); grant(existing, CollabNetRoles.HUDSON_DELETE_ROLE, delete_user); grant(existing, promote, promote_user); for (String name : new String[]{read_user,build_user,config_user,delete_user,promote_user}) { grant(existing,CollabNetRoles.HUDSON_READ_ROLE,name); } } private void grant(CTFList<CTFRole> existing, CollabNetRole role, String member) throws RemoteException { CTFRole r = existing.byTitle(role.getName()); if (r.getMembers().byTitle(member)==null) r.grant(member); } private void createUserIfNotExist(CollabNetApp cna, String user) throws Exception { if (cna.getUser(user)==null) createUser(cna,user); } public void testConfigRoundtrip() throws Exception { SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); // so that the test code can see everything assertRoundtrip(new CNAuthProjectProperty(teamforge_project, false, null, false)); assertRoundtrip(new CNAuthProjectProperty(teamforge_project, true, null, true)); } private void assertRoundtrip(CNAuthProjectProperty before) throws Exception { FreeStyleProject job = createFreeStyleProject(); job.addProperty(before); submit(createAdminWebClient().getPage(job,"configure").getFormByName("config")); CNAuthProjectProperty after = job.getProperty(CNAuthProjectProperty.class); assertEqualBeans(before,after,"project,createRoles,grantDefaultRoles"); } public void testReadUsersAccess() throws Exception { FreeStyleProject job = this.setupProjectForAuth(); WebClient logIn = createWebClient().login(read_user, read_user); logIn.getPage(job); Util.checkPageUnreachable(logIn, job.getShortUrl() + "configure"); Util.checkPageUnreachable(logIn, job.getShortUrl() + "build"); setupPromotionAndBuild(job); assert (!isBuildPromotable(logIn, job)); assert (!isProjectDeletable(logIn, job)); } public void testBuildUsersAccess() throws Exception { FreeStyleProject job = this.setupProjectForAuth(); WebClient logIn = new WebClient().login(build_user, build_user); logIn.goTo(job.getShortUrl()); Util.checkPageUnreachable(logIn, job.getShortUrl() + "configure"); buildAndAssertSuccess(job); setupPromotionAndBuild(job); assertFalse(isBuildPromotable(logIn, job)); assertFalse(isProjectDeletable(logIn, job)); } public void testPromoteUsersAccess() throws Exception { FreeStyleProject job = this.setupProjectForAuth(); WebClient logIn = new WebClient().login(promote_user, promote_user); logIn.goTo(job.getShortUrl()); Util.checkPageUnreachable(logIn, job.getShortUrl() + "configure"); Util.checkPageUnreachable(logIn, job.getShortUrl() + "build"); setupPromotionAndBuild(job); assert (isBuildPromotable(logIn, job)); assert (!isProjectDeletable(logIn, job)); } public void testConfigureUsersAccess() throws Exception { FreeStyleProject job = this.setupProjectForAuth(); WebClient logIn = new WebClient().login(config_user, config_user); logIn.goTo(job.getShortUrl()); logIn.goTo(job.getShortUrl() + "configure"); Util.checkPageUnreachable(logIn, job.getShortUrl() + "build"); setupPromotionAndBuild(job); assert (!isBuildPromotable(logIn, job)); assert (!isProjectDeletable(logIn, job)); } public void testDeleteUsersAccess() throws Exception { FreeStyleProject job = this.setupProjectForAuth(); WebClient logIn = new WebClient().login(delete_user, delete_user); logIn.goTo(job.getShortUrl()); Util.checkPageUnreachable(logIn, job.getShortUrl() + "configure"); Util.checkPageUnreachable(logIn, job.getShortUrl() + "build"); setupPromotionAndBuild(job); assert (!isBuildPromotable(logIn, job)); assert (isProjectDeletable(logIn, job)); } private boolean isProjectDeletable(WebClient logIn, FreeStyleProject job) throws Exception { boolean deletable = true; try { HtmlPage jobDeletePage = logIn.goTo(job.getShortUrl() + "delete"); this.submitDeleteForm(jobDeletePage); } catch (FailingHttpStatusCodeException fhsce) { deletable = false; } catch (FormNotFoundException fnfe) { deletable = false; } return deletable; } private boolean isBuildPromotable(WebClient logIn, FreeStyleProject job) throws Exception { boolean promotable = true; try { HtmlPage buildPromotePage = logIn. goTo(job.getShortUrl() + "lastBuild/promotion/"); this.submitPromoteForm(buildPromotePage); } catch (FailingHttpStatusCodeException fhsce) { promotable = false; } catch (FormNotFoundException fnfe) { promotable = false; } return promotable; } /** * Setup a new Hudson job to use authorization from the CN project. */ public FreeStyleProject setupProjectForAuth() throws Exception { FreeStyleProject job = this.createFreeStyleProject(); job.addProperty(new CNAuthProjectProperty(teamforge_project,false,null,false)); return job; } /** * Setup the Hudson job with a promotion and run one build (so that * promotion pages will show up). */ public void setupPromotionAndBuild(FreeStyleProject job) throws Exception { JobPropertyImpl prop = new JobPropertyImpl(job); job.addProperty(prop); PromotionProcess proc = prop.addProcess(PROMOTION_NAME); proc.conditions.add(new ManualCondition()); // wait til the build completes buildAndAssertSuccess(job); } /** * Handles submitting a form on a given page. */ public Page submitForm(HtmlPage page, String formName) throws Exception { HtmlForm form = page.getFormByName(formName); return this.submit(form); } /** * Handles submitting the Promote form. */ public Page submitPromoteForm(HtmlPage page) throws Exception { String action = "forcePromotion?name=" + PROMOTION_NAME; HtmlForm submitForm = Util.getFormWithAction(page, action); if (submitForm != null) { HtmlInput input = submitForm.getInputByValue("Force promotion"); return input.click(); } else { throw new FormNotFoundException("Promote form not found."); } } /** * Handles submitting the Delete form. */ public Page submitDeleteForm(HtmlPage page) throws Exception { HtmlForm submitForm = Util.getFormWithAction(page, "doDelete"); if (submitForm != null) { return this.submit(submitForm); } else { throw new FormNotFoundException("Form for delete " + " could not be found on page " + page); } } private class FormNotFoundException extends Exception { public FormNotFoundException(String msg) { super(msg); } } private static final String PROMOTION_NAME = "promote_name"; }