// ******************************************************************* // Public methods. // ******************************************************************* #region Public methods /// <summary> /// This method encrypts the value of any properties on the specified /// <paramref name="options"/> object that are: (1) decorated with a /// <see cref="ProtectedPropertyAttribute"/> attribute, (2) are of /// type: string, and (3) have a value in them. /// </summary> /// <param name="dataProtector">The data protector object to use for the /// operation.</param> /// <param name="options">The options object to use for the operation.</param> /// <returns>The value of the <paramref name="dataProtector"/> parameter, for /// chaining calls together.</returns> /// <exception cref="ArgumentException">This exception is thrown whenever /// one or more of the required parameters is missing or invalid.</exception> /// <exception cref="InvalidOperationException">This exception is thrown whenever /// the underlying cryptography operation fails, for any reason.</exception> public static IDataProtector ProtectProperties( this IDataProtector dataProtector, object options ) { // Validate the parameters before attempting to use them. Guard.Instance().ThrowIfNull(dataProtector, nameof(dataProtector)) .ThrowIfNull(options, nameof(options)); // Get a list of all object type properties. var props = options.GetType().GetProperties() .Where(x => x.PropertyType.IsClass) .Where(x => x.PropertyType != typeof(string)) .ToList(); // Loop and protect each property, recursively. props.ForEach(prop => { object obj = null; try { // Get the object reference. obj = prop.GetGetMethod().Invoke( options, Array.Empty <object>() ); // Check for missing references first ... if (null != obj) { // Protect any properties for the object. dataProtector.ProtectProperties( obj ); } } catch (Exception ex) { // Wrap the exception. throw new InvalidOperationException( message: string.Format( Resources.ConfigurationExtensions_ProtectProperties, prop.Name, obj.GetType().Name ), innerException: ex ).SetOriginator(nameof(DataProtectorExtensions)) .SetDateTime(); } }); // Get a list of all the read/write properties of type: string. props = options.GetType().GetProperties() .Where( x => x.CanRead && x.CanWrite && x.PropertyType == typeof(string) ).ToList(); // Loop and check each writeable property of type: string. props.ForEach(prop => { try { // Look for a custom attribute on the property. if (prop.GetCustomAttributes(true) .FirstOrDefault( x => x.GetType() == typeof(ProtectedPropertyAttribute) ) is ProtectedPropertyAttribute attr) { // If we get here then we should try to protect the value // of the decorated property. var unprotectedPropertyValue = prop.GetGetMethod().Invoke( options, Array.Empty <object>() ) as string; // Check for empty strings first ... if (false == string.IsNullOrEmpty(unprotectedPropertyValue)) { // Protect the value. var protectedPropertyValue = dataProtector.Protect( unprotectedPropertyValue ); // Write the protected string to the original property. prop.GetSetMethod().Invoke( options, new[] { protectedPropertyValue } ); } } } catch (Exception ex) { // Wrap the exception. throw new InvalidOperationException( message: string.Format( Resources.ConfigurationExtensions_ProtectProperties, prop.Name, options.GetType().Name ), innerException: ex ).SetOriginator(nameof(DataProtectorExtensions)) .SetDateTime(); } }); // Return the configuration. return(dataProtector); }