How We Implemented Live Help in BuildMaster

One of the new features we introduced in BuildMaster 2.3 was Live Help: if you’d like a quick answer to a question, just click to chat with a support engineer.

Though I’m sure we’re not the only software company to embed “instant support” in their product, I couldn’t find anyone else who was doing it. Of course, I didn’t look very far, since BuildMaster is a web-based application and there are plenty of places to go for web-based chat.

Choosing a Chat Provider

Being that we’d need to embed chat in our (download & install) application, our requirements were a little different than most. Namely, we needed:

  • Look & Feel Customization – having the standard “click here to chat” image on the sidebar doesn’t exactly fit in to our UI
  • Instant Message Integration – our support staff consists of developers, and the last thing we want is a clunky messaging app
  • Permission to Distribute – it’s pretty important that we’d be explicitly permitted to use chat in such a manner
  • Flexible Installation – we didn’t want to embed the actual chat code (in case we need to switch vendors, for example), but instead embed code that lived on inedo.com, which would then reference the chat vendor’s code
  • API – we don’t need much, just
    • ability to detect when operators are available
    • send metadata to operator (license key)
    • hide/show chat box

We evaluated a number of different vendors: SnapEngage, LivePerson, BoldChat, Olark, WhosOn, and a few others. Ultimately, we settled on Olark. While their software wasn’t as feature-rich as others, their support was topnotch (likely because we were chatting with the developers/founders) and it seemed to be the easiest to integrate with and customize.

Implementing Live Help with Olark

Because Live Help needs to “call home” to determine if an operator is available, we felt that users should be able to turn-off the feature. And that meant a simple configuration page:

When the checkbox is enabled, a single line of JavaScript is added to the page’s HEAD section:

$(function () {
  $('body').append('<sc' + 'ript type="text/javascript" ' +
     + 'src="http://links.inedo.com/integrated-support/?' +
     + 'lickey=00000000-0000-0000-0000-000000000000"></s' +
     + 'cript>');
});

It’s a JQuery call that essentially instructs the browser to append a <SCRIPT> tag to the body after the page has finished loaded. The script that loaded resides on our server, and looks something like this (with explanatory comments added):

// copy/paste of Olark's code; this will asynchronously
// contact their server to load the latest chat client
window.olark||(function (k) { var g = window, j = document, a =
g.location.protocol == "https:" ? "https:" : "http:", i = k.name,
... snip...
name: "olark", methods: ["configure", "extend", "declare", "identify"] }); 

//this is our Olark account number / api-key
olark.identify('0000-000-00-0000');

//set the IM "buddy" to be the user signed-in to BuildMaster
olark('api.chat.updateVisitorNickname', {
  snippet: 'BM~'+$('#BuildMaster_DisplayName').text(),
  hidesDefault: true});

//when an operator is available, display the appropriate link
olark('api.chat.onOperatorsAvailable', function () {

    $('#HeaderHelpBox').empty().append(
        $('<a href="#">Live Help is Available</a>')
            .click(function () {
                olark('api.box.show');
                olark('api.box.expand');
            })
        );
});

//when an operator is away, display the appropriate link 
olark('api.chat.onOperatorsAway', function () {
    $('#HeaderHelpBox').empty().append(
        $('<a href="#">Send Message To Support</a>')
            .click(function () {
                olark('api.box.show');
                olark('api.box.expand');
            })
        );
});

The Result

Success! Although we’ve always been around to answer support questions, having Live Support embedded right in the system has given users the confidence that we actually are there.

Though there were some concerns that it might overburden support, it’s actually made our lives easier. While simple questions have always been simple to ask, complex questions require a lot of back-and-forth to understand the underlying situation. And as it turns out, chat is ideal for that.

Default Passwords

Any software with any kind of security or auditing will have some kind of identity system. BuildMaster is capable of using an LDAP directory for authentication, but also has a simple built-in user name and password system – primarily for testing and initial configuration of the software.

Because BuildMaster integrates with so many third-party toolsets (most of which have their own user/password based system), it was a real pain point for us when some of these systems would set up a default user account to use but only mention what it is somewhere deep in the documentation. This is somewhat similar to the process you have gone through if you’ve ever tried logging into a consumer router’s web interface without remembering what combination of administrator/admin/vendor/model/{blank}/… is required for the user and password.

Login Prompt

Of course, during development the exact same could have been said about BuildMaster. Fortunately, one of our developers came up with a very simple solution in the form of an item in our issue tracker: “Why don’t we just tell them what the default user account is?”

BuildMaster Login

This is effectively the same as having the entire user/identity system disabled until the default settings are changed, but it won’t give new users the impression that BuildMaster has no concept of users at all. Also, it was a low-risk, 5-minute change that will hopefully make life a little easier for someone trying out BuildMaster for the first time.

