unwound dependencies relating to ssh keys and crypt

This commit is contained in:
Adrian Cole 2013-01-13 11:48:13 -08:00
parent 6a8ac673aa
commit 3ac6f475e7
44 changed files with 360 additions and 330 deletions

View File

@ -29,13 +29,13 @@ import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.cloudstack.filters.QuerySigner; import org.jclouds.cloudstack.filters.QuerySigner;
import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest; import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
import org.jclouds.cloudstack.options.ListSSHKeyPairsOptions; import org.jclouds.cloudstack.options.ListSSHKeyPairsOptions;
import org.jclouds.crypto.SshKeys;
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions; import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
import org.jclouds.functions.IdentityFunction; import org.jclouds.functions.IdentityFunction;
import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.http.functions.ReleasePayloadAndReturn;
import com.google.common.reflect.Invokable; import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Functions; import com.google.common.base.Functions;

View File

@ -27,9 +27,9 @@ import java.net.URI;
import org.jclouds.cloudstack.CloudStackContext; import org.jclouds.cloudstack.CloudStackContext;
import org.jclouds.cloudstack.domain.SshKeyPair; import org.jclouds.cloudstack.domain.SshKeyPair;
import org.jclouds.cloudstack.internal.BaseCloudStackExpectTest; import org.jclouds.cloudstack.internal.BaseCloudStackExpectTest;
import org.jclouds.crypto.SshKeys;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.ssh.SshKeys;
import org.jclouds.util.Strings2; import org.jclouds.util.Strings2;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -27,7 +27,7 @@ import java.util.Set;
import org.jclouds.cloudstack.domain.SshKeyPair; import org.jclouds.cloudstack.domain.SshKeyPair;
import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest; import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
import org.jclouds.crypto.SshKeys; import org.jclouds.ssh.SshKeys;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -21,8 +21,8 @@ package org.jclouds.ec2.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
import static org.jclouds.crypto.SshKeys.sha1PrivateKey; import static org.jclouds.ssh.SshKeys.sha1PrivateKey;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;

View File

@ -20,8 +20,8 @@ package org.jclouds.ec2.domain;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.crypto.SshKeys;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import org.jclouds.ssh.SshKeys;
/** /**
* *

View File

@ -22,9 +22,9 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.jclouds.crypto.SshKeys;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**

View File

@ -30,9 +30,9 @@ import java.util.SortedSet;
import org.jclouds.compute.ComputeTestUtils; import org.jclouds.compute.ComputeTestUtils;
import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
import org.jclouds.crypto.SshKeys;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.openstack.nova.ec2.NovaEC2ApiMetadata; import org.jclouds.openstack.nova.ec2.NovaEC2ApiMetadata;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -20,7 +20,7 @@ package org.jclouds.openstack.nova.v2_0.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

View File

@ -26,9 +26,9 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.crypto.SshKeys;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.ssh.SshKeys;
import org.jclouds.trmk.vcloud_0_8.compute.domain.OrgAndName; import org.jclouds.trmk.vcloud_0_8.compute.domain.OrgAndName;
import org.jclouds.trmk.vcloud_0_8.compute.functions.CreateUniqueKeyPair; import org.jclouds.trmk.vcloud_0_8.compute.functions.CreateUniqueKeyPair;
import org.jclouds.trmk.vcloud_0_8.compute.options.TerremarkVCloudTemplateOptions; import org.jclouds.trmk.vcloud_0_8.compute.options.TerremarkVCloudTemplateOptions;

View File

@ -24,8 +24,8 @@ import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.jclouds.crypto.SshKeys;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.ssh.SshKeys;
import org.jclouds.trmk.vcloud_0_8.domain.KeyPair; import org.jclouds.trmk.vcloud_0_8.domain.KeyPair;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -0,0 +1,123 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.compute.config;
import static com.google.common.base.Charsets.UTF_8;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.compute.config.AdminAccessConfiguration.Default;
import org.jclouds.compute.functions.Sha512Crypt;
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
import org.jclouds.ssh.SshKeys;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.google.inject.ImplementedBy;
/**
*
* @author Adrian Cole
*
*/
@ImplementedBy(Default.class)
public interface AdminAccessConfiguration extends Configuration {
@Singleton
static class Default implements AdminAccessConfiguration {
private final Supplier<String> defaultAdminUsername = Suppliers.ofInstance(System.getProperty("user.name"));
private final Supplier<Map<String, String>> defaultAdminSshKeys = new Supplier<Map<String, String>>() {
public Map<String, String> get() {
try {
return ImmutableMap.of("public",
Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa.pub"), UTF_8), "private",
Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa"), UTF_8));
} catch (IOException e) {
return SshKeys.generate();
}
}
};
/**
* Cheap, lightweight, low-security password generator.
*
* @see <a href=
* "http://www.java-forums.org/java-lang/7355-how-create-lightweight-low-security-password-generator.html" />
*/
enum PasswordGenerator implements Supplier<String> {
INSTANCE;
/** Minimum length for a decent password */
public static final int MIN_LENGTH = 10;
/** The random number generator. */
protected static final SecureRandom r = new SecureRandom();
/*
* Set of characters that is valid. Must be printable, memorable, and "won't break HTML" (i.e., not ' <', '>',
* '&', '=', ...). or break shell commands (i.e., not ' <', '>', '$', '!', ...). I, L and O are good to leave
* out, as are numeric zero and one.
*/
public static final char[] goodChar = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-',
'@', };
@Override
public String get() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < MIN_LENGTH; i++) {
sb.append(goodChar[r.nextInt(goodChar.length)]);
}
return sb.toString();
}
}
private final Function<String, String> cryptFunction = Sha512Crypt.function();
@Override
public Supplier<String> defaultAdminUsername() {
return defaultAdminUsername;
}
@Override
public Supplier<Map<String, String>> defaultAdminSshKeys() {
return defaultAdminSshKeys;
}
@Override
public Supplier<String> passwordGenerator() {
return PasswordGenerator.INSTANCE;
}
@Override
public Function<String, String> cryptFunction() {
return cryptFunction;
}
}
}

View File

