using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Codelyzer.Analysis;
using Codelyzer.Analysis.Model;
using CTA.FeatureDetection.Common.Extensions;
using CTA.FeatureDetection.Common.Models.WCF;
using CTA.Rules.Common.WebConfigManagement;
namespace CTA.FeatureDetection.Common.WCFConfigUtils
{
public class WCFBindingAndTransportUtil
{
///
/// Get a Dictionary of bindings and transport modes from the Analyzer Result.
///
/// Codelyzer Analysis object
/// Dictionary with binding name as key and BindingConfiguration as value
public static Dictionary GetBindingAndTransport(AnalyzerResult analyzerResult)
{
string projectDir = analyzerResult.ProjectResult.ProjectRootPath;
string webConfigFile = Path.Combine(projectDir, Rules.Config.Constants.WebConfig);
string appConfigFile = Path.Combine(projectDir, Rules.Config.Constants.AppConfig);
var bindingsTransportMap = new Dictionary();
if (File.Exists(webConfigFile) || File.Exists(appConfigFile))
{
ConfigBasedCheck(projectDir, bindingsTransportMap);
}
var projectWorkspace = analyzerResult.ProjectResult;
CodeBasedCheck(projectWorkspace, bindingsTransportMap);
return bindingsTransportMap;
}
///
/// Check for Binding and Mode in Config based WCF Service
///
/// Project Directory for Config based WCF Service
/// Dictionary of binding and transport mode
public static void ConfigBasedCheck(string projectDir, Dictionary bindingsTransportMap)
{
var webConfig = WebConfigManager.LoadWebConfigAsXDocument(projectDir);
var appConfig = WebConfigManager.LoadAppConfigAsXDocument(projectDir);
if (webConfig !=null && webConfig.ContainsElement(Constants.WCFBindingElementPath))
{
BindingTagCheck(webConfig, bindingsTransportMap);
}
else if (appConfig != null && appConfig.ContainsElement(Constants.WCFBindingElementPath))
{
BindingTagCheck(appConfig, bindingsTransportMap);
}
else if (webConfig != null && webConfig.ContainsElement(Constants.WCFEndpointElementPath))
{
EndpointTagCheck(webConfig, bindingsTransportMap);
}
else if (appConfig != null && appConfig.ContainsElement(Constants.WCFEndpointElementPath))
{
EndpointTagCheck(appConfig, bindingsTransportMap);
}
}
///
/// Given XML Config with element, check for binding and transport mode.
///
/// XML object for config
/// Dictionary of binding and transport mode
public static void BindingTagCheck(WebConfigXDocument config, Dictionary bindingsTransportMap)
{
var bindingsElement = config.GetElementByPath(Constants.WCFBindingElementPath);
var bindingsList = bindingsElement.Elements();
foreach (var binding in bindingsList)
{
var bindingName = binding.Name.ToString().ToLower();
var bindingElements = binding.Elements();
foreach (var bindingElement in bindingElements)
{
var modeList = bindingElement.Descendants(Constants.SecurityElement);
if (modeList.IsNullOrEmpty())
{
bindingsTransportMap[bindingName] = new BindingConfiguration();
}
foreach (var securityElement in modeList)
{
var modeName = securityElement.Attribute(Constants.ModeAttribute);
if (modeName != null)
{
bindingsTransportMap[bindingName] = new BindingConfiguration
{
Mode = modeName.Value.ToLower()
};
break;
}
}
}
}
}
///
/// Given XML Config with element check for binding.
///
/// XML object for config
/// Dictionary of binding and transport mode
public static void EndpointTagCheck(WebConfigXDocument config, Dictionary bindingsTransportMap)
{
var endpointElement = config.GetElementByPath(Constants.WCFEndpointElementPath);
var binding = endpointElement.Attribute(Constants.BindingAttribute);
if (binding != null)
{
bindingsTransportMap[binding.Value.ToLower()] = new BindingConfiguration();
}
}
///
/// Given XML Config with element, check for binding and transport mode.
///
/// XML object for config
/// Dictionary of binding and transport mode
public static void ProtocolTagCheck(WebConfigXDocument config, Dictionary bindingsTransportMap)
{
var protocolElement = config.GetElementByPath(Constants.WCFProtocolMappingElement);
var addProtocolElementsList = protocolElement.Elements(Constants.AddElement);
foreach (var addProtocolElement in addProtocolElementsList)
{
var binding = addProtocolElement.Attribute(Constants.BindingAttribute);
if (binding != null)
{
var bindingName = binding.Value.ToLower();
bindingsTransportMap[bindingName] = new BindingConfiguration();
}
}
}
///
/// Check for Binding and Mode in Code based WCF Service
///
/// ProjectWorkspace object of Code based WCF Service
/// Dictionary of binding and transport mode
public static void CodeBasedCheck(ProjectWorkspace project, Dictionary bindingsTransportMap)
{
IEnumerable addEndpointInvocations = project.GetInvocationExpressionsByMethodName(Constants.AddServiceEndpointType);
foreach (var addEndpointInvocation in addEndpointInvocations)
{
var argumentCount = addEndpointInvocation.Arguments.Count();
if (argumentCount == 1)
{
var endpointIdentifier = addEndpointInvocation.Arguments.First();
IEnumerable serviceEndpointObjectExpressions = project.GetObjectCreationExpressionBySemanticClassType(Constants.ServiceEndpointClass);
var endpointArgumentObjects = serviceEndpointObjectExpressions.
SelectMany(s => s.GetObjectCreationExpressionBySemanticNamespace(Constants.SystemServiceModelClass));
var bindingArgumentObjects = endpointArgumentObjects.Where(e => e.SemanticClassType != Constants.EndpointAddressType);
foreach(var binding in bindingArgumentObjects)
{
var bindingName = binding.SemanticClassType;
var bindingNameFormatted = bindingName.ToString().ToLower();
bindingsTransportMap[bindingName] = new BindingConfiguration
{
Mode = GetModeFromObjectArguments(binding.Arguments),
EndpointAddress = GetEndpointFromInvocationArguments(addEndpointInvocation.Arguments)
};
}
}
var bindingArgument = addEndpointInvocation.Arguments.Where(a => a.SemanticType.ToLower().Contains("binding"));
var objectDeclarations = addEndpointInvocation.GetObjectCreationExpressionBySemanticNamespace(Constants.SystemServiceModelClass);
ObjectCreationExpression objectCreationExpression;
if(objectDeclarations.IsNullOrEmpty())
{
if(!bindingArgument.IsNullOrEmpty())
{
var bindingName = bindingArgument.First().SemanticType;
var objectCreationExpressionList = project.GetObjectCreationExpressionBySemanticClassType(bindingName);
if(!objectCreationExpressionList.IsNullOrEmpty())
{
objectCreationExpression = objectCreationExpressionList.First();
}
else
{
bindingsTransportMap[bindingName] = new BindingConfiguration();
return;
}
}
else
{
break;
}
}
else
{
objectCreationExpression = objectDeclarations.First();
}
if (objectCreationExpression != null)
{
var bindingName = objectCreationExpression.SemanticClassType.ToLower();
bindingsTransportMap[bindingName] = new BindingConfiguration
{
Mode = GetModeFromObjectArguments(objectCreationExpression.Arguments),
EndpointAddress = GetEndpointFromInvocationArguments(addEndpointInvocation.Arguments)
};
}
}
var webConfig = WebConfigManager.LoadWebConfigAsXDocument(project.ProjectRootPath);
var appConfig = WebConfigManager.LoadAppConfigAsXDocument(project.ProjectRootPath);
if (webConfig != null && webConfig.ContainsElement(Constants.WCFBindingElementPath))
{
BindingTagCheck(webConfig, bindingsTransportMap);
}
else if (appConfig != null && appConfig.ContainsElement(Constants.WCFBindingElementPath))
{
BindingTagCheck(appConfig, bindingsTransportMap);
}
if (webConfig != null && webConfig.ContainsElement(Constants.WCFProtocolMappingElement))
{
ProtocolTagCheck(webConfig, bindingsTransportMap);
}
else if (appConfig != null && appConfig.ContainsElement(Constants.WCFProtocolMappingElement))
{
ProtocolTagCheck(appConfig, bindingsTransportMap);
}
}
///
/// Get Security Mode from Binding Object Creation Arguments.
/// For instance : new BasicHttpBinding(BasicHttpSecurityMode.Message)
///
/// List of Arguments which are part of Binding object creati
/// The Security Mode being used
public static string GetModeFromObjectArguments(List arguments)
{
var mode = Constants.NoneMode;
foreach (var argument in arguments)
{
if (argument.SemanticType == Constants.BasicHttpSecurityMode ||
argument.SemanticType == Constants.BasicHttpsSecurityMode ||
argument.SemanticType == Constants.SecurityMode)
{
mode = argument.Identifier.Substring(argument.Identifier.LastIndexOf(Constants.ModeSeparator) + 1);
break;
}
}
return mode;
}
///
/// Get the endpoint string from Endpoint Invoction Arguments
/// For instance : sh.AddServiceEndpoint(typeof(ISample), new BasicHttpBinding(BasicHttpSecurityMode.Transport), "/basicHttps")
/// Get /basicHttps string
///
/// Endpoint Invocation Arguments
/// Endpoint String
public static string GetEndpointFromInvocationArguments(List arguments)
{
string endpointAddress = null;
foreach (var argument in arguments)
{
if (argument.SemanticType != null && argument.SemanticType.ToLower() == Constants.StringType)
{
endpointAddress = argument.Identifier;
break;
}
}
return endpointAddress;
}
///
/// For Code Based, check for Service Interface and class which implements the same
///
/// ProjectWorkspace object for Code Based Service
/// A tuple of Service Interface and Implementing Class if any, otherwise null
public static Tuple GetServiceInterfaceAndClass(ProjectWorkspace project)
{
Tuple serviceInterfaceAndClass;
var interfaces = project.GetAllInterfaceDeclarations()?.ToList();
if (interfaces.IsNullOrEmpty()) { return null; }
var interfacesWithServiceContract = interfaces
.Where(i => i.HasAttribute(Constants.ServiceContractAttribute))
?.ToList();
if (interfacesWithServiceContract.IsNullOrEmpty()) { return null; }
var interfaceWithServiceContractMethods = interfacesWithServiceContract
.SelectMany(i => i.GetMethodDeclarations())?.ToList();
if (interfaceWithServiceContractMethods.IsNullOrEmpty()) { return null; }
var serviceInterfaceMethodWithObjectContract = interfaceWithServiceContractMethods
.Where(m => m.HasAttribute(Constants.OperationContractAttribute))
?.ToList();
if (!serviceInterfaceMethodWithObjectContract.IsNullOrEmpty())
{
var classes = project.GetAllClassDeclarations()?.ToList();
if (classes.IsNullOrEmpty()) { return null; }
foreach (var interfaceWithServiceContract in interfacesWithServiceContract)
{
foreach (var classDeclaration in classes)
{
if (classDeclaration.InheritsInterface(interfaceWithServiceContract.Identifier))
{
//Filter out generated Code
var IsGeneratedCode = classDeclaration.HasAnnotation(Constants.DebuggerStepThroughAttribute)
|| classDeclaration.HasAnnotation(Constants.GeneratedCodeAttribute);
if(!IsGeneratedCode)
{
serviceInterfaceAndClass = new Tuple(interfaceWithServiceContract.Identifier,
classDeclaration.Identifier);
return serviceInterfaceAndClass;
}
}
}
}
return null;
}
else
{
return null;
}
}
}
}