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);
        }
Beispiel #2
0
        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));
        }
Beispiel #4
0
        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);
                }
            }
        }
Beispiel #9
0
 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;
 }
Beispiel #11
0
 public void IsValid_NullKeyRing_ReturnsFalse()
 {
     Assert.False(CacheableKeyRing.IsValid(null, DateTime.UtcNow));
 }