Tuesday, 29 May 2012

Mocking Basic HTTP Authentication in SoapUI


Working on a project this week, I came across an interesting little problem; namely, a web service we need to talk to in a production environment mandates basic HTTP authentication. Here's the kicker - we don't have ready access to a reference version of that web service within our development environment.

So what do we do?

Well, like many projects, we use the excellent SoapUI to exercise our published endpoints, and drive the functionality within our system. We make heavy use of SoapUI's Mock Service functionality to generate endpoints, and send our SOAP messages to these mocks during developer testing. The ability to send back different Responses based on the data we chuck out is invaluable to us, as it allows us to test the internal workings of our system under many different use cases, giving us a lot of confidence in what we've written.

Back to the problem at hand, I quickly discovered that whilst Basic HTTP Authentication is supported as an option sending SOAP messages out of SoapUI, Mock Services do not currently (as of version 4.0.1) support decoding and validation of user credentials passed over in the HTTP Request header. To get around this, I ended up writing a small piece of Groovy that sits in the OnRequest Script tab on the Mock Service you want to wrap.

Essentially, what we want to do is pull apart the Request headers, extract the header marked 'Authorization' (note Americanised spelling), parse it to remove excess padding and useless data, then decode it back into a String object to be interrogated.

 import com.eviware.soapui.support.types.StringToStringsMap;   
 // Get the global properties  
 def globalProperties = com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils.globalProperties;   
 String httpUsername = globalProperties['httpUsername'].value;  
 String httpPassword = globalProperties['httpPassword'].value;   
 // Get the Request Headers  
 StringToStringsMap headers = mockRequest.getRequestHeaders();   
 headers.each  
 {  
     if (it.key.equals("Authorization"))  
     {  
         // Pull the Auth string out of the request, and tidy it up  
         String content = it.value;  
         String [] contentArray = content.split();   
         if (contentArray.length == 2)  
         {  
           // Decode the authorisation String  
           String base64Enc = contentArray[1].minues("]");  
             byte [] decoded = base64Enc.decodeBase64();  
             String s = new String(decoded);   
             if (httpUsername != null && httpPassword != null)  
             {  
                 def credentials = s.split(":");  
                 assert credentials[0].equals(httpUsername);  
                 assert credentials[1].equals(httpPassword);  
                 log.info("Mock Service authenticated request for credentials: " + s);  
             }  
         }  
     }   
 }  

And that's it really. As long as you're setting up your HTTP Requests to use Basic Authentication, and you place properties (listed above as 'httpUsername' and 'httpPassword') into the Global Property store containing your expected credentials, then you should be good to go.

Hopefully this will help someone else out. If all you want to do is validate the fact your code is sending out the right user credentials, this should do the trick.