public void KeyRing_Prop() { // Arrange var keyRing = new Mock<IKeyRing>().Object; var cacheableKeyRing = new CacheableKeyRing(CancellationToken.None, DateTimeOffset.Now, keyRing); // Act & assert Assert.Same(keyRing, cacheableKeyRing.KeyRing); }
public void KeyRing_Prop() { // Arrange var keyRing = new Mock <IKeyRing>().Object; var cacheableKeyRing = new CacheableKeyRing(CancellationToken.None, DateTimeOffset.Now, keyRing); // Act & assert Assert.Same(keyRing, cacheableKeyRing.KeyRing); }
public void IsValid_Expired_ReturnsFalse() { // Arrange var keyRing = new Mock<IKeyRing>().Object; DateTimeOffset now = DateTimeOffset.UtcNow; var cts = new CancellationTokenSource(); var cacheableKeyRing = new CacheableKeyRing(cts.Token, now.AddHours(1), keyRing); // Act & assert Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now.UtcDateTime)); Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now.AddHours(1).UtcDateTime)); }
public void IsValid_Expired_ReturnsFalse() { // Arrange var keyRing = new Mock <IKeyRing>().Object; DateTimeOffset now = DateTimeOffset.UtcNow; var cts = new CancellationTokenSource(); var cacheableKeyRing = new CacheableKeyRing(cts.Token, now.AddHours(1), keyRing); // Act & assert Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now.UtcDateTime)); Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now.AddHours(1).UtcDateTime)); }
public void CreateCacheableKeyRing_GenerationRequired_NoDefaultKey_CreatesNewKeyWithImmediateActivation() { // Arrange var callSequence = new List <string>(); var expirationCts1 = new CancellationTokenSource(); var expirationCts2 = new CancellationTokenSource(); var now = StringToDateTime("2015-03-01 00:00:00Z"); var allKeys1 = new IKey[0]; var key1 = CreateKey("2015-03-01 00:00:00Z", "2016-03-01 00:00:00Z"); var key2 = CreateKey("2016-03-01 00:00:00Z", "2017-03-01 00:00:00Z"); var allKeys2 = new[] { key1, key2 }; var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager( callSequence: callSequence, getCacheExpirationTokenReturnValues: new[] { expirationCts1.Token, expirationCts2.Token }, getAllKeysReturnValues: new[] { allKeys1, allKeys2 }, createNewKeyCallbacks: new[] { Tuple.Create((DateTimeOffset)now, (DateTimeOffset)now + TimeSpan.FromDays(90), CreateKey()) }, resolveDefaultKeyPolicyReturnValues: new[] { Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys1, new DefaultKeyResolution() { DefaultKey = null, ShouldGenerateNewKey = true }), Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys2, new DefaultKeyResolution() { DefaultKey = key1, ShouldGenerateNewKey = false }) }); // Act var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now); // Assert Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId); AssertWithinJitterRange(cacheableKeyRing.ExpirationTimeUtc, now); Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now)); expirationCts1.Cancel(); Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now)); expirationCts2.Cancel(); Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now)); Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy", "CreateNewKey", "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence); }
public void CreateCacheableKeyRing_GenerationRequired_WithFallbackKey_KeyGenerationDisabled_DoesNotCreateDefaultKey() { // Arrange var callSequence = new List <string>(); var expirationCts = new CancellationTokenSource(); var now = StringToDateTime("2016-02-01 00:00:00Z"); var key1 = CreateKey("2015-03-01 00:00:00Z", "2015-03-01 00:00:00Z"); var allKeys = new[] { key1 }; var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager( callSequence: callSequence, getCacheExpirationTokenReturnValues: new[] { expirationCts.Token }, getAllKeysReturnValues: new[] { allKeys }, createNewKeyCallbacks: null, // empty resolveDefaultKeyPolicyReturnValues: new[] { Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys, new DefaultKeyResolution() { FallbackKey = key1, ShouldGenerateNewKey = true }) }, keyManagementOptions: new KeyManagementOptions() { AutoGenerateKeys = false }); // Act var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now); // Assert Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId); AssertWithinJitterRange(cacheableKeyRing.ExpirationTimeUtc, now); Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now)); expirationCts.Cancel(); Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now)); Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence); }
public void CreateCacheableKeyRing_NoGenerationRequired_DefaultKeyExpiresBeforeRefreshPeriod() { // Arrange var callSequence = new List <string>(); var expirationCts = new CancellationTokenSource(); var now = StringToDateTime("2016-02-29 20:00:00Z"); var key1 = CreateKey("2015-03-01 00:00:00Z", "2016-03-01 00:00:00Z"); var key2 = CreateKey("2016-03-01 00:00:00Z", "2017-03-01 00:00:00Z"); var allKeys = new[] { key1, key2 }; var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager( callSequence: callSequence, getCacheExpirationTokenReturnValues: new[] { expirationCts.Token }, getAllKeysReturnValues: new[] { allKeys }, createNewKeyCallbacks: null, resolveDefaultKeyPolicyReturnValues: new[] { Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys, new DefaultKeyResolution() { DefaultKey = key1, ShouldGenerateNewKey = false }) }); // Act var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now); // Assert Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId); Assert.Equal(StringToDateTime("2016-03-01 00:00:00Z"), cacheableKeyRing.ExpirationTimeUtc); Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now)); expirationCts.Cancel(); Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now)); Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence); }
internal IKeyRing GetCurrentKeyRingCore(DateTime utcNow) { Debug.Assert(utcNow.Kind == DateTimeKind.Utc); // Can we return the cached keyring to the caller? var existingCacheableKeyRing = Volatile.Read(ref _cacheableKeyRing); if (CacheableKeyRing.IsValid(existingCacheableKeyRing, utcNow)) { return(existingCacheableKeyRing.KeyRing); } // The cached keyring hasn't been created or must be refreshed. We'll allow one thread to // update the keyring, and all other threads will continue to use the existing cached // keyring while the first thread performs the update. There is an exception: if there // is no usable existing cached keyring, all callers must block until the keyring exists. bool acquiredLock = false; try { Monitor.TryEnter(_cacheableKeyRingLockObj, (existingCacheableKeyRing != null) ? 0 : Timeout.Infinite, ref acquiredLock); if (acquiredLock) { // This thread acquired the critical section and is responsible for updating the // cached keyring. But first, let's make sure that somebody didn't sneak in before // us and update the keyring on our behalf. existingCacheableKeyRing = Volatile.Read(ref _cacheableKeyRing); if (CacheableKeyRing.IsValid(existingCacheableKeyRing, utcNow)) { return(existingCacheableKeyRing.KeyRing); } if (existingCacheableKeyRing != null && _logger.IsVerboseLevelEnabled()) { _logger.LogVerbose("Existing cached key ring is expired. Refreshing."); } // It's up to us to refresh the cached keyring. // This call is performed *under lock*. CacheableKeyRing newCacheableKeyRing; try { newCacheableKeyRing = _cacheableKeyRingProvider.GetCacheableKeyRing(utcNow); } catch (Exception ex) { if (_logger.IsErrorLevelEnabled()) { if (existingCacheableKeyRing != null) { _logger.LogError(ex, "An error occurred while refreshing the key ring. Will try again in 2 minutes."); } else { _logger.LogError(ex, "An error occurred while reading the key ring."); } } // Failures that occur while refreshing the keyring are most likely transient, perhaps due to a // temporary network outage. Since we don't want every subsequent call to result in failure, we'll // create a new keyring object whose expiration is now + some short period of time (currently 2 min), // and after this period has elapsed the next caller will try refreshing. If we don't have an // existing keyring (perhaps because this is the first call), then there's nothing to extend, so // each subsequent caller will keep going down this code path until one succeeds. if (existingCacheableKeyRing != null) { Volatile.Write(ref _cacheableKeyRing, existingCacheableKeyRing.WithTemporaryExtendedLifetime(utcNow)); } // The immediate caller should fail so that he can report the error up his chain. This makes it more likely // that an administrator can see the error and react to it as appropriate. The caller can retry the operation // and will probably have success as long as he falls within the temporary extension mentioned above. throw; } Volatile.Write(ref _cacheableKeyRing, newCacheableKeyRing); return(newCacheableKeyRing.KeyRing); } else { // We didn't acquire the critical section. This should only occur if we passed // zero for the Monitor.TryEnter timeout, which implies that we had an existing // (but outdated) keyring that we can use as a fallback. Debug.Assert(existingCacheableKeyRing != null); return(existingCacheableKeyRing.KeyRing); } } finally { if (acquiredLock) { Monitor.Exit(_cacheableKeyRingLockObj); } } }
internal static bool IsValid(CacheableKeyRing keyRing, DateTime utcNow) { return(keyRing != null && !keyRing._expirationToken.IsCancellationRequested && keyRing.ExpirationTimeUtc > utcNow); }
internal static bool IsValid(CacheableKeyRing keyRing, DateTime utcNow) { return keyRing != null && !keyRing._expirationToken.IsCancellationRequested && keyRing.ExpirationTimeUtc > utcNow; }
public void IsValid_NullKeyRing_ReturnsFalse() { Assert.False(CacheableKeyRing.IsValid(null, DateTime.UtcNow)); }