Securing an API

There are two primary ways that you can get BuildMaster to do more than what is inside the box.

  • Write an Extension – allow BuildMaster to do new things by developing your own actions, providers, notifiers, etc.
  • Use the BuildMaster API – create scripts or applications that pull data from, or tell BuildMaster to do certain things

While the extensions have been available since Day One (it’s actually how we implemented the included providers and actions), the BuildMaster API is relatively new. As you might imagine, there are a lot of considerations that go into developing a useful, long-term API, and one that I found particularly interesting was security.

Why Security is Important

We didn’t really have to worry about security with extensions: you either have permission to upload an extension into BuildMaster, or you don’t. There’s really no sense in getting more granular than that.

But the API is different. As a web application, BuildMaster is accessible by anyone on the intranet, and it’s secured by traditional, browser-based methods: integrated authentication, single-sign on, or a log-in form. None of these work very well with an API.

API Security Options

Obviously, leaving the API unsecured is not an option: a malicious developer could simply read the documentation, figure out what the API URL is (it’s /api/), and do whatever he wants. Security is a must, and there are a few ways to go about it.

  1. Protocol-based Security – use something like WS-Security to handle things “behind the scenes”. This would allow consumers of the API to write very straightforward code:

        apiClient.Username = "userid";
        apiClient.Password="secret";
        applicationData = apiClient.Applications_GetApplication(appId);

    Of course, this would mean that they’d either have to use some built-in SOAP client, or simply write one that added the following to SOAP header:
     
        <wsse:UsernameToken>
         <wsse:Username>userid</wsse:Username>
          <wsse:Password>password</wsse:Password>
       </wsse:UsernameToken><'code>
  2. Log-in/Token Security – add “meta” methods (login, logout) and secure all other methods unless those are called first. This would look something like this.

      loginToken = apiClient.login("userid","secret");
      applicationData = apiClient.Applications_GetApplication(loginToken, appId);
      apiClient.logout();
  3. Key-based Security – secure all methods by requiring that a key (known only to the server) is passed in first.

      applicationData = apiClient.Applications_GetApplication("348dcvn2-sxn21l35n!24", appId);

Why We Chose Key-based Security

Requiring that a username and password are used to access the API presents a few interesting challenges, the largest being identifying who the actual user is.

Let’s say you build a desktop gadget that looks like a big red button and lets the user kick off a build. You’d either need to have the user enter his name/password, or hard-code a username/password (like “big-red-button-user”) to perform the action (and completely ignore any logging/auditing). Grabbing his username from the operating system environment doesn’t quite work, since you can’t get the password programmatically.

It’s equally challenging when you want to build a web-application. If you don’t prompt for a username and password, you’re stuck using a different user (and losing all of the auditing).

Add to that the additional complexity – WS-Security is no picnic (especially for consumers), and creating a token-based authentication system is yet-another subsystem to support – and options 1 and 2 become much less appealing.

That leaves us Option 3, Key-based Security. It’s incredibly simple to implement (if the key entered matches the server key, then you’re golden), and there’s only one vector of attack (the key) instead of every users’ name/password.

 

While the key is sacred, there is a very simple work-around if you don’t want users (on a desktop application) having it: create your own name/password web-service that wraps the BuildMaster API service. It may sound a bit convoluted, but since the method calls are available through WSDL, it’s more an exercise in code generation than tedium.

The method we chose to implement wasn’t any more “correct” than the other methods. But, for the users who consume our API, the simplicity of an API Key vastly outweighed the benefits of protocol- or login-based security.

Rendering a Newline at the Beginning of a Multi-Line TextBox Control using ASP.NET 2.0 Web Forms

Recently I have just completed a custom action for BuildMaster; more specifically, an action that allows a collection of files to be “concatenated” into one output file during a build. Using such an action can be useful during a software build if you are, for example:

  • Combining CSS or JavaScript files into one file for release
  • Getting the latest Stored Procedure code files from source control and combining them into one script

In the first attempt in creating the action, I used a constant newline as a separator between file contents… quickly realizing that this may not be useful for all file types. Sometimes you may want to add a GO batch separator for SQL Server scripts, other times you may just want to separate your file contents with an obnoxious:

/********************SEPARATOR!!!!!!***********************/

Whatever the case, there needs to be a section in our UI to accomplish this! In BuildMaster, it looks like this (specifically the Content Separation Text section):

image

The Problem

Creating this action was all well and good and worked perfectly, until I had to edit the one that I had saved previously. I noticed that any text saved to a multi-line textbox would NOT load the first newline when it was being edited.