@ -61,6 +61,7 @@ import org.jclouds.location.Provider;
import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import com.google.common.base.Function; import com.google.common.base.Function;
@ -85,6 +86,7 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(AdminAccess.Configuration.class).to(AdminAccessConfiguration.class);
install(new ComputeServiceTimeoutsModule()); install(new ComputeServiceTimeoutsModule());
bind(new TypeLiteral<Function<NodeMetadata, SshClient>>() { bind(new TypeLiteral<Function<NodeMetadata, SshClient>>() {
}).to(CreateSshClientOncePortIsListeningOnNode.class); }).to(CreateSshClientOncePortIsListeningOnNode.class);

View File

@ -62,7 +62,7 @@
*/ */
package org.jclouds.crypto; package org.jclouds.compute.functions;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;

View File

@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.ssh;
import java.util.Map; import java.util.Map;
import org.jclouds.ssh.internal.RsaSshKeyPairGenerator;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.inject.ImplementedBy; import com.google.inject.ImplementedBy;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.ssh;
import static com.google.common.base.Joiner.on; import static com.google.common.base.Joiner.on;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -48,6 +48,8 @@ import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import java.util.Map; import java.util.Map;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.Pems;
import org.jclouds.io.InputSuppliers; import org.jclouds.io.InputSuppliers;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.ssh; package org.jclouds.ssh.config;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.ssh.internal;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -25,10 +25,14 @@ import java.util.Map;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.crypto.Crypto;
import org.jclouds.ssh.SshKeyPairGenerator;
import org.jclouds.ssh.SshKeys;
import com.google.inject.Inject; import com.google.inject.Inject;
@Singleton @Singleton
class RsaSshKeyPairGenerator implements SshKeyPairGenerator { public class RsaSshKeyPairGenerator implements SshKeyPairGenerator {
private final Crypto crypto; private final Crypto crypto;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;

View File

@ -32,6 +32,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.easymock.IArgumentMatcher; import org.easymock.IArgumentMatcher;
import org.jclouds.compute.config.AdminAccessConfiguration;
import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.internal.BaseComputeServiceLiveTest; import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
@ -42,8 +43,6 @@ import org.jclouds.io.Payload;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.predicates.SocketOpen; import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.AuthorizationException;
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.jclouds.util.Strings2; import org.jclouds.util.Strings2;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -104,31 +103,23 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes
@Override @Override
protected Module getSshModule() { protected Module getSshModule() {
return new AbstractModule() { return new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
bind(AdminAccess.Configuration.class).toInstance(new Configuration() { bind(AdminAccessConfiguration.class).toInstance(new AdminAccessConfiguration() {
@Override
public Supplier<String> defaultAdminUsername() { public Supplier<String> defaultAdminUsername() {
return Suppliers.ofInstance("defaultAdminUsername"); return Suppliers.ofInstance("defaultAdminUsername");
} }
@Override
public Supplier<Map<String, String>> defaultAdminSshKeys() { public Supplier<Map<String, String>> defaultAdminSshKeys() {
return Suppliers.<Map<String, String>> ofInstance(ImmutableMap.of("public", "publicKey", "private", return Suppliers.<Map<String, String>> ofInstance(ImmutableMap.of("public", "publicKey", "private",
Pems.PRIVATE_PKCS1_MARKER)); Pems.PRIVATE_PKCS1_MARKER));
} }
@Override
public Function<String, String> cryptFunction() { public Function<String, String> cryptFunction() {
return new Function<String, String>() { return new Function<String, String>() {
@Override
public String apply(String input) { public String apply(String input) {
return String.format("crypt(%s)", input); return String.format("crypt(%s)", input);
} }
}; };
} }

View File

@ -16,10 +16,11 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.compute.functions;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import org.jclouds.compute.functions.Sha512Crypt;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -16,14 +16,14 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.ssh;
import static org.jclouds.crypto.SshKeys.fingerprint; import static org.jclouds.ssh.SshKeys.fingerprint;
import static org.jclouds.crypto.SshKeys.generate; import static org.jclouds.ssh.SshKeys.generate;
import static org.jclouds.crypto.SshKeys.privateKeyHasFingerprint; import static org.jclouds.ssh.SshKeys.privateKeyHasFingerprint;
import static org.jclouds.crypto.SshKeys.privateKeyHasSha1; import static org.jclouds.ssh.SshKeys.privateKeyHasSha1;
import static org.jclouds.crypto.SshKeys.privateKeyMatchesPublicKey; import static org.jclouds.ssh.SshKeys.privateKeyMatchesPublicKey;
import static org.jclouds.crypto.SshKeys.publicKeySpecFromOpenSSH; import static org.jclouds.ssh.SshKeys.publicKeySpecFromOpenSSH;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
@ -35,7 +35,9 @@ import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import java.util.Map; import java.util.Map;
import org.jclouds.crypto.Pems;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.jclouds.ssh.SshKeys;
import org.jclouds.util.Strings2; import org.jclouds.util.Strings2;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.crypto; package org.jclouds.ssh.config;
import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
@ -37,7 +37,11 @@ import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.Pems;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.jclouds.ssh.SshKeys;
import org.jclouds.ssh.internal.RsaSshKeyPairGenerator;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -19,6 +19,9 @@
package org.jclouds.rest.internal; package org.jclouds.rest.internal;
import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Optional.fromNullable;
import static com.google.common.collect.ObjectArrays.concat;
import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -39,6 +42,8 @@ import com.google.common.base.Optional;
import com.google.common.reflect.Invokable; import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
public class BlockOnFuture implements Function<ListenableFuture<?>, Result> { public class BlockOnFuture implements Function<ListenableFuture<?>, Result> {
@ -77,20 +82,40 @@ public class BlockOnFuture implements Function<ListenableFuture<?>, Result> {
try { try {
if (timeoutNanos.isPresent()) { if (timeoutNanos.isPresent()) {
logger.debug(">> blocking on %s for %s", future, timeoutNanos); logger.debug(">> blocking on %s for %s", future, timeoutNanos);
return Result.success(future.get(timeoutNanos.get(), TimeUnit.NANOSECONDS)); return Result.success(getUninterruptibly(future, timeoutNanos.get(), NANOSECONDS));
} else { } else {
logger.debug(">> blocking on %s", future); logger.debug(">> blocking on %s", future);
return Result.success(future.get()); return Result.success(getUninterruptibly(future));
} }
} catch (ExecutionException e) { } catch (ExecutionException e) {
return Result.fail(e.getCause()); throw propagateCause(e);
} catch (InterruptedException e) {
return Result.fail(e); // TODO: should we kill the future?
} catch (TimeoutException e) { } catch (TimeoutException e) {
return Result.fail(e); future.cancel(true);
throw new UncheckedTimeoutException(e);
} }
} }
private static RuntimeException propagateCause(Exception e) {
Throwable cause = e.getCause();
if (cause == null) {
UncheckedExecutionException unchecked = new UncheckedExecutionException(e.getMessage()) {
private static final long serialVersionUID = 1L;
};
unchecked.setStackTrace(e.getStackTrace());
throw unchecked;
}
StackTraceElement[] combined = concat(cause.getStackTrace(), e.getStackTrace(), StackTraceElement.class);
cause.setStackTrace(combined);
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
// The cause is a weird kind of Throwable, so throw the outer exception.
throw new RuntimeException(e);
}
// override timeout by values configured in properties(in ms) // override timeout by values configured in properties(in ms)
private Optional<Long> timeoutInNanos(Invokable<?, ?> invoked, Map<String, Long> timeouts) { private Optional<Long> timeoutInNanos(Invokable<?, ?> invoked, Map<String, Long> timeouts) {
String className = enclosingType.getRawType().getSimpleName(); String className = enclosingType.getRawType().getSimpleName();

View File

@ -28,8 +28,8 @@ import static com.google.common.base.Throwables.getCausalChain;
import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.any;
import static com.google.common.hash.Hashing.md5; import static com.google.common.hash.Hashing.md5;
import static com.google.common.io.BaseEncoding.base16; import static com.google.common.io.BaseEncoding.base16;
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
import static org.jclouds.crypto.SshKeys.sha1PrivateKey; import static org.jclouds.ssh.SshKeys.sha1PrivateKey;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;

View File

@ -24,8 +24,8 @@ import org.jclouds.Constants;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler; import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.ssh.ConfiguresSshClient;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.config.ConfiguresSshClient;
import org.jclouds.ssh.jsch.JschSshClient; import org.jclouds.ssh.jsch.JschSshClient;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;

View File

@ -28,8 +28,8 @@ import static com.google.common.base.Throwables.getCausalChain;
import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.any;
import static com.google.common.hash.Hashing.md5; import static com.google.common.hash.Hashing.md5;
import static com.google.common.io.BaseEncoding.base16; import static com.google.common.io.BaseEncoding.base16;
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
import static org.jclouds.crypto.SshKeys.sha1PrivateKey; import static org.jclouds.ssh.SshKeys.sha1PrivateKey;
import java.io.Closeable; import java.io.Closeable;
import java.io.FilterInputStream; import java.io.FilterInputStream;

View File

@ -24,8 +24,8 @@ import org.jclouds.Constants;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler; import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.ssh.ConfiguresSshClient;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.config.ConfiguresSshClient;
import org.jclouds.sshj.SshjSshClient; import org.jclouds.sshj.SshjSshClient;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;

View File

@ -28,12 +28,12 @@ import javax.inject.Singleton;
import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi; import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey; import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key; import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.joyent.cloudapi.v6_5.domain.datacenterscoped.DatacenterAndName; import org.jclouds.joyent.cloudapi.v6_5.domain.datacenterscoped.DatacenterAndName;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.ssh.SshKeyPairGenerator;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.inject.Inject; import com.google.inject.Inject;

View File

@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.BaseJoyentCloudComputeServiceExpectTest; import org.jclouds.joyent.cloudapi.v6_5.compute.internal.BaseJoyentCloudComputeServiceExpectTest;
@ -37,6 +36,7 @@ import org.jclouds.joyent.cloudapi.v6_5.features.DatasetApiExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.features.MachineApiExpectTest; import org.jclouds.joyent.cloudapi.v6_5.features.MachineApiExpectTest;
import org.jclouds.joyent.cloudapi.v6_5.features.PackageApiExpectTest; import org.jclouds.joyent.cloudapi.v6_5.features.PackageApiExpectTest;
import org.jclouds.location.reference.LocationConstants; import org.jclouds.location.reference.LocationConstants;
import org.jclouds.ssh.SshKeyPairGenerator;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;

View File

@ -31,12 +31,12 @@ import java.util.Map;
import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.functions.GroupNamingConvention.Factory; import org.jclouds.compute.functions.GroupNamingConvention.Factory;
import org.jclouds.crypto.SshKeyPairGenerator;
import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi; import org.jclouds.joyent.cloudapi.v6_5.JoyentCloudApi;
import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey; import org.jclouds.joyent.cloudapi.v6_5.compute.internal.KeyAndPrivateKey;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key; import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.joyent.cloudapi.v6_5.domain.datacenterscoped.DatacenterAndName; import org.jclouds.joyent.cloudapi.v6_5.domain.datacenterscoped.DatacenterAndName;
import org.jclouds.joyent.cloudapi.v6_5.features.KeyApi; import org.jclouds.joyent.cloudapi.v6_5.features.KeyApi;
import org.jclouds.ssh.SshKeyPairGenerator;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -22,10 +22,10 @@ import static org.testng.Assert.assertEquals;
import java.util.Set; import java.util.Set;
import org.jclouds.crypto.SshKeys;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key; import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.joyent.cloudapi.v6_5.features.KeyApi; import org.jclouds.joyent.cloudapi.v6_5.features.KeyApi;
import org.jclouds.joyent.cloudapi.v6_5.internal.BaseJoyentCloudApiLiveTest; import org.jclouds.joyent.cloudapi.v6_5.internal.BaseJoyentCloudApiLiveTest;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -27,7 +27,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.crypto.SshKeys;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.joyent.cloudapi.v6_5.domain.Key; import org.jclouds.joyent.cloudapi.v6_5.domain.Key;
import org.jclouds.joyent.cloudapi.v6_5.domain.Machine; import org.jclouds.joyent.cloudapi.v6_5.domain.Machine;
@ -37,6 +36,7 @@ import org.jclouds.joyent.cloudapi.v6_5.reference.Metadata;
import org.jclouds.predicates.InetSocketAddressConnect; import org.jclouds.predicates.InetSocketAddressConnect;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.SshKeys;
import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.sshj.config.SshjSshClientModule;
import org.jclouds.util.InetAddresses2; import org.jclouds.util.InetAddresses2;
import org.testng.annotations.AfterGroups; import org.testng.annotations.AfterGroups;

View File

@ -38,9 +38,9 @@ import java.net.URISyntaxException;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jclouds.crypto.SshKeys;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.jclouds.json.Json; import org.jclouds.json.Json;
import org.jclouds.ssh.SshKeys;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
import org.jclouds.vcloud.director.v1_5.domain.Checks; import org.jclouds.vcloud.director.v1_5.domain.Checks;
import org.jclouds.vcloud.director.v1_5.domain.File; import org.jclouds.vcloud.director.v1_5.domain.File;

View File

@ -19,7 +19,7 @@
package org.jclouds.aws.ec2.functions; package org.jclouds.aws.ec2.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.crypto.SshKeys.fingerprintPublicKey; import static org.jclouds.ssh.SshKeys.fingerprintPublicKey;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;

View File

@ -22,7 +22,7 @@ import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify; import static org.easymock.EasyMock.verify;
import static org.jclouds.crypto.SshKeys.fingerprintPublicKey; import static org.jclouds.ssh.SshKeys.fingerprintPublicKey;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.AWSEC2Client;

View File

@ -21,30 +21,30 @@ package org.jclouds.scriptbuilder.statements.login;
import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.isNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Lists.newArrayList;
import static org.jclouds.scriptbuilder.statements.login.ShadowStatements.resetLoginUserPasswordTo;
import static org.jclouds.scriptbuilder.statements.ssh.SshStatements.lockSshd;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import org.jclouds.crypto.Sha512Crypt;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.domain.StatementList;
import org.jclouds.scriptbuilder.statements.ssh.SshStatements;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.inject.ImplementedBy;
/** /**
* Controls the administrative access to a node. By default, it will perform the following: * Controls the administrative access to a node. By default, it will perform the following:
@ -74,19 +74,14 @@ import com.google.inject.ImplementedBy;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AdminAccess implements Statement { public class AdminAccess implements Statement {
public static AdminAccess.Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
public static AdminAccess.Builder builder(Function<String, String> cryptFunction) {
return new Builder(cryptFunction);
}
public static AdminAccess standard() { public static AdminAccess standard() {
return new Builder().build(); return new Builder().build();
} }
@ImplementedBy(DefaultConfiguration.class)
public static interface Configuration { public static interface Configuration {
Supplier<String> defaultAdminUsername(); Supplier<String> defaultAdminUsername();
@ -98,16 +93,7 @@ public class AdminAccess implements Statement {
} }
public static class Builder { public static class Builder {
private final Function<String, String> cryptFunction; private Function<String, String> cryptFunction;
public Builder() {
this(Sha512Crypt.function());
}
public Builder(Function<String, String> cryptFunction) {
this.cryptFunction = cryptFunction;
}
private String adminUsername; private String adminUsername;
private String adminFullName; private String adminFullName;
private String adminHome; private String adminHome;
@ -123,86 +109,91 @@ public class AdminAccess implements Statement {
private boolean installAdminPrivateKey = false; private boolean installAdminPrivateKey = false;
private boolean resetLoginPassword = true; private boolean resetLoginPassword = true;
public AdminAccess.Builder adminUsername(String adminUsername) { public Builder cryptFunction(Function<String, String> cryptFunction) {
this.adminUsername = adminUsername; this.cryptFunction = cryptFunction;
return this; return this;
} }
public AdminAccess.Builder adminFullName(String adminFullName) { public Builder adminUsername(String adminUsername) {
this.adminUsername = checkNotRoot(adminUsername);
return this;
}
public Builder adminFullName(String adminFullName) {
this.adminFullName = adminFullName; this.adminFullName = adminFullName;
return this; return this;
} }
public AdminAccess.Builder adminHome(String adminHome) { public Builder adminHome(String adminHome) {
this.adminHome = adminHome; this.adminHome = adminHome;
return this; return this;
} }
public AdminAccess.Builder adminPassword(String adminPassword) { public Builder adminPassword(String adminPassword) {
this.adminPassword = adminPassword; this.adminPassword = adminPassword;
return this; return this;
} }
public AdminAccess.Builder loginPassword(String loginPassword) { public Builder loginPassword(String loginPassword) {
this.loginPassword = loginPassword; this.loginPassword = loginPassword;
return this; return this;
} }
public AdminAccess.Builder lockSsh(boolean lockSsh) { public Builder lockSsh(boolean lockSsh) {
this.lockSsh = lockSsh; this.lockSsh = lockSsh;
return this; return this;
} }
public AdminAccess.Builder resetLoginPassword(boolean resetLoginPassword) { public Builder resetLoginPassword(boolean resetLoginPassword) {
this.resetLoginPassword = resetLoginPassword; this.resetLoginPassword = resetLoginPassword;
return this; return this;
} }
public AdminAccess.Builder authorizeAdminPublicKey(boolean authorizeAdminPublicKey) { public Builder authorizeAdminPublicKey(boolean authorizeAdminPublicKey) {
this.authorizeAdminPublicKey = authorizeAdminPublicKey; this.authorizeAdminPublicKey = authorizeAdminPublicKey;
return this; return this;
} }
public AdminAccess.Builder installAdminPrivateKey(boolean installAdminPrivateKey) { public Builder installAdminPrivateKey(boolean installAdminPrivateKey) {
this.installAdminPrivateKey = installAdminPrivateKey; this.installAdminPrivateKey = installAdminPrivateKey;
return this; return this;
} }
public AdminAccess.Builder grantSudoToAdminUser(boolean grantSudoToAdminUser) { public Builder grantSudoToAdminUser(boolean grantSudoToAdminUser) {
this.grantSudoToAdminUser = grantSudoToAdminUser; this.grantSudoToAdminUser = grantSudoToAdminUser;
return this; return this;
} }
public AdminAccess.Builder adminPublicKey(File adminPublicKey) { public Builder adminPublicKey(File adminPublicKey) {
this.adminPublicKeyFile = adminPublicKey; this.adminPublicKeyFile = adminPublicKey;
this.adminPublicKey = null; this.adminPublicKey = null;
return this; return this;
} }
public AdminAccess.Builder adminPublicKey(String adminPublicKey) { public Builder adminPublicKey(String adminPublicKey) {
this.adminPublicKey = adminPublicKey; this.adminPublicKey = adminPublicKey;
this.adminPublicKeyFile = null; this.adminPublicKeyFile = null;
return this; return this;
} }
public AdminAccess.Builder adminPrivateKey(File adminPrivateKey) { public Builder adminPrivateKey(File adminPrivateKey) {
this.adminPrivateKeyFile = adminPrivateKey; this.adminPrivateKeyFile = adminPrivateKey;
this.adminPrivateKey = null; this.adminPrivateKey = null;
return this; return this;
} }
public AdminAccess.Builder adminPrivateKey(String adminPrivateKey) { public Builder adminPrivateKey(String adminPrivateKey) {
this.adminPrivateKey = adminPrivateKey; this.adminPrivateKey = adminPrivateKey;
this.adminPrivateKeyFile = null; this.adminPrivateKeyFile = null;
return this; return this;
} }
public AdminAccess.Builder from(AdminAccessBuilderSpec spec) { public Builder from(AdminAccessBuilderSpec spec) {
return spec.copyTo(this); return spec.copyTo(this);
} }
public AdminAccess.Builder from(String spec) { public Builder from(String spec) {
return from(AdminAccessBuilderSpec.parse(spec)); return from(AdminAccessBuilderSpec.parse(spec));
} }
@ -222,7 +213,7 @@ public class AdminAccess implements Statement {
lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey, resetLoginPassword, lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey, resetLoginPassword,
cryptFunction); cryptFunction);
} catch (IOException e) { } catch (IOException e) {
throw Throwables.propagate(e); throw propagate(e);
} }
} }
} }
@ -379,7 +370,8 @@ public class AdminAccess implements Statement {
} }
public AdminAccess init(Configuration configuration) { public AdminAccess init(Configuration configuration) {
Builder builder = AdminAccess.builder(configuration.cryptFunction()); Builder builder = AdminAccess.builder();
builder.cryptFunction(config.getCryptFunction() != null ? config.getCryptFunction() : configuration.cryptFunction());
builder.adminUsername(config.getAdminUsername() != null ? config.getAdminUsername() : configuration builder.adminUsername(config.getAdminUsername() != null ? config.getAdminUsername() : configuration
.defaultAdminUsername().get()); .defaultAdminUsername().get());
builder.adminFullName(config.getAdminFullName() != null ? config.getAdminFullName() : builder.adminUsername); builder.adminFullName(config.getAdminFullName() != null ? config.getAdminFullName() : builder.adminUsername);
@ -407,14 +399,10 @@ public class AdminAccess implements Statement {
checkNotNull(family, "family"); checkNotNull(family, "family");
if (family == OsFamily.WINDOWS) if (family == OsFamily.WINDOWS)
throw new UnsupportedOperationException("windows not yet implemented"); throw new UnsupportedOperationException("windows not yet implemented");
checkArgument(!"root".equals(config.getAdminUsername()), "cannot create admin user 'root'; " + checkState(
"ensure jclouds is not running as root, or specify an explicit non-root username in AdminAccess"); !any(newArrayList(config.getAdminUsername(), config.getAdminPassword(), config.getAdminPublicKey(),
if (Iterables.any( config.getAdminPrivateKey(), config.getLoginPassword()), isNull()), "please call init() first");
Lists.newArrayList(config.getAdminUsername(), config.getAdminPassword(), config.getAdminPublicKey(), checkNotRoot(config.getAdminUsername());
config.getAdminPrivateKey(), config.getLoginPassword()), Predicates.isNull()))
init(new DefaultConfiguration());
checkNotNull(config.getAdminUsername(), "adminUsername");
checkNotNull(config.getAdminPassword(), "adminPassword"); checkNotNull(config.getAdminPassword(), "adminPassword");
checkNotNull(config.getAdminPublicKey(), "adminPublicKey"); checkNotNull(config.getAdminPublicKey(), "adminPublicKey");
checkNotNull(config.getAdminPrivateKey(), "adminPrivateKey"); checkNotNull(config.getAdminPrivateKey(), "adminPrivateKey");
@ -435,16 +423,21 @@ public class AdminAccess implements Statement {
statements.add(SudoStatements.createWheel()); statements.add(SudoStatements.createWheel());
userBuilder.group("wheel"); userBuilder.group("wheel");
} }
statements.add(userBuilder.build().cryptFunction(config.getCryptFunction())); statements.add(userBuilder.cryptFunction(config.getCryptFunction()).build());
if (config.shouldLockSsh()) if (config.shouldLockSsh())
statements.add(SshStatements.lockSshd()); statements.add(lockSshd());
if (config.shouldResetLoginPassword()) { if (config.shouldResetLoginPassword()) {
statements.add(ShadowStatements.resetLoginUserPasswordTo(config.getLoginPassword()).cryptFunction( statements.add(resetLoginUserPasswordTo(config.getCryptFunction(), config.getLoginPassword()));
config.getCryptFunction()));
} }
return new StatementList(statements.build()).render(family); return new StatementList(statements.build()).render(family);
} }
private static String checkNotRoot(String user) {
checkArgument(!"root".equals(checkNotNull(user, "adminUsername")), "cannot create admin user 'root'; "
+ "ensure jclouds is not running as root, or specify an explicit non-root username in AdminAccess");
return user;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder builder2 = new StringBuilder(); StringBuilder builder2 = new StringBuilder();
@ -453,6 +446,4 @@ public class AdminAccess implements Statement {
.append(shouldGrantSudoToAdminUser()).append("]"); .append(shouldGrantSudoToAdminUser()).append("]");
return builder2.toString(); return builder2.toString();
} }
} }

View File

@ -1,120 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.scriptbuilder.statements.login;
import static com.google.common.base.Charsets.UTF_8;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.crypto.Sha512Crypt;
import org.jclouds.crypto.SshKeys;
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
/**
*
* @author Adrian Cole
*
*/
@Singleton
public class DefaultConfiguration implements Configuration {
private final Supplier<String> defaultAdminUsername = Suppliers.ofInstance(System.getProperty("user.name"));
private final Supplier<Map<String, String>> defaultAdminSshKeys = new Supplier<Map<String, String>>() {
@Override
public Map<String, String> get() {
try {
return ImmutableMap.of(
"public", Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa.pub"), UTF_8),
"private", Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa"), UTF_8));
} catch (IOException e) {
return SshKeys.generate();
}
}
};
/**
* Cheap, lightweight, low-security password generator.
*
* @see <a href=
* "http://www.java-forums.org/java-lang/7355-how-create-lightweight-low-security-password-generator.html" />
*/
public enum PasswordGenerator implements Supplier<String> {
INSTANCE;
/** Minimum length for a decent password */
public static final int MIN_LENGTH = 10;
/** The random number generator. */
protected static final SecureRandom r = new SecureRandom();
/*
* Set of characters that is valid. Must be printable, memorable, and "won't break HTML" (i.e., not ' <', '>',
* '&', '=', ...). or break shell commands (i.e., not ' <', '>', '$', '!', ...). I, L and O are good to leave out,
* as are numeric zero and one.
*/
public static final char[] goodChar = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-',
'@', };
@Override
public String get() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < MIN_LENGTH; i++) {
sb.append(goodChar[r.nextInt(goodChar.length)]);
}
return sb.toString();
}
}
private final Function<String, String> cryptFunction = Sha512Crypt.function();
@Override
public Supplier<String> defaultAdminUsername() {
return defaultAdminUsername;
}
@Override
public Supplier<Map<String, String>> defaultAdminSshKeys() {
return defaultAdminSshKeys;
}
@Override
public Supplier<String> passwordGenerator() {
return PasswordGenerator.INSTANCE;
}
@Override
public Function<String, String> cryptFunction() {
return cryptFunction;
}
}

View File

@ -23,17 +23,12 @@ import static com.google.common.base.Throwables.propagate;
import static java.lang.String.format; import static java.lang.String.format;
import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.jclouds.scriptbuilder.domain.Statements.exec;
import javax.inject.Named;
import org.jclouds.crypto.Sha512Crypt;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.domain.StatementList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
/** /**
* Replaces the password entry for a user in the shadow file, using SHA-512 * Replaces the password entry for a user in the shadow file, using SHA-512
@ -43,20 +38,12 @@ import com.google.inject.Inject;
*/ */
public class ReplaceShadowPasswordEntry implements Statement { public class ReplaceShadowPasswordEntry implements Statement {
private final Function<String, String> cryptFunction;
private final String login; private final String login;
private final String password; private final String password;
private Function<String, String> cryptFunction = Sha512Crypt.function(); public ReplaceShadowPasswordEntry(Function<String, String> cryptFunction, String login, String password) {
this.cryptFunction = checkNotNull(cryptFunction, "cryptFunction");
@Inject(optional = true)
@Named("CRYPT")
@VisibleForTesting
ReplaceShadowPasswordEntry cryptFunction(Function<String, String> cryptFunction) {
this.cryptFunction = cryptFunction;
return this;
}
public ReplaceShadowPasswordEntry(String login, String password) {
this.login = checkNotNull(login, "login"); this.login = checkNotNull(login, "login");
this.password = checkNotNull(password, "password"); this.password = checkNotNull(password, "password");
} }

View File

@ -18,6 +18,8 @@
*/ */
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import com.google.common.base.Function;
/** /**
* *
@ -25,8 +27,8 @@ package org.jclouds.scriptbuilder.statements.login;
*/ */
public class ReplaceShadowPasswordEntryOfLoginUser extends ReplaceShadowPasswordEntry { public class ReplaceShadowPasswordEntryOfLoginUser extends ReplaceShadowPasswordEntry {
public ReplaceShadowPasswordEntryOfLoginUser(String password) { public ReplaceShadowPasswordEntryOfLoginUser(Function<String, String> cryptFunction, String password) {
super("${SUDO_USER:=${USER}}", password); super(cryptFunction, "${SUDO_USER:=${USER}}", password);
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import com.google.common.base.Function;
/** /**
* Statements used to manipulate the shadow file * Statements used to manipulate the shadow file
@ -27,10 +28,10 @@ package org.jclouds.scriptbuilder.statements.login;
public class ShadowStatements { public class ShadowStatements {
/** /**
* note must be run as root, and will either reset the root password, or * note must be run as root, and will either reset the root password, or whoever sudoed to root.
* whoever sudoed to root.
*/ */
public static ReplaceShadowPasswordEntryOfLoginUser resetLoginUserPasswordTo(String password) { public static ReplaceShadowPasswordEntryOfLoginUser resetLoginUserPasswordTo(Function<String, String> cryptFunction,
return new ReplaceShadowPasswordEntryOfLoginUser(password); String password) {
return new ReplaceShadowPasswordEntryOfLoginUser(cryptFunction, password);
} }
} }

View File

@ -18,17 +18,11 @@
*/ */
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import com.google.common.annotations.VisibleForTesting; import static com.google.common.base.Objects.equal;
import com.google.common.base.Function; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Joiner;
import com.google.common.base.Objects; import java.util.List;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import org.jclouds.crypto.Sha512Crypt;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
@ -37,11 +31,14 @@ import org.jclouds.scriptbuilder.domain.Statements;
import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey; import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
import javax.inject.Named; import com.google.common.base.Function;
import java.util.List; import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import static com.google.common.base.Objects.equal; import com.google.common.base.Strings;
import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
/** /**
* Creates a statement that will add a given user to a machine ("login"), with optional * Creates a statement that will add a given user to a machine ("login"), with optional
@ -60,6 +57,7 @@ public class UserAdd implements Statement {
} }
public static class Builder { public static class Builder {
private Function<String, String> cryptFunction;
private String defaultHome = "/home/users"; private String defaultHome = "/home/users";
private String home; private String home;
private String login; private String login;
@ -70,6 +68,14 @@ public class UserAdd implements Statement {
private String shell = "/bin/bash"; private String shell = "/bin/bash";
private String fullName; private String fullName;
/**
* @see org.jclouds.compute.functions.Sha512Crypt
*/
public Builder cryptFunction(Function<String, String> cryptFunction) {
this.cryptFunction = cryptFunction;
return this;
}
/** /**
* See --home in `man useradd`. * See --home in `man useradd`.
*/ */
@ -132,20 +138,26 @@ public class UserAdd implements Statement {
this.fullName = fullName; this.fullName = fullName;
return this; return this;
} }
public UserAdd build() { public UserAdd build() {
return new UserAdd(login, groups, password, RSAPrivateKey, authorizeRSAPublicKeys, home, defaultHome, shell, fullName); return new UserAdd(cryptFunction, login, groups, password, RSAPrivateKey, authorizeRSAPublicKeys, home,
defaultHome, shell, fullName);
} }
} }
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey, public UserAdd(Function<String, String> cryptFunction, String login, List<String> groups, @Nullable String password,
List<String> authorizeRSAPublicKeys, String defaultHome, String shell) { @Nullable String installRSAPrivateKey, List<String> authorizeRSAPublicKeys, String defaultHome, String shell) {
this(login, groups, password, installRSAPrivateKey, authorizeRSAPublicKeys, null, defaultHome, shell, login); this(cryptFunction, login, groups, password, installRSAPrivateKey, authorizeRSAPublicKeys, null, defaultHome,
shell, login);
} }
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey, public UserAdd(Function<String, String> cryptFunction, String login, List<String> groups, @Nullable String password,
List<String> authorizeRSAPublicKeys, @Nullable String home, String defaultHome, String shell, String fullName) { @Nullable String installRSAPrivateKey, List<String> authorizeRSAPublicKeys, @Nullable String home,
String defaultHome, String shell, String fullName) {
this.login = checkNotNull(login, "login"); this.login = checkNotNull(login, "login");
this.password = password; this.password = password;
this.cryptFunction = password == null ? null : checkNotNull(cryptFunction,
"cryptFunction must be set! ex. org.jclouds.compute.functions.Sha512Crypt.INSTANCE");
this.groups = ImmutableList.copyOf(checkNotNull(groups, "groups")); this.groups = ImmutableList.copyOf(checkNotNull(groups, "groups"));
this.installRSAPrivateKey = installRSAPrivateKey; this.installRSAPrivateKey = installRSAPrivateKey;
this.authorizeRSAPublicKeys = ImmutableList this.authorizeRSAPublicKeys = ImmutableList
@ -156,6 +168,7 @@ public class UserAdd implements Statement {
this.fullName = fullName; this.fullName = fullName;
} }
private final Function<String, String> cryptFunction;
private final String home; private final String home;
private final String defaultHome; private final String defaultHome;
private final String login; private final String login;
@ -166,16 +179,6 @@ public class UserAdd implements Statement {
private final String shell; private final String shell;
private final String fullName; private final String fullName;
private Function<String, String> cryptFunction = Sha512Crypt.function();
@Inject(optional = true)
@Named("CRYPT")
@VisibleForTesting
UserAdd cryptFunction(Function<String, String> cryptFunction) {
this.cryptFunction = cryptFunction;
return this;
}
@Override @Override
public Iterable<String> functionDependencies(OsFamily family) { public Iterable<String> functionDependencies(OsFamily family) {
return ImmutableList.of(); return ImmutableList.of();

View File

@ -19,7 +19,6 @@
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.IOException; import java.io.IOException;
@ -50,10 +49,9 @@ public class AdminAccessTest {
try { try {
assertEquals( assertEquals(
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey") AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
.adminPublicKey("fooPublicKey").adminUsername("foo") .adminPublicKey("fooPublicKey").adminUsername("foo").adminHome("/over/ridden/foo").build()
.adminHome("/over/ridden/foo").build()
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX), .init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
Resources.toString(Resources.getResource("test_adminaccess_params.sh"), Charsets.UTF_8)); Resources.toString(Resources.getResource("test_adminaccess_params.sh"), Charsets.UTF_8));
} finally { } finally {
TestConfiguration.INSTANCE.reset(); TestConfiguration.INSTANCE.reset();
@ -66,8 +64,7 @@ public class AdminAccessTest {
assertEquals( assertEquals(
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey") AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
.adminPublicKey("fooPublicKey").adminUsername("foo").adminFullName("JClouds Foo") .adminPublicKey("fooPublicKey").adminUsername("foo").adminFullName("JClouds Foo")
.adminHome("/over/ridden/foo").build() .adminHome("/over/ridden/foo").build().init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
Resources.toString(Resources.getResource("test_adminaccess_params_and_fullname.sh"), Charsets.UTF_8)); Resources.toString(Resources.getResource("test_adminaccess_params_and_fullname.sh"), Charsets.UTF_8));
} finally { } finally {
@ -75,7 +72,6 @@ public class AdminAccessTest {
} }
} }
public void testOnlyInstallUserUNIX() throws IOException { public void testOnlyInstallUserUNIX() throws IOException {
TestConfiguration.INSTANCE.reset(); TestConfiguration.INSTANCE.reset();
try { try {
@ -83,20 +79,19 @@ public class AdminAccessTest {
AdminAccess.builder().grantSudoToAdminUser(false).authorizeAdminPublicKey(true) AdminAccess.builder().grantSudoToAdminUser(false).authorizeAdminPublicKey(true)
.installAdminPrivateKey(true).lockSsh(false).resetLoginPassword(false).build() .installAdminPrivateKey(true).lockSsh(false).resetLoginPassword(false).build()
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX), .init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
Resources.toString(Resources.getResource("test_adminaccess_plainuser.sh"), Charsets.UTF_8)); Resources.toString(Resources.getResource("test_adminaccess_plainuser.sh"), Charsets.UTF_8));
} finally { } finally {
TestConfiguration.INSTANCE.reset(); TestConfiguration.INSTANCE.reset();
} }
} }
@Test(expectedExceptions = UnsupportedOperationException.class) @Test(expectedExceptions = UnsupportedOperationException.class, expectedExceptionsMessageRegExp = "windows not yet implemented")
public void testCreateWheelWindowsNotSupported() { public void testCreateWheelWindowsNotSupported() {
AdminAccess.standard().init(TestConfiguration.INSTANCE).render(OsFamily.WINDOWS); AdminAccess.standard().init(TestConfiguration.INSTANCE).render(OsFamily.WINDOWS);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "cannot create admin user 'root'; ensure jclouds is not running as root, or specify an explicit non-root username in AdminAccess")
// for issue 682 public void testRootNotAllowed() {
public void testRootNotAllowed() throws IOException {
TestConfiguration.INSTANCE.reset(); TestConfiguration.INSTANCE.reset();
try { try {
AdminAccess.builder().adminUsername("root").build().init(TestConfiguration.INSTANCE).render(OsFamily.UNIX); AdminAccess.builder().adminUsername("root").build().init(TestConfiguration.INSTANCE).render(OsFamily.UNIX);
@ -105,12 +100,13 @@ public class AdminAccessTest {
} }
} }
@Test(expectedExceptions = NullPointerException.class) @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "family")
public void testFamilyRequiredAllowed() throws IOException { public void testFamilyRequiredAllowed() {
AdminAccess.standard().render(null); AdminAccess.standard().render(null);
} }
public void testWhenUninitializedLazyInitWithDefaultConfiguration() throws IOException { @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "please call init\\(\\) first")
public void testIllegalStateExceptionUnlessCalledInit() {
AdminAccess access = AdminAccess.standard(); AdminAccess access = AdminAccess.standard();
// before rendered, holder is empty // before rendered, holder is empty
assertEquals(access.config.getAdminUsername(), null); assertEquals(access.config.getAdminUsername(), null);
@ -119,20 +115,6 @@ public class AdminAccessTest {
assertEquals(access.config.getAdminPrivateKey(), null); assertEquals(access.config.getAdminPrivateKey(), null);
assertEquals(access.config.getLoginPassword(), null); assertEquals(access.config.getLoginPassword(), null);
access.render(OsFamily.UNIX); access.render(OsFamily.UNIX);
// DefaultConfiguration
try {
assertEquals(access.config.getAdminUsername(), System.getProperty("user.name"));
assertNotNull(access.config.getAdminPassword());
assertNotNull(access.config.getAdminPublicKey());
assertNotNull(access.config.getAdminPrivateKey());
assertNotNull(access.config.getLoginPassword());
} catch (AssertionError e) {
throw e;
} catch (Throwable e) {
// we are catching throwables here, in case the test runner doesn't
// have ssh keys setup
}
} }
} }

View File

@ -18,24 +18,34 @@
*/ */
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import static org.testng.Assert.assertEquals;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit")
public class ReplaceShadowPasswordEntryOfLoginUserTest { public class ReplaceShadowPasswordEntryOfLoginUserTest {
Function<String, String> crypt = new Function<String, String>() {
public String apply(String in) {
assertEquals(in, "password");
return "CRYPT";
}
};
public void testWithPasswordUNIX() { public void testWithPasswordUNIX() {
String cmd = new ReplaceShadowPasswordEntryOfLoginUser("bar").render(OsFamily.UNIX); String cmd = new ReplaceShadowPasswordEntryOfLoginUser(crypt, "password").render(OsFamily.UNIX);
assert cmd.startsWith("awk -v user=^${SUDO_USER:=${USER}}: -v password='$6$") : cmd; assert cmd.startsWith("awk -v user=^${SUDO_USER:=${USER}}: -v password='CRYPT") : cmd;
assert cmd assert cmd
.endsWith("' 'BEGIN { FS=OFS=\":\" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}\ntest -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow\n") : cmd; .endsWith("' 'BEGIN { FS=OFS=\":\" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}\ntest -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow\n") : cmd;
} }
@Test(expectedExceptions = UnsupportedOperationException.class) @Test(expectedExceptions = UnsupportedOperationException.class)
public void testAddUserWindowsNotSupported() { public void testAddUserWindowsNotSupported() {
new ReplaceShadowPasswordEntryOfLoginUser("password").render(OsFamily.WINDOWS); new ReplaceShadowPasswordEntryOfLoginUser(crypt, "password").render(OsFamily.WINDOWS);
} }
} }

View File

@ -18,24 +18,34 @@
*/ */
package org.jclouds.scriptbuilder.statements.login; package org.jclouds.scriptbuilder.statements.login;
import static org.testng.Assert.*;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit")
public class ReplaceShadowPasswordEntryTest { public class ReplaceShadowPasswordEntryTest {
Function<String, String> crypt = new Function<String, String>() {
public String apply(String in) {
assertEquals(in, "password");
return "CRYPT";
}
};
public void testWithPasswordUNIX() { public void testWithPasswordUNIX() {
String userAdd = new ReplaceShadowPasswordEntry("foo", "bar").render(OsFamily.UNIX); String userAdd = new ReplaceShadowPasswordEntry(crypt, "foo", "password").render(OsFamily.UNIX);
assert userAdd.startsWith("awk -v user=^foo: -v password='$6$") : userAdd; assert userAdd.startsWith("awk -v user=^foo: -v password='CRYPT") : userAdd;
assert userAdd assert userAdd
.endsWith("' 'BEGIN { FS=OFS=\":\" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.foo\ntest -f /etc/shadow.foo && mv /etc/shadow.foo /etc/shadow\n") : userAdd; .endsWith("' 'BEGIN { FS=OFS=\":\" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.foo\ntest -f /etc/shadow.foo && mv /etc/shadow.foo /etc/shadow\n") : userAdd;
} }
@Test(expectedExceptions = UnsupportedOperationException.class) @Test(expectedExceptions = UnsupportedOperationException.class)
public void testAddUserWindowsNotSupported() { public void testAddUserWindowsNotSupported() {
new ReplaceShadowPasswordEntry("user", "password").render(OsFamily.WINDOWS); new ReplaceShadowPasswordEntry(crypt, "user", "password").render(OsFamily.WINDOWS);
} }
} }

View File

@ -23,6 +23,7 @@ import static org.testng.Assert.assertEquals;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
/** /**
@ -58,9 +59,16 @@ public class UserAddTest {
"mkdir -p /home/users\ngroupadd -f wheel\ngroupadd -f candy\nuseradd -c me -s /bin/bash -g wheel -G candy -m -d /home/users/me me\nchown -R me /home/users/me\n"); "mkdir -p /home/users\ngroupadd -f wheel\ngroupadd -f candy\nuseradd -c me -s /bin/bash -g wheel -G candy -m -d /home/users/me me\nchown -R me /home/users/me\n");
} }
Function<String, String> crypt = new Function<String, String>() {
public String apply(String in) {
assertEquals(in, "password");
return "CRYPT";
}
};
public void testWithPasswordUNIX() { public void testWithPasswordUNIX() {
String userAdd = UserAdd.builder().login("me").password("foo").group("wheel").build().render(OsFamily.UNIX); String userAdd = UserAdd.builder().cryptFunction(crypt).login("me").password("password").group("wheel").build().render(OsFamily.UNIX);
assert userAdd.startsWith("mkdir -p /home/users\ngroupadd -f wheel\nuseradd -c me -s /bin/bash -g wheel -m -d /home/users/me -p '$6$") : userAdd; assert userAdd.startsWith("mkdir -p /home/users\ngroupadd -f wheel\nuseradd -c me -s /bin/bash -g wheel -m -d /home/users/me -p 'CRYPT'") : userAdd;
assert userAdd.endsWith("' me\nchown -R me /home/users/me\n") : userAdd; assert userAdd.endsWith("' me\nchown -R me /home/users/me\n") : userAdd;
} }