com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel.java Source code

Java tutorial

Introduction

Here is the source code for com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel.java

Source

/*
 * The MIT License
 *
 * Copyright (c) 2011-2013, 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.common;

import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.util.ListBoxModel;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.apache.commons.lang.StringUtils;

/**
 * {@link ListBoxModel} with support for credentials.
 * <p>
 * This class is convenient for providing the {@code config.groovy} or {@code config.jelly} fragment for a collection
 * of objects of some {@link IdCredentials} subtype.
 * </p><p>
 * If you want to let the user configure a credentials object, do the following:
 * </p><p>
 * First, create a field that stores the credentials ID and defines a corresponding parameter in the constructor:
 * </p>
 * <pre>
 * private String credentialsId;
 *
 * &#64;DataBoundConstructor
 * public MyModel( .... , String credentialsId) {
 *     this.credentialsId = credentialsId;
 *     ...
 * }
 * public String getCredentialsId() {return credentialsId;}
 * </pre>
 * <p>
 * Your <tt>config.groovy</tt> should have the following entry to render a drop-down list box:
 * </p>
 * <pre>
 * f.entry(title:_("Credentials"), field:"credentialsId") {
 *     f.select()
 * }
 * </pre>
 * <p>
 * Finally, your {@link Descriptor} implementation should have the <tt>doFillCredentialsIdItems</tt> method, which
 * lists up the credentials available in this context:
 * </p>
 * <pre>
 * public ListBoxModel doFillCredentialsIdItems(&#64;QueryParam String value) {
 *     if (!Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) { // or whatever permission is appropriate for
 *     this page
 *         // Important! Otherwise you expose credentials metadata to random web requests.
 *         return new StandardUsernameListBoxModel().includeCurrentValue(value);
 *     }
 *     return new StandardUsernameListBoxModel()
 *             .includeEmptySelection()
 *             .include(StandardUsernameCredentials.class,...))
 *             .includeCurrentValue(value);
 * }
 * </pre>
 * <p>
 * Exactly which overloaded version of the {@link #include(Item, Class)} depends on
 * the context in which your model operates. Here are a few common examples:
 * </p>
 * <dl>
 * <dt>System-level settings
 * <dd>
 * If your model is a singleton in the whole Jenkins instance, things that belong to the root {@link Jenkins}
 * (such as slaves), or do not have any ancestors serving as the context, then use {@link Jenkins#getInstance} as the
 * context.
 * <dt>Job-level settings
 * <dd>
 * If your model is a configuration fragment added to a {@link Item} (such as its major subtype {@link Job}),
 * then use that {@link Item} as the context.
 * For example:
 * <pre>
 * public ListBoxModel doFillCredentialsIdItems(&#64;AncestorInPath Item context, &#64;QueryParameter String source) {
 *     if (context == null || !context.hasPermission(Item.CONFIGURE)) {
 *         return new StandardUsernameListBoxModel().includeCurrentValue(value);
 *     }
 *     return new StandardUsernameListBoxModel()
 *             .includeEmptySelection()
 *             .includeAs(Tasks.getAuthenticationOf(context), context, StandardUsernameCredentials.class,
 *                 URIRequirementBuilder.fromUri(source).build())
 *             .includeCurrentValue(value);
 * }
 * </pre>
 * </dl>
 *
 * @since 1.6
 */