I’m going to repeat that last statement in more formal, highly-searchable, highly-indexable terms: an ASP.NET 2.0 multi-line textbox does not render the first newline string, if its text property starts with a newline!

The only way I noticed this was by testing 1 newline as a separator, then going back to edit the action to add an extra space… but lo-and-behold, the pre-loaded textbox was not populated with the previously saved 1 newline, but instead appeared empty! A quick check of the database indicated that the value should have been \r\n, and stepping through with the debugger validated this further.

Since BuildMaster runs on .NET 2.0, I had to fire up some quick test projects in both ASP.NET 2.0 and 4.0, and the issue could not be reproduced in ASP.NET 4.0. Comparing the Render() methods between the two framework versions using Reflector, I noticed an enlightening difference:

ASP.NET 2.0 System.Web.UI.WebControls.TextBox Render Method

protected internal override void Render(HtmlTextWriter writer)
{
    this.RenderBeginTag(writer);
    if (this.TextMode == TextBoxMode.MultiLine)
    {
        HttpUtility.HtmlEncode(this.Text, writer);
    }
    this.RenderEndTag(writer);
}

ASP.NET 4.0 System.Web.UI.WebControls.TextBox Render Method

protected internal override void Render(HtmlTextWriter writer)
{
    this.RenderBeginTag(writer);
    if (this.TextMode == TextBoxMode.MultiLine)
    {
        HttpUtility.HtmlEncode(Environment.NewLine + this.Text, writer);
    }
    this.RenderEndTag(writer);
} 

It looks as though Microsoft resolved the issue by adding a newline directly to the text before it’s encoded!

The Solution

If Microsoft did it, why can’t I! Unfortunately, I cannot edit the TextBox class directly, and I would prefer not to use some sentinel value to represent a newline. Luckily for me, BuildMaster uses a custom “ValidatingTextBox” control for all textboxes (basically a composite control of a textbox with some validators that can be enabled via properties), so it was easy for me to override the Render() method:

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    string newline = null;
    if (this.TextMode == TextBoxMode.MultiLine && this.Text.StartsWith("\n"))
    {
        newline = "\n";
        this.Text = newline + this.Text;
    }
    else if (this.TextMode == TextBoxMode.MultiLine && this.Text.StartsWith("\r\n")) 
    {
        newline = "\r\n";
        this.Text = newline + this.Text;
    }

    /* snip other rendering code, call to base.Render()... */

    if (newline != null)
    {
        this.Text = this.Text.Substring(newline.Length);
    }
}

Now when I load up an existing action that starts with a newline, it actually appears in the textbox!

MessageBoxToEvent

Integrating various software with BuildMaster required working with a number of different API’s, some better supported than others, but all with their own quirks and gotchas. Sybase SqlAnywhere has a .NET API that was easy enough to use and adding integration into BuildMaster was straightforward. Until there was an error.

After some basic analysis, we determined that the .NET API wraps a native Windows DLL as a resource.  As part of initialization, it writes this resource out to a file and P/Invokes the Windows LoadLibrary function. Using this approach, their .NET API is essentially a single DLL (a number of other API’s work this way too). All of this works perfectly as long as the native DLL shares the same “bitness” as the host process. To make a long story short, if the x86 distribution of the .NET API is used in a 64-bit process, eventually a 32-bit native DLL will try to be loaded, which causes an error. If the x64 distribution is used instead, then the error will occur on 32-bit systems for the same reason. This is perfectly reasonable; however, it brought to light SqlAnywhere’s unfortunate approach to error reporting.

Not an actual screen shot

Both the native DLL and the .NET connector reported a number of errors using only Windows message boxes. While this may make sense during testing or in a standalone application, it would have been more helpful to use something like an exception or an error code to notify the caller of the error instead of only notifying the user. Making matters worse, this library would be invoked from the BuildMaster service – therefore, there would be no user to acknowledge the error, and the call would simply hang. If this was the only problem, then we could live with a timeout error, we discovered that attempting to use the SqlAnywhere library after one of these errors completely hosed the entire process: random crashes would happen, text was garbled, and eventually the whole process would just terminate. Clearly, we needed a way to detect SqlAnywhere’s unique way of reporting errors.

Fortunately, there is a way to hook into window creation on a per-thread basis using the Windows Computer Based Training hooks. By registering a method with the CBT hook for a thread, I was able to get a notification every time a window was created:

this.hookProc = new NativeMethods.HookProc(CBTHook);this.wndHookProc = new NativeMethods.WindowHookProc(WindowHook);

var result = NativeMethods.SetWindowsHookEx(NativeMethods.Hook.CBT, hookProc, IntPtr.Zero, threadId);
if (result == IntPtr.Zero)
    throw new Win32Exception();

