Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-28407] Adding some infrastructure to allow for a create cred…
…entials CLI command
  • Loading branch information
stephenc committed Jun 10, 2016
1 parent 3897f93 commit cea53f3
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 1 deletion.
6 changes: 6 additions & 0 deletions pom.xml
Expand Up @@ -105,6 +105,12 @@
<artifactId>antlr4-runtime</artifactId>
<version>${antlr4.version}</version>
</dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Expand Up @@ -82,6 +82,23 @@
public abstract class CredentialsProvider extends Descriptor<CredentialsProvider>
implements ExtensionPoint, Describable<CredentialsProvider>, IconSpec {

/**
* A {@link CredentialsProvider} that does nothing for use as a marker
*
* @since 2.1.1
*/
public static CredentialsProvider NONE = new CredentialsProvider() {
/**
* {@inheritDoc}
*/
@NonNull
@Override
public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @Nullable ItemGroup itemGroup,
@Nullable Authentication authentication) {
return Collections.emptyList();
}
};

/**
* The permission group for credentials.
*
Expand Down
Expand Up @@ -29,7 +29,7 @@
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.Util;
import hudson.model.Computer;
import hudson.cli.declarative.CLIResolver;
import hudson.model.ComputerSet;
import hudson.model.Describable;
import hudson.model.Descriptor;
Expand All @@ -46,15 +46,22 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jenkins.ui.icon.IconSpec;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Localizable;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
Expand Down Expand Up @@ -189,6 +196,154 @@ public WrappedContextResolver getResolver(String className) {
return null;
}

/**
* Resolves a {@link CredentialsStore} instance for CLI commands.
*
* @param storeId the store identifier.
* @return the {@link CredentialsStore} instance.
* @throws CmdLineException if the store cannot be resolved.
* @since 2.1.1
*/
@CLIResolver
public static CredentialsStore resolveForCLI(
@Argument(required = true, metaVar = "STORE", usage = "Store ID") String storeId) throws
CmdLineException {
int index1 = storeId.indexOf("::");
int index2 = index1 == -1 ? -1 : storeId.indexOf("::", index1 + 2);
if (index1 == -1 || index1 == 0 || index2 == -1 || index2 < (index1 + 2) || index2 == storeId.length() - 2) {
throw new CmdLineException(null, new Localizable() {
@Override
public String formatWithLocale(Locale locale, Object... objects) {
return Messages._CredentialsSelectHelper_CLIMalformedStoreId(objects[0]).toString(locale);
}

@Override
public String format(Object... objects) {
return Messages._CredentialsSelectHelper_CLIMalformedStoreId(objects[0]).toString();
}
}, storeId);
}
String providerName = storeId.substring(0, index1);
String resolverName = storeId.substring(index1 + 2, index2);
String token = storeId.substring(index2 + 2);

CredentialsProvider provider = getProvidersByName().get(providerName);
if (provider == null || provider == CredentialsProvider.NONE) {
throw new CmdLineException(null, new Localizable() {
@Override
public String formatWithLocale(Locale locale, Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchProvider(objects[0]).toString(locale);
}

@Override
public String format(Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchProvider(objects[0]).toString();
}
}, storeId);
}
ContextResolver resolver = getResolversByName().get(resolverName);
if (resolver == null || resolver == ContextResolver.NONE) {
throw new CmdLineException(null, new Localizable() {
@Override
public String formatWithLocale(Locale locale, Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchResolver(objects[0]).toString(locale);
}

@Override
public String format(Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchResolver(objects[0]).toString();
}
}, storeId);
}
ModelObject context = resolver.getContext(token);
if (context == null) {
throw new CmdLineException(null, new Localizable() {
@Override
public String formatWithLocale(Locale locale, Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchContext(objects[0]).toString(locale);
}

@Override
public String format(Object... objects) {
return Messages._CredentialsSelectHelper_CLINoSuchContext(objects[0]).toString();
}
}, storeId);
}
CredentialsStore store = provider.getStore(context);
if (store == null) {
throw new CmdLineException(null, new Localizable() {
@Override
public String formatWithLocale(Locale locale, Object... objects) {
return Messages._CredentialsSelectHelper_CLINoStore().toString(locale);
}

@Override
public String format(Object... objects) {
return Messages._CredentialsSelectHelper_CLINoStore().toString();
}
}, storeId);
}
return store;
}

