- refactor and tests

This commit is contained in:
nathaniel.doef 2022-07-12 10:18:27 -04:00
parent a85b0aece4
commit a5e4d488c4
27 changed files with 329 additions and 114 deletions

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.VersionUtil; import ca.uhn.fhir.util.VersionUtil;

View File

@ -26,7 +26,7 @@ import java.util.Optional;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
public interface IRestfulClientFactory { public interface IRestfulClientFactory {

View File

@ -1,15 +0,0 @@
package ca.uhn.fhir.rest.https;
public class KeyStoreInfo extends StoreInfo {
private final char[] myKeyPass;
public KeyStoreInfo(String theFilePath, String theStorePass, String theKeyPass, String theAlias) {
super(theFilePath, theStorePass, theAlias);
this.myKeyPass = toCharArray(theKeyPass);
}
public char[] getKeyPass() {
return myKeyPass;
}
}

View File

@ -1,31 +0,0 @@
package ca.uhn.fhir.rest.https;
import ca.uhn.fhir.i18n.Msg;
import java.util.Arrays;
import java.util.List;
public enum KeyStoreType {
PKCS12("p12", "pfx"),
JKS("jks");
private List<String> myFileExtensions;
KeyStoreType(String... theFileExtensions){
myFileExtensions = Arrays.asList(theFileExtensions);
}
public List<String> getFileExtensions() {
return myFileExtensions;
}
public static KeyStoreType fromFileExtension(String theFileExtension) {
for(KeyStoreType type : KeyStoreType.values()){
if(type.getFileExtensions().contains(theFileExtension.toLowerCase())){
return type;
}
}
throw new IllegalArgumentException(Msg.code(2106)+"Invalid KeyStore Type");
}
}

View File

@ -1,17 +0,0 @@
package ca.uhn.fhir.rest.https;
public enum PathType {
FILE("file://"),
RESOURCE("classpath:");
private String myPrefix;
PathType(String thePrefix) {
myPrefix = thePrefix;
}
public String getPrefix(){
return myPrefix;
}
}

View File

@ -1,22 +0,0 @@
package ca.uhn.fhir.rest.https;
import java.util.Optional;
public class TlsAuthentication {
private final Optional<KeyStoreInfo> myKeyStoreInfo;
private final Optional<TrustStoreInfo> myTrustStoreInfo;
public TlsAuthentication(Optional<KeyStoreInfo> theKeyStoreInfo, Optional<TrustStoreInfo> theTrustStoreInfo) {
myKeyStoreInfo = theKeyStoreInfo;
myTrustStoreInfo = theTrustStoreInfo;
}
public Optional<KeyStoreInfo> getKeyStoreInfo() {
return myKeyStoreInfo;
}
public Optional<TrustStoreInfo> getTrustStoreInfo() {
return myTrustStoreInfo;
}
}

View File

@ -1,9 +0,0 @@
package ca.uhn.fhir.rest.https;
public class TrustStoreInfo extends StoreInfo{
public TrustStoreInfo(String theFilePath, String theStorePass, String theAlias) {
super(theFilePath, theStorePass, theAlias);
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.tls;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class KeyStoreInfo extends StoreInfo {
private final char[] myKeyPass;
public KeyStoreInfo(String theFilePath, String theStorePass, String theKeyPass, String theAlias) {
super(theFilePath, theStorePass, theAlias);
this.myKeyPass = toCharArray(theKeyPass);
}
public char[] getKeyPass() {
return myKeyPass;
}
}

View File

@ -0,0 +1,51 @@
package ca.uhn.fhir.tls;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import java.util.Arrays;
import java.util.List;
public enum KeyStoreType {
PKCS12("p12", "pfx"),
JKS("jks");
private List<String> myFileExtensions;
KeyStoreType(String... theFileExtensions){
myFileExtensions = Arrays.asList(theFileExtensions);
}
public List<String> getFileExtensions() {
return myFileExtensions;
}
public static KeyStoreType fromFileExtension(String theFileExtension) {
for(KeyStoreType type : KeyStoreType.values()){
if(type.getFileExtensions().contains(theFileExtension.toLowerCase())){
return type;
}
}
throw new IllegalArgumentException(Msg.code(2106)+"Invalid KeyStore Type");
}
}

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.tls;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public enum PathType {
FILE("file://"),
RESOURCE("classpath:");
private String myPrefix;
PathType(String thePrefix) {
myPrefix = thePrefix;
}
public String getPrefix(){
return myPrefix;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.https; package ca.uhn.fhir.tls;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;

View File

@ -0,0 +1,42 @@
package ca.uhn.fhir.tls;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.Optional;
public class TlsAuthentication {
private final Optional<KeyStoreInfo> myKeyStoreInfo;
private final Optional<TrustStoreInfo> myTrustStoreInfo;
public TlsAuthentication(Optional<KeyStoreInfo> theKeyStoreInfo, Optional<TrustStoreInfo> theTrustStoreInfo) {
myKeyStoreInfo = theKeyStoreInfo;
myTrustStoreInfo = theTrustStoreInfo;
}
public Optional<KeyStoreInfo> getKeyStoreInfo() {
return myKeyStoreInfo;
}
public Optional<TrustStoreInfo> getTrustStoreInfo() {
return myTrustStoreInfo;
}
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.tls;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class TrustStoreInfo extends StoreInfo{
public TrustStoreInfo(String theFilePath, String theStorePass, String theAlias) {
super(theFilePath, theStorePass, theAlias);
}
}

View File

@ -26,9 +26,9 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.https.KeyStoreInfo; import ca.uhn.fhir.tls.KeyStoreInfo;
import ca.uhn.fhir.rest.https.TrustStoreInfo; import ca.uhn.fhir.tls.TrustStoreInfo;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@ -5,9 +5,9 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.https.TlsAuthenticationSvc; import ca.uhn.fhir.rest.client.tls.TlsAuthenticationSvc;
import ca.uhn.fhir.rest.https.TrustStoreInfo; import ca.uhn.fhir.tls.TrustStoreInfo;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;

View File

@ -25,8 +25,8 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.https.TlsAuthenticationSvc; import ca.uhn.fhir.rest.client.tls.TlsAuthenticationSvc;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;

View File

@ -23,7 +23,7 @@ import ca.uhn.fhir.i18n.Msg;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -1,6 +1,11 @@
package ca.uhn.fhir.rest.https; package ca.uhn.fhir.rest.client.tls;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.tls.KeyStoreInfo;
import ca.uhn.fhir.tls.PathType;
import ca.uhn.fhir.tls.StoreInfo;
import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.tls.TrustStoreInfo;
import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.conn.ssl.TrustSelfSignedStrategy;

View File

@ -0,0 +1,110 @@
package ca.uhn.fhir.rest.client.tls;
import ca.uhn.fhir.tls.KeyStoreInfo;
import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.tls.TrustStoreInfo;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TlsAuthenticationSvcTest {
private KeyStoreInfo myServerKeyStoreInfo;
private TrustStoreInfo myServerTrustStoreInfo;
private TlsAuthentication myServerTlsAuthentication;
private KeyStoreInfo myClientKeyStoreInfo;
private TrustStoreInfo myClientTrustStoreInfo;
private TlsAuthentication myClientTlsAuthentication;
@BeforeEach
public void beforeEach(){
myServerKeyStoreInfo = new KeyStoreInfo("classpath:/server-keystore.p12", "changeit", "changeit", "server");
myServerTrustStoreInfo = new TrustStoreInfo("classpath:/server-truststore.p12", "changeit", "client");
myServerTlsAuthentication = new TlsAuthentication(Optional.of(myServerKeyStoreInfo), Optional.of(myServerTrustStoreInfo));
myClientKeyStoreInfo = new KeyStoreInfo("classpath:/client-keystore.p12", "changeit", "changeit", "client");
myClientTrustStoreInfo = new TrustStoreInfo("classpath:/client-truststore.p12", "changeit", "server");
myClientTlsAuthentication = new TlsAuthentication(Optional.of(myClientKeyStoreInfo), Optional.of(myClientTrustStoreInfo));
}
@Test
public void testCreateSslContextEmpty(){
Optional<TlsAuthentication> emptyAuthentication = Optional.empty();
Optional<SSLContext> result = TlsAuthenticationSvc.createSslContext(emptyAuthentication);
assertTrue(result.isEmpty());
}
@Test
public void testCreateSslContextPresent(){
Optional<SSLContext> result = TlsAuthenticationSvc.createSslContext(Optional.of(myServerTlsAuthentication));
assertFalse(result.isEmpty());
assertEquals("TLS", result.get().getProtocol());
}
@Test
public void testCreateSslContextPresentInvalid(){
KeyStoreInfo invalidKeyStoreInfo = new KeyStoreInfo("INVALID.p12", "changeit", "changeit", "server");
TlsAuthentication invalidTlsAuthentication = new TlsAuthentication(Optional.of(invalidKeyStoreInfo), Optional.of(myServerTrustStoreInfo));
assertThrows(TlsAuthenticationSvc.TlsAuthenticationException.class, () -> {
TlsAuthenticationSvc.createSslContext(Optional.of(invalidTlsAuthentication));
});
}
@Test
public void testCreateKeyStore() throws Exception {
KeyStore keyStore = TlsAuthenticationSvc.createKeyStore(myServerKeyStoreInfo);
assertNotNull(keyStore.getKey("server", myServerKeyStoreInfo.getKeyPass()));
}
@Test
public void testCreateTrustStore() throws Exception {
KeyStore keyStore = TlsAuthenticationSvc.createKeyStore(myServerTrustStoreInfo);
assertNotNull(keyStore.getCertificate(myServerTrustStoreInfo.getAlias()));
}
@Test
public void testCreateTrustManager() throws Exception{
X509TrustManager trustManager = TlsAuthenticationSvc.createTrustManager(Optional.of(myClientTrustStoreInfo));
KeyStore keyStore = TlsAuthenticationSvc.createKeyStore(myServerKeyStoreInfo);
Certificate serverCertificate = keyStore.getCertificate(myServerKeyStoreInfo.getAlias());
assertEquals(1, trustManager.getAcceptedIssuers().length);
assertEquals(serverCertificate, trustManager.getAcceptedIssuers()[0]);
}
@Test
public void testCreateTrustManagerInvalid() throws Exception{
TrustStoreInfo invalidKeyStoreInfo = new TrustStoreInfo("INVALID.p12", "changeit", "client");
X509TrustManager trustManager = TlsAuthenticationSvc.createTrustManager(Optional.of(invalidKeyStoreInfo));
assertEquals(0, trustManager.getAcceptedIssuers().length);
}
@Test
public void testCreateHostnameVerifierEmptyTrustStoreInfo(){
Optional<TrustStoreInfo> trustStoreInfo = Optional.empty();
HostnameVerifier result = TlsAuthenticationSvc.createHostnameVerifier(trustStoreInfo);
assertEquals(NoopHostnameVerifier.class, result.getClass());
}
@Test
public void testCreateHostnameVerifierPresentTrustStoreInfo(){
Optional<TrustStoreInfo> trustStoreInfo = Optional.of(myServerTrustStoreInfo);
HostnameVerifier result = TlsAuthenticationSvc.createHostnameVerifier(trustStoreInfo);
assertEquals(DefaultHostnameVerifier.class, result.getClass());
}
}

View File

@ -6,8 +6,8 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.https.TlsAuthenticationSvc; import ca.uhn.fhir.rest.client.tls.TlsAuthenticationSvc;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client; import javax.ws.rs.client.Client;

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.test.utilities.BaseRequestGeneratingCommandTestUtil; import ca.uhn.fhir.test.utilities.BaseRequestGeneratingCommandTestUtil;
import ca.uhn.fhir.test.utilities.BaseRestServerHelper; import ca.uhn.fhir.test.utilities.BaseRestServerHelper;
import ca.uhn.fhir.test.utilities.RestServerDstu3Helper; import ca.uhn.fhir.test.utilities.RestServerDstu3Helper;

View File

@ -21,9 +21,9 @@ package ca.uhn.fhir.test.utilities;
*/ */
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.https.KeyStoreInfo; import ca.uhn.fhir.tls.KeyStoreInfo;
import ca.uhn.fhir.rest.https.TlsAuthentication; import ca.uhn.fhir.tls.TlsAuthentication;
import ca.uhn.fhir.rest.https.TrustStoreInfo; import ca.uhn.fhir.tls.TrustStoreInfo;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.https.KeyStoreType; import ca.uhn.fhir.tls.KeyStoreType;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;