package denominator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static denominator.common.Preconditions.checkNotNull; /** * Abstractly encapsulates credentials used by a Provider. Descriptions of what type of Credentials * a Provider can use are found via {@link Provider#credentialTypeToParameterNames()}. * * <br> <br> <b>All Credentials are Anonymous, implement Map or List</b><br> * * You must either subclass roots listed here, or ensure your implementation classes implement * {@code Map<String, ?>} or {@code List<?>}. * * <br> <br> <b>Validation Rules</b><br> <ul> <li>{@link AnonymousCredentials}, null, or empty * credentials are permitted when {@link Provider#credentialTypeToParameterNames()} is empty</li> * <li>{@link ListCredentials} or {@code Credentials} which implement {@code List} are permitted * when its {@code size} match the size of an entry in {@link Provider#credentialTypeToParameterNames()}</li> * <li>{@link MapCredentials} or {@code Credentials} which implement {@code Map} are permitted when * its keys match the values of an entry in {@link Provider#credentialTypeToParameterNames()}</li> * </ul> * * <br> <br> <b>Parameters</b><br> * * While most credential parameters are Strings, and most providers will be able to coerce * parameters from Strings, if you choose to create your own Credential implementations, allow users * to pass {@code Object} parameters of the correct type where possible. <br> For example, certain * providers may prefer a {@code Certificate} or {@code PrivateKey} parameter vs a relatively * expensive coercion from a PEM-encoded String. */ public interface Credentials { /** * For use in providers that do not authenticate, or those that permit anonymous calls. */ public static enum AnonymousCredentials implements Credentials { INSTANCE; } /** * Credentials where the position and count of parameters is enough to differentiate one type of * credentials from another. * * <br> <br> <b>Example</b><br> * * In {@code route53}, one can use normal {@code accessKey} credentials, or temporary ones from * {code STS}. In this case, you can get by simply by differentiating on count of parameters. * * <br> Normally, the caller passes: * * <pre> * ["AAFF12AA", "BB34FF"] * </pre> * * When using STS, the caller passes an additional parameter corresponding to the session token: * * <pre> * ["AAFF12AA", "BB34FF", "FFFeeeEE"] * </pre> * * @see Provider#credentialTypeToParameterNames() */ public static class ListCredentials extends ArrayList<Object> implements Credentials { private static final long serialVersionUID = 1L; protected ListCredentials(Collection<?> args) { super(args); } /** * returns a {@code Credentials} view of the input {@code parts}. * * @param parts corresponds directly to the credentials needed by a provider * @return credentials view of {@code parts} or {@link AnonymousCredentials} if empty */ public static Credentials from(final List<?> parts) { if (parts == null || parts.isEmpty()) { return AnonymousCredentials.INSTANCE; } return new ListCredentials(parts); } /** * see {@link #from(List)} */ public static Credentials from(Object... parts) { return from(Arrays.asList(checkNotNull(parts, "credentials"))); } /** * Make it very easy on providers who only accept a single credentials type. The following will * coerce anything that implements {@code Map} or {@code List} to {@code ListCredentials}. * * <br> <br> <b>Example</b><br> The following example is how this could be used from within a * method in {@link Provider} to simplify credential conversion regardless of input type. * * <pre> * Credentials validatedInput = CredentialsConfiguration.firstValidCredentialsForProvider(sources, * this); * List<Object> creds = ListCredentials.asList(validatedInput); * return new BasicAWSCredentials(creds.get(0).toString(), creds.get(1).toString()); * </pre> * * @return copy of the credentials values * @throws IllegalArgumentException if input is not a {@code Map} or {@code List}, or it is * empty. * @deprecated This method is deprecated because it converts map credentials into list containing * items in same order as in map. Providers should handle each type of credential separately. */ public static List<Object> asList(Credentials in) throws IllegalArgumentException { checkNotNull(in, "credentials"); if (in instanceof ListCredentials) { return ListCredentials.class.cast(in); } else if (in instanceof Map || in instanceof List) { Collection<?> values = (in instanceof Map) ? Map.class.cast(in).values() : List.class.cast(in); if (values.isEmpty()) { throw new IllegalArgumentException("cannot convert empty credentials to List<Object>"); } return new ArrayList<Object>(values); } throw new IllegalArgumentException("cannot convert " + in.getClass() + " to ListCredentials"); } } /** * Credentials in the form of named parameters, useful when a provider accepts two different * credential types with the same count of parameters. * * <br> <br> <b>Example</b><br> * * In OpenStack, both {@code accessKey} and {@code password} credentials require two parts. In * this case, {@code MapCredentials} can name the parts to differentiate them. * * <br> For example, when using {@code password}, the caller passes: * * <pre> * {"username": "foo", "password": "bar"} * </pre> * * Whereas for {@code accessKey}, the caller passes: * * <pre> * {"accessKey": "AAFF12AA", "secretKey": "BB34FF"} * </pre> * * @see Provider#credentialTypeToParameterNames() */ public static class MapCredentials extends LinkedHashMap<String, Object> implements Credentials { private static final long serialVersionUID = 1L; protected MapCredentials(Map<String, ?> kwargs) { super(kwargs); } /** * returns a {@code Credentials} view of the input {@code kwargs} * * @param kwargs corresponds directly to the credentials needed by a provider * @return credentials view of {@code kwargs} or {@link AnonymousCredentials} if null or empty */ public static Credentials from(final Map<String, ?> kwargs) { if (kwargs == null || kwargs.isEmpty()) { return AnonymousCredentials.INSTANCE; } return new MapCredentials(kwargs); } } }