예제 #1
0
        public void ImportExport_ThrowException()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();
            var cache         = new MockTokenCache();
            var storage       = new MsalCacheStorage(
                _storageCreationPropertiesBuilder.Build(),
                cacheAccessor,
                new TraceSourceLogger(new TraceSource("ts")));
            var helper = new MsalCacheHelper(cache, storage, _logger);

            byte[] dataToSave = Encoding.UTF8.GetBytes("Hello World 2");
            var    ex         = new InvalidCastException();

            cacheAccessor.Read().Throws(ex);

            // Act
            var actualEx = AssertException.Throws <InvalidCastException>(
                () => helper.LoadUnencryptedTokenCache());

            // Assert
            Assert.AreEqual(ex, actualEx);

            // Arrange
            cacheAccessor.WhenForAnyArgs(c => c.Write(default)).Throw(ex);
        public void ImportExport()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();
            var cache         = new MockTokenCache();
            var storage       = new Storage(
                _storageCreationPropertiesBuilder.Build(),
                cacheAccessor,
                new TraceSourceLogger(new TraceSource("ts")));
            var helper = new MsalCacheHelper(cache, storage, _logger);

            byte[] dataToSave = Encoding.UTF8.GetBytes("Hello World 2");

            cacheAccessor.Read().Returns(Encoding.UTF8.GetBytes("Hello World"));

            // Act
            byte[] actualData = helper.LoadUnencryptedTokenCache();
            helper.SaveUnencryptedTokenCache(dataToSave);

            // Assert
            Assert.AreEqual("Hello World", Encoding.UTF8.GetString(actualData));
            cacheAccessor.Received().Write(dataToSave);
        }
예제 #3
0
        public void MultiAccessSerializationAsync()
        {
            var cache1  = new MockTokenCache();
            var helper1 = new MsalCacheHelper(
                cache1,
                new MsalCacheStorage(s_storageCreationProperties, _logger),
                _logger);

            var cache2  = new MockTokenCache();
            var helper2 = new MsalCacheHelper(
                cache2,
                new MsalCacheStorage(s_storageCreationProperties, _logger),
                _logger);

            //Test signalling thread 1
            var resetEvent1 = new ManualResetEventSlim(initialState: false);

            //Test signalling thread 2
            var resetEvent2 = new ManualResetEventSlim(initialState: false);

            //Thread 1 signalling test
            var resetEvent3 = new ManualResetEventSlim(initialState: false);

            // Thread 2 signalling test
            var resetEvent4 = new ManualResetEventSlim(initialState: false);

            var thread1 = new Thread(() =>
            {
                var args = new TokenCacheNotificationArgs
                {
                    TokenCache = cache1
                };

                helper1.BeforeAccessNotification(args);
                resetEvent3.Set();
                resetEvent1.Wait();
                helper1.AfterAccessNotification(args);
            });

            var thread2 = new Thread(() =>
            {
                var args = new TokenCacheNotificationArgs
                {
                    TokenCache = cache2
                };

                helper2.BeforeAccessNotification(args);
                resetEvent4.Set();
                resetEvent2.Wait();
                helper2.AfterAccessNotification(args);
                resetEvent4.Set();
            });

            // Let thread 1 start and get the lock
            thread1.Start();
            resetEvent3.Wait();

            // Start thread 2 and give it enough time to get blocked on the lock
            thread2.Start();
            Thread.Sleep(5000);

            // Make sure helper1 has the lock still, and helper2 doesn't
            Assert.IsNotNull(helper1.CacheLock);
            Assert.IsNull(helper2.CacheLock);

            // Allow thread1 to give up the lock, and wait for helper2 to get it
            resetEvent1.Set();
            resetEvent4.Wait();
            resetEvent4.Reset();

            // Make sure helper1 gave it up properly, and helper2 now owns the lock
            Assert.IsNull(helper1.CacheLock);
            Assert.IsNotNull(helper2.CacheLock);

            // Allow thread2 to give up the lock, and wait for it to complete
            resetEvent2.Set();
            resetEvent4.Wait();

            // Make sure thread2 cleaned up after itself as well
            Assert.IsNull(helper2.CacheLock);
        }
