fcs-utils/Generators.cs

327 lines
13 KiB
C#
Raw Normal View History

2020-10-19 11:22:06 +02:00
// ***********************************************************************
2022-02-24 11:57:37 +01:00
// Assembly : FCS.Lib.Utility
2020-10-19 11:22:06 +02:00
// Author : FH
// Created : 2020-07-01
//
// Last Modified By : FH
2022-02-24 11:40:38 +01:00
// Last Modified On : 02-24-2022
2020-10-19 11:22:06 +02:00
// ***********************************************************************
2022-02-04 08:39:06 +01:00
// <copyright file="Generators.cs" company="FCS">
2022-02-24 10:11:45 +01:00
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
2022-03-14 14:23:33 +01:00
// it under the terms of the Affero GNU General Public License as
2022-02-24 10:11:45 +01:00
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
2022-01-01 17:00:15 +01:00
//
2022-02-24 10:11:45 +01:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2022-03-14 14:23:33 +01:00
// Affero GNU General Public License for more details.
2022-01-01 17:00:15 +01:00
//
2022-03-14 14:23:33 +01:00
// You should have received a copy of the Affero GNU General Public License
2022-02-24 14:00:27 +01:00
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
2022-02-24 10:11:45 +01:00
// </copyright>
// <summary></summary>
2020-10-19 11:22:06 +02:00
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
2022-02-24 11:40:38 +01:00
namespace FCS.Lib.Utility
2020-10-19 11:22:06 +02:00
{
/// <summary>
2022-02-04 08:39:06 +01:00
/// Class Generators
/// <remarks>generates varioous kinds of random strings. </remarks>
2020-10-19 11:22:06 +02:00
/// </summary>
public static class Generators
{
/// <summary>
2021-11-06 11:16:18 +01:00
/// Shorts the URL generator.
2020-10-19 11:22:06 +02:00
/// </summary>
/// <returns>System.String.</returns>
public static string ShortUrlGenerator()
{
return ShortUrlGenerator(6);
}
/// <summary>
2021-11-06 11:16:18 +01:00
/// Randoms the string.
2020-10-19 11:22:06 +02:00
/// </summary>
/// <param name="length">The lengt h.</param>
/// <returns>System.String.</returns>
2022-02-24 10:11:45 +01:00
/// <remarks>derived from https://sourceforge.net/projects/shorturl-dotnet/</remarks>
2020-10-19 11:22:06 +02:00
public static string ShortUrlGenerator(int length)
{
const string charsLower = "cdfghjkmnpqrstvwxyz";
const string charsUpper = "BCDFGHJKLMNPQRSTVWXYZ-_";
const string charsNumeric = "23456789";
// Create a local array containing supported short-url characters
// grouped by types.
var charGroups = new[]
{
charsLower.ToCharArray(),
charsUpper.ToCharArray(),
charsNumeric.ToCharArray()
};
// Use this array to track the number of unused characters in each
// character group.
var charsLeftInGroup = new int[charGroups.Length];
// Initially, all characters in each group are not used.
for (var i = 0; i < charsLeftInGroup.Length; i++)
charsLeftInGroup[i] = charGroups[i].Length;
// Use this array to track (iterate through) unused character groups.
var leftGroupsOrder = new int[charGroups.Length];
// Initially, all character groups are not used.
for (var i = 0; i < leftGroupsOrder.Length; i++)
leftGroupsOrder[i] = i;
// Using our private randomizer
var random = RandomSeed();
// This array will hold short-url characters.
// Allocate appropriate memory for the short-url.
var shortUrl = new char[random.Next(length, length)];
// Index of the last non-processed group.
var lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
// Generate short-url characters one at a time.
for (var i = 0; i < shortUrl.Length; i++)
{
// If only one character group remained unprocessed, process it;
// otherwise, pick a random character group from the unprocessed
// group list. To allow a special character to appear in the
// first position, increment the second parameter of the Next
// function call by one, i.e. lastLeftGroupsOrderIdx + 1.
int nextLeftGroupsOrderIdx;
if (lastLeftGroupsOrderIdx == 0)
nextLeftGroupsOrderIdx = 0;
else
nextLeftGroupsOrderIdx = random.Next(0,
lastLeftGroupsOrderIdx);
// Get the actual index of the character group, from which we will
// pick the next character.
var nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];
// Get the index of the last unprocessed characters in this group.
var lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;
// If only one unprocessed character is left, pick it; otherwise,
// get a random character from the unused character list.
var nextCharIdx = lastCharIdx == 0 ? 0 : random.Next(0, lastCharIdx + 1);
// Add this character to the short-url.
shortUrl[i] = charGroups[nextGroupIdx][nextCharIdx];
// If we processed the last character in this group, start over.
if (lastCharIdx == 0)
{
2022-02-24 10:11:45 +01:00
charsLeftInGroup[nextGroupIdx] =
charGroups[nextGroupIdx].Length;
2020-10-19 11:22:06 +02:00
}
// There are more unprocessed characters left.
else
{
// Swap processed character with the last unprocessed character
// so that we don't pick it until we process all characters in
// this group.
if (lastCharIdx != nextCharIdx)
{
2022-02-24 10:11:45 +01:00
var temp = charGroups[nextGroupIdx][lastCharIdx];
charGroups[nextGroupIdx][lastCharIdx] =
charGroups[nextGroupIdx][nextCharIdx];
charGroups[nextGroupIdx][nextCharIdx] = temp;
2020-10-19 11:22:06 +02:00
}
// Decrement the number of unprocessed characters in
// this group.
charsLeftInGroup[nextGroupIdx]--;
}
// If we processed the last group, start all over.
if (lastLeftGroupsOrderIdx == 0)
{
lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
}
// There are more unprocessed groups left.
else
{
// Swap processed group with the last unprocessed group
// so that we don't pick it until we process all groups.
if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
{
2022-02-24 10:11:45 +01:00
var temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
leftGroupsOrder[lastLeftGroupsOrderIdx] =
leftGroupsOrder[nextLeftGroupsOrderIdx];
leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
2020-10-19 11:22:06 +02:00
}
// Decrement the number of unprocessed groups.
lastLeftGroupsOrderIdx--;
}
}
// Convert password characters into a string and return the result.
return new string(shortUrl);
}
2021-11-06 11:16:18 +01:00
/// <summary>
/// Generates the username.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
2020-10-19 11:22:06 +02:00
public static string GenerateUsername(StringOptions options = null)
{
options ??= new StringOptions
{
RequiredLength = 10,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
RequiredUniqueChars = 4,
RequireNonLetterOrDigit = false,
RequireNonAlphanumeric = false
};
return GenerateRandomString(options);
}
2021-11-06 11:16:18 +01:00
/// <summary>
/// Generates the password.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
2020-10-19 11:22:06 +02:00
public static string GeneratePassword(StringOptions options = null)
{
options ??= new StringOptions
{
RequiredLength = 10,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
RequiredUniqueChars = 4,
RequireNonLetterOrDigit = true,
RequireNonAlphanumeric = true
};
return GenerateRandomString(options);
}
2021-11-06 11:16:18 +01:00
/// <summary>
/// Generates the random text.
/// </summary>
/// <param name="length">The length.</param>
/// <returns>System.String.</returns>
2020-10-19 11:22:06 +02:00
public static string GenerateRandomText(int length)
{
const string consonants = "bdfghjklmnprstvyBDFGHJKLMNPRSTVY";
const string vowels = "aeiouAEIOU";
var rndString = "";
var randomNum = RandomSeed();
while (rndString.Length < length)
{
rndString += consonants[randomNum.Next(consonants.Length)];
if (rndString.Length < length)
rndString += vowels[randomNum.Next(vowels.Length)];
}
return rndString;
}
/// <summary>
2021-11-06 11:16:18 +01:00
/// Generates the random password.
2020-10-19 11:22:06 +02:00
/// </summary>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
public static string GenerateRandomString(StringOptions options = null)
{
options ??= new StringOptions
{
RequiredLength = 10,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
RequiredUniqueChars = 4,
RequireNonLetterOrDigit = true,
RequireNonAlphanumeric = true
};
var randomChars = new[]
{
"ABCDEFGHJKLMNOPQRSTUVWXYZ", // uppercase
"abcdefghijkmnopqrstuvwxyz", // lowercase
"0123456789", // digits
"!@$?_-" // non-alphanumeric
};
// Using our private randomizer
var rand = RandomSeed();
var chars = new List<char>();
if (options.RequireUppercase)
chars.Insert(rand.Next(0, chars.Count),
randomChars[0][rand.Next(0, randomChars[0].Length)]);
if (options.RequireLowercase)
chars.Insert(rand.Next(0, chars.Count),
randomChars[1][rand.Next(0, randomChars[1].Length)]);
if (options.RequireDigit)
chars.Insert(rand.Next(0, chars.Count),
randomChars[2][rand.Next(0, randomChars[2].Length)]);
if (options.RequireNonAlphanumeric)
chars.Insert(rand.Next(0, chars.Count),
randomChars[3][rand.Next(0, randomChars[3].Length)]);
var rcs = randomChars[rand.Next(0, randomChars.Length)];
for (var i = chars.Count;
i < options.RequiredLength
|| chars.Distinct().Count() < options.RequiredUniqueChars;
i++)
chars.Insert(rand.Next(0, chars.Count),
rcs[rand.Next(0, rcs.Length)]);
return new string(chars.ToArray());
}
/// <summary>
2021-11-06 11:16:18 +01:00
/// Randoms the seed.
2020-10-19 11:22:06 +02:00
/// </summary>
/// <returns>Random.</returns>
2022-02-24 10:11:45 +01:00
/// <remarks>derived from https://sourceforge.net/projects/shorturl-dotnet/</remarks>
2020-10-19 11:22:06 +02:00
public static Random RandomSeed()
{
// As the default randomizer is based on the current time
// so it produces the same "random" number within a second
// Use a crypto randomizer to create the seed value
// 4-byte array to fill with random bytes
var randomBytes = new byte[4];
// Generate 4 random bytes.
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(randomBytes);
}
// Convert 4 bytes into a 32-bit integer value.
var seed = ((randomBytes[0] & 0x7f) << 24) |
(randomBytes[1] << 16) |
(randomBytes[2] << 8) |
randomBytes[3];
// Return a truly randomized random generator
return new Random(seed);
}
}
}