Skip to content

Commit f696c79

Browse files
authored
Call into $bulkdata-status passing in the encoded job number (#2129)
Signed-off-by: Lee Surprenant <[email protected]>
1 parent 109c1ef commit f696c79

File tree

7 files changed

+39
-27
lines changed

7 files changed

+39
-27
lines changed

docs/src/pages/guides/FHIRServerUsersGuide.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2256,7 +2256,7 @@ must restart the server for that change to take effect.
22562256
|`fhirServer/bulkdata/core/cos/requestTimeout`|N|N|
22572257
|`fhirServer/bulkdata/core/cos/socketTimeout`|N|N|
22582258
|`fhirServer/bulkdata/core/cos/useServerTruststore`|Y|Y|
2259-
|`fhirServer/bulkdata/core/batchIdEncryptionKey`|N|N|
2259+
|`fhirServer/bulkdata/core/batchIdEncryptionKey`|Y|N|
22602260
|`fhirServer/bulkdata/core/pageSize`|Y|Y|
22612261
|`fhirServer/bulkdata/core/maxPartitions`|Y|Y|
22622262
|`fhirServer/bulkdata/core/maxInputs`|Y|Y|

operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/StatusOperation.java

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ protected Parameters doInvoke(FHIROperationContext operationContext, Class<? ext
5555
if (logicalId == null && versionId == null && resourceType == null) {
5656
String method = (String) operationContext.getProperty(FHIROperationContext.PROPNAME_METHOD_TYPE);
5757
if ("DELETE".equalsIgnoreCase(method)) {
58-
// Assume GET or POST
5958
String job = export.checkAndValidateJob(parameters);
6059
// For now, we're going to execute the status update, and check.
6160
// If Base, Export Status (Else Invalid)

operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/client/BulkDataClient.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ public class BulkDataClient {
7878

7979
private static final HttpWrapper wrapper = new HttpWrapper();
8080

81-
private static final JobIdEncodingTransformer transformer = new JobIdEncodingTransformer();
82-
8381
private static final BulkDataExportUtil export = new BulkDataExportUtil();
8482

8583
// @formatter:off
@@ -270,7 +268,7 @@ public String submitExport(Instant since, List<String> types, ExportType exportT
270268
}
271269
cli.close();
272270

273-
return baseUri + "/$bulkdata-status?job=" + transformer.endcodeJobId(jobId);
271+
return baseUri + "/$bulkdata-status?job=" + JobIdEncodingTransformer.getInstance().encodeJobId(jobId);
274272
}
275273

276274
/**
@@ -829,7 +827,7 @@ public String submitImport(String inputFormat, String inputSource, List<Input> i
829827
}
830828
cli.close();
831829

832-
return baseUri + "/$bulkdata-status?job=" + transformer.endcodeJobId(jobId);
830+
return baseUri + "/$bulkdata-status?job=" + JobIdEncodingTransformer.getInstance().encodeJobId(jobId);
833831
}
834832

835833
/**

operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/impl/AbstractSystemConfigurationImpl.java

-5
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ public abstract class AbstractSystemConfigurationImpl implements ConfigurationAd
7575
private static final int coreFileResourceCountThreshold = defaultCoreFileResourceCountThreshold();
7676
private static final int coreFileWriteTriggerSize = defaultCoreFileWriteTriggerSize();
7777
private static final long coreFileSizeThreshold = defaultCoreFileSizeThreshold();
78-
private static final String coreBatchIdEncryptionKey = defaultCoreBatchIdEncryptionKey();
7978
private static final int coreMaxParititions = defaultCoreMaxParititions();
8079
private static final int inputLimits = defaultInputLimits();
8180

@@ -95,10 +94,6 @@ private static final int defaultCoreMaxParititions() {
9594

9695
@Override
9796
public String getCoreBatchIdEncryptionKey() {
98-
return coreBatchIdEncryptionKey;
99-
}
100-
101-
private static final String defaultCoreBatchIdEncryptionKey() {
10297
return FHIRConfigHelper.getStringProperty("fhirServer/bulkdata/core/batchIdEncryptionKey", null);
10398
}
10499

operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/model/transformer/JobIdEncodingTransformer.java

+33-11
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
import java.security.MessageDigest;
1111
import java.util.Arrays;
1212
import java.util.Base64;
13+
import java.util.concurrent.ConcurrentHashMap;
14+
import java.util.concurrent.ConcurrentMap;
1315
import java.util.logging.Level;
1416
import java.util.logging.Logger;
1517

1618
import javax.crypto.Cipher;
1719
import javax.crypto.spec.SecretKeySpec;
1820

21+
import com.ibm.fhir.config.FHIRRequestContext;
1922
import com.ibm.fhir.operation.bulkdata.config.ConfigurationFactory;
2023

2124
/**
@@ -26,18 +29,32 @@ public class JobIdEncodingTransformer {
2629
private static final String CLASSNAME = JobIdEncodingTransformer.class.getName();
2730
private static final Logger logger = Logger.getLogger(CLASSNAME);
2831

29-
// Encryption key used for JavaBatch Job ID
30-
private static final SecretKeySpec BATCHJOBID_ENCRYPTION_KEY = getJobIdEncryptionKey();
32+
// Tenant Encryption key used for JavaBatch Job ID
33+
private static ConcurrentMap<String, SecretKeySpec> KEY_MAP = new ConcurrentHashMap<>();
3134

32-
public JobIdEncodingTransformer() {
35+
private static JobIdEncodingTransformer transformer = null;
36+
37+
private JobIdEncodingTransformer() {
3338
// No Operation
3439
}
3540

41+
/**
42+
* get the instance
43+
*
44+
* @return
45+
*/
46+
public static JobIdEncodingTransformer getInstance() {
47+
if (transformer == null) {
48+
transformer = new JobIdEncodingTransformer();
49+
}
50+
return transformer;
51+
}
52+
3653
/*
37-
* gets the server-wide specific encryption key.
54+
* gets the tenant specific encryption key.
3855
* @return
3956
*/
40-
private static SecretKeySpec getJobIdEncryptionKey() {
57+
private SecretKeySpec getJobIdEncryptionKey() {
4158
String encryptionKey = ConfigurationFactory.getInstance().getCoreBatchIdEncryptionKey();
4259
SecretKeySpec secretKey = null;
4360

@@ -65,15 +82,17 @@ private static SecretKeySpec getJobIdEncryptionKey() {
6582
* @param jobId
6683
* @return
6784
*/
68-
public String endcodeJobId(String jobId) {
85+
public String encodeJobId(String jobId) {
86+
String tenantId = FHIRRequestContext.get().getTenantId();
87+
SecretKeySpec key = KEY_MAP.computeIfAbsent(tenantId, k -> getJobIdEncryptionKey());
6988
// Encrypt and UrlEncode the batch job id.
70-
if (BATCHJOBID_ENCRYPTION_KEY == null) {
89+
if (key == null) {
7190
return jobId;
7291
} else {
7392
try {
7493
// Use light weight encryption without salt to simplify both the encryption/decryption and also config.
7594
Cipher cp = Cipher.getInstance("AES/ECB/PKCS5Padding");
76-
cp.init(Cipher.ENCRYPT_MODE, BATCHJOBID_ENCRYPTION_KEY);
95+
cp.init(Cipher.ENCRYPT_MODE, key);
7796

7897
// Encrypt the job id, base64-encode it, and replace all `/` chars with the less problematic `_` char
7998
String encodedJobId = Base64.getEncoder().withoutPadding().encodeToString(cp.doFinal(jobId.getBytes("UTF-8"))).replaceAll("/", "_");
@@ -88,20 +107,23 @@ public String endcodeJobId(String jobId) {
88107
/**
89108
* decodes the job id.
90109
*
91-
* @implNote note a Cipher is used here, however it is not used to encrypt sensitive information rather encode the jobId.
110+
* @implNote note a Cipher is used here, however it is not used to encrypt sensitive information rather encode the
111+
* jobId.
92112
*
93113
* @param encodedJobId
94114
* @return
95115
*/
96116
public String decodeJobId(String encodedJobId) {
117+
String tenantId = FHIRRequestContext.get().getTenantId();
118+
SecretKeySpec key = KEY_MAP.computeIfAbsent(tenantId, k -> getJobIdEncryptionKey());
97119
// Decrypt to get the batch job id.
98-
if (BATCHJOBID_ENCRYPTION_KEY == null) {
120+
if (key == null) {
99121
return encodedJobId;
100122
} else {
101123
try {
102124
// Use light weight encryption without salt to simplify both the encryption/decryption and also config.
103125
Cipher cp = Cipher.getInstance("AES/ECB/PKCS5PADDING");
104-
cp.init(Cipher.DECRYPT_MODE, BATCHJOBID_ENCRYPTION_KEY);
126+
cp.init(Cipher.DECRYPT_MODE, key);
105127
// The encrypted job id has already been urldecoded by liberty runtime before reaching this function,
106128
// so, we don't do urldecode here.)
107129
return new String(cp.doFinal(Base64.getDecoder().decode(encodedJobId.replaceAll("_", "/"))), "UTF-8");

operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtil.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
* BulkData Util captures common methods
3939
*/
4040
public class BulkDataExportUtil {
41-
42-
private static JobIdEncodingTransformer transformer = new JobIdEncodingTransformer();
4341
private static Set<String> RESOURCE_TYPES = ModelSupport.getResourceTypes(false).stream()
4442
.map(m -> m.getSimpleName())
4543
.collect(Collectors.toSet());
@@ -296,7 +294,7 @@ public String checkAndValidateJob(Parameters parameters) throws FHIROperationExc
296294
for (Parameters.Parameter parameter : parameters.getParameter()) {
297295
if (OperationConstants.PARAM_JOB.equals(parameter.getName().getValue())
298296
&& parameter.getValue() != null && parameter.getValue().is(com.ibm.fhir.model.type.String.class)) {
299-
String job = transformer.decodeJobId(parameter.getValue().as(com.ibm.fhir.model.type.String.class).getValue());
297+
String job = JobIdEncodingTransformer.getInstance().decodeJobId(parameter.getValue().as(com.ibm.fhir.model.type.String.class).getValue());
300298

301299
// The job is never going to be empty or null as STRING is never empty at this point.
302300
if (job.contains("/") || job.contains("?")) {

operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/model/transformer/JobIdEncodingTransformerTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public void clearThreadLocal() {
5353
@Test
5454
public void testTransformerRoundTrip() throws Exception {
5555
// Using the legacy implementation for the configuration the encode/decode uses change-password
56-
final JobIdEncodingTransformer transformer = new JobIdEncodingTransformer();
56+
final JobIdEncodingTransformer transformer = JobIdEncodingTransformer.getInstance();
5757

5858
String jobId = transformer.decodeJobId("1");
5959
assertNotNull(jobId);
@@ -65,7 +65,7 @@ public void testTransformerRoundTrip() throws Exception {
6565
for (int i = 0; i < 2000; i++) {
6666
jobId = String.valueOf(i);
6767

68-
String encodedJobId = transformer.endcodeJobId(jobId);
68+
String encodedJobId = transformer.encodeJobId(jobId);
6969
assertNotNull(encodedJobId);
7070
assertFalse(encodedJobId.equals(jobId));
7171
assertFalse(encodedJobId.startsWith("/"));

0 commit comments

Comments
 (0)