diff --git a/cadc-cdp-server/build.gradle b/cadc-cdp-server/build.gradle
index 0a3e7b3..0d931c3 100644
--- a/cadc-cdp-server/build.gradle
+++ b/cadc-cdp-server/build.gradle
@@ -3,6 +3,7 @@ plugins {
id 'maven'
id 'maven-publish'
id 'com.jfrog.bintray' version '1.8.4'
+ id 'checkstyle'
}
repositories {
@@ -10,17 +11,19 @@ repositories {
mavenLocal()
}
-sourceCompatibility = 1.7
+apply from: '../opencadc.gradle'
+
+sourceCompatibility = 1.8
group = 'org.opencadc'
-version = '1.1.5'
+version = '1.4'
-configurations.all {
- resolutionStrategy {
- force 'javax.servlet:javax.servlet-api:3.1.0'
- }
-}
+//configurations.all {
+// resolutionStrategy {
+// force 'javax.servlet:javax.servlet-api:3.1.0'
+// }
+//}
dependencies {
compile 'log4j:log4j:1.+'
@@ -29,9 +32,8 @@ dependencies {
compile 'org.springframework:spring-jdbc:2.5.6.SEC01'
compile 'org.opencadc:cadc-log:[1.1.0,)'
- compile 'org.opencadc:cadc-util:[1.2.25,)'
- compile 'org.opencadc:cadc-cdp:[1.2.3,)'
- compile 'org.opencadc:cadc-vosi:[1.3.1,)'
+ compile 'org.opencadc:cadc-util:[1.3.10,)'
+ compile 'org.opencadc:cadc-cdp:[1.4,)'
testCompile 'junit:junit:4.+'
testCompile 'org.easymock:easymock:3.+'
diff --git a/cadc-cdp-server/src/intTest/java/ca/nrc/cadc/cred/server/CertificateDAOTest.java b/cadc-cdp-server/src/intTest/java/ca/nrc/cadc/cred/server/CertificateDAOTest.java
new file mode 100644
index 0000000..457e96c
--- /dev/null
+++ b/cadc-cdp-server/src/intTest/java/ca/nrc/cadc/cred/server/CertificateDAOTest.java
@@ -0,0 +1,174 @@
+/*
+************************************************************************
+******************* CANADIAN ASTRONOMY DATA CENTRE *******************
+************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
+*
+* (c) 2020. (c) 2020.
+* Government of Canada Gouvernement du Canada
+* National Research Council Conseil national de recherches
+* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
+* All rights reserved Tous droits réservés
+*
+* NRC disclaims any warranties, Le CNRC dénie toute garantie
+* expressed, implied, or énoncée, implicite ou légale,
+* statutory, of any kind with de quelque nature que ce
+* respect to the software, soit, concernant le logiciel,
+* including without limitation y compris sans restriction
+* any warranty of merchantability toute garantie de valeur
+* or fitness for a particular marchande ou de pertinence
+* purpose. NRC shall not be pour un usage particulier.
+* liable in any event for any Le CNRC ne pourra en aucun cas
+* damages, whether direct or être tenu responsable de tout
+* indirect, special or general, dommage, direct ou indirect,
+* consequential or incidental, particulier ou général,
+* arising from the use of the accessoire ou fortuit, résultant
+* software. Neither the name de l'utilisation du logiciel. Ni
+* of the National Research le nom du Conseil National de
+* Council of Canada nor the Recherches du Canada ni les noms
+* names of its contributors may de ses participants ne peuvent
+* be used to endorse or promote être utilisés pour approuver ou
+* products derived from this promouvoir les produits dérivés
+* software without specific prior de ce logiciel sans autorisation
+* written permission. préalable et particulière
+* par écrit.
+*
+* This file is part of the Ce fichier fait partie du projet
+* OpenCADC project. OpenCADC.
+*
+* OpenCADC is free software: OpenCADC est un logiciel libre ;
+* you can redistribute it and/or vous pouvez le redistribuer ou le
+* modify it under the terms of modifier suivant les termes de
+* the GNU Affero General Public la “GNU Affero General Public
+* License as published by the License” telle que publiée
+* Free Software Foundation, par la Free Software Foundation
+* either version 3 of the : soit la version 3 de cette
+* License, or (at your option) licence, soit (à votre gré)
+* any later version. toute version ultérieure.
+*
+* OpenCADC is distributed in the OpenCADC est distribué
+* hope that it will be useful, dans l’espoir qu’il vous
+* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
+* without even the implied GARANTIE : sans même la garantie
+* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
+* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
+* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
+* General Public License for Générale Publique GNU Affero
+* more details. pour plus de détails.
+*
+* You should have received Vous devriez avoir reçu une
+* a copy of the GNU Affero copie de la Licence Générale
+* General Public License along Publique GNU Affero avec
+* with OpenCADC. If not, see OpenCADC ; si ce n’est
+* . pas le cas, consultez :
+* .
+*
+************************************************************************
+*/
+
+package ca.nrc.cadc.cred.server;
+
+import ca.nrc.cadc.auth.SSLUtil;
+import ca.nrc.cadc.auth.X509CertificateChain;
+import ca.nrc.cadc.db.ConnectionConfig;
+import ca.nrc.cadc.db.DBConfig;
+import ca.nrc.cadc.db.DBUtil;
+import ca.nrc.cadc.util.FileUtil;
+import ca.nrc.cadc.util.Log4jInit;
+import java.io.File;
+import java.util.List;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author pdowler
+ */
+public class CertificateDAOTest {
+ private static final Logger log = Logger.getLogger(CertificateDAOTest.class);
+
+ static {
+ Log4jInit.setLevel("ca.nrc.cadc.cred", Level.DEBUG);
+ Log4jInit.setLevel("ca.nrc.cadc.db.version", Level.DEBUG);
+ }
+
+ CertificateDAO dao;
+
+ public CertificateDAOTest() throws Exception {
+ try {
+ DBConfig dbrc = new DBConfig();
+ ConnectionConfig cc = dbrc.getConnectionConfig("CRED_TEST", "cadctest");
+ DBUtil.createJNDIDataSource("jdbc/CertificateDAOTest", cc);
+
+ CertificateDAO.CertificateSchema config = new CertificateDAO.CertificateSchema("jdbc/CertificateDAOTest", null, "cred");
+ this.dao = new CertificateDAO(config);
+
+ } catch (Exception ex) {
+ log.error("setup failed", ex);
+ throw ex;
+ }
+ }
+
+ @Before
+ public void init_cleanup() throws Exception {
+ log.info("init database...");
+ InitDatabaseCDP init = new InitDatabaseCDP(dao.getDataSource(), null, "cred");
+ init.doInit();
+ log.info("init database... OK");
+ }
+
+ //@Test
+ public void testNoOp() {
+
+ }
+
+ //@Test
+ public void testListKeys() {
+ try {
+ List keys = dao.getAllHashKeys();
+ log.info("found keys: " + keys.size());
+ } catch (Exception unexpected) {
+ log.error("unexpected exception", unexpected);
+ Assert.fail("unexpected exception: " + unexpected);
+ }
+ }
+
+ @Test
+ public void testPutGetUpdateCertificateChain() {
+ try {
+ File pemFile = FileUtil.getFileFromResource("cdp-test.pem", CertificateDAOTest.class);
+ X509CertificateChain cc1 = SSLUtil.readPemCertificateAndKey(pemFile);
+
+ // not found
+ X509CertificateChain nf = dao.get(cc1.getHashKey());
+ Assert.assertNull(nf);
+
+ // insert
+ dao.put(cc1);
+ X509CertificateChain cc2 = dao.get(cc1.getHashKey());
+ Assert.assertNotNull(cc2);
+
+ Thread.sleep(1000L);
+
+ // update
+ dao.put(cc2);
+ X509CertificateChain cc3 = dao.get(cc1.getHashKey());
+ Assert.assertNotNull(cc3);
+
+ Thread.sleep(1000L);
+
+ // delete
+ //dao.delete(cc1.getHashKey());
+ //X509CertificateChain cc4 = dao.get(cc1.getHashKey());
+ //Assert.assertNull(cc4);
+
+ } catch (Exception unexpected) {
+ log.error("unexpected exception", unexpected);
+ Assert.fail("unexpected exception: " + unexpected);
+ }
+ }
+
+
+}
diff --git a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CadcDelegationServlet.java b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CadcDelegationServlet.java
index 4e9d904..b59dad2 100644
--- a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CadcDelegationServlet.java
+++ b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CadcDelegationServlet.java
@@ -3,7 +3,7 @@
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
-* (c) 2011. (c) 2011.
+* (c) 2020. (c) 2020.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
@@ -65,10 +65,13 @@
* $Revision: 5 $
*
************************************************************************
-*/
+ */
package ca.nrc.cadc.cred.server;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.log.ServletLogInfo;
+import ca.nrc.cadc.log.WebServiceLogInfo;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.PrivilegedActionException;
@@ -78,26 +81,20 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
-
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-
import org.apache.log4j.Logger;
import org.astrogrid.security.delegation.DelegationServlet;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.log.ServletLogInfo;
-import ca.nrc.cadc.log.WebServiceLogInfo;
-
/**
* This servlet is used in inject AccessControl information
*/
-public class CadcDelegationServlet extends DelegationServlet
-{
+public class CadcDelegationServlet extends DelegationServlet {
+
private static final long serialVersionUID = 2740612605831268729L;
private static Logger LOGGER = Logger.getLogger(CadcDelegationServlet.class);
public static final String SU_DNS = "suDNs";
@@ -106,16 +103,13 @@ public class CadcDelegationServlet extends DelegationServlet
/**
* Read the configuration.
*/
- public void init(final ServletConfig config)
- throws ServletException
- {
+ public void init(final ServletConfig config)
+ throws ServletException {
super.init(config);
String suDNStr = config.getInitParameter(SU_DNS);
- if (suDNStr != null)
- {
+ if (suDNStr != null) {
String[] dns = suDNStr.split("\n");
- for (String dn : dns)
- {
+ for (String dn : dns) {
String sp = dn.replaceAll("\"", "").trim();
X500Principal su = new X500Principal(sp);
suDNs.add(su);
@@ -127,129 +121,106 @@ public void init(final ServletConfig config)
/**
* Determines the caller subject before passing the request to the
* DelegationServlet.
- *
+ *
* @param request
- * servlet request
+ * servlet request
* @param response
- * servlet response
+ * servlet response
*/
@Override
protected void service(final HttpServletRequest request,
- final HttpServletResponse response) throws IOException
- {
+ final HttpServletResponse response) throws IOException {
WebServiceLogInfo logInfo = new ServletLogInfo(request);
LOGGER.info(logInfo.start());
long start = System.currentTimeMillis();
- try
- {
+ try {
final Subject currentSubject = createSubject(request);
logInfo.setSubject(currentSubject);
-
+
Subject.doAs(currentSubject,
- new PrivilegedExceptionAction()
- {
- public Object run() throws IOException
- {
- handleService(request, response);
- return null; // nothing to return
- }
- });
- }
- catch (Throwable t)
- {
+ new PrivilegedExceptionAction() {
+ public Object run() throws IOException {
+ handleService(request, response);
+ return null; // nothing to return
+ }
+ });
+ } catch (Throwable t) {
LOGGER.debug(t);
- if (t instanceof PrivilegedActionException)
- {
- t = ((PrivilegedActionException)t).getCause();
+ if (t instanceof PrivilegedActionException) {
+ t = ((PrivilegedActionException) t).getCause();
LOGGER.debug(t);
}
-
+
logInfo.setMessage(t.getMessage());
logInfo.setSuccess(false);
-
- if (t instanceof AccessControlException)
- {
+
+ if (t instanceof AccessControlException) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().println("Unauthorized");
- }
- else if (t instanceof IllegalArgumentException)
- {
+ } else if (t instanceof IllegalArgumentException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().println("Bad Request: " + t.getMessage());
- }
- else
- {
+ } else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().println("Internal Error: " + t.getMessage());
}
- }
- finally
- {
+ } finally {
logInfo.setElapsedTime(System.currentTimeMillis() - start);
LOGGER.info(logInfo.end());
}
}
private void handleService(HttpServletRequest request,
- HttpServletResponse response) throws IOException
- {
+ HttpServletResponse response) throws IOException {
super.service(request, response);
}
/**
* Create a subject, and add all of the X509 Principals into it.
- *
+ *
* @param request
- * The Request to create from.
+ * The Request to create from.
* @return An instance of a Subject.
*/
@SuppressWarnings("unchecked")
- private Subject createSubject(final HttpServletRequest request)
- {
+ private Subject createSubject(final HttpServletRequest request) {
X509Certificate[] ca = (X509Certificate[]) request
.getAttribute("javax.servlet.request.X509Certificate");
Collection certs = null;
- if (ca != null && ca.length > 0)
+ if (ca != null && ca.length > 0) {
certs = Arrays.asList(ca);
+ }
// only ssl connections accepted (no remote user)
Subject callerSubject = AuthenticationUtil.getSubject(request);
Subject ret = callerSubject;
-
+
// check whether user is superuser executing something on users
// behalf
String userDN = request.getParameter("DN");
- if (userDN != null)
- {
+ if (userDN != null) {
Set authPS = callerSubject.getPrincipals(X500Principal.class);
boolean isSU = false;
- for (X500Principal caller : authPS)
- {
- for (X500Principal suDN : suDNs)
- {
- if (AuthenticationUtil.equals(caller, suDN))
- {
+ for (X500Principal caller : authPS) {
+ for (X500Principal suDN : suDNs) {
+ if (AuthenticationUtil.equals(caller, suDN)) {
// super user doing something on users behalf
// build and return a different subject
isSU = true;
}
-
+
}
}
- if (isSU)
- {
+ if (isSU) {
X500Principal delegatedPrinc = new X500Principal(userDN);
ret = new Subject();
ret.getPrincipals().add(delegatedPrinc);
LOGGER.debug("Superuser ... access on behalf of user " + userDN);
- }
- else
- {
+ } else {
throw new AccessControlException("create certficate for " + userDN);
}
}
-
-
+
return ret;
}
}
diff --git a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CertificateDAO.java b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CertificateDAO.java
index 56ad311..a48a7e3 100644
--- a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CertificateDAO.java
+++ b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/CertificateDAO.java
@@ -3,7 +3,7 @@
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
-* (c) 2011. (c) 2011.
+* (c) 2020. (c) 2020.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
@@ -65,129 +65,106 @@
* $Revision: 5 $
*
************************************************************************
-*/
+ */
package ca.nrc.cadc.cred.server;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.SSLUtil;
+import ca.nrc.cadc.auth.X509CertificateChain;
+import ca.nrc.cadc.date.DateUtil;
+import ca.nrc.cadc.db.DBUtil;
+import ca.nrc.cadc.profiler.Profiler;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
-
+import javax.naming.NamingException;
import javax.security.auth.x500.X500Principal;
import javax.sql.DataSource;
-
import org.apache.log4j.Logger;
import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.SSLUtil;
-import ca.nrc.cadc.auth.X509CertificateChain;
-import ca.nrc.cadc.db.DBUtil;
-import ca.nrc.cadc.profiler.Profiler;
-import ca.nrc.cadc.vosi.avail.CheckDataSource;
-import ca.nrc.cadc.vosi.avail.CheckResource;
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.spec.InvalidKeySpecException;
-import javax.naming.NamingException;
-import org.springframework.jdbc.core.JdbcTemplate;
-
/**
- * Class to persist certificates in a relational database table. This class has
+ * Class to persist certificates in a relational database table. This class has
* been only tested with Sybase ASE 15 so far.
- *
+ *
* @author pdowler
*
*/
-public class CertificateDAO
-{
- private static final Logger logger = Logger.getLogger(CertificateDAO.class);
+public class CertificateDAO {
+ private static final Logger log = Logger.getLogger(CertificateDAO.class);
- private final CertificateSchema config;
+ private final String tableName;
+ private final DataSource dataSource;
- public static class CertificateSchema
- {
- private final String dataSourceName;
- public static String DEFAULT_CERT_TABLE = "x509_certificates";
+ public static class CertificateSchema {
+ private final String dataSourceName;
private final String certTable;
-
- /**
- * Backwards compatible constructor with default table name.
- *
- * @param dataSourceName
- * @param catalog
- * @param schema
- */
- public CertificateSchema(String dataSourceName, String catalog, String schema)
- {
- this(dataSourceName, catalog, schema, DEFAULT_CERT_TABLE);
- }
+
/**
* Constructor for certificate table description. The catalog and schema are optional
- * (null values are allowed).
- *
+ * (null values are allowed).
+ *
* @param dataSourceName JNDI DataSource name
* @param catalog optional catalog (database) name
* @param schema optional schema name
* @param table certificate table name (required)
*/
- public CertificateSchema(String dataSourceName, String catalog, String schema, String table)
- {
+ public CertificateSchema(String dataSourceName, String catalog, String schema) {
this.dataSourceName = dataSourceName;
- if (table == null)
- throw new IllegalArgumentException("table name cannot be null");
-
+
StringBuilder sb = new StringBuilder();
- if (catalog != null)
+ if (catalog != null) {
sb.append(catalog).append(".");
- if (schema != null)
+ }
+ if (schema != null) {
sb.append(schema);
- if (sb.length() > 0)
+ }
+ if (sb.length() > 0) {
sb.append("."); // yeah: double dot if catalog!= null and schema==null
- sb.append(table);
+ }
+ sb.append(X509CertificateChain.class.getSimpleName());
this.certTable = sb.toString();
}
-
- public String getTable()
- {
+
+ public String getTable() {
return certTable;
}
- public DataSource getDataSource()
- {
- try
- {
- logger.debug("lookup datasource: " + dataSourceName);
- return DBUtil.getDataSource(dataSourceName);
- }
- catch(NamingException ex)
- {
- throw new RuntimeException("CONFIG: failed to find DataSource " + dataSourceName);
- }
- }
}
- public CertificateDAO(CertificateSchema config)
- {
- this.config = config;
- }
-
- public CheckResource getCheckResource()
- {
- String sql = "select top 1 hash_dn from " + config.getTable();
- return new CheckDataSource(config.getDataSource(), sql);
+
+ public CertificateDAO(CertificateSchema config) {
+ this.tableName = config.certTable;
+ try {
+ log.debug("lookup datasource: " + config.dataSourceName);
+ this.dataSource = DBUtil.findJNDIDataSource(config.dataSourceName);
+ } catch (NamingException ex) {
+ throw new RuntimeException("CONFIG: failed to find DataSource " + config.dataSourceName);
+ }
}
- public void put(X509CertificateChain chain)
- {
+ // needed by intTest
+ DataSource getDataSource() {
+ return dataSource;
+ }
+
+ public void put(X509CertificateChain chain) {
Profiler profiler = new Profiler(this.getClass());
String hashKey = chain.getHashKey();
String canonDn = AuthenticationUtil.canonizeDistinguishedName(chain.getPrincipal().getName());
@@ -195,77 +172,70 @@ public void put(X509CertificateChain chain)
String certChainStr = chain.certificateString();
byte[] bytesPrivateKey = chain.getPrivateKey().getEncoded();
//TODO just for testing - padded with zeros
- byte[] testBytesPrivateKey = Arrays.copyOf(bytesPrivateKey, bytesPrivateKey.length+1);
- testBytesPrivateKey[testBytesPrivateKey.length-1]=1;
+ byte[] testBytesPrivateKey = Arrays.copyOf(bytesPrivateKey, bytesPrivateKey.length + 1);
+ testBytesPrivateKey[testBytesPrivateKey.length - 1] = 1;
String csr = chain.getCsrString();
-
- JdbcTemplate jdbc = new JdbcTemplate(config.getDataSource());
- if (recordExists(hashKey))
- {
- String sql = "update " + config.getTable()
- + " set canon_dn = ?, exp_date = ?, cert_chain = ?, private_key = ?, csr = ? where hash_dn=?";
- Object[] args = new Object[] { canonDn, expDate, certChainStr, testBytesPrivateKey, csr, hashKey };
- int[] argTypes = new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARBINARY, Types.VARCHAR, Types.VARCHAR };
- jdbc.update(sql, args, argTypes);
- }
- else
- {
- String sql = "insert into " + config.getTable()
- + " (canon_dn, exp_date, cert_chain, private_key, csr, hash_dn) values (?,?,?,?,?,?)";
- Object[] args = new Object[] { canonDn, expDate, certChainStr, testBytesPrivateKey, csr, hashKey };
- int[] argTypes =
- new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARBINARY, Types.VARCHAR, Types.VARCHAR };
+ JdbcTemplate jdbc = new JdbcTemplate(dataSource);
+ Date now = new Date(); // TODO: should get from the database
+ if (exists(hashKey)) {
+ String sql = "update " + tableName
+ + " set canon_dn = ?, exp_date = ?, cert_chain = ?, private_key = ?, csr = ?, lastModified = ? where hash_dn=?";
+ Object[] args = new Object[]{canonDn, expDate, certChainStr, testBytesPrivateKey, csr, now, hashKey};
+ int[] argTypes = new int[]{Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARBINARY, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR};
+ log.debug("put: " + sql);
+ jdbc.update(sql, args, argTypes);
+ } else {
+ String sql = "insert into " + tableName
+ + " (canon_dn, exp_date, cert_chain, private_key, csr, hash_dn, lastModified) values (?,?,?,?,?,?,?)";
+ Object[] args = new Object[]{canonDn, expDate, certChainStr, testBytesPrivateKey, csr, hashKey, now};
+ int[] argTypes
+ = new int[]{Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARBINARY, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP};
+ log.debug("put: " + sql);
jdbc.update(sql, args, argTypes);
}
profiler.checkpoint("put");
}
-
- public X509CertificateChain get(X500Principal principal)
- {
- if (principal == null)
+
+ public X509CertificateChain get(X500Principal principal) {
+ if (principal == null) {
return null;
+ }
String hashKey = X509CertificateChain.genHashKey(principal);
return get(hashKey);
}
- public X509CertificateChain get(String hashKey)
- {
+ public X509CertificateChain get(String hashKey) {
Profiler profiler = new Profiler(this.getClass());
X509CertificateChain x509CertificateChain = null;
- String query = "select canon_dn, exp_date, cert_chain, private_key, csr from " + config.getTable() + " where hash_dn = ? ";
-
-
- try
- {
- JdbcTemplate jdbc = new JdbcTemplate(config.getDataSource());
- Map map = jdbc.queryForMap(query, new String[] { hashKey });
+ String sql = "select canon_dn, exp_date, cert_chain, private_key, csr from " + tableName + " where hash_dn = ? ";
+ log.debug("get: " + sql);
+ try {
+ JdbcTemplate jdbc = new JdbcTemplate(dataSource);
+ Map map = jdbc.queryForMap(sql, new String[]{hashKey});
String canonDn = (String) map.get("canon_dn");
Date expDate = (Date) map.get("exp_date");
String certChainStr = (String) map.get("cert_chain");
byte[] bytesPrivateKey = (byte[]) map.get("private_key");
-
+
// Sybase trims the trailing 0's of a varbinary. To compensate we add 0's to the
// privateKey byte array. Extra bytes in the private key array are ignored
// when the key is built so the added 0's are only used when needed.
// ad 20/07/2011
- bytesPrivateKey = Arrays.copyOf(bytesPrivateKey, bytesPrivateKey.length+10);
-
+ bytesPrivateKey = Arrays.copyOf(bytesPrivateKey, bytesPrivateKey.length + 10);
+
String csrStr = (String) map.get("csr");
PrivateKey privateKey = SSLUtil.readPrivateKey(bytesPrivateKey);
X500Principal principal = new X500Principal(canonDn);
- if (certChainStr != null)
- {
+ if (certChainStr != null) {
byte[] bytesCertChain = certChainStr.getBytes();
X509Certificate[] certs = SSLUtil.readCertificateChain(bytesCertChain);
x509CertificateChain = new X509CertificateChain(Arrays.asList(certs));
- }
- else
- {
+ } else {
x509CertificateChain = new X509CertificateChain(principal, privateKey, csrStr);
}
x509CertificateChain.setCsrString(csrStr);
@@ -273,26 +243,16 @@ public X509CertificateChain get(String hashKey)
x509CertificateChain.setHashKey(hashKey);
x509CertificateChain.setKey(privateKey);
x509CertificateChain.setPrincipal(principal);
- }
- catch (EmptyResultDataAccessException e)
- {
+ } catch (EmptyResultDataAccessException e) {
// Record not exists.
return null;
- }
- catch(InvalidKeySpecException ex)
- {
+ } catch (InvalidKeySpecException ex) {
throw new RuntimeException("BUG: failed to read private key", ex);
- }
- catch(NoSuchAlgorithmException ex)
- {
+ } catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("BUG: failed to read private key", ex);
- }
- catch(CertificateException ex)
- {
+ } catch (CertificateException ex) {
throw new RuntimeException("BUG: failed to read certficate chain", ex);
- }
- catch(IOException ex)
- {
+ } catch (IOException ex) {
throw new RuntimeException("BUG: failed to read certificate chain", ex);
}
profiler.checkpoint("get");
@@ -302,34 +262,33 @@ public X509CertificateChain get(String hashKey)
/* (non-Javadoc)
* @see ca.nrc.cadc.accesscontrol.dao.CertificateDAO#delete(java.lang.String)
*/
- public void delete(String hashKey)
- {
+ public void delete(String hashKey) {
Profiler profiler = new Profiler(this.getClass());
- String sql = "delete from " + config.getTable() + " where hash_dn = ? ";
- JdbcTemplate jdbc = new JdbcTemplate(config.getDataSource());
- jdbc.update(sql, new String[] { hashKey });
+ String sql = "delete from " + tableName + " where hash_dn = ? ";
+ log.debug("delete: " + sql);
+ JdbcTemplate jdbc = new JdbcTemplate(dataSource);
+ jdbc.update(sql, new String[]{hashKey});
profiler.checkpoint("delete");
}
- private boolean recordExists(String hashKey)
- {
+ // used by intTest
+ boolean exists(String hashKey) {
RowMapper rowMapper = new SingleColumnRowMapper(String.class);
- String query = "select canon_dn from " + config.getTable() + " where hash_dn = ? ";
- JdbcTemplate jdbc = new JdbcTemplate(config.getDataSource());
- List dnList = jdbc.query(query, new String[] { hashKey }, rowMapper);
+ String sql = "select canon_dn from " +tableName + " where hash_dn = ? ";
+ log.debug("exists: " + sql);
+ JdbcTemplate jdbc = new JdbcTemplate(dataSource);
+ List dnList = jdbc.query(sql, new String[] { hashKey }, rowMapper);
return (dnList != null && dnList.size() == 1);
}
- public List getAllHashKeys()
- {
+ public List getAllHashKeys() {
Profiler profiler = new Profiler(this.getClass());
- String query = "select hash_dn from " + config.getTable();
+ String query = "select hash_dn from " + tableName;
RowMapper rowMapper = new SingleColumnRowMapper(String.class);
- JdbcTemplate jdbc = new JdbcTemplate(config.getDataSource());
+ JdbcTemplate jdbc = new JdbcTemplate(dataSource);
List hashKeyList = jdbc.query(query, rowMapper);
profiler.checkpoint("getAllHashKeys");
return hashKeyList;
}
-
}
diff --git a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/DatabaseDelegations.java b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/DatabaseDelegations.java
index ad9498c..6a74aa2 100644
--- a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/DatabaseDelegations.java
+++ b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/DatabaseDelegations.java
@@ -3,7 +3,7 @@
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
-* (c) 2009. (c) 2009.
+* (c) 2020. (c) 2020.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
@@ -65,10 +65,13 @@
* $Revision: 4 $
*
************************************************************************
-*/
+ */
package ca.nrc.cadc.cred.server;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.X509CertificateChain;
+import ca.nrc.cadc.cred.CertUtil;
import java.io.IOException;
import java.io.Writer;
import java.security.AccessControlContext;
@@ -83,54 +86,43 @@
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Set;
-
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMWriter;
-
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.X509CertificateChain;
-import ca.nrc.cadc.cred.CertUtil;
import org.apache.log4j.Logger;
import org.astrogrid.security.delegation.CertificateSigningRequest;
import org.astrogrid.security.delegation.Delegations;
import org.astrogrid.security.delegation.Util;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMWriter;
/**
- * Implementation of the base Delegations that stores certificates in a
+ * Implementation of the base Delegations that stores certificates in a
* relational database.
- *
+ *
* @author pdowler
*/
-public class DatabaseDelegations extends Delegations
-{
+public class DatabaseDelegations extends Delegations {
+
private static final Logger log = Logger.getLogger(DatabaseDelegations.class);
-
+
private CertificateDAO certificateDAO = null;
private KeyPairGenerator keyPairGenerator;
-
- protected DatabaseDelegations(String dataSourceName, CertificateDAO.CertificateSchema config)
- {
+
+ protected DatabaseDelegations(String dataSourceName, CertificateDAO.CertificateSchema config) {
// Add the Bouncy Castle JCE provider. This allows the CSR
// classes to work. The BC implementation of PKCS#10 depends on
// the ciphers in the BC provider.
- if (Security.getProvider("BC") == null)
- {
+ if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
- try
- {
+ try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(CertUtil.DEFAULT_KEY_LENGTH);
- }
- catch (NoSuchAlgorithmException ex)
- {
+ } catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("BUG/CONFIG: cannot load RSA key-pair generator", ex);
}
-
+
certificateDAO = new CertificateDAO(config);
}
@@ -138,21 +130,15 @@ protected DatabaseDelegations(String dataSourceName, CertificateDAO.CertificateS
* @see org.astrogrid.security.delegation.Delegations#initializeIdentity(java.lang.String)
*/
@Override
- public String initializeIdentity(String identity) throws GeneralSecurityException
- {
- try
- {
+ public String initializeIdentity(String identity) throws GeneralSecurityException {
+ try {
String canonizedDn = AuthenticationUtil.canonizeDistinguishedName(identity);
X500Principal p = new X500Principal(canonizedDn);
return initializeIdentity(p);
- }
- catch(GeneralSecurityException gex)
- {
+ } catch (GeneralSecurityException gex) {
log.debug("initializeIdentity failed", gex);
throw gex;
- }
- catch(RuntimeException t)
- {
+ } catch (RuntimeException t) {
log.debug("initializeIdentity failed", t);
throw t;
}
@@ -162,10 +148,8 @@ public String initializeIdentity(String identity) throws GeneralSecurityExceptio
* @see org.astrogrid.security.delegation.Delegations#initializeIdentity(javax.security.auth.x500.X500Principal)
*/
@Override
- public String initializeIdentity(X500Principal principal) throws GeneralSecurityException
- {
- try
- {
+ public String initializeIdentity(X500Principal principal) throws GeneralSecurityException {
+ try {
String canonizedDn = AuthenticationUtil.canonizeDistinguishedName(principal.getName());
X500Principal p = new X500Principal(canonizedDn);
String hashKey = hash(p);
@@ -178,14 +162,10 @@ public String initializeIdentity(X500Principal principal) throws GeneralSecurity
certificateDAO.put(chain);
return hashKey;
- }
- catch(GeneralSecurityException gex)
- {
+ } catch (GeneralSecurityException gex) {
log.debug("initializeIdentity failed", gex);
throw gex;
- }
- catch(RuntimeException t)
- {
+ } catch (RuntimeException t) {
log.debug("initializeIdentity failed", t);
throw t;
}
@@ -195,11 +175,9 @@ public String initializeIdentity(X500Principal principal) throws GeneralSecurity
* @see org.astrogrid.security.delegation.Delegations#getCsr(java.lang.String)
*/
@Override
- public CertificateSigningRequest getCsr(String hashKey)
- {
+ public CertificateSigningRequest getCsr(String hashKey) {
X509CertificateChain x509CertificateChain = certificateDAO.get(hashKey);
- if (x509CertificateChain == null)
- {
+ if (x509CertificateChain == null) {
return null;
}
String csrString = x509CertificateChain.getCsrString();
@@ -211,11 +189,9 @@ public CertificateSigningRequest getCsr(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#getPrivateKey(java.lang.String)
*/
@Override
- public PrivateKey getPrivateKey(String hashKey)
- {
+ public PrivateKey getPrivateKey(String hashKey) {
X509CertificateChain x509CertificateChain = certificateDAO.get(hashKey);
- if (x509CertificateChain == null)
- {
+ if (x509CertificateChain == null) {
return null;
}
return x509CertificateChain.getPrivateKey();
@@ -225,11 +201,9 @@ public PrivateKey getPrivateKey(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#getCertificate(java.lang.String)
*/
@Override
- public X509Certificate[] getCertificates(String hashKey)
- {
+ public X509Certificate[] getCertificates(String hashKey) {
X509CertificateChain x509CertificateChain = certificateDAO.get(hashKey);
- if (x509CertificateChain == null)
- {
+ if (x509CertificateChain == null) {
return null;
}
return x509CertificateChain.getChain();
@@ -239,8 +213,7 @@ public X509Certificate[] getCertificates(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#remove(java.lang.String)
*/
@Override
- public void remove(String hashKey)
- {
+ public void remove(String hashKey) {
certificateDAO.delete(hashKey);
}
@@ -248,8 +221,7 @@ public void remove(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#isKnown(java.lang.String)
*/
@Override
- public boolean isKnown(String hashKey)
- {
+ public boolean isKnown(String hashKey) {
X509CertificateChain chain = certificateDAO.get(hashKey);
return (chain != null);
}
@@ -258,24 +230,21 @@ public boolean isKnown(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#setCertificate(java.lang.String, java.security.cert.X509Certificate)
*/
@Override
- public void setCertificates(String hashKey, X509Certificate[] certificates) throws InvalidKeyException
- {
+ public void setCertificates(String hashKey, X509Certificate[] certificates) throws InvalidKeyException {
X509CertificateChain chain = certificateDAO.get(hashKey);
- if (chain != null)
- {
+ if (chain != null) {
chain.setChain(certificates);
certificateDAO.put(chain);
- }
- else
+ } else {
throw new InvalidKeyException("No identity matches the hash key " + hashKey);
+ }
}
/* (non-Javadoc)
* @see org.astrogrid.security.delegation.Delegations#getPrincipals()
*/
@Override
- public Object[] getPrincipals()
- {
+ public Object[] getPrincipals() {
// List hashKeyList = certificateDAO.getAllHashKeys();
// return hashKeyList.toArray();
//TODO AD: this is a workaround to send the hash to the caller when it
@@ -284,29 +253,24 @@ public Object[] getPrincipals()
Subject subject = Subject.getSubject(acContext);
Set principals = subject
.getPrincipals(X500Principal.class);
- if (principals.size() == 0)
- {
+ if (principals.size() == 0) {
throw new AccessControlException(
"Delegation failed because the caller is not authenticated.");
- }
- else if (principals.size() > 1)
- {
+ } else if (principals.size() > 1) {
throw new AccessControlException(
"Delegation failed because caller autheticated with multiple certificates.");
}
- return new String[] { X509CertificateChain.genHashKey(principals
- .iterator().next()) };
+ return new String[]{X509CertificateChain.genHashKey(principals
+ .iterator().next())};
}
/* (non-Javadoc)
* @see org.astrogrid.security.delegation.Delegations#getName(java.lang.String)
*/
@Override
- public String getName(String hashKey)
- {
+ public String getName(String hashKey) {
X509CertificateChain x509CertificateChain = certificateDAO.get(hashKey);
- if (x509CertificateChain == null)
- {
+ if (x509CertificateChain == null) {
return null;
}
String dn = x509CertificateChain.getPrincipal().getName();
@@ -317,17 +281,15 @@ public String getName(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#getKeys(java.lang.String)
*/
@Override
- public KeyPair getKeys(String hashKey)
- {
- throw new RuntimeException("getKeys() not implemented in DAO version implementation.");
+ public KeyPair getKeys(String hashKey) {
+ throw new RuntimeException("getKeys() not implemented in DAO version implementation.");
}
/* (non-Javadoc)
* @see org.astrogrid.security.delegation.Delegations#hasCertificate(java.lang.String)
*/
@Override
- public boolean hasCertificate(String hashKey)
- {
+ public boolean hasCertificate(String hashKey) {
X509CertificateChain chain = certificateDAO.get(hashKey);
return (chain.getChain() != null);
}
@@ -336,17 +298,14 @@ public boolean hasCertificate(String hashKey)
* @see org.astrogrid.security.delegation.Delegations#writeCertificate(java.lang.String, java.io.Writer)
*/
@Override
- public void writeCertificate(String hashKey, Writer out) throws IOException
- {
+ public void writeCertificate(String hashKey, Writer out) throws IOException {
PEMWriter pem = new PEMWriter(out);
X509Certificate[] certs = getCertificates(hashKey);
- if (certs == null)
- {
+ if (certs == null) {
throw new IllegalArgumentException(
"No certificate corresponding to the haskey: " + hashKey);
}
- for (X509Certificate cert : certs)
- {
+ for (X509Certificate cert : certs) {
pem.writeObject(cert);
}
pem.flush();
diff --git a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/InitDatabaseCDP.java b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/InitDatabaseCDP.java
new file mode 100644
index 0000000..b7d5399
--- /dev/null
+++ b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/InitDatabaseCDP.java
@@ -0,0 +1,113 @@
+/*
+************************************************************************
+******************* CANADIAN ASTRONOMY DATA CENTRE *******************
+************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
+*
+* (c) 2020. (c) 2020.
+* Government of Canada Gouvernement du Canada
+* National Research Council Conseil national de recherches
+* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
+* All rights reserved Tous droits réservés
+*
+* NRC disclaims any warranties, Le CNRC dénie toute garantie
+* expressed, implied, or énoncée, implicite ou légale,
+* statutory, of any kind with de quelque nature que ce
+* respect to the software, soit, concernant le logiciel,
+* including without limitation y compris sans restriction
+* any warranty of merchantability toute garantie de valeur
+* or fitness for a particular marchande ou de pertinence
+* purpose. NRC shall not be pour un usage particulier.
+* liable in any event for any Le CNRC ne pourra en aucun cas
+* damages, whether direct or être tenu responsable de tout
+* indirect, special or general, dommage, direct ou indirect,
+* consequential or incidental, particulier ou général,
+* arising from the use of the accessoire ou fortuit, résultant
+* software. Neither the name de l'utilisation du logiciel. Ni
+* of the National Research le nom du Conseil National de
+* Council of Canada nor the Recherches du Canada ni les noms
+* names of its contributors may de ses participants ne peuvent
+* be used to endorse or promote être utilisés pour approuver ou
+* products derived from this promouvoir les produits dérivés
+* software without specific prior de ce logiciel sans autorisation
+* written permission. préalable et particulière
+* par écrit.
+*
+* This file is part of the Ce fichier fait partie du projet
+* OpenCADC project. OpenCADC.
+*
+* OpenCADC is free software: OpenCADC est un logiciel libre ;
+* you can redistribute it and/or vous pouvez le redistribuer ou le
+* modify it under the terms of modifier suivant les termes de
+* the GNU Affero General Public la “GNU Affero General Public
+* License as published by the License” telle que publiée
+* Free Software Foundation, par la Free Software Foundation
+* either version 3 of the : soit la version 3 de cette
+* License, or (at your option) licence, soit (à votre gré)
+* any later version. toute version ultérieure.
+*
+* OpenCADC is distributed in the OpenCADC est distribué
+* hope that it will be useful, dans l’espoir qu’il vous
+* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
+* without even the implied GARANTIE : sans même la garantie
+* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
+* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
+* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
+* General Public License for Générale Publique GNU Affero
+* more details. pour plus de détails.
+*
+* You should have received Vous devriez avoir reçu une
+* a copy of the GNU Affero copie de la Licence Générale
+* General Public License along Publique GNU Affero avec
+* with OpenCADC. If not, see OpenCADC ; si ce n’est
+* . pas le cas, consultez :
+* .
+*
+************************************************************************
+*/
+
+package ca.nrc.cadc.cred.server;
+
+import ca.nrc.cadc.db.version.InitDatabase;
+import java.net.URL;
+import javax.sql.DataSource;
+import org.apache.log4j.Logger;
+
+/**
+ *
+ * @author pdowler
+ */
+public class InitDatabaseCDP extends InitDatabase {
+ private static final Logger log = Logger.getLogger(InitDatabaseCDP.class);
+ public static final String MODEL_NAME = "CDP";
+ public static final String MODEL_VERSION = "1.4";
+ public static final String PREV_MODEL_VERSION = "n/a";
+ //public static final String PREV_MODEL_VERSION = "DO-NOT_UPGRADE-BY-ACCIDENT";
+
+ static String[] CREATE_SQL = new String[] {
+ "cred.ModelVersion.sql",
+ "cred.X509CertificateChain.sql",
+ "cred.permissions.sql"
+ };
+
+ static String[] UPGRADE_SQL = new String[] {
+ "cred.permissions.sql"
+ };
+
+ public InitDatabaseCDP(DataSource ds, String database, String schema) {
+ super(ds, database, schema, MODEL_NAME, MODEL_VERSION, PREV_MODEL_VERSION);
+ for (String s : CREATE_SQL) {
+ createSQL.add(s);
+ }
+ for (String s : UPGRADE_SQL) {
+ upgradeSQL.add(s);
+ }
+ }
+
+ @Override
+ protected URL findSQL(String fname) {
+ // SQL files are stored inside the jar file
+ return InitDatabase.class.getClassLoader().getResource(fname);
+ }
+
+
+}
diff --git a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/ProxyServlet.java b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/ProxyServlet.java
index 37160ef..b9dd449 100644
--- a/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/ProxyServlet.java
+++ b/cadc-cdp-server/src/main/java/ca/nrc/cadc/cred/server/ProxyServlet.java
@@ -3,7 +3,7 @@
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
-* (c) 2011. (c) 2011.
+* (c) 2020. (c) 2020.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
@@ -65,20 +65,28 @@
* $Revision: 5 $
*
************************************************************************
-*/
+ */
package ca.nrc.cadc.cred.server;
import ca.nrc.cadc.auth.AuthMethod;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.X509CertificateChain;
+import ca.nrc.cadc.cred.server.actions.DelegationAction;
+import ca.nrc.cadc.cred.server.actions.DelegationActionFactory;
+import ca.nrc.cadc.io.ByteCountWriter;
+import ca.nrc.cadc.log.ServletLogInfo;
+import ca.nrc.cadc.log.WebServiceLogInfo;
+import ca.nrc.cadc.net.ResourceNotFoundException;
import java.io.BufferedWriter;
import java.io.IOException;
+import java.io.PrintWriter;
import java.security.AccessControlException;
import java.security.PrivilegedActionException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
-
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
import javax.servlet.ServletConfig;
@@ -86,27 +94,16 @@
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-
import org.apache.log4j.Logger;
import org.bouncycastle.openssl.PEMWriter;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.X509CertificateChain;
-import ca.nrc.cadc.cred.server.actions.DelegationAction;
-import ca.nrc.cadc.cred.server.actions.DelegationActionFactory;
-import ca.nrc.cadc.io.ByteCountWriter;
-import ca.nrc.cadc.log.ServletLogInfo;
-import ca.nrc.cadc.log.WebServiceLogInfo;
-import ca.nrc.cadc.net.ResourceNotFoundException;
-import java.io.PrintWriter;
-
/**
- * Servlet used to download a proxy certificate (PEM file) for the caller or an
+ * Servlet used to download a proxy certificate (PEM file) for the caller or an
* optionally specified identity.
- *
+ *
*/
-public class ProxyServlet extends HttpServlet
-{
+public class ProxyServlet extends HttpServlet {
+
public static final String TRUSTED_PRINCIPALS_PARAM = "trustedPrincipals";
public static final String DSNAME = "datasource";
public static final String CATALOG = "catalog";
@@ -121,108 +118,97 @@ public class ProxyServlet extends HttpServlet
//
static final String CERTIFICATE_CONTENT_TYPE = "application/x-pem-file";
static final String CERTIFICATE_FILENAME = "cadcproxy.pem";
-
+
private static final long serialVersionUID = 2740612605831266225L;
private static Logger LOGGER = Logger.getLogger(ProxyServlet.class);
// The set of trusted principals allowed to call this service
- private Map trustedPrincipals =
- new HashMap();
+ private Map trustedPrincipals
+ = new HashMap();
private String dataSourceName;
private String database;
private String schema;
/**
* Read the configuration.
- * @param config The ServletConfig as provided by the container.
+ *
+ * @param config The ServletConfig as provided by the container.
* @throws javax.servlet.ServletException
*/
@Override
- public void init(final ServletConfig config)
- throws ServletException
- {
+ public void init(final ServletConfig config)
+ throws ServletException {
super.init(config);
// get the trusted principals from config
- String trustedPrincipalsValue =
- config.getInitParameter(TRUSTED_PRINCIPALS_PARAM);
- if (trustedPrincipalsValue != null)
- {
+ String trustedPrincipalsValue
+ = config.getInitParameter(TRUSTED_PRINCIPALS_PARAM);
+ if (trustedPrincipalsValue != null) {
StringTokenizer st = new StringTokenizer(trustedPrincipalsValue,
- "\n\t\r", false);
- while (st.hasMoreTokens())
- {
+ "\n\t\r", false);
+ while (st.hasMoreTokens()) {
String principalStr = st.nextToken();
StringTokenizer st2 = new StringTokenizer(principalStr, ":",
- false);
+ false);
String principal; // the principal of the trusted client
Float maxDaysValid; // maximum lifetime of the returned proxy
- if (st2.countTokens() == 1)
- {
+ if (st2.countTokens() == 1) {
principal = principalStr.trim();
maxDaysValid = 30.0f;
- }
- else if (st2.countTokens() == 2)
- {
+ } else if (st2.countTokens() == 2) {
principal = st2.nextToken().trim();
maxDaysValid = Float.parseFloat(st2.nextToken().trim());
- if (maxDaysValid <= 0)
- {
+ if (maxDaysValid <= 0) {
throw new IllegalArgumentException(
"Maximum valid days must be positive, "
+ maxDaysValid);
}
- }
- else
- {
+ } else {
throw new IllegalArgumentException(
- "Cannot parse trusted principal from servlet " +
- "config: " + principalStr);
+ "Cannot parse trusted principal from servlet "
+ + "config: " + principalStr);
}
- if (principal != null)
- {
+ if (principal != null) {
principal = principal.replaceAll("\"", "");
LOGGER.info("trusted: " + principal + " , max days valid: " + maxDaysValid);
trustedPrincipals.put(new X500Principal(principal), maxDaysValid);
}
}
}
-
+
this.dataSourceName = config.getInitParameter(DSNAME);
this.database = config.getInitParameter(CATALOG);
this.schema = config.getInitParameter(SCHEMA);
-
+
LOGGER.info("persistence: " + dataSourceName + " " + database + " "
- + schema);
+ + schema);
}
/**
* Obtain the current Subject.
*
- * @param request The HTTP Request.
- * @return Subject for the current Request, or null if none.
+ * @param request The HTTP Request.
+ * @return Subject for the current Request, or null if none.
* @throws IOException
*/
Subject getCurrentSubject(final HttpServletRequest request)
- throws IOException
- {
+ throws IOException {
return AuthenticationUtil.getSubject(request);
}
/**
* Obtain the current X509 certificate chain.
- * @param request The HTTP Request.
- * @param subject The current Subject.
- * @return X509CertificateChain instance.
+ *
+ * @param request The HTTP Request.
+ * @param subject The current Subject.
+ * @return X509CertificateChain instance.
* @throws Exception
*/
X509CertificateChain getX509CertificateChain(
final HttpServletRequest request, final Subject subject)
- throws Exception
- {
+ throws Exception {
AuthMethod am = AuthenticationUtil.getAuthMethod(subject);
- if ((am == null) || AuthMethod.ANON.equals(am))
- {
+ if ((am == null) || AuthMethod.ANON.equals(am)) {
throw new AccessControlException("permission denied");
}
@@ -231,21 +217,15 @@ X509CertificateChain getX509CertificateChain(
DelegationAction delegationAction = factory.getDelegationAction();
X509CertificateChain certificateChain;
- try
- {
+ try {
certificateChain = Subject.doAs(subject, delegationAction);
- }
- catch(PrivilegedActionException ex)
- {
+ } catch (PrivilegedActionException ex) {
throw ex.getException();
}
- if (certificateChain.getChain() == null)
- {
+ if (certificateChain.getChain() == null) {
throw new ResourceNotFoundException("No signed certificate");
- }
- else
- {
+ } else {
return certificateChain;
}
}
@@ -253,39 +233,32 @@ X509CertificateChain getX509CertificateChain(
/**
* Write out the certificate chain to the response as a download.
*
- * @param certificateChain The X509CertificateChain instance to write.
- * @param response The HTTP Response.
- * @param logInfo The logging object to update.
+ * @param certificateChain The X509CertificateChain instance to write.
+ * @param response The HTTP Response.
+ * @param logInfo The logging object to update.
* @throws Exception
*/
void writeCertificateChain(final X509CertificateChain certificateChain,
- final HttpServletResponse response,
- final WebServiceLogInfo logInfo)
- throws Exception
- {
+ final HttpServletResponse response,
+ final WebServiceLogInfo logInfo)
+ throws Exception {
// This is streamed directly, so there is no way to set the content
// length.
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(CERTIFICATE_CONTENT_TYPE);
response.setHeader("Content-Disposition",
- "attachment; filename=" + CERTIFICATE_FILENAME);
- final ByteCountWriter out =
- new ByteCountWriter(new BufferedWriter(response.getWriter(),
- 8192));
+ "attachment; filename=" + CERTIFICATE_FILENAME);
+ final ByteCountWriter out
+ = new ByteCountWriter(new BufferedWriter(response.getWriter(),
+ 8192));
final PEMWriter pemWriter = new PEMWriter(out);
- try
- {
+ try {
writePEM(certificateChain, pemWriter);
- }
- finally
- {
- try
- {
+ } finally {
+ try {
pemWriter.close();
- }
- catch(IOException ex)
- {
+ } catch (IOException ex) {
// Do nothing
}
@@ -296,18 +269,16 @@ void writeCertificateChain(final X509CertificateChain certificateChain,
/**
* Write out the PEM information.
*
- * @param certificateChain The certificate chain to write.
- * @param pemWriter The PEM Writer to write out to.
+ * @param certificateChain The certificate chain to write.
+ * @param pemWriter The PEM Writer to write out to.
* @throws IOException
*/
void writePEM(final X509CertificateChain certificateChain,
- final PEMWriter pemWriter) throws IOException
- {
+ final PEMWriter pemWriter) throws IOException {
pemWriter.writeObject(certificateChain.getChain()[0]);
pemWriter.writeObject(certificateChain.getPrivateKey());
- for (int i = 1; i < certificateChain.getChain().length; i++)
- {
+ for (int i = 1; i < certificateChain.getChain().length; i++) {
pemWriter.writeObject(certificateChain.getChain()[i]);
}
@@ -316,76 +287,61 @@ void writePEM(final X509CertificateChain certificateChain,
/**
* Handles the HTTP GET
method.
- *
+ *
* @param request servlet request
* @param response servlet response
* @throws java.io.IOException
*/
@Override
protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException
- {
+ HttpServletResponse response)
+ throws IOException {
WebServiceLogInfo logInfo = new ServletLogInfo(request);
LOGGER.info(logInfo.start());
long start = System.currentTimeMillis();
- try
- {
+ try {
final Subject subject = getCurrentSubject(request);
logInfo.setSubject(subject);
-
- final X509CertificateChain certificateChain =
- getX509CertificateChain(request, subject);
+
+ final X509CertificateChain certificateChain
+ = getX509CertificateChain(request, subject);
writeCertificateChain(certificateChain, response, logInfo);
- }
- catch(IllegalArgumentException ex)
- {
+ } catch (IllegalArgumentException ex) {
logInfo.setMessage(ex.getMessage());
logInfo.setSuccess(true);
LOGGER.debug("invalid input", ex);
writeError(response, HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
- }
- catch(UnsupportedOperationException ex)
- {
+ } catch (UnsupportedOperationException ex) {
logInfo.setMessage(ex.getMessage());
logInfo.setSuccess(true);
LOGGER.debug("unsupported", ex);
writeError(response, HttpServletResponse.SC_NOT_IMPLEMENTED, ex.getMessage());
- }
- catch(AccessControlException ex)
- {
+ } catch (AccessControlException ex) {
logInfo.setMessage(ex.getMessage());
logInfo.setSuccess(true);
LOGGER.debug("unauthorized", ex);
writeError(response, HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
- }
- catch(ResourceNotFoundException ex)
- {
+ } catch (ResourceNotFoundException ex) {
logInfo.setMessage(ex.getMessage());
logInfo.setSuccess(true);
LOGGER.debug("certificate not found", ex);
writeError(response, HttpServletResponse.SC_NOT_FOUND, ex.getMessage());
- }
- catch (Throwable t)
- {
+ } catch (Throwable t) {
String message = t.getMessage();
logInfo.setMessage(message);
logInfo.setSuccess(false);
LOGGER.error(message, t);
writeError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
- }
- finally
- {
+ } finally {
logInfo.setElapsedTime(System.currentTimeMillis() - start);
LOGGER.info(logInfo.end());
}
}
-
+
private void writeError(HttpServletResponse response, int code, String message)
- throws IOException
- {
+ throws IOException {
response.setContentType("text/plain");
response.setStatus(code);
PrintWriter pw = new PrintWriter(response.getWriter());
@@ -394,8 +350,7 @@ private void writeError(HttpServletResponse response, int code, String message)
pw.close();
}
- public Map getTrustedPrincipals()
- {
+ public Map getTrustedPrincipals() {
return Collections.unmodifiableMap(trustedPrincipals);
}
}
diff --git a/cadc-cdp-server/src/main/resources/cred.ModelVersion.sql b/cadc-cdp-server/src/main/resources/cred.ModelVersion.sql
new file mode 100644
index 0000000..ca91266
--- /dev/null
+++ b/cadc-cdp-server/src/main/resources/cred.ModelVersion.sql
@@ -0,0 +1,9 @@
+
+create table .ModelVersion
+(
+ model varchar(32) not null primary key,
+ version varchar(32) not null,
+ lastModified timestamp not null
+)
+;
+
diff --git a/cadc-cdp-server/src/main/resources/cred.X509CertificateChain.sql b/cadc-cdp-server/src/main/resources/cred.X509CertificateChain.sql
new file mode 100644
index 0000000..9c0a251
--- /dev/null
+++ b/cadc-cdp-server/src/main/resources/cred.X509CertificateChain.sql
@@ -0,0 +1,19 @@
+
+
+create table .X509CertificateChain (
+ hash_dn varchar(32) not null primary key,
+ canon_dn varchar(512) not null,
+ private_key bytea not null,
+
+ exp_date timestamp,
+ csr text,
+ cert_chain text,
+
+ lastModified timestamp not null
+);
+
+create unique index x509_canon_dn on .X509CertificateChain(canon_dn);
+
+create index x509_expiry on .X509CertificateChain(exp_date);
+
+create index x509_lastModified on .X509CertificateChain(lastModified);
diff --git a/cadc-cdp-server/src/main/resources/cred.permissions.sql b/cadc-cdp-server/src/main/resources/cred.permissions.sql
new file mode 100644
index 0000000..fa8027b
--- /dev/null
+++ b/cadc-cdp-server/src/main/resources/cred.permissions.sql
@@ -0,0 +1,4 @@
+-- very open permisions; suitable for dedicated database with minimal accounts
+grant usage on schema to public;
+grant select on all tables in schema to public;
+
diff --git a/cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/actions/DelegationActionFactoryTest.java b/cadc-cdp-server/src/obsolete/java/ca/nrc/cadc/cred/server/actions/DelegationActionFactoryTest.java
similarity index 100%
rename from cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/actions/DelegationActionFactoryTest.java
rename to cadc-cdp-server/src/obsolete/java/ca/nrc/cadc/cred/server/actions/DelegationActionFactoryTest.java
diff --git a/cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/CertificateSchemaTest.java b/cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/CertificateSchemaTest.java
index 820fe9f..be08ff2 100644
--- a/cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/CertificateSchemaTest.java
+++ b/cadc-cdp-server/src/test/java/ca/nrc/cadc/cred/server/CertificateSchemaTest.java
@@ -95,10 +95,8 @@ public void testTableNameAll()
try
{
CertificateDAO.CertificateSchema s = new CertificateDAO.CertificateSchema(null, "cat", "sch");
- Assert.assertEquals("cat.sch.x509_certificates", s.getTable());
+ Assert.assertTrue(s.getTable().startsWith("cat.sch."));
- s = new CertificateDAO.CertificateSchema(null, "cat", "sch", "foo");
- Assert.assertEquals("cat.sch.foo", s.getTable());
}
catch(Exception unexpected)
{
@@ -113,10 +111,7 @@ public void testTableNameCatalogOnly()
try
{
CertificateDAO.CertificateSchema s = new CertificateDAO.CertificateSchema(null, "cat", null);
- Assert.assertEquals("cat..x509_certificates", s.getTable());
-
- s = new CertificateDAO.CertificateSchema(null, "cat", null, "foo");
- Assert.assertEquals("cat..foo", s.getTable());
+ Assert.assertTrue(s.getTable().startsWith("cat.."));
}
catch(Exception unexpected)
{
@@ -131,10 +126,7 @@ public void testTableNameSchemaOnly()
try
{
CertificateDAO.CertificateSchema s = new CertificateDAO.CertificateSchema(null, null, "sch");
- Assert.assertEquals("sch.x509_certificates", s.getTable());
-
- s = new CertificateDAO.CertificateSchema(null, null, "sch", "foo");
- Assert.assertEquals("sch.foo", s.getTable());
+ Assert.assertTrue(s.getTable().startsWith("sch."));
}
catch(Exception unexpected)
{
diff --git a/opencadc.gradle b/opencadc.gradle
new file mode 100644
index 0000000..25e6331
--- /dev/null
+++ b/opencadc.gradle
@@ -0,0 +1,78 @@
+configurations {
+ checkstyleDep
+ intTestCompile.extendsFrom testCompile
+ intTestRuntime.extendsFrom testRuntime
+}
+
+dependencies {
+ testCompile 'com.puppycrawl.tools:checkstyle:8.2'
+ checkstyleDep 'org.opencadc:cadc-quality:[1.0,)'
+}
+
+checkstyle {
+ ignoreFailures = false
+ config = resources.text.fromArchiveEntry(configurations.checkstyleDep, 'cadc_checkstyle.xml')
+ toolVersion = '8.2'
+ sourceSets = []
+}
+
+sourceSets {
+ test {
+ resources.srcDirs += 'src/test/resources'
+ }
+ intTest {
+ java {
+ compileClasspath += main.output + test.output
+ runtimeClasspath += main.output + test.output
+ }
+ resources.srcDir file('src/intTest/resources')
+ resources.srcDirs += new File(System.getenv('A') + '/test-certificates/')
+ }
+}
+
+// Temporary work around for issue https://github.com/gradle/gradle/issues/881 -
+// gradle not displaying fail build status when warnings reported -->
+
+tasks.withType(Checkstyle).each { checkstyleTask ->
+ checkstyleTask.doLast {
+ reports.all { report ->
+ def outputFile = report.destination
+ if (outputFile.exists() && outputFile.text.contains("