From adc3ff08fe2dd01e013e24cfdf974e5ca0f57b15 Mon Sep 17 00:00:00 2001 From: Dirk Eisenberg Date: Mon, 12 Sep 2022 23:51:40 +0200 Subject: [PATCH] Added support for Sha256 hasing and Base64 conversion of RowKey --- .../IStorageContext.cs | 3 +- .../IStorageContextQuery.cs | 2 +- .../nVirtualValueEncoding.cs | 11 ++ .../ITS24VirtualRowKey.cs | 113 ++++++++++++++++++ .../AssemblyInfo.cs | 5 +- .../Attributes/VirtualRowKeyAttribute.cs | 10 +- .../Extensions/StringExternsions.cs | 39 ++++++ .../Internal/StorageContextQueryNow.cs | 1 - .../Internal/StorageContextQueryWithFilter.cs | 1 - .../StorageContextQueryWithPartitionKey.cs | 1 - .../Internal/StorageContextQueryWithRowKey.cs | 1 - .../Serialization/TableEntityBuilder.cs | 19 ++- .../Serialization/TableEntityDynamic.cs | 12 +- .../StoargeEntityMapper.cs | 5 +- .../StorageContextAttributeMapping.cs | 13 +- .../StorageContextQueryEntities.cs | 1 - 16 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 CoreHelpers.WindowsAzure.Storage.Table.Abstractions/nVirtualValueEncoding.cs create mode 100644 CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS24VirtualRowKey.cs create mode 100644 CoreHelpers.WindowsAzure.Storage.Table/Extensions/StringExternsions.cs diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContext.cs b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContext.cs index c79db17..20cec1d 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContext.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContext.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; namespace CoreHelpers.WindowsAzure.Storage.Table { @@ -27,6 +26,8 @@ public interface IStorageContext : IDisposable void AddEntityMapper(Type entityType, String partitionKeyFormat, String rowKeyFormat, String tableName); + void AddEntityMapper(Type entityType, String partitionKeyFormat, String rowKeyFormat, nVirtualValueEncoding rowKeyEncoding, String tableName); + IStorageContext CreateChildContext(); IStorageContext EnableAutoCreateTable(); diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContextQuery.cs b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContextQuery.cs index d91725a..fa028be 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContextQuery.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/IStorageContextQuery.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading.Tasks; -namespace CoreHelpers.WindowsAzure.Storage.Table.Abstractions +namespace CoreHelpers.WindowsAzure.Storage.Table { public interface IStorageContextQueryNow { diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/nVirtualValueEncoding.cs b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/nVirtualValueEncoding.cs new file mode 100644 index 0000000..6f6200c --- /dev/null +++ b/CoreHelpers.WindowsAzure.Storage.Table.Abstractions/nVirtualValueEncoding.cs @@ -0,0 +1,11 @@ +using System; +namespace CoreHelpers.WindowsAzure.Storage.Table +{ + public enum nVirtualValueEncoding + { + None, + Base64, + Sha256 + } +} + diff --git a/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS24VirtualRowKey.cs b/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS24VirtualRowKey.cs new file mode 100644 index 0000000..c858eb9 --- /dev/null +++ b/CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS24VirtualRowKey.cs @@ -0,0 +1,113 @@ +using System; +using CoreHelpers.WindowsAzure.Storage.Table.Attributes; +using CoreHelpers.WindowsAzure.Storage.Table.Serialization; +using CoreHelpers.WindowsAzure.Storage.Table.Tests.Extensions; +using CoreHelpers.WindowsAzure.Storage.Table.Tests.Models; + +namespace CoreHelpers.WindowsAzure.Storage.Table.Tests +{ + [Storable(Tablename = "VirtualRowKeyNone")] + [VirtualRowKey("{{RK}}")] + public class VirtualRowKeyNone + { + [PartitionKey] + public string PK { get; set; } = String.Empty; + + public string RK { get; set; } = String.Empty; + } + + [Storable(Tablename = "VirtualRowKeyNone")] + [VirtualRowKey("{{RK}}-{{RK2}}")] + public class VirtualRowKeyCombinedNone: VirtualRowKeyNone + { + public string RK2 { get; set; } = String.Empty; + } + + [Storable(Tablename = "VirtualRowKeyNone")] + [VirtualRowKey("{{RK}}", nVirtualValueEncoding.Base64)] + public class VirtualRowKeyBase64 : VirtualRowKeyNone + {} + + [Storable(Tablename = "VirtualRowKeyNone")] + [VirtualRowKey("{{RK}}", nVirtualValueEncoding.Sha256)] + public class VirtualRowKeySha256: VirtualRowKeyNone + { } + + public class ITS24VirtualRowKey + { + private readonly IStorageContext _rootContext; + + public ITS24VirtualRowKey(IStorageContext context) + { + _rootContext = context; + } + + [Fact] + public void VerifyVirtualRowKeyNoneEncoding() + { + using (var scp = _rootContext.CreateChildContext()) + { + // set the tablename context + scp.SetTableContext(); + + // configure the entity mapper + scp.AddAttributeMapper(); + + // check the entity + var entity = TableEntityDynamic.ToEntity(new VirtualRowKeyNone() { PK = "P01", RK = "R01" }, scp); + Assert.Equal("R01", entity.RowKey); + } + } + + [Fact] + public void VerifyVirtualRowKeyNoneEncodingCombined() + { + using (var scp = _rootContext.CreateChildContext()) + { + // set the tablename context + scp.SetTableContext(); + + // configure the entity mapper + scp.AddAttributeMapper(); + + // check the entity + var entity = TableEntityDynamic.ToEntity(new VirtualRowKeyCombinedNone() { PK = "P01", RK = "R01", RK2 = "CMB" }, scp); + Assert.Equal("R01-CMB", entity.RowKey); + } + } + + [Fact] + public void VerifyVirtualRowKeyBase64Encoding() + { + using (var scp = _rootContext.CreateChildContext()) + { + // set the tablename context + scp.SetTableContext(); + + // configure the entity mapper + scp.AddAttributeMapper(); + + // check the entity + var entity = TableEntityDynamic.ToEntity(new VirtualRowKeyBase64() { PK = "P01", RK = "R01" }, scp); + Assert.Equal("UjAx", entity.RowKey); + } + } + + [Fact] + public void VerifyVirtualRowKeySha256Encoding() + { + using (var scp = _rootContext.CreateChildContext()) + { + // set the tablename context + scp.SetTableContext(); + + // configure the entity mapper + scp.AddAttributeMapper(); + + // check the entity + var entity = TableEntityDynamic.ToEntity(new VirtualRowKeySha256() { PK = "P01", RK = "R01" }, scp); + Assert.Equal("e0a64b0b6d837fa4edc328ab9ddea0e3e7e0e4f715304c1d6bf3d0adc9d5292a", entity.RowKey); + } + } + } +} \ No newline at end of file diff --git a/CoreHelpers.WindowsAzure.Storage.Table/AssemblyInfo.cs b/CoreHelpers.WindowsAzure.Storage.Table/AssemblyInfo.cs index 6df5311..575305e 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/AssemblyInfo.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Runtime.CompilerServices; [assembly: AssemblyCompany("Core Helpers")] [assembly: AssemblyProduct("WindowsAzure.Storage.Table")] @@ -12,4 +13,6 @@ [assembly: AssemblyConfiguration("Debug")] #else [assembly: AssemblyConfiguration("Release")] -#endif \ No newline at end of file +#endif + +[assembly: InternalsVisibleTo("CoreHelpers.WindowsAzure.Storage.Table.Tests")] \ No newline at end of file diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Attributes/VirtualRowKeyAttribute.cs b/CoreHelpers.WindowsAzure.Storage.Table/Attributes/VirtualRowKeyAttribute.cs index d8c1985..ee1f57a 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Attributes/VirtualRowKeyAttribute.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Attributes/VirtualRowKeyAttribute.cs @@ -1,14 +1,18 @@ using System; +using CoreHelpers.WindowsAzure.Storage.Table; namespace CoreHelpers.WindowsAzure.Storage.Table.Attributes -{ +{ [AttributeUsage(AttributeTargets.Class)] public class VirtualRowKeyAttribute : Attribute { public string RowKeyFormat { get; set; } - - public VirtualRowKeyAttribute(string RowKeyFormat) { + + public nVirtualValueEncoding Encoding { get; set; } + + public VirtualRowKeyAttribute(string RowKeyFormat, nVirtualValueEncoding Encoding = nVirtualValueEncoding.None) { this.RowKeyFormat = RowKeyFormat; + this.Encoding = Encoding; } } } diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Extensions/StringExternsions.cs b/CoreHelpers.WindowsAzure.Storage.Table/Extensions/StringExternsions.cs new file mode 100644 index 0000000..01d36a7 --- /dev/null +++ b/CoreHelpers.WindowsAzure.Storage.Table/Extensions/StringExternsions.cs @@ -0,0 +1,39 @@ +using System; +using System.Text; + +namespace CoreHelpers.WindowsAzure.Storage.Table.Extensions +{ + public static class StringExternsions + { + public static string ToBase64(this string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + + public static string ToSha256(this string value) + { + // convert the string + byte[] toBytes = Encoding.ASCII.GetBytes(value); + + // generate the SHA1 key + var sha256 = global::System.Security.Cryptography.SHA256.Create(); + var hash = sha256.ComputeHash(toBytes); + + // done + return GenerateHexStringFromBytes(hash); + } + + private static string GenerateHexStringFromBytes(byte[] bytes) + { + var sb = new StringBuilder(); + foreach (byte b in bytes) + { + var hex = b.ToString("x2"); + sb.Append(hex); + } + return sb.ToString(); + } + } +} + diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryNow.cs b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryNow.cs index 974c402..e556dc0 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryNow.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryNow.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Azure.Data.Tables; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; using CoreHelpers.WindowsAzure.Storage.Table.Serialization; namespace CoreHelpers.WindowsAzure.Storage.Table.Internal diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithFilter.cs b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithFilter.cs index 996c879..90ab772 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithFilter.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithFilter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; using CoreHelpers.WindowsAzure.Storage.Table.Serialization; namespace CoreHelpers.WindowsAzure.Storage.Table.Internal diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithPartitionKey.cs b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithPartitionKey.cs index 5832f5c..48fec38 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithPartitionKey.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithPartitionKey.cs @@ -1,5 +1,4 @@ using System; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; namespace CoreHelpers.WindowsAzure.Storage.Table.Internal { diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithRowKey.cs b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithRowKey.cs index 06837ae..06cc797 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithRowKey.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Internal/StorageContextQueryWithRowKey.cs @@ -1,5 +1,4 @@ using System; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; namespace CoreHelpers.WindowsAzure.Storage.Table.Internal { diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityBuilder.cs b/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityBuilder.cs index 48a5d67..bed65fa 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityBuilder.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityBuilder.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Azure.Data.Tables; +using CoreHelpers.WindowsAzure.Storage.Table.Attributes; +using CoreHelpers.WindowsAzure.Storage.Table.Extensions; namespace CoreHelpers.WindowsAzure.Storage.Table.Serialization { @@ -15,9 +17,22 @@ public TableEntityBuilder AddPartitionKey(string pkey) return this; } - public TableEntityBuilder AddRowKey(string rkey) + public TableEntityBuilder AddRowKey(string rkey, nVirtualValueEncoding encoding = nVirtualValueEncoding.None) { - _data.Add("RowKey", rkey); + + switch (encoding) + { + case nVirtualValueEncoding.None: + _data.Add("RowKey", rkey); + break; + case nVirtualValueEncoding.Base64: + _data.Add("RowKey", rkey.ToBase64()); + break; + case nVirtualValueEncoding.Sha256: + _data.Add("RowKey", rkey.ToSha256()); + break; + } + return this; } diff --git a/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityDynamic.cs b/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityDynamic.cs index 44a3fbc..503dce5 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityDynamic.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/Serialization/TableEntityDynamic.cs @@ -12,14 +12,22 @@ namespace CoreHelpers.WindowsAzure.Storage.Table.Serialization { internal static class TableEntityDynamic - { + { + public static TableEntity ToEntity(T model, IStorageContext context) where T : new() + { + if (context as StorageContext == null) + throw new Exception("Invalid interface implemnetation"); + else + return TableEntityDynamic.ToEntity(model, (context as StorageContext).GetEntityMapper()); + } + public static TableEntity ToEntity(T model, StorageEntityMapper entityMapper) where T: new() { var builder = new TableEntityBuilder(); // set the keys builder.AddPartitionKey(GetTableStorageDefaultProperty(entityMapper.PartitionKeyFormat, model)); - builder.AddRowKey(GetTableStorageDefaultProperty(entityMapper.RowKeyFormat, model)); + builder.AddRowKey(GetTableStorageDefaultProperty(entityMapper.RowKeyFormat, model), entityMapper.RowKeyEncoding); // get all properties from model IEnumerable objectProperties = model.GetType().GetTypeInfo().GetProperties(); diff --git a/CoreHelpers.WindowsAzure.Storage.Table/StoargeEntityMapper.cs b/CoreHelpers.WindowsAzure.Storage.Table/StoargeEntityMapper.cs index 7c693e7..4003157 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/StoargeEntityMapper.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/StoargeEntityMapper.cs @@ -1,4 +1,5 @@ using System; +using CoreHelpers.WindowsAzure.Storage.Table.Attributes; namespace CoreHelpers.WindowsAzure.Storage.Table { @@ -6,7 +7,8 @@ public class StorageEntityMapper { public String PartitionKeyFormat { get; set; } public String RowKeyFormat { get; set; } - public String TableName { get; set; } + public nVirtualValueEncoding RowKeyEncoding { get; set; } + public String TableName { get; set; } public StorageEntityMapper() {} @@ -15,6 +17,7 @@ public StorageEntityMapper(StorageEntityMapper src) { this.PartitionKeyFormat = src.PartitionKeyFormat; this.RowKeyFormat = src.RowKeyFormat; + this.RowKeyEncoding = src.RowKeyEncoding; this.TableName = src.TableName; } } diff --git a/CoreHelpers.WindowsAzure.Storage.Table/StorageContextAttributeMapping.cs b/CoreHelpers.WindowsAzure.Storage.Table/StorageContextAttributeMapping.cs index 04425f7..995d5fb 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/StorageContextAttributeMapping.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/StorageContextAttributeMapping.cs @@ -14,11 +14,17 @@ public void AddEntityMapper(Type entityType, StorageEntityMapper entityMapper) => _entityMapperRegistry.Add(entityType, entityMapper); public void AddEntityMapper(Type entityType, string partitionKeyFormat, string rowKeyFormat, string tableName) + { + AddEntityMapper(entityType, partitionKeyFormat, rowKeyFormat, nVirtualValueEncoding.None, tableName); + } + + public void AddEntityMapper(Type entityType, String partitionKeyFormat, String rowKeyFormat, nVirtualValueEncoding rowKeyEncoding, String tableName) { _entityMapperRegistry.Add(entityType, new StorageEntityMapper() { PartitionKeyFormat = partitionKeyFormat, RowKeyFormat = rowKeyFormat, + RowKeyEncoding = rowKeyEncoding, TableName = tableName }); } @@ -66,6 +72,7 @@ public void AddAttributeMapper(Type type, String optionalTablenameOverride) // store the neded properties string partitionKeyFormat = null; string rowKeyFormat = null; + var rowKeyEncoding = nVirtualValueEncoding.None; // get the partitionkey property & rowkey property var properties = type.GetRuntimeProperties(); @@ -91,6 +98,9 @@ public void AddAttributeMapper(Type type, String optionalTablenameOverride) if (virtualRowKeyAttribute != null && !String.IsNullOrEmpty(virtualRowKeyAttribute.RowKeyFormat)) rowKeyFormat = virtualRowKeyAttribute.RowKeyFormat; + if (virtualRowKeyAttribute != null && virtualRowKeyAttribute.Encoding != nVirtualValueEncoding.None) + rowKeyEncoding = virtualRowKeyAttribute.Encoding; + // check if (partitionKeyFormat == null || rowKeyFormat == null) throw new Exception("Missing Partition or RowKey Attribute"); @@ -100,7 +110,8 @@ public void AddAttributeMapper(Type type, String optionalTablenameOverride) { TableName = String.IsNullOrEmpty(optionalTablenameOverride) ? storableAttribute.Tablename : optionalTablenameOverride, PartitionKeyFormat = partitionKeyFormat, - RowKeyFormat = rowKeyFormat + RowKeyFormat = rowKeyFormat, + RowKeyEncoding = rowKeyEncoding }); } diff --git a/CoreHelpers.WindowsAzure.Storage.Table/StorageContextQueryEntities.cs b/CoreHelpers.WindowsAzure.Storage.Table/StorageContextQueryEntities.cs index 1c0835c..fb7e121 100644 --- a/CoreHelpers.WindowsAzure.Storage.Table/StorageContextQueryEntities.cs +++ b/CoreHelpers.WindowsAzure.Storage.Table/StorageContextQueryEntities.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CoreHelpers.WindowsAzure.Storage.Table.Abstractions; using CoreHelpers.WindowsAzure.Storage.Table.Internal; namespace CoreHelpers.WindowsAzure.Storage.Table