/* * The MIT License * * Copyright (c) 2016, CloudBees, Inc., Stephen Connolly. * * 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 com.cloudbees.plugins.credentials; import com.google.common.base.Predicate; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.BulkChange; import hudson.Extension; import hudson.ExtensionList; import hudson.Functions; import hudson.model.Describable; import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; import hudson.model.ManagementLink; import hudson.security.GlobalSecurityConfiguration; import hudson.util.FormApply; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import jenkins.model.GlobalConfigurationCategory; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.jenkins.ui.icon.IconSpec; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.interceptor.RequirePOST; /** * {@link ManagementLink} to expose the global credentials configuration screen. * * @see CredentialsProviderManager.Configuration * @see GlobalCredentialsConfiguration.Category * @since 2.0 */ @Extension(ordinal = Integer.MAX_VALUE - 211) public class GlobalCredentialsConfiguration extends ManagementLink implements Describable<GlobalCredentialsConfiguration> // TODO once context menu is Icon spec aware //, IconSpec { /** * Our logger. */ private static final Logger LOGGER = Logger.getLogger(GlobalSecurityConfiguration.class.getName()); /** * Our filter. */ public static final Predicate<GlobalConfigurationCategory> FILTER = new Predicate<GlobalConfigurationCategory>() { public boolean apply(GlobalConfigurationCategory input) { return input instanceof Category; } }; /** * {@inheritDoc} */ @Override public String getIconFileName() { return ExtensionList.lookup(CredentialsDescriptor.class).isEmpty() ? null : "/plugin/credentials/images/48x48/credentials.png"; } /** * {@inheritDoc} */ @Override public String getDisplayName() { return getDescriptor().getDisplayName(); } /** * {@inheritDoc} */ @Override public String getDescription() { return Messages.GlobalCredentialsConfiguration_Description(); } /** * {@inheritDoc} */ @Override public String getUrlName() { return "configureCredentials"; } // TODO uncomment once ContextMenu is IconSpec aware // /** // * {@inheritDoc} // */ // @Override // public String getIconClassName() { // return ExtensionList.lookup(CredentialsDescriptor.class).isEmpty() // ? null // : "icon-credentials-credentials"; // } /** * Handles the form submission * * @param req the request. * @return the response. * @throws IOException if something goes wrong. * @throws ServletException if something goes wrong. * @throws FormException if something goes wrong. */ @RequirePOST @NonNull @Restricted(NoExternalUse.class) @SuppressWarnings("unused") // stapler web method binding public synchronized HttpResponse doConfigure(@NonNull StaplerRequest req) throws IOException, ServletException, FormException { Jenkins jenkins = Jenkins.getActiveInstance(); jenkins.checkPermission(Jenkins.ADMINISTER); // logically this change starts from Jenkins BulkChange bc = new BulkChange(jenkins); try { boolean result = configure(req, req.getSubmittedForm()); LOGGER.log(Level.FINE, "credentials configuration saved: " + result); jenkins.save(); return FormApply .success(result ? req.getContextPath() + "/manage" : req.getContextPath() + "/" + getUrlName()); } finally { bc.commit(); } } /** * Performs the configuration. * * @param req the request. * @param json the JSON object. * @return {@code false} to keep the client in the same config page. * @throws FormException if something goes wrong. */ private boolean configure(StaplerRequest req, JSONObject json) throws FormException { // for compatibility reasons, the actual value is stored in Jenkins Jenkins j = Jenkins.getActiveInstance(); j.checkPermission(Jenkins.ADMINISTER); // persist all the provider configs boolean result = true; for (Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)) { result &= configureDescriptor(req, json, d); } return result; } /** * Performs the configuration of a specific {@link Descriptor}. * * @param req the request. * @param json the JSON object. * @param d the {@link Descriptor}. * @return {@code false} to keep the client in the same config page. * @throws FormException if something goes wrong. */ private boolean configureDescriptor(StaplerRequest req, JSONObject json, Descriptor<?> d) throws FormException { // collapse the structure to remain backward compatible with the JSON structure before 1. String name = d.getJsonSafeClassName(); JSONObject js = json.has(name) ? json.getJSONObject(name) : new JSONObject(); // if it doesn't have the property, the method returns invalid null object. json.putAll(js); return d.configure(req, js); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public Descriptor<GlobalCredentialsConfiguration> getDescriptor() { return Jenkins.getActiveInstance().getDescriptorOrDie(getClass()); } /** * Our {@link Descriptor}. */ @Extension public static final class DescriptorImpl extends Descriptor<GlobalCredentialsConfiguration> { /** * {@inheritDoc} */ @Override public String getDisplayName() { return Messages.GlobalCredentialsConfiguration_DisplayName(); } } /** * Security related configurations. */ @Extension public static class Category extends GlobalConfigurationCategory { /** * {@inheritDoc} */ @Override public String getShortDescription() { return Messages.GlobalCredentialsConfiguration_Description(); } /** * {@inheritDoc} */ @Override public String getDisplayName() { return Messages.GlobalCredentialsConfiguration_DisplayName(); } } }