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); }
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); }
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"); }
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"); }