From 4365b531d8a4ec319ff26a1e661a6991efbd140f Mon Sep 17 00:00:00 2001 From: Levi B Date: Mon, 16 Mar 2015 18:45:31 -0700 Subject: [PATCH] Use C# 6 string interpolation feature Provides cleanup in logging and removes calls to String.Format --- .../CngCbcAuthenticatedEncryptionOptions.cs | 4 +- .../CngGcmAuthenticatedEncryptionOptions.cs | 2 +- .../ManagedAuthenticatedEncryptionOptions.cs | 4 +- .../DataProtectionServices.cs | 10 ++-- .../KeyManagement/DefaultKeyResolver.cs | 4 +- .../KeyRingBasedDataProtector.cs | 23 +++++---- .../KeyManagement/KeyRingProvider.cs | 4 +- .../KeyManagement/XmlKeyManager.cs | 49 +++++++++---------- .../LoggingExtensions.cs | 40 ++++++++++++--- .../Properties/Resources.Designer.cs | 12 ++--- .../Repositories/FileSystemXmlRepository.cs | 6 +-- .../Repositories/RegistryXmlRepository.cs | 9 ++-- .../Resources.resx | 6 +-- .../StringInterpolation.cs | 43 ++++++++++++++++ .../XmlEncryption/CertificateXmlEncryptor.cs | 6 +-- .../XmlEncryption/DpapiNGXmlDecryptor.cs | 2 +- .../XmlEncryption/DpapiNGXmlEncryptor.cs | 7 +-- .../XmlEncryption/DpapiXmlEncryptor.cs | 2 +- 18 files changed, 152 insertions(+), 81 deletions(-) create mode 100644 src/Microsoft.AspNet.DataProtection/StringInterpolation.cs diff --git a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngCbcAuthenticatedEncryptionOptions.cs b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngCbcAuthenticatedEncryptionOptions.cs index baa2ca43c0..18ed508884 100644 --- a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngCbcAuthenticatedEncryptionOptions.cs +++ b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngCbcAuthenticatedEncryptionOptions.cs @@ -113,7 +113,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption if (logger.IsVerboseLevelEnabled()) { - logger.LogVerbose("Opening CNG algorithm '{0}' from provider '{1}' with HMAC.", HashAlgorithm, HashAlgorithmProvider); + logger.LogVerboseF($"Opening CNG algorithm '{HashAlgorithm}' from provider '{HashAlgorithmProvider}' with HMAC."); } BCryptAlgorithmHandle algorithmHandle = null; @@ -154,7 +154,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption if (logger.IsVerboseLevelEnabled()) { - logger.LogVerbose("Opening CNG algorithm '{0}' from provider '{1}' with chaining mode CBC.", EncryptionAlgorithm, EncryptionAlgorithmProvider); + logger.LogVerboseF($"Opening CNG algorithm '{EncryptionAlgorithm}' from provider '{EncryptionAlgorithmProvider}' with chaining mode CBC."); } BCryptAlgorithmHandle algorithmHandle = null; diff --git a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngGcmAuthenticatedEncryptionOptions.cs b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngGcmAuthenticatedEncryptionOptions.cs index 0213fb1598..3f0f39392a 100644 --- a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngGcmAuthenticatedEncryptionOptions.cs +++ b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/CngGcmAuthenticatedEncryptionOptions.cs @@ -94,7 +94,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption if (logger.IsVerboseLevelEnabled()) { - logger.LogVerbose("Opening CNG algorithm '{0}' from provider '{1}' with chaining mode GCM.", EncryptionAlgorithm, EncryptionAlgorithmProvider); + logger.LogVerboseF($"Opening CNG algorithm '{EncryptionAlgorithm}' from provider '{EncryptionAlgorithmProvider}' with chaining mode GCM."); } // Special-case cached providers diff --git a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/ManagedAuthenticatedEncryptionOptions.cs b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/ManagedAuthenticatedEncryptionOptions.cs index 944b5b9721..9b446d5fbe 100644 --- a/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/ManagedAuthenticatedEncryptionOptions.cs +++ b/src/Microsoft.AspNet.DataProtection/AuthenticatedEncryption/ManagedAuthenticatedEncryptionOptions.cs @@ -88,7 +88,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption if (logger.IsVerboseLevelEnabled()) { - logger.LogVerbose("Using managed keyed hash algorithm '{0}'.", ValidationAlgorithmType.FullName); + logger.LogVerboseF($"Using managed keyed hash algorithm '{ValidationAlgorithmType.FullName}'."); } if (ValidationAlgorithmType == typeof(HMACSHA256)) @@ -120,7 +120,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption if (logger.IsVerboseLevelEnabled()) { - logger.LogVerbose("Using managed symmetric algorithm '{0}'.", EncryptionAlgorithmType.FullName); + logger.LogVerboseF($"Using managed symmetric algorithm '{EncryptionAlgorithmType.FullName}'."); } if (EncryptionAlgorithmType == typeof(Aes)) diff --git a/src/Microsoft.AspNet.DataProtection/DataProtectionServices.cs b/src/Microsoft.AspNet.DataProtection/DataProtectionServices.cs index 234099ea4d..0b5890e810 100644 --- a/src/Microsoft.AspNet.DataProtection/DataProtectionServices.cs +++ b/src/Microsoft.AspNet.DataProtection/DataProtectionServices.cs @@ -43,7 +43,7 @@ namespace Microsoft.Framework.DependencyInjection { if (log.IsInformationLevelEnabled()) { - log.LogInformation("Azure Web Sites environment detected. Using '{0}' as key repository; keys will not be encrypted at rest.", azureWebSitesKeysFolder.FullName); + log.LogInformationF($"Azure Web Sites environment detected. Using '{azureWebSitesKeysFolder.FullName}' as key repository; keys will not be encrypted at rest."); } // Cloud DPAPI isn't yet available, so we don't encrypt keys at rest. @@ -68,11 +68,11 @@ namespace Microsoft.Framework.DependencyInjection { if (keyEncryptorDescriptor != null) { - log.LogInformation("User profile is available. Using '{0}' as key repository and Windows DPAPI to encrypt keys at rest.", localAppDataKeysFolder.FullName); + log.LogInformationF($"User profile is available. Using '{localAppDataKeysFolder.FullName}' as key repository and Windows DPAPI to encrypt keys at rest."); } else { - log.LogInformation("User profile is available. Using '{0}' as key repository; keys will not be encrypted at rest.", localAppDataKeysFolder.FullName); + log.LogInformationF($"User profile is available. Using '{localAppDataKeysFolder.FullName}' as key repository; keys will not be encrypted at rest."); } } } @@ -93,11 +93,11 @@ namespace Microsoft.Framework.DependencyInjection { if (keyEncryptorDescriptor != null) { - log.LogInformation("User profile not available. Using '{0}' as key repository and Windows DPAPI to encrypt keys at rest.", regKeyStorageKey.Name); + log.LogInformationF($"User profile not available. Using '{regKeyStorageKey.Name}' as key repository and Windows DPAPI to encrypt keys at rest."); } else { - log.LogInformation("User profile not available. Using '{0}' as key repository; keys will not be encrypted at rest.", regKeyStorageKey.Name); + log.LogInformationF($"User profile not available. Using '{regKeyStorageKey.Name}' as key repository; keys will not be encrypted at rest."); } } } diff --git a/src/Microsoft.AspNet.DataProtection/KeyManagement/DefaultKeyResolver.cs b/src/Microsoft.AspNet.DataProtection/KeyManagement/DefaultKeyResolver.cs index 34c64e134e..795751fa50 100644 --- a/src/Microsoft.AspNet.DataProtection/KeyManagement/DefaultKeyResolver.cs +++ b/src/Microsoft.AspNet.DataProtection/KeyManagement/DefaultKeyResolver.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Considering key '{0:D}' with expiration date {1:u} as default key.", preferredDefaultKey.KeyId, preferredDefaultKey.ExpirationDate); + _logger.LogVerboseF($"Considering key {preferredDefaultKey.KeyId:B} with expiration date {preferredDefaultKey.ExpirationDate:u} as default key."); } // if the key has been revoked or is expired, it is no longer a candidate @@ -70,7 +70,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Key '{0:D}' is no longer under consideration as default key because it is expired or revoked.", preferredDefaultKey.KeyId); + _logger.LogVerboseF($"Key {preferredDefaultKey.KeyId:B} is no longer under consideration as default key because it is expired or revoked."); } preferredDefaultKey = null; } diff --git a/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingBasedDataProtector.cs b/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingBasedDataProtector.cs index 5528cc45e9..b807d90e41 100644 --- a/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingBasedDataProtector.cs +++ b/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingBasedDataProtector.cs @@ -63,6 +63,11 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement newPurpose: purpose); } + private static string JoinPurposesForLog(IEnumerable purposes) + { + return "(" + String.Join(", ", purposes.Select(p => "'" + p + "'")) + ")"; + } + // allows decrypting payloads whose keys have been revoked public byte[] DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked) { @@ -97,8 +102,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsDebugLevelEnabled()) { - _logger.LogDebug("Performing protect operation to key '{0:D}' with purposes ({1}).", - defaultKeyId, String.Join(", ", Purposes.Select(p => "'" + p + "'"))); + _logger.LogDebugF($"Performing protect operation to key {defaultKeyId:B} with purposes {JoinPurposesForLog(Purposes)}."); } // We'll need to apply the default key id to the template if it hasn't already been applied. @@ -218,8 +222,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsDebugLevelEnabled()) { - _logger.LogDebug("Performing unprotect operation to key '{0:D}' with purposes ({1}).", - keyIdFromPayload, String.Join(", ", Purposes.Select(p => "'" + p + "'"))); + _logger.LogDebugF($"Performing unprotect operation to key {keyIdFromPayload:B} with purposes {JoinPurposesForLog(Purposes)}."); } // Find the correct encryptor in the keyring. @@ -228,9 +231,9 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement var requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked); if (requestedEncryptor == null) { - if (_logger.IsWarningLevelEnabled()) + if (_logger.IsDebugLevelEnabled()) { - _logger.LogWarning("Key '{0:D}' was not found in the key ring. Unprotect operation cannot proceed.", keyIdFromPayload); + _logger.LogDebugF($"Key {keyIdFromPayload:B} was not found in the key ring. Unprotect operation cannot proceed."); } throw Error.Common_KeyNotFound(keyIdFromPayload); } @@ -247,17 +250,17 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (allowOperationsOnRevokedKeys) { - if (_logger.IsWarningLevelEnabled()) + if (_logger.IsVerboseLevelEnabled()) { - _logger.LogWarning("Key '{0:D}' was revoked. Caller requested unprotect operation proceed regardless.", keyIdFromPayload); + _logger.LogVerboseF($"Key {keyIdFromPayload:B} was revoked. Caller requested unprotect operation proceed regardless."); } status = UnprotectStatus.DecryptionKeyWasRevoked; } else { - if (_logger.IsWarningLevelEnabled()) + if (_logger.IsVerboseLevelEnabled()) { - _logger.LogWarning("Key '{0:D}' was revoked. Unprotect operation cannot proceed.", keyIdFromPayload); + _logger.LogVerboseF($"Key {keyIdFromPayload:B} was revoked. Unprotect operation cannot proceed."); } throw Error.Common_KeyRevoked(keyIdFromPayload); } diff --git a/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingProvider.cs b/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingProvider.cs index 7aafa50a03..9f12454e40 100644 --- a/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingProvider.cs +++ b/src/Microsoft.AspNet.DataProtection/KeyManagement/KeyRingProvider.cs @@ -82,7 +82,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (_logger.IsWarningLevelEnabled()) { - _logger.LogWarning("Policy resolution states that a new key should be added to the key ring, but automatic generation of keys is disabled. Using fallback key '{0:D}' with expiration {1:u} as default key.", keyToUse.KeyId, keyToUse.ExpirationDate); + _logger.LogWarningF($"Policy resolution states that a new key should be added to the key ring, but automatic generation of keys is disabled. Using fallback key {keyToUse.KeyId:B} with expiration {keyToUse.ExpirationDate:u} as default key."); } return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, keyToUse, allKeys); } @@ -111,7 +111,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Using key '{0:D}' as the default key.", defaultKey.KeyId); + _logger.LogVerboseF($"Using key {defaultKey.KeyId:B} as the default key."); } DateTimeOffset nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod); diff --git a/src/Microsoft.AspNet.DataProtection/KeyManagement/XmlKeyManager.cs b/src/Microsoft.AspNet.DataProtection/KeyManagement/XmlKeyManager.cs index 4466158062..baabe020ae 100644 --- a/src/Microsoft.AspNet.DataProtection/KeyManagement/XmlKeyManager.cs +++ b/src/Microsoft.AspNet.DataProtection/KeyManagement/XmlKeyManager.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -19,6 +18,8 @@ using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; +using static System.FormattableString; + namespace Microsoft.AspNet.DataProtection.KeyManagement { /// @@ -168,7 +169,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement // Skip unknown elements. if (_logger.IsWarningLevelEnabled()) { - _logger.LogWarning("Unknown element with name '{0}' found in keyring, skipping.", element.Name); + _logger.LogWarningF($"Unknown element with name '{element.Name}' found in keyring, skipping."); } } } @@ -185,14 +186,14 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement key.SetRevoked(); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Marked key '{0:D}' as revoked in the keyring.", revokedKeyId); + _logger.LogVerboseF($"Marked key {revokedKeyId:B} as revoked in the keyring."); } } else { if (_logger.IsWarningLevelEnabled()) { - _logger.LogWarning("Tried to process revocation of key '{0:D}', but no such key was found in keyring. Skipping.", revokedKeyId); + _logger.LogWarningF($"Tried to process revocation of key {revokedKeyId:B}, but no such key was found in keyring. Skipping."); } } } @@ -208,7 +209,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement key.SetRevoked(); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Marked key '{0:D}' as revoked in the keyring.", key.KeyId); + _logger.LogVerboseF($"Marked key {key.KeyId:B} as revoked in the keyring."); } } } @@ -247,7 +248,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement // Finally, create the Key instance if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Found key '{0:D}'.", keyId); + _logger.LogVerboseF($"Found key {keyId:B}."); } return new Key( keyId: keyId, @@ -264,19 +265,15 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (_logger.IsWarningLevelEnabled()) { - _logger.LogWarning("An exception of type '{0}' occurred while processing the key element '{1}', so the key will not be included in the keyring." + Environment.NewLine - + "Full details of the exception will be written to the 'Debug' log.", - ex.GetType().FullName, keyElement.WithoutChildNodes()); + _logger.LogWarningF($"An exception of type '{ex.GetType().FullName}' occurred while processing the key element '{keyElement.WithoutChildNodes()}', so the key will not be included in the keyring. Full details of the exception will be written to the 'Debug' log."); } - _logger.LogDebug(ex, "An exception occurred while processing the key element '{0}'.", keyElement); + _logger.LogDebugF(ex, $"An exception occurred while processing the key element '{keyElement}'."); } else { if (_logger.IsWarningLevelEnabled()) { - _logger.LogWarning("An exception of type '{0}' occurred while processing the key element '{1}', so the key will not be included in the keyring." + Environment.NewLine - + "To prevent accidental disclosure of sensitive information the full exception details are not being logged. To enable logging full exception details, enable 'Debug' level logging for this provider.", - ex.GetType().FullName, keyElement.WithoutChildNodes()); + _logger.LogWarningF($"An exception of type '{ex.GetType().FullName}' occurred while processing the key element '{keyElement.WithoutChildNodes()}', so the key will not be included in the keyring. To prevent accidental disclosure of sensitive information the full exception details are not being logged. To enable logging full exception details, enable 'Debug' level logging for this provider."); } } @@ -299,7 +296,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement DateTimeOffset massRevocationDate = (DateTimeOffset)revocationElement.Element(RevocationDateElementName); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Found revocation of all keys created prior to {0:u}.", massRevocationDate); + _logger.LogVerboseF($"Found revocation of all keys created prior to {massRevocationDate:u}."); } return massRevocationDate; } @@ -309,7 +306,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement Guid keyId = XmlConvert.ToGuid(keyIdAsString); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Found revocation of key '{0:D}'.", keyId); + _logger.LogVerboseF($"Found revocation of key {keyId:B}."); } return keyId; } @@ -320,7 +317,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement // revocation information. if (_logger.IsErrorLevelEnabled()) { - _logger.LogError(ex, "An exception occurred while processing the revocation element '{0}'. Cannot continue keyring processing.", revocationElement); + _logger.LogErrorF(ex, $"An exception occurred while processing the revocation element '{revocationElement}'. Cannot continue keyring processing."); } throw; } @@ -337,7 +334,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsInformationLevelEnabled()) { - _logger.LogInformation("Revoking all keys as of {0:u} for reason '{1}'.", revocationDate, reason); + _logger.LogInformationF($"Revoking all keys as of {revocationDate:u} for reason '{reason}'."); } var revocationElement = new XElement(RevocationElementName, @@ -366,7 +363,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (!suppressLogging && _logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Key cache expiration token triggered by '{0}' operation.", opName); + _logger.LogVerboseF($"Key cache expiration token triggered by '{opName}' operation."); } Interlocked.Exchange(ref _cacheExpirationTokenSource, new CancellationTokenSource())?.Cancel(); @@ -385,7 +382,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsInformationLevelEnabled()) { - _logger.LogInformation("Creating key {0:D} with creation date {1:u}, activation date {2:u}, and expiration date {3:u}.", keyId, creationDate, activationDate, expirationDate); + _logger.LogInformationF($"Creating key {keyId:B} with creation date {creationDate:u}, activation date {activationDate:u}, and expiration date {expirationDate:u}."); } var newDescriptor = _authenticatedEncryptorConfiguration.CreateNewDescriptor() @@ -394,7 +391,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Descriptor deserializer type for key {0:D} is {1}.", keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName); + _logger.LogVerboseF($"Descriptor deserializer type for key {keyId:B} is '{descriptorXmlInfo.DeserializerType.AssemblyQualifiedName}'."); } // build the element @@ -413,11 +410,11 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement { if (_keyEscrowSink != null) { - _logger.LogVerbose("Key escrow sink found. Writing key {0:D} to escrow.", keyId); + _logger.LogVerboseF($"Key escrow sink found. Writing key {keyId:B} to escrow."); } else { - _logger.LogVerbose("No key escrow sink found. Not writing key {0:D} to escrow.", keyId); + _logger.LogVerboseF($"No key escrow sink found. Not writing key {keyId:B} to escrow."); } } _keyEscrowSink?.Store(keyId, keyElement); @@ -425,12 +422,12 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement // If an XML encryptor has been configured, protect secret key material now. if (KeyEncryptor == null && _logger.IsWarningLevelEnabled()) { - _logger.LogWarning("No XML encryptor configured. Key {0:D} may be persisted to storage in unencrypted form.", keyId); + _logger.LogWarningF($"No XML encryptor configured. Key {keyId:B} may be persisted to storage in unencrypted form."); } var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement; // Persist it to the underlying repository and trigger the cancellation token. - string friendlyName = String.Format(CultureInfo.InvariantCulture, "key-{0:D}", keyId); + string friendlyName = Invariant($"key-{keyId:D}"); KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName); TriggerAndResetCacheExpirationToken(); @@ -453,7 +450,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement if (_logger.IsInformationLevelEnabled()) { - _logger.LogInformation("Revoking key {0:D} at {1:u} for reason '{2}'.", keyId, revocationDate, reason); + _logger.LogInformationF($"Revoking key {keyId:B} at {revocationDate:u} for reason '{reason}'."); } var revocationElement = new XElement(RevocationElementName, @@ -464,7 +461,7 @@ namespace Microsoft.AspNet.DataProtection.KeyManagement new XElement(ReasonElementName, reason)); // Persist it to the underlying repository and trigger the cancellation token - string friendlyName = String.Format(CultureInfo.InvariantCulture, "revocation-{0:D}", keyId); + string friendlyName = Invariant($"revocation-{keyId:D}"); KeyRepository.StoreElement(revocationElement, friendlyName); TriggerAndResetCacheExpirationToken(); } diff --git a/src/Microsoft.AspNet.DataProtection/LoggingExtensions.cs b/src/Microsoft.AspNet.DataProtection/LoggingExtensions.cs index ee7735fe2f..2ff0a23f79 100644 --- a/src/Microsoft.AspNet.DataProtection/LoggingExtensions.cs +++ b/src/Microsoft.AspNet.DataProtection/LoggingExtensions.cs @@ -8,7 +8,8 @@ using Microsoft.Framework.Logging.Internal; namespace Microsoft.Framework.Logging { /// - /// Helpful extension methods on ILogger. + /// Helpful extension methods on . + /// Methods ending in *F take as a parameter. /// internal static class LoggingExtensions { @@ -68,19 +69,44 @@ namespace Microsoft.Framework.Logging return (logger != null && logger.IsEnabled(level)); } - public static void LogDebug(this ILogger logger, Exception error, string message, params object[] args) + public static void LogDebugF(this ILogger logger, FormattableString message) { - logger.LogDebug(new FormattedLogValues(message, args), error); + logger.LogDebug(message.Format, message.GetArguments()); } - public static void LogError(this ILogger logger, Exception error, string message, params object[] args) + public static void LogDebugF(this ILogger logger, Exception error, FormattableString message) { - logger.LogError(new FormattedLogValues(message, args), error); + logger.LogDebug(new FormattedLogValues(message.Format, message.GetArguments()), error); } - public static void LogWarning(this ILogger logger, Exception error, string message, params object[] args) + public static void LogError(this ILogger logger, Exception error, string message) { - logger.LogWarning(new FormattedLogValues(message, args), error); + logger.LogError(message, error); + } + + public static void LogErrorF(this ILogger logger, Exception error, FormattableString message) + { + logger.LogError(new FormattedLogValues(message.Format, message.GetArguments()), error); + } + + public static void LogInformationF(this ILogger logger, FormattableString message) + { + logger.LogInformation(message.Format, message.GetArguments()); + } + + public static void LogVerboseF(this ILogger logger, FormattableString message) + { + logger.LogVerbose(message.Format, message.GetArguments()); + } + + public static void LogWarningF(this ILogger logger, FormattableString message) + { + logger.LogWarning(message.Format, message.GetArguments()); + } + + public static void LogWarningF(this ILogger logger, Exception error, FormattableString message) + { + logger.LogWarning(new FormattedLogValues(message.Format, message.GetArguments()), error); } } } diff --git a/src/Microsoft.AspNet.DataProtection/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.DataProtection/Properties/Resources.Designer.cs index 9edb8c1b05..e9ac9e8f90 100644 --- a/src/Microsoft.AspNet.DataProtection/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.DataProtection/Properties/Resources.Designer.cs @@ -107,7 +107,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' was not found in the key ring. + /// The key {0:B} was not found in the key ring. /// internal static string Common_KeyNotFound { @@ -115,7 +115,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' was not found in the key ring. + /// The key {0:B} was not found in the key ring. /// internal static string FormatCommon_KeyNotFound() { @@ -123,7 +123,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' has been revoked. + /// The key {0:B} has been revoked. /// internal static string Common_KeyRevoked { @@ -131,7 +131,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' has been revoked. + /// The key {0:B} has been revoked. /// internal static string FormatCommon_KeyRevoked() { @@ -235,7 +235,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' already exists in the keyring. + /// The key {0:B} already exists in the keyring. /// internal static string XmlKeyManager_DuplicateKey { @@ -243,7 +243,7 @@ namespace Microsoft.AspNet.DataProtection } /// - /// The key '{0:D}' already exists in the keyring. + /// The key {0:B} already exists in the keyring. /// internal static string FormatXmlKeyManager_DuplicateKey() { diff --git a/src/Microsoft.AspNet.DataProtection/Repositories/FileSystemXmlRepository.cs b/src/Microsoft.AspNet.DataProtection/Repositories/FileSystemXmlRepository.cs index 52c1718aa9..a21a92f4d8 100644 --- a/src/Microsoft.AspNet.DataProtection/Repositories/FileSystemXmlRepository.cs +++ b/src/Microsoft.AspNet.DataProtection/Repositories/FileSystemXmlRepository.cs @@ -153,7 +153,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories { if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Reading data from file '{0}'.", fullPath); + _logger.LogVerboseF($"Reading data from file '{fullPath}'."); } using (var fileStream = File.OpenRead(fullPath)) @@ -169,7 +169,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories string newFriendlyName = Guid.NewGuid().ToString(); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("The name '{0}' is not a safe file name, using '{1}' instead.", friendlyName, newFriendlyName); + _logger.LogVerboseF($"The name '{friendlyName}' is not a safe file name, using '{newFriendlyName}' instead."); } friendlyName = newFriendlyName; } @@ -198,7 +198,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories // Renames are atomic operations on the file systems we support. if (_logger.IsInformationLevelEnabled()) { - _logger.LogInformation("Writing data to file '{0}.", finalFilename); + _logger.LogInformationF($"Writing data to file '{finalFilename}'."); } File.Move(tempFilename, finalFilename); } diff --git a/src/Microsoft.AspNet.DataProtection/Repositories/RegistryXmlRepository.cs b/src/Microsoft.AspNet.DataProtection/Repositories/RegistryXmlRepository.cs index bc42ef4a23..9e0d036ef7 100644 --- a/src/Microsoft.AspNet.DataProtection/Repositories/RegistryXmlRepository.cs +++ b/src/Microsoft.AspNet.DataProtection/Repositories/RegistryXmlRepository.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Security.Principal; using System.Xml.Linq; @@ -11,6 +10,8 @@ using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; using Microsoft.Win32; +using static System.FormattableString; + namespace Microsoft.AspNet.DataProtection.Repositories { /// @@ -96,7 +97,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories // Even though this is in HKLM, WAS ensures that applications hosted in IIS are properly isolated. // See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info. // The version number will need to change if IIS hosts Core CLR directly. - string aspnetAutoGenKeysBaseKeyName = String.Format(CultureInfo.InvariantCulture, @"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{0}", WindowsIdentity.GetCurrent().User.Value); + string aspnetAutoGenKeysBaseKeyName = Invariant($@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{WindowsIdentity.GetCurrent().User.Value}"); var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true); if (aspnetBaseKey != null) { @@ -132,7 +133,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories { if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Reading data from registry key '{0}', value '{1}'.", regKey.ToString(), valueName); + _logger.LogVerboseF($"Reading data from registry key '{regKey}', value '{valueName}'."); } string data = regKey.GetValue(valueName) as string; @@ -146,7 +147,7 @@ namespace Microsoft.AspNet.DataProtection.Repositories string newFriendlyName = Guid.NewGuid().ToString(); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("The name '{0}' is not a safe registry value name, using '{1}' instead.", friendlyName, newFriendlyName); + _logger.LogVerboseF($"The name '{friendlyName}' is not a safe registry value name, using '{newFriendlyName}' instead."); } friendlyName = newFriendlyName; } diff --git a/src/Microsoft.AspNet.DataProtection/Resources.resx b/src/Microsoft.AspNet.DataProtection/Resources.resx index 3562a6a959..5f368a39c0 100644 --- a/src/Microsoft.AspNet.DataProtection/Resources.resx +++ b/src/Microsoft.AspNet.DataProtection/Resources.resx @@ -136,10 +136,10 @@ An error occurred while trying to encrypt the provided data. Refer to the inner exception for more information. - The key '{0:D}' was not found in the key ring. + The key {0:B} was not found in the key ring. - The key '{0:D}' has been revoked. + The key {0:B} has been revoked. The provided payload cannot be decrypted because it was not protected with this protection provider. @@ -160,7 +160,7 @@ The new key lifetime must be at least one week. - The key '{0:D}' already exists in the keyring. + The key {0:B} already exists in the keyring. Argument cannot be null or empty. diff --git a/src/Microsoft.AspNet.DataProtection/StringInterpolation.cs b/src/Microsoft.AspNet.DataProtection/StringInterpolation.cs new file mode 100644 index 0000000000..e7ba7f9dae --- /dev/null +++ b/src/Microsoft.AspNet.DataProtection/StringInterpolation.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if !DNXCORE50 +// These classes allow using the C# string interpolation feature from .NET 4.5.1. +// They're slimmed-down versions of the classes that exist in .NET 4.6. + +using System.Globalization; + +namespace System +{ + internal struct FormattableString + { + private readonly object[] _arguments; + public readonly string Format; + + internal FormattableString(string format, params object[] arguments) + { + Format = format; + _arguments = arguments; + } + + public object[] GetArguments() => _arguments; + + public static string Invariant(FormattableString formattable) + { + return String.Format(CultureInfo.InvariantCulture, formattable.Format, formattable.GetArguments()); + } + } +} + +namespace System.Runtime.CompilerServices +{ + internal static class FormattableStringFactory + { + public static FormattableString Create(string format, params object[] arguments) + { + return new FormattableString(format, arguments); + } + } +} + +#endif diff --git a/src/Microsoft.AspNet.DataProtection/XmlEncryption/CertificateXmlEncryptor.cs b/src/Microsoft.AspNet.DataProtection/XmlEncryption/CertificateXmlEncryptor.cs index f89820b02c..2c6401305b 100644 --- a/src/Microsoft.AspNet.DataProtection/XmlEncryption/CertificateXmlEncryptor.cs +++ b/src/Microsoft.AspNet.DataProtection/XmlEncryption/CertificateXmlEncryptor.cs @@ -132,7 +132,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption { if (_logger.IsErrorLevelEnabled()) { - _logger.LogError(ex, "An exception occurred while trying to resolve certificate with thumbprint '{0}'.", thumbprint); + _logger.LogErrorF(ex, $"An exception occurred while trying to resolve certificate with thumbprint '{thumbprint}'."); } throw; } @@ -146,7 +146,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Encrypting to X.509 certificate with thumbprint '{0}'.", cert.Thumbprint); + _logger.LogVerboseF($"Encrypting to X.509 certificate with thumbprint '{cert.Thumbprint}'."); } try @@ -157,7 +157,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption { if (_logger.IsErrorLevelEnabled()) { - _logger.LogError(ex, "An error occurred while encrypting to X.509 certificate with thumbprint '{0}'.", cert.Thumbprint); + _logger.LogErrorF(ex, $"An error occurred while encrypting to X.509 certificate with thumbprint '{cert.Thumbprint}'."); } throw; } diff --git a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlDecryptor.cs b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlDecryptor.cs index e804c1d7cb..e356c2e259 100644 --- a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlDecryptor.cs +++ b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlDecryptor.cs @@ -68,7 +68,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption // swallow all errors - it's just a log protectionDescriptorRule = null; } - _logger.LogVerbose("Decrypting secret element using Windows DPAPI-NG with protection descriptor '{0}'.", protectionDescriptorRule); + _logger.LogVerboseF($"Decrypting secret element using Windows DPAPI-NG with protection descriptor rule '{protectionDescriptorRule}'."); } using (Secret secret = DpapiSecretSerializerHelper.UnprotectWithDpapiNG(protectedSecret)) diff --git a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlEncryptor.cs b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlEncryptor.cs index 1a11ce10a7..4b6180ce5b 100644 --- a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlEncryptor.cs +++ b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiNGXmlEncryptor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Globalization; using System.Security.Principal; using System.Xml.Linq; using Microsoft.AspNet.Cryptography; @@ -11,6 +10,8 @@ using Microsoft.AspNet.DataProtection.Cng; using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; +using static System.FormattableString; + namespace Microsoft.AspNet.DataProtection.XmlEncryption { /// @@ -65,7 +66,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption string protectionDescriptorRuleString = _protectionDescriptorHandle.GetProtectionDescriptorRuleString(); if (_logger.IsVerboseLevelEnabled()) { - _logger.LogVerbose("Encrypting to Windows DPAPI-NG using protection descriptor '{0}'.", protectionDescriptorRuleString); + _logger.LogVerboseF($"Encrypting to Windows DPAPI-NG using protection descriptor rule '{protectionDescriptorRuleString}'."); } // Convert the XML element to a binary secret so that it can be run through DPAPI @@ -114,7 +115,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent()) { // use the SID to create an SDDL string - return String.Format(CultureInfo.InvariantCulture, "SID={0}", currentIdentity.User.Value); + return Invariant($"SID={currentIdentity.User.Value}"); } } } diff --git a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiXmlEncryptor.cs b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiXmlEncryptor.cs index d0b5908092..4ec5f0cef4 100644 --- a/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiXmlEncryptor.cs +++ b/src/Microsoft.AspNet.DataProtection/XmlEncryption/DpapiXmlEncryptor.cs @@ -66,7 +66,7 @@ namespace Microsoft.AspNet.DataProtection.XmlEncryption } else { - _logger.LogVerbose("Encrypting to Windows DPAPI for current user account ({0}).", WindowsIdentity.GetCurrent().Name); + _logger.LogVerboseF($"Encrypting to Windows DPAPI for current user account ({WindowsIdentity.GetCurrent().Name})."); } }