├── .trivyignore ├── .gitattributes ├── ballerina ├── icon.png ├── modules │ ├── wssec │ │ ├── tests │ │ │ ├── resources │ │ │ │ ├── wss40.p12 │ │ │ │ ├── keystore.jks │ │ │ │ ├── keystore.p12 │ │ │ │ ├── keystoretest.jks │ │ │ │ ├── mykeystore.jks │ │ │ │ ├── private_key.pem │ │ │ │ ├── invalid_keystore.jks │ │ │ │ ├── x509_certificate.p12 │ │ │ │ ├── x509_certificate_2.p12 │ │ │ │ ├── x509_certificate_2.crt │ │ │ │ ├── x509_certificate.crt │ │ │ │ ├── xml │ │ │ │ │ └── soap_envelope.xml │ │ │ │ ├── certificate.pem │ │ │ │ └── private_key1.pem │ │ │ └── test_utils.bal │ │ ├── error.bal │ │ ├── init.bal │ │ ├── sec_header.bal │ │ ├── encryption.bal │ │ ├── document.bal │ │ ├── types.bal │ │ ├── ws_security.bal │ │ ├── signature.bal │ │ ├── records.bal │ │ └── ws_security_methods.bal │ ├── soap12 │ │ ├── tests │ │ │ ├── http_secured_service.bal │ │ │ └── http_soap_service.bal │ │ ├── error.bal │ │ ├── soap12.bal │ │ └── README.md │ └── soap11 │ │ ├── tests │ │ ├── http_secured_service.bal │ │ └── http_soap_service.bal │ │ ├── error.bal │ │ ├── soap11.bal │ │ └── README.md ├── constants.bal ├── error.bal ├── Ballerina.toml ├── configs.bal ├── types.bal ├── build.gradle ├── Dependencies.toml ├── README.md └── soap_utils.bal ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── codecov.yml ├── .github ├── pull_request_template.md ├── CODEOWNERS └── workflows │ ├── trivy-scan.yml │ ├── build-timestamped-master.yml │ ├── pull-request.yml │ ├── publish-release.yml │ ├── central-publish.yml │ ├── build-with-bal-test-graalvm.yml │ └── update_specs.yml ├── .gitignore ├── changelog.md ├── issue_template.md ├── spotbugs-exclude.xml ├── native ├── src │ └── main │ │ ├── java │ │ ├── module-info.java │ │ ├── org │ │ │ └── wssec │ │ │ │ ├── ModuleUtils.java │ │ │ │ ├── Utils.java │ │ │ │ ├── WsSecurityHeader.java │ │ │ │ ├── Signature.java │ │ │ │ ├── DocumentBuilder.java │ │ │ │ ├── Encryption.java │ │ │ │ ├── Constants.java │ │ │ │ └── WsSecurityUtils.java │ │ └── io │ │ │ └── ballerina │ │ │ └── lib │ │ │ └── soap │ │ │ └── Soap.java │ │ └── resources │ │ └── META-INF │ │ └── native-image │ │ └── io.ballerina.stdlib │ │ └── soap-native │ │ └── resource-config.json └── build.gradle ├── gradle.properties ├── settings.gradle ├── pull_request_template.md ├── gradlew.bat ├── gradlew ├── LICENSE └── README.md /.trivyignore: -------------------------------------------------------------------------------- 1 | # False positive 2 | CVE-2014-3623 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ensure all Java files use LF. 2 | *.java eol=lf 3 | -------------------------------------------------------------------------------- /ballerina/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/icon.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: "60...80" 5 | status: 6 | project: 7 | default: 8 | target: 80 -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/wss40.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/wss40.p12 -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/keystore.jks -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/keystore.p12 -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/keystoretest.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/keystoretest.jks -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/mykeystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/mykeystore.jks -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/private_key.pem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/private_key.pem -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/invalid_keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/invalid_keystore.jks -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/x509_certificate.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/x509_certificate.p12 -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/x509_certificate_2.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-soap/HEAD/ballerina/modules/wssec/tests/resources/x509_certificate_2.p12 -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | ## Examples 4 | 5 | ## Checklist 6 | - [ ] Linked to an issue 7 | - [ ] Updated the changelog 8 | - [ ] Added tests 9 | - [ ] Updated the spec 10 | - [ ] Checked native-image compatibility 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @NipunaRanasinghe @Nuvindu @shafreenAnfar 8 | -------------------------------------------------------------------------------- /.github/workflows/trivy-scan.yml: -------------------------------------------------------------------------------- 1 | name: Trivy 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "30 20 * * *" 7 | 8 | jobs: 9 | call_workflow: 10 | name: Run Trivy Scan Workflow 11 | if: ${{ github.repository_owner == 'ballerina-platform' }} 12 | uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@main 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /.github/workflows/build-timestamped-master.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - '*.md' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | call_workflow: 13 | name: Run Build Workflow 14 | if: ${{ github.repository_owner == 'ballerina-platform' }} 15 | uses: ballerina-platform/ballerina-library/.github/workflows/build-timestamp-master-template.yml@main 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: PR Build 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 5 | cancel-in-progress: true 6 | 7 | on: pull_request 8 | 9 | jobs: 10 | call_workflow: 11 | name: Run PR Build Workflow 12 | if: ${{ github.repository_owner == 'ballerina-platform' }} 13 | uses: ballerina-platform/ballerina-library/.github/workflows/pull-request-build-template.yml@main 14 | secrets: inherit 15 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | workflow_dispatch: 5 | repository_dispatch: 6 | types: [stdlib-release-pipeline] 7 | 8 | jobs: 9 | call_workflow: 10 | name: Run Release Workflow 11 | if: ${{ github.repository_owner == 'ballerina-platform' }} 12 | uses: ballerina-platform/ballerina-library/.github/workflows/release-package-template.yml@main 13 | secrets: inherit 14 | with: 15 | package-name: soap 16 | package-org: ballerina 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | !gradle/wrapper/gradle-wrapper.jar 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | build 27 | .gradle/ 28 | target 29 | # IDEA Files 30 | .idea/ 31 | *.iml 32 | *.ipr 33 | *.iws 34 | 35 | # MacOS 36 | *.DS_Store 37 | 38 | # Ballerina 39 | velocity.log* 40 | *Ballerina.lock 41 | 42 | .vscode 43 | config-dir 44 | -------------------------------------------------------------------------------- /.github/workflows/central-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to the Ballerina central 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | environment: 7 | type: choice 8 | description: Select Environment 9 | required: true 10 | options: 11 | - DEV CENTRAL 12 | - STAGE CENTRAL 13 | 14 | jobs: 15 | call_workflow: 16 | name: Run Central Publish Workflow 17 | if: ${{ github.repository_owner == 'ballerina-platform' }} 18 | uses: ballerina-platform/ballerina-library/.github/workflows/central-publish-template.yml@main 19 | secrets: inherit 20 | with: 21 | environment: ${{ github.event.inputs.environment }} 22 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | This file contains all the notable changes done to the Ballerina SOAP package through the releases. 3 | 4 | ## [Unreleased] 5 | 6 | ### Changed 7 | - [Make some of the Java classes proper utility classes](https://github.com/ballerina-platform/ballerina-standard-library/issues/5075) 8 | 9 | ## [0.8.0] - 2023-10-16 10 | 11 | ### Added 12 | - [Introduce soap standard library module](https://github.com/ballerina-platform/ballerina-standard-library/issues/4500) 13 | - [Introduce basic soap client](https://github.com/ballerina-platform/ballerina-standard-library/issues/4618) 14 | 15 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 16 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/error.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | # Represents any error related to the wssec module. 18 | public type Error distinct error; 19 | -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | **Description:** 2 | 3 | 4 | **Suggested Labels:** 5 | 6 | 7 | **Suggested Assignees:** 8 | 9 | 10 | **Affected Product Version:** 11 | 12 | **OS, DB, other environment details and versions:** 13 | 14 | **Steps to reproduce:** 15 | 16 | 17 | **Related Issues:** 18 | -------------------------------------------------------------------------------- /ballerina/constants.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import soap.wssec; 18 | 19 | const SOAP_ACTION = "SOAPAction"; 20 | const ACTION = "action"; 21 | const wssec:NoPolicy NO_POLICY = "NoPolicy"; 22 | -------------------------------------------------------------------------------- /spotbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/init.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | isolated function init() { 20 | setModule(); 21 | } 22 | 23 | isolated function setModule() = @java:Method { 24 | 'class: "org.wssec.ModuleUtils" 25 | } external; 26 | -------------------------------------------------------------------------------- /ballerina/modules/soap12/tests/http_secured_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | service / on new http:Listener(9091) { 20 | 21 | resource function post .(http:Request request) returns xml|error { 22 | return check request.getXmlPayload(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ballerina/modules/soap11/tests/http_secured_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | service / on new http:Listener(9091) { 20 | 21 | resource function post .(http:Request request) returns xml|error { 22 | return check request.getXmlPayload(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ballerina/error.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | # Defines the common error type for the module. 18 | public type Error distinct error; 19 | 20 | const SOAP_RESPONSE_ERROR = "Failed to create SOAP response"; 21 | const INVALID_PROTOCOL_ERROR = "Invalid protocol detected: Please use the `https` protocol instead of `http`."; 22 | -------------------------------------------------------------------------------- /native/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | module soap { 18 | requires io.ballerina.runtime; 19 | requires org.apache.wss4j.dom; 20 | requires org.apache.wss4j.common; 21 | requires org.apache.santuario.xmlsec; 22 | requires java.xml; 23 | requires java.xml.crypto; 24 | requires org.apache.commons.codec; 25 | } 26 | -------------------------------------------------------------------------------- /native/src/main/resources/META-INF/native-image/io.ballerina.stdlib/soap-native/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[{ 4 | "pattern":"\\QMETA-INF/axiom.xml\\E" 5 | }, { 6 | "pattern":"\\QMETA-INF/services/javax.xml.stream.XMLInputFactory\\E" 7 | }, { 8 | "pattern":"\\QMETA-INF/services/javax.xml.stream.XMLOutputFactory\\E" 9 | }, { 10 | "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" 11 | }, { 12 | "pattern":"\\Qlogging.properties\\E" 13 | }, { 14 | "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/nfc.nrm\\E" 15 | }, { 16 | "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/nfkc.nrm\\E" 17 | }, { 18 | "pattern":"java.xml:\\Qcom/sun/org/apache/xml/internal/serializer/Encodings.properties\\E" 19 | }]}, 20 | "bundles":[{ 21 | "name":"com.sun.org.apache.xml.internal.serializer.XMLEntities", 22 | "locales":[""] 23 | }, { 24 | "name":"messages.wss4j_errors", 25 | "locales":[""] 26 | }, { 27 | "name":"org/apache/xml/security/resource/xmlsecurity", 28 | "locales":["en"] 29 | }] 30 | } 31 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | group=io.ballerina.stdlib 3 | version=2.3.1-SNAPSHOT 4 | 5 | checkstylePluginVersion=10.12.0 6 | spotbugsPluginVersion=6.0.18 7 | shadowJarPluginVersion=8.1.1 8 | downloadPluginVersion=5.4.0 9 | releasePluginVersion=2.8.0 10 | ballerinaGradlePluginVersion=2.3.0 11 | 12 | ballerinaLangVersion=2201.12.0 13 | 14 | stdlibIoVersion=1.8.0 15 | stdlibTimeVersion=2.7.0 16 | stdlibUrlVersion=2.6.0 17 | 18 | stdlibConstraintVersion=1.7.0 19 | stdlibCryptoVersion=2.9.0 20 | stdlibLogVersion=2.12.0 21 | stdlibOsVersion=1.10.0 22 | stdlibRandomVersion=1.7.0 23 | stdlibTaskVersion=2.7.0 24 | 25 | stdlibCacheVersion=3.10.0 26 | stdlibFileVersion=1.12.0 27 | stdlibMimeVersion=2.12.0 28 | stdlibUuidVersion=1.10.0 29 | 30 | stdlibAuthVersion=2.14.0 31 | stdlibDataJsonDataVersion=1.1.0 32 | stdlibJwtVersion=2.15.0 33 | stdlibOAuth2Version=2.14.0 34 | 35 | stdlibHttpVersion=2.14.0 36 | 37 | wsSecurityDomVersion=3.0.1 38 | wsSecurityCommonVersion=3.0.1 39 | xmlSecVersion=3.0.3 40 | guavaVersion=32.1.1-jre 41 | 42 | # Ballerinax Observer 43 | observeVersion=1.5.0 44 | observeInternalVersion=1.5.0 45 | -------------------------------------------------------------------------------- /ballerina/modules/soap12/error.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | # Defines the common error type for the SOAP 1.2 module. 18 | public type Error distinct error; 19 | 20 | const SOAP_RESPONSE_ERROR = "Failed to create SOAP response"; 21 | const SOAP_CLIENT_ERROR = "Failed to initialize SOAP 1.2 client"; 22 | const SOAP_ERROR = "Failed to generate a response"; 23 | const INVALID_OUTBOUND_SECURITY_ERROR = "Outbound security configurations do not match with the SOAP response"; 24 | -------------------------------------------------------------------------------- /ballerina/modules/soap11/error.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | # Defines the common error type for the SOAP 1.1 module. 18 | public type Error distinct error; 19 | 20 | const SOAP_RESPONSE_ERROR = "Failed to create SOAP response"; 21 | const SOAP_CLIENT_ERROR = "Failed to initialize SOAP 1.1 client"; 22 | const SOAP_ERROR = "Error occurred while executing the API"; 23 | const INVALID_OUTBOUND_SECURITY_ERROR = "Outbound security configurations do not match with the SOAP response"; 24 | -------------------------------------------------------------------------------- /ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "ballerina" 3 | name = "soap" 4 | version = "2.3.0" 5 | authors = ["Ballerina"] 6 | export=["soap", "soap.soap11", "soap.soap12"] 7 | keywords = ["soap"] 8 | repository = "https://github.com/ballerina-platform/module-ballerina-soap" 9 | icon = "icon.png" 10 | license = ["Apache-2.0"] 11 | distribution = "2201.12.0" 12 | 13 | [build-options] 14 | observabilityIncluded = true 15 | 16 | [platform.java21] 17 | graalvmCompatible = true 18 | 19 | [[platform.java21.dependency]] 20 | groupId = "io.ballerina.stdlib" 21 | artifactId = "soap-native" 22 | version = "2.3.0" 23 | path = "../native/build/libs/soap-native-2.3.0.jar" 24 | 25 | [[platform.java21.dependency]] 26 | groupId = "org.apache.wss4j" 27 | artifactId = "wss4j-ws-security-dom" 28 | version = "3.0.1" 29 | path = "./lib/wss4j-ws-security-dom-3.0.1.jar" 30 | 31 | [[platform.java21.dependency]] 32 | groupId = "org.apache.wss4j" 33 | artifactId = "wss4j-ws-security-common" 34 | version = "3.0.1" 35 | path = "./lib/wss4j-ws-security-common-3.0.1.jar" 36 | 37 | [[platform.java21.dependency]] 38 | groupId = "org.apache.santuario" 39 | artifactId = "xmlsec" 40 | version = "3.0.3" 41 | path = "./lib/xmlsec-3.0.3.jar" 42 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/ModuleUtils.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.Environment; 20 | import io.ballerina.runtime.api.Module; 21 | 22 | public class ModuleUtils { 23 | 24 | private static Module module; 25 | 26 | private ModuleUtils() {} 27 | 28 | public static void setModule(Environment environment) { 29 | module = environment.getCurrentModule(); 30 | } 31 | 32 | public static Module getModule() { 33 | return module; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/x509_certificate_2.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDETCCAfkCFDKN6J0IJE2sBxy0yoWYwwpBdhxBMA0GCSqGSIb3DQEBCwUAMEUx 3 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 4 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwOTAzMjEyNjI1WhcNMjMxMDAzMjEy 5 | NjI1WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 6 | CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC 7 | AQ8AMIIBCgKCAQEAxG2CEtglz5hRwN3Yybb1gnsd5eJNBAm2nlzPFL1tazM6eAg1 8 | k5QggDKh5VV9XIAejEv3xVnSMACWgKEJvwDJjNivta7qTBOqPi/EM3oj0CR3IK4/ 9 | U51p2z3JPaVSRapmkXpJX1k5Bf6n2n7laztOJxj2wwt5LkgJAmjDXLuiVScnPyzo 10 | Nsg08R8BBFqmFWI+ulyaR0Kza+m70e2K9Cj6s2C3thPyEQ4252qZY0JdgftGQmL2 11 | QGsMYEDmTNT0caZHbosdw1AlQ1l2JXcAfi2T07iXJkojSWVzf1SkVJ6A4j1+J3ON 12 | pT9zutZzjpkGRJkGxs1A4jZKWoSxXSv5J7KObwIDAQABMA0GCSqGSIb3DQEBCwUA 13 | A4IBAQAIKHVsFujOyXhn8nWenrg8xgJHJ1t76i1h91S8dtJz12u3OjvhHKt78mZq 14 | DOtOWKevY/h/StGu5Qmm/hDfTRKA4g+fIDYIf/WGAs6PO3Kac1GfWaTAwtDBfeP2 15 | Sl4Jy47w5oAlUPu0LKszrpN/tPVfteSvhuo/ObTnj6lW4z/nArzPrrTDirzrpxVW 16 | CIJrTpKr2N8EoBufE2XVA54w249nVjd1pPgPJ8bpBVD2UgNZ95nZDYCCTKb4CDdc 17 | JZ69CxnWq+SwRk4Gy0A3YOIjzocTzEDaGlBYaSd33zRDbOuEb8S85Tg3dzATPPtm 18 | 7yQGi2sB6Lrs3qL9lc2fE/t7Sjgj 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /ballerina/configs.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | # SOAP client configurations. 20 | # 21 | # + httpConfig - HTTP Configuration 22 | # + outboundSecurity - Web service security configurations for SOAP requests 23 | # + inboundSecurity - Web service security configurations to decrypt and verify SOAP responses 24 | public type ClientConfig record {| 25 | http:ClientConfiguration httpConfig = {}; 26 | OutboundSecurityConfig|OutboundSecurityConfig[] outboundSecurity = NO_POLICY; 27 | InboundSecurityConfig inboundSecurity = {}; 28 | |}; 29 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/sec_header.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | isolated class WSSecurityHeader { 20 | 21 | private handle nativeSecHeader; 22 | 23 | isolated function init(Document document) returns Error? { 24 | self.nativeSecHeader = newSecHeader(document); 25 | } 26 | 27 | isolated function insertSecHeader() returns Error? = @java:Method { 28 | 'class: "org.wssec.WsSecurityHeader" 29 | } external; 30 | } 31 | 32 | isolated function newSecHeader(Document document) returns handle = @java:Constructor { 33 | 'class: "org.wssec.WsSecurityHeader" 34 | } external; 35 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/x509_certificate.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUHYnC3ALHIdLNO+JlFu/8usNrq2owDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA4MjUwNzE5NDVaFw0yNDA4 5 | MjQwNzE5NDVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQDdfueteXm2iSP+Pjr0+tK4G5pCxshqq0Z/ldDRpMzx 8 | vCAOFWIDS/ewcigUx5gMM7KHZfSzVXjlxahF5gqDbriunTSymFPfAQHimM13q1XZ 9 | uBCdHh1ayKmXuP4H/T1zp35rfElyGlfocWNx/Lef4y5ZFJN0BMScVdtue4+vCtst 10 | 5NLOBaFBoZU3DGk7muu6+7+ml8P4WqSW7CR0usTvrmPssDHUqXCA7vARlGoGgTHy 11 | trp7e7epNf5AZD3QjSWpLEIb6DuY9LyQYeC2Yry6GGN129lNJjh4HsXG6ldu0QQU 12 | rCkma6oP9JSCHI0Dm0i0lDVTFYLjJ60tap7r5dXEKBXRAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBTEWR0+4AmZuo10dDXp0D9bVeDGlDAfBgNVHSMEGDAWgBTEWR0+4AmZuo10 14 | dDXp0D9bVeDGlDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAi 15 | NlCjK/OQaKPanfyacR+v8k3RU4qs7CJJ78el8iN7ZWAQn9Z3o5VIwnHWCNYIBZr9 16 | CWSzV72EO7j9qXSLkbQL594a6+lDSxF7vwsEx8N1ktxz4Dkg0GPyrViBDTlcloOG 17 | g5/ZNKsP++QczspdnJuyOTCL3DLd/P1d3WzfJF+NefvtDhmcpZxZL1YPKpeAobqf 18 | nRLQVeWhZbNn/XbiWnrwx4LfN+r8aQh5kiO4xl31lPzg9cflPGhTHqG3Undeis9k 19 | upnsjgsfBNas54lgYpOadP0f8Ooc0nJvT7ry0LbUgbkk7dGkWm9Qh2eu2e5v7Fw8 20 | 0qWPT/H83ANtk/4Y5XCA 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /.github/workflows/build-with-bal-test-graalvm.yml: -------------------------------------------------------------------------------- 1 | name: GraalVM Check 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | lang_tag: 7 | description: Branch/Release Tag of the Ballerina Lang 8 | required: true 9 | default: master 10 | lang_version: 11 | description: Ballerina Lang Version (If given ballerina lang build will be skipped) 12 | required: false 13 | default: '' 14 | native_image_options: 15 | description: Default native-image options 16 | required: false 17 | default: '' 18 | schedule: 19 | - cron: '30 18 * * *' 20 | pull_request: 21 | branches: 22 | - master 23 | types: [opened, synchronize, reopened, labeled, unlabeled] 24 | 25 | concurrency: 26 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | call_stdlib_workflow: 31 | name: Run StdLib Workflow 32 | if: ${{ github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository_owner == 'ballerina-platform') }} 33 | uses: ballerina-platform/ballerina-library/.github/workflows/build-with-bal-test-graalvm-template.yml@main 34 | with: 35 | lang_tag: ${{ inputs.lang_tag }} 36 | lang_version: ${{ inputs.lang_version }} 37 | native_image_options: ${{ inputs.native_image_options }} 38 | -------------------------------------------------------------------------------- /.github/workflows/update_specs.yml: -------------------------------------------------------------------------------- 1 | name: Update Specifications 2 | 3 | env: 4 | SPEC_FOLDER_PATH: 'docs/spec' 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - master 11 | paths: 12 | - 'docs/spec/**' 13 | 14 | jobs: 15 | update_specs: 16 | name: Update Specifications 17 | if: github.repository_owner == 'ballerina-platform' 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout Repository 22 | uses: actions/checkout@v2 23 | 24 | - name: Get current date 25 | id: date 26 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 27 | 28 | - name: Get Repo Name 29 | id: repo_name 30 | run: | 31 | MODULE=${{ github.event.repository.name }} 32 | echo "::set-output name=short_name::${MODULE##*-}" 33 | 34 | - name: Trigger Workflow 35 | run: | 36 | curl --request POST \ 37 | 'https://api.github.com/repos/ballerina-platform/ballerina-dev-website/dispatches' \ 38 | -H 'Accept: application/vnd.github.v3+json' \ 39 | -H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \ 40 | --data "{ 41 | \"event_type\": \"update-stdlib-specs\", 42 | \"client_payload\": { 43 | \"module_name\": \"${{ github.event.repository.name }}\", 44 | \"short_name\": \"${{ steps.repo_name.outputs.short_name }}\", 45 | \"file_dir\": \"${{ github.event.repository.name }}/${{ env.SPEC_FOLDER_PATH }}\", 46 | \"release_date\": \"${{ steps.date.outputs.date }}\" 47 | } 48 | }" 49 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/encryption.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | isolated class Encryption { 20 | 21 | private handle nativeEncryption; 22 | 23 | isolated function init() returns Error? { 24 | self.nativeEncryption = newEncryption(); 25 | } 26 | 27 | public isolated function setEncryptionAlgorithm(string encryptionAlgorithm) = @java:Method { 28 | 'class: "org.wssec.Encryption" 29 | } external; 30 | 31 | public isolated function setEncryptedData(byte[] encryptedData) = @java:Method { 32 | 'class: "org.wssec.Encryption" 33 | } external; 34 | 35 | public isolated function getEncryptedKeyElements(byte[] encryptedKey) returns string|Error = @java:Method { 36 | 'class: "org.wssec.Encryption" 37 | } external; 38 | } 39 | 40 | isolated function newEncryption() returns handle = @java:Constructor { 41 | 'class: "org.wssec.Encryption" 42 | } external; 43 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/xml/soap_envelope.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 3c9c5c6d-2123-405f-ab25-7399001e9512 10 | 2023-08-08T16:15:36.806+02:00 11 | 12 | MunicipalityCitizenRight 13 | 14 | 15 | 01051537140 16 | 17 | 2024-08-08+02:00 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/certificate.pem: -------------------------------------------------------------------------------- 1 | Bag Attributes 2 | friendlyName: mykey 3 | localKeyID: 54 69 6D 65 20 31 37 32 38 32 32 31 33 34 37 35 35 38 4 | subject=C=Unknown, ST=Unknown, L=Unknown, O=Unknown, OU=Unknown, CN=Unknown 5 | issuer=C=Unknown, ST=Unknown, L=Unknown, O=Unknown, OU=Unknown, CN=Unknown 6 | -----BEGIN CERTIFICATE----- 7 | MIIDezCCAmOgAwIBAgIIZbI69qIsxNgwDQYJKoZIhvcNAQELBQAwbDEQMA4GA1UE 8 | BhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQ 9 | MA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5r 10 | bm93bjAeFw0yNDEwMDYxMzI3MzhaFw0yNTEwMDYxMzI3MzhaMGwxEDAOBgNVBAYT 11 | B1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24xEDAO 12 | BgNVBAoTB1Vua25vd24xEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAMTB1Vua25v 13 | d24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7rrO9u6/H4VboCxbs 14 | WX7eF/Kbjhzjx61zd2gIot4t+/e7j4R/4Hejn9+cgB++NY3uHtJ5wlVYtSDn5XJO 15 | XwjyNClqv8adBCkiCiwGClzgd+pUk6w+8ObbCgT7NZzVU7oh6LNpzYZXYsr4FkPr 16 | tRwopksO1pFGMJtDxWEkUAIj/NjscwgTmZJ+Tt2ISWx8Vv0h3rYVSyLqHdgjIl/W 17 | FAPMbHgiPL2lqWRK7/sOUqtn+hHuj2W+T1rzSf70642Ng++DHKwiGc+N33AMXyvH 18 | 0qkhCBytC1Bd6JeWwZaonZKkdWj/4qhWhLcKGxJWrMDLIDhEo/I2aB4DUgAEFgkv 19 | GaUNAgMBAAGjITAfMB0GA1UdDgQWBBTxqECeIvo1iOqIh+ewk2O9H9+PTDANBgkq 20 | hkiG9w0BAQsFAAOCAQEAGUbw159JMWFavJulqNaBzDe70Zd5Rtp+uGP9xgQN3Gqa 21 | TBK/UhlmnMJw+0ess9iHELDJsMAexcJFajZc2p2W3qCrGvWtMglB7FGWjJU9N5SQ 22 | eX1d5wXSLg4i2G0DFx6bQcRbbBqwoF392JQjx6aiPXfP90hp/R3OkznNUXhpFFmq 23 | AFFe/eg3RtI3aDBYxlDDHh92JxwVrq1QMh2vaGuLJFGV3AEa+Zn4lSRX5cB2NcxI 24 | 1PZSCQx/cl8AO8x0WRyU+xvMVCtXG/Izz+iCUSnkC7tSVTAccaJgnIVPHRx+UkGm 25 | vBX2ttTLDhFUrDYMYEEyXu+aqmEIXPoIHwa2qevgtQ== 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/Utils.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.creators.ErrorCreator; 20 | import io.ballerina.runtime.api.utils.StringUtils; 21 | import io.ballerina.runtime.api.values.BError; 22 | 23 | import static org.wssec.ModuleUtils.getModule; 24 | 25 | public class Utils { 26 | 27 | private Utils() { 28 | } 29 | 30 | public static final String ERROR_TYPE = "Error"; 31 | public static final String POLICY_NOT_SUPPORTED_ERROR = "Given ws security policy is currently not supported"; 32 | 33 | public static BError createError(String message) { 34 | return ErrorCreator.createError(getModule(), ERROR_TYPE, 35 | StringUtils.fromString(message), null, null); 36 | } 37 | 38 | public static BError createError(String message, BError cause) { 39 | return ErrorCreator.createError(getModule(), ERROR_TYPE, StringUtils.fromString(message), cause, null); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html 8 | */ 9 | 10 | pluginManagement { 11 | plugins { 12 | id "com.github.spotbugs" version "${spotbugsPluginVersion}" 13 | id "com.github.johnrengelman.shadow" version "${shadowJarPluginVersion}" 14 | id "de.undercouch.download" version "${downloadPluginVersion}" 15 | id "net.researchgate.release" version "${releasePluginVersion}" 16 | id "io.ballerina.plugin" version "${ballerinaGradlePluginVersion}" 17 | } 18 | 19 | repositories { 20 | gradlePluginPortal() 21 | maven { 22 | url = 'https://maven.pkg.github.com/ballerina-platform/*' 23 | credentials { 24 | username System.getenv("packageUser") 25 | password System.getenv("packagePAT") 26 | } 27 | } 28 | } 29 | } 30 | 31 | plugins { 32 | id "com.gradle.enterprise" version "3.13.2" 33 | } 34 | 35 | include ':checkstyle' 36 | include ':soap-native' 37 | include ':soap-ballerina' 38 | 39 | project(':checkstyle').projectDir = file("build-config${File.separator}checkstyle") 40 | project(':soap-native').projectDir = file('native') 41 | project(':soap-ballerina').projectDir = file('ballerina') 42 | 43 | gradleEnterprise { 44 | buildScan { 45 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 46 | termsOfServiceAgree = 'yes' 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/document.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | public isolated class Document { 20 | 21 | public isolated function init(xml xmlPayload) returns Error? { 22 | handle|error documentBuilder = newDocument(self, xmlPayload); 23 | if documentBuilder is error { 24 | return error Error(documentBuilder.message()); 25 | } 26 | } 27 | 28 | public isolated function getEnvelope() returns xml|Error = @java:Method { 29 | 'class: "org.wssec.DocumentBuilder" 30 | } external; 31 | 32 | public isolated function getEncryptedData() returns byte[] = @java:Method { 33 | 'class: "org.wssec.DocumentBuilder" 34 | } external; 35 | 36 | public isolated function getSignatureData() returns byte[] = @java:Method { 37 | 'class: "org.wssec.DocumentBuilder" 38 | } external; 39 | } 40 | 41 | isolated function newDocument(Document doc, xml xmlPayload) returns handle|error = @java:Constructor { 42 | 'class: "org.wssec.DocumentBuilder" 43 | } external; 44 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/resources/private_key1.pem: -------------------------------------------------------------------------------- 1 | Bag Attributes 2 | friendlyName: mykey 3 | localKeyID: 54 69 6D 65 20 31 37 32 38 32 32 31 33 34 37 35 35 38 4 | Key Attributes: 5 | -----BEGIN PRIVATE KEY----- 6 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7rrO9u6/H4Vbo 7 | CxbsWX7eF/Kbjhzjx61zd2gIot4t+/e7j4R/4Hejn9+cgB++NY3uHtJ5wlVYtSDn 8 | 5XJOXwjyNClqv8adBCkiCiwGClzgd+pUk6w+8ObbCgT7NZzVU7oh6LNpzYZXYsr4 9 | FkPrtRwopksO1pFGMJtDxWEkUAIj/NjscwgTmZJ+Tt2ISWx8Vv0h3rYVSyLqHdgj 10 | Il/WFAPMbHgiPL2lqWRK7/sOUqtn+hHuj2W+T1rzSf70642Ng++DHKwiGc+N33AM 11 | XyvH0qkhCBytC1Bd6JeWwZaonZKkdWj/4qhWhLcKGxJWrMDLIDhEo/I2aB4DUgAE 12 | FgkvGaUNAgMBAAECggEALm7cfTZwGM2BSMtlknfZ0WyvUxjnwNrn6MdD788LlOjf 13 | s4GgUFrifpLRKdDxCYgKYz0w6XrQzq+RQo4bp5QPjIynKofjxXkADDHkDmKF8+r3 14 | CJG4baIAG3TxIo1zDbw3Mqh1qtl0QS5p9NLdXvVh0BTEQRmIu4rO/wdYLzm0Ld61 15 | nnB8TCxdgniJfFyHP86bZuOOtFT2X7FpDxP+4UMZzR+9rjvfFezuCZin8Gf4oq18 16 | Yf+6evBYSIPwAlAm9QbM5iwLREdVbAceNe1/utrIIl//OAH10DOzYGrLpLH2/D7v 17 | aYjWT8FlCt5Yspsv50W1m8f44xn+cpdVDsFzEt73QQKBgQDJtd8QCjohjNzViKn/ 18 | LN43EYuvDH2tal+vg2lOrz8OcQDHiz8DFJzoxAwoSwcwcN+JPOVIJqKov+ztgVYq 19 | S1+9lK+6Gv4JasTcypPMCLN27bchp7KsZCBu2wHdr8sp19+vfEu1X48Ubb0fkX/c 20 | 4huiAV1wjaaa+nmOpcA4/oo+VQKBgQDuMkgjqqEgsO56vM3iRM0A9stgVnUP3yyC 21 | vSL6pgblwROLO913l/bKDV5B1hXAFBcLLlTRGdMbVzNgnNpdZdwKkfufQfWyMKx1 22 | z2SoRlxgwlxrBtfmunHdMqLT+piwwuRLlbYWSmyDL9ghDEo/22oAHn57rJAZlmlb 23 | tHB/waCT2QKBgQClu+HD9CM/XdY1PU0wdVVAOhJjigfZbQWh2H+2Pxe4bfEOA8OK 24 | bG1gc3TpxnvpuVRyFq7tUZFkxg2OOC7sIXJQ+tJIP9VrN2b5Yxl9E8khdsB5zqho 25 | LPzZGOm3lLGBd/Y64g3ywMl3J5O1VH+SpdW+jxCPYlP6EsO+CUKfkcVU4QKBgQDm 26 | 96Kz0vRCes4D/ae1y/jtAmHanHsOVN0YOMX+PZdamYmV7QqmuJf4/FV1iV21zsU5 27 | dkeQKnZlgHy1JeMnxWlEZqGSn6bajg/sfJmiAff5av2qWgxoEknurvbsjYYZgCFW 28 | mWji3G+0FWSBRyWIHf3+95K14XIpHYwz/BdKCjrmoQKBgDwoPA5+foz6BH/Ut/KV 29 | Hj+zqrJO8M0p4Ys4/+vB7r4Mcin8hcdY920dYxPmkmz2pMEnplOas8GZUzE/KyX5 30 | wOSNzobJN+Rj3q851+u1fgl/vO78nXod2e9tvojXVz9a8EW23i17fsRt9221Nfdv 31 | 6RlOnqnMghUgVz8D7irlWLnD 32 | -----END PRIVATE KEY----- 33 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/types.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | public enum PasswordType { 18 | TEXT, 19 | DIGEST, 20 | DERIVED_KEY_TEXT, 21 | DERIVED_KEY_DIGEST 22 | } 23 | 24 | public enum SignatureAlgorithm { 25 | RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1", 26 | RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", 27 | RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", 28 | RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" 29 | } 30 | 31 | public enum CanonicalizationAlgorithm { 32 | C14N_OMIT_COMMENTS = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", 33 | C14N_WITH_COMMENTS = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", 34 | C14N_EXCL_OMIT_COMMENTS = "http://www.w3.org/2001/10/xml-exc-c14n#", 35 | C14N_EXCL_WITH_COMMENTS = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" 36 | } 37 | 38 | public enum DigestAlgorithm { 39 | SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1", 40 | SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256", 41 | SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384", 42 | SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512" 43 | } 44 | 45 | public enum EncryptionAlgorithm { 46 | TRIPLE_DES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc", 47 | AES_128 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc", 48 | AES_256 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc" 49 | } 50 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/WsSecurityHeader.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.utils.StringUtils; 20 | import io.ballerina.runtime.api.values.BError; 21 | import io.ballerina.runtime.api.values.BHandle; 22 | import io.ballerina.runtime.api.values.BObject; 23 | import org.apache.wss4j.common.ext.WSSecurityException; 24 | import org.apache.wss4j.dom.message.WSSecHeader; 25 | import org.w3c.dom.Document; 26 | 27 | import static org.wssec.Constants.NATIVE_DOCUMENT; 28 | import static org.wssec.Constants.NATIVE_SEC_HEADER; 29 | import static org.wssec.Utils.createError; 30 | 31 | public class WsSecurityHeader { 32 | 33 | private final WSSecHeader wsSecHeader; 34 | private final Document document; 35 | 36 | public WsSecurityHeader(BObject documentBuilder) { 37 | Document document = (Document) documentBuilder.getNativeData().get(NATIVE_DOCUMENT); 38 | this.wsSecHeader = new WSSecHeader(document); 39 | this.document = document; 40 | } 41 | 42 | protected Document getDocument() { 43 | return document; 44 | } 45 | 46 | protected WSSecHeader getWsSecHeader() { 47 | return wsSecHeader; 48 | } 49 | 50 | public static BError insertSecHeader(BObject secHeader) { 51 | BHandle handle = (BHandle) secHeader.get(StringUtils.fromString(NATIVE_SEC_HEADER)); 52 | WsSecurityHeader wsSecurityHeader = (WsSecurityHeader) handle.getValue(); 53 | try { 54 | wsSecurityHeader.getWsSecHeader().insertSecurityHeader(); 55 | } catch (WSSecurityException e) { 56 | return createError(e.getMessage()); 57 | } 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/lib/soap/Soap.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package io.ballerina.lib.soap; 18 | 19 | import io.ballerina.runtime.api.Environment; 20 | import io.ballerina.runtime.api.values.BError; 21 | import io.ballerina.runtime.api.values.BMap; 22 | import io.ballerina.runtime.api.values.BObject; 23 | import io.ballerina.runtime.api.values.BString; 24 | import io.ballerina.runtime.api.values.BTypedesc; 25 | 26 | public class Soap { 27 | private static final String REMOTE_FUNCTION = "generateResponse"; 28 | 29 | public static Object sendReceive11(Environment env, BObject soap11, Object body, BString action, 30 | BMap headers, BString path, BTypedesc typeDesc) { 31 | return env.yieldAndRun(() -> { 32 | try { 33 | Object[] arguments = new Object[]{body, action, headers, path}; 34 | Object result = env.getRuntime().callMethod(soap11, REMOTE_FUNCTION, null, arguments); 35 | if (result instanceof BError) { 36 | ((BError) result).printStackTrace(); 37 | } 38 | return result; 39 | } catch (BError bError) { 40 | bError.printStackTrace(); 41 | System.exit(1); 42 | } 43 | return null; 44 | }); 45 | } 46 | 47 | public static Object sendReceive12(Environment env, BObject soap12, Object body, Object action, 48 | BMap headers, BString path, BTypedesc typeDesc) { 49 | return env.yieldAndRun(() -> { 50 | try { 51 | Object[] arguments = new Object[]{body, action, headers, path}; 52 | return env.getRuntime().callMethod(soap12, REMOTE_FUNCTION, null, arguments); 53 | } catch (BError bError) { 54 | bError.printStackTrace(); 55 | System.exit(1); 56 | } 57 | return null; 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/Signature.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.creators.ValueCreator; 20 | import io.ballerina.runtime.api.utils.StringUtils; 21 | import io.ballerina.runtime.api.values.BArray; 22 | import io.ballerina.runtime.api.values.BHandle; 23 | import io.ballerina.runtime.api.values.BObject; 24 | import io.ballerina.runtime.api.values.BString; 25 | 26 | import static org.wssec.Constants.NATIVE_SIGNATURE; 27 | 28 | public class Signature { 29 | 30 | private String signatureAlgorithm; 31 | private byte[] signatureValue; 32 | 33 | public static void setSignatureAlgorithm(BObject sign, BString signatureAlgorithm) { 34 | BHandle handle = (BHandle) sign.get(StringUtils.fromString(NATIVE_SIGNATURE)); 35 | Signature signature = (Signature) handle.getValue(); 36 | signature.setSignatureAlgorithm(signatureAlgorithm.getValue()); 37 | } 38 | 39 | public static void setSignatureValue(BObject sign, BArray signatureValue) { 40 | BHandle handle = (BHandle) sign.get(StringUtils.fromString(NATIVE_SIGNATURE)); 41 | Signature signature = (Signature) handle.getValue(); 42 | signature.setSignatureValue(signatureValue.getByteArray()); 43 | } 44 | 45 | public static BArray getSignatureValue(BObject sign) { 46 | BHandle handle = (BHandle) sign.get(StringUtils.fromString(NATIVE_SIGNATURE)); 47 | Signature signature = (Signature) handle.getValue(); 48 | return ValueCreator.createArrayValue(signature.getSignatureValue()); 49 | } 50 | 51 | protected String getSignatureAlgorithm() { 52 | return signatureAlgorithm; 53 | } 54 | 55 | protected void setSignatureAlgorithm(String signatureAlgorithm) { 56 | this.signatureAlgorithm = signatureAlgorithm; 57 | } 58 | 59 | protected byte[] getSignatureValue() { 60 | return signatureValue; 61 | } 62 | 63 | protected void setSignatureValue(byte[] signatureValue) { 64 | this.signatureValue = signatureValue; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | > Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc. 3 | 4 | ## Goals 5 | > Describe the solutions that this feature/fix will introduce to resolve the problems described above 6 | 7 | ## Approach 8 | > Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here. 9 | 10 | ## User stories 11 | > Summary of user stories addressed by this change> 12 | 13 | ## Release note 14 | > Brief description of the new feature or bug fix as it will appear in the release notes 15 | 16 | ## Documentation 17 | > Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact 18 | 19 | ## Training 20 | > Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable 21 | 22 | ## Certification 23 | > Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why. 24 | 25 | ## Marketing 26 | > Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable 27 | 28 | ## Automation tests 29 | - Unit tests 30 | > Code coverage information 31 | - Integration tests 32 | > Details about the test cases and coverage 33 | 34 | ## Security checks 35 | - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no 36 | - Ran FindSecurityBugs plugin and verified report? yes/no 37 | - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no 38 | 39 | ## Samples 40 | > Provide high-level details about the samples related to this feature 41 | 42 | ## Related PRs 43 | > List any other related PRs 44 | 45 | ## Migrations (if applicable) 46 | > Describe migration steps and platforms on which migration has been tested 47 | 48 | ## Test environment 49 | > List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested 50 | 51 | ## Learning 52 | > Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem. -------------------------------------------------------------------------------- /ballerina/types.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import soap.wssec; 18 | 19 | # Represents enums for all the supported password types. 20 | # 21 | public enum PasswordType { 22 | TEXT, 23 | DIGEST, 24 | DERIVED_KEY_TEXT, 25 | DERIVED_KEY_DIGEST 26 | } 27 | 28 | # Represents enums for all the supported signature algorithms. 29 | # 30 | public enum SignatureAlgorithm { 31 | RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1", 32 | RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", 33 | RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", 34 | RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" 35 | } 36 | 37 | # Represents enums for all the supported encryption algorithms. 38 | # 39 | public enum EncryptionAlgorithm { 40 | TRIPLE_DES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc", 41 | AES_128 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc", 42 | AES_256 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc" 43 | } 44 | 45 | # Represents enums for all the supported canonicalization algorithms. 46 | # 47 | public enum CanonicalizationAlgorithm { 48 | C14N_OMIT_COMMENTS = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", 49 | C14N_WITH_COMMENTS = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", 50 | C14N_EXCL_OMIT_COMMENTS = "http://www.w3.org/2001/10/xml-exc-c14n#", 51 | C14N_EXCL_WITH_COMMENTS = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" 52 | } 53 | 54 | # Represents enums for all the supported digest algorithms. 55 | # 56 | public enum DigestAlgorithm { 57 | SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1", 58 | SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256", 59 | SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384", 60 | SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512" 61 | } 62 | 63 | # Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 64 | # 65 | # + decryptKeystore - The keystore to decrypt the SOAP envelope 66 | # + signatureKeystore - The keystore to verify the signature of the SOAP envelope 67 | public type InboundSecurityConfig wssec:InboundConfig; 68 | 69 | # Union type of all the outbound web service security configurations. 70 | public type OutboundSecurityConfig wssec:OutboundSecurityConfig; 71 | 72 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/DocumentBuilder.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.creators.ValueCreator; 20 | import io.ballerina.runtime.api.values.BArray; 21 | import io.ballerina.runtime.api.values.BObject; 22 | import io.ballerina.runtime.api.values.BXml; 23 | import org.w3c.dom.Document; 24 | import org.xml.sax.InputSource; 25 | 26 | import java.io.StringReader; 27 | 28 | import javax.xml.parsers.DocumentBuilderFactory; 29 | 30 | import static org.wssec.Constants.NATIVE_DOCUMENT; 31 | import static org.wssec.Utils.createError; 32 | import static org.wssec.WsSecurityUtils.convertDocumentToString; 33 | 34 | public final class DocumentBuilder { 35 | private final Document document; 36 | 37 | public DocumentBuilder(BObject documentBuilder, BXml xmlPayload) throws Exception { 38 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 39 | factory.setNamespaceAware(true); 40 | this.document = factory.newDocumentBuilder().parse(new InputSource( 41 | new StringReader(xmlPayload.toString()))); 42 | documentBuilder.addNativeData(NATIVE_DOCUMENT, this.document); 43 | } 44 | 45 | protected DocumentBuilder(Document document) { 46 | this.document = document; 47 | } 48 | 49 | public static Object getEnvelope(BObject document) { 50 | Document nativeDocument = (Document) document.getNativeData().get(NATIVE_DOCUMENT); 51 | try { 52 | return ValueCreator.createXmlValue(convertDocumentToString(nativeDocument).toString()); 53 | } catch (Exception e) { 54 | return createError(e.getMessage()); 55 | } 56 | } 57 | 58 | public static BArray getSignatureData(BObject document) { 59 | Document nativeDocument = (Document) document.getNativeData().get(NATIVE_DOCUMENT); 60 | return ValueCreator.createArrayValue(WsSecurityUtils.getSignatureValue(nativeDocument)); 61 | } 62 | 63 | public static BArray getEncryptedData(BObject document) { 64 | Document nativeDocument = (Document) document.getNativeData().get(NATIVE_DOCUMENT); 65 | return ValueCreator.createArrayValue(WsSecurityUtils.getEncryptedData(nativeDocument)); 66 | } 67 | 68 | protected Document getNativeDocument() { 69 | return this.document; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/ws_security.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | isolated class WsSecurity { 20 | isolated function applyUsernameTokenPolicy(WSSecurityHeader wsSecurityHeader, string username, 21 | string password, string pwType) returns string|Error = @java:Method { 22 | 'class: "org.wssec.WsSecurity" 23 | } external; 24 | 25 | isolated function applyTimestampPolicy(WSSecurityHeader wsSecurityHeader, int timeToLive) 26 | returns string|Error = @java:Method { 27 | 'class: "org.wssec.WsSecurity" 28 | } external; 29 | 30 | isolated function applySignatureOnlyPolicy(WSSecurityHeader wsSecurityPolicy, Signature signature, 31 | string? x509FilePath) returns string|Error = @java:Method { 32 | 'class: "org.wssec.WsSecurity" 33 | } external; 34 | 35 | isolated function applySignatureOnly(Document soapEnvelope, boolean soap12, SignatureConfig signatureConfig) 36 | returns string|Error = @java:Method { 37 | 'class: "org.wssec.WsSecurity" 38 | } external; 39 | 40 | isolated function applyEncryptionOnly(Document soapEnvelope, boolean soap12, EncryptionConfig encryptionConfig) 41 | returns string|Error = @java:Method { 42 | 'class: "org.wssec.WsSecurity" 43 | } external; 44 | 45 | isolated function applySignatureAndEncryption(Document soapEnvelope, boolean soap12, 46 | SignatureConfig signatureConfig, EncryptionConfig encryptionConfig) 47 | returns string|Error = @java:Method { 48 | 'class: "org.wssec.WsSecurity" 49 | } external; 50 | 51 | isolated function applyEncryptionOnlyPolicy(WSSecurityHeader wsSecurityPolicy, Encryption encryption) 52 | returns string|Error = @java:Method { 53 | 'class: "org.wssec.WsSecurity" 54 | } external; 55 | 56 | isolated function verifySignature(Document soapEnvelope, InboundConfig config) 57 | returns boolean|error = @java:Method { 58 | 'class: "org.wssec.WsSecurity" 59 | } external; 60 | 61 | isolated function decryptEnvelope(Document soapEnvelope, InboundConfig config) 62 | returns Document|error = @java:Method { 63 | 'class: "org.wssec.WsSecurity" 64 | } external; 65 | } 66 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/Encryption.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.creators.ValueCreator; 20 | import io.ballerina.runtime.api.utils.StringUtils; 21 | import io.ballerina.runtime.api.values.BArray; 22 | import io.ballerina.runtime.api.values.BHandle; 23 | import io.ballerina.runtime.api.values.BObject; 24 | import io.ballerina.runtime.api.values.BString; 25 | 26 | import static org.wssec.Constants.NATIVE_ENCRYPTION; 27 | import static org.wssec.Utils.createError; 28 | 29 | public class Encryption { 30 | 31 | private String encryptionAlgorithm; 32 | private byte[] encryptedData; 33 | 34 | public static void setEncryptionAlgorithm(BObject encrypt, BString encryptionAlgorithm) { 35 | BHandle handle = (BHandle) encrypt.get(StringUtils.fromString(NATIVE_ENCRYPTION)); 36 | Encryption encryption = (Encryption) handle.getValue(); 37 | encryption.setEncryptionAlgorithm(encryptionAlgorithm.getValue()); 38 | } 39 | 40 | public static void setEncryptedData(BObject encrypt, BArray encryptedData) { 41 | BHandle handle = (BHandle) encrypt.get(StringUtils.fromString(NATIVE_ENCRYPTION)); 42 | Encryption encryption = (Encryption) handle.getValue(); 43 | encryption.setEncryptedData(encryptedData.getByteArray()); 44 | } 45 | 46 | public static BArray getEncryptedData(BObject encrypt) { 47 | BHandle handle = (BHandle) encrypt.get(StringUtils.fromString(NATIVE_ENCRYPTION)); 48 | Encryption encryption = (Encryption) handle.getValue(); 49 | return ValueCreator.createArrayValue(encryption.getEncryptedData()); 50 | } 51 | 52 | public static Object getEncryptedKeyElements(BArray encryptedData) { 53 | try { 54 | return WsSecurityUtils.getEncryptedKeyElement(encryptedData.getByteArray()); 55 | } catch (Exception e) { 56 | return createError(e.getMessage()); 57 | } 58 | } 59 | 60 | protected String getEncryptionAlgorithm() { 61 | return encryptionAlgorithm; 62 | } 63 | 64 | protected byte[] getEncryptedData() { 65 | return encryptedData; 66 | } 67 | 68 | protected void setEncryptionAlgorithm(String encryptionAlgorithm) { 69 | this.encryptionAlgorithm = encryptionAlgorithm; 70 | } 71 | 72 | protected void setEncryptedData(byte[] encryptedData) { 73 | this.encryptedData = encryptedData; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/signature.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/crypto; 18 | import ballerina/jballerina.java; 19 | 20 | isolated class Signature { 21 | 22 | private handle nativeSignature; 23 | 24 | isolated function init() returns Error? { 25 | self.nativeSignature = newSignature(); 26 | } 27 | 28 | public isolated function signData(string dataString, SignatureAlgorithm signatureAlgorithm, 29 | crypto:PrivateKey privateKey) returns byte[]|Error { 30 | byte[] data = dataString.toBytes(); 31 | do { 32 | match signatureAlgorithm { 33 | RSA_SHA1 => { 34 | return check crypto:signRsaSha1(data, privateKey); 35 | } 36 | RSA_SHA256 => { 37 | return check crypto:signRsaSha256(data, privateKey); 38 | } 39 | RSA_SHA384 => { 40 | return check crypto:signRsaSha384(data, privateKey); 41 | } 42 | _ => { 43 | return check crypto:signRsaSha512(data, privateKey); 44 | } 45 | } 46 | } on fail error signatureError { 47 | return error Error("Error occurred while signing the data", signatureError); 48 | } 49 | } 50 | 51 | public isolated function verifySignature(byte[] data, byte[] signature, crypto:PublicKey publicKey, 52 | SignatureAlgorithm signatureAlgorithm) returns boolean|Error { 53 | do { 54 | match signatureAlgorithm { 55 | RSA_SHA1 => { 56 | return check crypto:verifyRsaSha1Signature(data, signature, publicKey); 57 | } 58 | RSA_SHA256 => { 59 | return check crypto:verifyRsaSha256Signature(data, signature, publicKey); 60 | } 61 | RSA_SHA384 => { 62 | return check crypto:verifyRsaSha384Signature(data, signature, publicKey); 63 | } 64 | _ => { 65 | return check crypto:verifyRsaSha512Signature(data, signature, publicKey); 66 | } 67 | } 68 | } on fail error signatureError { 69 | return error Error("Error occurred while verifying the signature", signatureError); 70 | } 71 | } 72 | 73 | public isolated function setSignatureAlgorithm(string signatureAlgorithm) = @java:Method { 74 | 'class: "org.wssec.Signature" 75 | } external; 76 | 77 | public isolated function setSignatureValue(byte[] signatureValue) = @java:Method { 78 | 'class: "org.wssec.Signature" 79 | } external; 80 | } 81 | 82 | isolated function newSignature() returns handle = @java:Constructor { 83 | 'class: "org.wssec.Signature" 84 | } external; 85 | -------------------------------------------------------------------------------- /native/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | plugins { 19 | id 'java' 20 | id 'checkstyle' 21 | id 'com.github.spotbugs' 22 | } 23 | 24 | description = 'Ballerina - SOAP Java Utils' 25 | 26 | dependencies { 27 | checkstyle project(':checkstyle') 28 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 29 | 30 | implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 31 | implementation group: 'com.google.guava', name: 'guava', version: "${guavaVersion}" 32 | implementation group: 'org.apache.wss4j', name: 'wss4j-ws-security-dom', version: "${wsSecurityDomVersion}" 33 | implementation group: 'org.apache.wss4j', name: 'wss4j-ws-security-common', version: "${wsSecurityCommonVersion}" 34 | implementation group: 'org.apache.santuario', name: 'xmlsec', version: "${xmlSecVersion}" 35 | compileOnly group: 'org.graalvm.nativeimage', name: 'svm', version: '22.2.0' 36 | } 37 | 38 | checkstyle { 39 | toolVersion "${project.checkstylePluginVersion}" 40 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 41 | configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 42 | } 43 | 44 | checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") 45 | 46 | spotbugsMain { 47 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 48 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 49 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 50 | effort = SpotBugsEffort.MAX 51 | reportLevel = SpotBugsConfidence.LOW 52 | reportsDir = file("$project.buildDir/reports/spotbugs") 53 | reports { 54 | html.enabled true 55 | text.enabled = true 56 | } 57 | def excludeFile = file("${rootDir}/spotbugs-exclude.xml") 58 | if(excludeFile.exists()) { 59 | excludeFilter = excludeFile 60 | } 61 | } 62 | 63 | def excludePattern = '**/module-info.java' 64 | tasks.withType(Checkstyle) { 65 | exclude excludePattern 66 | } 67 | 68 | publishing { 69 | publications { 70 | mavenJava(MavenPublication) { 71 | groupId project.group 72 | artifactId "soap-native" 73 | version = project.version 74 | artifact jar 75 | } 76 | } 77 | 78 | repositories { 79 | maven { 80 | name = "GitHubPackages" 81 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-ballerina-constraint") 82 | credentials { 83 | username = System.getenv("publishUser") 84 | password = System.getenv("publishPAT") 85 | } 86 | } 87 | } 88 | } 89 | 90 | compileJava { 91 | doFirst { 92 | options.compilerArgs = [ 93 | '--module-path', classpath.asPath, 94 | ] 95 | classpath = files() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/Constants.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | public class Constants { 20 | 21 | private Constants() { 22 | } 23 | 24 | public static final int ITERATION = 1000; 25 | public static final String DIGEST = "DIGEST"; 26 | public static final String DERIVED_KEY_TEXT = "DERIVED_KEY_TEXT"; 27 | public static final String DERIVED_KEY_DIGEST = "DERIVED_KEY_DIGEST"; 28 | public static final String PASSWORD = "password"; 29 | public static final String NATIVE_SEC_HEADER = "nativeSecHeader"; 30 | public static final String NATIVE_DOCUMENT = "nativeDocumentBuilder"; 31 | public static final String NATIVE_SIGNATURE = "nativeSignature"; 32 | public static final String NATIVE_ENCRYPTION = "nativeEncryption"; 33 | public static final String SIGNATURE_VALUE_TAG = "ds:SignatureValue"; 34 | public static final String SIGNATURE_METHOD_TAG = "ds:SignatureMethod"; 35 | public static final String ALGORITHM = "Algorithm"; 36 | public static final String KEY_INFO_TAG = "ds:KeyInfo"; 37 | public static final String CIPHER_DATA_TAG = "xenc:CipherData"; 38 | public static final String XML_ENC_NS = "xmlns:xenc"; 39 | public static final String XML_DS_NS = "xmlns:ds"; 40 | public static final String ENCRYPTED_KEY_TAG = "xenc:EncryptedKey"; 41 | public static final String ENCRYPTION_METHOD_TAG = "xenc:EncryptionMethod"; 42 | public static final String NAMESPACE_URI_ENC = "http://www.w3.org/2001/04/xmlenc#"; 43 | public static final String CIPHER_VALUE_TAG = "CipherValue"; 44 | public static final String X509 = "X.509"; 45 | public static final String AES = "AES"; 46 | public static final String EMPTY_XML_DOCUMENT_ERROR = "XML Document is empty"; 47 | public static final String CRYPTO_PROVIDER_FIELD = "org.apache.ws.security.crypto.provider"; 48 | public static final String CRYPTO_PROVIDER_VALUE = "org.apache.wss4j.common.crypto.Merlin"; 49 | public static final String KEYSTORE = "keystore"; 50 | public static final String DECRYPT_KEYSTORE = "decryptKeystore"; 51 | public static final String SIGNATURE_KEYSTORE = "signatureKeystore"; 52 | public static final String PATH = "path"; 53 | public static final String DIGEST_ALGORITHM = "digestAlgorithm"; 54 | public static final String CANONICALIZATION_ALGORITHM = "canonicalizationAlgorithm"; 55 | public static final String SIGNATURE_ALGORITHM = "signatureAlgorithm"; 56 | public static final String PRIVATE_KEY_PASSWORD = "privateKeyPassword"; 57 | public static final String PRIVATE_KEY_ALIAS = "privateKeyAlias"; 58 | public static final String PUBLIC_KEY_ALIAS = "publicKeyAlias"; 59 | public static final String ENCRYPTION_ALGORITHM = "encryptionAlgorithm"; 60 | public static final String KEYSTORE_PATH_FIELD = "org.apache.ws.security.crypto.merlin.keystore.file"; 61 | public static final String KEYSTORE_PASSWORD_FIELD = "org.apache.ws.security.crypto.merlin.keystore.password"; 62 | } 63 | -------------------------------------------------------------------------------- /ballerina/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import org.apache.tools.ant.taskdefs.condition.Os 18 | 19 | plugins { 20 | id 'io.ballerina.plugin' 21 | } 22 | 23 | description = 'Ballerina - SOAP Module' 24 | 25 | def packageName = "soap" 26 | def packageOrg = "ballerina" 27 | def tomlVersion = stripBallerinaExtensionVersion("${project.version}") 28 | def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") 29 | def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") 30 | 31 | def stripBallerinaExtensionVersion(String extVersion) { 32 | if (extVersion.matches(project.ext.timestampedVersionRegex)) { 33 | def splitVersion = extVersion.split('-') 34 | if (splitVersion.length > 3) { 35 | def strippedValues = splitVersion[0..-4] 36 | return strippedValues.join('-') 37 | } else { 38 | return extVersion 39 | } 40 | } else { 41 | return extVersion.replace("${project.ext.snapshotVersion}", "") 42 | } 43 | } 44 | 45 | ballerina { 46 | packageOrganization = packageOrg 47 | module = packageName 48 | langVersion = ballerinaLangVersion 49 | testCoverageParam = "--code-coverage --coverage-format=xml --includes=io.ballerina.stdlib.soap.*:ballerina.soap*" 50 | } 51 | 52 | task updateTomlFiles { 53 | doLast { 54 | def wsSecurityDomVersion = project.wsSecurityDomVersion 55 | def wsSecurityCommonVersion = project.wsSecurityCommonVersion 56 | def xmlSecVersion = project.xmlSecVersion 57 | def newConfig = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) 58 | newConfig = newConfig.replace("@wssecuritydom.version@", wsSecurityDomVersion) 59 | newConfig = newConfig.replace("@wssecuritycommon.version@", wsSecurityCommonVersion) 60 | newConfig = newConfig.replace("@xmlsec.version@", xmlSecVersion) 61 | newConfig = newConfig.replace("@toml.version@", tomlVersion) 62 | ballerinaTomlFile.text = newConfig 63 | } 64 | } 65 | 66 | task commitTomlFiles { 67 | doLast { 68 | project.exec { 69 | ignoreExitValue true 70 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 71 | commandLine 'cmd', '/c', "git commit -m \"[Automated] Update the native jar versions\" Ballerina.toml Dependencies.toml" 72 | } else { 73 | commandLine 'sh', '-c', "git commit -m '[Automated] Update the native jar versions' Ballerina.toml Dependencies.toml" 74 | } 75 | } 76 | } 77 | } 78 | 79 | publishing { 80 | publications { 81 | maven(MavenPublication) { 82 | artifact source: createArtifactZip, extension: 'zip' 83 | } 84 | } 85 | 86 | repositories { 87 | maven { 88 | name = "GitHubPackages" 89 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-${packageOrg}-${packageName}") 90 | credentials { 91 | username = System.getenv("publishUser") 92 | password = System.getenv("publishPAT") 93 | } 94 | } 95 | } 96 | } 97 | 98 | updateTomlFiles.dependsOn copyStdlibs 99 | 100 | test.dependsOn ":${packageName}-native:build" 101 | build.dependsOn "generatePomFileForMavenPublication" 102 | build.dependsOn ":${packageName}-native:build" 103 | publishToMavenLocal.dependsOn build 104 | publish.dependsOn build 105 | -------------------------------------------------------------------------------- /ballerina/modules/soap11/tests/http_soap_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | import ballerina/crypto; 17 | import ballerina/http; 18 | import ballerina/mime; 19 | import ballerina/soap; 20 | 21 | const crypto:KeyStore serverKeyStore = { 22 | path: X509_KEY_STORE_PATH, 23 | password: KEY_PASSWORD 24 | }; 25 | crypto:PrivateKey serverPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(serverKeyStore, KEY_ALIAS, 26 | KEY_PASSWORD); 27 | crypto:PublicKey serverPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(serverKeyStore, KEY_ALIAS); 28 | 29 | service / on new http:Listener(9090) { 30 | 31 | resource function post .(http:Request request) returns xml|error { 32 | string action = check request.getHeader("SOAPAction"); 33 | if action == "http://tempuri.org/Add" { 34 | return xml `5`; 35 | } else { 36 | return xml `soap:ClientSystem.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://tempuri.org/invalid_action. 37 | at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest() 38 | at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message) 39 | at System.Web.Services.Protocols.SoapServerProtocol.Initialize() 40 | at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response) 41 | at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)`; 42 | } 43 | } 44 | 45 | resource function post getPayload(http:Request request) returns xml|error { 46 | return check (check request.getBodyParts())[0].getXml(); 47 | } 48 | 49 | resource function post getMimePayload(http:Request request) returns http:Response|error { 50 | http:Response response = new; 51 | mime:Entity[] mtomMessage = []; 52 | mime:Entity envelope = new; 53 | check envelope.setContentType("application/xop+xml"); 54 | envelope.setContentId(""); 55 | envelope.setBody(check (check request.getBodyParts())[0].getXml()); 56 | mtomMessage.push(envelope); 57 | response.setBodyParts(mtomMessage); 58 | response.setPayload(mtomMessage); 59 | return response; 60 | } 61 | 62 | resource function post getSamePayload(http:Request request) returns xml|error { 63 | return check request.getXmlPayload(); 64 | } 65 | 66 | resource function post getSecuredMimePayload(http:Request request) returns http:Response|error { 67 | xml payload = check (check request.getBodyParts())[0].getXml(); 68 | http:Response response = new; 69 | mime:Entity[] mtomMessage = []; 70 | mime:Entity envelope = new; 71 | check envelope.setContentType("application/xop+xml"); 72 | envelope.setContentId(""); 73 | envelope.setBody(payload); 74 | mtomMessage.push(envelope); 75 | response.setBodyParts(mtomMessage); 76 | response.setPayload(mtomMessage); 77 | return response; 78 | } 79 | 80 | resource function post getSecuredPayload(http:Request request) returns xml|error { 81 | xml payload = check request.getXmlPayload(); 82 | xml applyInboundConfig = check soap:applyInboundConfig( 83 | { 84 | decryptKeystore: { 85 | path: KEY_STORE_PATH_2, 86 | password: PASSWORD 87 | }, 88 | signatureKeystore: { 89 | path: KEY_STORE_PATH_2, 90 | password: PASSWORD 91 | } 92 | }, 93 | payload, 94 | false 95 | ); 96 | return applyInboundConfig; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/records.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/crypto; 18 | 19 | # Union type of all the inbound web service security configurations. 20 | public type OutboundSecurityConfig NoPolicy|UsernameTokenConfig|TimestampTokenConfig|SymmetricBindingConfig 21 | |TransportBindingConfig|AsymmetricBindingConfig; 22 | 23 | # Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 24 | # 25 | # + verificationKey - The public key to verify the signature of the SOAP envelope 26 | # + decryptionKey - The private key to decrypt the SOAP envelope 27 | # + signatureAlgorithm - The algorithm to verify the SOAP envelope 28 | # + decryptionAlgorithm - The algorithm to decrypt the SOAP body 29 | public type InboundSecurityConfig record {| 30 | crypto:PublicKey verificationKey?; 31 | crypto:PrivateKey|crypto:PublicKey decryptionKey?; 32 | SignatureAlgorithm signatureAlgorithm?; 33 | EncryptionAlgorithm decryptionAlgorithm?; 34 | |}; 35 | 36 | # Represents the record for Username Token policy. 37 | # 38 | # + username - The name of the user 39 | # + password - The password of the user 40 | # + passwordType - The password type of the username token 41 | public type UsernameTokenConfig record {| 42 | string username; 43 | string password; 44 | PasswordType passwordType; 45 | |}; 46 | 47 | # Represents the record for Timestamp Token policy. 48 | # 49 | # + timeToLive - The time to get expired 50 | public type TimestampTokenConfig record {| 51 | int timeToLive = 300; 52 | |}; 53 | 54 | # Represents the record for Symmetric Binding policy. 55 | # 56 | # + symmetricKey - The key to sign and encrypt the SOAP envelope 57 | # + servicePublicKey - The key to encrypt the symmetric key 58 | # + signatureAlgorithm - The algorithm to sign the SOAP envelope 59 | # + encryptionAlgorithm - The algorithm to encrypt the SOAP envelope 60 | # + x509Token - The path or token of the X509 certificate 61 | public type SymmetricBindingConfig record {| 62 | crypto:PrivateKey symmetricKey; 63 | crypto:PublicKey servicePublicKey; 64 | SignatureAlgorithm signatureAlgorithm?; 65 | EncryptionAlgorithm encryptionAlgorithm?; 66 | string x509Token?; 67 | |}; 68 | 69 | # Represents the record for Asymmetric Binding policy. 70 | # 71 | # + signatureConfig - Configuration for applying digital signatures 72 | # + encryptionConfig - Configuration for applying encryption 73 | # + x509Token - The path or token of the X509 certificate 74 | public type AsymmetricBindingConfig record {| 75 | SignatureConfig signatureConfig?; 76 | EncryptionConfig encryptionConfig?; 77 | string x509Token?; 78 | |}; 79 | 80 | # Represents the record for signature configurations. 81 | # 82 | # + keystore - The keystore to store the private key 83 | # + privateKeyAlias - The alias of the private key 84 | # + privateKeyPassword - The password of the private key 85 | # + signatureAlgorithm - The algorithm to sign the SOAP envelope 86 | # + canonicalizationAlgorithm - The algorithm to canonicalize the SOAP envelope 87 | # + digestAlgorithm - The algorithm to digest the SOAP envelope 88 | public type SignatureConfig record {| 89 | crypto:KeyStore keystore; 90 | string privateKeyAlias; 91 | string privateKeyPassword; 92 | SignatureAlgorithm signatureAlgorithm?; 93 | CanonicalizationAlgorithm canonicalizationAlgorithm = C14N_EXCL_OMIT_COMMENTS; 94 | DigestAlgorithm digestAlgorithm = SHA1; 95 | |}; 96 | 97 | # Represents the record for encryption configurations. 98 | # 99 | # + keystore - The keystore to store the public key 100 | # + publicKeyAlias - The alias of the public key 101 | # + encryptionAlgorithm - The algorithm to encrypt the SOAP envelope 102 | public type EncryptionConfig record {| 103 | crypto:KeyStore keystore; 104 | string publicKeyAlias; 105 | EncryptionAlgorithm encryptionAlgorithm?; 106 | |}; 107 | 108 | # Represents the record for Transport Binding policy. 109 | # + protocol - Protocol of the endpoint 110 | public type TransportBindingConfig "TransportBinding"; 111 | 112 | # Represents the record to send SOAP envelopes with no security policy. 113 | public type NoPolicy "NoPolicy"; 114 | 115 | # Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 116 | # 117 | # + decryptKeystore - The keystore to decrypt the SOAP envelope 118 | # + signatureKeystore - The keystore to verify the signature of the SOAP envelope 119 | public type InboundConfig record {| 120 | crypto:KeyStore decryptKeystore?; 121 | crypto:KeyStore signatureKeystore?; 122 | |}; 123 | -------------------------------------------------------------------------------- /ballerina/modules/soap12/tests/http_soap_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/crypto; 18 | import ballerina/http; 19 | import ballerina/mime; 20 | import ballerina/soap; 21 | 22 | const crypto:KeyStore serverKeyStore = { 23 | path: X509_KEY_STORE_PATH, 24 | password: KEY_PASSWORD 25 | }; 26 | crypto:PrivateKey serverPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(serverKeyStore, KEY_ALIAS, 27 | KEY_PASSWORD); 28 | crypto:PublicKey serverPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(serverKeyStore, KEY_ALIAS); 29 | 30 | service / on new http:Listener(9090) { 31 | 32 | resource function post .() returns xml|error { 33 | return xml `5`; 34 | } 35 | 36 | resource function post getPayload(http:Request request) returns xml|error { 37 | return check (check request.getBodyParts())[0].getXml(); 38 | } 39 | 40 | resource function post getMimePayload(http:Request request) returns http:Response|error { 41 | http:Response response = new; 42 | mime:Entity[] mtomMessage = []; 43 | mime:Entity envelope = new; 44 | check envelope.setContentType("application/xop+xml"); 45 | envelope.setContentId(""); 46 | envelope.setBody(check (check request.getBodyParts())[0].getXml()); 47 | mtomMessage.push(envelope); 48 | response.setBodyParts(mtomMessage); 49 | response.setPayload(mtomMessage); 50 | return response; 51 | } 52 | 53 | resource function post getActionPayload(http:Request request) returns xml|error { 54 | string[] headers = check request.getHeaders(mime:CONTENT_TYPE); 55 | mime:MediaType mediaHeader = check mime:getMediaType(headers[0]); 56 | map actionMap = mediaHeader.parameters; 57 | string action = actionMap.get("action"); 58 | if action == "http://tempuri.org/Add" { 59 | return check request.getXmlPayload(); 60 | } 61 | return xml `soap:ClientSystem.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://tempuri.org/invalid_action. 62 | at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest() 63 | at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message) 64 | at System.Web.Services.Protocols.SoapServerProtocol.Initialize() 65 | at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response) 66 | at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)`; 67 | } 68 | 69 | resource function post getErrorPayload(http:Request request) returns xml|http:InternalServerError { 70 | return { 71 | body: "Error occurred in the server" 72 | }; 73 | } 74 | 75 | resource function post getSamePayload(http:Request request) returns xml|error { 76 | return check request.getXmlPayload(); 77 | } 78 | 79 | resource function post getSecuredPayload(http:Request request) returns xml|error { 80 | xml payload = check request.getXmlPayload(); 81 | xml applyInboundConfig = check soap:applyInboundConfig( 82 | { 83 | decryptKeystore: { 84 | path: KEY_STORE_PATH_2, 85 | password: PASSWORD 86 | }, 87 | signatureKeystore: { 88 | path: KEY_STORE_PATH_2, 89 | password: PASSWORD 90 | } 91 | }, 92 | payload 93 | ); 94 | return applyInboundConfig; 95 | } 96 | 97 | resource function post getSecuredMimePayload(http:Request request) returns http:Response|error { 98 | xml payload = check (check request.getBodyParts())[0].getXml(); 99 | xml applyInboundConfig = check soap:applyInboundConfig( 100 | { 101 | decryptKeystore: { 102 | path: KEY_STORE_PATH_2, 103 | password: PASSWORD 104 | }, 105 | signatureKeystore: { 106 | path: KEY_STORE_PATH_2, 107 | password: PASSWORD 108 | } 109 | }, 110 | payload 111 | ); 112 | http:Response response = new; 113 | mime:Entity[] mtomMessage = []; 114 | mime:Entity envelope = new; 115 | check envelope.setContentType("application/xop+xml"); 116 | envelope.setContentId(""); 117 | envelope.setBody(applyInboundConfig); 118 | mtomMessage.push(envelope); 119 | response.setBodyParts(mtomMessage); 120 | response.setPayload(mtomMessage); 121 | return response; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ballerina/modules/soap12/soap12.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import soap; 18 | 19 | import ballerina/http; 20 | import ballerina/mime; 21 | import ballerina/jballerina.java; 22 | import soap.wssec; 23 | 24 | # Object for the basic SOAP 1.2 client endpoint. 25 | public isolated client class Client { 26 | private final http:Client soapClient; 27 | private final readonly & soap:OutboundSecurityConfig|soap:OutboundSecurityConfig[] outboundSecurity; 28 | private final readonly & soap:InboundSecurityConfig inboundSecurity; 29 | 30 | # Gets invoked during object initialization. 31 | # 32 | # + url - URL endpoint 33 | # + config - Configurations for SOAP client 34 | # + return - `error` in case of errors or `()` otherwise 35 | public isolated function init(string url, *soap:ClientConfig config) returns Error? { 36 | do { 37 | check soap:validateTransportBindingPolicy(config); 38 | self.soapClient = check new (url, config.httpConfig); 39 | readonly & soap:ClientConfig readonlyConfig = soap:getReadOnlyClientConfig(config); 40 | self.outboundSecurity = readonlyConfig.outboundSecurity; 41 | self.inboundSecurity = readonlyConfig.inboundSecurity; 42 | } on fail var err { 43 | return error Error(SOAP_CLIENT_ERROR, err); 44 | } 45 | } 46 | 47 | # Sends SOAP request and expects a response. 48 | # ```ballerina 49 | # xml response = check soapClient->sendReceive(body); 50 | # -- OR -- 51 | # mime:Entity[] response = check soapClient->sendReceive(body); 52 | # ``` 53 | # 54 | # + body - SOAP request body as an `XML` or `mime:Entity[]` to work with SOAP attachments 55 | # + action - SOAP action as a `string` 56 | # + headers - SOAP headers as a `map` 57 | # + path - The resource path 58 | # + T - Default parameter use to infer the user specified type (`xml` or `mime:Entity[]`) 59 | # + return - If successful, returns the response. Else, returns an error 60 | remote isolated function sendReceive(xml|mime:Entity[] body, string? action = (), map headers = {}, 61 | string path = "", typedesc T = <>) 62 | returns T|Error = @java:Method { 63 | 'class: "io.ballerina.lib.soap.Soap", 64 | name: "sendReceive12" 65 | } external; 66 | 67 | isolated function generateResponse(xml|mime:Entity[] body, string? action = (), 68 | map headers = {}, string path = "") 69 | returns xml|mime:Entity[]|Error { 70 | do { 71 | xml securedBody; 72 | xml mimeEntity = body is xml ? body : check body[0].getXml(); 73 | lock { 74 | xml envelope = body is xml ? body.clone() : mimeEntity.clone(); 75 | securedBody = check soap:applySecurityPolicies(self.outboundSecurity.clone(), envelope.clone()); 76 | } 77 | xml|mime:Entity[] response; 78 | if body is mime:Entity[] { 79 | body[0].setXml(securedBody); 80 | response = check soap:sendReceive(body, self.soapClient, action, headers, path); 81 | } else { 82 | response = check soap:sendReceive(securedBody, self.soapClient, action, headers, path); 83 | } 84 | lock { 85 | wssec:InboundConfig? inboundSecurity = self.inboundSecurity.clone(); 86 | do { 87 | if inboundSecurity is wssec:InboundConfig && inboundSecurity != {} { 88 | if response is xml { 89 | return check soap:applyInboundConfig(inboundSecurity.clone(), response.clone()); 90 | } else { 91 | return check soap:applyInboundConfig(inboundSecurity.clone(), 92 | check response[0].getXml().clone()); 93 | } 94 | } 95 | } on fail error soapError { 96 | return error Error(INVALID_OUTBOUND_SECURITY_ERROR, soapError); 97 | } 98 | return response; 99 | } 100 | } on fail error soapError { 101 | return error Error(SOAP_ERROR, soapError); 102 | } 103 | } 104 | 105 | # Fires and forgets requests. Sends the request without the possibility of any response from the 106 | # service (even an error). 107 | # ```ballerina 108 | # check soapClient->sendOnly(body); 109 | # ``` 110 | # 111 | # + body - SOAP request body as an `XML` or `mime:Entity[]` to work with SOAP attachments 112 | # + action - SOAP action as a `string` 113 | # + headers - SOAP headers as a `map` 114 | # + path - The resource path 115 | # + return - If successful, returns `nil`. Else, returns an error 116 | remote isolated function sendOnly(xml|mime:Entity[] body, string? action = (), 117 | map headers = {}, string path = "") returns Error? { 118 | do { 119 | xml securedBody; 120 | xml mimeEntity = body is xml ? body : check body[0].getXml(); 121 | lock { 122 | xml envelope = body is xml ? body.clone() : mimeEntity.clone(); 123 | securedBody = check soap:applySecurityPolicies(self.outboundSecurity.clone(), envelope.clone()); 124 | } 125 | return check soap:sendOnly(securedBody, self.soapClient, action, headers, path); 126 | } on fail error soapError { 127 | return error Error(SOAP_ERROR, soapError); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ballerina/modules/soap11/soap11.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import soap; 18 | 19 | import ballerina/http; 20 | import ballerina/mime; 21 | import ballerina/jballerina.java; 22 | import soap.wssec; 23 | 24 | # Object for the basic SOAP 1.1 client endpoint. 25 | public isolated client class Client { 26 | private final http:Client soapClient; 27 | private final readonly & soap:OutboundSecurityConfig|soap:OutboundSecurityConfig[] outboundSecurity; 28 | private final readonly & soap:InboundSecurityConfig inboundSecurity; 29 | 30 | # Gets invoked during object initialization. 31 | # 32 | # + url - URL endpoint 33 | # + config - Configurations for SOAP client 34 | # + return - `error` in case of errors or `()` otherwise 35 | public isolated function init(string url, *soap:ClientConfig config) returns Error? { 36 | do { 37 | check soap:validateTransportBindingPolicy(config); 38 | self.soapClient = check new (url, config.httpConfig); 39 | readonly & soap:ClientConfig readonlyConfig = soap:getReadOnlyClientConfig(config); 40 | self.inboundSecurity = readonlyConfig.inboundSecurity; 41 | self.outboundSecurity = readonlyConfig.outboundSecurity; 42 | } on fail var err { 43 | return error Error(SOAP_CLIENT_ERROR, err); 44 | } 45 | } 46 | 47 | # Sends SOAP request and expects a response. 48 | # ```ballerina 49 | # xml response = check soapClient->sendReceive(body, action); 50 | # -- OR -- 51 | # mime:Entity[] response = check soapClient->sendReceive(body, action); 52 | # ``` 53 | # 54 | # + body - SOAP request body as an `XML` or `mime:Entity[]` to work with SOAP attachments 55 | # + action - SOAP action as a `string` 56 | # + headers - SOAP headers as a `map` 57 | # + path - The resource path 58 | # + T - Default parameter use to infer the user specified type (`xml` or `mime:Entity[]`) 59 | # + return - If successful, returns the response. Else, returns an error 60 | remote isolated function sendReceive(xml|mime:Entity[] body, string action, map headers = {}, 61 | string path = "", typedesc T = <>) 62 | returns T|Error = @java:Method { 63 | 'class: "io.ballerina.lib.soap.Soap", 64 | name: "sendReceive11" 65 | } external; 66 | 67 | isolated function generateResponse(xml|mime:Entity[] body, string action, 68 | map headers = {}, string path = "") 69 | returns xml|mime:Entity[]|Error { 70 | do { 71 | xml securedBody; 72 | xml mimeEntity = body is xml ? body : check body[0].getXml(); 73 | lock { 74 | xml envelope = body is xml ? body.clone() : mimeEntity.clone(); 75 | securedBody = check soap:applySecurityPolicies(self.outboundSecurity.clone(), envelope.clone(), false); 76 | } 77 | xml|mime:Entity[] response; 78 | if body is mime:Entity[] { 79 | body[0].setXml(securedBody); 80 | response = check soap:sendReceive(body, self.soapClient, action, headers, path, false); 81 | } else { 82 | response = check soap:sendReceive(securedBody, self.soapClient, action, headers, path, false); 83 | } 84 | lock { 85 | wssec:InboundConfig? inboundSecurity = self.inboundSecurity.clone(); 86 | do { 87 | if inboundSecurity is wssec:InboundConfig && inboundSecurity != {} { 88 | if response is xml { 89 | return check soap:applyInboundConfig(inboundSecurity.clone(), response.clone(), false); 90 | } else { 91 | return check soap:applyInboundConfig(inboundSecurity.clone(), 92 | check response[0].getXml().clone(), false); 93 | } 94 | } 95 | } on fail error soapError { 96 | return error Error(INVALID_OUTBOUND_SECURITY_ERROR, soapError); 97 | } 98 | return response; 99 | } 100 | } on fail error soapError { 101 | return error Error(SOAP_ERROR, soapError); 102 | } 103 | } 104 | 105 | # Fires and forgets requests. Sends the request without the possibility of any response from the 106 | # service (even an error). 107 | # ```ballerina 108 | # check soapClient->sendOnly(body, action); 109 | # ``` 110 | # 111 | # + body - SOAP request body as an `XML` or `mime:Entity[]` to work with SOAP attachments 112 | # + action - SOAP action as a `string` 113 | # + headers - SOAP headers as a `map` 114 | # + path - The resource path 115 | # + return - If successful, returns `nil`. Else, returns an error 116 | remote isolated function sendOnly(xml|mime:Entity[] body, string action, 117 | map headers = {}, string path = "") returns Error? { 118 | do { 119 | xml securedBody; 120 | xml mimeEntity = body is xml ? body : check body[0].getXml(); 121 | lock { 122 | xml envelope = body is xml ? body.clone() : mimeEntity.clone(); 123 | securedBody = check soap:applySecurityPolicies(self.outboundSecurity.clone(), envelope.clone(), false); 124 | } 125 | return check soap:sendOnly(securedBody, self.soapClient, action, headers, path, false); 126 | } on fail error soapError { 127 | return error Error(SOAP_ERROR, soapError); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /native/src/main/java/org/wssec/WsSecurityUtils.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | package org.wssec; 18 | 19 | import io.ballerina.runtime.api.creators.ErrorCreator; 20 | import io.ballerina.runtime.api.utils.StringUtils; 21 | import org.apache.wss4j.common.WSEncryptionPart; 22 | import org.apache.wss4j.dom.handler.RequestData; 23 | import org.apache.wss4j.dom.message.WSSecSignature; 24 | import org.apache.wss4j.dom.message.WSSecUsernameToken; 25 | import org.apache.wss4j.dom.util.WSSecurityUtil; 26 | import org.w3c.dom.Document; 27 | import org.w3c.dom.Element; 28 | import org.w3c.dom.NodeList; 29 | 30 | import java.io.StringWriter; 31 | import java.util.ArrayList; 32 | import java.util.Base64; 33 | import java.util.List; 34 | 35 | import javax.xml.crypto.dsig.Reference; 36 | import javax.xml.parsers.DocumentBuilderFactory; 37 | import javax.xml.transform.Transformer; 38 | import javax.xml.transform.TransformerFactory; 39 | import javax.xml.transform.dom.DOMSource; 40 | import javax.xml.transform.stream.StreamResult; 41 | 42 | import static org.apache.wss4j.common.WSS4JConstants.ENC_NS; 43 | import static org.apache.wss4j.common.WSS4JConstants.KEYTRANSPORT_RSA15; 44 | import static org.apache.wss4j.common.WSS4JConstants.PASSWORD_DIGEST; 45 | import static org.apache.wss4j.common.WSS4JConstants.PASSWORD_TEXT; 46 | import static org.apache.wss4j.common.WSS4JConstants.SIG_NS; 47 | import static org.wssec.Constants.ALGORITHM; 48 | import static org.wssec.Constants.CIPHER_DATA_TAG; 49 | import static org.wssec.Constants.CIPHER_VALUE_TAG; 50 | import static org.wssec.Constants.DERIVED_KEY_DIGEST; 51 | import static org.wssec.Constants.DIGEST; 52 | import static org.wssec.Constants.EMPTY_XML_DOCUMENT_ERROR; 53 | import static org.wssec.Constants.ENCRYPTED_KEY_TAG; 54 | import static org.wssec.Constants.ENCRYPTION_METHOD_TAG; 55 | import static org.wssec.Constants.KEY_INFO_TAG; 56 | import static org.wssec.Constants.NAMESPACE_URI_ENC; 57 | import static org.wssec.Constants.SIGNATURE_METHOD_TAG; 58 | import static org.wssec.Constants.SIGNATURE_VALUE_TAG; 59 | import static org.wssec.Constants.XML_DS_NS; 60 | import static org.wssec.Constants.XML_ENC_NS; 61 | 62 | public final class WsSecurityUtils { 63 | 64 | private WsSecurityUtils() {} 65 | 66 | public static void buildSignature(RequestData reqData, WSSecSignature sign) throws Exception { 67 | List parts = new ArrayList<>(1); 68 | Document doc = reqData.getSecHeader().getSecurityHeaderElement().getOwnerDocument(); 69 | parts.add(WSSecurityUtil.getDefaultEncryptionPart(doc)); 70 | List referenceList = sign.addReferencesToSign(parts); 71 | sign.computeSignature(referenceList); 72 | reqData.getSignatureValues().add(sign.getSignatureValue()); 73 | } 74 | 75 | public static void setSignatureValue(Document doc, byte[] signature, String algorithm) { 76 | doc.getElementsByTagName(SIGNATURE_METHOD_TAG) 77 | .item(0).getAttributes().item(0).setNodeValue(algorithm); 78 | NodeList digestValueList = doc.getElementsByTagName(SIGNATURE_VALUE_TAG); 79 | digestValueList.item(0).getFirstChild().setNodeValue(Base64.getEncoder().encodeToString(signature)); 80 | } 81 | 82 | public static byte[] getSignatureValue(Document doc) { 83 | String signature = doc.getElementsByTagName(SIGNATURE_VALUE_TAG).item(0).getFirstChild().getNodeValue(); 84 | return Base64.getDecoder().decode(signature); 85 | } 86 | 87 | public static void setEncryptedData(Document doc, byte[] encryptedData, String algorithm) { 88 | Element cipherDataElement = (Element) doc 89 | .getElementsByTagNameNS(NAMESPACE_URI_ENC, CIPHER_VALUE_TAG).item(0); 90 | cipherDataElement.getFirstChild().setNodeValue(Base64.getEncoder().encodeToString(encryptedData)); 91 | doc.getElementsByTagName(ENCRYPTION_METHOD_TAG).item(0).getAttributes().item(0) 92 | .setNodeValue(algorithm); 93 | } 94 | 95 | public static byte[] getEncryptedData(Document document) { 96 | String encryptedText = document 97 | .getElementsByTagNameNS(NAMESPACE_URI_ENC, CIPHER_VALUE_TAG).item(0) 98 | .getFirstChild().getNodeValue(); 99 | return Base64.getDecoder().decode(encryptedText); 100 | } 101 | 102 | public static Object getEncryptedKeyElement(byte[] encryptKey) throws Exception { 103 | Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 104 | Element encryptedKey = document.createElement(ENCRYPTED_KEY_TAG); 105 | encryptedKey.setAttribute(XML_ENC_NS, ENC_NS); 106 | Element encryptionMethod = document.createElement(ENCRYPTION_METHOD_TAG); 107 | encryptionMethod.setAttribute(ALGORITHM, KEYTRANSPORT_RSA15); 108 | encryptedKey.appendChild(encryptionMethod); 109 | Element keyInfo = document.createElement(KEY_INFO_TAG); 110 | keyInfo.setAttribute(XML_DS_NS, SIG_NS); 111 | encryptedKey.appendChild(keyInfo); 112 | Element cipherData = document.createElement(CIPHER_DATA_TAG); 113 | cipherData.appendChild(document.createTextNode(Base64.getEncoder().encodeToString(encryptKey))); 114 | encryptedKey.appendChild(cipherData); 115 | document.appendChild(encryptedKey); 116 | return convertDocumentToString(document); 117 | } 118 | 119 | public static void setUTChildElements(WSSecUsernameToken usernameToken, String passwordType, 120 | String username, String password) { 121 | if (DIGEST.equals(passwordType) || DERIVED_KEY_DIGEST.equals(passwordType)) { 122 | usernameToken.setPasswordType(PASSWORD_DIGEST); 123 | usernameToken.setUserInfo(username, password); 124 | usernameToken.addCreated(); 125 | usernameToken.addNonce(); 126 | } else { 127 | usernameToken.setPasswordType(PASSWORD_TEXT); 128 | usernameToken.setUserInfo(username, password); 129 | } 130 | } 131 | 132 | public static Object convertDocumentToString(Document document) throws Exception { 133 | if (document == null) { 134 | return ErrorCreator.createError(StringUtils.fromString(EMPTY_XML_DOCUMENT_ERROR)); 135 | } 136 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); 137 | StringWriter writer = new StringWriter(); 138 | transformer.transform(new DOMSource(document), new StreamResult(writer)); 139 | return StringUtils.fromString(writer.toString()); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /ballerina/modules/soap12/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This module offers a set of APIs that facilitate the transmission of XML requests to a SOAP 1.2 backend. It excels in managing security policies within SOAP requests, ensuring the transmission of secured SOAP envelopes. Moreover, it possesses the capability to efficiently extract data from security-applied SOAP responses. 4 | 5 | SOAP module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message. 6 | 7 | ## Client 8 | 9 | The `Client` is used to connect to and interact with `SOAP` 1.2 endpoints. 10 | 11 | ### SOAP 1.2 Client 12 | 13 | ```ballerina 14 | import ballerina/soap.soap12; 15 | 16 | soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 17 | ``` 18 | 19 | ## APIs associated with SOAP 20 | 21 | - **Send & Receive**: Sends SOAP request and receives a response. 22 | - **Send Only**: Fires and forgets requests. Sends the request without the possibility of any response from the service. 23 | 24 | The SOAP 1.1 specification requires the inclusion of the `action` parameter as a mandatory component within its APIs. In contrast, SOAP 1.2 relaxes this requirement, making the action parameter optional. 25 | 26 | ### Example: Send & Receive 27 | 28 | ```ballerina 29 | import ballerina/soap.soap12; 30 | 31 | public function main() returns error? { 32 | soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 33 | 34 | xml envelope = xml ` 35 | 36 | 37 | 2 38 | 3 39 | 40 | 41 | `; 42 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 43 | } 44 | ``` 45 | 46 | ### Example: Send Only 47 | 48 | ```ballerina 49 | import ballerina/soap.soap12; 50 | 51 | public function main() returns error? { 52 | soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 53 | 54 | xml envelope = xml ` 55 | 56 | 57 | 2 58 | 3 59 | 60 | 61 | `; 62 | check soapClient->sendOnly(envelope, "http://tempuri.org/Add"); 63 | } 64 | ``` 65 | 66 | ## Security 67 | 68 | The SOAP client module introduces a robust framework for configuring security measures in SOAP communication. Security is a critical concern when exchanging data via web services, and this module offers comprehensive options to fortify SOAP requests and responses. 69 | 70 | There are two primary security configurations available for SOAP clients: 71 | 72 | - `outboundSecurity`: This configuration applies WS-Security policies to outgoing SOAP messages. It supports multiple security options, such as Username Token, Timestamp Token, X.509 Token, Symmetric Binding, Asymmetric Binding, and Transport Binding. These can be used individually or in combination to secure the message. 73 | 74 | - `inboundSecurity`: This configuration handles the security of incoming SOAP messages. It decrypts encrypted data and verifies the digital signature to confirm the authenticity of the message. 75 | 76 | 77 | ### Policies 78 | 79 | This library currently supports the following WS Security policies: 80 | 81 | - **Username Token**: Provides authentication through username and password credentials. 82 | - **Timestamp Token**: Enhances message integrity by incorporating timestamp information. 83 | - **X509 Token**: Allows the use of X.509 certificates for secure communication. 84 | - **Symmetric Binding**: Enables symmetric key-based security mechanisms. 85 | - **Asymmetric Binding**: Facilitates the use of asymmetric cryptography for enhanced security. 86 | 87 | These policies empower SOAP clients to enhance the security of their web service communications by selecting and implementing the appropriate security mechanisms to safeguard their SOAP envelopes. 88 | 89 | ### Security Policy Configuration Types 90 | 91 | #### Outbound Security Configurations 92 | 93 | - `TimestampTokenConfig`: Represents the record for Timestamp Token policy. 94 | - Fields: 95 | - `int` timeToLive : The time to get expired 96 | 97 | - `UsernameTokenConfig`: Represents the record for Username Token policy. 98 | - Fields: 99 | - `string` username : The name of the user 100 | - `string` password : The password of the user 101 | - `PasswordType` passwordType : The password type of the username token 102 | 103 | - `SymmetricBindingConfig`: Represents the record for Symmetric Binding policy. 104 | - Fields: 105 | - `crypto:PrivateKey` symmetricKey : The key to sign and encrypt the SOAP envelope 106 | - `crypto:PublicKey` servicePublicKey : The key to encrypt the symmetric key 107 | - `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope 108 | - `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP envelope 109 | - `string` x509Token : The path or token of the X509 certificate 110 | 111 | - `AsymmetricBindingConfig`: Represents the record for Asymmetric Binding policy. 112 | - Fields: 113 | - `SignatureConfig` signatureConfig : Configuration for applying digital signatures 114 | - `EncryptionConfig` encryptionConfig : Configuration for applying encryption 115 | - `string` x509Token : The path or token of the X509 certificate 116 | 117 | #### Inbound Security Configurations 118 | 119 | - `InboundSecurityConfig`: Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 120 | - Fields: 121 | - `crypto:KeyStore` decryptKeystore - The keystore to decrypt the SOAP envelope 122 | - `crypto:KeyStore` signatureKeystore - The keystore to verify the signature of the SOAP envelope 123 | 124 | ### Apply Security Policies 125 | 126 | #### SOAP 1.2 Client with Asymmetric Binding and Outbound Security Configuration 127 | 128 | ```ballerina 129 | import ballerina/crypto; 130 | import ballerina/mime; 131 | import ballerina/soap; 132 | import ballerina/soap.soap12; 133 | 134 | public function main() returns error? { 135 | soap12:Client soapClient = check new ("http://www.secured-soap-endpoint.com", 136 | { 137 | outboundSecurity: { 138 | signatureConfig: { 139 | keystore: { 140 | path: KEY_STORE_PATH_2, 141 | password: PASSWORD 142 | }, 143 | privateKeyAlias: ALIAS, 144 | privateKeyPassword: PASSWORD, 145 | signatureAlgorithm: wssec:RSA_SHA1 146 | }, 147 | encryptionConfig: { 148 | keystore: { 149 | path: KEY_STORE_PATH_2, 150 | password: PASSWORD 151 | }, 152 | publicKeyAlias: ALIAS, 153 | encryptionAlgorithm: wssec:AES_128 154 | } 155 | }, 156 | inboundSecurity: { 157 | decryptKeystore: { 158 | path: KEY_STORE_PATH_2, 159 | password: PASSWORD 160 | }, 161 | signatureKeystore: { 162 | path: KEY_STORE_PATH_2, 163 | password: PASSWORD 164 | } 165 | } 166 | }); 167 | } 168 | ``` 169 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/tests/test_utils.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/test; 18 | import ballerina/crypto; 19 | 20 | const USERNAME = "username"; 21 | const PASSWORD = "password"; 22 | 23 | const KEY_ALIAS = "wss40"; 24 | const KEY_PASSWORD = "security"; 25 | 26 | const SOAP_ENVELOPE_PATH = "modules/wssec/tests/resources/xml/soap_envelope.xml"; 27 | const PUBLIC_KEY_PATH = "modules/wssec/tests/resources/public_key.cer"; 28 | const PRIVATE_KEY_PATH = "modules/wssec/tests/resources/private_key.pem"; 29 | const KEY_STORE_PATH = "modules/wssec/tests/resources/wss40.p12"; 30 | const X509_PUBLIC_CERT_PATH = "modules/wssec/tests/resources/x509_certificate.crt"; 31 | const X509_PUBLIC_CERT_PATH_2 = "modules/wssec/tests/resources/x509_certificate_2.crt"; 32 | const X509_KEY_STORE_PATH = "modules/wssec/tests/resources/x509_certificate.p12"; 33 | const X509_KEY_STORE_PATH_2 = "modules/wssec/tests/resources/x509_certificate_2.p12"; 34 | 35 | const KEY_STORE_PATH_2 = "modules/wssec/tests/resources/keystore.jks"; 36 | const ALIAS = "mykey"; 37 | 38 | const crypto:KeyStore clientKeyStore = { 39 | path: X509_KEY_STORE_PATH_2, 40 | password: KEY_PASSWORD 41 | }; 42 | crypto:PrivateKey clientPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(clientKeyStore, KEY_ALIAS, 43 | KEY_PASSWORD); 44 | crypto:PublicKey clientPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(clientKeyStore, KEY_ALIAS); 45 | 46 | const crypto:KeyStore serverKeyStore = { 47 | path: X509_KEY_STORE_PATH, 48 | password: KEY_PASSWORD 49 | }; 50 | crypto:PrivateKey serverPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(serverKeyStore, KEY_ALIAS, 51 | KEY_PASSWORD); 52 | crypto:PublicKey serverPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(serverKeyStore, KEY_ALIAS); 53 | 54 | crypto:KeyStore keyStore = { 55 | path: KEY_STORE_PATH, 56 | password: KEY_PASSWORD 57 | }; 58 | crypto:PrivateKey symmetricKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, KEY_ALIAS, KEY_PASSWORD); 59 | crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, KEY_ALIAS); 60 | 61 | function assertTimestampToken(string envelopeString) { 62 | string:RegExp ts_token = re ``; 63 | string:RegExp created = re `.*`; 64 | string:RegExp expires = re `.*`; 65 | test:assertTrue(envelopeString.includesMatch(ts_token)); 66 | test:assertTrue(envelopeString.includesMatch(created)); 67 | test:assertTrue(envelopeString.includesMatch(expires)); 68 | } 69 | 70 | function assertUsernameToken(string envelopeString, PasswordType passwordType) { 71 | string:RegExp usernameTokenTag = re `.*`; 72 | string:RegExp usernameTag = re `${USERNAME}`; 73 | test:assertTrue(envelopeString.includesMatch(usernameTokenTag)); 74 | test:assertTrue(envelopeString.includesMatch(usernameTag)); 75 | match passwordType { 76 | TEXT => { 77 | string:RegExp passwordTag = re `${PASSWORD}`; 78 | test:assertTrue(envelopeString.includesMatch(passwordTag)); 79 | } 80 | DIGEST => { 81 | string:RegExp passwordTag = re `.*`; 82 | string:RegExp nonce = re `.*`; 83 | string:RegExp created = re `.*`; 84 | test:assertTrue(envelopeString.includesMatch(passwordTag)); 85 | test:assertTrue(envelopeString.includesMatch(nonce)); 86 | test:assertTrue(envelopeString.includesMatch(created)); 87 | } 88 | _ => { 89 | string:RegExp salt = re `.*`; 90 | string:RegExp iteration = re `.*`; 91 | test:assertTrue(envelopeString.includesMatch(salt)); 92 | test:assertTrue(envelopeString.includesMatch(iteration)); 93 | } 94 | } 95 | } 96 | 97 | function assertSignatureWithX509(string securedEnvelope) { 98 | string:RegExp keyIdentifier = re `.*`; 99 | test:assertTrue(securedEnvelope.includesMatch(keyIdentifier)); 100 | assertSignatureWithoutX509(securedEnvelope); 101 | } 102 | 103 | function assertSignatureWithoutX509(string securedEnvelope) { 104 | string:RegExp signature = re ``; 105 | string:RegExp signatureInfo = re ``; 106 | string:RegExp canonicalizationMethod = re ``; 107 | string:RegExp signatureMethod = re ``; 108 | string:RegExp transformMethod = re ``; 109 | string:RegExp digestMethod = re ``; 110 | string:RegExp digestValue = re `ds:DigestValue>`; 111 | string:RegExp signatureValue = re ``; 112 | 113 | test:assertTrue(securedEnvelope.includesMatch(signature)); 114 | test:assertTrue(securedEnvelope.includesMatch(signatureInfo)); 115 | test:assertTrue(securedEnvelope.includesMatch(canonicalizationMethod)); 116 | test:assertTrue(securedEnvelope.includesMatch(signatureMethod)); 117 | test:assertTrue(securedEnvelope.includesMatch(transformMethod)); 118 | test:assertTrue(securedEnvelope.includesMatch(digestMethod)); 119 | test:assertTrue(securedEnvelope.includesMatch(digestValue)); 120 | test:assertTrue(securedEnvelope.includesMatch(signatureValue)); 121 | } 122 | 123 | function assertEncryptedSymmetricKey(string securedEnvelope) { 124 | string:RegExp encryptedKey = re ``; 125 | string:RegExp encryptionMethod = re ``; 126 | string:RegExp cipherData = re ``; 127 | 128 | test:assertTrue(securedEnvelope.includesMatch(encryptedKey)); 129 | test:assertTrue(securedEnvelope.includesMatch(encryptionMethod)); 130 | test:assertTrue(securedEnvelope.includesMatch(cipherData)); 131 | } 132 | 133 | function assertEncryptedPart(string securedEnvelope) { 134 | string:RegExp encryptedData = re ``; 136 | string:RegExp cipherData = re ``; 137 | string:RegExp cipherValue = re ``; 138 | 139 | test:assertTrue(securedEnvelope.includesMatch(encryptedData)); 140 | test:assertTrue(securedEnvelope.includesMatch(encMethod)); 141 | test:assertTrue(securedEnvelope.includesMatch(cipherData)); 142 | test:assertTrue(securedEnvelope.includesMatch(cipherValue)); 143 | } 144 | -------------------------------------------------------------------------------- /ballerina/modules/soap11/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This module offers a set of APIs that facilitate the transmission of XML requests to a SOAP 1.1 backend. It excels in managing security policies within SOAP requests, ensuring the transmission of secured SOAP envelopes. Moreover, it possesses the capability to efficiently extract data from security-applied SOAP responses. 4 | 5 | SOAP module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message. 6 | 7 | ## Client 8 | 9 | The `Client` is used to connect to and interact with `SOAP` 1.1 endpoints. 10 | 11 | ### SOAP 1.1 Client 12 | 13 | ```ballerina 14 | import ballerina/soap.soap11; 15 | 16 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 17 | ``` 18 | 19 | ## APIs associated with SOAP 20 | 21 | - **Send & Receive**: Sends SOAP request and receives a response. 22 | - **Send Only**: Fires and forgets requests. Sends the request without the possibility of any response from the service. 23 | 24 | The SOAP 1.1 specification requires the inclusion of the `action` parameter as a mandatory component within its APIs. In contrast, SOAP 1.2 relaxes this requirement, making the action parameter optional. 25 | 26 | ### Example: Send & Receive 27 | 28 | ```ballerina 29 | import ballerina/soap.soap11; 30 | 31 | public function main() returns error? { 32 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 33 | 34 | xml envelope = xml ` 35 | 36 | 37 | 2 38 | 3 39 | 40 | 41 | `; 42 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 43 | } 44 | ``` 45 | 46 | ### Example: Send Only 47 | 48 | ```ballerina 49 | import ballerina/soap.soap11; 50 | 51 | public function main() returns error? { 52 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 53 | 54 | xml envelope = xml ` 55 | 56 | 57 | 2 58 | 3 59 | 60 | 61 | `; 62 | check soapClient->sendOnly(envelope, "http://tempuri.org/Add"); 63 | } 64 | ``` 65 | 66 | ## Security 67 | 68 | The SOAP client module introduces a robust framework for configuring security measures in SOAP communication. Security is a critical concern when exchanging data via web services, and this module offers comprehensive options to fortify SOAP requests and responses. 69 | 70 | There are two primary security configurations available for SOAP clients: 71 | 72 | - `outboundSecurity`: This configuration applies WS-Security policies to outgoing SOAP messages. It supports multiple security options, such as Username Token, Timestamp Token, X.509 Token, Symmetric Binding, Asymmetric Binding, and Transport Binding. These can be used individually or in combination to secure the message. 73 | 74 | - `inboundSecurity`: This configuration handles the security of incoming SOAP messages. It decrypts encrypted data and verifies the digital signature to confirm the authenticity of the message. 75 | 76 | ### Policies 77 | 78 | This library currently supports the following WS Security policies: 79 | 80 | - **Username Token**: Provides authentication through username and password credentials. 81 | - **Timestamp Token**: Enhances message integrity by incorporating timestamp information. 82 | - **X509 Token**: Allows the use of X.509 certificates for secure communication. 83 | - **Symmetric Binding**: Enables symmetric key-based security mechanisms. 84 | - **Asymmetric Binding**: Facilitates the use of asymmetric cryptography for enhanced security. 85 | 86 | These policies empower SOAP clients to enhance the security of their web service communications by selecting and implementing the appropriate security mechanisms to safeguard their SOAP envelopes. 87 | 88 | ### Security Policy Configuration Types 89 | 90 | #### Outbound Security Configurations 91 | 92 | - `TimestampTokenConfig`: Represents the record for Timestamp Token policy. 93 | - Fields: 94 | - `int` timeToLive : The time to get expired 95 | 96 | - `UsernameTokenConfig`: Represents the record for Username Token policy. 97 | - Fields: 98 | - `string` username : The name of the user 99 | - `string` password : The password of the user 100 | - `PasswordType` passwordType : The password type of the username token 101 | 102 | - `SymmetricBindingConfig`: Represents the record for Symmetric Binding policy. 103 | - Fields: 104 | - `crypto:PrivateKey` symmetricKey : The key to sign and encrypt the SOAP envelope 105 | - `crypto:PublicKey` servicePublicKey : The key to encrypt the symmetric key 106 | - `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope 107 | - `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP envelope 108 | - `string` x509Token : The path or token of the X509 certificate 109 | 110 | - `AsymmetricBindingConfig`: Represents the record for Asymmetric Binding policy. 111 | - Fields: 112 | - `SignatureConfig` signatureConfig : Configuration for applying digital signatures 113 | - `EncryptionConfig` encryptionConfig : Configuration for applying encryption 114 | - `string` x509Token : The path or token of the X509 certificate 115 | 116 | #### Inbound Security Configurations 117 | 118 | - `InboundSecurityConfig`: Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 119 | - Fields: 120 | - `crypto:KeyStore` decryptKeystore - The keystore to decrypt the SOAP envelope 121 | - `crypto:KeyStore` signatureKeystore - The keystore to verify the signature of the SOAP envelope 122 | 123 | ### Apply Security Policies 124 | 125 | #### SOAP 1.1 Client with Asymmetric Binding and Outbound Security Configuration 126 | 127 | ```ballerina 128 | import ballerina/crypto; 129 | import ballerina/mime; 130 | import ballerina/soap; 131 | import ballerina/soap.soap11; 132 | 133 | public function main() returns error? { 134 | crypto:PrivateKey clientPrivateKey = ...// 135 | crypto:PublicKey clientPublicKey = ...// 136 | ​​crypto:PublicKey serverPublicKey = ...// 137 | 138 | soap11:Client soapClient = check new ("http://www.secured-soap-endpoint.com", 139 | { 140 | outboundSecurity: { 141 | signatureConfig: { 142 | keystore: { 143 | path: KEY_STORE_PATH_2, 144 | password: PASSWORD 145 | }, 146 | privateKeyAlias: ALIAS, 147 | privateKeyPassword: PASSWORD, 148 | signatureAlgorithm: wssec:RSA_SHA1 149 | }, 150 | encryptionConfig: { 151 | keystore: { 152 | path: KEY_STORE_PATH_2, 153 | password: PASSWORD 154 | }, 155 | publicKeyAlias: ALIAS, 156 | encryptionAlgorithm: wssec:AES_128 157 | } 158 | }, 159 | inboundSecurity: { 160 | decryptKeystore: { 161 | path: KEY_STORE_PATH_2, 162 | password: PASSWORD 163 | }, 164 | signatureKeystore: { 165 | path: KEY_STORE_PATH_2, 166 | password: PASSWORD 167 | } 168 | } 169 | }); 170 | } 171 | ``` 172 | -------------------------------------------------------------------------------- /ballerina/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.12.0" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "auth" 13 | version = "2.14.0" 14 | dependencies = [ 15 | {org = "ballerina", name = "crypto"}, 16 | {org = "ballerina", name = "jballerina.java"}, 17 | {org = "ballerina", name = "lang.array"}, 18 | {org = "ballerina", name = "lang.string"}, 19 | {org = "ballerina", name = "log"} 20 | ] 21 | 22 | [[package]] 23 | org = "ballerina" 24 | name = "cache" 25 | version = "3.10.0" 26 | dependencies = [ 27 | {org = "ballerina", name = "constraint"}, 28 | {org = "ballerina", name = "jballerina.java"}, 29 | {org = "ballerina", name = "task"}, 30 | {org = "ballerina", name = "time"} 31 | ] 32 | 33 | [[package]] 34 | org = "ballerina" 35 | name = "constraint" 36 | version = "1.7.0" 37 | dependencies = [ 38 | {org = "ballerina", name = "jballerina.java"} 39 | ] 40 | 41 | [[package]] 42 | org = "ballerina" 43 | name = "crypto" 44 | version = "2.9.0" 45 | dependencies = [ 46 | {org = "ballerina", name = "jballerina.java"}, 47 | {org = "ballerina", name = "time"} 48 | ] 49 | modules = [ 50 | {org = "ballerina", packageName = "crypto", moduleName = "crypto"} 51 | ] 52 | 53 | [[package]] 54 | org = "ballerina" 55 | name = "data.jsondata" 56 | version = "1.1.0" 57 | dependencies = [ 58 | {org = "ballerina", name = "jballerina.java"}, 59 | {org = "ballerina", name = "lang.object"} 60 | ] 61 | 62 | [[package]] 63 | org = "ballerina" 64 | name = "file" 65 | version = "1.12.0" 66 | dependencies = [ 67 | {org = "ballerina", name = "io"}, 68 | {org = "ballerina", name = "jballerina.java"}, 69 | {org = "ballerina", name = "os"}, 70 | {org = "ballerina", name = "time"} 71 | ] 72 | 73 | [[package]] 74 | org = "ballerina" 75 | name = "http" 76 | version = "2.14.0" 77 | dependencies = [ 78 | {org = "ballerina", name = "auth"}, 79 | {org = "ballerina", name = "cache"}, 80 | {org = "ballerina", name = "constraint"}, 81 | {org = "ballerina", name = "crypto"}, 82 | {org = "ballerina", name = "data.jsondata"}, 83 | {org = "ballerina", name = "file"}, 84 | {org = "ballerina", name = "io"}, 85 | {org = "ballerina", name = "jballerina.java"}, 86 | {org = "ballerina", name = "jwt"}, 87 | {org = "ballerina", name = "lang.array"}, 88 | {org = "ballerina", name = "lang.decimal"}, 89 | {org = "ballerina", name = "lang.int"}, 90 | {org = "ballerina", name = "lang.regexp"}, 91 | {org = "ballerina", name = "lang.runtime"}, 92 | {org = "ballerina", name = "lang.string"}, 93 | {org = "ballerina", name = "lang.value"}, 94 | {org = "ballerina", name = "log"}, 95 | {org = "ballerina", name = "mime"}, 96 | {org = "ballerina", name = "oauth2"}, 97 | {org = "ballerina", name = "observe"}, 98 | {org = "ballerina", name = "time"}, 99 | {org = "ballerina", name = "url"} 100 | ] 101 | modules = [ 102 | {org = "ballerina", packageName = "http", moduleName = "http"}, 103 | {org = "ballerina", packageName = "http", moduleName = "http.httpscerr"} 104 | ] 105 | 106 | [[package]] 107 | org = "ballerina" 108 | name = "io" 109 | version = "1.8.0" 110 | dependencies = [ 111 | {org = "ballerina", name = "jballerina.java"}, 112 | {org = "ballerina", name = "lang.value"} 113 | ] 114 | modules = [ 115 | {org = "ballerina", packageName = "io", moduleName = "io"} 116 | ] 117 | 118 | [[package]] 119 | org = "ballerina" 120 | name = "jballerina.java" 121 | version = "0.0.0" 122 | modules = [ 123 | {org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"} 124 | ] 125 | 126 | [[package]] 127 | org = "ballerina" 128 | name = "jwt" 129 | version = "2.15.0" 130 | dependencies = [ 131 | {org = "ballerina", name = "cache"}, 132 | {org = "ballerina", name = "crypto"}, 133 | {org = "ballerina", name = "io"}, 134 | {org = "ballerina", name = "jballerina.java"}, 135 | {org = "ballerina", name = "lang.int"}, 136 | {org = "ballerina", name = "lang.string"}, 137 | {org = "ballerina", name = "log"}, 138 | {org = "ballerina", name = "time"} 139 | ] 140 | 141 | [[package]] 142 | org = "ballerina" 143 | name = "lang.__internal" 144 | version = "0.0.0" 145 | dependencies = [ 146 | {org = "ballerina", name = "jballerina.java"}, 147 | {org = "ballerina", name = "lang.object"} 148 | ] 149 | 150 | [[package]] 151 | org = "ballerina" 152 | name = "lang.array" 153 | version = "0.0.0" 154 | dependencies = [ 155 | {org = "ballerina", name = "jballerina.java"}, 156 | {org = "ballerina", name = "lang.__internal"} 157 | ] 158 | 159 | [[package]] 160 | org = "ballerina" 161 | name = "lang.decimal" 162 | version = "0.0.0" 163 | dependencies = [ 164 | {org = "ballerina", name = "jballerina.java"} 165 | ] 166 | 167 | [[package]] 168 | org = "ballerina" 169 | name = "lang.error" 170 | version = "0.0.0" 171 | dependencies = [ 172 | {org = "ballerina", name = "jballerina.java"} 173 | ] 174 | 175 | [[package]] 176 | org = "ballerina" 177 | name = "lang.int" 178 | version = "0.0.0" 179 | dependencies = [ 180 | {org = "ballerina", name = "jballerina.java"}, 181 | {org = "ballerina", name = "lang.__internal"}, 182 | {org = "ballerina", name = "lang.object"} 183 | ] 184 | 185 | [[package]] 186 | org = "ballerina" 187 | name = "lang.object" 188 | version = "0.0.0" 189 | 190 | [[package]] 191 | org = "ballerina" 192 | name = "lang.regexp" 193 | version = "0.0.0" 194 | dependencies = [ 195 | {org = "ballerina", name = "jballerina.java"} 196 | ] 197 | modules = [ 198 | {org = "ballerina", packageName = "lang.regexp", moduleName = "lang.regexp"} 199 | ] 200 | 201 | [[package]] 202 | org = "ballerina" 203 | name = "lang.runtime" 204 | version = "0.0.0" 205 | dependencies = [ 206 | {org = "ballerina", name = "jballerina.java"} 207 | ] 208 | 209 | [[package]] 210 | org = "ballerina" 211 | name = "lang.string" 212 | version = "0.0.0" 213 | dependencies = [ 214 | {org = "ballerina", name = "jballerina.java"}, 215 | {org = "ballerina", name = "lang.regexp"} 216 | ] 217 | 218 | [[package]] 219 | org = "ballerina" 220 | name = "lang.value" 221 | version = "0.0.0" 222 | dependencies = [ 223 | {org = "ballerina", name = "jballerina.java"} 224 | ] 225 | 226 | [[package]] 227 | org = "ballerina" 228 | name = "log" 229 | version = "2.12.0" 230 | dependencies = [ 231 | {org = "ballerina", name = "io"}, 232 | {org = "ballerina", name = "jballerina.java"}, 233 | {org = "ballerina", name = "lang.value"}, 234 | {org = "ballerina", name = "observe"} 235 | ] 236 | 237 | [[package]] 238 | org = "ballerina" 239 | name = "mime" 240 | version = "2.12.0" 241 | dependencies = [ 242 | {org = "ballerina", name = "io"}, 243 | {org = "ballerina", name = "jballerina.java"}, 244 | {org = "ballerina", name = "lang.int"}, 245 | {org = "ballerina", name = "log"} 246 | ] 247 | modules = [ 248 | {org = "ballerina", packageName = "mime", moduleName = "mime"} 249 | ] 250 | 251 | [[package]] 252 | org = "ballerina" 253 | name = "oauth2" 254 | version = "2.14.0" 255 | dependencies = [ 256 | {org = "ballerina", name = "cache"}, 257 | {org = "ballerina", name = "crypto"}, 258 | {org = "ballerina", name = "jballerina.java"}, 259 | {org = "ballerina", name = "log"}, 260 | {org = "ballerina", name = "time"}, 261 | {org = "ballerina", name = "url"} 262 | ] 263 | 264 | [[package]] 265 | org = "ballerina" 266 | name = "observe" 267 | version = "1.5.0" 268 | dependencies = [ 269 | {org = "ballerina", name = "jballerina.java"} 270 | ] 271 | 272 | [[package]] 273 | org = "ballerina" 274 | name = "os" 275 | version = "1.10.0" 276 | dependencies = [ 277 | {org = "ballerina", name = "io"}, 278 | {org = "ballerina", name = "jballerina.java"} 279 | ] 280 | 281 | [[package]] 282 | org = "ballerina" 283 | name = "soap" 284 | version = "2.3.0" 285 | dependencies = [ 286 | {org = "ballerina", name = "crypto"}, 287 | {org = "ballerina", name = "http"}, 288 | {org = "ballerina", name = "io"}, 289 | {org = "ballerina", name = "jballerina.java"}, 290 | {org = "ballerina", name = "lang.regexp"}, 291 | {org = "ballerina", name = "mime"}, 292 | {org = "ballerina", name = "test"}, 293 | {org = "ballerinai", name = "observe"} 294 | ] 295 | modules = [ 296 | {org = "ballerina", packageName = "soap", moduleName = "soap"}, 297 | {org = "ballerina", packageName = "soap", moduleName = "soap.soap11"}, 298 | {org = "ballerina", packageName = "soap", moduleName = "soap.soap12"}, 299 | {org = "ballerina", packageName = "soap", moduleName = "soap.wssec"} 300 | ] 301 | 302 | [[package]] 303 | org = "ballerina" 304 | name = "task" 305 | version = "2.7.0" 306 | dependencies = [ 307 | {org = "ballerina", name = "jballerina.java"}, 308 | {org = "ballerina", name = "time"} 309 | ] 310 | 311 | [[package]] 312 | org = "ballerina" 313 | name = "test" 314 | version = "0.0.0" 315 | dependencies = [ 316 | {org = "ballerina", name = "jballerina.java"}, 317 | {org = "ballerina", name = "lang.array"}, 318 | {org = "ballerina", name = "lang.error"} 319 | ] 320 | modules = [ 321 | {org = "ballerina", packageName = "test", moduleName = "test"} 322 | ] 323 | 324 | [[package]] 325 | org = "ballerina" 326 | name = "time" 327 | version = "2.7.0" 328 | dependencies = [ 329 | {org = "ballerina", name = "jballerina.java"} 330 | ] 331 | 332 | [[package]] 333 | org = "ballerina" 334 | name = "url" 335 | version = "2.6.0" 336 | dependencies = [ 337 | {org = "ballerina", name = "jballerina.java"} 338 | ] 339 | 340 | [[package]] 341 | org = "ballerinai" 342 | name = "observe" 343 | version = "0.0.0" 344 | dependencies = [ 345 | {org = "ballerina", name = "jballerina.java"}, 346 | {org = "ballerina", name = "observe"} 347 | ] 348 | modules = [ 349 | {org = "ballerinai", packageName = "observe", moduleName = "observe"} 350 | ] 351 | 352 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command; 206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 207 | # shell script including quotes and variable substitutions, so put them in 208 | # double quotes to make sure that they get re-expanded; and 209 | # * put everything else in single quotes, so that it's not re-expanded. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /ballerina/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This module offers a set of APIs that facilitate the transmission of XML requests to a SOAP backend. It excels in managing security policies within SOAP requests, ensuring the transmission of secured SOAP envelopes. Moreover, it possesses the capability to efficiently extract data from security-applied SOAP responses. 4 | 5 | SOAP module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message. 6 | 7 | ## Client 8 | 9 | The `Client` is used to connect to and interact with `SOAP` endpoints. 10 | 11 | ### SOAP 1.1 Client 12 | 13 | ```ballerina 14 | import ballerina/soap.soap11; 15 | 16 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 17 | ``` 18 | 19 | ### SOAP 1.2 Client 20 | 21 | ```ballerina 22 | import ballerina/soap.soap12; 23 | 24 | soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 25 | ``` 26 | 27 | ## APIs associated with SOAP 28 | 29 | - **Send & Receive**: Sends SOAP request and receives a response. 30 | - **Send Only**: Fires and forgets requests. Sends the request without the possibility of any response from the service. 31 | 32 | The SOAP 1.1 specification requires the inclusion of the `action` parameter as a mandatory component within its APIs. In contrast, SOAP 1.2 relaxes this requirement, making the action parameter optional. 33 | 34 | ### Example: Send & Receive 35 | 36 | ```ballerina 37 | import ballerina/soap.soap11; 38 | 39 | public function main() returns error? { 40 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 41 | 42 | xml envelope = xml ` 43 | 44 | 45 | 2 46 | 3 47 | 48 | 49 | `; 50 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 51 | } 52 | ``` 53 | 54 | ### Example: Send Only 55 | 56 | ```ballerina 57 | import ballerina/soap.soap11; 58 | 59 | public function main() returns error? { 60 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 61 | 62 | xml envelope = xml ` 63 | 64 | 65 | 2 66 | 3 67 | 68 | 69 | `; 70 | check soapClient->sendOnly(envelope, "http://tempuri.org/Add"); 71 | } 72 | ``` 73 | 74 | ## Security 75 | 76 | The SOAP client module introduces a robust framework for configuring security measures in SOAP communication. Security is a critical concern when exchanging data via web services, and this module offers comprehensive options to fortify SOAP requests and responses. 77 | 78 | There are two primary security configurations available for SOAP clients: 79 | 80 | - `outboundSecurity`: This configuration applies WS-Security policies to outgoing SOAP messages. It supports multiple security options, such as Username Token, Timestamp Token, X.509 Token, Symmetric Binding, Asymmetric Binding, and Transport Binding. These can be used individually or in combination to secure the message. 81 | 82 | - `inboundSecurity`: This configuration handles the security of incoming SOAP messages. It decrypts encrypted data and verifies the digital signature to confirm the authenticity of the message. 83 | 84 | ### Policies 85 | 86 | This library currently supports the following WS Security policies: 87 | 88 | - **Username Token**: Provides authentication through username and password credentials. 89 | - **Timestamp Token**: Enhances message integrity by incorporating timestamp information. 90 | - **X509 Token**: Allows the use of X.509 certificates for secure communication. 91 | - **Symmetric Binding**: Enables symmetric key-based security mechanisms. 92 | - **Asymmetric Binding**: Facilitates the use of asymmetric cryptography for enhanced security. 93 | 94 | These policies empower SOAP clients to enhance the security of their web service communications by selecting and implementing the appropriate security mechanisms to safeguard their SOAP envelopes. 95 | 96 | ### Security Policy Configuration Types 97 | 98 | #### Outbound Security Configurations 99 | 100 | - `TimestampTokenConfig`: Represents the record for Timestamp Token policy. 101 | - Fields: 102 | - `int` timeToLive : The time to get expired 103 | 104 | - `UsernameTokenConfig`: Represents the record for Username Token policy. 105 | - Fields: 106 | - `string` username : The name of the user 107 | - `string` password : The password of the user 108 | - `PasswordType` passwordType : The password type of the username token 109 | 110 | - `SymmetricBindingConfig`: Represents the record for Symmetric Binding policy. 111 | - Fields: 112 | - `crypto:PrivateKey` symmetricKey : The key to sign and encrypt the SOAP envelope 113 | - `crypto:PublicKey` servicePublicKey : The key to encrypt the symmetric key 114 | - `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope 115 | - `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP envelope 116 | - `string` x509Token : The path or token of the X509 certificate 117 | 118 | - `AsymmetricBindingConfig`: Represents the record for Asymmetric Binding policy. 119 | - Fields: 120 | - `SignatureConfig` signatureConfig : Configuration for applying digital signatures 121 | - `EncryptionConfig` encryptionConfig : Configuration for applying encryption 122 | - `string` x509Token : The path or token of the X509 certificate 123 | 124 | #### Inbound Security Configurations 125 | 126 | - `InboundSecurityConfig`: Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 127 | - Fields: 128 | - `crypto:KeyStore` decryptKeystore - The keystore to decrypt the SOAP envelope 129 | - `crypto:KeyStore` signatureKeystore - The keystore to verify the signature of the SOAP envelope 130 | 131 | ### Apply Security Policies 132 | 133 | #### SOAP 1.1 Client: UsernameToken and TranportBinding Policy 134 | 135 | ```ballerina 136 | import ballerina/crypto; 137 | import ballerina/mime; 138 | import ballerina/soap; 139 | import ballerina/soap.soap11; 140 | 141 | public function main() returns error? { 142 | soap11:Client soapClient = check new ("https://www.secured-soap-endpoint.com", 143 | { 144 | outboundSecurity: [ 145 | { 146 | username: "username", 147 | password: "password", 148 | passwordType: soap:TEXT 149 | }, 150 | TRANSPORT_BINDING 151 | ] 152 | }); 153 | 154 | xml envelope = xml ` 155 | 156 | 157 | 2 158 | 3 159 | 160 | 161 | `; 162 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 163 | } 164 | ``` 165 | 166 | #### SOAP 1.2 Client with Asymmetric Binding and Outbound Security Configuration 167 | 168 | ```ballerina 169 | import ballerina/crypto; 170 | import ballerina/mime; 171 | import ballerina/soap; 172 | import ballerina/soap.soap12; 173 | 174 | public function main() returns error? { 175 | configurable crypto:PrivateKey clientPrivateKey = ?; 176 | configurable crypto:PublicKey clientPublicKey = ?; 177 | configurable ​crypto:PublicKey serverPublicKey = ?; 178 | 179 | soap12:Client soapClient = check new ("https://www.secured-soap-endpoint.com", 180 | { 181 | outboundSecurity: { 182 | signatureConfig: { 183 | keystore: { 184 | path: KEY_STORE_PATH, 185 | password: PASSWORD 186 | }, 187 | privateKeyAlias: ALIAS, 188 | privateKeyPassword: PASSWORD, 189 | canonicalizationAlgorithm: wssec:C14N_EXCL_OMIT_COMMENTS, 190 | digestAlgorithm: wssec:SHA1 191 | }, 192 | encryptionConfig: { 193 | keystore: { 194 | path: KEY_STORE_PATH_2, 195 | password: PASSWORD 196 | }, 197 | publicKeyAlias: ALIAS, 198 | encryptionAlgorithm: wssec:AES_128 199 | } 200 | }, 201 | inboundSecurity: { 202 | decryptKeystore: { 203 | path: KEY_STORE_PATH_2, 204 | password: PASSWORD 205 | }, 206 | signatureKeystore: { 207 | path: KEY_STORE_PATH, 208 | password: PASSWORD 209 | } 210 | } 211 | }); 212 | xml envelope = xml ` 213 | 214 | 215 | 2 216 | 3 217 | 218 | 219 | `; 220 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 221 | } 222 | ``` 223 | -------------------------------------------------------------------------------- /ballerina/soap_utils.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import soap.wssec; 18 | 19 | import ballerina/crypto; 20 | import ballerina/http; 21 | import ballerina/jballerina.java; 22 | import ballerina/lang.regexp; 23 | import ballerina/mime; 24 | import ballerina/test; 25 | 26 | public isolated function validateTransportBindingPolicy(ClientConfig config) returns Error? { 27 | if config.httpConfig.secureSocket is () { 28 | wssec:OutboundSecurityConfig|wssec:OutboundSecurityConfig[] securityPolicy = config.outboundSecurity; 29 | if securityPolicy is wssec:TransportBindingConfig { 30 | return error Error(INVALID_PROTOCOL_ERROR); 31 | } else if securityPolicy is wssec:OutboundSecurityConfig[] { 32 | foreach wssec:OutboundSecurityConfig policy in securityPolicy { 33 | if policy is wssec:TransportBindingConfig { 34 | return error Error(INVALID_PROTOCOL_ERROR); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | public isolated function getReadOnlyClientConfig(ClientConfig original) returns readonly & ClientConfig = @java:Method { 42 | 'class: "org.wssec.WsSecurity" 43 | } external; 44 | 45 | public isolated function applySecurityPolicies(wssec:OutboundSecurityConfig|wssec:OutboundSecurityConfig[] security, 46 | xml envelope, boolean soap12 = true) 47 | returns xml|crypto:Error|wssec:Error { 48 | if security is wssec:TimestampTokenConfig { 49 | return wssec:applyTimestampToken(envelope, security); 50 | } else if security is wssec:UsernameTokenConfig { 51 | return wssec:applyUsernameToken(envelope, security); 52 | } else if security is wssec:SymmetricBindingConfig { 53 | return wssec:applySymmetricBinding(envelope, soap12, security); 54 | } else if security is wssec:AsymmetricBindingConfig { 55 | return wssec:applyAsymmetricConfigurations(envelope, soap12, security); 56 | } else if security is wssec:OutboundSecurityConfig { 57 | return envelope; 58 | } else { 59 | xml securedEnvelope = envelope.clone(); 60 | foreach wssec:OutboundSecurityConfig policy in security { 61 | securedEnvelope = check applySecurityPolicies(policy, securedEnvelope, soap12); 62 | } 63 | return securedEnvelope; 64 | } 65 | } 66 | 67 | public isolated function applyInboundConfig(wssec:InboundConfig inboundSecurity, xml envelope, 68 | boolean soap12 = true) returns xml|Error { 69 | xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soap11; 70 | xmlns "http://www.w3.org/2003/05/soap-envelope" as soap12; 71 | xml soapEnvelope = envelope; 72 | do { 73 | crypto:KeyStore? encryptionAlgorithm = inboundSecurity.decryptKeystore; 74 | if encryptionAlgorithm is crypto:KeyStore { 75 | wssec:Document doc = check wssec:decryptEnvelope(envelope, inboundSecurity); 76 | soapEnvelope = check doc.getEnvelope(); 77 | } 78 | crypto:KeyStore? signatureAlgorithm = inboundSecurity.signatureKeystore; 79 | if signatureAlgorithm is crypto:KeyStore { 80 | boolean validity = check wssec:verifySignature(soapEnvelope, inboundSecurity); 81 | if !validity { 82 | return error Error("Signature verification failed"); 83 | } 84 | } 85 | return soapEnvelope; 86 | } on fail error soapError { 87 | return error Error("Outbound security configurations do not match with the SOAP response", soapError); 88 | } 89 | } 90 | 91 | public isolated function sendReceive(xml|mime:Entity[] body, http:Client httpClient, string? soapAction = (), 92 | map headers = {}, string path = "", boolean soap12 = true) 93 | returns xml|mime:Entity[]|Error { 94 | http:Request req = soap12 ? createSoap12HttpRequest(body, soapAction, headers) 95 | : createSoap11HttpRequest(body, soapAction, headers); 96 | do { 97 | http:Response response = check httpClient->post(path, req); 98 | return check createSoapResponse(response); 99 | } on fail var soapError { 100 | return error Error(SOAP_RESPONSE_ERROR, soapError); 101 | } 102 | } 103 | 104 | public isolated function sendOnly(xml|mime:Entity[] body, http:Client httpClient, string? soapAction = (), 105 | map headers = {}, string path = "", boolean soap12 = true) 106 | returns Error? { 107 | http:Request req = soap12 ? createSoap12HttpRequest(body, soapAction, headers) 108 | : createSoap11HttpRequest(body, soapAction, headers); 109 | http:Response|http:ClientError response = httpClient->post(path, req); 110 | if response is http:ClientError { 111 | return error Error(response.message()); 112 | } 113 | } 114 | 115 | isolated function createSoap11HttpRequest(xml|mime:Entity[] body, string soapAction, 116 | map headers = {}) returns http:Request { 117 | http:Request req = new; 118 | if body is xml { 119 | req.setXmlPayload(body); 120 | req.setHeader(mime:CONTENT_TYPE, mime:TEXT_XML); 121 | } else { 122 | req.setBodyParts(body); 123 | req.setHeader(mime:CONTENT_TYPE, mime:MULTIPART_MIXED); 124 | } 125 | req.addHeader(SOAP_ACTION, soapAction); 126 | foreach string key in headers.keys() { 127 | req.addHeader(key, headers[key].toBalString()); 128 | } 129 | return req; 130 | } 131 | 132 | isolated function createSoap12HttpRequest(xml|mime:Entity[] body, string? soapAction, 133 | map headers = {}) returns http:Request { 134 | http:Request req = new; 135 | if body is xml { 136 | req.setXmlPayload(body); 137 | } else { 138 | req.setBodyParts(body); 139 | } 140 | if soapAction is string { 141 | mime:MediaType|mime:InvalidContentTypeError mediaType; 142 | if body is xml { 143 | mediaType = mime:getMediaType(mime:APPLICATION_SOAP_XML); 144 | } else { 145 | mediaType = mime:getMediaType(mime:MULTIPART_MIXED); 146 | } 147 | if mediaType is mime:MediaType { 148 | mediaType.parameters = {"action": string `"${soapAction}"`}; 149 | req.setHeader(mime:CONTENT_TYPE, mediaType.toString()); 150 | } 151 | } else if body is xml { 152 | req.setHeader(mime:CONTENT_TYPE, mime:TEXT_XML); 153 | } else { 154 | req.setHeader(mime:CONTENT_TYPE, mime:MULTIPART_MIXED); 155 | } 156 | foreach string key in headers.keys() { 157 | req.addHeader(key, headers[key].toBalString()); 158 | } 159 | return req; 160 | } 161 | 162 | isolated function createSoapResponse(http:Response response) returns xml|mime:Entity[]|error { 163 | mime:Entity[]|http:ClientError payload = response.getBodyParts(); 164 | if payload !is mime:Entity[] { 165 | xml|error responsePayload = response.getXmlPayload(); 166 | if responsePayload is xml { 167 | return responsePayload; 168 | } 169 | return error(response.reasonPhrase); 170 | } 171 | return payload; 172 | } 173 | 174 | public function assertUsernameToken(string envelopeString, string username, string password, 175 | wssec:PasswordType passwordType, string body) returns error? { 176 | string:RegExp bodyData = check regexp:fromString(body); 177 | test:assertTrue(envelopeString.includesMatch(bodyData)); 178 | string:RegExp usernameTokenTag = re `.*`; 179 | string:RegExp usernameTag = re `${username}`; 180 | test:assertTrue(envelopeString.includesMatch(usernameTokenTag)); 181 | test:assertTrue(envelopeString.includesMatch(usernameTag)); 182 | string:RegExp passwordTag = re `${password}`; 183 | test:assertTrue(envelopeString.includesMatch(passwordTag)); 184 | } 185 | 186 | public function assertSymmetricBinding(string envelopeString, string body) returns error? { 187 | string:RegExp bodyData = check regexp:fromString(body); 188 | test:assertTrue(envelopeString.includesMatch(bodyData)); 189 | assertSignatureWithoutX509(envelopeString); 190 | } 191 | 192 | public function assertSignatureWithoutX509(string securedEnvelope) { 193 | string:RegExp signature = re `.*`; 194 | string:RegExp signatureInfo = re `.*`; 195 | string:RegExp canonicalizationMethod = re ``; 196 | string:RegExp signatureMethod = re ``; 197 | string:RegExp transformMethod = re ``; 198 | string:RegExp digestMethod = re ``; 199 | string:RegExp signatureValue = re `.*`; 200 | 201 | test:assertTrue(securedEnvelope.includesMatch(signature)); 202 | test:assertTrue(securedEnvelope.includesMatch(signatureInfo)); 203 | test:assertTrue(securedEnvelope.includesMatch(canonicalizationMethod)); 204 | test:assertTrue(securedEnvelope.includesMatch(signatureMethod)); 205 | test:assertTrue(securedEnvelope.includesMatch(transformMethod)); 206 | test:assertTrue(securedEnvelope.includesMatch(digestMethod)); 207 | test:assertTrue(securedEnvelope.includesMatch(signatureValue)); 208 | } 209 | -------------------------------------------------------------------------------- /ballerina/modules/wssec/ws_security_methods.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/crypto; 18 | import ballerina/lang.regexp; 19 | 20 | xmlns "http://schemas.xmlsoap.org/soap/envelope/" as soap11; 21 | xmlns "http://www.w3.org/2003/05/soap-envelope" as soap12; 22 | xmlns "http://www.w3.org/2000/09/xmldsig#" as ds; 23 | 24 | 25 | public isolated function verifySignature(xml|Document soapEnvelope, InboundConfig config) returns boolean|error { 26 | WsSecurity wsSecurity = new; 27 | Document document; 28 | if soapEnvelope is xml { 29 | document = check new (soapEnvelope); 30 | } else { 31 | document = soapEnvelope; 32 | } 33 | return check wsSecurity.verifySignature(document, config); 34 | } 35 | 36 | public isolated function decryptEnvelope(xml soapEnvelope, InboundConfig config) returns Document|error { 37 | WsSecurity wsSecurity = new; 38 | Document document = check new (soapEnvelope); 39 | return check wsSecurity.decryptEnvelope(document, config); 40 | } 41 | 42 | isolated function addSecurityHeader(Document document) returns WSSecurityHeader|Error { 43 | WSSecurityHeader wsSecHeader = check new (document); 44 | Error? insertHeader = wsSecHeader.insertSecHeader(); 45 | return insertHeader ?: wsSecHeader; 46 | } 47 | 48 | public isolated function verifyData(byte[] data, byte[] signature, crypto:PublicKey publicKey, 49 | SignatureAlgorithm signatureAlgorithm) returns Error? { 50 | Signature sign = check new (); 51 | boolean verifySignature = check sign.verifySignature(data, signature, publicKey, signatureAlgorithm); 52 | if !verifySignature { 53 | return error Error("Signature verification of the SOAP envelope has been failed"); 54 | } 55 | } 56 | 57 | isolated function addSignature(Signature sign, string signatureAlgorithm, byte[] signature) returns Signature|Error { 58 | sign.setSignatureAlgorithm(signatureAlgorithm); 59 | sign.setSignatureValue(signature); 60 | return sign; 61 | } 62 | 63 | isolated function addEncryption(Encryption encrypt, string encryptionAlgorithm, byte[] encryption) returns Encryption|Error { 64 | encrypt.setEncryptionAlgorithm(encryptionAlgorithm); 65 | encrypt.setEncryptedData(encryption); 66 | return encrypt; 67 | } 68 | 69 | isolated function applyEncryptedKey(string envelopeString, crypto:PrivateKey symmetricKey, crypto:PublicKey encryptKey) 70 | returns string|Error { 71 | string securedEnvelope = envelopeString; 72 | do { 73 | Encryption encryption = check new (); 74 | byte[] encryptedKey = check crypto:encryptRsaEcb(symmetricKey.toBalString().toBytes(), encryptKey); 75 | string encryptedKeyElements = check encryption.getEncryptedKeyElements(encryptedKey); 76 | string replace = regexp:replace(re `<`, encryptedKeyElements, "<"); 77 | string:RegExp securityToken = 78 | re ``; 79 | if securedEnvelope.includesMatch(securityToken) { 80 | securedEnvelope = regexp:replace(securityToken, securedEnvelope, replace); 81 | } 82 | else if securedEnvelope.includesMatch(re ``) { 83 | securedEnvelope = regexp:replace(re ``, securedEnvelope, replace); 84 | } 85 | return securedEnvelope; 86 | } on fail error encryptionError { 87 | return error Error("Error occurred while applying the encrypted key to the envelope", encryptionError); 88 | } 89 | } 90 | 91 | isolated function convertStringToXml(string envelope) returns xml|Error { 92 | xml|error xmlEnvelope = xml:fromString(regexp:replace(re `<`, envelope, "<")); 93 | if xmlEnvelope is error { 94 | return error Error(xmlEnvelope.message()); 95 | } 96 | return xmlEnvelope; 97 | } 98 | 99 | # Returns the encrypted data of the SOAP envelope. 100 | # 101 | # + envelope - The SOAP envelope 102 | # + return - A `byte[]` if the encrypted data is successfully decoded or else `wssec:Error` 103 | public isolated function getEncryptedData(xml envelope) returns byte[]|Error { 104 | Document document = check new (envelope); 105 | return document.getEncryptedData(); 106 | } 107 | 108 | # Returns the signed data of the SOAP envelope. 109 | # 110 | # + envelope - The SOAP envelope 111 | # + return - A `byte[]` if the signed data is successfully decoded or else `wssec:Error` 112 | public isolated function getSignatureData(xml envelope) returns byte[]|Error { 113 | Document document = check new (envelope); 114 | return document.getSignatureData(); 115 | } 116 | 117 | # Apply timestamp token security policy to the SOAP envelope. 118 | # 119 | # + envelope - The SOAP envelope 120 | # + timestampToken - The `TSRecord` record with the required parameters 121 | # + return - A `xml` type of SOAP envelope if the security binding is successfully added or else `wssec:Error` 122 | public isolated function applyTimestampToken(xml envelope, *TimestampTokenConfig timestampToken) returns xml|Error { 123 | if timestampToken.timeToLive <= 0 { 124 | return error Error("Invalid value for `timeToLive`"); 125 | } 126 | Document document = check new (envelope); 127 | WSSecurityHeader wsSecurityHeader = check addSecurityHeader(document); 128 | WsSecurity wsSecurity = new; 129 | string securedEnvelope = check wsSecurity.applyTimestampPolicy(wsSecurityHeader, timestampToken.timeToLive); 130 | return convertStringToXml(securedEnvelope); 131 | } 132 | 133 | # Apply username token security policy to the SOAP envelope. 134 | # 135 | # + envelope - The SOAP envelope 136 | # + usernameToken - The `UsernameTokenConfig` record with the required parameters 137 | # + return - A `xml` type of SOAP envelope if the security binding is successfully added or else `wssec:Error` 138 | public isolated function applyUsernameToken(xml envelope, *UsernameTokenConfig usernameToken) returns xml|Error { 139 | Document document = check new (envelope); 140 | WSSecurityHeader wsSecurityHeader = check addSecurityHeader(document); 141 | WsSecurity wsSecurity = new; 142 | string securedEnvelope = check wsSecurity 143 | .applyUsernameTokenPolicy(wsSecurityHeader, usernameToken.username, usernameToken.password, 144 | usernameToken.passwordType); 145 | return convertStringToXml(securedEnvelope); 146 | } 147 | 148 | # Apply symmetric binding security policy with username token to the SOAP envelope. 149 | # 150 | # + envelope - The SOAP envelope 151 | # + soap12 - A boolean flag. Set to `true` for SOAP 1.2, or `false` for SOAP 1.1. 152 | # + symmetricBinding - The `SymmetricBindingConfig` record with the required parameters 153 | # + return - A `xml` type of SOAP envelope if the security binding is successfully added or else `wssec:Error` 154 | public isolated function applySymmetricBinding(xml envelope, boolean soap12, *SymmetricBindingConfig symmetricBinding) 155 | returns xml|crypto:Error|Error { 156 | Document document = check new (envelope); 157 | WSSecurityHeader wsSecurityHeader = check addSecurityHeader(document); 158 | string securedEnvelope = envelope.toBalString(); 159 | SignatureAlgorithm? signatureAlgorithm = symmetricBinding.signatureAlgorithm; 160 | EncryptionAlgorithm? encryptionAlgorithm = symmetricBinding.encryptionAlgorithm; 161 | if signatureAlgorithm is SignatureAlgorithm { 162 | Signature signature = check new (); 163 | byte[] signedData; 164 | if soap12 { 165 | signedData = check signature.signData((envelope//*).toString(), signatureAlgorithm, 166 | symmetricBinding.symmetricKey); 167 | } else { 168 | signedData = check signature.signData((envelope//*).toString(), signatureAlgorithm, 169 | symmetricBinding.symmetricKey); 170 | } 171 | 172 | Signature signatureResult = check addSignature(signature, signatureAlgorithm, signedData); 173 | WsSecurity wsSecurity = new; 174 | securedEnvelope = check wsSecurity.applySignatureOnlyPolicy(wsSecurityHeader, signatureResult, 175 | symmetricBinding.x509Token); 176 | } 177 | if encryptionAlgorithm is EncryptionAlgorithm { 178 | Encryption encryption = check new (); 179 | byte[] encryptData; 180 | if soap12 { 181 | encryptData = check crypto:encryptRsaEcb((envelope//*).toString().toBytes(), 182 | symmetricBinding.symmetricKey); 183 | } else { 184 | encryptData = check crypto:encryptRsaEcb((envelope//*).toString().toBytes(), 185 | symmetricBinding.symmetricKey); 186 | } 187 | Encryption encryptionResult = check addEncryption(encryption, encryptionAlgorithm, encryptData); 188 | WsSecurity wsSecurity = new; 189 | securedEnvelope = check wsSecurity.applyEncryptionOnlyPolicy(wsSecurityHeader, encryptionResult); 190 | } 191 | securedEnvelope = check applyEncryptedKey(securedEnvelope, symmetricBinding.symmetricKey, 192 | symmetricBinding.servicePublicKey); 193 | return convertStringToXml(securedEnvelope); 194 | } 195 | 196 | # Apply asymmetric binding security policy with X509 token to the SOAP envelope. 197 | # 198 | # + envelope - The SOAP envelope 199 | # + soap12 - A boolean flag. Set to `true` for SOAP 1.2, or `false` for SOAP 1.1. 200 | # + asymmetricBinding - The `AsymmetricBindingConfig` record with the required parameters 201 | # + return - A `xml` type of SOAP envelope if the security binding is successfully added or else `wssec:Error` 202 | public isolated function applyAsymmetricConfigurations(xml envelope, boolean soap12, 203 | *AsymmetricBindingConfig asymmetricBinding) 204 | returns xml|Error { 205 | Document document = check new (envelope); 206 | WsSecurity wsSecurity = new; 207 | _ = check addSecurityHeader(document); 208 | SignatureConfig? signatureConfig = asymmetricBinding.signatureConfig; 209 | EncryptionConfig? encryptionConfig = asymmetricBinding.encryptionConfig; 210 | string securedEnvelope = envelope.toString(); 211 | if signatureConfig !is () && encryptionConfig !is () { 212 | securedEnvelope = check wsSecurity 213 | .applySignatureAndEncryption(document, soap12, signatureConfig, encryptionConfig); 214 | } else if signatureConfig !is () { 215 | securedEnvelope = check wsSecurity.applySignatureOnly(document, soap12, signatureConfig); 216 | } else if encryptionConfig !is () { 217 | securedEnvelope = check wsSecurity.applyEncryptionOnly(document, soap12, encryptionConfig); 218 | } 219 | return convertStringToXml(securedEnvelope); 220 | } 221 | 222 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ballerina SOAP Library 2 | 3 | [![Build](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/build-timestamped-master.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/build-timestamped-master.yml) 4 | [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerina-soap/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerina-soap) 5 | [![Trivy](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/trivy-scan.yml) 6 | [![GraalVM Check](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/build-with-bal-test-graalvm.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-soap/actions/workflows/build-with-bal-test-graalvm.yml) 7 | [![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/module-ballerina-soap.svg)](https://github.com/ballerina-platform/module-ballerina-soap/commits/master) 8 | [![Github issues](https://img.shields.io/github/issues/ballerina-platform/ballerina-standard-library/module/soap.svg?label=Open%20Issues)](https://github.com/ballerina-platform/ballerina-standard-library/labels/module%2Fsoap) 9 | [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerina-soap/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerina-soap) 10 | 11 | This module offers a set of APIs that facilitate the transmission of XML requests to a SOAP backend. It excels in managing security policies within SOAP requests, ensuring the transmission of secured SOAP envelopes. Moreover, it possesses the capability to efficiently extract data from security-applied SOAP responses. 12 | 13 | SOAP module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message. 14 | 15 | ## Client 16 | 17 | The `Client` is used to connect to and interact with `SOAP` endpoints. 18 | 19 | ### SOAP 1.1 Client 20 | 21 | ```ballerina 22 | import ballerina/soap.soap11; 23 | 24 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 25 | ``` 26 | 27 | ### SOAP 1.2 Client 28 | 29 | ```ballerina 30 | import ballerina/soap.soap12; 31 | 32 | soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 33 | ``` 34 | 35 | ## APIs associated with SOAP 36 | 37 | - **Send & Receive**: Sends SOAP request and receives a response. 38 | - **Send Only**: Fires and forgets requests. Sends the request without the possibility of any response from the service. 39 | 40 | The SOAP 1.1 specification requires the inclusion of the `action` parameter as a mandatory component within its APIs. In contrast, SOAP 1.2 relaxes this requirement, making the action parameter optional. 41 | 42 | ### Example: Send & Receive 43 | 44 | ```ballerina 45 | import ballerina/soap.soap11; 46 | 47 | public function main() returns error? { 48 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 49 | 50 | xml envelope = xml ` 51 | 52 | 53 | 2 54 | 3 55 | 56 | 57 | `; 58 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 59 | } 60 | ``` 61 | 62 | ### Example: Send Only 63 | 64 | ```ballerina 65 | import ballerina/soap.soap11; 66 | 67 | public function main() returns error? { 68 | soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL"); 69 | 70 | xml envelope = xml ` 71 | 72 | 73 | 2 74 | 3 75 | 76 | 77 | `; 78 | check soapClient->sendOnly(envelope, "http://tempuri.org/Add"); 79 | } 80 | ``` 81 | 82 | ## Security 83 | 84 | The SOAP client module introduces a robust framework for configuring security measures in SOAP communication. Security is a critical concern when exchanging data via web services, and this module offers comprehensive options to fortify SOAP requests and responses. 85 | 86 | There are two primary security configurations available for SOAP clients: 87 | 88 | - `outboundSecurity`: This configuration applies ws-security policies to outgoing SOAP messages. It supports multiple security options, such as Username Token, Timestamp Token, X.509 Token, Symmetric Binding, Asymmetric Binding, and Transport Binding. These can be used individually or in combination to secure the message. 89 | 90 | - `inboundSecurity`: This configuration handles the security of incoming SOAP messages. It decrypts encrypted data and verifies the digital signature to confirm the authenticity of the message. 91 | 92 | ### Policies 93 | 94 | This library currently supports the following WS Security policies: 95 | 96 | - **Username Token**: Provides authentication through username and password credentials. 97 | - **Timestamp Token**: Enhances message integrity by incorporating timestamp information. 98 | - **X509 Token**: Allows the use of X.509 certificates for secure communication. 99 | - **Symmetric Binding**: Enables symmetric key-based security mechanisms. 100 | - **Asymmetric Binding**: Facilitates the use of asymmetric cryptography for enhanced security. 101 | 102 | These policies empower SOAP clients to enhance the security of their web service communications by selecting and implementing the appropriate security mechanisms to safeguard their SOAP envelopes. 103 | 104 | ### Security Policy Configuration Types 105 | 106 | #### Outbound Security Configurations 107 | 108 | - `TimestampTokenConfig`: Represents the record for Timestamp Token policy. 109 | - Fields: 110 | - `int` timeToLive : The time to get expired 111 | 112 | - `UsernameTokenConfig`: Represents the record for Username Token policy. 113 | - Fields: 114 | - `string` username : The name of the user 115 | - `string` password : The password of the user 116 | - `PasswordType` passwordType : The password type of the username token 117 | 118 | - `SymmetricBindingConfig`: Represents the record for Symmetric Binding policy. 119 | - Fields: 120 | - `crypto:PrivateKey` symmetricKey : The key to sign and encrypt the SOAP envelope 121 | - `crypto:PublicKey` servicePublicKey : The key to encrypt the symmetric key 122 | - `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope 123 | - `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP envelope 124 | - `string` x509Token : The path or token of the X509 certificate 125 | 126 | - `AsymmetricBindingConfig`: Represents the record for Asymmetric Binding policy. 127 | - Fields: 128 | - `SignatureConfig` signatureConfig : Configuration for applying digital signatures 129 | - `EncryptionConfig` encryptionConfig : Configuration for applying encryption 130 | - `string` x509Token : The path or token of the X509 certificate 131 | 132 | #### Inbound Security Configurations 133 | 134 | - `InboundSecurityConfig`: Represents the record for outbound security configurations to verify and decrypt SOAP envelopes. 135 | - Fields: 136 | - `crypto:KeyStore` decryptKeystore - The keystore to decrypt the SOAP envelope 137 | - `crypto:KeyStore` signatureKeystore - The keystore to verify the signature of the SOAP envelope 138 | 139 | ### Apply Security Policies 140 | 141 | #### SOAP 1.1 Client: UsernameToken and TranportBinding Policy 142 | 143 | ```ballerina 144 | import ballerina/crypto; 145 | import ballerina/mime; 146 | import ballerina/soap; 147 | import ballerina/soap.soap11; 148 | 149 | public function main() returns error? { 150 | soap11:Client soapClient = check new ("https://www.secured-soap-endpoint.com", 151 | { 152 | outboundSecurity: [ 153 | { 154 | username: "username", 155 | password: "password", 156 | passwordType: soap:TEXT 157 | }, 158 | TRANSPORT_BINDING 159 | ] 160 | }); 161 | 162 | xml envelope = xml ` 163 | 164 | 165 | 2 166 | 3 167 | 168 | 169 | `; 170 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 171 | } 172 | ``` 173 | 174 | #### SOAP 1.2 Client with Asymmetric Binding and Outbound Security Configuration 175 | 176 | ```ballerina 177 | import ballerina/crypto; 178 | import ballerina/mime; 179 | import ballerina/soap; 180 | import ballerina/soap.soap12; 181 | 182 | public function main() returns error? { 183 | 184 | soap12:Client soapClient = check new ("http://www.secured-soap-endpoint.com", 185 | { 186 | outboundSecurity: { 187 | signatureConfig: { 188 | keystore: { 189 | path: KEY_STORE_PATH, 190 | password: PASSWORD 191 | }, 192 | privateKeyAlias: ALIAS, 193 | privateKeyPassword: PASSWORD, 194 | signatureAlgorithm: wssec:RSA_SHA1 195 | }, 196 | encryptionConfig: { 197 | keystore: { 198 | path: KEY_STORE_PATH_2, 199 | password: PASSWORD 200 | }, 201 | publicKeyAlias: ALIAS, 202 | encryptionAlgorithm: wssec:AES_128 203 | } 204 | }, 205 | inboundSecurity: { 206 | decryptKeystore: { 207 | path: KEY_STORE_PATH_2, 208 | password: PASSWORD 209 | }, 210 | signatureKeystore: { 211 | path: KEY_STORE_PATH_2, 212 | password: PASSWORD 213 | } 214 | } 215 | }); 216 | 217 | xml envelope = xml ` 218 | 219 | 220 | 2 221 | 3 222 | 223 | 224 | `; 225 | xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); 226 | } 227 | ``` 228 | 229 | ## Issues and projects 230 | 231 | The **Issues** and **Projects** tabs are disabled for this repository as this is part of the Ballerina Standard Library. To report bugs, request new features, start new discussions, view project boards, etc., go to the Ballerina Standard Library [parent repository](https://github.com/ballerina-platform/ballerina-standard-library). 232 | 233 | This repository contains only the source code of the package. 234 | 235 | ## Build from the source 236 | 237 | ### Set up the prerequisites 238 | 239 | 1. Download and install Java SE Development Kit (JDK) version 21 (from one of the following locations). 240 | - [Oracle](https://www.oracle.com/java/technologies/downloads/) 241 | 242 | - [OpenJDK](https://adoptium.net/) 243 | 244 | > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. 245 | 246 | 2. Export your Github Personal access token with the read package permissions as follows. 247 | 248 | ```bash 249 | export packageUser= 250 | export packagePAT= 251 | ``` 252 | 253 | ### Build the source 254 | 255 | Execute the commands below to build from source. 256 | 257 | 1. To build the library: 258 | 259 | ```bash 260 | ./gradlew clean build 261 | ``` 262 | 263 | 2. To run the integration tests: 264 | 265 | ```bash 266 | ./gradlew clean test 267 | ``` 268 | 269 | 3. To build the module without the tests: 270 | 271 | ```bash 272 | ./gradlew clean build -x test 273 | ``` 274 | 275 | 4. To debug module implementation: 276 | 277 | ```bash 278 | ./gradlew clean build -Pdebug= 279 | ./gradlew clean test -Pdebug= 280 | ``` 281 | 282 | 5. To debug the module with Ballerina language: 283 | 284 | ```bash 285 | ./gradlew clean build -PbalJavaDebug= 286 | ./gradlew clean test -PbalJavaDebug= 287 | ``` 288 | 289 | 6. Publish ZIP artifact to the local `.m2` repository: 290 | 291 | ```bash 292 | ./gradlew clean build publishToMavenLocal 293 | ``` 294 | 295 | 7. Publish the generated artifacts to the local Ballerina central repository: 296 | 297 | ```bash 298 | ./gradlew clean build -PpublishToLocalCentral=true 299 | ``` 300 | 301 | 8. Publish the generated artifacts to the Ballerina central repository: 302 | 303 | ```bash 304 | ./gradlew clean build -PpublishToCentral=true 305 | ``` 306 | 307 | ## Contribute to Ballerina 308 | 309 | As an open source project, Ballerina welcomes contributions from the community. 310 | 311 | For more information, go to the [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md). 312 | 313 | ## Code of conduct 314 | 315 | All contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct). 316 | 317 | ## Useful links 318 | 319 | - Chat live with us via our [Discord server](https://discord.gg/ballerinalang). 320 | - Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag. 321 | - For more information go to the [`soap` library](https://lib.ballerina.io/ballerina/soap/latest). 322 | - For example demonstrations of the usage, go to [Ballerina By Examples](https://ballerina.io/swan-lake/learn/by-example/). 323 | --------------------------------------------------------------------------------