November Happy Hour will be moved to Thursday December 5th.
November Happy Hour will be moved to Thursday December 5th.
Although I see v3 has literally just been released, so may upgrade to that
In Settings Tab, You will see a tab with name "WebHook URL", that can be used.
Regards
/K
Thanks for the reply but that's not really what I'm after. I don't need to post JSON anywhere, I need to override the rendering of the form submissions in episerver itself.
Only supported events are
FormsSubmitting //Before each step, at least once
FormsStepSubmitted //After each step, at least once
FormsSubmissionFinalized //When final step is posted
FormsStructureChange //When a Form Container Block is published
Although Customozied Actor can also be added but they will also not fill your requirement as you want to decrypt whenever reading taht data back. I am not expecting any new event in new release. Will be interested to know what solution you will get from EPiserver team.
Regards
/K
Hey Tim!
I implemented it in a slightly different way. You can hopefully make it work for you.
I created a new implementation of the IPermanentStorage that supports encrypting elements. Then just add your implementation to the container to let Episerver use it instead. An element can be marked for encryption by using an attribute. Threw in a simple encryption class as well.
public class EncryptedFormsDataStorage : DdsPermanentStorage { private readonly ILogger _log = LogManager.GetLogger(typeof(EncryptedFormsDataStorage)); private Injected<EPiServer.Forms.Core.Data.Internal.DdsStructureStorage> _formStructureStorage; private Injected<IContentLoader> _contentLoader; public override Guid SaveToStorage(FormIdentity formIden, Submission submission) { var cryptographyService = new CryptographyService(); var form = formIden.GetFormBlock(); var elements = form.ElementsArea.FilteredItems; foreach (var contentAreaItem in elements) { var elementblock = _contentLoader.Service.Get<ElementBlockBase>(contentAreaItem.ContentLink); if (ShouldEncryptElement(elementblock)) { var oldText = submission.Data[elementblock.FormElement.ElementName]; if (oldText != null) { submission.Data.Remove(elementblock.FormElement.ElementName); var encryptedText = cryptographyService.Encrypt(oldText.ToString()); submission.Data.Add(elementblock.FormElement.ElementName, encryptedText); } } } return base.SaveToStorage(formIden, submission); } public override IEnumerable<PropertyBag> LoadSubmissionFromStorage(FormIdentity formIden, DateTime beginDate, DateTime endDate, bool finalizeOnly = false) { try { var cryptographyService = new CryptographyService(); var propertyBags = base.LoadSubmissionFromStorage(formIden, beginDate, endDate, finalizeOnly); var form = formIden.GetFormBlock(); var elements = form.ElementsArea.FilteredItems; foreach (var contentAreaItem in elements) { var elementblock = _contentLoader.Service.Get<ElementBlockBase>(contentAreaItem.ContentLink); if (ShouldEncryptElement(elementblock)) { foreach (var propertyBag in propertyBags) { var oldValue = propertyBag[elementblock.FormElement.ElementName]; if (oldValue != null) { string newValue; if (cryptographyService.TryDecrypt(oldValue.ToString(), out newValue)) { _log.Information("Decrypted text"); propertyBag.Remove(elementblock.FormElement.ElementName); propertyBag.Add(elementblock.FormElement.ElementName, newValue); } else { _log.Error("Failed to decrypt element text"); } } } } } return propertyBags; } catch (Exception ex) { _log.Error("Error while decrypting", ex); throw; } } private bool ShouldEncryptElement(ElementBlockBase element) { var encryptionAttribute = Attribute.GetCustomAttribute(element.GetType(), typeof(EncryptedFormsElementAttribute)); if (encryptionAttribute == null) { _log.Information("Element lacks attribute for encryption"); } else { _log.Information("Element has attribute for encryption"); return true; } return false; } } [ContentType(GUID = "95A98808-4BFC-43FE-B723-4FD2F7E01234")] [EncryptedFormsElement] public class EncryptedTextBoxElementBlock : TextboxElementBlock { } public class EncryptedFormsElementAttribute : Attribute { } /// <summary> /// Remember to set crypto key before trying to encrypt / decrypt anything. /// </summary> public class CryptographyService { /// <summary> /// Array for password salt /// </summary> public byte[] PasswordKeyByteArray { get; set; } /// <summary> /// Set this to a tough to break string before trying to encrypt /// </summary> public string CryptoKey { get; set; } public CryptographyService() { CryptoKey = "ASDLKFJALSDKFJAKDFJ"; PasswordKeyByteArray = new byte[] {0x4b, 0x49, 0xa1, 0x6e, 0x11, 0x4d, 0x58, 0x45, 0x76, 0x61, 0x62, 0x45, 0xa5}; } //Encrypt a byte array into a byte array using a key and an IV public byte[] Encrypt(byte[] clearData, byte[] key, byte[] iv) { // Create a MemoryStream to accept the encrypted bytes var ms = new MemoryStream(); var alg = Rijndael.Create(); alg.Key = key; alg.IV = iv; var cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(clearData, 0, clearData.Length); cs.Close(); byte[] encryptedData = ms.ToArray(); return encryptedData; } public string Encrypt(string clearText) { if (string.IsNullOrEmpty(clearText)) { return clearText; } byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); var pdb = new Rfc2898DeriveBytes(CryptoKey, PasswordKeyByteArray); byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16)); return Convert.ToBase64String(encryptedData); } /// <summary> /// Given wrong key this may cast an exception of type CryptographicException with message "Padding is invalid and cannot be removed" /// Decrypt a byte array into a byte array using a key and an IV /// Uses Decrypt(byte[], byte[], byte[]) /// </summary> /// <returns></returns> public byte[] Decrypt(byte[] cipherData, byte[] key, byte[] iv) { var ms = new MemoryStream(); var alg = Rijndael.Create(); alg.Key = key; alg.IV = iv; var cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(cipherData, 0, cipherData.Length); cs.Close(); byte[] decryptedData = ms.ToArray(); return decryptedData; } /// <summary> /// Given wrong key this may cast an exception of type CryptographicException with message "Padding is invalid and cannot be removed" /// Decrypt a string into a string using a password /// Uses Decrypt(byte[], byte[], byte[]) /// </summary> /// <param name="cipherText"></param> /// <returns></returns> public bool TryDecrypt(string cipherText, out string result) { if (string.IsNullOrEmpty(cipherText)) { result = cipherText; return false; } try { byte[] cipherBytes = Convert.FromBase64String(cipherText); var pdb = new Rfc2898DeriveBytes(CryptoKey, PasswordKeyByteArray); byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16)); result = Encoding.Unicode.GetString(decryptedData); return true; } catch (Exception) { result = null; return false; } } }
...and the view for the custom element:
@using EPiServer.Forms.Core @using EPiServerSiteV9.Business @model EPiServerSiteV9.Business.EncryptedTextBoxElementBlock <div class="Form__Element FormTextbox" data-epiforms-element-name="@Model.FormElement.ElementName"> <label for="@Model.FormElement.Guid"> @{ var label = Model.FormElement.SourceContent.Property["Label"]; } @label </label> <input type="text" name="@Model.FormElement.ElementName" class="FormTextbox__Input" id="@Model.FormElement.Guid" /> <span data-epiforms-linked-name="@Model.FormElement.ElementName" class="Form__Element__ValidationError" style="display: none;">*</span> </div>
...and the registration of the new storage:
.... private static void ConfigureContainer(ConfigurationExpression container) { container.For<IPermanentStorage>().Use<EncryptedFormsDataStorage>();
Daniel,
Wow thank you, I didn't expect a full working solution. This works perfectly, thanks very much.
Hi All
I've created an "EncryptedTextElementBlock" that inherits from the TextElementBlock. I've got a delegate registered for the FormsSubmitting event that checks for these fields and encrypts the data as it is saved.
My issue is I can't find a way to hook into the bit where EPiServer reads the data back, so the administrators will see garbled text in the form submissions view/export view. Does anybody know of a way of rendering the decrypted data in these views?
Thanks