diff --git a/CapyKit/Attributes/ParameterAttribute.cs b/CapyKit/Attributes/ParameterAttribute.cs
new file mode 100644
index 0000000..e2dc68b
--- /dev/null
+++ b/CapyKit/Attributes/ParameterAttribute.cs
@@ -0,0 +1,108 @@
+namespace CapyKit.Attributes;
+
+/// Attribute describing parameters used in automatically generating search forms for paged models.
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+public class ParameterAttribute : Attribute
+{
+ #region Members
+
+ //
+
+ #endregion Members
+
+ #region Properties
+
+ ///
+ /// Gets or sets the name parameter as passed in a POST method from the Browser. This can be hardcoded at the class level.
+ ///
+ /// The name of the parameter.
+ public string ParameterName { get; private set; }
+
+ ///
+ /// Gets the description of the parameter. This serves as a human-readable explanation or label for the parameter's purpose or usage.
+ ///
+ /// The description text of the parameter.
+ public string Description { get; private set; }
+
+ /// Gets or sets the FontAwesome icon name.
+ /// The FontAwesome icon name.
+ public string IconName { get; private set; }
+
+ /// Gets or sets the ordinal position used when displaying the parameter.
+ /// The ordinal position.
+ public int Ordinal { get; private set; }
+
+ #endregion Properties
+
+ #region Constructors
+
+ /// Constructor.
+ /// The name of the parameter.
+ public ParameterAttribute(string parameterName)
+ {
+ this.ParameterName = parameterName;
+ this.Description = string.Empty;
+ this.IconName = string.Empty;
+ this.Ordinal = 0;
+ }
+
+ /// Constructor.
+ /// The name of the system.
+ /// Name of the resource that contains the description of
+ /// the parameter.
+ public ParameterAttribute(string systemName, string description)
+ {
+ this.ParameterName = systemName;
+ this.IconName = string.Empty;
+ this.Description = description;
+ this.Ordinal = 0;
+ }
+
+ /// Constructor.
+ /// The name of the system.
+ /// Name of the resource that contains the description of
+ /// the parameter.
+ /// The FontAwesome icon name.
+ public ParameterAttribute(string systemName, string description, string iconName)
+ {
+ this.ParameterName = systemName;
+ this.Description = description;
+ this.IconName = iconName;
+ this.Ordinal = 0;
+ }
+
+ /// Constructor.
+ /// The name of the system.
+ /// Name of the resource that contains the description of
+ /// the parameter.
+ /// The FontAwesome icon name.
+ /// The ordinal position.
+ public ParameterAttribute(string systemName, string description, string iconName, int ordinal)
+ {
+ this.ParameterName = systemName;
+ this.Description = description;
+ this.IconName = iconName;
+ this.Ordinal = ordinal;
+ }
+
+ /// Constructor.
+ /// The name of the system.
+ /// Name of the resource that contains the description of
+ /// the parameter.
+ /// The ordinal position.
+ public ParameterAttribute(string systemName, string description, int ordinal)
+ {
+ this.ParameterName = systemName;
+ this.Description = description;
+ this.IconName = string.Empty;
+ this.Ordinal = ordinal;
+ }
+
+ #endregion Constructors
+
+ #region Methods
+
+ //
+
+ #endregion Methods
+}
\ No newline at end of file
diff --git a/CapyKit/Color.cs b/CapyKit/Color.cs
deleted file mode 100644
index 87a48ff..0000000
--- a/CapyKit/Color.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace CapyKit
-{
- /// An object representing a color.
- public class Color
- {
- }
-}
diff --git a/CapyKit/EncryptedValue.cs b/CapyKit/EncryptedValue.cs
index dff9016..20d49a5 100644
--- a/CapyKit/EncryptedValue.cs
+++ b/CapyKit/EncryptedValue.cs
@@ -6,6 +6,10 @@ using System.Threading.Tasks;
namespace CapyKit
{
+ ///
+ /// Holds a value that has been encrypted.
+ ///
+ ///
public class EncryptedValue
{
#region Members
@@ -16,6 +20,9 @@ namespace CapyKit
#region Properties
+ ///
+ /// The encrypted value.
+ ///
public T Value { get; set; }
#endregion
diff --git a/CapyKit/Helpers/EnumerationHelper.cs b/CapyKit/Helpers/EnumerationHelper.cs
new file mode 100644
index 0000000..5017a8d
--- /dev/null
+++ b/CapyKit/Helpers/EnumerationHelper.cs
@@ -0,0 +1,102 @@
+using CapyKit.Extensions;
+
+namespace CapyKit.Helpers;
+
+/// A class that contains methods for managing objects.
+public static class EnumerationHelper
+{
+ #region Methods
+
+ /// Gets enumeration value from its integer value.
+ /// Generic type parameter representing an enumeration.
+ /// The integer value.
+ /// The enumeration value.
+ public static T GetEnumerationValue(int value) where T : struct, Enum
+ {
+ if (value >= 0)
+ {
+ return (T)(object)value;
+ }
+
+ return default(T);
+ }
+
+ /// Gets enumeration value from a string value.
+ /// Generic type parameter represending an enumeration.
+ /// The string value.
+ /// The enumeration value.
+ public static T GetEnumerationValue(string value) where T : struct, Enum
+ {
+ foreach (var t in DeconstructNonDefault())
+ {
+ var match = string.Equals(t.GetName(), value, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(t.GetPrettyName(), value, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(t.GetName(), value.Replace(" ", ""));
+
+ if (match)
+ {
+ return t;
+ }
+ }
+
+ return default(T);
+ }
+
+ ///
+ /// An Enum extension method that deconstructs the given enumeration into a dictionary of its
+ /// integer value and its name.
+ ///
+ /// Generic type parameter.
+ ///
+ /// An enumerator that allows foreach to be used to process deconstruct in this collection.
+ ///
+ public static IEnumerable Deconstruct() where T : struct, Enum
+ {
+ var dictionary = new Dictionary();
+ var enumerationType = typeof(T);
+
+ foreach (var e in Enum.GetValues(enumerationType))
+ {
+ yield return (T)e;
+ }
+ }
+
+ ///
+ /// An Enum extension method that deconstructs the given enumeration into a dictionary of its
+ /// integer value and its name.
+ ///
+ /// Generic type parameter.
+ /// The enumeration to act on.
+ ///
+ /// An enumerator that allows foreach to be used to process deconstruct in this collection.
+ ///
+ public static IEnumerable Deconstruct(this T enumeration) where T : struct, Enum
+ {
+ return Deconstruct();
+ }
+
+ /// An Enum extension method that deconstruct non default.
+ /// Generic type parameter.
+ ///
+ /// An enumerator that allows foreach to be used to process deconstruct non default in this
+ /// collection.
+ ///
+ public static IEnumerable DeconstructNonDefault() where T : struct, Enum
+ {
+ return Deconstruct().Filter(item => item.GetValue() == 0);
+ }
+
+ /// An Enum extension method that deconstruct non default.
+ /// Generic type parameter.
+ /// The enumeration to act on.
+ ///
+ /// An enumerator that allows foreach to be used to process deconstruct non default in this
+ /// collection.
+ ///
+ public static IEnumerable DeconstructNonDefault(this T enumeration) where T : struct, Enum
+ {
+ return DeconstructNonDefault();
+ }
+
+ #endregion Methods
+}
\ No newline at end of file
diff --git a/CapyKit/Helpers/PropertyHelper.cs b/CapyKit/Helpers/PropertyHelper.cs
new file mode 100644
index 0000000..694b5ac
--- /dev/null
+++ b/CapyKit/Helpers/PropertyHelper.cs
@@ -0,0 +1,128 @@
+using System.Linq.Expressions;
+using System.Reflection;
+using CapyKit.Attributes;
+
+namespace CapyKit.Helpers;
+
+/// A class that contains methods for managing programatic access to C# proerties through reflection.
+/// Generic type parameter.
+public static class PropertyHelper
+{
+ ///
+ /// Gets the reflected value of an instanced
+ /// object.
+ ///
+ /// Type of the property.
+ /// The instanced object.
+ /// Name of the property.
+ /// The reflected property value.
+ public static TProperty GetPropertyValue(T obj, string propertyName)
+ {
+ return (TProperty)GetProperty(obj, propertyName).GetValue(obj);
+ }
+
+ ///
+ /// Gets the reflected of an instanced
+ /// object.
+ ///
+ /// The instanced object.
+ /// Name of the property.
+ /// The reflected .
+ public static PropertyInfo GetProperty(T obj, string propertyName)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException("obj",
+ string.Format("The value provided for the property {0} was null.", propertyName));
+ }
+
+ return obj.GetType().GetProperties().Where(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
+ .FirstOrDefault();
+ }
+
+ /// Gets the reflected value of an instanced object property value as described by a .
+ /// Type of the property.
+ /// The instanced object.
+ /// The property selector function that identifies the property.
+ /// The reflected property value.
+ public static TProperty GetPropertyValue(T obj, Expression> selector)
+ {
+ return (TProperty)GetProperty(selector).GetValue(obj);
+ }
+
+ ///
+ /// Gets the reflected of an object as
+ /// described by a .
+ ///
+ /// Thrown when the does
+ /// not return a
+ /// of type
+ /// .
+ /// Type of the property.
+ /// The property selector function that identifies the property.
+ /// The reflected .
+ public static PropertyInfo GetProperty(Expression> selector)
+ {
+ Expression body = selector;
+
+ if (body is LambdaExpression)
+ {
+ body = ((LambdaExpression)body).Body;
+ }
+
+ switch (body.NodeType)
+ {
+ case ExpressionType.MemberAccess:
+ return (PropertyInfo)((MemberExpression)body).Member;
+
+ default:
+ throw new InvalidOperationException(
+ "The selector provided did not return a member or property accessor.");
+ }
+ }
+
+ ///
+ /// Gets an attribute from a given property.
+ ///
+ /// Type of the property.
+ /// The property selector function that identifies the property.
+ /// The enumeration description of the property.
+ public static EnumerationDescriptionAttribute GetEnumerationDescription(
+ Expression> selector)
+ {
+ return GetPropertyCustomAttribute(selector);
+ }
+
+ ///
+ /// Gets a attribute from the given property.
+ ///
+ /// Type of the property.
+ /// The property selector function that identifies the property.
+ /// The value format of the property.
+ public static ValueFormatAttribute GetValueFormat(Expression> selector)
+ {
+ return GetPropertyCustomAttribute(selector);
+ }
+
+ ///
+ /// Gets a attribute from the given property.
+ ///
+ /// Type of the property.
+ /// The property selector function that identifies the property.
+ /// The paged link parameter of the property.
+ public static ParameterAttribute GetPagedLinkParameter(Expression> selector)
+ {
+ return GetPropertyCustomAttribute(selector);
+ }
+
+ /// Gets custom from a given property.
+ /// Type of the attribute.
+ /// Type of the property.
+ /// The property selector function that identifies the property.
+ /// The custom of the property.
+ public static TAttribute GetPropertyCustomAttribute(Expression> selector)
+ where TAttribute : Attribute
+ {
+ return GetProperty(selector).GetCustomAttribute(typeof(TAttribute)) as TAttribute;
+ }
+}
\ No newline at end of file
diff --git a/CapyKit/Helpers/SecurityHelper.cs b/CapyKit/Helpers/SecurityHelper.cs
index 680bf47..af929e4 100644
--- a/CapyKit/Helpers/SecurityHelper.cs
+++ b/CapyKit/Helpers/SecurityHelper.cs
@@ -30,7 +30,7 @@ namespace CapyKit.Helpers
#endregion Members
#region Methods
-
+
public static bool CompareHashedPassword(Password existingPassword, string password, string salt,
params object[] args)
where T : IPasswordAlgorithm
@@ -71,7 +71,8 @@ namespace CapyKit.Helpers
params object[] args)
where T : IPasswordAlgorithm
{
- if (string.IsNullOrWhiteSpace(storedHash) || string.IsNullOrEmpty(password) || string.IsNullOrWhiteSpace(salt))
+ if (string.IsNullOrWhiteSpace(storedHash) || string.IsNullOrEmpty(password) ||
+ string.IsNullOrWhiteSpace(salt))
{
return false;
}
@@ -88,48 +89,52 @@ namespace CapyKit.Helpers
}
///
- /// Compares a plaintext password with a persisted hash and salt.
+ /// Compares a hashed password with a plaintext password by using a specified hashing algorithm .
+ /// This method requires the existing hash, the plaintext password to compare, and the associated salt value.
///
- /// The password hashing algorithm.
- /// The persisted hash.
+ /// The type of the password hashing algorithm implementing .
+ /// The stored hash of the password as a byte array.
/// The plaintext password to verify.
- /// The persisted salt.
- /// Arguments used to construct the password algorithm.
- /// when the password matches the persisted hash.
+ /// The salt used during the password hashing process as a byte array.
+ /// Optional arguments required for constructing the password hashing algorithm instance.
+ ///
+ /// if the hashed password matches the stored hash, otherwise.
+ ///
public static bool CompareHashedPassword(byte[] storedHash, string password, byte[] salt,
params object[] args)
where T : IPasswordAlgorithm
{
- if (storedHash == null || storedHash.Length == 0 || string.IsNullOrEmpty(password) || salt == null || salt.Length == 0)
+ if (storedHash == null || storedHash.Length == 0 || string.IsNullOrEmpty(password) || salt == null ||
+ salt.Length == 0)
{
return false;
}
var candidatePassword = GetPassword(password, salt, args);
return candidatePassword is not null && candidatePassword.Hash.Length == storedHash.Length &&
- CryptographicOperations.FixedTimeEquals(storedHash, candidatePassword.Hash);
+ CryptographicOperations.FixedTimeEquals(storedHash, candidatePassword.Hash);
}
-
+
public static bool CompareHashedPassword(Password existingPassword, string password, string salt,
IPasswordAlgorithm algorithm, params object[] args)
{
var saltBytes = Convert.FromBase64String(salt);
-
+
return CompareHashedPassword(existingPassword, password, saltBytes, algorithm, args);
}
///
/// Compares an unencrypted with a stored, encrypted .
- /// This method uses the specified to retrieve the hashed version
- /// of the and then compares it with the .
+ /// This method uses the specified password hashing algorithm of type to hash the
+ /// and then compares it with the .
///
- /// The existing, encrypted password.
- /// The unencrypted password to be compared.
- /// The salt value used in password hashing.
- /// The password hashing algorithm.
- /// Additional arguments required for constructing the password algorithm instance.
+ /// The type of the password hashing algorithm, implementing .
+ /// The stored, encrypted password to compare against.
+ /// The plain text password to be compared.
+ /// The salt value used for hashing the plain text password.
+ /// Optional additional arguments required for initializing the password hashing algorithm.
///
- /// if hash comparison succeeds, if it fails.
+ /// if the password hashes match, otherwise .
///
public static bool CompareHashedPassword(Password existingPassword, string password, byte[] salt,
IPasswordAlgorithm algorithm, params object[] args)
@@ -172,7 +177,8 @@ namespace CapyKit.Helpers
/// instance of created using any optional constructor arguments provided.
///
///
- public static Password GetPassword(string password, int saltSize, params object[] args) where T : IPasswordAlgorithm
+ public static Password GetPassword(string password, int saltSize, params object[] args)
+ where T : IPasswordAlgorithm
{
var salt = GetRandomBytes(saltSize);
return GetPassword(password, salt, args);
@@ -298,8 +304,10 @@ namespace CapyKit.Helpers
"No valid characters were provided, so all valid caharacters will be assumed.");
validCharacters = new[]
{
- ValidCharacterCollection.Lowercase, ValidCharacterCollection.Uppercase,
- ValidCharacterCollection.Numbers, ValidCharacterCollection.Special
+ ValidCharacterCollection.Lowercase,
+ ValidCharacterCollection.Uppercase,
+ ValidCharacterCollection.Numbers,
+ ValidCharacterCollection.Special
};
}
@@ -474,4 +482,4 @@ namespace CapyKit.Helpers
[EnumerationDescriptionAttribute(SecurityHelper.SPECIAL_CHARACTERS)]
Special,
}
-}
+}
\ No newline at end of file
diff --git a/CapyKit/Password.cs b/CapyKit/Password.cs
index e0288f0..76ba48e 100644
--- a/CapyKit/Password.cs
+++ b/CapyKit/Password.cs
@@ -67,6 +67,17 @@ namespace CapyKit
this.Salt = salt;
this.Algorithm = algorithm;
}
+
+ public Password(string password, string salt, IPasswordAlgorithm algorithm, params object[] args)
+ {
+ // We know there will always be a salt, so we can prepend it to h
+ var saltBytes = Convert.FromBase64String(salt);
+ var augmented = args.Prepend(saltBytes).ToArray();
+
+ this.Hash = algorithm.Encrypt(password, augmented);
+ this.Salt = saltBytes;
+ this.Algorithm = algorithm;
+ }
#region Methods
diff --git a/Docs/assets/favicon.svg b/Docs/assets/favicon.svg
new file mode 100644
index 0000000..04fb406
--- /dev/null
+++ b/Docs/assets/favicon.svg
@@ -0,0 +1,96 @@
+
+
+
+
diff --git a/Docs/assets/logo.svg b/Docs/assets/logo.svg
new file mode 100644
index 0000000..052bdfd
--- /dev/null
+++ b/Docs/assets/logo.svg
@@ -0,0 +1,98 @@
+
+
+
+
diff --git a/Docs/docfx.json b/Docs/docfx.json
index 6faf4ec..a2fbbe3 100644
--- a/Docs/docfx.json
+++ b/Docs/docfx.json
@@ -27,6 +27,13 @@
]
}
],
+ "resource": [
+ {
+ "files": [
+ "assets/**"
+ ]
+ }
+ ],
"output": "_site",
"template": [
"default",
@@ -36,6 +43,8 @@
"globalMetadata": {
"_appName": "CapyKit",
"_appTitle": "CapyKit Documentation",
+ "_appLogoPath": "assets/logo.svg",
+ "_appFaviconPath": "assets/favicon.svg",
"_enableSearch": true,
"pdf": false
}