Skip to content

Commit

Permalink
Implement v2 REST end-point for /ca/v2/certs
Browse files Browse the repository at this point in the history
Implement the REST operations:
- GET of /ca/v2/certs
- POST to /ca/v2/certs/search.

These implement the behaviour of the current REST operations (see [1, 2]). The new version is not based on RESTeasy framework for the REST operation and the DB searches have replaced VLV with paged queries.

The only differences with the previous implementation are:
- the total value now show the number of returned item;
- the number of returned entry is limited (this is hard-coded for now).

1. https://github.com/dogtagpki/pki/wiki/CA-List-Certificates-REST-API
2. https://github.com/dogtagpki/pki/wiki/CA-Search-Certificates-REST-API
  • Loading branch information
fmarco76 committed Dec 11, 2023
1 parent d9b0fd7 commit 20cfb69
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import org.mozilla.jss.netscape.security.x509.CertificateValidity;
Expand Down Expand Up @@ -1112,6 +1114,33 @@ public Enumeration<CertRecord> searchCertificates(String filter, int maxSize,

}

/**
* Finds a list of certificate records that satisfies the filter.
*
* The filter should follow RFC1558 LDAP filter syntax.
* For example,
*
* {@Code (&(certRecordId=5)(x509Cert.notBefore=934398398))}
*
* @param filter search filter
* @param maxSize max size to return
* @return a list of certificates
* @exception EBaseException failed to search
*/
public Iterator<CertRecord> searchCertificates(String filter, int timeLimit, int start, int size)
throws EBaseException {

ArrayList<CertRecord> records = new ArrayList<>();
logger.debug("searchCertificates filter {filter}, start {start} and size {size}", filter, start, size);
try (DBSSession s = dbSubsystem.createSession()) {
DBSearchResults sr = s.pagedSearch(mBaseDN, filter, start, size);
while (sr.hasMoreElements()) {
records.add((CertRecord) sr.nextElement());
}
}
return records.iterator();
}


/**
* Finds certificate records.
Expand Down
2 changes: 2 additions & 0 deletions base/ca/src/main/java/org/dogtagpki/server/ca/CAServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public class CAServlet extends HttpServlet {


public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}

public void post(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}

@Override
Expand Down
59 changes: 32 additions & 27 deletions base/ca/src/main/java/org/dogtagpki/server/ca/v2/CertServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//
package org.dogtagpki.server.ca.v2;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
Expand All @@ -13,9 +14,10 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -45,19 +47,20 @@
import com.netscape.certsrv.cert.CertData;
import com.netscape.certsrv.cert.CertDataInfo;
import com.netscape.certsrv.cert.CertDataInfos;
import com.netscape.certsrv.cert.CertSearchRequest;
import com.netscape.certsrv.dbs.certdb.CertId;
import com.netscape.certsrv.util.JSONSerializer;
import com.netscape.cms.servlet.cert.FilterBuilder;
import com.netscape.cmscore.dbs.CertRecord;
import com.netscape.cmscore.dbs.CertificateRepository;
import com.netscape.cmscore.dbs.RevocationInfo;
import com.netscape.cmsutil.ldap.LDAPUtil;

/**
* @author Marco Fargetta <[email protected]>
*/
@WebServlet("/v2/certs/*")
public class CertServlet extends CAServlet {
public static final int DEFAULT_MAXTIME = 0;
public static final int DEFAULT_MAXRESULTS = 20;
public final static int DEFAULT_SIZE = 20;

private static final long serialVersionUID = 1L;
Expand All @@ -82,15 +85,14 @@ public void get(HttpServletRequest request, HttpServletResponse response) throws
return;
}

int maxResults = request.getParameter("maxResults") == null ?
DEFAULT_MAXRESULTS : Integer.parseInt(request.getParameter("maxResults"));
int maxTime = request.getParameter("maxTime") == null ?
DEFAULT_MAXTIME : Integer.parseInt(request.getParameter("maxTime"));
int size = request.getParameter("size") == null ?
DEFAULT_SIZE : Integer.parseInt(request.getParameter("size"));
int start = request.getParameter("start") == null ? 0 : Integer.parseInt(request.getParameter("start"));

