Skip to content

Commit

Permalink
Merge pull request #103 from Nuvindu/ws-security-policies
Browse files Browse the repository at this point in the history
Improve the SOAP module to support web service securities
  • Loading branch information
ThisaruGuruge authored Oct 16, 2023
2 parents 5708a11 + af0ac20 commit 96414b6
Show file tree
Hide file tree
Showing 64 changed files with 5,130 additions and 366 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ensure all Java files use LF.
*.java eol=lf
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ target
# Ballerina
velocity.log*
*Ballerina.lock

.vscode
config-dir
208 changes: 206 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,213 @@ Ballerina SOAP Library
[![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%2Fyaml)
[![codecov](https://codecov.io/gh/ballerina-platform/module-ballerina-soap/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerina-soap)

This library provides APIs to send an ordinary XML request to a SOAP backend by specifying the necessary details to construct a SOAP envelope.
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.

Soap module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message.
SOAP module abstracts out the details of the creation of a SOAP envelope, headers, and the body in a SOAP message.

## Client

The `Client` is used to connect to and interact with `SOAP` endpoints.

### SOAP 1.1 Client

```ballerina
import ballerina/soap:soap11;
soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL");
```

### SOAP 1.2 Client

```ballerina
import ballerina/soap:soap12;
soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL");
```

## APIs associated with SOAP

- **Send & Receive**: Sends SOAP request and receives a response.
- **Send Only**: Fires and forgets requests. Sends the request without the possibility of any response from the service.

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.

### Example: Send & Receive

```ballerina
import ballerina/soap:soap11;
public function main() returns error? {
soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL");
xml envelope = xml `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
}
```

### Example: Send Only

```ballerina
import ballerina/soap:soap11;
public function main() returns error? {
soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL");
xml envelope = xml `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
check soapClient->sendOnly(envelope, "http://tempuri.org/Add");
}
```

## Security

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.

There are two primary security configurations available for SOAP clients:

- `inboundSecurity`: This configuration is applied to the SOAP envelope when a request is made. It includes various ws security policies such as Username Token, Timestamp Token, X509 Token, Symmetric Binding, Asymmetric Binding, and Transport Binding, either individually or in combination with each other.

- `outboundSecurity`: This configuration is applied to the SOAP envelope when a response is received. Its purpose is to decrypt the data within the envelope and verify the digital signature for security validation.

### Policies

This library currently supports the following WS Security policies:

- **Username Token**: Provides authentication through username and password credentials.
- **Timestamp Token**: Enhances message integrity by incorporating timestamp information.
- **X509 Token**: Allows the use of X.509 certificates for secure communication.
- **Symmetric Binding**: Enables symmetric key-based security mechanisms.
- **Asymmetric Binding**: Facilitates the use of asymmetric cryptography for enhanced security.

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.

### Security Policy Configuration Types

#### Inbound Security Configurations

- `TimestampTokenConfig`: Represents the record for Timestamp Token policy.
- Fields:
- `int` timeToLive : The time to get expired

- `UsernameTokenConfig`: Represents the record for Username Token policy.
- Fields:
- `string` username : The name of the user
- `string` password : The password of the user
- `PasswordType` passwordType : The password type of the username token

- `SymmetricBindingConfig`: Represents the record for Symmetric Binding policy.
- Fields:
- `crypto:PrivateKey` symmetricKey : The key to sign and encrypt the SOAP envelope
- `crypto:PublicKey` servicePublicKey : The key to encrypt the symmetric key
- `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope
- `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP envelope
- `string` x509Token : The path or token of the X509 certificate

- `AsymmetricBindingConfig`: Represents the record for Username Token with Asymmetric Binding policy.
- Fields:
- `crypto:PrivateKey` signatureKey : The private key to sign the SOAP envelope
- `crypto:PublicKey` encryptionKey : The public key to encrypt the SOAP body
- `SignatureAlgorithm` signatureAlgorithm : The algorithm to sign the SOAP envelope
- `EncryptionAlgorithm` encryptionAlgorithm : The algorithm to encrypt the SOAP body
- `string` x509Token : field description

#### Outbound Security Configurations

- `OutboundSecurityConfig`: Represents the record for outbound security configurations to verify and decrypt SOAP envelopes.
- Fields:
- `crypto:PublicKey` verificationKey : The public key to verify the signature of the SOAP envelope
- `crypto:PrivateKey`|`crypto:PublicKey` decryptionKey : The private key to decrypt the SOAP envelope
- `SignatureAlgorithm` signatureAlgorithm : The algorithm to verify the SOAP envelope
- `EncryptionAlgorithm` decryptionAlgorithm : The algorithm to decrypt the SOAP body

### Apply Security Policies

#### SOAP 1.1 Client: UsernameToken and TranportBinding Policy

```ballerina
import ballerina/crypto;
import ballerina/mime;
import ballerina/soap;
import ballerina/soap:soap11;
public function main() returns error? {
soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
inboundSecurity: [
{
username: "username",
password: "password",
passwordType: soap:TEXT
},
TRANSPORT_BINDING
]
});
xml envelope = xml `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
}
```

#### SOAP 1.2 Client with Asymmetric Binding and Outbound Security Configuration

```ballerina
import ballerina/crypto;
import ballerina/mime;
import ballerina/soap;
import ballerina/soap:soap12;
public function main() returns error? {
configurable crypto:PrivateKey clientPrivateKey = ?;
configurable crypto:PublicKey clientPublicKey = ?;
configurable ​crypto:PublicKey serverPublicKey = ?;
soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
inboundSecurity: {
signatureAlgorithm: soap:RSA_SHA256,
encryptionAlgorithm: soap:RSA_ECB,
signatureKey: clientPrivateKey,
encryptionKey: serverPublicKey,
},
outboundSecurity: {
verificationKey: serverPublicKey,
signatureAlgorithm: soap:RSA_SHA256,
decryptionKey: clientPrivateKey,
decryptionAlgorithm: soap:RSA_ECB
}
});
xml envelope = xml `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
}
```

## Issues and projects

Expand Down
31 changes: 31 additions & 0 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,39 @@ org = "ballerina"
name = "soap"
version = "0.2.0"
authors = ["Ballerina"]
export=["soap", "soap.soap11", "soap.soap12"]
keywords = ["soap"]
repository = "https://github.com/ballerina-platform/module-ballerina-soap"
icon = "icon.png"
license = ["Apache-2.0"]
distribution = "2201.8.0"

[build-options]
observabilityIncluded = true

[platform.java17]
graalvmCompatible = true

[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "soap-native"
version = "0.2.0"
path = "../native/build/libs/soap-native-0.2.0-SNAPSHOT.jar"

[[platform.java17.dependency]]
groupId = "org.apache.wss4j"
artifactId = "wss4j-ws-security-dom"
version = "3.0.1"
path = "./lib/wss4j-ws-security-dom-3.0.1.jar"

[[platform.java17.dependency]]
groupId = "org.apache.wss4j"
artifactId = "wss4j-ws-security-common"
version = "3.0.1"
path = "./lib/wss4j-ws-security-common-3.0.1.jar"

[[platform.java17.dependency]]
groupId = "org.apache.santuario"
artifactId = "xmlsec"
version = "3.0.2"
path = "./lib/xmlsec-3.0.2.jar"
44 changes: 37 additions & 7 deletions ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.8.0-20230726-145300-b2bdf796"
distribution-version = "2201.8.0"

[[package]]
org = "ballerina"
Expand All @@ -22,7 +22,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "cache"
version = "3.7.0"
version = "3.7.1"
dependencies = [
{org = "ballerina", name = "constraint"},
{org = "ballerina", name = "jballerina.java"},
Expand All @@ -46,6 +46,9 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "time"}
]
modules = [
{org = "ballerina", packageName = "crypto", moduleName = "crypto"}
]

[[package]]
org = "ballerina"
Expand All @@ -61,7 +64,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "http"
version = "2.10.0"
version = "2.10.3"
dependencies = [
{org = "ballerina", name = "auth"},
{org = "ballerina", name = "cache"},
Expand Down Expand Up @@ -98,11 +101,17 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.value"}
]
modules = [
{org = "ballerina", packageName = "io", moduleName = "io"}
]

[[package]]
org = "ballerina"
name = "jballerina.java"
version = "0.0.0"
modules = [
{org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"}
]

[[package]]
org = "ballerina"
Expand Down Expand Up @@ -148,7 +157,6 @@ dependencies = [
org = "ballerina"
name = "lang.error"
version = "0.0.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
Expand All @@ -175,6 +183,9 @@ version = "0.0.0"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
modules = [
{org = "ballerina", packageName = "lang.regexp", moduleName = "lang.regexp"}
]

[[package]]
org = "ballerina"
Expand Down Expand Up @@ -260,12 +271,20 @@ org = "ballerina"
name = "soap"
version = "0.2.0"
dependencies = [
{org = "ballerina", name = "crypto"},
{org = "ballerina", name = "http"},
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.regexp"},
{org = "ballerina", name = "mime"},
{org = "ballerina", name = "test"}
{org = "ballerina", name = "test"},
{org = "ballerinai", name = "observe"}
]
modules = [
{org = "ballerina", packageName = "soap", moduleName = "soap"}
{org = "ballerina", packageName = "soap", moduleName = "soap"},
{org = "ballerina", packageName = "soap", moduleName = "soap.soap11"},
{org = "ballerina", packageName = "soap", moduleName = "soap.soap12"},
{org = "ballerina", packageName = "soap", moduleName = "soap.wssec"}
]

[[package]]
Expand All @@ -281,7 +300,6 @@ dependencies = [
org = "ballerina"
name = "test"
version = "0.0.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.error"}
Expand All @@ -306,3 +324,15 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"}
]

[[package]]
org = "ballerinai"
name = "observe"
version = "0.0.0"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "observe"}
]
modules = [
{org = "ballerinai", packageName = "observe", moduleName = "observe"}
]

Loading

0 comments on commit 96414b6

Please sign in to comment.