From f8d4ce0c468f7d181742e4e8e1a7e46c28e75da4 Mon Sep 17 00:00:00 2001 From: SofteqDG Date: Tue, 6 Mar 2018 13:02:20 +0300 Subject: [PATCH] Folder specific access rights --- .../plugins/ownership/OwnershipPlugin.java | 38 ++++- .../ownership/jobs/JobOwnerHelper.java | 13 ++ .../rolestrategy/ItemSpecificRoleMacro.java | 21 +-- .../util/AbstractOwnershipHelper.java | 9 +- .../model/folders/FolderOwnershipAction.java | 81 +++++++-- .../model/folders/FolderOwnershipHelper.java | 106 ++++++++---- .../folders/FolderOwnershipProperty.java | 154 ++++++++++++++---- .../FolderSpecificSecurity.java | 102 ++++++++++++ .../configure-project-specifics.jelly | 57 +++++++ .../folders/FolderOwnershipAction/index.jelly | 5 +- .../FolderSpecificSecurity/config.jelly | 9 + .../wrappers/OwnershipBuildWrapperTest.java | 6 +- .../test/util/OwnershipPluginConfigurer.java | 9 +- 13 files changed, 507 insertions(+), 103 deletions(-) mode change 100644 => 100755 src/main/java/com/synopsys/arc/jenkins/plugins/ownership/util/AbstractOwnershipHelper.java mode change 100644 => 100755 src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction.java mode change 100644 => 100755 src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipHelper.java mode change 100644 => 100755 src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipProperty.java create mode 100644 src/main/java/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity.java create mode 100644 src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/configure-project-specifics.jelly create mode 100644 src/main/resources/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity/config.jelly diff --git a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/OwnershipPlugin.java b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/OwnershipPlugin.java index 03aa55f..525644e 100644 --- a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/OwnershipPlugin.java +++ b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/OwnershipPlugin.java @@ -28,6 +28,7 @@ import com.synopsys.arc.jenkins.plugins.ownership.extensions.item_ownership_policy.AssignCreatorPolicy; import com.synopsys.arc.jenkins.plugins.ownership.extensions.item_ownership_policy.DropOwnershipPolicy; import com.synopsys.arc.jenkins.plugins.ownership.security.itemspecific.ItemSpecificSecurity; +import org.jenkinsci.plugins.ownership.security.folderspecific.FolderSpecificSecurity; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.ExtensionList; import hudson.Plugin; @@ -78,6 +79,7 @@ public class OwnershipPlugin extends Plugin { private final List pluginActions = new ArrayList<>(); public String mailResolverClassName; private ItemSpecificSecurity defaultJobsSecurity; + private FolderSpecificSecurity defaultFoldersSecurity; private OwnershipPluginConfiguration configuration; /** @@ -101,9 +103,9 @@ public static OwnershipPlugin getInstance() { @Override public void start() throws Exception { - load(); + load(); reinitActionsList(); - Jenkins.getActiveInstance().getActions().addAll(pluginActions); + Jenkins.getActiveInstance().getActions().addAll(pluginActions); } @Override @@ -140,6 +142,11 @@ public ItemSpecificSecurity getDefaultJobsSecurity() { return defaultJobsSecurity; } + @CheckForNull + public FolderSpecificSecurity getDefaultFoldersSecurity() { + return defaultFoldersSecurity; + } + public OwnershipPluginConfiguration getConfiguration() { return configuration; } @@ -153,6 +160,16 @@ public OwnershipPluginConfiguration getConfiguration() { public ItemSpecificSecurity.ItemSpecificDescriptor getItemSpecificDescriptor() { return ItemSpecificSecurity.DESCRIPTOR; } + + /** + * Gets descriptor of FolderSpecificProperty. + * Required for jelly. + * @return Descriptor + */ + @Nonnull + public FolderSpecificSecurity.FolderSpecificDescriptor getFolderSpecificDescriptor() { + return FolderSpecificSecurity.DESCRIPTOR; + } /** * {@link OwnershipPlugin} initialization for test suites. @@ -162,23 +179,26 @@ public ItemSpecificSecurity.ItemSpecificDescriptor getItemSpecificDescriptor() { * @param configuration * @throws IOException */ - public void configure(boolean requiresConfigureRights, String mailResolverClassName, - ItemSpecificSecurity defaultJobsSecurity, + public void configure(boolean requiresConfigureRights, + String mailResolverClassName, + ItemSpecificSecurity defaultJobsSecurity, + FolderSpecificSecurity defaultFoldersSecurity, OwnershipPluginConfiguration configuration) throws IOException { this.requiresConfigureRights = requiresConfigureRights; this.mailResolverClassName = mailResolverClassName; this.defaultJobsSecurity = defaultJobsSecurity; + this.defaultFoldersSecurity = defaultFoldersSecurity; this.configuration = configuration; reinitActionsList(); - save(); + save(); Jenkins.getActiveInstance().getActions().addAll(pluginActions); } @Override public void configure(StaplerRequest req, JSONObject formData) throws IOException, ServletException, Descriptor.FormException { - Jenkins.getActiveInstance().getActions().removeAll(pluginActions); + Jenkins.getActiveInstance().getActions().removeAll(pluginActions); requiresConfigureRights = formData.getBoolean("requiresConfigureRights"); // Configurations @@ -194,9 +214,13 @@ public void configure(StaplerRequest req, JSONObject formData) if (formData.containsKey("defaultJobsSecurity")) { this.defaultJobsSecurity = getItemSpecificDescriptor().newInstance(req, formData.getJSONObject("defaultJobsSecurity")); } + + if (formData.containsKey("defaultFoldersSecurity")) { + this.defaultFoldersSecurity = getFolderSpecificDescriptor().newInstance(req, formData.getJSONObject("defaultFoldersSecurity")); + } reinitActionsList(); - save(); + save(); Jenkins.getActiveInstance().getActions().addAll(pluginActions); } diff --git a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/jobs/JobOwnerHelper.java b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/jobs/JobOwnerHelper.java index 2ee65a4..4c878c9 100644 --- a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/jobs/JobOwnerHelper.java +++ b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/jobs/JobOwnerHelper.java @@ -38,6 +38,7 @@ import hudson.model.Job; import hudson.model.JobProperty; import hudson.model.User; +import hudson.security.Permission; import java.io.IOException; import java.util.Collection; import javax.annotation.CheckForNull; @@ -142,6 +143,18 @@ public OwnershipInfo getOwnershipInfo(Job job) { return OwnershipInfo.DISABLED_INFO; } + @Override + public boolean hasItemSpecificPermission(@Nonnull Job job, String sid, Permission p) { + JobOwnerJobProperty prop = getOwnerProperty(job); + if (prop != null) { + ItemSpecificSecurity sec = prop.getItemSpecificSecurity(); + if (sec != null) { + return sec.getPermissionsMatrix().hasPermission(sid, p); + } + } + return false; + } + /** * Sets the ownership information. * @param job A job to be modified diff --git a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/security/rolestrategy/ItemSpecificRoleMacro.java b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/security/rolestrategy/ItemSpecificRoleMacro.java index 3ba08af..7eb53bb 100644 --- a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/security/rolestrategy/ItemSpecificRoleMacro.java +++ b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/security/rolestrategy/ItemSpecificRoleMacro.java @@ -23,15 +23,13 @@ */ package com.synopsys.arc.jenkins.plugins.ownership.security.rolestrategy; +import org.jenkinsci.plugins.ownership.model.OwnershipHelperLocator; +import com.synopsys.arc.jenkins.plugins.ownership.util.AbstractOwnershipHelper; import com.synopsys.arc.jenkins.plugins.ownership.Messages; -import com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerHelper; -import com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerJobProperty; -import com.synopsys.arc.jenkins.plugins.ownership.security.itemspecific.ItemSpecificSecurity; import com.synopsys.arc.jenkins.plugins.rolestrategy.Macro; import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; -import hudson.model.Job; import hudson.security.AccessControlled; import hudson.security.Permission; @@ -61,19 +59,12 @@ public boolean IsApplicable(RoleType roleType) { @Override public boolean hasPermission(String sid, Permission p, RoleType type, AccessControlled item, Macro macro) { - if (type == RoleType.Project && item instanceof Job) { - Job prj = (Job)item; - JobOwnerJobProperty prop = JobOwnerHelper.getOwnerProperty(prj); - - if (prop != null) { - ItemSpecificSecurity sec = prop.getItemSpecificSecurity(); - if (sec != null) { - return sec.getPermissionsMatrix().hasPermission(sid, p); - } + if (type == RoleType.Project) { + AbstractOwnershipHelper helper = OwnershipHelperLocator.locate(item); + if (helper != null) { + return helper.hasItemSpecificPermission(item, sid, p); } } - return false; } - } diff --git a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/util/AbstractOwnershipHelper.java b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/util/AbstractOwnershipHelper.java old mode 100644 new mode 100755 index 6fc9903..3983889 --- a/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/util/AbstractOwnershipHelper.java +++ b/src/main/java/com/synopsys/arc/jenkins/plugins/ownership/util/AbstractOwnershipHelper.java @@ -27,6 +27,7 @@ import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription; import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin; import hudson.model.User; +import hudson.security.Permission; import java.util.Collection; import java.util.Collections; import javax.annotation.CheckForNull; @@ -98,7 +99,7 @@ public boolean isDisplayOwnershipSummaryBox(@Nonnull TObjectType item) { return true; } - + /** * Gets ownership info of the requested item. * @param item Item to be described @@ -108,7 +109,11 @@ public boolean isDisplayOwnershipSummaryBox(@Nonnull TObjectType item) { */ @Nonnull public abstract OwnershipInfo getOwnershipInfo(@Nonnull TObjectType item); - + + public boolean hasItemSpecificPermission(@Nonnull TObjectType item, String sid, Permission p) { + return false; + } + /** * Gets permission required to manage ownership for the item. * {@link Jenkins#ADMINISTER} by default if not overridden. diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction.java b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction.java old mode 100644 new mode 100755 index 0363ee8..9cc3c9e --- a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction.java +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction.java @@ -29,10 +29,12 @@ import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription; import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin; import com.synopsys.arc.jenkins.plugins.ownership.util.ui.OwnershipLayoutFormatter; +import org.jenkinsci.plugins.ownership.security.folderspecific.FolderSpecificSecurity; import hudson.model.Descriptor; import hudson.security.Permission; import java.io.IOException; import java.io.UnsupportedEncodingException; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.servlet.ServletException; import net.sf.json.JSONObject; @@ -48,14 +50,20 @@ */ public class FolderOwnershipAction extends ItemOwnershipAction> { - //TODO: May become a problem once we need to make it flexible (not implemented). Move to helper? - private static final OwnershipLayoutFormatter> DEFAULT_FOLDER_FORMATTER - = new OwnershipLayoutFormatter.DefaultJobFormatter<>(); - public FolderOwnershipAction(@Nonnull AbstractFolder folder) { super(folder); } + @Nonnull + @Override + public IOwnershipHelper> helper() { + return FolderOwnershipHelper.Instance; + } + + public OwnershipLayoutFormatter> getLayoutFormatter() { + return FolderOwnershipHelper.Instance.getLayoutFormatter(); + } + @Override public Permission getOwnerPermission() { return OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP; @@ -67,17 +75,41 @@ public Permission getProjectSpecificPermission() { } @Override - public boolean actionIsAvailable() { - return getDescribedItem().hasPermission(OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP); + public OwnershipDescription getOwnership() { + return FolderOwnershipHelper.Instance.getOwnershipDescription(getDescribedItem()); } - - @Override - public IOwnershipHelper> helper() { - return FolderOwnershipHelper.getInstance(); + + @CheckForNull + public FolderSpecificSecurity getItemSpecificSecurity() { + FolderOwnershipProperty prop = FolderOwnershipHelper.getOwnerProperty(getDescribedItem()); + if (prop != null && prop.hasItemSpecificSecurity()) { + return prop.getItemSpecificSecurity(); + } + return OwnershipPlugin.getInstance().getDefaultFoldersSecurity(); } - public OwnershipLayoutFormatter> getLayoutFormatter() { - return DEFAULT_FOLDER_FORMATTER; + /** + * Checks if the described item has a folder-specific security defined. + * @return true if the item has a folder-specific security + * @since 0.3.1 + */ + public boolean hasItemSpecificSecurity() { + FolderOwnershipProperty prop = FolderOwnershipHelper.getOwnerProperty(getDescribedItem()); + return prop != null && prop.hasItemSpecificSecurity(); + } + + /** + * Gets descriptor of folder-specific security page. + * This method is being used by UI. + * @return A descriptor of {@link FolderSpecificSecurity} + */ + public FolderSpecificSecurity.FolderSpecificDescriptor getItemSpecificDescriptor() { + return FolderSpecificSecurity.DESCRIPTOR; + } + + @Override + public boolean actionIsAvailable() { + return getDescribedItem().hasPermission(OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP); } public HttpResponse doOwnersSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, UnsupportedEncodingException, ServletException, Descriptor.FormException { @@ -89,4 +121,29 @@ public HttpResponse doOwnersSubmit(StaplerRequest req, StaplerResponse rsp) thro return HttpResponses.redirectViaContextPath(getDescribedItem().getUrl()); } + + public HttpResponse doProjectSpecificSecuritySubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, Descriptor.FormException { + getDescribedItem().checkPermission(OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP); + JSONObject form = req.getSubmittedForm(); + + if (form.containsKey("itemSpecificSecurity")) { + JSONObject jsonSpecificSecurity = req.getSubmittedForm().getJSONObject("itemSpecificSecurity"); + FolderSpecificSecurity specific = FolderSpecificSecurity.DESCRIPTOR.newInstance(req, jsonSpecificSecurity); + FolderOwnershipHelper.setProjectSpecificSecurity(getDescribedItem(), specific); + } else { // drop security + FolderOwnershipHelper.setProjectSpecificSecurity(getDescribedItem(), null); + } + + return HttpResponses.redirectViaContextPath(getDescribedItem().getUrl()); + } + + public HttpResponse doRestoreDefaultSpecificSecuritySubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, Descriptor.FormException { + getDescribedItem().checkPermission(OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP); + // Get default security + FolderSpecificSecurity defaultFoldersSecurity = OwnershipPlugin.getInstance().getDefaultFoldersSecurity(); + FolderSpecificSecurity val = defaultFoldersSecurity != null ? defaultFoldersSecurity.clone() : null; + + FolderOwnershipHelper.setProjectSpecificSecurity(getDescribedItem(), val); + return HttpResponses.redirectViaContextPath(getDescribedItem().getUrl()); + } } diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipHelper.java b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipHelper.java old mode 100644 new mode 100755 index 8908894..da7e7a5 --- a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipHelper.java +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipHelper.java @@ -27,14 +27,17 @@ import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription; import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin; import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPluginConfiguration; +import org.jenkinsci.plugins.ownership.security.folderspecific.FolderSpecificSecurity; import com.synopsys.arc.jenkins.plugins.ownership.util.AbstractOwnershipHelper; import com.synopsys.arc.jenkins.plugins.ownership.util.UserCollectionFilter; import com.synopsys.arc.jenkins.plugins.ownership.util.userFilters.AccessRightsFilter; import com.synopsys.arc.jenkins.plugins.ownership.util.userFilters.IUserFilter; +import com.synopsys.arc.jenkins.plugins.ownership.util.ui.OwnershipLayoutFormatter; import hudson.Extension; import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.User; +import hudson.security.Permission; import java.io.IOException; import java.util.Collection; import javax.annotation.CheckForNull; @@ -53,11 +56,19 @@ */ public class FolderOwnershipHelper extends AbstractOwnershipHelper> { - static final FolderOwnershipHelper INSTANCE = new FolderOwnershipHelper(); + static final FolderOwnershipHelper Instance + = new FolderOwnershipHelper(); + + private static final OwnershipLayoutFormatter> DEFAULT_FOLDER_FORMATTER + = new OwnershipLayoutFormatter.DefaultJobFormatter<>(); @Nonnull public static FolderOwnershipHelper getInstance() { - return INSTANCE; + return Instance; + } + + public OwnershipLayoutFormatter> getLayoutFormatter() { + return DEFAULT_FOLDER_FORMATTER; } /** @@ -71,19 +82,13 @@ public static FolderOwnershipProperty getOwnerProperty(@Nonnull AbstractFolder item) { - return "folder"; + public static boolean isUserExists(@Nonnull User user) { + assert (user != null); + return isUserExists(user.getId()); } - - @Override - public String getItemDisplayName(AbstractFolder item) { - return item.getDisplayName(); - } - - @Override - public String getItemURL(AbstractFolder item) { - return item.getUrl(); + + public static boolean isUserExists(@Nonnull String userIdOrFullName) { + return User.getById(userIdOrFullName, false) != null; } @Override @@ -91,18 +96,18 @@ public OwnershipDescription getOwnershipDescription(AbstractFolder item) { // TODO: Maybe makes sense to unwrap the method to get a better performance (esp. for Security) return getOwnershipInfo(item).getDescription(); } - + @Nonnull @Override public Permission getRequiredPermission() { return OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP; } - + @Override public boolean hasLocallyDefinedOwnership(@Nonnull AbstractFolder folder) { return getOwnerProperty(folder) != null; } - + @Override public OwnershipInfo getOwnershipInfo(AbstractFolder item) { if (item == null) { // Handle renames, etc. @@ -114,7 +119,7 @@ public OwnershipInfo getOwnershipInfo(AbstractFolder item) { if (prop != null) { OwnershipDescription d = prop.getOwnership(); if (d.isOwnershipEnabled()) { - return new OwnershipInfo(prop.getOwnership(), new FolderOwnershipDescriptionSource(item)); + return new OwnershipInfo(d, new FolderOwnershipDescriptionSource(item)); } } @@ -141,32 +146,75 @@ public OwnershipInfo getOwnershipInfo(AbstractFolder item) { } @Override - public Collection getPossibleOwners(AbstractFolder item) { - if (OwnershipPlugin.getInstance().isRequiresConfigureRights()) { - IUserFilter filter = new AccessRightsFilter(item, AbstractFolder.CONFIGURE); - return UserCollectionFilter.filterUsers(User.getAll(), true, filter); - } else { - return User.getAll(); + public boolean hasItemSpecificPermission(@Nonnull AbstractFolder folder, String sid, Permission p) { + FolderOwnershipProperty prop = getOwnerProperty(folder); + if (prop != null) { + FolderSpecificSecurity sec = prop.getItemSpecificSecurity(); + if (sec != null) { + return sec.getPermissionsMatrix().hasPermission(sid, p); + } } + return false; } /** * Sets the ownership information. * @param folder Folder to be modified * @param descr A description to be set. Use null to drop settings. - * @throws IOException + * @throws IOException */ - public static void setOwnership(@Nonnull AbstractFolder folder, - @CheckForNull OwnershipDescription descr) throws IOException { + public static void setOwnership(@Nonnull AbstractFolder folder, + @CheckForNull OwnershipDescription descr) throws IOException { FolderOwnershipProperty prop = getOwnerProperty(folder); if (prop == null) { - prop = new FolderOwnershipProperty(descr); + prop = new FolderOwnershipProperty(descr, null); folder.addProperty(prop); } else { prop.setOwnershipDescription(descr); } } + /** + * Sets the project-specific security. + * @param job A job to be modified + * @param security Security settings to be set. Use null to drop settings + * @throws IOException + */ + public static void setProjectSpecificSecurity(@Nonnull AbstractFolder folder, + @CheckForNull FolderSpecificSecurity security) throws IOException { + FolderOwnershipProperty prop = getOwnerProperty(folder); + if (prop == null) { + throw new IOException("Ownership is not configured for "+folder); + } else { + prop.setItemSpecificSecurity(security); + } + } + + @Override + public Collection getPossibleOwners(AbstractFolder item) { + if (OwnershipPlugin.getInstance().isRequiresConfigureRights()) { + IUserFilter filter = new AccessRightsFilter(item, AbstractFolder.CONFIGURE); + return UserCollectionFilter.filterUsers(User.getAll(), true, filter); + } else { + return User.getAll(); + } + } + + @Override + public String getItemTypeName(AbstractFolder item) { + return "folder"; + } + + @Override + public String getItemDisplayName(AbstractFolder item) { + return item.getDisplayName(); + } + + @Override + public String getItemURL(AbstractFolder item) { + return item.getUrl(); + } + @Extension(optional = true) @Restricted(NoExternalUse.class) public static class LocatorImpl extends OwnershipHelperLocator> { @@ -174,7 +222,7 @@ public static class LocatorImpl extends OwnershipHelperLocator @Override public AbstractOwnershipHelper> findHelper(Object item) { if (item instanceof AbstractFolder) { - return INSTANCE; + return Instance; } return null; } diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipProperty.java b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipProperty.java old mode 100644 new mode 100755 index 05f1bb3..7b49e64 --- a/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipProperty.java +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipProperty.java @@ -23,39 +23,103 @@ */ package org.jenkinsci.plugins.ownership.model.folders; -import com.cloudbees.hudson.plugins.folder.AbstractFolder; -import com.cloudbees.hudson.plugins.folder.AbstractFolderProperty; -import com.cloudbees.hudson.plugins.folder.AbstractFolderPropertyDescriptor; import com.synopsys.arc.jenkins.plugins.ownership.IOwnershipHelper; import com.synopsys.arc.jenkins.plugins.ownership.IOwnershipItem; +import com.synopsys.arc.jenkins.plugins.ownership.Messages; import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin; +import org.jenkinsci.plugins.ownership.security.folderspecific.FolderSpecificSecurity; +import com.synopsys.arc.jenkins.plugins.ownership.util.ui.OwnershipLayoutFormatter; +import com.synopsys.arc.jenkins.plugins.ownership.util.UserCollectionFilter; +import com.synopsys.arc.jenkins.plugins.ownership.util.userFilters.AccessRightsFilter; +import com.synopsys.arc.jenkins.plugins.ownership.util.userFilters.IUserFilter; +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + import hudson.Extension; import hudson.model.Descriptor; +import hudson.model.Items; +import com.cloudbees.hudson.plugins.folder.AbstractFolder; +import com.cloudbees.hudson.plugins.folder.AbstractFolderProperty; +import com.cloudbees.hudson.plugins.folder.AbstractFolderPropertyDescriptor; +import hudson.model.User; +import hudson.util.XStream2; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; import javax.annotation.CheckForNull; -import net.sf.json.JSONObject; -import org.kohsuke.stapler.StaplerRequest; +import javax.servlet.ServletException; +import org.kohsuke.stapler.StaplerResponse; /** * Ownership property for {@link AbstractFolder}s. * @author Oleg Nenashev * @since 0.9 */ -public class FolderOwnershipProperty - extends AbstractFolderProperty> - implements IOwnershipItem>{ - +public class FolderOwnershipProperty extends AbstractFolderProperty> + implements IOwnershipItem> +{ @CheckForNull OwnershipDescription ownership; - - public FolderOwnershipProperty(@CheckForNull OwnershipDescription ownership) { + + /** + * Additional matrix with project security + */ + @CheckForNull + FolderSpecificSecurity itemSpecificSecurity; + + @DataBoundConstructor + public FolderOwnershipProperty(OwnershipDescription ownership, FolderSpecificSecurity security) { this.ownership = ownership; + this.itemSpecificSecurity = security; } + @Override + public OwnershipDescription getOwnership() { + return (ownership!=null) ? ownership : OwnershipDescription.DISABLED_DESCR; + } + + /** + * Gets current configuration of item-specific security. + * The function returns a default configuration if security is not + * configured. Use {@link #hasItemSpecificSecurity() hasItemSpecificSecurity} + * to check an origin of permissions. + * @return ItemSpecific security or {@code null} if it is not configured + * @since 0.3 + */ + @CheckForNull + public FolderSpecificSecurity getItemSpecificSecurity() { + return itemSpecificSecurity != null ? itemSpecificSecurity : OwnershipPlugin.getInstance().getDefaultFoldersSecurity(); + } + + /** + * Checks if job-specific security is configured. + * @return true if job-specific security is configured + * @since 0.3.1 + */ + public boolean hasItemSpecificSecurity() { + return itemSpecificSecurity != null; + } + + public String getDisplayName(User usr) { + return FolderOwnershipHelper.Instance.getDisplayName(usr); + } + + public Collection getUsers() + { + //TODO: Sort users + IUserFilter filter = new AccessRightsFilter(owner, AbstractFolder.CONFIGURE); + Collection res = UserCollectionFilter.filterUsers(User.getAll(), true, filter); + return res; + } + @Override public IOwnershipHelper> helper() { - return FolderOwnershipHelper.getInstance(); + return FolderOwnershipHelper.Instance; } @Override @@ -64,35 +128,59 @@ public AbstractFolder getDescribedItem() { } @Override - public OwnershipDescription getOwnership() { - return ownership != null ? ownership : OwnershipDescription.DISABLED_DESCR; + public AbstractFolderProperty reconfigure(StaplerRequest req, JSONObject form) throws Descriptor.FormException { + return new FolderOwnershipProperty(ownership, itemSpecificSecurity); } - /** - * Sets the new ownership description. - * @param description Description to be set. Use {@code null} to drop settings. - * @throws IOException Property cannot be saved. - */ - public void setOwnershipDescription(@CheckForNull OwnershipDescription description) throws IOException { - ownership = description; - owner.save(); - } - - @Override - public AbstractFolderProperty reconfigure(StaplerRequest req, JSONObject form) throws Descriptor.FormException { - // Retain the current configuration in order to prevent changes by form submissions - return new FolderOwnershipProperty(ownership); + public OwnershipLayoutFormatter> getLayoutFormatter() { + return FolderOwnershipHelper.Instance.getLayoutFormatter(); } @Extension(optional = true) public static class DescriptorImpl extends AbstractFolderPropertyDescriptor { - @Override - @SuppressFBWarnings(value = "NP_NONNULL_RETURN_VIOLATION", justification = "TODO: should be fixed, see jenkinsci PR #1880") public String getDisplayName() { - // It prevents the property from displaying - return null; + return Messages.JobOwnership_Config_SectionTitle(); } + @Override + public boolean isApplicable(Class jobType) { + return true; + } + } + + @Override + public String toString() { + StringBuilder bldr = new StringBuilder(); + bldr.append(ownership != null ? ownership.toString() : "ownership not set"); + bldr.append(" "); + bldr.append(itemSpecificSecurity != null ? "with specific permissions" : "without specific permissions"); + return bldr.toString(); + } + + public void doOwnersSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, UnsupportedEncodingException, ServletException, Descriptor.FormException { + JSONObject formData = req.getSubmittedForm(); + JSONObject jsonOwnership = formData.getJSONObject("owners"); + setOwnershipDescription(OwnershipDescription.parseJSON(jsonOwnership)); + } + + public void setOwnershipDescription(@CheckForNull OwnershipDescription descr) throws IOException { + ownership = descr; + owner.save(); + } + + public void setItemSpecificSecurity(@CheckForNull FolderSpecificSecurity security) throws IOException { + itemSpecificSecurity = security; + owner.save(); + } + + static { + // TODO: Remove reflection once baseline is updated past 2.85. + try { + Method m = XStream2.class.getMethod("addCriticalField", Class.class, String.class); + m.invoke(Items.XSTREAM2, FolderOwnershipProperty.class, "ownership"); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } } } diff --git a/src/main/java/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity.java b/src/main/java/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity.java new file mode 100644 index 0000000..199c95e --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity.java @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Copyright 2013 Oleg Nenashev, Synopsys 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 org.jenkinsci.plugins.ownership.security.folderspecific; + +import com.cloudbees.hudson.plugins.folder.properties.AuthorizationMatrixProperty; +import hudson.security.Permission; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.Extension; +import java.util.Set; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import jenkins.model.Jenkins; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +/** + * Implements folder-specific property map. + * This class relies on {@link AuthorizationMatrixProperty} from Jenkins core. + * @author Oleg Nenashev + * @since 0.3 + */ +public class FolderSpecificSecurity implements Describable, Cloneable { + + private @Nonnull AuthorizationMatrixProperty permissionsMatrix; + + @DataBoundConstructor + public FolderSpecificSecurity(@CheckForNull AuthorizationMatrixProperty permissionsMatrix) { + this.permissionsMatrix = permissionsMatrix != null + ? permissionsMatrix + : new AuthorizationMatrixProperty(new TreeMap>()); + } + + @Override + public FolderSpecificDescriptor getDescriptor() { + return DESCRIPTOR; + } + + @Nonnull + public AuthorizationMatrixProperty getPermissionsMatrix() { + return permissionsMatrix; + } + + @Override + public FolderSpecificSecurity clone() { + FolderSpecificSecurity newItem; + try { + newItem = (FolderSpecificSecurity)super.clone(); + newItem.permissionsMatrix = new AuthorizationMatrixProperty(this.permissionsMatrix.getGrantedPermissions()); + return newItem; + } catch (CloneNotSupportedException ex) { + Logger.getLogger(FolderSpecificSecurity.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + public static final FolderSpecificDescriptor DESCRIPTOR = new FolderSpecificDescriptor(); + + @Extension + public static class FolderSpecificDescriptor extends Descriptor { + @Override + public String getDisplayName() { + return "Folder-specific security"; + } + + @Override + public FolderSpecificSecurity newInstance(StaplerRequest req, JSONObject formData) throws FormException { + AuthorizationMatrixProperty prop = null; + if (formData.containsKey("permissionsMatrix")) { + Descriptor d= Jenkins.getActiveInstance().getDescriptor(AuthorizationMatrixProperty.class); + prop = (AuthorizationMatrixProperty)d.newInstance(req, formData.getJSONObject("permissionsMatrix")); + } + return new FolderSpecificSecurity(prop); + } + } +} diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/configure-project-specifics.jelly b/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/configure-project-specifics.jelly new file mode 100644 index 0000000..4fcf095 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/configure-project-specifics.jelly @@ -0,0 +1,57 @@ + + + + + + +

+ + ${%Manage item-specific access rights} +

+ + + + + + + + + + + + + + + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/index.jelly b/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/index.jelly index c1fdf62..176f60c 100644 --- a/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/index.jelly +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/folders/FolderOwnershipAction/index.jelly @@ -49,7 +49,10 @@ ${%Manage primary and secondary owners} - + + + ${%Configure job-specific access rights}. + diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity/config.jelly b/src/main/resources/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity/config.jelly new file mode 100644 index 0000000..e94d169 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/security/folderspecific/FolderSpecificSecurity/config.jelly @@ -0,0 +1,9 @@ + + + + + +
+
+
\ No newline at end of file diff --git a/src/test/java/com/synopsys/arc/jenkins/plugins/ownership/wrappers/OwnershipBuildWrapperTest.java b/src/test/java/com/synopsys/arc/jenkins/plugins/ownership/wrappers/OwnershipBuildWrapperTest.java index d39b850..fe3768e 100644 --- a/src/test/java/com/synopsys/arc/jenkins/plugins/ownership/wrappers/OwnershipBuildWrapperTest.java +++ b/src/test/java/com/synopsys/arc/jenkins/plugins/ownership/wrappers/OwnershipBuildWrapperTest.java @@ -84,7 +84,7 @@ public void initJenkinsInstance() throws Exception { // Configure ownership plugin r.jenkins.getPlugin(OwnershipPlugin.class).configure( - true, null, null, new OwnershipPluginConfiguration(new AssignCreatorPolicy())); + true, null, null, null, new OwnershipPluginConfiguration(new AssignCreatorPolicy())); // Create node with ownership node = r.createOnlineSlave(); @@ -118,7 +118,7 @@ public void initJenkinsInstance() throws Exception { final OwnershipPluginConfiguration pluginConf = new OwnershipPluginConfiguration( new AssignCreatorPolicy(),MailOptions.DEFAULT, new EnvSetupOptions(true, true)); - r.jenkins.getPlugin(OwnershipPlugin.class).configure(true, null, null, pluginConf); + r.jenkins.getPlugin(OwnershipPlugin.class).configure(true, null, null, null, pluginConf); testVarsPresense(true); } @@ -128,7 +128,7 @@ public void initJenkinsInstance() throws Exception { final OwnershipPluginConfiguration pluginConf = new OwnershipPluginConfiguration( new AssignCreatorPolicy(),MailOptions.DEFAULT, new EnvSetupOptions(true, true)); - r.jenkins.getPlugin(OwnershipPlugin.class).configure(true, null, null, pluginConf); + r.jenkins.getPlugin(OwnershipPlugin.class).configure(true, null, null, null, pluginConf); FreeStyleBuild build = testVarsPresense(false); r.assertLogContains("NODE_COOWNERS="+NODE_OWNER_ID, build); diff --git a/src/test/java/org/jenkinsci/plugins/ownership/test/util/OwnershipPluginConfigurer.java b/src/test/java/org/jenkinsci/plugins/ownership/test/util/OwnershipPluginConfigurer.java index edafc49..7fc26d0 100644 --- a/src/test/java/org/jenkinsci/plugins/ownership/test/util/OwnershipPluginConfigurer.java +++ b/src/test/java/org/jenkinsci/plugins/ownership/test/util/OwnershipPluginConfigurer.java @@ -28,6 +28,7 @@ import com.synopsys.arc.jenkins.plugins.ownership.extensions.ItemOwnershipPolicy; import com.synopsys.arc.jenkins.plugins.ownership.extensions.item_ownership_policy.DropOwnershipPolicy; import com.synopsys.arc.jenkins.plugins.ownership.security.itemspecific.ItemSpecificSecurity; +import org.jenkinsci.plugins.ownership.security.folderspecific.FolderSpecificSecurity; import java.io.IOException; import javax.annotation.Nonnull; import jenkins.model.Jenkins; @@ -48,6 +49,7 @@ public class OwnershipPluginConfigurer { private boolean requiresConfigurePermissions; private String mailResolverClassName; private ItemSpecificSecurity defaultJobsSecurity; + private FolderSpecificSecurity defaultFoldersSecurity; private ItemOwnershipPolicy itemOwnershipPolicy; private MailOptions mailOptions; private DisplayOptions displayOptions; @@ -82,6 +84,11 @@ public OwnershipPluginConfigurer withDefaultJobsSecurity(ItemSpecificSecurity de this.defaultJobsSecurity = defaultJobsSecurity; return this; } + + public OwnershipPluginConfigurer withDefaultFoldersSecurity(FolderSpecificSecurity defaultFoldersSecurity) { + this.defaultFoldersSecurity = defaultFoldersSecurity; + return this; + } public OwnershipPluginConfigurer withItemOwnershipPolicy(@Nonnull ItemOwnershipPolicy itemOwnershipPolicy) { this.itemOwnershipPolicy = itemOwnershipPolicy; @@ -112,6 +119,6 @@ public void configure() throws IOException { OwnershipPluginConfiguration conf = new OwnershipPluginConfiguration (itemOwnershipPolicy, mailOptions, globalEnvSetupOptions, displayOptions, inheritanceOptions); jenkins.getPlugin(OwnershipPlugin.class).configure - (requiresConfigurePermissions, mailResolverClassName, defaultJobsSecurity, conf); + (requiresConfigurePermissions, mailResolverClassName, defaultJobsSecurity, defaultFoldersSecurity, conf); } }