public abstract class AbstractIdCredentialsListBoxModel<T extends AbstractIdCredentialsListBoxModel<T, C>, C extends IdCredentials>
        extends ListBoxModel {

    /**
     * Generate a description of the supplied credential.
     *
     * @param c the credential.
     * @return the description.
     */
    @NonNull
    protected abstract String describe(@NonNull C c);

    /**
     * Adds a single credential.
     *
     * @param u the credential to add.
     * @return {@code this} for method chaining.
     */
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> with(@CheckForNull C u) {
        if (u != null) {
            add(describe(u), u.getId());
        }
        return this;
    }

    /**
     * Adds an "empty" credential to signify selection of no credential.
     *
     * @return {@code this} for method chaining.
     * @deprecated use {@link #includeEmptyValue()}
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withEmptySelection() {
        return includeEmptyValue();
    }

    /**
     * Adds an "empty" credential to signify selection of no credential.
     *
     * @return {@code this} for method chaining.
     * @since 2.1.0
     */
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> includeEmptyValue() {
        for (Option a : this) {
            if (StringUtils.equals("", a.value)) {
                return this;
            }
        }
        add(0, new Option(Messages.AbstractIdCredentialsListBoxModel_EmptySelection(), ""));
        return this;
    }

    /**
     * Adds supplied credentials to the model.
     *
     * @param credentials the credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #include(Item, Class)} or {@link #includeAs(Authentication, Item, Class)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withAll(@NonNull C... credentials) {
        return withMatching(CredentialsMatchers.always(), Arrays.asList(credentials));
    }

    /**
     * Adds supplied credentials to the model.
     *
     * @param credentials the credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #include(Item, Class)} or {@link #includeAs(Authentication, Item, Class)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withAll(@NonNull Iterable<? extends C> credentials) {
        return withMatching(CredentialsMatchers.always(), credentials.iterator());
    }

    /**
     * Adds supplied credentials to the model.
     *
     * @param credentials the credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #include(Item, Class)} or {@link #includeAs(Authentication, Item, Class)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withAll(@NonNull Iterator<? extends C> credentials) {
        return withMatching(CredentialsMatchers.always(), credentials);
    }

    /**
     * Adds the matching subset of suppled credentials to the model.
     *
     * @param matcher     the matcher.
     * @param credentials the superset of credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #includeMatching(Item, Class, List, CredentialsMatcher)}
     * or {@link #includeMatchingAs(Authentication, Item, Class, List, CredentialsMatcher)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withMatching(@NonNull CredentialsMatcher matcher,
            @NonNull C... credentials) {
        return withMatching(matcher, Arrays.asList(credentials));
    }

    /**
     * Adds the matching subset of suppled credentials to the model.
     *
     * @param matcher     the matcher.
     * @param credentials the superset of credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #includeMatching(Item, Class, List, CredentialsMatcher)}
     * or {@link #includeMatchingAs(Authentication, Item, Class, List, CredentialsMatcher)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withMatching(@NonNull CredentialsMatcher matcher,
            @NonNull Iterable<? extends C> credentials) {
        return withMatching(matcher, credentials.iterator());
    }

    /**
     * Adds the matching subset of suppled credentials to the model.
     *
     * @param matcher     the matcher.
     * @param credentials the superset of credentials.
     * @return {@code this} for method chaining.
     * @deprecated prefer using the {@link #includeMatching(Item, Class, List, CredentialsMatcher)}
     * or {@link #includeMatchingAs(Authentication, Item, Class, List, CredentialsMatcher)}
     * methods to build the list box contents in order to allow credentials providers to not have to instantiate
     * a full credential instance where those credential providers store the secrets external from Jenkins.
     */
    @Deprecated
    @NonNull
    public AbstractIdCredentialsListBoxModel<T, C> withMatching(@NonNull CredentialsMatcher matcher,
            @NonNull Iterator<? extends C> credentials) {
        while (credentials.hasNext()) {
            C c = credentials.next();
            if (matcher.matches(c)) {
                with(c);
            }
        }
        return this;
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication.
     *
     * @param context the context to add credentials from.
     * @param type    the base class of the credentials to add.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> include(@Nullable Item context,
            @NonNull Class<? extends C> type) {
        return include(context, type, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication.
     *
     * @param context the context to add credentials from.
     * @param type    the base class of the credentials to add.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> include(@NonNull ItemGroup context,
            @NonNull Class<? extends C> type) {
        return include(context, type, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication.
     *
     * @param authentication the authentication to search with
     * @param context        the context to add credentials from.
     * @param type           the base class of the credentials to add.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeAs(@NonNull Authentication authentication,
            @Nullable Item context, @NonNull Class<? extends C> type) {
        return includeAs(authentication, context, type, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication.
     *
     * @param authentication the authentication to search with
     * @param context        the context to add credentials from.
     * @param type           the base class of the credentials to add.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeAs(@NonNull Authentication authentication,
            @NonNull ItemGroup context, @NonNull Class<? extends C> type) {
        return includeAs(authentication, context, type, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication with the specified domain requirements.
     *
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> include(@Nullable Item context, @NonNull Class<? extends C> type,
            @NonNull List<DomainRequirement> domainRequirements) {
        return includeMatching(context, type, domainRequirements, CredentialsMatchers.always());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication with the specified domain requirements.
     *
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> include(@NonNull ItemGroup context,
            @NonNull Class<? extends C> type, @NonNull List<DomainRequirement> domainRequirements) {
        return includeMatching(context, type, domainRequirements, CredentialsMatchers.always());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication with the specified domain requirements.
     *
     * @param authentication     the authentication to search with
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeAs(@NonNull Authentication authentication,
            @Nullable Item context, @NonNull Class<? extends C> type,
            @NonNull List<DomainRequirement> domainRequirements) {
        return includeMatchingAs(authentication, context, type, domainRequirements, CredentialsMatchers.always());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication with the specified domain requirements.
     *
     * @param authentication     the authentication to search with
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeAs(@NonNull Authentication authentication,
            @NonNull ItemGroup context, @NonNull Class<? extends C> type,
            @NonNull List<DomainRequirement> domainRequirements) {
        return includeMatchingAs(authentication, context, type, domainRequirements, CredentialsMatchers.always());
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication with the specified domain requirements and match the specified filter.
     *
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @param matcher            the filter to apply to the credentials.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeMatching(@Nullable Item context,
            @NonNull Class<? extends C> type, @NonNull List<DomainRequirement> domainRequirements,
            @NonNull CredentialsMatcher matcher) {
        return includeMatchingAs(Jenkins.getAuthentication(), context, type, domainRequirements, matcher);
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the current
     * authentication with the specified domain requirements and match the specified filter.
     *
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @param matcher            the filter to apply to the credentials.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeMatching(@NonNull ItemGroup context,
            @NonNull Class<? extends C> type, @NonNull List<DomainRequirement> domainRequirements,
            @NonNull CredentialsMatcher matcher) {
        return includeMatchingAs(Jenkins.getAuthentication(), context, type, domainRequirements, matcher);
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication with the specified domain requirements and match the specified filter.
     *
     * @param authentication     the authentication to search with
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @param matcher            the filter to apply to the credentials.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, Item, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeMatchingAs(@NonNull Authentication authentication,
            @Nullable Item context, @NonNull Class<? extends C> type,
            @NonNull List<DomainRequirement> domainRequirements, @NonNull CredentialsMatcher matcher) {
        addMissing(CredentialsProvider.listCredentials(type, context, authentication, domainRequirements, matcher));
        return this;
    }

    /**
     * Adds the ids of the specified credential type that are available to the specified context as the specified
     * authentication with the specified domain requirements and match the specified filter.
     *
     * @param authentication     the authentication to search with
     * @param context            the context to add credentials from.
     * @param type               the base class of the credentials to add.
     * @param domainRequirements the domain requirements.
     * @param matcher            the filter to apply to the credentials.
     * @return {@code this} for method chaining.
     * @see CredentialsProvider#listCredentials(Class, ItemGroup, Authentication, List, CredentialsMatcher)
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeMatchingAs(@NonNull Authentication authentication,
            @NonNull ItemGroup context, @NonNull Class<? extends C> type,
            @NonNull List<DomainRequirement> domainRequirements, @NonNull CredentialsMatcher matcher) {
        addMissing(CredentialsProvider.listCredentials(type, context, authentication, domainRequirements, matcher));
        return this;
    }

    /**
     * Ensures that the current value is present so that the form can be idempotently saved in those cases where the
     * user saving the form cannot view the current credential
     *
     * @param value the current value.
     * @return {@code this} for method chaining.
     * @since 2.1.0
     */
    public AbstractIdCredentialsListBoxModel<T, C> includeCurrentValue(@NonNull String value) {
        if (StringUtils.isEmpty(value)) {
            return includeEmptyValue();
        }
        for (Option a : this) {
            if (StringUtils.equals(value, a.value)) {
                return this;
            }
        }
        // the current should be the first (unless the first is the empty selection
        int index = isEmpty() ? 0 : "".equals(get(0).value) ? 1 : 0;
        add(index, new Option(Messages.AbstractIdCredentialsListBoxModel_CurrentSelection(), value));
        return this;
    }

    /**
     * Appends all of the missing elements from the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @since 2.1.0
     */
    public boolean addMissing(@NonNull Collection<? extends Option> c) {
        Set<String> existing = new HashSet<String>();
        for (Option o : this) {
            existing.add(o.value);
        }
        boolean changed = false;
        for (Option o : c) {
            if (!existing.contains(o.value)) {
                add(o);
                changed = existing.add(o.value) || changed;
            }
        }
        return changed;
    }
}