You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/01/30 04:45:52 UTC
[06/10] incubator-brooklyn git commit: Create Password Sensor
Create Password Sensor
A simple initialiser for creating passwords in yaml as a sensor
Added a password generating function in the Identifiers class which uses
secure random
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/50f70fe5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/50f70fe5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/50f70fe5
Branch: refs/heads/master
Commit: 50f70fe5be5bdac65f446e8309fcf703f36c08ba
Parents: b28ba02
Author: Duncan Grant <du...@cloudsoftcorp.com>
Authored: Thu Jan 21 16:14:25 2016 +0000
Committer: Duncan Grant <du...@cloudsoftcorp.com>
Committed: Thu Jan 21 17:42:19 2016 +0000
----------------------------------------------------------------------
.../sensor/password/CreatePasswordSensor.java | 59 ++++++++++
.../password/CreatePasswordSensorTest.java | 67 ++++++++++++
.../apache/brooklyn/util/text/Identifiers.java | 107 +++++++++++++++----
.../brooklyn/util/text/IdentifiersTest.java | 14 ++-
4 files changed, 223 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/50f70fe5/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java
new file mode 100644
index 0000000..0352071
--- /dev/null
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.brooklyn.core.sensor.password;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.AddSensor;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.text.Identifiers;
+
+public class CreatePasswordSensor extends AddSensor<String> {
+
+ public static final ConfigKey<Integer> PASSWORD_LENGTH = ConfigKeys.newIntegerConfigKey("password.length", "The length of the password to be created", 12);
+
+ public static final ConfigKey<String> ACCEPTABLE_CHARS = ConfigKeys.newStringConfigKey("password.chars", "The characters allowed in password");
+
+ private Integer passwordLength;
+ private String accecptableChars;
+
+ public CreatePasswordSensor(Map<String, String> params) {
+ this(ConfigBag.newInstance(params));
+ }
+
+ public CreatePasswordSensor(ConfigBag params) {
+ super(params);
+ passwordLength = params.get(PASSWORD_LENGTH);
+ accecptableChars = params.get(ACCEPTABLE_CHARS);
+ }
+
+ @Override
+ public void apply(EntityLocal entity) {
+ super.apply(entity);
+
+ String password = accecptableChars == null
+ ? Identifiers.makeRandomPassword(passwordLength)
+ : Identifiers.makeRandomPassword(passwordLength, accecptableChars);
+
+ entity.sensors().set(sensor, password);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/50f70fe5/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java
new file mode 100644
index 0000000..5223ce5
--- /dev/null
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.brooklyn.core.sensor.password;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class CreatePasswordSensorTest {
+
+ final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString");
+ private EntityInternal entity;
+ private TestApplication app;
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ app = TestApplication.Factory.newManagedInstanceForTests();
+
+ entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+ .location(app.newLocalhostProvisioningLocation().obtain()));
+ app.start(ImmutableList.<Location>of());
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAll(app.getManagementContext());
+ }
+
+ @Test
+ public void testCreatePasswordCreatesAPasswordOfDefaultLength() {
+
+ final CreatePasswordSensor sensor = new CreatePasswordSensor(ConfigBag.newInstance()
+ .configure(CreatePasswordSensor.SENSOR_NAME, SENSOR_STRING.getName()));
+ sensor.apply(entity);
+
+ String password = entity.getAttribute(SENSOR_STRING);
+ Asserts.assertEquals(password.length(), 12);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/50f70fe5/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
index c2ec4a5..25fb588 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
@@ -18,16 +18,24 @@
*/
package org.apache.brooklyn.util.text;
+import java.security.SecureRandom;
import java.util.Random;
+import com.google.common.base.Preconditions;
+
public class Identifiers {
private static Random random = new Random();
- /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
- public static final String JAVA_GOOD_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
- /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
- public static final String JAVA_GOOD_NONSTART_CHARS = JAVA_GOOD_START_CHARS+"1234567890";
+ public static final String UPPER_CASE_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ public static final String LOWER_CASE_ALPHA = "abcdefghijklmnopqrstuvwxyz";
+ public static final String NUMERIC = "1234567890";
+ public static final String NON_ALPHA_NUMERIC = "!$%@#";
+
+ /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
+ public static final String JAVA_GOOD_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA +"_";
+ /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
+ public static final String JAVA_GOOD_NONSTART_CHARS = JAVA_GOOD_START_CHARS+NUMERIC;
/** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
public static final String JAVA_GOOD_SEGMENT_REGEX = "["+JAVA_GOOD_START_CHARS+"]"+"["+JAVA_GOOD_NONSTART_CHARS+"]*";
/** regex for a java package or class name using "good" chars, that is no accents or funny unicodes.
@@ -37,15 +45,21 @@ public class Identifiers {
public static final String JAVA_GOOD_PACKAGE_OR_CLASS_REGEX = "("+JAVA_GOOD_SEGMENT_REGEX+"\\."+")*"+JAVA_GOOD_SEGMENT_REGEX;
/** as {@link #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX} but allowing a dollar sign inside a class name (e.g. Foo$1) */
public static final String JAVA_GOOD_BINARY_REGEX = JAVA_GOOD_PACKAGE_OR_CLASS_REGEX+"(\\$["+JAVA_GOOD_NONSTART_CHARS+"]+)*";
-
- public static final String JAVA_GENERATED_IDENTIFIER_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- public static final String JAVA_GENERATED_IDENTIFIERNONSTART_CHARS = JAVA_GENERATED_IDENTIFIER_START_CHARS+"1234567890";
+
+ public static final String JAVA_GENERATED_IDENTIFIER_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA;
+
+ public static final String JAVA_GENERATED_IDENTIFIERNONSTART_CHARS = JAVA_GENERATED_IDENTIFIER_START_CHARS+NUMERIC;
public static final String BASE64_VALID_CHARS = JAVA_GENERATED_IDENTIFIERNONSTART_CHARS+"+=";
-
- public static final String ID_VALID_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- public static final String ID_VALID_NONSTART_CHARS = ID_VALID_START_CHARS+"1234567890";
-
+
+ public static final String ID_VALID_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA;
+ public static final String ID_VALID_NONSTART_CHARS = ID_VALID_START_CHARS+ NUMERIC;
+
+ public static final String PASSWORD_VALID_CHARS = NON_ALPHA_NUMERIC + ID_VALID_NONSTART_CHARS;
+
+ // We only create a secure random when it is first used
+ private static Random secureRandom = null;
+
/** makes a random id string (letters and numbers) of the given length;
* starts with letter (upper or lower) so can be used as java-id;
* tests ensure random distribution, so random ID of length 5
@@ -91,15 +105,53 @@ public class Identifiers {
return new String(id);
}
+ /**
+ *
+ * @param length of password to be returned
+ * @return randomly generated password containing at least one of each upper case,
+ * lower case, numeric, and non alpha-numeric characters. Hopefully this is acceptible
+ * for most password schemes.
+ */
+ public static String makeRandomPassword(final int length) {
+ return makeRandomPassword(length, UPPER_CASE_ALPHA, LOWER_CASE_ALPHA, NUMERIC, NON_ALPHA_NUMERIC, PASSWORD_VALID_CHARS);
+ }
+
+ /**
+ * A fairly slow but hopefully secure way to randomly select characters for a password
+ * Takes a pool of acceptible characters using the first set in the pool for the first character,
+ * second set for the second character, ..., nth set for all remaining character.
+ *
+ * @param length length of password
+ * @param passwordValidCharsPool pool of acceptable character sets
+ * @return a randomly generated password
+ */
+ public static String makeRandomPassword(final int length, String... passwordValidCharsPool) {
+ Preconditions.checkState(length >= passwordValidCharsPool.length);
+ int l = 0;
+
+ char[] id = new char[length];
+
+ for(int i = 0; i < passwordValidCharsPool.length; i++){
+ id[l++] = pickRandomCharFrom(passwordValidCharsPool[i]);
+ }
+
+ String remainingValidChars = passwordValidCharsPool[passwordValidCharsPool.length - 1];
+
+ while(l < length) {
+ id[l++] = pickRandomCharFrom(remainingValidChars);
+ }
+ return new String(id);
+ }
+
/** creates a short identifier comfortable in java and OS's, given an input hash code
* <p>
- * result is always at least of length 1, shorter if the hash is smaller */
+ * result is always at least of length 1, shorter if the hash is smaller */
public static String makeIdFromHash(long d) {
StringBuffer result = new StringBuffer();
if (d<0) d=-d;
// correction for Long.MIN_VALUE
if (d<0) d=-(d+1000);
-
+
result.append(ID_VALID_START_CHARS.charAt((int)(d % (26+26))));
d /= (26+26);
while (d!=0) {
@@ -108,31 +160,31 @@ public class Identifiers {
}
return result.toString();
}
-
+
/** makes a random id string (letters and numbers) of the given length;
* starts with letter (upper or lower) so can be used as java-id;
- * tests ensure random distribution, so random ID of length 5
- * is about 2^29 possibilities
+ * tests ensure random distribution, so random ID of length 5
+ * is about 2^29 possibilities
* <p>
- * implementation is efficient, uses char array, and
+ * implementation is efficient, uses char array, and
* makes one call to random per 5 chars; makeRandomId(5)
* takes about 4 times as long as a simple Math.random call,
* or about 50 times more than a simple x++ instruction;
* in other words, it's appropriate for contexts where random id's are needed,
- * but use efficiently (ie cache it per object), and
+ * but use efficiently (ie cache it per object), and
* prefer to use a counter where feasible
**/
public static String makeRandomJavaId(int l) {
// copied from Monterey util's com.cloudsoftcorp.util.StringUtils.
// TODO should share code with makeRandomId, just supplying different char sets (though the char sets in fact are the same..)
- //this version is 30-50% faster than the old double-based one,
+ //this version is 30-50% faster than the old double-based one,
//which computed a random every 3 turns --
//takes about 600 ns to do id of len 10, compared to 10000 ns for old version [on 1.6ghz machine]
if (l<=0) return "";
char[] id = new char[l];
int d = random.nextInt( (26+26) * (26+26+10) * (26+26+10) * (26+26+10) * (26+26+10));
- int i = 0;
+ int i = 0;
id[i] = JAVA_GENERATED_IDENTIFIER_START_CHARS.charAt(d % (26+26));
d /= (26+26);
if (++i<l) do {
@@ -151,6 +203,7 @@ public class Identifiers {
public static double randomDouble() {
return random.nextDouble();
}
+
public static long randomLong() {
return random.nextLong();
}
@@ -173,7 +226,6 @@ public class Identifiers {
byte[] buf = new byte[length];
return randomBytes(buf);
}
-
public static String makeRandomBase64Id(int length) {
StringBuilder s = new StringBuilder();
while (length>0) {
@@ -182,6 +234,7 @@ public class Identifiers {
}
return s.toString();
}
+
public static String getBase64IdFromValue(long value) {
return getBase64IdFromValue(value, 10);
}
@@ -210,7 +263,6 @@ public class Identifiers {
idx = idx >> 6;
}
}
-
public static boolean isValidToken(String token, String validStartChars, String validSubsequentChars) {
if (token==null || token.length()==0) return false;
if (validStartChars.indexOf(token.charAt(0))==-1) return false;
@@ -218,4 +270,15 @@ public class Identifiers {
if (validSubsequentChars.indexOf(token.charAt(i))==-1) return false;
return true;
}
+
+ private static char pickRandomCharFrom(String validChars) {
+ return validChars.charAt(getSecureRandom().nextInt(validChars.length()));
+ }
+
+ private static Random getSecureRandom() {
+ if(secureRandom == null) {
+ secureRandom = new SecureRandom();
+ }
+ return secureRandom;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/50f70fe5/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
index 7b4f999..37c6443 100644
--- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
+++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
@@ -21,8 +21,6 @@ package org.apache.brooklyn.util.text;
import java.util.HashSet;
import java.util.Set;
-import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
@@ -98,5 +96,17 @@ public class IdentifiersTest {
Assert.assertTrue("foo".matches(Identifiers.JAVA_GOOD_BINARY_REGEX));
Assert.assertTrue("foo.bar.Baz$1".matches(Identifiers.JAVA_GOOD_BINARY_REGEX));
}
+
+ @Test
+ public void testPassword() {
+ String upper = "ABC";
+ String numbers = "123";
+ String symbols = "!£$";
+ String id1 = Identifiers.makeRandomPassword(4, upper, numbers, symbols, Identifiers.PASSWORD_VALID_CHARS);
+
+ Assert.assertTrue(upper.contains(id1.substring(0,1)));
+ Assert.assertTrue(numbers.contains(id1.substring(1,2)));
+ Assert.assertTrue(symbols.contains(id1.substring(2,3)));
+ }
}