133 lines
4.9 KiB
Ceylon
133 lines
4.9 KiB
Ceylon
import ceylon.random {
|
|
DefaultRandom,
|
|
Random
|
|
}
|
|
|
|
Character[] lowerCaseChars = 'a'..'z';
|
|
Character[] upperCaseChars = 'A'..'Z';
|
|
Character[] numsAsChars = '0'..'9';
|
|
Character[] specialChars = "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~".sequence();
|
|
Character[] allChars = lowerCaseChars.append(upperCaseChars)
|
|
.append(numsAsChars)
|
|
.append(specialChars);
|
|
|
|
shared void run() {
|
|
print("2 passwords of size 8");
|
|
print("------");
|
|
generatePasswords(8,2).each(print);
|
|
print("------");
|
|
print("5 passwords of size 12");
|
|
print("------");
|
|
generatePasswords(12,5).each(print);
|
|
print("------");
|
|
print("7 passwords of size 16");
|
|
print("------");
|
|
generatePasswords(16,7).each(print);
|
|
print("------");
|
|
print("2 passwords of size 16 exclude AiX24!~");
|
|
print("------");
|
|
generatePasswords(16,7,"AiX24!~".sequence()).each(print);
|
|
}
|
|
|
|
String[] generatePasswords(Integer numChars, Integer numPasswords, Character[] excludedChars=[])
|
|
=> [for (count in 1..numPasswords) generatePassword(numChars,excludedChars) ];
|
|
|
|
//The generated password(s) must include at least one lower-case letter, one upper-case letter and one character from digits and 'other'.
|
|
String generatePassword(Integer numChars, Character[] excludedChars) {
|
|
"Must provide a password length of at least 4"
|
|
assert(numChars >= 4);
|
|
|
|
value random = DefaultRandom();
|
|
|
|
value fixedPartOfPassword = passwordFragmentForRequirements(random,excludedChars);
|
|
|
|
value randomPart =
|
|
randomPartOfPassword{ random = random;
|
|
index = fixedPartOfPassword.size + 1;
|
|
randomPart = "";
|
|
numChars = numChars;
|
|
candidateChars = filterCharsToExclude(allChars, excludedChars); };
|
|
|
|
value unshuffledPassword = fixedPartOfPassword + randomPart;
|
|
|
|
assert(unshuffledPassword.string.size == numChars);
|
|
|
|
"excluded chars contained"
|
|
assert(! unshuffledPassword.any((char) => excludedChars.contains(char)));
|
|
|
|
"Sizes not equal"
|
|
assert(unshuffledPassword.size == numChars);
|
|
|
|
value shuffledPassword =
|
|
shuffleCharsRecursive(unshuffledPassword.string,"",unshuffledPassword.indexes().sequence());
|
|
|
|
"Sizes not equal"
|
|
assert(shuffledPassword.string.size == numChars);
|
|
|
|
"set of characters not equal"
|
|
assert(set(unshuffledPassword) == set(shuffledPassword));
|
|
|
|
return shuffledPassword;
|
|
}
|
|
|
|
String passwordFragmentForRequirements(Random random, Character[] excludedChars)
|
|
=> String({filterAndUse(random,lowerCaseChars, excludedChars),
|
|
filterAndUse(random,upperCaseChars,excludedChars),
|
|
filterAndUse(random,numsAsChars,excludedChars),
|
|
filterAndUse(random,specialChars,excludedChars)});
|
|
|
|
String randomPartOfPassword(Random random,
|
|
Integer index,
|
|
String randomPart,
|
|
Integer numChars,
|
|
Character[] candidateChars) {
|
|
if(index <= numChars) {
|
|
value candidateCharsIndex = random.nextInteger(candidateChars.size);
|
|
assert(exists allChar = candidateChars[candidateCharsIndex]);
|
|
return randomPartOfPassword{ random=random;
|
|
index=index+1;
|
|
randomPart = randomPart + allChar.string;
|
|
numChars =numChars;
|
|
candidateChars = candidateChars; };
|
|
}
|
|
|
|
return randomPart;
|
|
}
|
|
|
|
String shuffleCharsRecursive(String orig,String shuffledString,Integer[] remainingOrigIndexes) {
|
|
value random = DefaultRandom();
|
|
|
|
if (nonempty constRemainingIndexes = remainingOrigIndexes) {
|
|
value randomIndex = random.nextInteger(orig.size);
|
|
|
|
assert(exists nextChar = orig[randomIndex]);
|
|
|
|
String newShuffledString;
|
|
Integer[] newRemainingIndexes;
|
|
|
|
if (constRemainingIndexes.contains(randomIndex)) {
|
|
newShuffledString = shuffledString + nextChar.string;
|
|
newRemainingIndexes = constRemainingIndexes.filter((index) => randomIndex != index).sequence();
|
|
} else {
|
|
newShuffledString = shuffledString;
|
|
newRemainingIndexes = constRemainingIndexes;
|
|
}
|
|
|
|
return shuffleCharsRecursive{ orig=orig;
|
|
shuffledString=newShuffledString;
|
|
remainingOrigIndexes=newRemainingIndexes; };
|
|
}
|
|
|
|
return shuffledString;
|
|
}
|
|
|
|
Character filterAndUse(Random random, Character[] chars, Character[] excludedChars) {
|
|
value charsToUse = filterCharsToExclude(chars, excludedChars);
|
|
value charToUseIndex = random.nextInteger(charsToUse.size);
|
|
assert( exists charToUse = charsToUse[charToUseIndex]);
|
|
return charToUse;
|
|
}
|
|
|
|
Character[] filterCharsToExclude(Character[] chars, Character[] charsToExclude)
|
|
=> chars.filter((char) => ! charsToExclude.contains(char)).sequence();
|