XmlEncryptionExtensionsTests.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Linq;
  5. using System.Xml.Linq;
  6. using Microsoft.AspNetCore.DataProtection.Internal;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using Moq;
  9. using Xunit;
  10. namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
  11. {
  12. public class XmlEncryptionExtensionsTests
  13. {
  14. [Fact]
  15. public void DecryptElement_NothingToDecrypt_ReturnsOriginalElement()
  16. {
  17. // Arrange
  18. var original = XElement.Parse(@"<element />");
  19. // Act
  20. var retVal = original.DecryptElement(activator: null);
  21. // Assert
  22. Assert.Same(original, retVal);
  23. XmlAssert.Equal("<element />", original); // unmutated
  24. }
  25. [Fact]
  26. public void DecryptElement_RootNodeRequiresDecryption_Success()
  27. {
  28. // Arrange
  29. var original = XElement.Parse(@"
  30. <x:encryptedSecret decryptorType='theDecryptor' xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  31. <node />
  32. </x:encryptedSecret>");
  33. var mockActivator = new Mock<IActivator>();
  34. mockActivator.ReturnDecryptedElementGivenDecryptorTypeNameAndInput("theDecryptor", "<node />", "<newNode />");
  35. var serviceCollection = new ServiceCollection();
  36. serviceCollection.AddSingleton<IActivator>(mockActivator.Object);
  37. var services = serviceCollection.BuildServiceProvider();
  38. var activator = services.GetActivator();
  39. // Act
  40. var retVal = original.DecryptElement(activator);
  41. // Assert
  42. XmlAssert.Equal("<newNode />", retVal);
  43. }
  44. [Fact]
  45. public void DecryptElement_MultipleNodesRequireDecryption_AvoidsRecursion_Success()
  46. {
  47. // Arrange
  48. var original = XElement.Parse(@"
  49. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  50. <x:encryptedSecret decryptorType='myDecryptor'>
  51. <node1 />
  52. </x:encryptedSecret>
  53. <node2 x:requiresEncryption='false'>
  54. <![CDATA[This data should stick around.]]>
  55. <x:encryptedSecret decryptorType='myDecryptor'>
  56. <node3 />
  57. </x:encryptedSecret>
  58. </node2>
  59. </rootNode>");
  60. var expected = @"
  61. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  62. <node1_decrypted>
  63. <x:encryptedSecret>nested</x:encryptedSecret>
  64. </node1_decrypted>
  65. <node2 x:requiresEncryption='false'>
  66. <![CDATA[This data should stick around.]]>
  67. <node3_decrypted>
  68. <x:encryptedSecret>nested</x:encryptedSecret>
  69. </node3_decrypted>
  70. </node2>
  71. </rootNode>";
  72. var mockDecryptor = new Mock<IXmlDecryptor>();
  73. mockDecryptor
  74. .Setup(o => o.Decrypt(It.IsAny<XElement>()))
  75. .Returns<XElement>(el => new XElement(el.Name.LocalName + "_decrypted", new XElement(XmlConstants.EncryptedSecretElementName, "nested")));
  76. var mockActivator = new Mock<IActivator>();
  77. mockActivator.Setup(o => o.CreateInstance(typeof(IXmlDecryptor), "myDecryptor")).Returns(mockDecryptor.Object);
  78. var serviceCollection = new ServiceCollection();
  79. serviceCollection.AddSingleton<IActivator>(mockActivator.Object);
  80. var services = serviceCollection.BuildServiceProvider();
  81. var activator = services.GetActivator();
  82. // Act
  83. var retVal = original.DecryptElement(activator);
  84. // Assert
  85. XmlAssert.Equal(expected, retVal);
  86. }
  87. [Fact]
  88. public void EncryptIfNecessary_NothingToEncrypt_ReturnsNull()
  89. {
  90. // Arrange
  91. var original = XElement.Parse(@"<element />");
  92. var xmlEncryptor = new Mock<IXmlEncryptor>(MockBehavior.Strict).Object;
  93. // Act
  94. var retVal = xmlEncryptor.EncryptIfNecessary(original);
  95. // Assert
  96. Assert.Null(retVal);
  97. XmlAssert.Equal("<element />", original); // unmutated
  98. }
  99. [Fact]
  100. public void EncryptIfNecessary_RootNodeRequiresEncryption_Success()
  101. {
  102. // Arrange
  103. var original = XElement.Parse(@"<rootNode x:requiresEncryption='true' xmlns:x='http://schemas.asp.net/2015/03/dataProtection' />");
  104. var mockXmlEncryptor = new Mock<IXmlEncryptor>();
  105. mockXmlEncryptor.Setup(o => o.Encrypt(It.IsAny<XElement>())).Returns(new EncryptedXmlInfo(new XElement("theElement"), typeof(MyXmlDecryptor)));
  106. // Act
  107. var retVal = mockXmlEncryptor.Object.EncryptIfNecessary(original);
  108. // Assert
  109. XmlAssert.Equal(@"<rootNode x:requiresEncryption='true' xmlns:x='http://schemas.asp.net/2015/03/dataProtection' />", original); // unmutated
  110. Assert.Equal(XmlConstants.EncryptedSecretElementName, retVal.Name);
  111. Assert.Equal(typeof(MyXmlDecryptor).AssemblyQualifiedName, (string)retVal.Attribute(XmlConstants.DecryptorTypeAttributeName));
  112. XmlAssert.Equal("<theElement />", retVal.Descendants().Single());
  113. }
  114. [Fact]
  115. public void EncryptIfNecessary_MultipleNodesRequireEncryption_Success()
  116. {
  117. // Arrange
  118. var original = XElement.Parse(@"
  119. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  120. <node1 x:requiresEncryption='true'>
  121. <![CDATA[This data should be encrypted.]]>
  122. </node1>
  123. <node2 x:requiresEncryption='false'>
  124. <![CDATA[This data should stick around.]]>
  125. <node3 x:requiresEncryption='true'>
  126. <node4 x:requiresEncryption='true' />
  127. </node3>
  128. </node2>
  129. </rootNode>");
  130. var expected = String.Format(@"
  131. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  132. <x:encryptedSecret decryptorType='{0}'>
  133. <node1_encrypted />
  134. </x:encryptedSecret>
  135. <node2 x:requiresEncryption='false'>
  136. <![CDATA[This data should stick around.]]>
  137. <x:encryptedSecret decryptorType='{0}'>
  138. <node3_encrypted />
  139. </x:encryptedSecret>
  140. </node2>
  141. </rootNode>",
  142. typeof(MyXmlDecryptor).AssemblyQualifiedName);
  143. var mockXmlEncryptor = new Mock<IXmlEncryptor>();
  144. mockXmlEncryptor
  145. .Setup(o => o.Encrypt(It.IsAny<XElement>()))
  146. .Returns<XElement>(element => new EncryptedXmlInfo(new XElement(element.Name.LocalName + "_encrypted"), typeof(MyXmlDecryptor)));
  147. // Act
  148. var retVal = mockXmlEncryptor.Object.EncryptIfNecessary(original);
  149. // Assert
  150. XmlAssert.Equal(expected, retVal);
  151. }
  152. [Fact]
  153. public void EncryptIfNecessary_NullEncryptorWithRecursion_NoStackDive_Success()
  154. {
  155. // Arrange
  156. var original = XElement.Parse(@"
  157. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  158. <node1 x:requiresEncryption='true'>
  159. <![CDATA[This data should be encrypted.]]>
  160. </node1>
  161. <node2 x:requiresEncryption='false'>
  162. <![CDATA[This data should stick around.]]>
  163. <node3 x:requiresEncryption='true'>
  164. <node4 x:requiresEncryption='true' />
  165. </node3>
  166. </node2>
  167. </rootNode>");
  168. var expected = String.Format(@"
  169. <rootNode xmlns:x='http://schemas.asp.net/2015/03/dataProtection'>
  170. <x:encryptedSecret decryptorType='{0}'>
  171. <node1 x:requiresEncryption='true'>
  172. <![CDATA[This data should be encrypted.]]>
  173. </node1>
  174. </x:encryptedSecret>
  175. <node2 x:requiresEncryption='false'>
  176. <![CDATA[This data should stick around.]]>
  177. <x:encryptedSecret decryptorType='{0}'>
  178. <node3 x:requiresEncryption='true'>
  179. <node4 x:requiresEncryption='true' />
  180. </node3>
  181. </x:encryptedSecret>
  182. </node2>
  183. </rootNode>",
  184. typeof(MyXmlDecryptor).AssemblyQualifiedName);
  185. var mockXmlEncryptor = new Mock<IXmlEncryptor>();
  186. mockXmlEncryptor
  187. .Setup(o => o.Encrypt(It.IsAny<XElement>()))
  188. .Returns<XElement>(element => new EncryptedXmlInfo(new XElement(element), typeof(MyXmlDecryptor)));
  189. // Act
  190. var retVal = mockXmlEncryptor.Object.EncryptIfNecessary(original);
  191. // Assert
  192. XmlAssert.Equal(expected, retVal);
  193. }
  194. private sealed class MyXmlDecryptor : IXmlDecryptor
  195. {
  196. public XElement Decrypt(XElement encryptedElement)
  197. {
  198. throw new NotImplementedException();
  199. }
  200. }
  201. }
  202. }