Skip to content
Permalink
Browse files
JCLOUDS-1362: Proper password generation with custom constraints for …
…each cloud
  • Loading branch information
nacx committed Jan 8, 2018
1 parent fa63f6b commit 036c68f2de1296585dcccb1e29b301cdf78f7607
Showing 9 changed files with 96 additions and 34 deletions.
@@ -53,6 +53,7 @@
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
import org.jclouds.net.domain.IpPermission;
import org.jclouds.util.PasswordGenerator;

import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
@@ -102,6 +103,21 @@ protected void configure() {
bind(new TypeLiteral<SecurityGroupExtension>() {
}).to(AzureComputeSecurityGroupExtension.class);
}

@Provides
@Singleton
protected PasswordGenerator.Config providePasswordGenerator() {
// Guest passwords must be between 6-72 characters long.
// Must contain an upper case character.
// Must contain a lower case character.
// Must contain a numeric digit.
// Must contain a special character. Control characters are not allowed.
return new PasswordGenerator()
.lower().min(2).max(10)
.upper().min(2).max(10)
.numbers().min(2).max(10)
.symbols().min(2).max(10);
}

@Provides
@Singleton
@@ -52,7 +52,7 @@
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;
import org.jclouds.util.Passwords;
import org.jclouds.util.PasswordGenerator;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
@@ -84,6 +84,7 @@ public class CreateResourcesThenCreateNodes extends CreateNodesWithGroupEncodedI
private final String defaultVnetAddressPrefix;
private final String defaultSubnetAddressPrefix;
private final TemplateToAvailabilitySet templateToAvailabilitySet;
private final PasswordGenerator.Config passwordGenerator;

