Question André-Claude Gendron · Oct 28, 2022

SOAP - Custom authentication when user/password is in Body

I have to create a SOAP WebService that receives the username/password as part of a field in the Request. I have no control of the client's application.

<soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:tem="http://tempuri.org"><soapenv:Body><tem:ProcessRequest><!--Optional:--><tem:myRequest><tem:NomUtilisateur>ACGendron</tem:NomUtilisateur><tem:MotDePasse>MyPassword</tem:MotDePasse><!-- Other request fields --><tem:PrenomMere>?</tem:PrenomMere><tem:NumeroTelephone>?</tem:NumeroTelephone><tem:CodeInstallation>1</tem:CodeInstallation></tem:myRequest></tem:ProcessRequest></soapenv:Body></soapenv:Envelope>
...

I'm looking for advice on the best way to authenticate the user. I could verify that the user/password is correct using %session.Login(user, pass) in my WebMethod and throwing a SoapFault when this fails but I'm not sure it's the best way of doing things in my %SOAP.WebService. Perhaps there is a callback I can implement or any properties I should set (Username, UsernameToken, etc.) for the proper SOAP Service internal work

Any help is welcome,

Kind regards

Product version: IRIS 2020.1
$ZV: IRIS for UNIX (Red Hat Enterprise Linux for x86-64) 2020.1.1 (Build 408_0_21233U) Thu Oct 28 2021 15:14:45 EDT

Comments

André-Claude Gendron · Nov 16, 2022

I ended-up creating a %CSP.Rest class that acts as a proxy which reordered the fields in order to generate a standard SOAP request with the proper headers. Works great for a minimum of code.

0
Armin Gayl  Nov 18, 2022 to André-Claude Gendron

Hi Andre-Claude, could you please explain your solution. I have a similar problem and could need a explination. Because i have also some troubles with the WS-Security...

0
André-Claude Gendron  Nov 25, 2022 to Armin Gayl

Here are a few code samples that could help : 
 

Class YourPackage.REST.CSoapAuthenticator Extends%CSP.REST
{

Parameter CONTENTTYPE = "text/xml";
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/:soapService" Method="POST" Call="PostSOAP"/>
</Routes>
}

ClassMethod PostSOAP(
    strSoapService As%String = "",
    test As%Integer = 0) As%Status
{
   #Dim%requestAs%CSP.Request#Dim%responseAs%CSP.Response#Dim httpClient As%Net.HttpRequest = ..GetLoopbackSOAPClient()
   do httpClient.EntityBody.Write(..UpdateSoapBody(%request.Content.Read($$$MaxStringLength)))
   do httpClient.SetHeader("SOAPAction", %request.SoapAction)
   do httpClient.Post($System.CSP.GetDefaultApp($Namespace) _ "/" _ strSoapService, test)

   set%response.ContentType = "text/xml"write httpClient.HttpResponse.Data.Read($$$MaxStringLength)

   return$$$OK
}

// -- Private utils --ClassMethod GetLoopbackSOAPClient() As%Net.HttpRequest [ Private ]
{
   set httpClient = ##class(%Net.HttpRequest).%New()
   set httpClient.Server = ...
   set httpClient.Port = ...
   set httpClient.Timeout = 5set httpClient.Https = 1set httpClient.SSLConfiguration = ...
  
   return httpClient
}

ClassMethod UpdateSoapBody(strInput As%String) As%String [ Private ]
{
   #Dim strSoap As%String = ""#Dim username As%String = ... (Extract from Input)
   #Dim password As%String = ... (Extract from Input)
   
   set strSoap = strSoap _ ... (Copy soap enveloppe from input)
   set strSoap = strSoap _ " <soap:Header>" _ ..GenerateSecurityHeader(username, password) _ " </soap:Header>"set strSoap = strSoap _ " <soap:Body>"
   ... 
   set strSoap = strSoap _ " </soap:Body>"set strSoap = strSoap _ "</soap:Envelope>"return strSoap
}

ClassMethod GenerateSecurityHeader(
    strUsername As%String,
    strPassword As%String) As%String [ Private ]
{
   set header = ##class(%SOAP.Security.Header).%New()
   set usernameToken = ##class(%SOAP.Security.UsernameToken).Create(strUsername, strPassword)
   do header.AddSecurityElement(usernameToken)

   return ... (Use XML Writer to Output header to a String.)
}

}
0