Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
fmarco76 committed Dec 7, 2023
1 parent 0493625 commit 5502760
Showing 6 changed files with 307 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -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;
@@ -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.
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
@@ -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
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
@@ -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;
@@ -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;
@@ -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 <mfargett@redhat.com>
*/
@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;
@@ -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());
}

@@ -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 {
@@ -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);
Original file line number Diff line number Diff line change
@@ -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;
@@ -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 5502760

Please sign in to comment.