CertDataInfos infos = listCerts(request.getParameter("status"), maxResults, maxTime, start, size);
CertSearchRequest searchElems = CertSearchRequest.fromMap(request.getParameterMap());
CertDataInfos infos = listCerts(searchElems, maxTime, start, size);
out.println(infos.toJSON());
}

Expand All @@ -99,16 +101,24 @@ public void post(HttpServletRequest request, HttpServletResponse response) throw
HttpSession session = request.getSession();
logger.debug("CertServlet.post(): session: {}", session.getId());

response.setContentType("application/json");
PrintWriter out = response.getWriter();

if(request.getPathInfo() == null || !request.getPathInfo().equals("/search")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI());
return;
}

CertDataInfos infos = new CertDataInfos();
out.println(infos.toString());
BufferedReader reader = request.getReader();
String postMessage = reader.lines().collect(Collectors.joining());

CertSearchRequest requestFilter = JSONSerializer.fromJSON(postMessage, CertSearchRequest.class);
int size = request.getParameter("size") == null ?
DEFAULT_SIZE : Integer.parseInt(request.getParameter("size"));
int start = request.getParameter("start") == null ? 0 : Integer.parseInt(request.getParameter("start"));

CertDataInfos infos = listCerts(requestFilter, start, size);

response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.println(infos.toJSON());
}