예제 #4
0
        public async Task TwoRegisteredCachesRemainInSyncTestAsync()
        {
            if (File.Exists(s_storageCreationProperties.CacheFilePath))
            {
                File.Delete(s_storageCreationProperties.CacheFilePath);
            }

            var helper = await MsalCacheHelper.CreateAsync(s_storageCreationProperties).ConfigureAwait(true);

            helper._cacheWatcher.EnableRaisingEvents = false;

            // Intentionally write the file after creating the MsalCacheHelper to avoid the initial inner PCA being created only to read garbage
            string startString = "Something to start with";
            var    startBytes  = ProtectedData.Protect(Encoding.UTF8.GetBytes(startString), optionalEntropy: null, scope: DataProtectionScope.CurrentUser);
            await File.WriteAllBytesAsync(s_storageCreationProperties.CacheFilePath, startBytes).ConfigureAwait(true);

            var cache1 = new MockTokenCache();
            var cache2 = new MockTokenCache();
            var cache3 = new MockTokenCache();

            helper.RegisterCache(cache1);
            helper.RegisterCache(cache2);
            helper.RegisterCache(cache3);

            // One call from register
            Assert.AreEqual(1, cache1.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache2.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(startString, cache1.LastDeserializedString);
            Assert.AreEqual(startString, cache2.LastDeserializedString);
            Assert.AreEqual(startString, cache3.LastDeserializedString);

            var args1 = new TokenCacheNotificationArgs
            {
                TokenCache = cache1
            };

            var args2 = new TokenCacheNotificationArgs
            {
                TokenCache = cache2
            };

            var args3 = new TokenCacheNotificationArgs
            {
                TokenCache = cache3
            };

            var changedString = "Hey look, the file changed";

            helper.BeforeAccessNotification(args1);
            cache1.LastDeserializedString = changedString;
            args1.HasStateChanged         = true;
            helper.AfterAccessNotification(args1);

            helper.BeforeAccessNotification(args2);
            helper.AfterAccessNotification(args2);

            helper.BeforeAccessNotification(args3);
            helper.AfterAccessNotification(args3);

            // Still only one call from register
            Assert.AreEqual(1, cache1.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache2.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_MergeCache);

            // Cache 1 should deserialize, in spite of just writing, just in case another process wrote in the intervening time
            Assert.AreEqual(1, cache1.DeserializeMsalV3_ClearCache);

            // Caches 2 and 3 simply need to deserialize
            Assert.AreEqual(1, cache2.DeserializeMsalV3_ClearCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_ClearCache);

            Assert.AreEqual(changedString, cache1.LastDeserializedString);
            Assert.AreEqual(changedString, cache2.LastDeserializedString);
            Assert.AreEqual(changedString, cache3.LastDeserializedString);

            File.Delete(s_storageCreationProperties.CacheFilePath);
            File.Delete(s_storageCreationProperties.CacheFilePath + ".version");
        }
예제 #5
0
        public void LockTimeoutTest()
        {
            // Total of 1000ms delay
            _storageCreationPropertiesBuilder.CustomizeLockRetry(20, 100);
            var properties = _storageCreationPropertiesBuilder.Build();


            var cache1  = new MockTokenCache();
            var helper1 = new MsalCacheHelper(
                cache1,
                new MsalCacheStorage(properties, _logger),
                _logger);

            var cache2  = new MockTokenCache();
            var helper2 = new MsalCacheHelper(
                cache2,
                new MsalCacheStorage(properties, _logger),
                _logger);

            //Test signalling thread 1
            var resetEvent1 = new ManualResetEventSlim(initialState: false);

            //Test signalling thread 2
            var resetEvent2 = new ManualResetEventSlim(initialState: false);

            //Thread 1 signalling test
            var resetEvent3 = new ManualResetEventSlim(initialState: false);

            // Thread 2 signalling test
            var resetEvent4 = new ManualResetEventSlim(initialState: false);

            var thread1 = new Thread(() =>
            {
                var args = new TokenCacheNotificationArgs(cache1, string.Empty, null, false);

                helper1.BeforeAccessNotification(args);
                // Indicate we are waiting
                resetEvent2.Set();
                resetEvent1.Wait();
                helper1.AfterAccessNotification(args);

                // Let thread 1 exit
                resetEvent3.Set();
            });

            Stopwatch getTime = new Stopwatch();

            var thread2 = new Thread(() =>
            {
                var args = new TokenCacheNotificationArgs(cache2, string.Empty, null, false);
                getTime.Start();
                try
                {
                    helper2.BeforeAccessNotification(args);
                }
                catch (InvalidOperationException)
                {
                    // Invalid operation is the exception thrown if the lock cannot be acquired
                    getTime.Stop();
                }

                resetEvent1.Set();
            });

            // Let thread 1 start and get the lock
            thread1.Start();

            // Wait for thread one to get into the lock
            resetEvent2.Wait();

            // Start thread 2 and give it enough time to get blocked on the lock
            thread2.Start();

            // Wait for the seconf thread to finish
            resetEvent1.Wait();

            Assert.IsTrue(getTime.ElapsedMilliseconds > 2000);
        }
        public async Task ThreeRegisteredCachesRemainInSyncTestAsync()
        {
            if (File.Exists(s_storageCreationProperties.CacheFilePath))
            {
                File.Delete(s_storageCreationProperties.CacheFilePath);
            }

            var helper = await MsalCacheHelper.CreateAsync(s_storageCreationProperties).ConfigureAwait(true);
            helper._cacheWatcher.EnableRaisingEvents = false;

            // Intentionally write the file after creating the MsalCacheHelper to avoid the initial inner PCA being created only to read garbage
            string startString = "Something to start with";
            var startBytes = ProtectedData.Protect(Encoding.UTF8.GetBytes(startString), optionalEntropy: null, scope: DataProtectionScope.CurrentUser);
            await File.WriteAllBytesAsync(s_storageCreationProperties.CacheFilePath, startBytes).ConfigureAwait(true);

            var cache1 = new MockTokenCache();
            var cache2 = new MockTokenCache();
            var cache3 = new MockTokenCache();

            helper.RegisterCache(cache1);
            helper.RegisterCache(cache2);
            helper.RegisterCache(cache3);

            var storeVersion0 = helper._store.LastVersionToken;

            // One call from register
            Assert.AreEqual(1, cache1.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache2.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(startString, cache1.LastDeserializedString);
            Assert.AreEqual(startString, cache2.LastDeserializedString);
            Assert.AreEqual(startString, cache3.LastDeserializedString);

            var args1 = new TokenCacheNotificationArgs
            {
                TokenCache = cache1
            };

            var args2 = new TokenCacheNotificationArgs
            {
                TokenCache = cache2
            };

            var args3 = new TokenCacheNotificationArgs
            {
                TokenCache = cache3
            };

            var changedString = "Hey look, the file changed";

            helper.BeforeAccessNotification(args1);
            cache1.LastDeserializedString = changedString;
            args1.HasStateChanged = true;
            helper.AfterAccessNotification(args1);

            // Note: Here, we haven't yet read anything in, so helper._store.LastVersionToken is out of date.
            // Validate that we at least have updated the version of the cache that was serialized.
            var cache1Version = helper._registeredCaches[cache1];
            Assert.AreNotEqual(storeVersion0, cache1Version);

            helper.BeforeAccessNotification(args2);
            helper.AfterAccessNotification(args2);

            var storeVersion1 = helper._store.LastVersionToken;
            Assert.AreEqual(cache1Version, storeVersion1);

            helper.BeforeAccessNotification(args3);
            helper.AfterAccessNotification(args3);

            var storeVersion2 = helper._store.LastVersionToken;
            Assert.AreEqual(storeVersion1, storeVersion2);

            // Still only one call from register
            Assert.AreEqual(1, cache1.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache2.DeserializeMsalV3_MergeCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_MergeCache);

            // Cache 1 shouldn't need to deserialize because it wrote the new data.
            Assert.AreEqual(0, cache1.DeserializeMsalV3_ClearCache);

            // Caches 2 and three should need to deserialize
            Assert.AreEqual(1, cache2.DeserializeMsalV3_ClearCache);
            Assert.AreEqual(1, cache3.DeserializeMsalV3_ClearCache);

            Assert.AreEqual(changedString, cache1.LastDeserializedString);
            Assert.AreEqual(changedString, cache2.LastDeserializedString);
            Assert.AreEqual(changedString, cache3.LastDeserializedString);

            File.Delete(s_storageCreationProperties.CacheFilePath);
            File.Delete(s_storageCreationProperties.CacheFilePath + ".version");
        }