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("