private CertData getCertData(CertId id, Locale loc) throws Exception {
Expand Down Expand Up @@ -177,44 +187,39 @@ private CertData getCertData(CertId id, Locale loc) throws Exception {
return certData;
}

private CertDataInfos listCerts(String status, int maxResults, int maxTime, int start, int size) {
private CertDataInfos listCerts(CertSearchRequest searchReq, int start, int size) {
return listCerts(searchReq, -1, start, size);
}

private CertDataInfos listCerts(CertSearchRequest searchReq, int maxTime, int start, int size) {
CAEngine engine = getCAEngine();
CertificateRepository repo = engine.getCertificateRepository();

logger.info("Listing certificates");
FilterBuilder builder = new FilterBuilder(searchReq);
String filter = builder.buildFilter();

String filter;
if (status == null) {
filter = "(certstatus=*)";

} else {
filter = "(certStatus=" + LDAPUtil.escapeFilter(status) + ")";
}
logger.info("Search filter: " + filter);

CertDataInfos infos = new CertDataInfos();
try {
Enumeration<CertRecord> e = repo.searchCertificates(filter, maxResults, maxTime);
Iterator<CertRecord> e = repo.searchCertificates(filter, maxTime, start, size);
if (e == null) {
throw new EBaseException("search results are null");
}

// store non-null results in a list
List<CertDataInfo> results = new ArrayList<>();
while (e.hasMoreElements()) {
CertRecord rec = e.nextElement();
while (e.hasNext()) {
CertRecord rec = e.next();
if (rec == null) continue;
results.add(createCertDataInfo(rec));
}

int total = results.size();
logger.info("Search results: " + total);
infos.setTotal(total);

// return entries in the requested page
for (int i = start; i < start + size && i < total ; i++) {
infos.addEntry(results.get(i));
}
infos.setEntries(results);
} catch (Exception e) {
logger.error("Unable to list certificates: " + e.getMessage(), e);
throw new PKIException("Unable to list certificates: " + e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;
import java.util.Objects;

import javax.ws.rs.core.MultivaluedMap;
Expand Down Expand Up @@ -1104,4 +1105,147 @@ public static CertSearchRequest fromXML(String xml) throws Exception {
Element rootElement = document.getDocumentElement();
return fromDOM(rootElement);
}

public static CertSearchRequest fromMap(Map<String, String[]> elements) {

CertSearchRequest request = new CertSearchRequest();

elements.forEach((key, values) -> {
switch (key) {
case "issuerDN":
request.setIssuerDN(values[0]);
break;
case "serialNumberRangeInUse":
request.setSerialNumberRangeInUse(Boolean.parseBoolean(values[0]));
break;
case "serialTo":
request.setSerialTo(values[0]);
break;
case "serialFrom":
request.setSerialFrom(values[0]);
break;
case "subjectInUse":
request.setSubjectInUse(Boolean.parseBoolean(values[0]));
break;
case "eMail":
request.setEmail(values[0]);
break;
case "commonName":
request.setCommonName(values[0]);
break;
case "userID":
request.setUserID(values[0]);
break;
case "orgUnit":
request.setOrgUnit(values[0]);
break;
case "org":
request.setOrg(values[0]);
break;
case "locality":
request.setLocality(values[0]);
break;
case "state":
request.setState(values[0]);
break;
case "country":
request.setCountry(values[0]);
break;
case "matchExactly":
request.setMatchExactly(Boolean.parseBoolean(values[0]));
break;
case "status":
request.setStatus(values[0]);
break;
case "revokedBy":
request.setRevokedBy(values[0]);
break;
case "revokedOnFrom":
request.setRevokedOnFrom(values[0]);
break;
case "revokedOnTo":
request.setRevokedOnTo(values[0]);
break;
case "revocationReason":
request.setRevocationReason(values[0]);
break;
case "issuedBy":
request.setIssuedBy(values[0]);
break;
case "issuedOnFrom":
request.setIssuedOnFrom(values[0]);
break;
case "issuedOnTo":
request.setIssuedOnTo(values[0]);
break;
case "validNotBeforeFrom":
request.setValidNotBeforeFrom(values[0]);
break;
case "validNotBeforeTo":
request.setValidNotBeforeTo(values[0]);
break;
case "validNotAfterFrom":
request.setValidNotAfterFrom(values[0]);
break;
case "validNotAfterTo":
request.setValidNotAfterTo(values[0]);
break;
case "validityOperation":
request.setValidityOperation(values[0]);
break;
case "validityCount":
request.setValidityCount(Integer.valueOf(values[0]));
break;
case "validityUnit":
request.setValidityUnit(Long.valueOf(values[0]));
break;
case "certTypeSubEmailCA":
request.setCertTypeSubEmailCA(values[0]);
break;
case "certTypeSubSSLCA":
request.setCertTypeSubSSLCA(values[0]);
break;
case "certTypeSecureEmail":
request.setCertTypeSecureEmail(values[0]);
break;
case "certTypeSSLClient":
request.setCertTypeSSLClient(values[0]);
break;
case "certTypeSSLServer":
request.setCertTypeSSLServer(values[0]);
break;
case "revokedByInUse":
request.setRevokedByInUse(Boolean.parseBoolean(values[0]));
break;
case "revokedOnInUse":
request.setRevokedOnInUse(Boolean.parseBoolean(values[0]));
break;
case "revocationReasonInUse":
request.setRevocationReasonInUse(Boolean.parseBoolean(values[0]));
break;
case "issuedByInUse":
request.setIssuedByInUse(Boolean.parseBoolean(values[0]));
break;
case "issuedOnInUse":
request.setIssuedOnInUse(Boolean.parseBoolean(values[0]));
break;
case "validNotBeforeInUse":
request.setValidNotBeforeInUse(Boolean.parseBoolean(values[0]));
break;
case "validNotAfterInUse":
request.setValidNotAfterInUse(Boolean.parseBoolean(values[0]));
break;
case "validityLengthInUse":
request.setValidityLengthInUse(Boolean.parseBoolean(values[0]));
break;
case "certTypeInUse":
request.setCertTypeInUse(Boolean.parseBoolean(values[0]));
break;
default:
}
});

return request;
}

}
Loading

0 comments on commit 20cfb69

Please sign in to comment.