package org.jenkinsci.plugins.rolestrategy; import hudson.Extension; import hudson.model.Failure; import hudson.model.Item; import hudson.security.Permission; import hudson.security.AuthorizationStrategy; import java.io.Serializable; import java.util.ArrayList; import java.util.Set; import java.util.SortedMap; import java.util.regex.Pattern; import jenkins.model.Jenkins; import jenkins.model.ProjectNamingStrategy; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import com.michelin.cio.hudson.plugins.rolestrategy.Messages; import com.michelin.cio.hudson.plugins.rolestrategy.Role; import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy; /** * @author Kanstantsin Shautsou * @since 2.2.0 */ /** * According to https://issues.jenkins-ci.org/browse/JENKINS-19934 * * If a user has a global role with "Job Create" then they can create any jobname. * If the user does not have such a global role, they cannot create any jobs. In fact, the "New View" link to create a job is not even displayed. * * */ public final class RoleBasedProjectNamingStrategy extends ProjectNamingStrategy implements Serializable { private static final long serialVersionUID = 1L; private final boolean forceExistingJobs; @DataBoundConstructor public RoleBasedProjectNamingStrategy(boolean forceExistingJobs) { this.forceExistingJobs = forceExistingJobs; Jenkins.getInstance().setProjectNamingStrategy(this); } @Override public void checkName(String name) throws Failure { boolean matches = false; ArrayList badList = new ArrayList(); AuthorizationStrategy auth = Jenkins.getInstance().getAuthorizationStrategy(); if (auth instanceof RoleBasedAuthorizationStrategy) { RoleBasedAuthorizationStrategy rbas = (RoleBasedAuthorizationStrategy) auth; // The current user String userName = Jenkins.getAuthentication().getName(); /* * In jenkins it seems there is a bug. If you only have the Job * Create Permission (and do not have the Global Create Permission), * you actually cannot create a job because the button 'New Item' * does not appear. So a user needs both to create a job. */ // Here we check if he has both permissions (Global and Project // Creation) // If he has both, it means we want him to respect a pattern if (hasGlobalCreationPermission(userName, Item.CREATE, rbas) && hasProjectCreationPermission(userName, Item.CREATE, rbas)) { final SortedMap> projectRoles = rbas.getGrantedRoles(RoleBasedAuthorizationStrategy.PROJECT); Role userRole = null; for (SortedMap.Entry> userPerRole : projectRoles.entrySet()) { userRole = userPerRole.getKey(); String namePattern = userRole.getPattern().toString(); if (StringUtils.isNotBlank(namePattern) && StringUtils.isNotBlank(name)) { if (Pattern.matches(namePattern, name)) { matches = true; } else { badList.add(namePattern); } } } // The user has only Global Project Permission, he does not need // to respect a pattern } else if (hasGlobalCreationPermission(userName, Item.CREATE, rbas)) { matches = true; } if (!matches) { String error; if (badList != null && !badList.isEmpty()) // TODO beatify long outputs? error = jenkins.model.Messages.Hudson_JobNameConventionNotApplyed(name, badList.toString()); else error = Messages.RoleBasedProjectNamingStrategy_NoPermissions(); throw new Failure(error); } } } private boolean hasGlobalCreationPermission(String userName, Permission permission, RoleBasedAuthorizationStrategy rbas) { return hasPermission(userName, permission, rbas.getGrantedRoles(RoleBasedAuthorizationStrategy.GLOBAL)); } private boolean hasProjectCreationPermission(String userName, Permission permission, RoleBasedAuthorizationStrategy rbas) { return hasPermission(userName, permission, rbas.getGrantedRoles(RoleBasedAuthorizationStrategy.PROJECT)); } private boolean hasPermission(String userName, Permission permission, SortedMap> roles) { Role role = null; for (SortedMap.Entry> userPerRole : roles.entrySet()) { role = userPerRole.getKey(); if (role.hasPermission(permission) && roles.get(role).contains(userName)) return true; } return false; } @Override public boolean isForceExistingJobs() { return forceExistingJobs; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) Jenkins.getInstance().getDescriptor(RoleBasedProjectNamingStrategy.class); } @Extension public static final class DescriptorImpl extends ProjectNamingStrategyDescriptor { @Override public String getDisplayName() { String name = Messages.RoleBasedAuthorizationStrategy_DisplayName(); if (!RoleBasedAuthorizationStrategy.isCreateAllowed()) name += " ((Require >1.565 core)"; return name; } } }