-
Notifications
You must be signed in to change notification settings - Fork 441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle certificates mentioned in HTTP connection during local entry deployment. #2243
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,16 +21,34 @@ | |
|
||
import org.apache.axiom.om.OMElement; | ||
import org.apache.axis2.deployment.DeploymentException; | ||
import org.apache.commons.io.IOUtils; | ||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.apache.commons.io.FileUtils; | ||
import org.apache.synapse.SynapseConstants; | ||
import org.apache.synapse.config.Entry; | ||
import org.apache.synapse.config.xml.EntryFactory; | ||
import org.apache.synapse.config.xml.EntrySerializer; | ||
import org.apache.synapse.config.xml.MultiXMLConfigurationBuilder; | ||
import org.apache.synapse.transport.dynamicconfigurations.KeyStoreReloaderHolder; | ||
import org.apache.synapse.transport.nhttp.config.SslSenderTrustStoreHolder; | ||
|
||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.security.KeyStore; | ||
import java.security.KeyStoreException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.cert.Certificate; | ||
import java.security.cert.CertificateException; | ||
import java.security.cert.CertificateFactory; | ||
import java.util.Iterator; | ||
import java.util.Properties; | ||
|
||
import javax.xml.namespace.QName; | ||
|
||
/** | ||
* Handles the <code>LocalEntry</code> deployment and undeployment tasks | ||
* | ||
|
@@ -39,6 +57,10 @@ | |
public class LocalEntryDeployer extends AbstractSynapseArtifactDeployer { | ||
|
||
private static Log log = LogFactory.getLog(LocalEntryDeployer.class); | ||
private static final String RESOURCES_IDENTIFIER = "resources:"; | ||
private static final String CONVERTED_RESOURCES_IDENTIFIER = "gov:mi-resources" + File.separator; | ||
private static final String HTTP_CONNECTION_IDENTIFIER = "http.init"; | ||
private static final String CERTIFICATE_EXTENSION = ".crt"; | ||
|
||
@Override | ||
public String deploySynapseArtifact(OMElement artifactConfig, String fileName, | ||
|
@@ -66,6 +88,7 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName, | |
} | ||
log.info("LocalEntry named '" + e.getKey() | ||
+ "' has been deployed from file : " + fileName); | ||
handleHttpConnectorCertificates(artifactConfig); | ||
return e.getKey(); | ||
} else { | ||
handleSynapseArtifactDeploymentError("LocalEntry Deployment Failed. The artifact " + | ||
|
@@ -79,6 +102,66 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName, | |
return null; | ||
} | ||
|
||
private void handleHttpConnectorCertificates(OMElement element) throws DeploymentException { | ||
|
||
OMElement httpInitElement = | ||
element.getFirstChildWithName(new QName(SynapseConstants.SYNAPSE_NAMESPACE, HTTP_CONNECTION_IDENTIFIER)); | ||
if (httpInitElement != null) { | ||
Iterator childElementIterator = httpInitElement.getChildElements(); | ||
while (childElementIterator.hasNext()) { | ||
OMElement childElement = (OMElement) childElementIterator.next(); | ||
String childElementValue = childElement.getText(); | ||
String transformedElementValue = getTransformedElementValue(childElementValue); | ||
if (transformedElementValue.endsWith(CERTIFICATE_EXTENSION)) { | ||
loadCertificateFileToStore(transformedElementValue); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void loadCertificateFileToStore(String certificateFileResourceKey) throws DeploymentException { | ||
|
||
String certificateFilePath = getSynapseConfiguration().getRegistry().getRegistryEntry(certificateFileResourceKey).getName(); | ||
File certificateFile = new File(certificateFilePath); | ||
String certificateAlias = certificateFile.getName().split("\\.")[0]; | ||
try { | ||
FileInputStream certificateFileInputStream = FileUtils.openInputStream(new File(certificateFilePath)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we use try block with resources to close the stream automatically? Relevant to other input/output streams. |
||
SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance(); | ||
KeyStore sslSenderTrustStore = sslSenderTrustStoreHolder.getKeyStore(); | ||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); | ||
Certificate certificate = certificateFactory.generateCertificate(certificateFileInputStream); | ||
sslSenderTrustStore.setCertificateEntry(certificateAlias, certificate); | ||
|
||
FileOutputStream fileOutputStream = new FileOutputStream(sslSenderTrustStoreHolder.getLocation()); | ||
sslSenderTrustStore.store(fileOutputStream, sslSenderTrustStoreHolder.getPassword().toCharArray()); | ||
|
||
FileInputStream fileInputStream = new FileInputStream(sslSenderTrustStoreHolder.getLocation()); | ||
InputStream dest = IOUtils.toBufferedInputStream(fileInputStream); | ||
fileInputStream.close(); | ||
sslSenderTrustStore.load(dest, sslSenderTrustStoreHolder.getPassword().toCharArray()); | ||
Comment on lines
+138
to
+141
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need these redundant truststore loads as we do it in line 145? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. They represent 2 things. First load is to load the certificate to the JKS and the second load is used to load the new JKS and create a new connection. |
||
dest.close(); | ||
|
||
sslSenderTrustStoreHolder.setKeyStore(sslSenderTrustStore); | ||
KeyStoreReloaderHolder.getInstance().reloadAllKeyStores(); | ||
} catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) { | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than throwing a generic runtime exception, shall we add a more descriptive message? ex: ("Failed to load certificate file to trust store", e) |
||
} | ||
} | ||
|
||
/** | ||
* Transforms the given element value if it indicates a resource file. | ||
* | ||
* @param elementValue the value of the element to be transformed | ||
* @return the transformed element value | ||
*/ | ||
private String getTransformedElementValue(String elementValue) { | ||
String transformedElementValue = elementValue.trim(); | ||
if (transformedElementValue.startsWith(RESOURCES_IDENTIFIER)) { | ||
transformedElementValue = transformedElementValue.replace(RESOURCES_IDENTIFIER, CONVERTED_RESOURCES_IDENTIFIER); | ||
} | ||
return transformedElementValue; | ||
} | ||
|
||
@Override | ||
public String updateSynapseArtifact(OMElement artifactConfig, String fileName, | ||
String existingArtifactName, Properties properties) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.apache.synapse.transport.dynamicconfigurations; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we add the license headers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please check other places as well. |
||
|
||
import org.apache.axis2.AxisFault; | ||
import org.apache.axis2.description.ParameterInclude; | ||
|
||
public interface IKeyStoreLoader { | ||
|
||
void loadKeyStore(ParameterInclude transport) throws AxisFault; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package org.apache.synapse.transport.dynamicconfigurations; | ||
|
||
import org.apache.axis2.AxisFault; | ||
import org.apache.axis2.description.ParameterInclude; | ||
|
||
public class KeyStoreReloader { | ||
|
||
private IKeyStoreLoader keyStoreLoader; | ||
private ParameterInclude transportOutDescription; | ||
|
||
public KeyStoreReloader(IKeyStoreLoader keyStoreLoader, ParameterInclude transportOutDescription) { | ||
|
||
this.keyStoreLoader = keyStoreLoader; | ||
this.transportOutDescription = transportOutDescription; | ||
|
||
registerListener(transportOutDescription); | ||
} | ||
|
||
private void registerListener(ParameterInclude transportOutDescription) { | ||
|
||
KeyStoreReloaderHolder.getInstance().addKeyStoreLoader(this); | ||
} | ||
|
||
public void update() { | ||
|
||
try { | ||
keyStoreLoader.loadKeyStore(transportOutDescription); | ||
} catch (AxisFault e) { | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we throwing a runtime exception here? instead throw an AxisFault with a proper message. |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.apache.synapse.transport.dynamicconfigurations; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class KeyStoreReloaderHolder { | ||
|
||
private static KeyStoreReloaderHolder instance = new KeyStoreReloaderHolder(); | ||
private List<KeyStoreReloader> keyStoreLoaders; | ||
|
||
private KeyStoreReloaderHolder() { | ||
keyStoreLoaders = new ArrayList<>(); | ||
} | ||
|
||
public static KeyStoreReloaderHolder getInstance() { | ||
return instance; | ||
} | ||
|
||
public void addKeyStoreLoader(KeyStoreReloader keyStoreLoader) { | ||
keyStoreLoaders.add(keyStoreLoader); | ||
} | ||
|
||
public void reloadAllKeyStores() { | ||
for (KeyStoreReloader keyStoreLoader : keyStoreLoaders) { | ||
keyStoreLoader.update(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.apache.synapse.transport.nhttp.config; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
public class SslSenderTrustStoreHolderTest { | ||
|
||
@Test | ||
public void testGetInstance() { | ||
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
SslSenderTrustStoreHolder instance = SslSenderTrustStoreHolder.getInstance(); | ||
SslSenderTrustStoreHolder instance2 = SslSenderTrustStoreHolder.getInstance(); | ||
Assert.assertEquals(instance, instance2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add an assert message here. |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn;t have to be specific to http connector right? If so shall we change the method name?