diff --git a/ballerina/soap_constants.bal b/ballerina/soap_constants.bal deleted file mode 100644 index a5f20fe..0000000 --- a/ballerina/soap_constants.bal +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except -// in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -// SOAP-related constants -final string SOAP11_NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/"; -final string SOAP12_NAMESPACE = "http://www.w3.org/2003/05/soap-envelope"; -final string SOAP11_ENCODING_STYLE = "http://schemas.xmlsoap.org/soap/encoding/"; -final string SOAP12_ENCODING_STYLE = "http://www.w3.org/2003/05/soap-encoding"; -final string PWD_TEXT = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"; -final string PWD_DIGEST = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"; -final string BASE64ENCODED = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; - -// Error codes -final string SOAP_ERROR_CODE = "{ballerina/soap}SOAPError"; diff --git a/ballerina/soap_types.bal b/ballerina/soap_types.bal index de94fec..bbe4606 100644 --- a/ballerina/soap_types.bal +++ b/ballerina/soap_types.bal @@ -14,73 +14,7 @@ // specific language governing permissions and limitations // under the License. -import ballerina/http; - public type SoapVersion SOAP11 | SOAP12; public const SOAP11 = "SOAP11"; public const SOAP12 = "SOAP12"; - -public const PASSWORD_DIGEST = "PasswordDigest"; -public const PASSWORD_TEXT = "PasswordText"; -public type PasswordType PASSWORD_DIGEST | PASSWORD_TEXT; - -# Represents the SOAP request. -# -# + headers - The array of SOAP headers that will be sent to the endpoint via the SOAP envelope -# + wsAddressing - SOAP WS-Addressing related options -# + usernameToken - SOAP WS-Username token related options -# + httpHeaders - Headers to be included in the HTTP request -public type Options record {| - xml[] headers?; - WsAddressing wsAddressing?; - UsernameToken usernameToken?; - map httpHeaders?; -|}; - -# Represents UsernameToken WS-Security. -# -# + username - The value of the username parameter used for the WS-Security Username Token -# + password - The value of the password parameter used for the WS-Security Username Token -# + passwordType - The value of the password type parameter used for the WS-Security Username Token -public type UsernameToken record {| - string username; - string password; - PasswordType passwordType = PASSWORD_TEXT; -|}; - -# Represents WsAddressing related properties. -# -# + requestFrom - The value of the source endpoint parameter used for WS-Addressing -# + requestTo - The value of the destination parameter used for WS-Addressing -# + wsaAction - The value of the action parameter used for WS-Addressing -# + relatesTo - The value of the relationship parameter used for WS-Addressing (i.e., in the form of a (URI, QName) pair) -# + relationshipType - The value of the relationship type parameter used for WS-Addressing -# + replyTo - The value of the reply endpoint parameter used for WS-Addressing. This element must be present if a reply -# is expected. If this element is present, messageId must be present -# + faultTo - The value of the fault endpoint parameter used for WS-Addressing. If this element is present, the messageId -# must be present -# + messageId - The value of the messageId parameter used for WS-Addressing -public type WsAddressing record {| - string requestFrom?; - string requestTo?; - string wsaAction?; - string relatesTo?; - string relationshipType?; - string replyTo?; - string faultTo?; - string messageId?; -|}; - -# Represents the SOAP response. -# -# + soapVersion - The version of SOAP -# + headers - The array of SOAP headers, which the SOAP envelope receives from the endpoint -# + payload - The XML of the SOAP payload, which the SOAP envelope receives from the endpoint -# + httpResponse - The HTTP response -public type SoapResponse record {| - SoapVersion soapVersion = SOAP11; - xml[] headers?; - xml payload?; - http:Response httpResponse; -|}; diff --git a/ballerina/soap_utils.bal b/ballerina/soap_utils.bal index e7aa2c9..bf56ab3 100644 --- a/ballerina/soap_utils.bal +++ b/ballerina/soap_utils.bal @@ -17,41 +17,14 @@ import ballerina/crypto; import ballerina/http; import ballerina/lang.'xml as xmllib; -import ballerina/log; import ballerina/mime; -import ballerina/uuid; -import ballerina/time; - -# Provides the namespace for the given SOAP version. -# -# + soapVersion - The SOAP version of the request -# + return - The namespace for the given SOAP version -function getNamespace(SoapVersion soapVersion) returns string { - if (soapVersion == SOAP11) { - return SOAP11_NAMESPACE; - } - return SOAP12_NAMESPACE; -} - -# Provides the encoding style for the given SOAP version. -# -# + soapVersion - The SOAP version of the request -# + return - The encoding style for the given SOAP version -function getEncodingStyle(SoapVersion soapVersion) returns string { - if (soapVersion == SOAP11) { - return SOAP11_ENCODING_STYLE; - } - return SOAP12_ENCODING_STYLE; -} # Provides an empty SOAP envelope for the given SOAP version. # # + soapVersion - The SOAP version of the request # + return - XML with the empty SOAP envelope function createSoapEnvelop(SoapVersion soapVersion) returns xmllib:Element { - string _ = getNamespace(soapVersion); - string _ = getEncodingStyle(soapVersion); - if (soapVersion == SOAP11) { + if soapVersion == SOAP11 { return xml ` @@ -64,157 +37,16 @@ function createSoapEnvelop(SoapVersion soapVersion) returns xmllib:Element { } } -# Provides the WS-Addressing header. -# -# + options - SOAP options to be sent -# + return - XML with the WS-addressing header -function getWSAddressingHeaders(Options options) returns xml { - xmlns "https://www.w3.org/2005/08/addressing" as wsa; - - // This `requestTo` parameter is already validated as an `xml` before calling this method. - string requestTo = options?.wsAddressing["requestTo"] ?: ""; - var wsaAction = options?.wsAddressing["wsaAction"]; - - xml headerElement = xml `${requestTo}`; - if (wsaAction is string) { - headerElement = headerElement + xml `${wsaAction}`; - } - - var relatesTo = options?.wsAddressing["relatesTo"]; - if (relatesTo is string) { - xmllib:Element relatesToElement = xml `${relatesTo}`; - var relationshipType = options?.wsAddressing["relationshipType"]; - if (relationshipType is string) { - map relatesAttr = relatesToElement.getAttributes(); - relatesAttr[wsa:RelatesTo] = relationshipType; - } else { - log:printDebug("relationshipType is not of type string"); - } - xml relatesToXml = relatesToElement; - headerElement = headerElement + relatesToXml; - } - - var requestFrom = options?.wsAddressing["requestFrom"]; - if (requestFrom is string) { - xml fromElement = xml `${requestFrom}`; - headerElement = headerElement + fromElement; - } - - var replyTo = options?.wsAddressing["replyTo"]; - if (replyTo is string) { - var messageId = options?.wsAddressing["messageId"]; - if (messageId is string) { - xml messageIDElement = xml `${messageId}`; - headerElement = headerElement + messageIDElement; - } else { - error err = error(SOAP_ERROR_CODE, - message = "If ReplyTo element is present, wsa:MessageID MUST be present"); - panic err; - } - xml replyToElement = xml `${replyTo}`; - headerElement = headerElement + replyToElement; - } - - var faultTo = options?.wsAddressing["faultTo"]; - if (faultTo is string) { - xml faultToElement = xml `${faultTo}`; - headerElement = headerElement + faultToElement; - } else { - log:printDebug("faultTo is not of type string"); - } - - return headerElement; -} - -# Provides the WS-secure username token headers. -# -# + options - SOAP options to be sent -# + return - XML with the WS-secure username token headers -function getWSSecureUsernameTokenHeaders(Options options) returns xml { - xmlns "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" as wsse; - xmlns "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" as wsu; - - string username = options?.usernameToken["username"] ?: ""; - string password = options?.usernameToken["password"] ?: ""; - - xmllib:Element securityRoot = xml ``; - xmllib:Element usernameTokenRoot = xml ` `; - xml usernameElement = xml `${username}`; - xml passwordElement; - - time:Utc time = time:utcNow(); - xml timestampElement = xml `${time.toString()}`; - - var passwordType = options?.usernameToken["passwordType"]; - if (passwordType is ()) { - passwordType = "PasswordText"; - } - string pwdType = passwordType; - if (equalsIgnoreCase("PasswordDigest", pwdType)) { - string nonce = uuid:createType4AsString(); - string encodedNonce = nonce.toBytes().toBase64(); - string createdTime = time.toString(); - password = createDigestPassword(nonce, password, createdTime); - xml passwordDigest = xml `${password}`; - xml nonceElement = xml `${encodedNonce}`; - xml createdTimeElement = xml `${createdTime}`; - passwordElement = passwordDigest + nonceElement + createdTimeElement; - } else { - passwordElement = xml `${password}`; - } - - xml headerElement = usernameElement + passwordElement; - usernameTokenRoot.setChildren(headerElement); - xml userTokenXml = usernameTokenRoot; - userTokenXml = userTokenXml + timestampElement; - securityRoot.setChildren(usernameTokenRoot); - return securityRoot; -} - # Provides the SOAP headers in the request as XML. # -# + options - SOAP options to be sent # + soapVersion - The SOAP version of the request # + return - XML with the empty SOAP header -function createSoapHeader(SoapVersion soapVersion, Options? options = ()) returns xml { - string _ = getNamespace(soapVersion); - xmllib:Element headersRoot; - if (soapVersion == SOAP11) { - headersRoot = xml ``; +function createSoapHeader(SoapVersion soapVersion) returns xml { + if soapVersion == SOAP11 { + return xml ``; } else { - headersRoot = xml ``; + return xml ``; } - xml? headerElement = (); - if (options is Options) { - xml[] headers = options["headers"] ?: []; - if (headers.length() != 0) { - int i = 1; - xml headersXML = headers[0]; - while (i < headers.length()) { - headersXML = headersXML + headers[i]; - i = i + 1; - } - headerElement = headersXML; - } - if (options["wsAddressing"]["requestTo"] is string) { - if (headerElement is ()) { - headerElement = getWSAddressingHeaders(options); - } else { - headerElement = headerElement + getWSAddressingHeaders(options); - } - } - if (options["usernameToken"]["username"] is string) { - if (headerElement is ()) { - headerElement = getWSSecureUsernameTokenHeaders(options); - } else { - headerElement = headerElement + getWSSecureUsernameTokenHeaders(options); - } - } - if (headerElement is xml && (headerElement/*).length() != 0) { - headersRoot.setChildren(headerElement); - } - } - return headersRoot; } # Provides the SOAP body in the request as XML. @@ -223,7 +55,6 @@ function createSoapHeader(SoapVersion soapVersion, Options? options = ()) return # + soapVersion - The SOAP version of the request # + return - XML with the SOAP body function createSoapBody(xml payload, SoapVersion soapVersion) returns xml { - string _ = getNamespace(soapVersion); xmllib:Element bodyRoot; if (soapVersion == SOAP11) { bodyRoot = xml ``; @@ -236,17 +67,15 @@ function createSoapBody(xml payload, SoapVersion soapVersion) returns xml { # Prepares a SOAP envelope with the XML to be sent. # -# + soapAction - SOAP action # + body - SOAP request body as an `XML` or `mime:Entity[]` to work with soap attachments -# + options - The SOAP options to be sent # + soapVersion - The SOAP version of the request # + return - The SOAP Request sent as `http:Request` with the SOAP envelope -function fillSOAPEnvelope(SoapVersion soapVersion, xml | mime:Entity[] body, string? soapAction = (), Options? options = ()) +function fillSoapEnvelope(SoapVersion soapVersion, xml | mime:Entity[] body) returns http:Request { - xml soapPayload = createSoapHeader(soapVersion, options = options); + xml soapPayload = createSoapHeader(soapVersion); http:Request req = new; var requestPayload = body; - if (requestPayload is xml) { + if requestPayload is xml { xml bodyPayload = createSoapBody(requestPayload, soapVersion); soapPayload = soapPayload + bodyPayload; @@ -256,107 +85,26 @@ returns http:Request { } else { req.setBodyParts(requestPayload); } - if (soapVersion == SOAP11) { + if soapVersion == SOAP11 { req.setHeader(mime:CONTENT_TYPE, mime:TEXT_XML); - if (soapAction is string) { - req.addHeader("SOAPAction", soapAction); - } - } else { - if (soapAction is string) { - map stringMap = {}; - stringMap["action"] = "\"" + soapAction + "\""; - var mediaType = mime:getMediaType(mime:APPLICATION_SOAP_XML); - if (mediaType is mime:MediaType) { - mediaType.parameters = stringMap; - req.setHeader(mime:CONTENT_TYPE, mediaType.toString()); - } - } else { - req.setHeader(mime:CONTENT_TYPE, mime:APPLICATION_SOAP_XML); - } - } - map? httpHeaders = options["httpHeaders"]; - if (httpHeaders is map) { - foreach var [headerName, headerValue] in httpHeaders.entries() { - req.setHeader(headerName, headerValue); - } - } - return req; -} - -# Creates the SOAP response from the HTTP Response. -# -# + response - The request to be sent -# + soapVersion - The SOAP version of the request -# + return - The SOAP response created from the `http:Response` or the `error` object when reading the payload -function createSOAPResponse(http:Response response, SoapVersion soapVersion) returns @tainted SoapResponse | error { - xml payload = check response.getXmlPayload(); - xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soap11; - xmlns "http://www.w3.org/2003/05/soap-envelope" as soap12; - xml soapHeaders; - if (soapVersion == SOAP11) { - soapHeaders = payload//*; - } else { - soapHeaders = payload//*; - } - xml[] soapResponseHeaders = []; - if ((soapHeaders/*).length() !=0) { - int i = 0; - xml[] headersXML = []; - while (i < soapHeaders.length()) { - headersXML[i] = soapHeaders[i]; - i += 1; - } - soapResponseHeaders = headersXML; - } - xml soapResponsePayload; - if (soapVersion == SOAP11) { - soapResponsePayload = payload/; } else { - soapResponsePayload = payload/; + req.setHeader(mime:CONTENT_TYPE, mime:APPLICATION_SOAP_XML); } - SoapResponse soapResponse = { - headers: soapResponseHeaders, - payload: soapResponsePayload, - soapVersion: soapVersion, - httpResponse: response - }; - return soapResponse; + return req; } function createSoapResponse(http:Response response, SoapVersion soapVersion) returns xml | error { xml payload = check response.getXmlPayload(); xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soap11; xmlns "http://www.w3.org/2003/05/soap-envelope" as soap12; - xml soapHeaders; - if (soapVersion == SOAP11) { - soapHeaders = payload//*; - } else { - soapHeaders = payload//*; - } - xml[] soapResponseHeaders = []; - if ((soapHeaders/*).length() !=0) { - int i = 0; - xml[] headersXML = []; - while (i < soapHeaders.length()) { - headersXML[i] = soapHeaders[i]; - i += 1; - } - soapResponseHeaders = headersXML; - } xml soapResponsePayload; - if (soapVersion == SOAP11) { + if soapVersion == SOAP11 { soapResponsePayload = payload/; } else { soapResponsePayload = payload/; } - SoapResponse soapResponse = { - headers: soapResponseHeaders, - payload: soapResponsePayload, - soapVersion: soapVersion, - httpResponse: response - }; return soapResponsePayload; } @@ -376,24 +124,12 @@ function createDigestPassword(string nonce, string password, string createdTime) string path = ""; function sendReceive(xml|mime:Entity[] body, http:Client httpClient) returns xml|error { - http:Request req = fillSOAPEnvelope(SOAP11, body); + http:Request req = fillSoapEnvelope(SOAP11, body); http:Response response = check httpClient->post(path, req); return createSoapResponse(response, SOAP11); } function sendOnly(xml|mime:Entity[] body, http:Client httpClient) returns error? { - http:Request req = fillSOAPEnvelope(SOAP11, body); + http:Request req = fillSoapEnvelope(SOAP11, body); http:Response _ = check httpClient->post(path, req); } - -# Returns the value equality of two strings despite of case. -# -# + stringOne - string one -# + stringTwo - string two -# + return - boolean equality -function equalsIgnoreCase(string stringOne, string stringTwo) returns boolean { - if (stringOne.toLowerAscii() == stringTwo.toLowerAscii()) { - return true; - } - return false; -}