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)));
+    }
     
 }