//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Configuration { using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Runtime; using System.Runtime.Diagnostics; using System.Security; using System.Security.Permissions; using System.ServiceModel; using System.ServiceModel.Diagnostics; using System.Xml; [ConfigurationPermission(SecurityAction.InheritanceDemand, Unrestricted = true)] public abstract class ServiceModelExtensionElement : ServiceModelConfigurationElement, IConfigurationContextProviderInternal { [Fx.Tag.SecurityNote(Critical = "Stores information used in a security decision.")] [SecurityCritical] EvaluationContextHelper contextHelper; ContextInformation containingEvaluationContext = null; string configurationElementName = String.Empty; string extensionCollectionName = String.Empty; bool modified = false; Type thisType; protected ServiceModelExtensionElement() : base() { } [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method UnsafeLookupCollection which elevates in order to load config.", Safe = "Does not leak any config objects.")] [SecuritySafeCritical] internal bool CanAdd(string extensionCollectionName, ContextInformation evaluationContext) { bool retVal = false; ExtensionElementCollection collection = ExtensionsSection.UnsafeLookupCollection(extensionCollectionName, evaluationContext); if (null != collection && collection.Count != 0) { string thisAssemblyQualifiedName = ThisType.AssemblyQualifiedName; string thisTypeName = ExtensionElement.GetTypeName(thisAssemblyQualifiedName); foreach (ExtensionElement extensionElement in collection) { string extensionTypeName = extensionElement.Type; if (extensionTypeName.Equals(thisAssemblyQualifiedName, StringComparison.Ordinal)) { retVal = true; break; } if (extensionElement.TypeName.Equals(thisTypeName, StringComparison.Ordinal)) { Type extensionType = Type.GetType(extensionTypeName, false); if (extensionType != null && extensionType.Equals(ThisType)) { retVal = true; break; } } } if (!retVal && DiagnosticUtility.ShouldTraceWarning) { TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ConfiguredExtensionTypeNotFound, SR.GetString(SR.TraceCodeConfiguredExtensionTypeNotFound), this.CreateCanAddRecord(extensionCollectionName), this, null); } } else if (DiagnosticUtility.ShouldTraceWarning) { int traceCode; string traceDescription; if (collection != null && collection.Count == 0) { traceCode = TraceCode.ExtensionCollectionIsEmpty; traceDescription = SR.GetString(SR.TraceCodeExtensionCollectionIsEmpty); } else { traceCode = TraceCode.ExtensionCollectionDoesNotExist; traceDescription = SR.GetString(SR.TraceCodeExtensionCollectionDoesNotExist); } TraceUtility.TraceEvent(TraceEventType.Warning, traceCode, traceDescription, this.CreateCanAddRecord(extensionCollectionName), this, null); } return retVal; } public string ConfigurationElementName { get { if (String.IsNullOrEmpty(this.configurationElementName)) { this.configurationElementName = this.GetConfigurationElementName(); } return this.configurationElementName; } internal set { if (!string.IsNullOrEmpty(this.configurationElementName)) { Fx.Assert(this.configurationElementName == value, string.Format(System.Globalization.CultureInfo.InvariantCulture, "The configuration element name has already being set to '{0} and cannot be reset to '{1}'", this.configurationElementName, value)); return; } this.configurationElementName = value; } } internal ContextInformation ContainingEvaluationContext { get { return this.containingEvaluationContext; } set { this.containingEvaluationContext = value; } } Type ThisType { get { if (thisType == null) { thisType = this.GetType(); } return thisType; } } public virtual void CopyFrom(ServiceModelExtensionElement from) { if (this.IsReadOnly()) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigReadOnly))); } if (from == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("from"); } } DictionaryTraceRecord CreateCanAddRecord(string extensionCollectionName) { Dictionary values = new Dictionary(2); values["ElementType"] = System.Runtime.Diagnostics.DiagnosticTraceBase.XmlEncode(ThisType.AssemblyQualifiedName); values["CollectionName"] = ConfigurationStrings.ExtensionsSectionPath + "/" + extensionCollectionName; return new DictionaryTraceRecord(values); } internal void DeserializeInternal(XmlReader reader, bool serializeCollectionKey) { this.DeserializeElement(reader, serializeCollectionKey); } internal string ExtensionCollectionName { set { this.extensionCollectionName = value; } get { return this.extensionCollectionName; } } internal ContextInformation EvalContext { get { return this.EvaluationContext; } } internal object FromProperty(ConfigurationProperty property) { return this[property]; } [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical methods UnsafeLookupCollection and UnsafeLookupAssociatedCollection which elevate in order to load config.", Safe = "Does not leak any config objects.")] [SecuritySafeCritical] string GetConfigurationElementName() { string configurationElementName = String.Empty; ExtensionElementCollection collection = null; Type extensionSectionType = ThisType; ContextInformation evaluationContext = this.ContainingEvaluationContext; if (evaluationContext == null) { evaluationContext = ConfigurationHelpers.GetEvaluationContext(this); } if (String.IsNullOrEmpty(this.extensionCollectionName)) { if (DiagnosticUtility.ShouldTraceWarning) { TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ExtensionCollectionNameNotFound, SR.GetString(SR.TraceCodeExtensionCollectionNameNotFound), this, (Exception)null); } collection = ExtensionsSection.UnsafeLookupAssociatedCollection(ThisType, evaluationContext, out this.extensionCollectionName); } else { collection = ExtensionsSection.UnsafeLookupCollection(this.extensionCollectionName, evaluationContext); } if (null == collection) { if (String.IsNullOrEmpty(this.extensionCollectionName)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigNoExtensionCollectionAssociatedWithType, extensionSectionType.AssemblyQualifiedName), this.ElementInformation.Source, this.ElementInformation.LineNumber)); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigExtensionCollectionNotFound, this.extensionCollectionName), this.ElementInformation.Source, this.ElementInformation.LineNumber)); } } for (int i = 0; i < collection.Count; i++) { ExtensionElement collectionElement = collection[i]; // Optimize for assembly qualified names. if (collectionElement.Type.Equals(extensionSectionType.AssemblyQualifiedName, StringComparison.Ordinal)) { configurationElementName = collectionElement.Name; break; } // Check type directly for the case that the extension is registered with something less than // an full assembly qualified name. Type collectionElementType = Type.GetType(collectionElement.Type, false); if (null != collectionElementType && extensionSectionType.Equals(collectionElementType)) { configurationElementName = collectionElement.Name; break; } } if (String.IsNullOrEmpty(configurationElementName)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigExtensionTypeNotRegisteredInCollection, extensionSectionType.AssemblyQualifiedName, this.extensionCollectionName), this.ElementInformation.Source, this.ElementInformation.LineNumber)); } return configurationElementName; } internal void InternalInitializeDefault() { this.InitializeDefault(); } protected override bool IsModified() { return this.modified | base.IsModified(); } internal bool IsModifiedInternal() { return this.IsModified(); } internal ConfigurationPropertyCollection PropertiesInternal { get { return this.Properties; } } internal void ResetModifiedInternal() { this.ResetModified(); } protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) { base.SerializeElement(writer, serializeCollectionKey); return true; } internal bool SerializeInternal(XmlWriter writer, bool serializeCollectionKey) { return this.SerializeElement(writer, serializeCollectionKey); } internal void SetReadOnlyInternal() { this.SetReadOnly(); } [Fx.Tag.SecurityNote(Critical = "Accesses critical field contextHelper.")] [SecurityCritical] protected override void Reset(ConfigurationElement parentElement) { this.contextHelper.OnReset(parentElement); base.Reset(parentElement); } ContextInformation IConfigurationContextProviderInternal.GetEvaluationContext() { return this.EvaluationContext; } [Fx.Tag.SecurityNote(Critical = "Accesses critical field contextHelper.", Miscellaneous = "RequiresReview -- the return value will be used for a security decision -- see comment in interface definition.")] [SecurityCritical] ContextInformation IConfigurationContextProviderInternal.GetOriginalEvaluationContext() { return this.contextHelper.GetOriginalContext(this); } } }