If I wanted to just dismiss the window right away, I could have simply returned a zero here to cancel the window creation. However, I wanted to be able to extract additional information, so I used SetWindowLongPtr to hook the window procedure for the newly-created window. In the WM_SHOW WINDOW message, I enumerated all of the child controls and and title, extracting window text used to raise an event, and allow event handlers to allow or suppress the window:

case NativeMethods.WM_SHOWWINDOW:
  var handles = GetChildWindows(hwnd);
  var text = string.Empty;
  if(handles.Count > 0)
    text = handles[0].Text;
    var args = new ShowWindowEventArgs(GetWindowText(hwnd), text);
    OnShowWindow(args);
    if(!args.Allow)
      NativeMethods.PostMessage(hwnd, NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    break;

I have now succeeded in transforming a message box into an event. Of course, this only works if the offending message box is displayed from code within the same process. The CBT hooks can’t help you suppress unwanted UI from a command-line application that pops up message boxes.

HTTP 418 (I’m a teapot): Finally, a “Legitimate” Use

Ever since IETFA published RFC 2324 (Hyper Text Coffee Pot Control Protocol, HTCPC/1.0), web developers all over have wondered how can I find a legitimate use of HTTP response code 418 (I’m a teapot). Well… at least I’ve been wondering that. And as it would happen, the moment I stopped looking for a way, HTTP 418 found me.

I was faced with a fairly unique problem: get the end-users web browser to prompt for credentials when our application’s authentication is configured to use forms and cookies (FormsAuthentication). In a language much “closer” to the webserver (like Perl), this would be easy: simply send a 401 code. Frameworks like ASP.NET, on the other hand, work very hard to make sure we developers never have to worry about things like HTTP Status codes.

So why would anyone want to have this type of double-authentication? Well, in my case, the Build Schedules module allows users to set-up a URL-triggered build and, for obvious security reasons, configure triggers to require authentication with a name/password. Because FormsAuthentication is already configured, this means that accessing the handler would redirect to the login page… which is not a very useful feature.

Not Authorized to Not Authorize

ASP.NET will gladly allow you to set any status code. This means that, in theory, implementing double-authentication should be easy.

if (!authorized)
{
    context.Response.StatusCode = 401;
    context.Response.Write("Not authorized");
    context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"BuildMaster URL Trigger\"");
    context.ApplicationInstance.CompleteRequest();
    return;
}

Of course, that didn’t quite turn out like expected: I was immediately redirected to the login page. As it turned out, the culprit was the FormsAuthenticationModule. Firing up reflector, I found that it was coded to listen to the Application.EndRequest event, look for a 401 status code, and when found, redirect to the login page.

if (context.Response.StatusCode == 401)
{
    string rawUrl = context.Request.RawUrl;
    if ((rawUrl.IndexOf("?ReturnUrl=", StringComparison.Ordinal) == -1)
        && (rawUrl.IndexOf("&ReturnUrl=", StringComparison.Ordinal) == -1))
    {
        ...snip...
        if (!string.IsNullOrEmpty(FormsAuthentication.LoginUrl))
            strUrl = AuthenticationConfig.GetCompleteLoginUrl(context, FormsAuthentication.LoginUrl);
        ...snip...
        str3 = strUrl + "?ReturnUrl=" + HttpUtility.UrlEncode(rawUrl, context.Request.ContentEncoding);
        ...snip...
        context.Response.Redirect(str3, false);  // Changes the status code to 302 Found...
     }
}

This code presented a bit of a problem. Short of disabling FormsAuthentication (which would obviously create a whole host of other problems), there was no way to avoid it.

I’m a Teapot

If the FormsAuthenticationModule can “listen” for 401 responses and change them to 302, why couldn’t I do the same? My code would simply run after the FormsAuthentication code and change it from some status back to 401. Of course, I’d have to pick another response code to “listen” to… and it’d need a status code that no other code could possibly be using.

It was a relatively easy change. First things first, I modified the status-code setting code:

if (!authorized)
{
    // No, "I'm a teapot" is not what we really want, but the BuildMaster security module will
    // replace it with a 401 (which we actually want)
    context.Response.StatusCode = 418;
    context.Response.Write("Not authorized");
    context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"BuildMaster URL Trigger\"");
    context.ApplicationInstance.CompleteRequest();
    return;
}

After that, I just needed to add something to listen to EndRequest. Since we already had a HttpModule for security, I added the code in there.

context.EndRequest += (s,e) =>
{
    HttpApplication app = (HttpApplication)s;
    if (app.Context.Response.StatusCode == 418)
        app.Context.Response.StatusCode = 401;
}

So while HTTP 418 is never actually sent to the browser, it is being used.

Follow

Get every new post delivered to your Inbox.