Friday 7 June 2013

CRM 2011 HTTPS Endpoint with SSIS and ADFS

After quite a bit of work, I finally managed to get a connection working with the CRM 2011 Https endpoint, using no config file, below is the code I used to achieve this.

private static IOrganizationService GetHttpsCRMService(string serverUrl, string adfsUrl, string username, string password)
        {
            Uri organizationUriIFD = new Uri(serverUrl + "/XRMServices/2011/Organization.svc");
                        
            EndpointAddress endpointAddress = new EndpointAddress(organizationUriIFD);

            CustomBinding customBinding = new CustomBinding();
            customBinding.Name = "CustomBinding";
            
            var tsBE = new TransportSecurityBindingElement();
            tsBE.AllowInsecureTransport = false;
            tsBE.IncludeTimestamp = true;


            EndpointAddress usernameMixed = new EndpointAddress(adfsUrl + "/adfs/services/trust/13/usernamemixed");
            WS2007HttpBinding wsbinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
            wsbinding.MaxReceivedMessageSize = 65536;
            wsbinding.MaxBufferPoolSize = 524288;
            wsbinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            wsbinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            wsbinding.Security.Message.EstablishSecurityContext = false;

            IssuedSecurityTokenParameters stP = new IssuedSecurityTokenParameters(null, usernameMixed, wsbinding);
            stP.IssuerMetadataAddress = new EndpointAddress(new Uri(adfsUrl + "/adfs/services/trust/mex"));

            var additionalRquestParameters =
                @"<trust:SecondaryParameters xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>" +
                @"    <trust:KeyType xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>" +
                @"    <trust:KeySize xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>256</trust:KeySize>" +
                @"    <trust:Claims Dialect='http://schemas.xmlsoap.org/ws/2005/05/identity' xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>" +
                @"        <wsid:ClaimType Uri='http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn' xmlns:wsid='http://schemas.xmlsoap.org/ws/2005/05/identity' />" +
                @"    </trust:Claims>" +
                @"    <trust:KeyWrapAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p</trust:KeyWrapAlgorithm>" +
                @"    <trust:EncryptWith xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptWith>" +
                @"    <trust:SignWith xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2000/09/xmldsig#hmac-sha1</trust:SignWith>" +
                @"    <trust:CanonicalizationAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>" +
                @"    <trust:EncryptionAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>" +
                @"</trust:SecondaryParameters>";

            System.Xml.XmlDocument additionalParamDoc = new System.Xml.XmlDocument();
            additionalParamDoc.LoadXml(additionalRquestParameters);

            stP.AdditionalRequestParameters.Add(additionalParamDoc.DocumentElement);
            stP.RequireDerivedKeys = false;
            stP.KeySize = 256;
            stP.KeyType = System.IdentityModel.Tokens.SecurityKeyType.SymmetricKey;
            
            tsBE.LocalClientSettings.DetectReplays = false;
            tsBE.LocalServiceSettings.DetectReplays = false;
            tsBE.EndpointSupportingTokenParameters.Endorsing.Add(stP);



            customBinding.Elements.Add(tsBE);

            var textEncoding = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
            var httpsTransport = new System.ServiceModel.Channels.HttpsTransportBindingElement();

            customBinding.Elements.Add(textEncoding);

            customBinding.Elements.Add(httpsTransport);
            customBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            customBinding.SendTimeout = new TimeSpan(0, 1, 0);
            customBinding.OpenTimeout = new TimeSpan(0, 1, 0);
            customBinding.CloseTimeout = new TimeSpan(0, 1, 0);

            var contract = new System.ServiceModel.Description.ContractDescription("IOraganizationService", "http://schemas.microsoft.com/xrm/2011/Contracts/Services");
            contract.ContractType = typeof(IOrganizationService);

            OrganizationServiceClient osClient = new OrganizationServiceClient(customBinding, endpointAddress);
            osClient.ClientCredentials.UserName.UserName = username;
            osClient.ClientCredentials.UserName.Password = password;
            
            return (IOrganizationService)osClient;
        }

Tuesday 5 March 2013

CRM 2011 Date Only Issue Plugin

As explained quite well in Kelvin's blog, CRM 2011 handles Date Only fields in a strange way.  So I have developed a plugin that will get round this.

The code for this is below and it should be deployed pre validation for the entities you wish to use it with, create or update.


/// <summary>
/// Fix for getting round the timezone issues with date only fields
/// </summary>
public class TimeZoneFix : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        //Get an instance of the helper class.
        PluginHelper helper = new PluginHelper(serviceProvider);
        helper.tracer.Trace("Loaded Helper");
        if (helper.targetEntity.Attributes.Any(a => a.Value != null && a.Value.GetType() == typeof(DateTime)))
        {
            IOrganizationService service = helper.service;
            helper.tracer.Trace("Building Request");
            RetrieveEntityRequest request = new RetrieveEntityRequest();
            request.LogicalName = helper.context.PrimaryEntityName;
            request.EntityFilters = EntityFilters.Attributes;
            RetrieveEntityResponse response =  (RetrieveEntityResponse)service.Execute(request);
            if (response != null && response.EntityMetadata != null)
            {
                List<KeyValuePair<string, object>> attribs = helper.targetEntity.Attributes.Where(a => a.Value != null && a.Value.GetType() == typeof(DateTime)).ToList();
                foreach (var att in attribs)
                {
                    DateTime date = (DateTime)helper.targetEntity[att.Key];
                    DateTime localDate = date.ToLocalTime();
                    TimeSpan dateDiff = localDate - date;
                    //Get the attribute metadata
                    AttributeMetadata meta = response.EntityMetadata.Attributes.FirstOrDefault(am => am.LogicalName.ToLower().Equals(att.Key));
                    //Check in the attribute metadata if the attribute is a date only.
                    if (meta != null && meta.AttributeType != null &&
                        meta.AttributeType == AttributeTypeCode.DateTime &&
                        ((DateTimeAttributeMetadata)meta).Format == DateTimeFormat.DateOnly)
                    {
                        helper.targetEntity[att.Key] = localDate.AddHours(dateDiff.Hours + 10);
                    }
                }
            }
        }
    }
}