/**
* Returns a map of the {@link ContextResolver} instances keyed by their name. A resolver may have more than one
* entry if there are inferred unique short nicknames.
*
* @return a map of the {@link ContextResolver} instances keyed by their name
* @since 2.1.1
*/
public static Map<String, ContextResolver> getResolversByName() {
Map<String, ContextResolver> resolverByName = new TreeMap<String, ContextResolver>();
for (ContextResolver r : ExtensionList.lookup(ContextResolver.class)) {
resolverByName.put(r.getClass().getName(), r);
String shortName = r.getClass().getSimpleName();
resolverByName.put(shortName, resolverByName.containsKey(shortName) ? ContextResolver.NONE : r);
shortName = shortName.toLowerCase(Locale.ENGLISH).replaceAll("(context|resolver|impl)*", "");
if (StringUtils.isNotBlank(shortName)) {
resolverByName.put(shortName, resolverByName.containsKey(shortName) ? ContextResolver.NONE : r);
}
}
for (Iterator<ContextResolver> iterator = resolverByName.values().iterator(); iterator.hasNext(); ) {
ContextResolver r = iterator.next();
if (r == ContextResolver.NONE) {
iterator.remove();
}
}
return resolverByName;
}

/**
* Returns a map of the {@link CredentialsProvider} instances keyed by their name. A provider may have more than one
* entry if there are inferred unique short nicknames.
*
* @return a map of the {@link CredentialsProvider} instances keyed by their name
* @since 2.1.1
*/
public static Map<String, CredentialsProvider> getProvidersByName() {
Map<String, CredentialsProvider> providerByName = new TreeMap<String, CredentialsProvider>();
for (CredentialsProvider r : ExtensionList.lookup(CredentialsProvider.class)) {
providerByName.put(r.getClass().getName(), r);
Class<?> clazz = r.getClass();
while (clazz != null) {
String shortName = clazz.getSimpleName();
clazz = clazz.getEnclosingClass();
String simpleName =
shortName.toLowerCase(Locale.ENGLISH).replaceAll("(credentials|provider|impl)*", "");
if (StringUtils.isBlank(simpleName)) continue;
providerByName.put(shortName, providerByName.containsKey(shortName) ? CredentialsProvider.NONE : r);
providerByName.put(simpleName, providerByName.containsKey(simpleName) ? CredentialsProvider.NONE : r);
}
}
for (Iterator<CredentialsProvider> iterator = providerByName.values().iterator(); iterator.hasNext(); ) {
CredentialsProvider p = iterator.next();
if (p == CredentialsProvider.NONE) {
iterator.remove();
}
}
return providerByName;
}

/**
* Value class to hold the details of a {@link CredentialsStore}.
*
Expand Down Expand Up @@ -530,6 +685,37 @@ public CredentialsStore getStore() {
*/
public static abstract class ContextResolver implements ExtensionPoint {

/**
* A {@link ContextResolver} that can be used as a marker.
*
* @since 2.1.1
*/
public static final ContextResolver NONE = new ContextResolver() {
/**
* {@inheritDoc}
*/
@Override
public String getToken(ModelObject context) {
return null;
}

/**
* {@inheritDoc}
*/
@Override
public ModelObject getContext(String token) {
return null;
}

/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return "Nothing";
}
};

/**
* Maps a context object (a {@link ModelObject}) into a token that can be used to recover the context.
*
Expand All @@ -549,6 +735,31 @@ public static abstract class ContextResolver implements ExtensionPoint {
*/
@CheckForNull
public abstract ModelObject getContext(String token);

/**
* Returns a human readable descripton of the type of context objects that this resolver resolves.
* @return a human readable descripton of the type of context objects that this resolver resolves.
* @throws AbstractMethodError if somebody compiled against pre-2.1.1 implementations. Use
* {@link #displayName(ContextResolver)} if you would prefer not to have to catch them.
* @since 2.1.1
*/
@NonNull
public abstract String getDisplayName();

/**
* Returns a human readable descripton of the type of context objects that the specified resolver resolves.
*
* @return a human readable descripton of the type of context objects that the specified resolver resolves.
* @since 2.1.1
*/
public static String displayName(ContextResolver resolver) {
try {
return resolver.getDisplayName();
} catch (AbstractMethodError e) {
// should not get here as do not anticipate new implementations that cannot target 2.1.1 as a baseline
return resolver.getClass().getName();
}
}
}

/**
Expand All @@ -574,6 +785,15 @@ public String getToken(ModelObject context) {
public ModelObject getContext(String token) {
return "jenkins".equals(token) ? Jenkins.getActiveInstance() : null;
}

/**
* {@inheritDoc}
*/
@NonNull
@Override
public String getDisplayName() {
return "Jenkins";
}
}

/**
Expand All @@ -599,6 +819,15 @@ public String getToken(ModelObject context) {
public ModelObject getContext(String token) {
return Jenkins.getActiveInstance().getItemByFullName(token);
}

/**
* {@inheritDoc}
*/
@NonNull
@Override
public String getDisplayName() {
return "Items";
}
}

/**
Expand Down Expand Up @@ -635,5 +864,14 @@ public ModelObject getContext(String token) {
return null;
}
}

/**
* {@inheritDoc}
*/
@NonNull
@Override
public String getDisplayName() {
return "Users";
}
}
}

0 comments on commit cea53f3

Please sign in to comment.