From 0b39b68e4e72cbcf0f4d28488d54ce06117efa9c Mon Sep 17 00:00:00 2001 From: Endi Sukma Dewata Date: Thu, 1 Mar 2012 19:58:34 -0600 Subject: Added CMSException. The CMSException was added to simplify error handling in REST services. The exception may include an error message and some other attributes. When the server throws a CMSException (or its subclass), the exception will be marshalled into XML and unmarshalled by the client, then thrown again as a new exception which can be caught by the application. Ticket #100 --- .../cms/servlet/test/DRMErrorInterceptor.java | 43 +++++++++ .../netscape/cms/servlet/test/DRMRestClient.java | 105 +++++++++++---------- .../src/com/netscape/cms/servlet/test/DRMTest.java | 50 +++++----- 3 files changed, 129 insertions(+), 69 deletions(-) create mode 100644 base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java (limited to 'base/kra/functional') diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java new file mode 100644 index 000000000..7572acef5 --- /dev/null +++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java @@ -0,0 +1,43 @@ +package com.netscape.cms.servlet.test; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.client.core.ClientErrorInterceptor; + +import com.netscape.cms.servlet.base.CMSException; + +public class DRMErrorInterceptor implements ClientErrorInterceptor { + + public void handle(ClientResponse response) { + + // handle HTTP code 4xx and 5xx + int code = response.getResponseStatus().getStatusCode(); + if (code < 400) return; + + MultivaluedMap headers = response.getHeaders(); + String contentType = headers.getFirst("Content-Type"); + + // handle XML content only + if (!contentType.startsWith(MediaType.TEXT_XML)) return; + + CMSException exception; + + try { + // Requires RESTEasy 2.3.2 + // https://issues.jboss.org/browse/RESTEASY-652 + CMSException.Data data = response.getEntity(CMSException.Data.class); + + Class clazz = Class.forName(data.className); + exception = (CMSException) clazz.getConstructor(CMSException.Data.class).newInstance(data); + + } catch (Exception e) { + e.printStackTrace(); + return; + } + + throw exception; + } + +} diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java index 651873b20..14b2aa3dc 100644 --- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java +++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java @@ -2,9 +2,9 @@ package com.netscape.cms.servlet.test; import java.io.IOException; import java.net.InetAddress; -import java.net.MalformedURLException; import java.net.Socket; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.Collection; import java.util.Enumeration; @@ -18,9 +18,14 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.jboss.resteasy.client.ClientExecutor; import org.jboss.resteasy.client.ClientResponse; import org.jboss.resteasy.client.ProxyFactory; +import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; +import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback; +import org.mozilla.jss.ssl.SSLSocket; + import com.netscape.certsrv.dbs.keydb.KeyId; import com.netscape.certsrv.request.RequestId; -import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor; import com.netscape.cms.servlet.admin.SystemCertificateResource; import com.netscape.cms.servlet.cert.model.CertificateData; import com.netscape.cms.servlet.key.KeyResource; @@ -36,10 +41,6 @@ import com.netscape.cms.servlet.request.model.KeyRequestInfos; import com.netscape.cms.servlet.request.model.RecoveryRequestData; import com.netscape.cmsutil.util.Utils; -import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; -import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback; -import org.mozilla.jss.ssl.SSLSocket; - public class DRMRestClient { // Callback to approve or deny returned SSL server certs @@ -47,7 +48,7 @@ public class DRMRestClient { // ToDO: Look into taking this JSS http client code and move it into // its own class to be used by possible future clients. private class ServerCertApprovalCB implements SSLCertificateApprovalCallback { - + public boolean approve(org.mozilla.jss.crypto.X509Certificate servercert, SSLCertificateApprovalCallback.ValidityStatus status) { @@ -81,7 +82,7 @@ public class DRMRestClient { reason == SSLCertificateApprovalCallback.ValidityStatus.BAD_CERT_DOMAIN) { //Allow these two since we haven't necessarily installed the CA cert for trust - // and we are choosing "localhost" as the host for this client. + // and we are choosing "localhost" as the host for this client. return true; @@ -93,91 +94,95 @@ public class DRMRestClient { return false; } } - + private class JSSProtocolSocketFactory implements ProtocolSocketFactory { @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - + SSLSocket sock = createJSSSocket(host,port, null, 0, null); - return (Socket) sock; + return sock; } @Override public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { - + SSLSocket sock = createJSSSocket(host,port, clientHost, clientPort, null); - return (Socket) sock; + return sock; } @Override public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { - + SSLSocket sock = createJSSSocket(host, port, localAddress, localPort, null); - return (Socket) sock; + return sock; } } - private SSLSocket createJSSSocket(String host, int port, InetAddress localAddress, + private SSLSocket createJSSSocket(String host, int port, InetAddress localAddress, int localPort, SSLClientCertificateSelectionCallback clientCertSelectionCallback) throws IOException, UnknownHostException, ConnectTimeoutException { - + SSLSocket sock = new SSLSocket(InetAddress.getByName(host), port, localAddress, localPort, new ServerCertApprovalCB(), null); - + if(sock != null && clientCertNickname != null) { sock.setClientCertNickname(clientCertNickname); } - + return sock; - + } private KeyResource keyClient; private KeysResource keysClient; private KeyRequestsResource keyRequestsClient; private KeyRequestResource keyRequestClient; private SystemCertificateResource systemCertClient; - + private String clientCertNickname = null; - - public DRMRestClient(String baseUri, String clientCertNick) throws MalformedURLException { - + + public DRMRestClient(String baseUri, String clientCertNick) throws URISyntaxException { + // For SSL we are assuming the caller has already intialized JSS and has // a valid CryptoManager and CryptoToken // optional clientCertNickname is provided for use if required. - - - URL url = new URL(baseUri); - - String protocol = url.getProtocol(); - int port = url.getPort(); - + + + URI uri = new URI(baseUri); + + String protocol = uri.getScheme(); + int port = uri.getPort(); + clientCertNickname = null; if(protocol != null && protocol.equals("https")) { if (clientCertNick != null) { clientCertNickname = clientCertNick; - } - - Protocol.registerProtocol("https", + } + + Protocol.registerProtocol("https", new Protocol(protocol, new JSSProtocolSocketFactory(), port)); } - + HttpClient httpclient = new HttpClient(); ClientExecutor executor = new ApacheHttpClientExecutor(httpclient); - systemCertClient = ProxyFactory.create(SystemCertificateResource.class, baseUri, executor); - keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, baseUri, executor); - keyRequestClient = ProxyFactory.create(KeyRequestResource.class, baseUri, executor); - keysClient = ProxyFactory.create(KeysResource.class, baseUri, executor); - keyClient = ProxyFactory.create(KeyResource.class, baseUri, executor); + ResteasyProviderFactory providerFactory = ResteasyProviderFactory.getInstance(); + providerFactory.addClientErrorInterceptor(new DRMErrorInterceptor()); + + systemCertClient = ProxyFactory.create(SystemCertificateResource.class, uri, executor, providerFactory); + keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, uri, executor, providerFactory); + keyRequestClient = ProxyFactory.create(KeyRequestResource.class, uri, executor, providerFactory); + keysClient = ProxyFactory.create(KeysResource.class, uri, executor, providerFactory); + keyClient = ProxyFactory.create(KeyResource.class, uri, executor, providerFactory); + keyClient = ProxyFactory.create(KeyResource.class, uri, executor, providerFactory); } - + public String getTransportCert() { @SuppressWarnings("unchecked") ClientResponse response = (ClientResponse) systemCertClient.getTransportCert(); @@ -185,7 +190,7 @@ public class DRMRestClient { String transportCert = certData.getB64(); return transportCert; } - + public Collection listRequests(String requestState, String requestType) { KeyRequestInfos infos = keyRequestsClient.listRequests( requestState, requestType, null, new RequestId(0), 100, 100, 10 @@ -193,7 +198,7 @@ public class DRMRestClient { Collection list = infos.getRequests(); return list; } - + public KeyRequestInfo archiveSecurityData(byte[] encoded, String clientId, String dataType) { // create archival request ArchivalRequestData data = new ArchivalRequestData(); @@ -205,7 +210,7 @@ public class DRMRestClient { KeyRequestInfo info = keyRequestClient.archiveKey(data); return info; } - + public KeyDataInfo getKeyData(String clientId, String status) { KeyDataInfos infos = keysClient.listKeys(clientId, status, 100, 10); Collection list = infos.getKeyInfos(); @@ -220,7 +225,7 @@ public class DRMRestClient { } return null; } - + public KeyRequestInfo requestRecovery(KeyId keyId, byte[] rpwd, byte[] rkey, byte[] nonceData) { // create recovery request RecoveryRequestData data = new RecoveryRequestData(); @@ -239,11 +244,11 @@ public class DRMRestClient { KeyRequestInfo info = keyRequestClient.recoverKey(data); return info; } - + public void approveRecovery(RequestId recoveryId) { keyRequestClient.approveRequest(recoveryId); } - + public KeyData retrieveKey(KeyId keyId, RequestId requestId, byte[] rpwd, byte[] rkey, byte[] nonceData) { // create recovery request RecoveryRequestData data = new RecoveryRequestData(); @@ -263,4 +268,8 @@ public class DRMRestClient { KeyData key = keyClient.retrieveKey(data); return key; } + + public KeyRequestInfo getRequest(RequestId id) { + return keyRequestClient.getRequestInfo(id); + } } diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java index 8d83247b8..5323777bd 100644 --- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java +++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java @@ -17,12 +17,17 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cms.servlet.test; -import java.net.MalformedURLException; import java.util.Calendar; import java.util.Collection; import java.util.Iterator; import java.util.Random; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; import org.mozilla.jss.CryptoManager; import org.mozilla.jss.crypto.AlreadyInitializedException; import org.mozilla.jss.crypto.CryptoToken; @@ -32,19 +37,13 @@ import org.mozilla.jss.crypto.KeyGenAlgorithm; import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.util.Password; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.apache.commons.cli.PosixParser; - import com.netscape.certsrv.dbs.keydb.KeyId; import com.netscape.certsrv.request.RequestId; import com.netscape.cms.servlet.base.CMSResourceService; import com.netscape.cms.servlet.key.model.KeyData; import com.netscape.cms.servlet.key.model.KeyDataInfo; import com.netscape.cms.servlet.request.KeyRequestResource; +import com.netscape.cms.servlet.request.RequestNotFoundException; import com.netscape.cms.servlet.request.model.KeyRequestInfo; import com.netscape.cmsutil.crypto.CryptoUtil; import com.netscape.cmsutil.util.Utils; @@ -102,18 +101,18 @@ public class DRMTest { if (cmd.hasOption("d")) { db_dir = cmd.getOptionValue("d"); } - + if (cmd.hasOption("s")) { if(cmd.getOptionValue("s") != null && cmd.getOptionValue("s").equals("true")) { protocol = "https"; } } - + if (cmd.hasOption("c")) { String nick = cmd.getOptionValue("c"); - + if (nick != null && protocol.equals("https")) { - clientCertNickname = nick; + clientCertNickname = nick; } } @@ -123,7 +122,7 @@ public class DRMTest { } // used for crypto operations - byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; IVParameterSpec ivps = null; IVParameterSpec ivps_server = null; @@ -168,7 +167,7 @@ public class DRMTest { try { CryptoManager.initialize(db_dir); } catch (AlreadyInitializedException e) { - // it is ok if it is already initialized + // it is ok if it is already initialized } catch (Exception e) { log("INITIALIZATION ERROR: " + e.toString()); System.exit(1); @@ -192,15 +191,14 @@ public class DRMTest { } // Set base URI and get client - - + + String baseUri = protocol + "://" + host + ":" + port + "/kra/pki"; DRMRestClient client; try { client = new DRMRestClient(baseUri, clientCertNickname); - } catch (MalformedURLException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); return; } @@ -294,7 +292,7 @@ public class DRMTest { ivps_server = new IVParameterSpec(Utils.base64decode(keyData.getNonceData())); try { - recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, + recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, Utils.base64decode(wrappedRecoveredKey), recoveryKey, EncryptionAlgorithm.DES3_CBC_PAD); } catch (Exception e) { @@ -406,7 +404,7 @@ public class DRMTest { wrappedRecoveredKey = keyData.getWrappedPrivateData(); ivps_server = new IVParameterSpec( Utils.base64decode(keyData.getNonceData())); try { - recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, + recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, Utils.base64decode(wrappedRecoveredKey), recoveryKey, EncryptionAlgorithm.DES3_CBC_PAD); recoveredKey = new String(Utils.base64decode(recoveredKey), "UTF-8"); @@ -476,6 +474,16 @@ public class DRMTest { } else { log("Success: recovered and archived passphrases do match!"); } + + // Test 23: Get non-existent request + RequestId requestId = new RequestId("0xabcdef"); + log("Getting non-existent request: " + requestId.toHexString()); + try { + client.getRequest(requestId); + log("Error: getting non-existent request does not throw an exception"); + } catch (RequestNotFoundException e) { + log("Success: getting non-existent request throws an exception: "+e.getMessage()+" ("+e.getRequestId().toHexString()+")"); + } } private static void log(String string) { -- cgit