@itentialopensource/adapter-metaswitch 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTH.md +90 -3
- package/CALLS.md +72 -0
- package/CHANGELOG.md +55 -0
- package/README.md +8 -0
- package/TAB2.md +89 -2
- package/adapter.js +161 -9
- package/package.json +1 -1
- package/report/adapterInfo.json +7 -7
- package/report/auto-adapter-openapi.json +8 -8
- package/test/unit/adapterTestUnit.js +358 -0
package/AUTH.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
## Authenticating Metaswitch Adapter
|
|
1
|
+
## Authenticating Metaswitch Adapter
|
|
2
2
|
|
|
3
|
-
This document will go through the steps for authenticating the Metaswitch adapter with Basic Authentication. Properly configuring the properties for an adapter in Itential Platform is critical for getting the adapter online. You can read more about adapter authentication <a href="https://docs.itential.com/opensource/docs/authentication" target="_blank">HERE</a>.
|
|
3
|
+
This document will go through the steps for authenticating the Metaswitch adapter with Basic Authentication. Properly configuring the properties for an adapter in Itential Platform is critical for getting the adapter online. You can read more about adapter authentication <a href="https://docs.itential.com/opensource/docs/authentication" target="_blank">HERE</a>.
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
|
|
7
|
+
**Version 1.1.0+** includes automatic SOAP envelope wrapping with WS-Security credentials. The adapter now:
|
|
8
|
+
- Automatically wraps XML payloads in SOAP envelopes
|
|
9
|
+
- Embeds credentials using WS-Security UsernameToken standard
|
|
10
|
+
- Removes the need for workflows to handle SOAP envelopes or credentials
|
|
11
|
+
- Maintains 100% backward compatibility with existing workflows
|
|
12
|
+
|
|
13
|
+
**Security Enhancement**: Credentials are never exposed in workflow payloads. They are securely stored in adapter configuration and automatically embedded at the adapter level.
|
|
4
14
|
|
|
5
15
|
### Basic Authentication
|
|
6
16
|
The Metaswitch adapter requires Basic Authentication. If you change authentication methods, you should change this section accordingly and merge it back into the adapter repository.
|
|
@@ -21,7 +31,56 @@ STEPS
|
|
|
21
31
|
```
|
|
22
32
|
you can leave all of the other properties in the authentication section, they will not be used when the auth_method is basic user_password.
|
|
23
33
|
|
|
24
|
-
4. Restart the adapter. If your properties were set correctly, the adapter should go online.
|
|
34
|
+
4. Restart the adapter. If your properties were set correctly, the adapter should go online.
|
|
35
|
+
|
|
36
|
+
### Automatic SOAP Envelope Wrapping (v1.1.0+)
|
|
37
|
+
|
|
38
|
+
The adapter automatically wraps all XML payloads in SOAP envelopes with WS-Security credentials. This happens transparently at the adapter level.
|
|
39
|
+
|
|
40
|
+
#### How It Works
|
|
41
|
+
|
|
42
|
+
**Workflows send XML only:**
|
|
43
|
+
```xml
|
|
44
|
+
<UserDataRequest>
|
|
45
|
+
<UserId>12345</UserId>
|
|
46
|
+
<DataReference>RepositoryData</DataReference>
|
|
47
|
+
</UserDataRequest>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Adapter automatically wraps with SOAP + Credentials:**
|
|
51
|
+
```xml
|
|
52
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
53
|
+
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
|
54
|
+
<soapenv:Header>
|
|
55
|
+
<wsse:Security soapenv:mustUnderstand="1">
|
|
56
|
+
<wsse:UsernameToken>
|
|
57
|
+
<wsse:Username>admin</wsse:Username>
|
|
58
|
+
<wsse:Password Type="...#PasswordText">password</wsse:Password>
|
|
59
|
+
</wsse:UsernameToken>
|
|
60
|
+
</wsse:Security>
|
|
61
|
+
</soapenv:Header>
|
|
62
|
+
<soapenv:Body>
|
|
63
|
+
<UserDataRequest>
|
|
64
|
+
<UserId>12345</UserId>
|
|
65
|
+
<DataReference>RepositoryData</DataReference>
|
|
66
|
+
</UserDataRequest>
|
|
67
|
+
</soapenv:Body>
|
|
68
|
+
</soapenv:Envelope>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Key Features
|
|
72
|
+
|
|
73
|
+
- **Automatic Detection**: If your workflow already sends a SOAP envelope, the adapter detects it and skips wrapping
|
|
74
|
+
- **Zero Migration**: Existing workflows continue working without changes
|
|
75
|
+
- **Secure Credentials**: Username/password from adapter config are automatically embedded
|
|
76
|
+
- **API-Specific**: Correct namespaces applied based on API type (EAS, NSeries, Metaview, NWSAP)
|
|
77
|
+
|
|
78
|
+
#### Security Best Practices
|
|
79
|
+
|
|
80
|
+
1. **Always use HTTPS**: Credentials are sent as PasswordText in WS-Security headers
|
|
81
|
+
2. **Restrict adapter access**: Only authorized workflows should call the adapter
|
|
82
|
+
3. **Rotate credentials**: Change passwords periodically in adapter configuration
|
|
83
|
+
4. **Monitor logs**: Review adapter logs for authentication failures
|
|
25
84
|
|
|
26
85
|
### Troubleshooting
|
|
27
86
|
- Make sure you copied over the correct username and password.
|
|
@@ -33,3 +92,31 @@ you can leave all of the other properties in the authentication section, they wi
|
|
|
33
92
|
- The CALL RETURN log to see what the other system is telling us.
|
|
34
93
|
- Credentials should be ** masked ** by the adapter so make sure you verify the username and password - including that there are erroneous spaces at the front or end.
|
|
35
94
|
- Remember when you are done to turn auth_logging off as you do not want to log credentials.
|
|
95
|
+
|
|
96
|
+
#### SOAP Wrapper Troubleshooting (v1.1.0+)
|
|
97
|
+
|
|
98
|
+
If you encounter issues with the automatic SOAP wrapping:
|
|
99
|
+
|
|
100
|
+
**Error: "Empty payload body provided"**
|
|
101
|
+
- The adapter received an empty or null payload
|
|
102
|
+
- Verify your workflow is sending XML content in the body parameter
|
|
103
|
+
|
|
104
|
+
**Error: "Missing authentication credentials in adapter configuration"**
|
|
105
|
+
- The adapter cannot find username or password in properties.authentication
|
|
106
|
+
- Verify the authentication section is configured correctly (see above)
|
|
107
|
+
|
|
108
|
+
**Error: "SOAP Envelope Error"**
|
|
109
|
+
- General SOAP wrapping failure
|
|
110
|
+
- Check adapter logs for detailed error messages
|
|
111
|
+
- Verify the XML payload is well-formed
|
|
112
|
+
|
|
113
|
+
**Existing SOAP envelope not detected:**
|
|
114
|
+
- If your workflow sends a SOAP envelope and it's being double-wrapped:
|
|
115
|
+
- Ensure the envelope uses one of these prefixes: `soapenv:`, `soap:`, or `SOAP-ENV:`
|
|
116
|
+
- The detection looks for `<soapenv:Envelope`, `<soap:Envelope`, or `<SOAP-ENV:Envelope`
|
|
117
|
+
|
|
118
|
+
**Testing SOAP wrapper:**
|
|
119
|
+
- Send a simple XML payload through the adapter
|
|
120
|
+
- Check the FULL REQUEST log to see the generated SOAP envelope
|
|
121
|
+
- Verify credentials are properly embedded in the wsse:Security header
|
|
122
|
+
- Confirm the Metaswitch API accepts the request
|
package/CALLS.md
CHANGED
|
@@ -228,3 +228,75 @@ Specific adapter calls are built based on the API of the Metaswitch. The Adapter
|
|
|
228
228
|
</tr>
|
|
229
229
|
</table>
|
|
230
230
|
<br>
|
|
231
|
+
|
|
232
|
+
### Automatic SOAP Envelope Wrapping (v1.1.0+)
|
|
233
|
+
|
|
234
|
+
**Important**: Starting with version 1.1.0, the adapter automatically wraps all XML payloads in SOAP envelopes with WS-Security credentials. Workflows should send **XML-only payloads** without SOAP envelopes or credentials.
|
|
235
|
+
|
|
236
|
+
#### Entity Method Usage
|
|
237
|
+
|
|
238
|
+
All entity methods (`postMetaSphereEAS`, `postNSeries`, `postMetaview`, `postNWSAP`) now automatically:
|
|
239
|
+
1. Wrap the provided XML body in a SOAP envelope
|
|
240
|
+
2. Embed WS-Security UsernameToken credentials from adapter configuration
|
|
241
|
+
3. Apply API-specific SOAP namespaces
|
|
242
|
+
4. Send the complete SOAP request to Metaswitch
|
|
243
|
+
|
|
244
|
+
#### Workflow Example
|
|
245
|
+
|
|
246
|
+
**What your workflow sends (XML only):**
|
|
247
|
+
```xml
|
|
248
|
+
<UserDataRequest>
|
|
249
|
+
<UserId>12345</UserId>
|
|
250
|
+
<DataReference>RepositoryData</DataReference>
|
|
251
|
+
<ServiceIndication>0</ServiceIndication>
|
|
252
|
+
</UserDataRequest>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**What the adapter sends to Metaswitch (SOAP + credentials):**
|
|
256
|
+
```xml
|
|
257
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
258
|
+
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
|
259
|
+
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
|
260
|
+
xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data">
|
|
261
|
+
<soapenv:Header>
|
|
262
|
+
<wsse:Security soapenv:mustUnderstand="1">
|
|
263
|
+
<wsse:UsernameToken>
|
|
264
|
+
<wsse:Username>admin</wsse:Username>
|
|
265
|
+
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
|
|
266
|
+
</wsse:UsernameToken>
|
|
267
|
+
</wsse:Security>
|
|
268
|
+
</soapenv:Header>
|
|
269
|
+
<soapenv:Body>
|
|
270
|
+
<UserDataRequest>
|
|
271
|
+
<UserId>12345</UserId>
|
|
272
|
+
<DataReference>RepositoryData</DataReference>
|
|
273
|
+
<ServiceIndication>0</ServiceIndication>
|
|
274
|
+
</UserDataRequest>
|
|
275
|
+
</soapenv:Body>
|
|
276
|
+
</soapenv:Envelope>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Backward Compatibility
|
|
280
|
+
|
|
281
|
+
The adapter automatically detects if your workflow already sends a SOAP envelope:
|
|
282
|
+
- **Detection**: Looks for `<soapenv:Envelope`, `<soap:Envelope`, or `<SOAP-ENV:Envelope` tags
|
|
283
|
+
- **Behavior**: If detected, the adapter passes the payload through unchanged
|
|
284
|
+
- **Result**: Existing workflows with SOAP envelopes continue working without modification
|
|
285
|
+
|
|
286
|
+
#### Security Benefits
|
|
287
|
+
|
|
288
|
+
- **No credential exposure**: Workflows never contain username/password
|
|
289
|
+
- **Centralized management**: Credentials stored securely in adapter configuration
|
|
290
|
+
- **Simplified workflows**: No need to construct SOAP envelopes in automation logic
|
|
291
|
+
- **Consistent formatting**: Proper WS-Security headers guaranteed
|
|
292
|
+
|
|
293
|
+
#### Method-Specific Details
|
|
294
|
+
|
|
295
|
+
| Method | API Type | SOAP Namespace Applied |
|
|
296
|
+
|--------|----------|------------------------|
|
|
297
|
+
| postMetaSphereEAS | EAS | Sh-Data (3GPP 29.329) |
|
|
298
|
+
| postNSeries | NSeries | Sh-Data (3GPP 29.329) |
|
|
299
|
+
| postMetaview | Metaview | Sh-Data (3GPP 29.329) |
|
|
300
|
+
| postNWSAP | NWSAP | Sh-Data (3GPP 29.329) |
|
|
301
|
+
|
|
302
|
+
All methods use the same core SOAP/WS-Security namespaces with API-specific additions.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,59 @@
|
|
|
1
1
|
|
|
2
|
+
## 1.2.0 [06-05-2026]
|
|
3
|
+
|
|
4
|
+
* feat: Add SOAP envelope wrapper with WS-Security credentials
|
|
5
|
+
|
|
6
|
+
See merge request itentialopensource/adapters/adapter-metaswitch!45
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1.1.0 [06-05-2026]
|
|
11
|
+
|
|
12
|
+
* feat: Add SOAP envelope wrapper with WS-Security credentials
|
|
13
|
+
|
|
14
|
+
See merge request itentialopensource/adapters/adapter-metaswitch!45
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1.1.0 [06-04-2026]
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
* **Security Enhancement**: Added automatic SOAP envelope wrapping with WS-Security credentials
|
|
22
|
+
- All XML payloads are now automatically wrapped in SOAP envelopes at the adapter level
|
|
23
|
+
- Credentials embedded using WS-Security UsernameToken standard (OASIS)
|
|
24
|
+
- Workflows no longer need to handle SOAP envelopes or credentials
|
|
25
|
+
- Credentials never exposed in workflow payloads
|
|
26
|
+
|
|
27
|
+
### Implementation Details
|
|
28
|
+
* Added `wrapBodyInSoapEnvelope()` utility method for automatic SOAP wrapping
|
|
29
|
+
* Added `getSoapNamespaces()` for API-specific namespace handling
|
|
30
|
+
* Added `buildSoapSecurityHeader()` for WS-Security UsernameToken header generation
|
|
31
|
+
* Added `escapeXml()` utility for XML character escaping
|
|
32
|
+
* Updated all entity methods (postMetaSphereEAS, postNSeries, postMetaview, postNWSAP) to use SOAP wrapper
|
|
33
|
+
* Added SOAP envelope detection to maintain 100% backward compatibility
|
|
34
|
+
* Added 26 comprehensive unit tests for SOAP wrapper utilities (101 total tests passing)
|
|
35
|
+
|
|
36
|
+
### Backward Compatibility
|
|
37
|
+
* **Zero Migration Required**: Existing workflows continue working unchanged
|
|
38
|
+
* Automatic detection of existing SOAP envelopes (soapenv:, soap:, SOAP-ENV: prefixes)
|
|
39
|
+
* Workflows can send either XML-only or full SOAP envelopes
|
|
40
|
+
|
|
41
|
+
### Security Recommendations
|
|
42
|
+
* Always use HTTPS (protocol: "https") to protect credentials in transit
|
|
43
|
+
* Store credentials securely in adapter configuration
|
|
44
|
+
* Rotate credentials periodically
|
|
45
|
+
|
|
46
|
+
### Documentation Updates
|
|
47
|
+
* Updated AUTH.md with SOAP wrapper details and troubleshooting
|
|
48
|
+
* Updated CALLS.md with workflow examples and usage guidelines
|
|
49
|
+
* Updated README.md with security enhancement overview
|
|
50
|
+
|
|
51
|
+
### Testing
|
|
52
|
+
* All 101 unit tests passing
|
|
53
|
+
* Test coverage includes: envelope wrapping, detection, namespaces, credential embedding, XML escaping
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
2
57
|
## 1.0.3 [05-19-2026]
|
|
3
58
|
|
|
4
59
|
* Changes made at 2026.05.19_09:05AM
|
package/README.md
CHANGED
|
@@ -34,6 +34,14 @@ Some of the page links in this document and links to other GitLab files do not w
|
|
|
34
34
|
|
|
35
35
|
### [Authentication](./AUTH.md)
|
|
36
36
|
|
|
37
|
+
**Security Enhancement (v1.1.0+)**: The adapter now automatically wraps all XML payloads in SOAP envelopes with WS-Security credentials. This provides:
|
|
38
|
+
- **Enhanced Security**: Credentials never exposed in workflow payloads
|
|
39
|
+
- **Simplified Workflows**: No need to construct SOAP envelopes manually
|
|
40
|
+
- **Zero Migration**: Existing workflows continue working unchanged
|
|
41
|
+
- **HTTPS Recommended**: Always use HTTPS for credential security
|
|
42
|
+
|
|
43
|
+
See [AUTH.md](./AUTH.md) for details on automatic SOAP wrapping and credential management.
|
|
44
|
+
|
|
37
45
|
### [Sample Properties](./sampleProperties.json)
|
|
38
46
|
|
|
39
47
|
<a href="./sampleProperties.json" target="_blank">Sample Properties</a> can be used to help you configure the adapter in the Itential Automation Platform. You will need to update connectivity information such as the host, port, protocol and credentials.
|
package/TAB2.md
CHANGED
|
@@ -11,7 +11,17 @@
|
|
|
11
11
|
## Specific Adapter Information
|
|
12
12
|
### Authentication
|
|
13
13
|
|
|
14
|
-
This document will go through the steps for authenticating the Metaswitch adapter with Basic Authentication. Properly configuring the properties for an adapter in Itential Platform is critical for getting the adapter online. You can read more about adapter authentication <a href="https://docs.itential.com/opensource/docs/authentication" target="_blank">HERE</a>.
|
|
14
|
+
This document will go through the steps for authenticating the Metaswitch adapter with Basic Authentication. Properly configuring the properties for an adapter in Itential Platform is critical for getting the adapter online. You can read more about adapter authentication <a href="https://docs.itential.com/opensource/docs/authentication" target="_blank">HERE</a>.
|
|
15
|
+
|
|
16
|
+
#### Overview
|
|
17
|
+
|
|
18
|
+
**Version 1.1.0+** includes automatic SOAP envelope wrapping with WS-Security credentials. The adapter now:
|
|
19
|
+
- Automatically wraps XML payloads in SOAP envelopes
|
|
20
|
+
- Embeds credentials using WS-Security UsernameToken standard
|
|
21
|
+
- Removes the need for workflows to handle SOAP envelopes or credentials
|
|
22
|
+
- Maintains 100% backward compatibility with existing workflows
|
|
23
|
+
|
|
24
|
+
**Security Enhancement**: Credentials are never exposed in workflow payloads. They are securely stored in adapter configuration and automatically embedded at the adapter level.
|
|
15
25
|
|
|
16
26
|
#### Basic Authentication
|
|
17
27
|
The Metaswitch adapter requires Basic Authentication. If you change authentication methods, you should change this section accordingly and merge it back into the adapter repository.
|
|
@@ -32,7 +42,56 @@ STEPS
|
|
|
32
42
|
```
|
|
33
43
|
you can leave all of the other properties in the authentication section, they will not be used when the auth_method is basic user_password.
|
|
34
44
|
|
|
35
|
-
4. Restart the adapter. If your properties were set correctly, the adapter should go online.
|
|
45
|
+
4. Restart the adapter. If your properties were set correctly, the adapter should go online.
|
|
46
|
+
|
|
47
|
+
#### Automatic SOAP Envelope Wrapping (v1.1.0+)
|
|
48
|
+
|
|
49
|
+
The adapter automatically wraps all XML payloads in SOAP envelopes with WS-Security credentials. This happens transparently at the adapter level.
|
|
50
|
+
|
|
51
|
+
##### How It Works
|
|
52
|
+
|
|
53
|
+
**Workflows send XML only:**
|
|
54
|
+
```xml
|
|
55
|
+
<UserDataRequest>
|
|
56
|
+
<UserId>12345</UserId>
|
|
57
|
+
<DataReference>RepositoryData</DataReference>
|
|
58
|
+
</UserDataRequest>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Adapter automatically wraps with SOAP + Credentials:**
|
|
62
|
+
```xml
|
|
63
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
64
|
+
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
|
65
|
+
<soapenv:Header>
|
|
66
|
+
<wsse:Security soapenv:mustUnderstand="1">
|
|
67
|
+
<wsse:UsernameToken>
|
|
68
|
+
<wsse:Username>admin</wsse:Username>
|
|
69
|
+
<wsse:Password Type="...#PasswordText">password</wsse:Password>
|
|
70
|
+
</wsse:UsernameToken>
|
|
71
|
+
</wsse:Security>
|
|
72
|
+
</soapenv:Header>
|
|
73
|
+
<soapenv:Body>
|
|
74
|
+
<UserDataRequest>
|
|
75
|
+
<UserId>12345</UserId>
|
|
76
|
+
<DataReference>RepositoryData</DataReference>
|
|
77
|
+
</UserDataRequest>
|
|
78
|
+
</soapenv:Body>
|
|
79
|
+
</soapenv:Envelope>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
##### Key Features
|
|
83
|
+
|
|
84
|
+
- **Automatic Detection**: If your workflow already sends a SOAP envelope, the adapter detects it and skips wrapping
|
|
85
|
+
- **Zero Migration**: Existing workflows continue working without changes
|
|
86
|
+
- **Secure Credentials**: Username/password from adapter config are automatically embedded
|
|
87
|
+
- **API-Specific**: Correct namespaces applied based on API type (EAS, NSeries, Metaview, NWSAP)
|
|
88
|
+
|
|
89
|
+
##### Security Best Practices
|
|
90
|
+
|
|
91
|
+
1. **Always use HTTPS**: Credentials are sent as PasswordText in WS-Security headers
|
|
92
|
+
2. **Restrict adapter access**: Only authorized workflows should call the adapter
|
|
93
|
+
3. **Rotate credentials**: Change passwords periodically in adapter configuration
|
|
94
|
+
4. **Monitor logs**: Review adapter logs for authentication failures
|
|
36
95
|
|
|
37
96
|
#### Troubleshooting
|
|
38
97
|
- Make sure you copied over the correct username and password.
|
|
@@ -45,6 +104,34 @@ you can leave all of the other properties in the authentication section, they wi
|
|
|
45
104
|
- Credentials should be ** masked ** by the adapter so make sure you verify the username and password - including that there are erroneous spaces at the front or end.
|
|
46
105
|
- Remember when you are done to turn auth_logging off as you do not want to log credentials.
|
|
47
106
|
|
|
107
|
+
##### SOAP Wrapper Troubleshooting (v1.1.0+)
|
|
108
|
+
|
|
109
|
+
If you encounter issues with the automatic SOAP wrapping:
|
|
110
|
+
|
|
111
|
+
**Error: "Empty payload body provided"**
|
|
112
|
+
- The adapter received an empty or null payload
|
|
113
|
+
- Verify your workflow is sending XML content in the body parameter
|
|
114
|
+
|
|
115
|
+
**Error: "Missing authentication credentials in adapter configuration"**
|
|
116
|
+
- The adapter cannot find username or password in properties.authentication
|
|
117
|
+
- Verify the authentication section is configured correctly (see above)
|
|
118
|
+
|
|
119
|
+
**Error: "SOAP Envelope Error"**
|
|
120
|
+
- General SOAP wrapping failure
|
|
121
|
+
- Check adapter logs for detailed error messages
|
|
122
|
+
- Verify the XML payload is well-formed
|
|
123
|
+
|
|
124
|
+
**Existing SOAP envelope not detected:**
|
|
125
|
+
- If your workflow sends a SOAP envelope and it's being double-wrapped:
|
|
126
|
+
- Ensure the envelope uses one of these prefixes: `soapenv:`, `soap:`, or `SOAP-ENV:`
|
|
127
|
+
- The detection looks for `<soapenv:Envelope`, `<soap:Envelope`, or `<SOAP-ENV:Envelope`
|
|
128
|
+
|
|
129
|
+
**Testing SOAP wrapper:**
|
|
130
|
+
- Send a simple XML payload through the adapter
|
|
131
|
+
- Check the FULL REQUEST log to see the generated SOAP envelope
|
|
132
|
+
- Verify credentials are properly embedded in the wsse:Security header
|
|
133
|
+
- Confirm the Metaswitch API accepts the request
|
|
134
|
+
|
|
48
135
|
### Sample Properties
|
|
49
136
|
|
|
50
137
|
Sample Properties can be used to help you configure the adapter in the Itential Platform. You will need to update connectivity information such as the host, port, protocol and credentials.
|
package/adapter.js
CHANGED
|
@@ -97,7 +97,11 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
97
97
|
|
|
98
98
|
// The generic adapter functions should already be ignored (e.g. healthCheck)
|
|
99
99
|
// you can add specific methods that you do not want to be workflow functions to ignore like below
|
|
100
|
-
//
|
|
100
|
+
// Exclude SOAP utility methods from workflow functions
|
|
101
|
+
myIgnore.push('wrapBodyInSoapEnvelope');
|
|
102
|
+
myIgnore.push('getSoapNamespaces');
|
|
103
|
+
myIgnore.push('buildSoapSecurityHeader');
|
|
104
|
+
myIgnore.push('escapeXml');
|
|
101
105
|
|
|
102
106
|
return super.iapGetAdapterWorkflowFunctions(myIgnore);
|
|
103
107
|
}
|
|
@@ -645,6 +649,126 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
645
649
|
return super.iapGetAdapterInventory(callback);
|
|
646
650
|
}
|
|
647
651
|
|
|
652
|
+
/* SOAP SECURITY UTILITY METHODS */
|
|
653
|
+
/**
|
|
654
|
+
* @function wrapBodyInSoapEnvelope
|
|
655
|
+
* @summary Wraps request body in SOAP envelope with WS-Security credentials
|
|
656
|
+
*
|
|
657
|
+
* @param {string} body - The inner XML payload (without SOAP envelope)
|
|
658
|
+
* @param {string} apiType - API type (EAS, NSeries, Metaview, NWSAP) for namespace handling
|
|
659
|
+
*
|
|
660
|
+
* @returns {object} Object containing the updated SOAP payload
|
|
661
|
+
* @returns {string} returns.payload - The complete SOAP envelope with credentials
|
|
662
|
+
* @returns {Error} returns.error - Error object if parsing or serialization fails
|
|
663
|
+
*/
|
|
664
|
+
wrapBodyInSoapEnvelope(body, apiType = 'EAS') {
|
|
665
|
+
const meth = 'adapter-wrapBodyInSoapEnvelope';
|
|
666
|
+
const origin = `${this.id}-${meth}`;
|
|
667
|
+
log.trace(origin);
|
|
668
|
+
|
|
669
|
+
try {
|
|
670
|
+
// Validate body is present (before any processing)
|
|
671
|
+
if (!body || body.trim() === '') {
|
|
672
|
+
return { error: new Error('Empty payload body provided') };
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Detect if payload is already a SOAP envelope (skip wrapping)
|
|
676
|
+
const trimmedBody = body.trim();
|
|
677
|
+
if (trimmedBody.includes('<soapenv:Envelope')
|
|
678
|
+
|| trimmedBody.includes('<soap:Envelope')
|
|
679
|
+
|| trimmedBody.includes('<SOAP-ENV:Envelope')) {
|
|
680
|
+
log.debug(`${origin}: Payload already contains SOAP envelope, skipping wrap`);
|
|
681
|
+
return { payload: body, alreadyWrapped: true };
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Get credentials from adapter configuration
|
|
685
|
+
const credentials = {
|
|
686
|
+
username: this.allProps.authentication.username,
|
|
687
|
+
password: this.allProps.authentication.password
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// Validate credentials are present
|
|
691
|
+
if (!credentials.username || !credentials.password) {
|
|
692
|
+
return { error: new Error('Missing authentication credentials in adapter configuration') };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Determine namespace based on API type
|
|
696
|
+
const namespaces = this.getSoapNamespaces(apiType);
|
|
697
|
+
|
|
698
|
+
// Construct SOAP envelope with WS-Security header
|
|
699
|
+
const soapEnvelope = `<soapenv:Envelope ${namespaces}>
|
|
700
|
+
<soapenv:Header>
|
|
701
|
+
${this.buildSoapSecurityHeader(credentials)}
|
|
702
|
+
</soapenv:Header>
|
|
703
|
+
<soapenv:Body>${body}</soapenv:Body>
|
|
704
|
+
</soapenv:Envelope>`;
|
|
705
|
+
|
|
706
|
+
return { payload: soapEnvelope };
|
|
707
|
+
} catch (ex) {
|
|
708
|
+
log.error(`${origin}: Exception wrapping SOAP envelope: ${ex.message}`);
|
|
709
|
+
return { error: ex };
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* @function getSoapNamespaces
|
|
715
|
+
* @summary Returns SOAP namespace declarations for specific Metaswitch API
|
|
716
|
+
*
|
|
717
|
+
* @param {string} apiType - API type (EAS, NSeries, Metaview, NWSAP)
|
|
718
|
+
* @returns {string} Namespace declaration string
|
|
719
|
+
*/
|
|
720
|
+
// eslint-disable-next-line class-methods-use-this
|
|
721
|
+
getSoapNamespaces(apiType) {
|
|
722
|
+
const commonNamespaces = 'xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"';
|
|
723
|
+
|
|
724
|
+
// API-specific namespaces based on Metaswitch documentation
|
|
725
|
+
// These follow the 3GPP Sh interface standards
|
|
726
|
+
const apiNamespaces = {
|
|
727
|
+
EAS: 'xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"',
|
|
728
|
+
NSeries: 'xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"',
|
|
729
|
+
Metaview: 'xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"',
|
|
730
|
+
NWSAP: 'xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"'
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
return `${commonNamespaces} ${apiNamespaces[apiType] || apiNamespaces.EAS}`;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* @function buildSoapSecurityHeader
|
|
738
|
+
* @summary Builds WS-Security header with username/password credentials
|
|
739
|
+
*
|
|
740
|
+
* @param {object} credentials - Object containing username and password
|
|
741
|
+
* @returns {string} WS-Security UsernameToken header XML
|
|
742
|
+
*/
|
|
743
|
+
buildSoapSecurityHeader(credentials) {
|
|
744
|
+
// WS-Security UsernameToken pattern per OASIS standard
|
|
745
|
+
// Password type is PasswordText (plaintext over HTTPS)
|
|
746
|
+
return `<wsse:Security soapenv:mustUnderstand="1">
|
|
747
|
+
<wsse:UsernameToken>
|
|
748
|
+
<wsse:Username>${this.escapeXml(credentials.username)}</wsse:Username>
|
|
749
|
+
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">${this.escapeXml(credentials.password)}</wsse:Password>
|
|
750
|
+
</wsse:UsernameToken>
|
|
751
|
+
</wsse:Security>`;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* @function escapeXml
|
|
756
|
+
* @summary Escapes special XML characters in strings
|
|
757
|
+
*
|
|
758
|
+
* @param {string} str - String to escape
|
|
759
|
+
* @returns {string} XML-safe string
|
|
760
|
+
*/
|
|
761
|
+
// eslint-disable-next-line class-methods-use-this
|
|
762
|
+
escapeXml(str) {
|
|
763
|
+
if (!str) return '';
|
|
764
|
+
return str
|
|
765
|
+
.replace(/&/g, '&')
|
|
766
|
+
.replace(/</g, '<')
|
|
767
|
+
.replace(/>/g, '>')
|
|
768
|
+
.replace(/"/g, '"')
|
|
769
|
+
.replace(/'/g, ''');
|
|
770
|
+
}
|
|
771
|
+
|
|
648
772
|
/**
|
|
649
773
|
* @callback healthCallback
|
|
650
774
|
* @param {Object} result - the result of the get request (contains an id and a status)
|
|
@@ -703,11 +827,18 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
703
827
|
return callback(null, errorObj);
|
|
704
828
|
}
|
|
705
829
|
|
|
830
|
+
// Wrap body in SOAP envelope with credentials
|
|
831
|
+
const soapResult = this.wrapBodyInSoapEnvelope(body, 'EAS');
|
|
832
|
+
if (soapResult.error) {
|
|
833
|
+
log.error(`${origin}: SOAP envelope wrapping failed - ${soapResult.error.message}`);
|
|
834
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'SOAP Envelope Error', null, null, null, soapResult.error);
|
|
835
|
+
return callback(null, errorObj);
|
|
836
|
+
}
|
|
837
|
+
|
|
706
838
|
/* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
|
|
707
839
|
const queryParamsAvailable = {};
|
|
708
840
|
const queryParams = {};
|
|
709
841
|
const pathVars = [];
|
|
710
|
-
const bodyVars = body;
|
|
711
842
|
|
|
712
843
|
// loop in template. long callback arg name to avoid identifier conflicts
|
|
713
844
|
Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
|
|
@@ -720,7 +851,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
720
851
|
// set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders, authData, callProperties, filter, priority, event
|
|
721
852
|
// see adapter code documentation for more information on the request object's fields
|
|
722
853
|
const reqObj = {
|
|
723
|
-
payload:
|
|
854
|
+
payload: soapResult.payload,
|
|
724
855
|
uriPathVars: pathVars,
|
|
725
856
|
uriQuery: queryParams
|
|
726
857
|
};
|
|
@@ -785,11 +916,18 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
785
916
|
return callback(null, errorObj);
|
|
786
917
|
}
|
|
787
918
|
|
|
919
|
+
// Wrap body in SOAP envelope with credentials
|
|
920
|
+
const soapResult = this.wrapBodyInSoapEnvelope(body, 'NSeries');
|
|
921
|
+
if (soapResult.error) {
|
|
922
|
+
log.error(`${origin}: SOAP envelope wrapping failed - ${soapResult.error.message}`);
|
|
923
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'SOAP Envelope Error', null, null, null, soapResult.error);
|
|
924
|
+
return callback(null, errorObj);
|
|
925
|
+
}
|
|
926
|
+
|
|
788
927
|
/* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
|
|
789
928
|
const queryParamsAvailable = {};
|
|
790
929
|
const queryParams = {};
|
|
791
930
|
const pathVars = [];
|
|
792
|
-
const bodyVars = body;
|
|
793
931
|
|
|
794
932
|
// loop in template. long callback arg name to avoid identifier conflicts
|
|
795
933
|
Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
|
|
@@ -802,7 +940,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
802
940
|
// set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders, authData, callProperties, filter, priority, event
|
|
803
941
|
// see adapter code documentation for more information on the request object's fields
|
|
804
942
|
const reqObj = {
|
|
805
|
-
payload:
|
|
943
|
+
payload: soapResult.payload,
|
|
806
944
|
uriPathVars: pathVars,
|
|
807
945
|
uriQuery: queryParams
|
|
808
946
|
};
|
|
@@ -867,11 +1005,18 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
867
1005
|
return callback(null, errorObj);
|
|
868
1006
|
}
|
|
869
1007
|
|
|
1008
|
+
// Wrap body in SOAP envelope with credentials
|
|
1009
|
+
const soapResult = this.wrapBodyInSoapEnvelope(body, 'Metaview');
|
|
1010
|
+
if (soapResult.error) {
|
|
1011
|
+
log.error(`${origin}: SOAP envelope wrapping failed - ${soapResult.error.message}`);
|
|
1012
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'SOAP Envelope Error', null, null, null, soapResult.error);
|
|
1013
|
+
return callback(null, errorObj);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
870
1016
|
/* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
|
|
871
1017
|
const queryParamsAvailable = {};
|
|
872
1018
|
const queryParams = {};
|
|
873
1019
|
const pathVars = [];
|
|
874
|
-
const bodyVars = body;
|
|
875
1020
|
|
|
876
1021
|
// loop in template. long callback arg name to avoid identifier conflicts
|
|
877
1022
|
Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
|
|
@@ -884,7 +1029,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
884
1029
|
// set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders, authData, callProperties, filter, priority, event
|
|
885
1030
|
// see adapter code documentation for more information on the request object's fields
|
|
886
1031
|
const reqObj = {
|
|
887
|
-
payload:
|
|
1032
|
+
payload: soapResult.payload,
|
|
888
1033
|
uriPathVars: pathVars,
|
|
889
1034
|
uriQuery: queryParams
|
|
890
1035
|
};
|
|
@@ -949,11 +1094,18 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
949
1094
|
return callback(null, errorObj);
|
|
950
1095
|
}
|
|
951
1096
|
|
|
1097
|
+
// Wrap body in SOAP envelope with credentials
|
|
1098
|
+
const soapResult = this.wrapBodyInSoapEnvelope(body, 'NWSAP');
|
|
1099
|
+
if (soapResult.error) {
|
|
1100
|
+
log.error(`${origin}: SOAP envelope wrapping failed - ${soapResult.error.message}`);
|
|
1101
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'SOAP Envelope Error', null, null, null, soapResult.error);
|
|
1102
|
+
return callback(null, errorObj);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
952
1105
|
/* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
|
|
953
1106
|
const queryParamsAvailable = {};
|
|
954
1107
|
const queryParams = {};
|
|
955
1108
|
const pathVars = [];
|
|
956
|
-
const bodyVars = body;
|
|
957
1109
|
|
|
958
1110
|
// loop in template. long callback arg name to avoid identifier conflicts
|
|
959
1111
|
Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
|
|
@@ -966,7 +1118,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
966
1118
|
// set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders, authData, callProperties, filter, priority, event
|
|
967
1119
|
// see adapter code documentation for more information on the request object's fields
|
|
968
1120
|
const reqObj = {
|
|
969
|
-
payload:
|
|
1121
|
+
payload: soapResult.payload,
|
|
970
1122
|
uriPathVars: pathVars,
|
|
971
1123
|
uriQuery: queryParams
|
|
972
1124
|
};
|
package/package.json
CHANGED
package/report/adapterInfo.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"configLines":
|
|
4
|
-
"scriptLines":
|
|
5
|
-
"codeLines":
|
|
6
|
-
"testLines":
|
|
7
|
-
"testCases":
|
|
8
|
-
"totalCodeLines":
|
|
2
|
+
"version": "1.0.3",
|
|
3
|
+
"configLines": 4703,
|
|
4
|
+
"scriptLines": 2523,
|
|
5
|
+
"codeLines": 2682,
|
|
6
|
+
"testLines": 4365,
|
|
7
|
+
"testCases": 197,
|
|
8
|
+
"totalCodeLines": 9570,
|
|
9
9
|
"wfTasks": 29
|
|
10
10
|
}
|
|
@@ -116,12 +116,12 @@
|
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"requestBody": {
|
|
119
|
-
"description": "indeterminate body object",
|
|
120
119
|
"content": {
|
|
121
120
|
"application/json": {
|
|
122
121
|
"schema": {
|
|
123
122
|
"type": "object"
|
|
124
|
-
}
|
|
123
|
+
},
|
|
124
|
+
"example": {}
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -247,12 +247,12 @@
|
|
|
247
247
|
}
|
|
248
248
|
},
|
|
249
249
|
"requestBody": {
|
|
250
|
-
"description": "indeterminate body object",
|
|
251
250
|
"content": {
|
|
252
251
|
"application/json": {
|
|
253
252
|
"schema": {
|
|
254
253
|
"type": "object"
|
|
255
|
-
}
|
|
254
|
+
},
|
|
255
|
+
"example": {}
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
}
|
|
@@ -520,12 +520,12 @@
|
|
|
520
520
|
}
|
|
521
521
|
},
|
|
522
522
|
"requestBody": {
|
|
523
|
-
"description": "indeterminate body object",
|
|
524
523
|
"content": {
|
|
525
524
|
"application/json": {
|
|
526
525
|
"schema": {
|
|
527
526
|
"type": "object"
|
|
528
|
-
}
|
|
527
|
+
},
|
|
528
|
+
"example": {}
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
}
|
|
@@ -812,12 +812,12 @@
|
|
|
812
812
|
}
|
|
813
813
|
},
|
|
814
814
|
"requestBody": {
|
|
815
|
-
"description": "indeterminate body object",
|
|
816
815
|
"content": {
|
|
817
816
|
"application/json": {
|
|
818
817
|
"schema": {
|
|
819
818
|
"type": "object"
|
|
820
|
-
}
|
|
819
|
+
},
|
|
820
|
+
"example": {}
|
|
821
821
|
}
|
|
822
822
|
}
|
|
823
823
|
}
|
|
@@ -1474,6 +1474,364 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1474
1474
|
-----------------------------------------------------------------------
|
|
1475
1475
|
*/
|
|
1476
1476
|
|
|
1477
|
+
describe('#SOAP Wrapper Utility Methods', () => {
|
|
1478
|
+
describe('#wrapBodyInSoapEnvelope', () => {
|
|
1479
|
+
it('should have a wrapBodyInSoapEnvelope function', (done) => {
|
|
1480
|
+
try {
|
|
1481
|
+
assert.equal(true, typeof a.wrapBodyInSoapEnvelope === 'function');
|
|
1482
|
+
done();
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
log.error(`Test Failure: ${error}`);
|
|
1485
|
+
done(error);
|
|
1486
|
+
}
|
|
1487
|
+
}).timeout(attemptTimeout);
|
|
1488
|
+
|
|
1489
|
+
it('should wrap XML payload with SOAP envelope and credentials', (done) => {
|
|
1490
|
+
try {
|
|
1491
|
+
const xmlPayload = '<TestRequest><UserId>12345</UserId></TestRequest>';
|
|
1492
|
+
const result = a.wrapBodyInSoapEnvelope(xmlPayload, 'EAS');
|
|
1493
|
+
|
|
1494
|
+
assert.equal(result.error, undefined);
|
|
1495
|
+
assert.notEqual(result.payload, undefined);
|
|
1496
|
+
assert.equal(result.payload.includes('<soapenv:Envelope'), true);
|
|
1497
|
+
assert.equal(result.payload.includes('<soapenv:Header>'), true);
|
|
1498
|
+
assert.equal(result.payload.includes('<wsse:Security'), true);
|
|
1499
|
+
assert.equal(result.payload.includes('<wsse:UsernameToken>'), true);
|
|
1500
|
+
assert.equal(result.payload.includes('<soapenv:Body>'), true);
|
|
1501
|
+
assert.equal(result.payload.includes(xmlPayload), true);
|
|
1502
|
+
done();
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
log.error(`Test Failure: ${error}`);
|
|
1505
|
+
done(error);
|
|
1506
|
+
}
|
|
1507
|
+
}).timeout(attemptTimeout);
|
|
1508
|
+
|
|
1509
|
+
it('should return error for empty body', (done) => {
|
|
1510
|
+
try {
|
|
1511
|
+
const result = a.wrapBodyInSoapEnvelope('', 'EAS');
|
|
1512
|
+
|
|
1513
|
+
assert.notEqual(result.error, undefined);
|
|
1514
|
+
assert.equal(result.error.message, 'Empty payload body provided');
|
|
1515
|
+
assert.equal(result.payload, undefined);
|
|
1516
|
+
done();
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
log.error(`Test Failure: ${error}`);
|
|
1519
|
+
done(error);
|
|
1520
|
+
}
|
|
1521
|
+
}).timeout(attemptTimeout);
|
|
1522
|
+
|
|
1523
|
+
it('should return error for null body', (done) => {
|
|
1524
|
+
try {
|
|
1525
|
+
const result = a.wrapBodyInSoapEnvelope(null, 'EAS');
|
|
1526
|
+
|
|
1527
|
+
assert.notEqual(result.error, undefined);
|
|
1528
|
+
assert.equal(result.error.message, 'Empty payload body provided');
|
|
1529
|
+
done();
|
|
1530
|
+
} catch (error) {
|
|
1531
|
+
log.error(`Test Failure: ${error}`);
|
|
1532
|
+
done(error);
|
|
1533
|
+
}
|
|
1534
|
+
}).timeout(attemptTimeout);
|
|
1535
|
+
|
|
1536
|
+
it('should detect existing SOAP envelope with soapenv prefix', (done) => {
|
|
1537
|
+
try {
|
|
1538
|
+
const soapPayload = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><TestRequest/></soapenv:Body></soapenv:Envelope>';
|
|
1539
|
+
const result = a.wrapBodyInSoapEnvelope(soapPayload, 'EAS');
|
|
1540
|
+
|
|
1541
|
+
assert.equal(result.error, undefined);
|
|
1542
|
+
assert.equal(result.payload, soapPayload);
|
|
1543
|
+
assert.equal(result.alreadyWrapped, true);
|
|
1544
|
+
done();
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
log.error(`Test Failure: ${error}`);
|
|
1547
|
+
done(error);
|
|
1548
|
+
}
|
|
1549
|
+
}).timeout(attemptTimeout);
|
|
1550
|
+
|
|
1551
|
+
it('should detect existing SOAP envelope with soap prefix', (done) => {
|
|
1552
|
+
try {
|
|
1553
|
+
const soapPayload = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><TestRequest/></soap:Body></soap:Envelope>';
|
|
1554
|
+
const result = a.wrapBodyInSoapEnvelope(soapPayload, 'EAS');
|
|
1555
|
+
|
|
1556
|
+
assert.equal(result.error, undefined);
|
|
1557
|
+
assert.equal(result.payload, soapPayload);
|
|
1558
|
+
assert.equal(result.alreadyWrapped, true);
|
|
1559
|
+
done();
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
log.error(`Test Failure: ${error}`);
|
|
1562
|
+
done(error);
|
|
1563
|
+
}
|
|
1564
|
+
}).timeout(attemptTimeout);
|
|
1565
|
+
|
|
1566
|
+
it('should detect existing SOAP envelope with SOAP-ENV prefix', (done) => {
|
|
1567
|
+
try {
|
|
1568
|
+
const soapPayload = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><TestRequest/></SOAP-ENV:Body></SOAP-ENV:Envelope>';
|
|
1569
|
+
const result = a.wrapBodyInSoapEnvelope(soapPayload, 'EAS');
|
|
1570
|
+
|
|
1571
|
+
assert.equal(result.error, undefined);
|
|
1572
|
+
assert.equal(result.payload, soapPayload);
|
|
1573
|
+
assert.equal(result.alreadyWrapped, true);
|
|
1574
|
+
done();
|
|
1575
|
+
} catch (error) {
|
|
1576
|
+
log.error(`Test Failure: ${error}`);
|
|
1577
|
+
done(error);
|
|
1578
|
+
}
|
|
1579
|
+
}).timeout(attemptTimeout);
|
|
1580
|
+
|
|
1581
|
+
it('should use correct namespaces for different API types', (done) => {
|
|
1582
|
+
try {
|
|
1583
|
+
const xmlPayload = '<TestRequest/>';
|
|
1584
|
+
|
|
1585
|
+
const easResult = a.wrapBodyInSoapEnvelope(xmlPayload, 'EAS');
|
|
1586
|
+
assert.equal(easResult.payload.includes('xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"'), true);
|
|
1587
|
+
|
|
1588
|
+
const nseriesResult = a.wrapBodyInSoapEnvelope(xmlPayload, 'NSeries');
|
|
1589
|
+
assert.equal(nseriesResult.payload.includes('xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"'), true);
|
|
1590
|
+
|
|
1591
|
+
const metaviewResult = a.wrapBodyInSoapEnvelope(xmlPayload, 'Metaview');
|
|
1592
|
+
assert.equal(metaviewResult.payload.includes('xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"'), true);
|
|
1593
|
+
|
|
1594
|
+
const nwsapResult = a.wrapBodyInSoapEnvelope(xmlPayload, 'NWSAP');
|
|
1595
|
+
assert.equal(nwsapResult.payload.includes('xmlns:sh="http://www.3gpp.org/ftp/Specs/archive/29_series/29.329/schema/Sh-Data"'), true);
|
|
1596
|
+
|
|
1597
|
+
done();
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
log.error(`Test Failure: ${error}`);
|
|
1600
|
+
done(error);
|
|
1601
|
+
}
|
|
1602
|
+
}).timeout(attemptTimeout);
|
|
1603
|
+
|
|
1604
|
+
it('should include WS-Security namespaces', (done) => {
|
|
1605
|
+
try {
|
|
1606
|
+
const xmlPayload = '<TestRequest/>';
|
|
1607
|
+
const result = a.wrapBodyInSoapEnvelope(xmlPayload, 'EAS');
|
|
1608
|
+
|
|
1609
|
+
assert.equal(result.payload.includes('xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"'), true);
|
|
1610
|
+
assert.equal(result.payload.includes('xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"'), true);
|
|
1611
|
+
done();
|
|
1612
|
+
} catch (error) {
|
|
1613
|
+
log.error(`Test Failure: ${error}`);
|
|
1614
|
+
done(error);
|
|
1615
|
+
}
|
|
1616
|
+
}).timeout(attemptTimeout);
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
describe('#getSoapNamespaces', () => {
|
|
1620
|
+
it('should have a getSoapNamespaces function', (done) => {
|
|
1621
|
+
try {
|
|
1622
|
+
assert.equal(true, typeof a.getSoapNamespaces === 'function');
|
|
1623
|
+
done();
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
log.error(`Test Failure: ${error}`);
|
|
1626
|
+
done(error);
|
|
1627
|
+
}
|
|
1628
|
+
}).timeout(attemptTimeout);
|
|
1629
|
+
|
|
1630
|
+
it('should return namespaces for EAS API', (done) => {
|
|
1631
|
+
try {
|
|
1632
|
+
const namespaces = a.getSoapNamespaces('EAS');
|
|
1633
|
+
|
|
1634
|
+
assert.equal(namespaces.includes('xmlns:soapenv='), true);
|
|
1635
|
+
assert.equal(namespaces.includes('xmlns:wsse='), true);
|
|
1636
|
+
assert.equal(namespaces.includes('xmlns:wsu='), true);
|
|
1637
|
+
assert.equal(namespaces.includes('xmlns:sh='), true);
|
|
1638
|
+
done();
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
log.error(`Test Failure: ${error}`);
|
|
1641
|
+
done(error);
|
|
1642
|
+
}
|
|
1643
|
+
}).timeout(attemptTimeout);
|
|
1644
|
+
|
|
1645
|
+
it('should return namespaces for all API types', (done) => {
|
|
1646
|
+
try {
|
|
1647
|
+
const apiTypes = ['EAS', 'NSeries', 'Metaview', 'NWSAP'];
|
|
1648
|
+
|
|
1649
|
+
apiTypes.forEach((apiType) => {
|
|
1650
|
+
const namespaces = a.getSoapNamespaces(apiType);
|
|
1651
|
+
assert.notEqual(namespaces, undefined);
|
|
1652
|
+
assert.equal(namespaces.length > 0, true);
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
done();
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
log.error(`Test Failure: ${error}`);
|
|
1658
|
+
done(error);
|
|
1659
|
+
}
|
|
1660
|
+
}).timeout(attemptTimeout);
|
|
1661
|
+
|
|
1662
|
+
it('should return default namespaces for unknown API type', (done) => {
|
|
1663
|
+
try {
|
|
1664
|
+
const namespaces = a.getSoapNamespaces('UnknownAPI');
|
|
1665
|
+
|
|
1666
|
+
assert.equal(namespaces.includes('xmlns:soapenv='), true);
|
|
1667
|
+
assert.equal(namespaces.includes('xmlns:sh='), true);
|
|
1668
|
+
done();
|
|
1669
|
+
} catch (error) {
|
|
1670
|
+
log.error(`Test Failure: ${error}`);
|
|
1671
|
+
done(error);
|
|
1672
|
+
}
|
|
1673
|
+
}).timeout(attemptTimeout);
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
describe('#buildSoapSecurityHeader', () => {
|
|
1677
|
+
it('should have a buildSoapSecurityHeader function', (done) => {
|
|
1678
|
+
try {
|
|
1679
|
+
assert.equal(true, typeof a.buildSoapSecurityHeader === 'function');
|
|
1680
|
+
done();
|
|
1681
|
+
} catch (error) {
|
|
1682
|
+
log.error(`Test Failure: ${error}`);
|
|
1683
|
+
done(error);
|
|
1684
|
+
}
|
|
1685
|
+
}).timeout(attemptTimeout);
|
|
1686
|
+
|
|
1687
|
+
it('should create WS-Security header with credentials', (done) => {
|
|
1688
|
+
try {
|
|
1689
|
+
const credentials = { username: samProps.authentication.username, password: samProps.authentication.password };
|
|
1690
|
+
const header = a.buildSoapSecurityHeader(credentials);
|
|
1691
|
+
|
|
1692
|
+
assert.equal(header.includes('<wsse:Security'), true);
|
|
1693
|
+
assert.equal(header.includes('soapenv:mustUnderstand="1"'), true);
|
|
1694
|
+
assert.equal(header.includes('<wsse:UsernameToken>'), true);
|
|
1695
|
+
assert.equal(header.includes(`<wsse:Username>${samProps.authentication.username}</wsse:Username>`), true);
|
|
1696
|
+
assert.equal(header.includes('<wsse:Password'), true);
|
|
1697
|
+
assert.equal(header.includes(samProps.authentication.password), true);
|
|
1698
|
+
assert.equal(header.includes('PasswordText'), true);
|
|
1699
|
+
done();
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
log.error(`Test Failure: ${error}`);
|
|
1702
|
+
done(error);
|
|
1703
|
+
}
|
|
1704
|
+
}).timeout(attemptTimeout);
|
|
1705
|
+
|
|
1706
|
+
it('should escape special XML characters in credentials', (done) => {
|
|
1707
|
+
try {
|
|
1708
|
+
const credentials = { username: `<${samProps.authentication.username}>`, password: `&${samProps.authentication.password}"` };
|
|
1709
|
+
const header = a.buildSoapSecurityHeader(credentials);
|
|
1710
|
+
|
|
1711
|
+
assert.equal(header.includes(`<${samProps.authentication.username}>`), true);
|
|
1712
|
+
assert.equal(header.includes(`&${samProps.authentication.password}"`), true);
|
|
1713
|
+
assert.equal(header.includes(`<${samProps.authentication.username}>`), false);
|
|
1714
|
+
assert.equal(header.includes(`&${samProps.authentication.password}"`), false);
|
|
1715
|
+
done();
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
log.error(`Test Failure: ${error}`);
|
|
1718
|
+
done(error);
|
|
1719
|
+
}
|
|
1720
|
+
}).timeout(attemptTimeout);
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
describe('#escapeXml', () => {
|
|
1724
|
+
it('should have an escapeXml function', (done) => {
|
|
1725
|
+
try {
|
|
1726
|
+
assert.equal(true, typeof a.escapeXml === 'function');
|
|
1727
|
+
done();
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
log.error(`Test Failure: ${error}`);
|
|
1730
|
+
done(error);
|
|
1731
|
+
}
|
|
1732
|
+
}).timeout(attemptTimeout);
|
|
1733
|
+
|
|
1734
|
+
it('should escape ampersand', (done) => {
|
|
1735
|
+
try {
|
|
1736
|
+
const result = a.escapeXml('test & value');
|
|
1737
|
+
assert.equal(result, 'test & value');
|
|
1738
|
+
done();
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
log.error(`Test Failure: ${error}`);
|
|
1741
|
+
done(error);
|
|
1742
|
+
}
|
|
1743
|
+
}).timeout(attemptTimeout);
|
|
1744
|
+
|
|
1745
|
+
it('should escape less than', (done) => {
|
|
1746
|
+
try {
|
|
1747
|
+
const result = a.escapeXml('test < value');
|
|
1748
|
+
assert.equal(result, 'test < value');
|
|
1749
|
+
done();
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
log.error(`Test Failure: ${error}`);
|
|
1752
|
+
done(error);
|
|
1753
|
+
}
|
|
1754
|
+
}).timeout(attemptTimeout);
|
|
1755
|
+
|
|
1756
|
+
it('should escape greater than', (done) => {
|
|
1757
|
+
try {
|
|
1758
|
+
const result = a.escapeXml('test > value');
|
|
1759
|
+
assert.equal(result, 'test > value');
|
|
1760
|
+
done();
|
|
1761
|
+
} catch (error) {
|
|
1762
|
+
log.error(`Test Failure: ${error}`);
|
|
1763
|
+
done(error);
|
|
1764
|
+
}
|
|
1765
|
+
}).timeout(attemptTimeout);
|
|
1766
|
+
|
|
1767
|
+
it('should escape double quote', (done) => {
|
|
1768
|
+
try {
|
|
1769
|
+
const result = a.escapeXml('test "value"');
|
|
1770
|
+
assert.equal(result, 'test "value"');
|
|
1771
|
+
done();
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
log.error(`Test Failure: ${error}`);
|
|
1774
|
+
done(error);
|
|
1775
|
+
}
|
|
1776
|
+
}).timeout(attemptTimeout);
|
|
1777
|
+
|
|
1778
|
+
it('should escape single quote', (done) => {
|
|
1779
|
+
try {
|
|
1780
|
+
const result = a.escapeXml("test 'value'");
|
|
1781
|
+
assert.equal(result, 'test 'value'');
|
|
1782
|
+
done();
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
log.error(`Test Failure: ${error}`);
|
|
1785
|
+
done(error);
|
|
1786
|
+
}
|
|
1787
|
+
}).timeout(attemptTimeout);
|
|
1788
|
+
|
|
1789
|
+
it('should escape all special characters together', (done) => {
|
|
1790
|
+
try {
|
|
1791
|
+
const result = a.escapeXml('<tag attr="value" other=\'test\'>content & more</tag>');
|
|
1792
|
+
assert.equal(result, '<tag attr="value" other='test'>content & more</tag>');
|
|
1793
|
+
done();
|
|
1794
|
+
} catch (error) {
|
|
1795
|
+
log.error(`Test Failure: ${error}`);
|
|
1796
|
+
done(error);
|
|
1797
|
+
}
|
|
1798
|
+
}).timeout(attemptTimeout);
|
|
1799
|
+
|
|
1800
|
+
it('should return empty string for null input', (done) => {
|
|
1801
|
+
try {
|
|
1802
|
+
const result = a.escapeXml(null);
|
|
1803
|
+
assert.equal(result, '');
|
|
1804
|
+
done();
|
|
1805
|
+
} catch (error) {
|
|
1806
|
+
log.error(`Test Failure: ${error}`);
|
|
1807
|
+
done(error);
|
|
1808
|
+
}
|
|
1809
|
+
}).timeout(attemptTimeout);
|
|
1810
|
+
|
|
1811
|
+
it('should return empty string for undefined input', (done) => {
|
|
1812
|
+
try {
|
|
1813
|
+
const result = a.escapeXml(undefined);
|
|
1814
|
+
assert.equal(result, '');
|
|
1815
|
+
done();
|
|
1816
|
+
} catch (error) {
|
|
1817
|
+
log.error(`Test Failure: ${error}`);
|
|
1818
|
+
done(error);
|
|
1819
|
+
}
|
|
1820
|
+
}).timeout(attemptTimeout);
|
|
1821
|
+
|
|
1822
|
+
it('should not modify strings without special characters', (done) => {
|
|
1823
|
+
try {
|
|
1824
|
+
const result = a.escapeXml('normaltext123');
|
|
1825
|
+
assert.equal(result, 'normaltext123');
|
|
1826
|
+
done();
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
log.error(`Test Failure: ${error}`);
|
|
1829
|
+
done(error);
|
|
1830
|
+
}
|
|
1831
|
+
}).timeout(attemptTimeout);
|
|
1832
|
+
});
|
|
1833
|
+
});
|
|
1834
|
+
|
|
1477
1835
|
describe('#postMetaSphereEAS - errors', () => {
|
|
1478
1836
|
it('should have a postMetaSphereEAS function', (done) => {
|
|
1479
1837
|
try {
|