From 13e7f4077da724c22cde97a8e0f1e96ba23fa636 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 17 Sep 2015 02:26:35 -0400 Subject: [PATCH 49/50] Lightweight CAs: add ability to modify attributes Add the ability to modify lightweight CA attributes, including enabling and disabling the CA (API and server aspects only). Part of: https://fedorahosted.org/pki/ticket/1604 --- .../src/com/netscape/ca/CertificateAuthority.java | 75 ++++++++++++++++++++++ .../dogtagpki/server/ca/rest/AuthorityService.java | 37 +++++++++++ .../netscape/certsrv/authority/AuthorityData.java | 11 +++- .../certsrv/authority/AuthorityResource.java | 31 +++++++++ .../netscape/certsrv/ca/ICertificateAuthority.java | 14 ++++ .../cmstools/authority/AuthorityCreateCLI.java | 2 +- 6 files changed, 168 insertions(+), 2 deletions(-) diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java index 05ab2a6024ab311b80cfd3ef026d77691169aac8..efaed10c76ee0f6b415ec220ae8b54008515c38d 100644 --- a/base/ca/src/com/netscape/ca/CertificateAuthority.java +++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java @@ -63,6 +63,8 @@ import netscape.ldap.LDAPAttributeSet; import netscape.ldap.LDAPConnection; import netscape.ldap.LDAPEntry; import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPModificationSet; import netscape.ldap.LDAPSearchResults; import org.mozilla.jss.CryptoManager; @@ -301,6 +303,10 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori throw new CADisabledException("Authority is disabled"); } + public boolean getAuthorityEnabled() { + return authorityEnabled; + } + /** * Retrieves subsystem identifier. */ @@ -2431,4 +2437,73 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori return new CertificateAuthority( topCA, aid, this.authorityID, nickname, description, true); } + + /** + * Update lightweight authority attributes. + * + * Pass null values to exclude an attribute from the update. + * + * If a passed value matches the current value, it is excluded + * from the update. + * + * To remove optional string values, pass the empty string. + */ + public void modifyAuthority(Boolean enabled, String desc) + throws EBaseException { + if (isTopCA()) return; + + LDAPModificationSet mods = new LDAPModificationSet(); + + boolean nextEnabled = authorityEnabled; + if (enabled != null && enabled.booleanValue() != authorityEnabled) { + mods.add( + LDAPModification.REPLACE, + new LDAPAttribute("authorityEnabled", enabled ? "TRUE" : "FALSE")); + nextEnabled = enabled; + } + + String nextDesc = authorityDescription; + if (desc != null) { + if (!desc.isEmpty() && authorityDescription != null + && !desc.equals(authorityDescription)) { + mods.add( + LDAPModification.REPLACE, + new LDAPAttribute("description", desc)); + nextDesc = desc; + } else if (desc.isEmpty() && authorityDescription != null) { + mods.add( + LDAPModification.DELETE, + new LDAPAttribute("description", authorityDescription)); + nextDesc = null; + } else if (!desc.isEmpty() && authorityDescription == null) { + mods.add( + LDAPModification.ADD, + new LDAPAttribute("description", desc)); + nextDesc = desc; + } + } + + if (mods.size() > 0) { + String dn = "cn=" + authorityID.toString() + ",ou=authorities,ou=" + + getId() + "," + getDBSubsystem().getBaseDN(); + + // connect to database + ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("updateAuthority"); + dbFactory.init(CMS.getConfigStore().getSubStore("internaldb")); + LDAPConnection conn = dbFactory.getConn(); + try { + conn.modify(dn, mods); + } catch (LDAPException e) { + throw new EBaseException("Error adding sub-CA entry to database: " + e); + } finally { + dbFactory.returnConn(conn); + dbFactory.reset(); + } + + // update was successful; update CA's state + authorityEnabled = nextEnabled; + authorityDescription = nextDesc; + } + + } } diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java index 5ca21892d441e4b0e611d117e4dd490df05cf6e6..9a542c8b8eec3a5122d1a977a6d721d540595dad 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java @@ -141,6 +141,42 @@ public class AuthorityService extends PKIService implements AuthorityResource { } } + @Override + public Response modifyCA(String aidString, AuthorityData data) { + // Ensure all (mutable) attributes are present + // because PUT expects complete representation + if ( + data.getEnabled() == null + || data.getDescription() == null + ) { + throw new BadRequestException("Incomplete data"); + } + + return patchCA(aidString, data); + } + + @Override + public Response patchCA(String aidString, AuthorityData data) { + AuthorityID aid = null; + try { + aid = new AuthorityID(aidString); + } catch (IllegalArgumentException e) { + throw new BadRequestException("Bad CA ID: " + aidString); + } + + ICertificateAuthority ca = topCA.getCA(aid); + if (ca == null) + throw new ResourceNotFoundException("CA \"" + aidString + "\" not found"); + + try { + ca.modifyAuthority(data.getEnabled(), data.getDescription()); + return createOKResponse(readAuthorityData(ca)); + } catch (EBaseException e) { + CMS.debug(e); + throw new PKIException("Error modifying authority: " + e.toString()); + } + } + private static AuthorityData readAuthorityData(ICertificateAuthority ca) throws PKIException { String dn; @@ -155,6 +191,7 @@ public class AuthorityService extends PKIService implements AuthorityResource { dn, ca.getAuthorityID().toString(), parentAID != null ? parentAID.toString() : null, + ca.getAuthorityEnabled(), ca.getAuthorityDescription() ); } diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java index 64f2af83720311e3eac0ee7a9197a05ff0e7198a..13c0cecab03816eee8f77409902a0ee17ec50f80 100644 --- a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java +++ b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java @@ -77,6 +77,14 @@ public class AuthorityData { @XmlAttribute + protected Boolean enabled; + + public Boolean getEnabled() { + return enabled; + } + + + @XmlAttribute protected String description; public String getDescription() { @@ -99,10 +107,11 @@ public class AuthorityData { public AuthorityData( String dn, String aid, String parentAID, - String description) { + Boolean enabled, String description) { this.dn = dn; this.aid = aid; this.parentAID = parentAID; + this.enabled = enabled; this.description = description; } diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java index 817885687b04d1b10ea35dca9a1942fe5ce201f4..24134d01178b0bee3f5c3aa951f20fbbc36e8c99 100644 --- a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java +++ b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java @@ -2,6 +2,7 @@ package com.netscape.certsrv.authority; import javax.ws.rs.GET; import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; @@ -11,6 +12,7 @@ import org.jboss.resteasy.annotations.ClientResponseType; import com.netscape.certsrv.acls.ACLMapping; import com.netscape.certsrv.authentication.AuthMethodMapping; +import com.netscape.certsrv.base.PATCH; @Path("authorities") public interface AuthorityResource { @@ -33,4 +35,33 @@ public interface AuthorityResource { //@AuthMethodMapping("certs") public Response createCA(AuthorityData data); + /** + * Modify a CA. + * + * AuthorityID, AuthorityParentID and DN are immutable; + * differences in these values are ignored. + */ + @PUT + @Path("{id}") + @ClientResponseType(entityType=AuthorityData.class) + public Response modifyCA( + @PathParam("id") String caIDString, + AuthorityData data); + + /** + * Modify a CA, supporting partial modifications. + * + * AuthorityID, AuthorityParentID and DN are immutable; + * these values are ignored. + * + * Other values, if null, are ignored, otherwise they are + * set to the new value. To remove the description, use an + * empty string. + */ + @PATCH + @Path("{id}") + @ClientResponseType(entityType=AuthorityData.class) + public Response patchCA( + @PathParam("id") String caIDString, + AuthorityData data); } diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java index d707bf848ba0973c2a817c45abeba8219bff574f..c31d08ac75553c2a03d0cee3cf2d55a893d50761 100644 --- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java +++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java @@ -536,6 +536,11 @@ public interface ICertificateAuthority extends ISubsystem { /** * Return CA description. May be null. */ + public boolean getAuthorityEnabled(); + + /** + * Return CA description. May be null. + */ public String getAuthorityDescription(); /** @@ -559,4 +564,13 @@ public interface ICertificateAuthority extends ISubsystem { public ICertificateAuthority createSubCA( String dn, String desc) throws EBaseException; + + /** + * Update authority configurables. + * + * @param enabled Whether CA is enabled or disabled + * @param desc Description; null or empty removes it + */ + public void modifyAuthority(Boolean enabled, String desc) + throws EBaseException; } diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java index 19329cbba0fac3f8c9722cc6854cbeaf6a31c75c..244dd51aabe5ec876849b2fb32c00f74cc140aff 100644 --- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java @@ -78,7 +78,7 @@ public class AuthorityCreateCLI extends CLI { String dn = cmdArgs[0]; AuthorityData data = new AuthorityData( - dn, null, parentAIDString, desc); + dn, null, parentAIDString, true /* enabled */, desc); AuthorityData newData = authorityCLI.authorityClient.createCA(data); AuthorityCLI.printAuthorityData(newData); } -- 2.4.3