Skip to content

Commit d6f0d46

Browse files
committed
Refactor to not store string version of secret key longer than necessary
1 parent 072a5b7 commit d6f0d46

File tree

4 files changed

+101
-67
lines changed

4 files changed

+101
-67
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public License,
3+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4+
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5+
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6+
*
7+
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8+
* graphic logo is a trademark of OpenMRS Inc.
9+
*/
10+
package org.openmrs.module.smartonfhir.util;
11+
12+
import java.io.File;
13+
import java.io.FileNotFoundException;
14+
import java.io.InputStream;
15+
import java.util.Base64;
16+
17+
import com.fasterxml.jackson.databind.ObjectMapper;
18+
import lombok.SneakyThrows;
19+
import lombok.extern.slf4j.Slf4j;
20+
import org.openmrs.module.smartonfhir.web.SmartSecretKey;
21+
import org.openmrs.util.OpenmrsUtil;
22+
import org.springframework.core.io.Resource;
23+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
24+
25+
@Slf4j
26+
public class SmartSecretKeyHolder {
27+
28+
private static final ObjectMapper objectMapper = new ObjectMapper();
29+
30+
private static volatile byte[] secretKey = null;
31+
32+
public static byte[] getSecretKey() {
33+
if (secretKey == null) {
34+
synchronized (SmartSecretKeyHolder.class) {
35+
if (secretKey == null) {
36+
loadSecretKey();
37+
}
38+
}
39+
}
40+
41+
return secretKey;
42+
}
43+
44+
@SneakyThrows
45+
private static void loadSecretKey() {
46+
final Base64.Decoder decoder = Base64.getDecoder();
47+
final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
48+
49+
Resource resource = resolver
50+
.getResource(OpenmrsUtil.getDirectoryInApplicationDataDirectory("config").getAbsolutePath() + File.separator
51+
+ "smart-secret-key.json");
52+
53+
if (resource != null && resource.isReadable()) {
54+
try (InputStream secretKeyStream = resource.getInputStream()) {
55+
secretKey = decoder
56+
.decode(objectMapper.readValue(secretKeyStream, SmartSecretKey.class).getSmartSharedSecretKey());
57+
return;
58+
}
59+
catch (FileNotFoundException e) {
60+
log.error("Could not load file [{}]", resource.getFilename(), e);
61+
}
62+
}
63+
64+
resource = resolver.getResource("classpath:smart-secret-key.json");
65+
66+
if (resource != null && resource.isReadable()) {
67+
try (InputStream secretKeyStream = resource.getInputStream()) {
68+
secretKey = decoder
69+
.decode(objectMapper.readValue(secretKeyStream, SmartSecretKey.class).getSmartSharedSecretKey());
70+
}
71+
}
72+
}
73+
}

omod/src/main/java/org/openmrs/module/smartonfhir/web/filter/AuthenticationByPassFilter.java

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,24 @@
1919
import javax.servlet.http.HttpServletResponse;
2020
import javax.servlet.http.HttpSession;
2121

22-
import java.io.File;
2322
import java.io.IOException;
24-
import java.io.InputStream;
2523
import java.util.ArrayList;
2624
import java.util.Arrays;
2725
import java.util.List;
2826
import java.util.regex.Matcher;
2927
import java.util.regex.Pattern;
3028
import java.util.stream.Collectors;
3129

32-
import com.fasterxml.jackson.databind.ObjectMapper;
3330
import lombok.extern.slf4j.Slf4j;
3431
import org.apache.commons.lang.StringUtils;
35-
import org.keycloak.common.util.Base64;
3632
import org.keycloak.jose.jws.JWSInput;
3733
import org.keycloak.jose.jws.JWSInputException;
3834
import org.keycloak.jose.jws.crypto.HMACProvider;
3935
import org.keycloak.representations.JsonWebToken;
4036
import org.openmrs.api.context.Context;
4137
import org.openmrs.api.context.ContextAuthenticationException;
42-
import org.openmrs.module.smartonfhir.web.SmartSecretKey;
38+
import org.openmrs.module.smartonfhir.util.SmartSecretKeyHolder;
4339
import org.openmrs.module.smartonfhir.web.smart.SmartTokenCredentials;
44-
import org.openmrs.util.OpenmrsUtil;
45-
import org.springframework.core.io.Resource;
46-
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
4740

