/*
* The MIT License
*
* Copyright (c) 2011-2012, 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.impl;
import com.cloudbees.plugins.credentials.BaseCredentials;
import com.cloudbees.plugins.credentials.ContextInPath;
import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsDescriptor;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsSelectHelper;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.CredentialsStoreAction;
import com.cloudbees.plugins.credentials.common.IdCredentials;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.Util;
import hudson.model.Item;
import hudson.model.ModelObject;
import hudson.model.User;
import hudson.util.FormValidation;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.EnumSet;
import java.util.Set;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import static com.cloudbees.plugins.credentials.CredentialsSelectHelper.*;
/**
* Base class for {@link StandardCredentials}.
*/
@ExportedBean
public abstract class BaseStandardCredentials extends BaseCredentials implements StandardCredentials {
/**
* Our ID.
*/
@NonNull
private final String id;
/**
* Our description.
*/
@NonNull
private final String description;
/**
* Constructor.
*
* @param id the id.
* @param description the description.
*/
public BaseStandardCredentials(@CheckForNull String id, @CheckForNull String description) {
super();
this.id = IdCredentials.Helpers.fixEmptyId(id);
this.description = Util.fixNull(description);
}
/**
* Constructor.
*
* @param scope the scope.
* @param id the id.
* @param description the description.
*/
public BaseStandardCredentials(@CheckForNull CredentialsScope scope, @CheckForNull String id,
@CheckForNull String description) {
super(scope);
this.id = IdCredentials.Helpers.fixEmptyId(id);
this.description = Util.fixNull(description);
}
/**
* {@inheritDoc}
*/
@NonNull
@Exported
public String getDescription() {
return description;
}
/**
* {@inheritDoc}
*/
@NonNull
@Exported
public String getId() {
return id;
}
/**
* {@inheritDoc}
*/
@Override
public final int hashCode() {
return IdCredentials.Helpers.hashCode(this);
}
/**
* {@inheritDoc}
*/
@Override
public final boolean equals(Object o) {
return IdCredentials.Helpers.equals(this, o);
}
/**
* Descriptor to use for subclasses of {@link BaseStandardCredentials}.
* <p>{@code <st:include page="id-and-description" class="${descriptor.clazz}"/>} in {@code credentials.jelly} to
* pick up standard controls for {@link #getId} and {@link #getDescription}.
*/
protected static abstract class BaseStandardCredentialsDescriptor extends CredentialsDescriptor {
protected BaseStandardCredentialsDescriptor() {
clazz.asSubclass(BaseStandardCredentials.class);
}
protected BaseStandardCredentialsDescriptor(Class<? extends BaseStandardCredentials> clazz) {
super(clazz);
}
@CheckForNull
private static FormValidation checkForDuplicates(String value, ModelObject context, ModelObject object) {
CredentialsMatcher withId = CredentialsMatchers.withId(value);
for (CredentialsStore store : CredentialsProvider.lookupStores(object)) {
if (!store.hasPermission(CredentialsProvider.VIEW)) {
continue;
}
ModelObject storeContext = store.getContext();
for (Domain domain : store.getDomains()) {
for (Credentials match : CredentialsMatchers.filter(store.getCredentials(domain), withId)) {
if (storeContext == context) {
return FormValidation.error("This ID is already in use");
} else {
CredentialsScope scope = match.getScope();
if (scope != null && !scope.isVisible(context)) {
// scope is not exported to child contexts
continue;
}
return FormValidation.warning("The ID ā%sā is already in use in %s", value,
storeContext instanceof Item
? ((Item) storeContext).getFullDisplayName()
: storeContext.getDisplayName());
}
}
}
}
return null;
}
/**
* Gets the check id url for the specified store.
*
* @param store the store.
* @return the url of the id check endpoint.
* @throws UnsupportedEncodingException if the JVM does not implement the JLS.
*/
public String getCheckIdUrl(CredentialsStore store) throws UnsupportedEncodingException {
ModelObject context = store.getContext();
for (ContextResolver r : ExtensionList.lookup(ContextResolver.class)) {
String token = r.getToken(context);
if (token != null) {
return Jenkins.getActiveInstance().getRootUrlFromRequest() + "/" + getDescriptorUrl()
+ "/checkId?provider=" + r.getClass().getName() + "&token="
+ URLEncoder.encode(token, "UTF-8");
}
}
return Jenkins.getActiveInstance().getRootUrlFromRequest() + "/" + getDescriptorUrl()
+ "/checkId?provider=null&token=null";
}
public final FormValidation doCheckId(@ContextInPath ModelObject context, @QueryParameter String value) {
if (value.isEmpty()) {
return FormValidation.ok();
}
if (!value.matches("[a-zA-Z0-9_.-]+")) { // anything else considered kosher?
return FormValidation.error("Unacceptable characters");
}
FormValidation problem = checkForDuplicates(value, context, context);
if (problem != null) {
return problem;
}
if (!(context instanceof User)) {
User me = User.current();
if (me != null) {
problem = checkForDuplicates(value, context, me);
if (problem != null) {
return problem;
}
}
}
if (!(context instanceof Jenkins)) {
// CredentialsProvider.lookupStores(User) does not return SystemCredentialsProvider.
Jenkins j = Jenkins.getInstance();
if (j != null) {
problem = checkForDuplicates(value, context, j);
if (problem != null) {
return problem;
}
}
}
return FormValidation.ok();
}
}
}