Tuesday, August 9, 2016

Using HTML in a Web.config file

I wanted some pop up contextual help in an app I was writing. I got to the point where my requirements were:

  1. Use the bootstrap "popover" so I could include HTML and style the pop-up
  2. Include the help text in the config so it could be easily changed in the future 
Here's how I did it. 


Step 1: add a new section to web.config – this will prevent the text from needing to be escaped or treated with any special handlers

<section name="contextHelp" type="System.Configuration.IgnoreSectionHandler" allowLocation="false" />



Step 2: add the values for the section – I’m using CDATA to hide the markup and that should be sufficient for hyperlinks or images too

<contextHelp>
    <groupTitle><![CDATA[A group title with specific identifying information will be easy to search for later.<br /><br />Use specific hierarchy to help.
]]>

    </groupTitle>
</contextHelp>

Step 3: add code to read the config in the code-behind

protected string HelpTextTitle { get; set; }

protected new void Page_Init(object sender, EventArgs e)
{
  // Help Sections
  XmlDocument doc = new XmlDocument();
  doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
  HelpTextTitle = doc.SelectSingleNode("/configuration/contextHelp/groupTitle").FirstChild.Value;
}

Step 4: add values into markup

<a href="#" rel="group_title"><i class="fa fa-question-circle"></i></a>
<script>
$(document).ready(function () {
       $("[rel=group_title]").popover({
             title : 'Group Title', 
             html: 'true', 
             trigger: 'focus', 
             content: '<%=HelpTextTitle %>'
       });
});
</script>


Friday, July 15, 2016

Authenticating with Web Services written in a different language

Ah, the great promise of web services! We'll all speak the same language, XML/SOAP/JSON! Yeah, not so much. My latest interaction with this was a service that wanted a WS-Security authentication header but didn't want all the fields that Microsoft uses by default; without laying blame my understanding is some Java-based services don't accept a timestamp and this was my experience. Here's how I solved the problem:

WCF wasn’t really an option; because one of the functions had a callback it wanted to do duplex communication which among other things doesn’t support SSL. So here’s what I did:
  1.  Added WSE3 to the project (Microsoft.Web.Services3.dll)

  2. Create an old-school Web Reference in .NET (advanced option in “Add Service Reference”)

  3. Changed the resulting Referencs.cs class from System.Web.Services.Protocols.SoapHttpClientProtocol to Microsoft.Web.Services3.WebServicesClientProtocol

  4. Created a policy to allow me to filter the SOAP

  5. public class ClientUserNamePolicyAssertion : SecurityPolicyAssertion
        {
            public string UserName { get; set; }
            public string Password { get; set; }
           
            public ClientUserNamePolicyAssertion(string userName, string password)
            {
                this.UserName = userName;
                this.Password = password;
            }

            // and let the assertion know you have an output filter for outgoing SOAP packets
            public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
            {
                return new ClientSendSecurityFilter(this);
            }

            public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
            {
                return null;
            }
        } 

  6. Created the filter – here is where I remove the timestamp header, right before the packet goes out

  7. public class ClientSendSecurityFilter : SendSecurityFilter
        {
            private ClientUserNamePolicyAssertion clientUserNamePolicyAssertion = null;

            public ClientSendSecurityFilter(ClientUserNamePolicyAssertion userNamePolicyAssertion)
                : base(userNamePolicyAssertion.ServiceActor, true)
            {
                this.clientUserNamePolicyAssertion = userNamePolicyAssertion;
            }

            public override void SecureMessage(SoapEnvelope soapEnvelope, Security security)
            {
                UsernameToken usernameToken = new UsernameToken(clientUserNamePolicyAssertion.UserName, clientUserNamePolicyAssertion.Password, PasswordOption.SendPlainText);
                security.Tokens.Add(usernameToken);  
            }

            public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
            {
                // go ahead and run the base so the headers and security are added
                SoapFilterResult res = base.ProcessMessage(envelope);

                // remove the timestamp from the security header
                foreach (XmlNode n in envelope.Header.ChildNodes) {
                    if (n.Name == "wsse:Security")
                    {
                        foreach (XmlNode p in n)
                        {
                            if (p.Name == "wsu:Timestamp") n.RemoveChild(p);
                        }
                    }
                }

                //Debug.Write(envelope.OuterXml);

                return res;
            }
        }


  8. Then in the main I can create the service, apply my policy and make calls

TheirJavaService svc = new TheirJavaService();
ClientUserNamePolicyAssertion assert = new ClientUserNamePolicyAssertion("myusername", "mypassword");
Policy policy = new Policy();
policy.Assertions.Add(assert);
svc.SetPolicy(policy);
TheirServiceRequest req = new TheirServiceRequest();
TheirServiceResponse res = svc.GetResponseFromTheirService(req);