4841
@Slf4j
4942
public class AuthenticationByPassFilter implements Filter {
@@ -54,10 +47,6 @@ public class AuthenticationByPassFilter implements Filter {
5447

5548
private static final Pattern KEY_PARAM = Pattern.compile("^key=([^&]*)(?:&|$)");
5649

57-
private static final ObjectMapper objectMapper = new ObjectMapper();
58-
59-
private SmartSecretKey smartSecretKey;
60-
6150
private List<String> validUrls = new ArrayList<>(0);
6251

6352
@Override
@@ -154,7 +143,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
154143
final String username;
155144
try {
156145
JWSInput jwsInput = new JWSInput(userToken);
157-
if (!HMACProvider.verify(jwsInput, Base64.decode(getSecretKey()))) {
146+
if (!HMACProvider.verify(jwsInput, SmartSecretKeyHolder.getSecretKey())) {
158147
log.error("Error validating user token");
159148
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Not authenticated");
160149
return;
@@ -187,22 +176,6 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
187176
filterChain.doFilter(request, response);
188177
}
189178

190-
private String getSecretKey() throws IOException {
191-
final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
192-
Resource resource = resolver.getResource(
193-
OpenmrsUtil.getDirectoryInApplicationDataDirectory("config") + File.separator + "smart-secret-key.json");
194-
if (resource != null) {
195-
resource = resolver.getResource("classpath:smart-secret-key.json");
196-
197-
InputStream secretKeyStream = resource.getInputStream();
198-
199-
smartSecretKey = new SmartSecretKey();
200-
smartSecretKey = objectMapper.readValue(secretKeyStream, SmartSecretKey.class);
201-
}
202-
203-
return smartSecretKey.getSmartSharedSecretKey();
204-
}
205-
206179
@Override
207180
public void destroy() {
208181
}

omod/src/main/java/org/openmrs/module/smartonfhir/web/servlet/SmartConfigServlet.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@
1515
import javax.servlet.http.HttpServletResponse;
1616

1717
import java.io.File;
18+
import java.io.FileNotFoundException;
1819
import java.io.IOException;
1920
import java.io.InputStream;
2021

2122
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import lombok.extern.slf4j.Slf4j;
2224
import org.openmrs.module.smartonfhir.web.KeycloakConfig;
2325
import org.openmrs.module.smartonfhir.web.SmartConformance;
2426
import org.openmrs.util.OpenmrsUtil;
2527
import org.springframework.core.io.Resource;
2628
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
2729

30+
@Slf4j
2831
public class SmartConfigServlet extends HttpServlet {
2932

3033
private static final ObjectMapper objectMapper = new ObjectMapper();
@@ -60,19 +63,31 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce
6063
res.setContentType("application/json");
6164
res.setCharacterEncoding("UTF-8");
6265
res.setStatus(200);
63-
objectMapper.writerWithType(SmartConformance.class).writeValue(res.getWriter(), smartConformance);
66+
objectMapper.writerFor(SmartConformance.class).writeValue(res.getWriter(), smartConformance);
6467
}
6568

6669
private KeycloakConfig getKeycloakConfig() throws IOException {
6770
final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
68-
Resource resource = resolver.getResource(
69-
OpenmrsUtil.getDirectoryInApplicationDataDirectory("config") + File.separator + "smart-keycloak.json");
70-
if (resource != null) {
71-
resource = resolver.getResource("classpath:smart-keycloak.json");
72-
InputStream secretKeyStream = resource.getInputStream();
73-
74-
keycloakConfig = new KeycloakConfig();
75-
keycloakConfig = objectMapper.readValue(secretKeyStream, KeycloakConfig.class);
71+
Resource resource = resolver
72+
.getResource(OpenmrsUtil.getDirectoryInApplicationDataDirectory("config").getAbsolutePath() + File.separator
73+
+ "smart-keycloak.json");
74+
75+
if (resource != null && resource.isReadable()) {
76+
try (InputStream keycloakConfigStream = resource.getInputStream()) {
77+
keycloakConfig = objectMapper.readValue(keycloakConfigStream, KeycloakConfig.class);
78+
return keycloakConfig;
79+
}
80+
catch (FileNotFoundException e) {
81+
log.error("Could not load file [{}]", resource.getFilename(), e);
82+
}
83+
}
84+
85+
resource = resolver.getResource("classpath:smart-keycloak.json");
86+
87+
if (resource != null && resource.isReadable()) {
88+
try (InputStream keycloakConfigStream = resource.getInputStream()) {
89+
keycloakConfig = objectMapper.readValue(keycloakConfigStream, KeycloakConfig.class);
90+
}
7691
}
7792

7893
return keycloakConfig;

omod/src/main/java/org/openmrs/module/smartonfhir/web/servlet/SmartPatientSelected.java

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,22 @@
1414
import javax.servlet.http.HttpServletRequest;
1515
import javax.servlet.http.HttpServletResponse;
1616

17-
import java.io.File;
1817
import java.io.IOException;
19-
import java.io.InputStream;
2018
import java.net.URLDecoder;
2119
import java.net.URLEncoder;
2220
import java.nio.charset.StandardCharsets;
23-
import java.util.Base64;
2421

25-
import com.fasterxml.jackson.databind.ObjectMapper;
2622
import org.keycloak.crypto.Algorithm;
2723
import org.keycloak.crypto.JavaAlgorithm;
2824
import org.keycloak.crypto.KeyWrapper;
2925
import org.keycloak.crypto.MacSignatureSignerContext;
3026
import org.keycloak.crypto.SignatureSignerContext;
3127
import org.keycloak.jose.jws.JWSBuilder;
3228
import org.keycloak.representations.JsonWebToken;
33-
import org.openmrs.module.smartonfhir.web.SmartSecretKey;
34-
import org.openmrs.util.OpenmrsUtil;
35-
import org.springframework.core.io.Resource;
36-
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
29+
import org.openmrs.module.smartonfhir.util.SmartSecretKeyHolder;
3730

3831
public class SmartPatientSelected extends HttpServlet {
3932

40-
private static final ObjectMapper objectMapper = new ObjectMapper();
41-
42-
private SmartSecretKey smartSecretKey;
43-
4433
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
4534
String token = req.getParameter("token");
4635
String patientId = req.getParameter("patientId");
@@ -52,7 +41,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce
5241
JsonWebToken tokenSentBack = new JsonWebToken();
5342
tokenSentBack.setOtherClaims("patient", patientId);
5443

55-
SecretKeySpec hmacSecretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(getSecretKey()), JavaAlgorithm.HS256);
44+
SecretKeySpec hmacSecretKeySpec = new SecretKeySpec(SmartSecretKeyHolder.getSecretKey(), JavaAlgorithm.HS256);
5645
KeyWrapper keyWrapper = new KeyWrapper();
5746
keyWrapper.setAlgorithm(Algorithm.HS256);
5847
keyWrapper.setSecretKey(hmacSecretKeySpec);
@@ -64,20 +53,4 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce
6453
String decodedUrl = URLDecoder.decode(token, StandardCharsets.UTF_8.name());
6554
res.sendRedirect(decodedUrl.replace("{APP_TOKEN}", encodedToken));
6655
}
67-
68-
private String getSecretKey() throws IOException {
69-
final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
70-
Resource resource = resolver.getResource(
71-
OpenmrsUtil.getDirectoryInApplicationDataDirectory("config") + File.separator + "smart-secret-key.json");
72-
if (resource != null) {
73-
resource = resolver.getResource("classpath:smart-secret-key.json");
74-
75-
InputStream secretKeyStream = resource.getInputStream();
76-
77-
smartSecretKey = new SmartSecretKey();
78-
smartSecretKey = objectMapper.readValue(secretKeyStream, SmartSecretKey.class);
79-
}
80-
81-
return smartSecretKey.getSmartSharedSecretKey();
82-
}
8356
}

0 commit comments

Comments
 (0)