PolicyReader.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Description
  5. {
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using System.Xml;
  10. using WsdlNS = System.Web.Services.Description;
  11. using System.Globalization;
  12. //
  13. // PolicyReader is a complex nested class in the MetadataImporter
  14. //
  15. public abstract partial class MetadataImporter
  16. {
  17. internal MetadataImporterQuotas Quotas;
  18. PolicyReader policyNormalizer = null;
  19. internal delegate void PolicyWarningHandler(XmlElement contextAssertion, string warningMessage);
  20. // Consider, [....], make this public?
  21. internal event PolicyWarningHandler PolicyWarningOccured;
  22. internal IEnumerable<IEnumerable<XmlElement>> NormalizePolicy(IEnumerable<XmlElement> policyAssertions)
  23. {
  24. if (this.policyNormalizer == null)
  25. {
  26. this.policyNormalizer = new PolicyReader(this);
  27. }
  28. return this.policyNormalizer.NormalizePolicy(policyAssertions);
  29. }
  30. //DevNote: The error handling goal for this class is to NEVER throw an exception.
  31. // * Any Ignored Policy should generate a warning
  32. // * All policy parsing errors should be logged as warnings in the WSDLImporter.Errors collection.
  33. sealed class PolicyReader
  34. {
  35. int nodesRead = 0;
  36. readonly MetadataImporter metadataImporter;
  37. internal PolicyReader(MetadataImporter metadataImporter)
  38. {
  39. this.metadataImporter = metadataImporter;
  40. }
  41. static IEnumerable<XmlElement> Empty = new PolicyHelper.EmptyEnumerable<XmlElement>();
  42. static IEnumerable<IEnumerable<XmlElement>> EmptyEmpty = new PolicyHelper.SingleEnumerable<IEnumerable<XmlElement>>(new PolicyHelper.EmptyEnumerable<XmlElement>());
  43. //
  44. // the core policy reading logic
  45. // each step returns a list of lists -- an "and of ors":
  46. // each inner list is a policy alternative: it contains the set of assertions that comprise the alternative
  47. // the outer list represents the choice between alternatives
  48. //
  49. IEnumerable<IEnumerable<XmlElement>> ReadNode(XmlNode node, XmlElement contextAssertion, YieldLimiter yieldLimiter)
  50. {
  51. if (nodesRead >= this.metadataImporter.Quotas.MaxPolicyNodes)
  52. {
  53. if (nodesRead == this.metadataImporter.Quotas.MaxPolicyNodes)
  54. {
  55. // add wirning once
  56. string warningMsg = SR.GetString(SR.ExceededMaxPolicyComplexity, node.Name, PolicyHelper.GetFragmentIdentifier((XmlElement)node));
  57. metadataImporter.PolicyWarningOccured.Invoke(contextAssertion, warningMsg);
  58. nodesRead++;
  59. }
  60. return EmptyEmpty;
  61. }
  62. nodesRead++;
  63. IEnumerable<IEnumerable<XmlElement>> nodes = EmptyEmpty;
  64. switch (PolicyHelper.GetNodeType(node))
  65. {
  66. case PolicyHelper.NodeType.Policy:
  67. case PolicyHelper.NodeType.All:
  68. nodes = ReadNode_PolicyOrAll((XmlElement)node, contextAssertion, yieldLimiter);
  69. break;
  70. case PolicyHelper.NodeType.ExactlyOne:
  71. nodes = ReadNode_ExactlyOne((XmlElement)node, contextAssertion, yieldLimiter);
  72. break;
  73. case PolicyHelper.NodeType.Assertion:
  74. nodes = ReadNode_Assertion((XmlElement)node, yieldLimiter);
  75. break;
  76. case PolicyHelper.NodeType.PolicyReference:
  77. nodes = ReadNode_PolicyReference((XmlElement)node, contextAssertion, yieldLimiter);
  78. break;
  79. case PolicyHelper.NodeType.UnrecognizedWSPolicy:
  80. string warningMsg = SR.GetString(SR.UnrecognizedPolicyElementInNamespace, node.Name, node.NamespaceURI);
  81. metadataImporter.PolicyWarningOccured.Invoke(contextAssertion, warningMsg);
  82. break;
  83. //consider [....], add more error handling here. default?
  84. }
  85. return nodes;
  86. }
  87. IEnumerable<IEnumerable<XmlElement>> ReadNode_PolicyReference(XmlElement element, XmlElement contextAssertion, YieldLimiter yieldLimiter)
  88. {
  89. string idRef = element.GetAttribute(MetadataStrings.WSPolicy.Attributes.URI);
  90. if (idRef == null)
  91. {
  92. string warningMsg = SR.GetString(SR.PolicyReferenceMissingURI, MetadataStrings.WSPolicy.Attributes.URI);
  93. metadataImporter.PolicyWarningOccured.Invoke(contextAssertion, warningMsg);
  94. return EmptyEmpty;
  95. }
  96. else if (idRef == string.Empty)
  97. {
  98. string warningMsg = SR.GetString(SR.PolicyReferenceInvalidId);
  99. metadataImporter.PolicyWarningOccured.Invoke(contextAssertion, warningMsg);
  100. return EmptyEmpty;
  101. }
  102. XmlElement policy = metadataImporter.ResolvePolicyReference(idRef, contextAssertion);
  103. if (policy == null)
  104. {
  105. string warningMsg = SR.GetString(SR.UnableToFindPolicyWithId, idRef);
  106. metadataImporter.PolicyWarningOccured.Invoke(contextAssertion, warningMsg);
  107. return EmptyEmpty;
  108. }
  109. //
  110. // Since we looked up a reference, the context assertion changes.
  111. //
  112. return ReadNode_PolicyOrAll(policy, policy, yieldLimiter);
  113. }
  114. IEnumerable<IEnumerable<XmlElement>> ReadNode_Assertion(XmlElement element, YieldLimiter yieldLimiter)
  115. {
  116. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  117. yield return Empty;
  118. else
  119. yield return new PolicyHelper.SingleEnumerable<XmlElement>(element);
  120. }
  121. IEnumerable<IEnumerable<XmlElement>> ReadNode_ExactlyOne(XmlElement element, XmlElement contextAssertion, YieldLimiter yieldLimiter)
  122. {
  123. foreach (XmlNode child in element.ChildNodes)
  124. {
  125. if (child.NodeType == XmlNodeType.Element)
  126. {
  127. foreach (IEnumerable<XmlElement> alternative in ReadNode(child, contextAssertion, yieldLimiter))
  128. {
  129. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  130. {
  131. yield break;
  132. }
  133. else
  134. {
  135. yield return alternative;
  136. }
  137. }
  138. }
  139. }
  140. }
  141. IEnumerable<IEnumerable<XmlElement>> ReadNode_PolicyOrAll(XmlElement element, XmlElement contextAssertion, YieldLimiter yieldLimiter)
  142. {
  143. IEnumerable<IEnumerable<XmlElement>> target = EmptyEmpty;
  144. foreach (XmlNode child in element.ChildNodes)
  145. {
  146. if (child.NodeType == XmlNodeType.Element)
  147. {
  148. IEnumerable<IEnumerable<XmlElement>> childPolicy = ReadNode(child, contextAssertion, yieldLimiter);
  149. target = PolicyHelper.CrossProduct<XmlElement>(target, childPolicy, yieldLimiter);
  150. }
  151. }
  152. return target;
  153. }
  154. internal IEnumerable<IEnumerable<XmlElement>> NormalizePolicy(IEnumerable<XmlElement> policyAssertions)
  155. {
  156. IEnumerable<IEnumerable<XmlElement>> target = EmptyEmpty;
  157. YieldLimiter yieldLimiter = new YieldLimiter(this.metadataImporter.Quotas.MaxYields, this.metadataImporter);
  158. foreach (XmlElement child in policyAssertions)
  159. {
  160. IEnumerable<IEnumerable<XmlElement>> childPolicy = ReadNode(child, child, yieldLimiter);
  161. target = PolicyHelper.CrossProduct<XmlElement>(target, childPolicy, yieldLimiter);
  162. }
  163. return target;
  164. }
  165. }
  166. internal class YieldLimiter
  167. {
  168. int maxYields;
  169. int yieldsHit;
  170. readonly MetadataImporter metadataImporter;
  171. internal YieldLimiter(int maxYields, MetadataImporter metadataImporter)
  172. {
  173. this.metadataImporter = metadataImporter;
  174. this.yieldsHit = 0;
  175. this.maxYields = maxYields;
  176. }
  177. internal bool IncrementAndLogIfExceededLimit()
  178. {
  179. if (++yieldsHit > maxYields)
  180. {
  181. string warningMsg = SR.GetString(SR.ExceededMaxPolicySize);
  182. metadataImporter.PolicyWarningOccured.Invoke(null, warningMsg);
  183. return true;
  184. }
  185. else
  186. {
  187. return false;
  188. }
  189. }
  190. }
  191. internal static class PolicyHelper
  192. {
  193. internal static string GetFragmentIdentifier(XmlElement element)
  194. {
  195. string id = element.GetAttribute(MetadataStrings.Wsu.Attributes.Id, MetadataStrings.Wsu.NamespaceUri);
  196. if (id == null)
  197. {
  198. id = element.GetAttribute(MetadataStrings.Xml.Attributes.Id, MetadataStrings.Xml.NamespaceUri);
  199. }
  200. if (string.IsNullOrEmpty(id))
  201. return string.Empty;
  202. else
  203. return string.Format(CultureInfo.InvariantCulture, "#{0}", id);
  204. }
  205. internal static bool IsPolicyURIs(XmlAttribute attribute)
  206. {
  207. return ((attribute.NamespaceURI == MetadataStrings.WSPolicy.NamespaceUri
  208. || attribute.NamespaceURI == MetadataStrings.WSPolicy.NamespaceUri15)
  209. && attribute.LocalName == MetadataStrings.WSPolicy.Attributes.PolicyURIs);
  210. }
  211. internal static NodeType GetNodeType(XmlNode node)
  212. {
  213. XmlElement currentElement = node as XmlElement;
  214. if (currentElement == null)
  215. return PolicyHelper.NodeType.NonElement;
  216. if (currentElement.NamespaceURI != MetadataStrings.WSPolicy.NamespaceUri
  217. && currentElement.NamespaceURI != MetadataStrings.WSPolicy.NamespaceUri15)
  218. return NodeType.Assertion;
  219. else if (currentElement.LocalName == MetadataStrings.WSPolicy.Elements.Policy)
  220. return NodeType.Policy;
  221. else if (currentElement.LocalName == MetadataStrings.WSPolicy.Elements.All)
  222. return NodeType.All;
  223. else if (currentElement.LocalName == MetadataStrings.WSPolicy.Elements.ExactlyOne)
  224. return NodeType.ExactlyOne;
  225. else if (currentElement.LocalName == MetadataStrings.WSPolicy.Elements.PolicyReference)
  226. return NodeType.PolicyReference;
  227. else
  228. return PolicyHelper.NodeType.UnrecognizedWSPolicy;
  229. }
  230. //
  231. // some helpers for dealing with ands of ors
  232. //
  233. internal static IEnumerable<IEnumerable<T>> CrossProduct<T>(IEnumerable<IEnumerable<T>> xs, IEnumerable<IEnumerable<T>> ys, YieldLimiter yieldLimiter)
  234. {
  235. foreach (IEnumerable<T> x in AtLeastOne<T>(xs, yieldLimiter))
  236. {
  237. foreach (IEnumerable<T> y in AtLeastOne<T>(ys, yieldLimiter))
  238. {
  239. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  240. {
  241. yield break;
  242. }
  243. else
  244. {
  245. yield return Merge<T>(x, y, yieldLimiter);
  246. }
  247. }
  248. }
  249. }
  250. static IEnumerable<IEnumerable<T>> AtLeastOne<T>(IEnumerable<IEnumerable<T>> xs, YieldLimiter yieldLimiter)
  251. {
  252. bool gotOne = false;
  253. foreach (IEnumerable<T> x in xs)
  254. {
  255. gotOne = true;
  256. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  257. {
  258. yield break;
  259. }
  260. else
  261. {
  262. yield return x;
  263. }
  264. }
  265. if (!gotOne)
  266. {
  267. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  268. {
  269. yield break;
  270. }
  271. else
  272. {
  273. yield return new EmptyEnumerable<T>();
  274. }
  275. }
  276. }
  277. static IEnumerable<T> Merge<T>(IEnumerable<T> e1, IEnumerable<T> e2, YieldLimiter yieldLimiter)
  278. {
  279. foreach (T t1 in e1)
  280. {
  281. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  282. {
  283. yield break;
  284. }
  285. else
  286. {
  287. yield return t1;
  288. }
  289. }
  290. foreach (T t2 in e2)
  291. {
  292. if (yieldLimiter.IncrementAndLogIfExceededLimit())
  293. {
  294. yield break;
  295. }
  296. else
  297. {
  298. yield return t2;
  299. }
  300. }
  301. }
  302. //
  303. // some helper enumerators
  304. //
  305. internal class EmptyEnumerable<T> : IEnumerable<T>, IEnumerator<T>
  306. {
  307. IEnumerator IEnumerable.GetEnumerator()
  308. {
  309. return this.GetEnumerator();
  310. }
  311. public IEnumerator<T> GetEnumerator()
  312. {
  313. return this;
  314. }
  315. object IEnumerator.Current
  316. {
  317. get { return this.Current; }
  318. }
  319. public T Current
  320. {
  321. get
  322. {
  323. #pragma warning suppress 56503 // [....], IEnumerator guidelines, Current throws exception before calling MoveNext
  324. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.NoValue0)));
  325. }
  326. }
  327. public bool MoveNext()
  328. {
  329. return false;
  330. }
  331. public void Dispose()
  332. {
  333. }
  334. void IEnumerator.Reset()
  335. {
  336. }
  337. }
  338. internal class SingleEnumerable<T> : IEnumerable<T>
  339. {
  340. T value;
  341. internal SingleEnumerable(T value)
  342. {
  343. this.value = value;
  344. }
  345. IEnumerator IEnumerable.GetEnumerator()
  346. {
  347. return this.GetEnumerator();
  348. }
  349. public IEnumerator<T> GetEnumerator()
  350. {
  351. yield return this.value;
  352. }
  353. }
  354. //
  355. // the NodeType enum
  356. //
  357. internal enum NodeType
  358. {
  359. NonElement,
  360. Policy,
  361. All,
  362. ExactlyOne,
  363. Assertion,
  364. PolicyReference,
  365. UnrecognizedWSPolicy,
  366. }
  367. }
  368. }
  369. }