@Inject
protected CreateResourcesThenCreateNodes(
@@ -95,14 +96,16 @@ protected CreateResourcesThenCreateNodes(
AzureComputeApi api, @Named(DEFAULT_VNET_ADDRESS_SPACE_PREFIX) String defaultVnetAddressPrefix,
@Named(DEFAULT_SUBNET_ADDRESS_PREFIX) String defaultSubnetAddressPrefix,
LoadingCache<ResourceGroupAndNameAndIngressRules, String> securityGroupMap,
TemplateToAvailabilitySet templateToAvailabilitySet) {
TemplateToAvailabilitySet templateToAvailabilitySet,
PasswordGenerator.Config passwordGenerator) {
super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
this.api = api;
this.securityGroupMap = securityGroupMap;
this.defaultVnetAddressPrefix = defaultVnetAddressPrefix;
this.defaultSubnetAddressPrefix = defaultSubnetAddressPrefix;
this.templateToAvailabilitySet = templateToAvailabilitySet;
this.passwordGenerator = passwordGenerator;
}

@Override
@@ -141,7 +144,7 @@ private void generatePasswordIfNoneProvided(Template template) {
TemplateOptions options = template.getOptions();
if (options.getLoginPassword() == null) {
Optional<String> passwordOptional = template.getImage().getDefaultCredentials().getOptionalPassword();
options.overrideLoginPassword(passwordOptional.or(Passwords.generate()));
options.overrideLoginPassword(passwordOptional.or(passwordGenerator.generate()));
}
}

@@ -24,6 +24,7 @@
import org.jclouds.azurecompute.arm.domain.Subnet;
import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.azurecompute.arm.features.SubnetApi;
import org.jclouds.util.PasswordGenerator;
import org.testng.annotations.Test;

import com.google.common.collect.ImmutableList;
@@ -101,7 +102,7 @@ public void testProviderSpecificNetworkOptions() {
}

private static CreateResourcesThenCreateNodes strategy(AzureComputeApi api) {
return new CreateResourcesThenCreateNodes(null, null, null, null, null, api, null, null, null, null);
return new CreateResourcesThenCreateNodes(null, null, null, null, null, api, null, null, null, null, new PasswordGenerator().lower());
}

private static String netResource(String resource) {
@@ -43,7 +43,6 @@
import org.apache.jclouds.oneandone.rest.domain.options.GenericQueryOptions;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.options.TemplateOptions;
@@ -53,7 +52,7 @@
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Passwords;
import org.jclouds.util.PasswordGenerator;

@Singleton
public class OneandoneComputeServiceAdapter implements ComputeServiceAdapter<Server, HardwareFlavour, SingleServerAppliance, DataCenter> {
@@ -65,13 +64,16 @@ public class OneandoneComputeServiceAdapter implements ComputeServiceAdapter<Ser
private final CleanupResources cleanupResources;
private final OneAndOneApi api;
private final Predicate<Server> waitServerUntilAvailable;
private final PasswordGenerator.Config passwordGenerator;

@Inject
OneandoneComputeServiceAdapter(OneAndOneApi api, CleanupResources cleanupResources,
@Named(POLL_PREDICATE_SERVER) Predicate<Server> waitServerUntilAvailable) {
@Named(POLL_PREDICATE_SERVER) Predicate<Server> waitServerUntilAvailable,
PasswordGenerator.Config passwordGenerator) {
this.api = api;
this.cleanupResources = cleanupResources;
this.waitServerUntilAvailable = waitServerUntilAvailable;
this.passwordGenerator = passwordGenerator;
}

@Override
@@ -83,7 +85,7 @@ public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(Stri
Server updateServer = null;

final String loginUser = isNullOrEmpty(options.getLoginUser()) ? "root" : options.getLoginUser();
final String password = options.hasLoginPassword() ? options.getLoginPassword() : Passwords.generate();
final String password = options.hasLoginPassword() ? options.getLoginPassword() : passwordGenerator.generate();
final String privateKey = options.hasLoginPrivateKey() ? options.getPrivateKey() : null;
final org.jclouds.compute.domain.Image image = template.getImage();
final int[] inboundPorts = template.getOptions().getInboundPorts();
@@ -92,7 +94,6 @@ public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(Stri
List<? extends Volume> volumes = hardware.getVolumes();
List<Hdd.CreateHdd> hdds = new ArrayList<Hdd.CreateHdd>();

int i = 1;
for (final Volume volume : volumes) {
try {
//check if the bootable device has enough size to run the appliance(image).
@@ -122,7 +123,6 @@ public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(Stri
}

try {
List<? extends Processor> processors = hardware.getProcessors();
org.apache.jclouds.oneandone.rest.domain.Hardware.CreateHardware hardwareRequest
= org.apache.jclouds.oneandone.rest.domain.Hardware.CreateHardware.create(cores, 1, ram, hdds);
final Server.CreateServer serverRequest = Server.CreateServer.builder()
@@ -51,12 +51,13 @@
import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
import org.jclouds.domain.Location;
import org.jclouds.util.PasswordGenerator;

import static org.jclouds.util.Predicates2.retry;

public class OneAndOneComputeServiceContextModule extends
ComputeServiceAdapterContextModule<Server, HardwareFlavour, SingleServerAppliance, DataCenter> {

@SuppressWarnings("unchecked")
@Override
protected void configure() {
super.configure();
@@ -85,7 +86,18 @@ protected void configure() {

bind(new TypeLiteral<Function<DataCenter, Location>>() {
}).to(DataCenterToLocation.class);

}

@Provides
@Singleton
protected PasswordGenerator.Config providePasswordGenerator() {
// Guest passwords must contain more than 8 characters using upper case letters,
// numbers and other special symbols.
return new PasswordGenerator()
.lower().min(2).max(5)
.upper().min(2).max(5)
.numbers().min(2).max(5)
.symbols().min(2).max(5);
}

@Provides
@@ -17,39 +17,36 @@
package org.apache.jclouds.profitbricks.rest.compute;

import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Predicate;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
import com.google.common.collect.Lists;
import static com.google.common.util.concurrent.Futures.getUnchecked;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.inject.Inject;
import static java.lang.String.format;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_NIC;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_SERVER;
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.util.ComputeServiceUtils.getPortRangesFromList;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.jclouds.profitbricks.rest.ProfitBricksApi;
import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningJob;
import org.apache.jclouds.profitbricks.rest.compute.concurrent.ProvisioningManager;
import org.apache.jclouds.profitbricks.rest.compute.function.ProvisionableToImage;
import org.apache.jclouds.profitbricks.rest.compute.strategy.TemplateWithDataCenter;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_DATACENTER;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_NIC;
import static org.apache.jclouds.profitbricks.rest.config.ProfitBricksComputeProperties.POLL_PREDICATE_SERVER;
import org.apache.jclouds.profitbricks.rest.domain.DataCenter;
import org.apache.jclouds.profitbricks.rest.domain.FirewallRule;
import org.apache.jclouds.profitbricks.rest.domain.Image;
@@ -67,10 +64,7 @@
import org.apache.jclouds.profitbricks.rest.ids.ServerRef;
import org.apache.jclouds.profitbricks.rest.ids.VolumeRef;
import org.apache.jclouds.profitbricks.rest.util.Trackables;
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
import org.jclouds.compute.ComputeServiceAdapter;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Processor;
@@ -80,13 +74,23 @@
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.util.ComputeServiceUtils;
import static org.jclouds.compute.util.ComputeServiceUtils.getPortRangesFromList;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Passwords;
import org.jclouds.util.PasswordGenerator;

import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.inject.Inject;

@Singleton
public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<ServerInDataCenter, Hardware, Provisionable, Location> {
@@ -106,6 +110,7 @@ public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<
private final ListeningExecutorService executorService;
private final ProvisioningJob.Factory jobFactory;
private final ProvisioningManager provisioningManager;
private final PasswordGenerator.Config passwordGenerator;
private List<DataCenter> datacetners;

private static final Integer DEFAULT_LAN_ID = 1;
@@ -121,7 +126,8 @@ public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<
@Named(POLL_PREDICATE_NIC) Predicate<NicRef> waitNICUntilAvailable,
Trackables trackables,
ProvisioningJob.Factory jobFactory,
ProvisioningManager provisioningManager) {
ProvisioningManager provisioningManager,
PasswordGenerator.Config passwordGenerator) {
this.api = api;
this.waitDcUntilAvailable = waitDcUntilAvailable;
this.waitVolumeUntilAvailable = waitVolumeUntilAvailable;
@@ -133,6 +139,7 @@ public class ProfitBricksComputeServiceAdapter implements ComputeServiceAdapter<
this.executorService = executorService;
this.jobFactory = jobFactory;
this.provisioningManager = provisioningManager;
this.passwordGenerator = passwordGenerator;
this.datacetners = ImmutableList.of();
}

@@ -149,7 +156,7 @@ protected NodeAndInitialCredentials<ServerInDataCenter> createNodeWithGroupEncod
TemplateOptions options = template.getOptions();
final String loginUser = isNullOrEmpty(options.getLoginUser()) ? "root" : options.getLoginUser();
final String pubKey = options.getPublicKey();
final String password = options.hasLoginPassword() ? options.getLoginPassword() : Passwords.generate();
final String password = options.hasLoginPassword() ? options.getLoginPassword() : passwordGenerator.generate();
final org.jclouds.compute.domain.Image image = template.getImage();
final int[] inboundPorts = template.getOptions().getInboundPorts();

@@ -70,6 +70,8 @@
import org.jclouds.lifecycle.Closer;
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
import org.jclouds.util.PasswordGenerator;

import static org.jclouds.util.Predicates2.retry;

public class ProfitBricksComputeServiceContextModule extends
@@ -106,6 +108,16 @@ protected void configure() {
bind(new TypeLiteral<ImageExtension>() {
}).to(ProfitBricksImageExtension.class);
}

@Provides
@Singleton
protected PasswordGenerator.Config providePasswordGenerator() {
return new PasswordGenerator()
.lower().min(2).max(10).exclude("ilowyz".toCharArray())
.upper().min(2).max(10).exclude("IOWYZ".toCharArray())
.numbers().min(2).max(10).exclude("10".toCharArray())
.symbols().count(0);
}

@Provides
@Singleton
@@ -20,9 +20,10 @@
import com.google.common.base.Enums;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Set;

import org.apache.jclouds.profitbricks.rest.util.Preconditions;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import org.jclouds.util.Passwords;

@AutoValue
public abstract class Volume extends Trackable {
@@ -197,7 +198,7 @@ public CreatePayload build() {
CreatePayload payload = autoBuild();

if (payload.imagePassword() != null) {
checkArgument(Passwords.isValidPassword(payload.imagePassword()), "Password's format is not valid");
Preconditions.checkPassword(payload.imagePassword());
}

checkArgument(
@@ -87,4 +87,14 @@ public static void checkSize(Float size) {
checkArgument(size > 1, "Storage size must be > 1GB");
}

private static final int VALID_PASSWORD_MIN_LENGTH = 8;
private static final int VALID_PASSWORD_MAX_LENGTH = 50;
private static final String PASSWORD_FORMAT = String.format(
"[a-zA-Z0-9][^iIloOwWyYzZ10]{%d,%d}", VALID_PASSWORD_MIN_LENGTH - 1, VALID_PASSWORD_MAX_LENGTH);
private static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_FORMAT);

public static void checkPassword(String password) {
checkArgument(PASSWORD_PATTERN.matcher(password).matches(), "Password must be between 8 and 50 characters, "
+ "only a-z, A-Z, 0-9 without characters i, I, l, o, O, w, W, y, Y, z, Z and 1, 0");
}
}

0 comments on commit 036c68f

Please sign in to comment.