From ftweedal at redhat.com Wed Nov 12 05:52:47 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Wed, 12 Nov 2014 15:52:47 +1000 Subject: [Pki-devel] CLI for editing profiles In-Reply-To: <54369FFC.1070304@redhat.com> References: <20140807071556.GS6278@dhcp-40-8.bne.redhat.com> <1407521661.22643.48.camel@aleeredhat.laptop> <20140903021628.GB11840@dhcp-40-8.bne.redhat.com> <54069B52.4@redhat.com> <20140903052551.GC11840@dhcp-40-8.bne.redhat.com> <54073575.2050606@redhat.com> <20140903235716.GE11840@dhcp-40-8.bne.redhat.com> <5407CBE6.2020207@redhat.com> <20140919074644.GS5346@dhcp-40-8.bne.redhat.com> <54369FFC.1070304@redhat.com> Message-ID: <20141112055213.GM3743@dhcp-40-8.bne.redhat.com> On Thu, Oct 09, 2014 at 09:47:24AM -0500, Endi Sukma Dewata wrote: > On 9/19/2014 2:46 AM, Fraser Tweedale wrote: > >Third cut of the LDAP profiles feature. All comments from previous > >reviews have been addressed in this patchset. > > > >The switch from byte[] to Properties in the ProfileClient API (item > >#13 from Endi's first review) is currently in a separte patch > >(#0014) to make it clear what had to happen for this change, and to > >make it easy to change or remove if there are problems. If it's > >preferred to squash it when acked, let me know (or it can stay > >separate - I don't mind either way). > > > >Cheers, > > > >Fraser > > > Sorry for the delay. I have some more comments below. > > > Patch #4-3: > > 1. What do you think about renaming classId into certProfileClassID? Is > there a plan to use the classId for something other than profile? > The classId represents a Java class. It is not yet used elsewhere, but if we move to finer-grained LDAP profile config schema it will be used for the inputs/outputs/defaults also, therefore I will leave it as-is. > 2. Let's use real descriptions instead of generic 'CMS defined > attribute/class', for example: > * classId: Certificate profile class ID > * certProfileConfig: Certificate profile configuration > * certProfile: Certificate profile > Will be adjusted in next patch revision. > No major issue, but see comments about database upgrade below. ACK. > > > Patch #5-3: > > 1. The commit() parameter "createBackup" doesn't match the @param backup. > Fixed the javadoc. > Nothing major. ACK. > > > Patch #6-3: > > 1. The LDAP-based profile subsystem is fine, but it might not run on > existing installation that still uses file-based profile. Are we going to > (a) support both types and provide a migration tool that can be run at a > later time, or (b) require the people to migrate to LDAP before they can use > the new code? > > Option (a) is more flexible, but we would need to keep both the old > ProfileSubsystem and the new LDAPProfileSubsystem, and then switch the class > when we migrate to LDAP. With (b) once the RPM is upgraded, the database has > to be upgraded and the profiles have to be migrated before we can restart > the server, otherwise the profile subsystem will not work. See also comments > about profile migration tool below. > Need to have a deeper discussion of the implications. Will create a new thread for this topic, or maybe we can discuss in next ipa-cs team meeting. > 2. If the default profile subsystem is changed to LDAP, we shouldn't need to > copy the profile files anymore to the instance. Look for > "pki_subsystem_profiles_path" in subsystem_layout.py. > > 3. Are the profile.* properties in CS.cfg still used by any code (other than > importProfiles()) if we use LDAP-based profile? If not, we probably should > remove them from CS.cfg and move them into the corresponding profile files > (just like the raw format), and use file list instead of profile.list during > import. > If we go with option b) above, this will clean things up a bit. If we go with option a) we either keep it or make more changes to move the classid into the file-based representation. > I'd suggest we use option (a), change the default to LDAP, and remove > profile.* properties after we have the database upgrade and profile > migration tool ready. > > > Patch #7-3: > > 1. By default the subsystems are enabled, so in setSubsystemEnabled() if > enabled=true we can actually remove the PROP_ENABLED property to keep it > consistent with other subsystems that are enabled by default. > > Nothing major. ACK. > > > Patch #8-3: > 1. There's an unused variable: > > String className = info.getClassName(); > Removed it. > Nothing major. ACK. > > > Patch #9-3: > > 1. The ca-profile-add --raw works, but the regular ca-profile-add throws an > error. If this is an existing problem please open a ticket. > This is a regression. Will have it fixed in next patch revision. > 2. The ca-profile-show --raw prepends a message to the raw output, for > example: > > --------------------- > Profile "caAdminCert" > --------------------- > ... > > I think the message should not be included in the raw output so it can be > redirected into a file, for example: > > pki ca-profile-show caAdminCert --raw > caAdminCert.cfg > Was included it for consistency with non-raw output. Happy to see it go. > 3. On the server side we should use CMS.debug(e) to log exceptions instead > of e.printStackTrace() so that the amount of logging can be controlled. > OK > 4. On the CLI side it would be better to throw an exception on execution > error (not option error), or just don't catch the original exception, rather > than printing an error message. The exception will be caught and displayed > properly by the MainCLI, and we can see the full stack trace in verbose > mode. > > 5. The code that reads the raw profile data in ProfileAddCLI and > ProfileModifyCLI probably can be refactored into > ProfileData.loadRawProfile(). The ProfileCLI.readProfileFromFile() probably > can also be moved into ProfileData.loadXMLProfile() for consistency. > OK > 6. The ca-profile-edit can also support both formats: XML and raw. > > I think #1 and #2 should be fixed. The others can be addressed later. > > > Patch #14-3: > > 1. The ProfileResource interface still uses byte[]. I originally thought it > will use Properties as well. Is that possible? > > 2. Related to #1, the response content-type is application/xml, but we > should be returning text/plain or application/octet-stream. I need to take a > look at the REST framework to see how we can support it. > > These issues require further investigation. The code itself is fine. ACK. > I have found RESTeasy to be anything but. Thanks for ACK; happy to revisit later with someone who groks RESTeasy more than I. > > Other comments: > > 1. For database upgrade, we need to add the profile schema and the profile > base entry into the database. We don't have a database upgrade framework > yet, but I suppose we can prepare a script that will connect to the database > and make the modifications. Later we can convert that script into an proper > database upgrade scriptlet. > > 2. For profile migration, we need to read profile files in the existing > instance, add it to LDAP, and remove the profile files and the profile > properties in CS.cfg. If it's a one-way migration the migration tool can be > implemented as database upgrade scriptlet too. If it's two-way, it would > have to be implemented as a stand-alone tool. > > 3. Since these patches are targeted for 10.3, check with Matt/Ade to make > sure the repo is branched properly before you push. > > 4. The patches can be pushed separately or squashed, it doesn't really > matter. Just make sure that each patch pushed contains a complete set of > changes so the build doesn't break. This would be important if we need to > cherry-pick or revert some patches later. > > -- > Endi S. Dewata From akoneru at redhat.com Fri Nov 14 17:02:56 2014 From: akoneru at redhat.com (Abhishek Koneru) Date: Fri, 14 Nov 2014 12:02:56 -0500 Subject: [Pki-devel] [PATCH] 109 Fixes for tickets 1155, 1156 Message-ID: <1415984576.10934.5.camel@akoneru.redhat.com> Please review the patch with fixes for trac tickets 1155 and 1156 Thanks, Abhishek -------------- next part -------------- A non-text attachment was scrubbed... Name: pki-akoneru-0109-Minor-fixes-for-trac-tickets-1155-and-1156.patch Type: text/x-patch Size: 3194 bytes Desc: not available URL: From ftweedal at redhat.com Thu Nov 20 06:27:48 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 20 Nov 2014 16:27:48 +1000 Subject: [Pki-devel] CLI for editing profiles In-Reply-To: <20141112055213.GM3743@dhcp-40-8.bne.redhat.com> References: <1407521661.22643.48.camel@aleeredhat.laptop> <20140903021628.GB11840@dhcp-40-8.bne.redhat.com> <54069B52.4@redhat.com> <20140903052551.GC11840@dhcp-40-8.bne.redhat.com> <54073575.2050606@redhat.com> <20140903235716.GE11840@dhcp-40-8.bne.redhat.com> <5407CBE6.2020207@redhat.com> <20140919074644.GS5346@dhcp-40-8.bne.redhat.com> <54369FFC.1070304@redhat.com> <20141112055213.GM3743@dhcp-40-8.bne.redhat.com> Message-ID: <20141120062748.GB8412@dhcp-40-8.bne.redhat.com> Fourth cut of the feature. Please review w.r.t. the issues below. The big issue mentioned in the review but *not* dealt with in this patchset is support for using file-based profiles OR LDAP-based profiles. This will involve copying the new ProfileSystem (as it appears in the patches) to a new class ("LDAPProfileSubsystem" I suppose), restoring the original ProfileSubsystem with the file-based behaviour, providing config and mechanism for loading the preferred variant of the subsystem, and reconciling any conflicts. But the implementation of the LDAP-based version of ProfileSubsystem won't be changed once these patches are ACKed. (See also patch 0015-2 in the other thread) Cheers, Fraser On Wed, Nov 12, 2014 at 03:52:47PM +1000, Fraser Tweedale wrote: > On Thu, Oct 09, 2014 at 09:47:24AM -0500, Endi Sukma Dewata wrote: > > On 9/19/2014 2:46 AM, Fraser Tweedale wrote: > > >Third cut of the LDAP profiles feature. All comments from previous > > >reviews have been addressed in this patchset. > > > > > >The switch from byte[] to Properties in the ProfileClient API (item > > >#13 from Endi's first review) is currently in a separte patch > > >(#0014) to make it clear what had to happen for this change, and to > > >make it easy to change or remove if there are problems. If it's > > >preferred to squash it when acked, let me know (or it can stay > > >separate - I don't mind either way). > > > > > >Cheers, > > > > > >Fraser > > > > > > Sorry for the delay. I have some more comments below. > > > > > > Patch #4-3: > > > > 1. What do you think about renaming classId into certProfileClassID? Is > > there a plan to use the classId for something other than profile? > > > The classId represents a Java class. It is not yet used elsewhere, > but if we move to finer-grained LDAP profile config schema it will > be used for the inputs/outputs/defaults also, therefore I will leave > it as-is. > > > 2. Let's use real descriptions instead of generic 'CMS defined > > attribute/class', for example: > > * classId: Certificate profile class ID > > * certProfileConfig: Certificate profile configuration > > * certProfile: Certificate profile > > > Will be adjusted in next patch revision. > > > No major issue, but see comments about database upgrade below. ACK. > > > > > > Patch #5-3: > > > > 1. The commit() parameter "createBackup" doesn't match the @param backup. > > > Fixed the javadoc. > > > Nothing major. ACK. > > > > > > Patch #6-3: > > > > 1. The LDAP-based profile subsystem is fine, but it might not run on > > existing installation that still uses file-based profile. Are we going to > > (a) support both types and provide a migration tool that can be run at a > > later time, or (b) require the people to migrate to LDAP before they can use > > the new code? > > > > Option (a) is more flexible, but we would need to keep both the old > > ProfileSubsystem and the new LDAPProfileSubsystem, and then switch the class > > when we migrate to LDAP. With (b) once the RPM is upgraded, the database has > > to be upgraded and the profiles have to be migrated before we can restart > > the server, otherwise the profile subsystem will not work. See also comments > > about profile migration tool below. > > > Need to have a deeper discussion of the implications. Will create a > new thread for this topic, or maybe we can discuss in next ipa-cs > team meeting. > > > 2. If the default profile subsystem is changed to LDAP, we shouldn't need to > > copy the profile files anymore to the instance. Look for > > "pki_subsystem_profiles_path" in subsystem_layout.py. > > > > 3. Are the profile.* properties in CS.cfg still used by any code (other than > > importProfiles()) if we use LDAP-based profile? If not, we probably should > > remove them from CS.cfg and move them into the corresponding profile files > > (just like the raw format), and use file list instead of profile.list during > > import. > > > If we go with option b) above, this will clean things up a bit. If > we go with option a) we either keep it or make more changes to move > the classid into the file-based representation. > > > I'd suggest we use option (a), change the default to LDAP, and remove > > profile.* properties after we have the database upgrade and profile > > migration tool ready. > > > > > > Patch #7-3: > > > > 1. By default the subsystems are enabled, so in setSubsystemEnabled() if > > enabled=true we can actually remove the PROP_ENABLED property to keep it > > consistent with other subsystems that are enabled by default. > > > > Nothing major. ACK. > > > > > > Patch #8-3: > > 1. There's an unused variable: > > > > String className = info.getClassName(); > > > Removed it. > > > Nothing major. ACK. > > > > > > Patch #9-3: > > > > 1. The ca-profile-add --raw works, but the regular ca-profile-add throws an > > error. If this is an existing problem please open a ticket. > > > This is a regression. Will have it fixed in next patch revision. > > > 2. The ca-profile-show --raw prepends a message to the raw output, for > > example: > > > > --------------------- > > Profile "caAdminCert" > > --------------------- > > ... > > > > I think the message should not be included in the raw output so it can be > > redirected into a file, for example: > > > > pki ca-profile-show caAdminCert --raw > caAdminCert.cfg > > > Was included it for consistency with non-raw output. Happy to see > it go. > > > 3. On the server side we should use CMS.debug(e) to log exceptions instead > > of e.printStackTrace() so that the amount of logging can be controlled. > > > OK > > > 4. On the CLI side it would be better to throw an exception on execution > > error (not option error), or just don't catch the original exception, rather > > than printing an error message. The exception will be caught and displayed > > properly by the MainCLI, and we can see the full stack trace in verbose > > mode. > > > > 5. The code that reads the raw profile data in ProfileAddCLI and > > ProfileModifyCLI probably can be refactored into > > ProfileData.loadRawProfile(). The ProfileCLI.readProfileFromFile() probably > > can also be moved into ProfileData.loadXMLProfile() for consistency. > > > OK > > > 6. The ca-profile-edit can also support both formats: XML and raw. > > > > I think #1 and #2 should be fixed. The others can be addressed later. > > > > > > Patch #14-3: > > > > 1. The ProfileResource interface still uses byte[]. I originally thought it > > will use Properties as well. Is that possible? > > > > 2. Related to #1, the response content-type is application/xml, but we > > should be returning text/plain or application/octet-stream. I need to take a > > look at the REST framework to see how we can support it. > > > > These issues require further investigation. The code itself is fine. ACK. > > > I have found RESTeasy to be anything but. Thanks for ACK; happy to > revisit later with someone who groks RESTeasy more than I. > > > > > Other comments: > > > > 1. For database upgrade, we need to add the profile schema and the profile > > base entry into the database. We don't have a database upgrade framework > > yet, but I suppose we can prepare a script that will connect to the database > > and make the modifications. Later we can convert that script into an proper > > database upgrade scriptlet. > > > > 2. For profile migration, we need to read profile files in the existing > > instance, add it to LDAP, and remove the profile files and the profile > > properties in CS.cfg. If it's a one-way migration the migration tool can be > > implemented as database upgrade scriptlet too. If it's two-way, it would > > have to be implemented as a stand-alone tool. > > > > 3. Since these patches are targeted for 10.3, check with Matt/Ade to make > > sure the repo is branched properly before you push. > > > > 4. The patches can be pushed separately or squashed, it doesn't really > > matter. Just make sure that each patch pushed contains a complete set of > > changes so the build doesn't break. This would be important if we need to > > cherry-pick or revert some patches later. > > > > -- > > Endi S. Dewata > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From ftweedal at redhat.com Thu Nov 20 06:30:11 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 20 Nov 2014 16:30:11 +1000 Subject: [Pki-devel] CLI for editing profiles In-Reply-To: <20141120062748.GB8412@dhcp-40-8.bne.redhat.com> References: <20140903021628.GB11840@dhcp-40-8.bne.redhat.com> <54069B52.4@redhat.com> <20140903052551.GC11840@dhcp-40-8.bne.redhat.com> <54073575.2050606@redhat.com> <20140903235716.GE11840@dhcp-40-8.bne.redhat.com> <5407CBE6.2020207@redhat.com> <20140919074644.GS5346@dhcp-40-8.bne.redhat.com> <54369FFC.1070304@redhat.com> <20141112055213.GM3743@dhcp-40-8.bne.redhat.com> <20141120062748.GB8412@dhcp-40-8.bne.redhat.com> Message-ID: <20141120063011.GC8412@dhcp-40-8.bne.redhat.com> Whups, it helps to attach the patches! On Thu, Nov 20, 2014 at 04:27:48PM +1000, Fraser Tweedale wrote: > Fourth cut of the feature. > > Please review w.r.t. the issues below. > > The big issue mentioned in the review but *not* dealt with in this > patchset is support for using file-based profiles OR LDAP-based > profiles. This will involve copying the new ProfileSystem (as it > appears in the patches) to a new class ("LDAPProfileSubsystem" I > suppose), restoring the original ProfileSubsystem with the > file-based behaviour, providing config and mechanism for loading the > preferred variant of the subsystem, and reconciling any conflicts. > But the implementation of the LDAP-based version of ProfileSubsystem > won't be changed once these patches are ACKed. > > (See also patch 0015-2 in the other thread) > > Cheers, > > Fraser > > On Wed, Nov 12, 2014 at 03:52:47PM +1000, Fraser Tweedale wrote: > > On Thu, Oct 09, 2014 at 09:47:24AM -0500, Endi Sukma Dewata wrote: > > > On 9/19/2014 2:46 AM, Fraser Tweedale wrote: > > > >Third cut of the LDAP profiles feature. All comments from previous > > > >reviews have been addressed in this patchset. > > > > > > > >The switch from byte[] to Properties in the ProfileClient API (item > > > >#13 from Endi's first review) is currently in a separte patch > > > >(#0014) to make it clear what had to happen for this change, and to > > > >make it easy to change or remove if there are problems. If it's > > > >preferred to squash it when acked, let me know (or it can stay > > > >separate - I don't mind either way). > > > > > > > >Cheers, > > > > > > > >Fraser > > > > > > > > > Sorry for the delay. I have some more comments below. > > > > > > > > > Patch #4-3: > > > > > > 1. What do you think about renaming classId into certProfileClassID? Is > > > there a plan to use the classId for something other than profile? > > > > > The classId represents a Java class. It is not yet used elsewhere, > > but if we move to finer-grained LDAP profile config schema it will > > be used for the inputs/outputs/defaults also, therefore I will leave > > it as-is. > > > > > 2. Let's use real descriptions instead of generic 'CMS defined > > > attribute/class', for example: > > > * classId: Certificate profile class ID > > > * certProfileConfig: Certificate profile configuration > > > * certProfile: Certificate profile > > > > > Will be adjusted in next patch revision. > > > > > No major issue, but see comments about database upgrade below. ACK. > > > > > > > > > Patch #5-3: > > > > > > 1. The commit() parameter "createBackup" doesn't match the @param backup. > > > > > Fixed the javadoc. > > > > > Nothing major. ACK. > > > > > > > > > Patch #6-3: > > > > > > 1. The LDAP-based profile subsystem is fine, but it might not run on > > > existing installation that still uses file-based profile. Are we going to > > > (a) support both types and provide a migration tool that can be run at a > > > later time, or (b) require the people to migrate to LDAP before they can use > > > the new code? > > > > > > Option (a) is more flexible, but we would need to keep both the old > > > ProfileSubsystem and the new LDAPProfileSubsystem, and then switch the class > > > when we migrate to LDAP. With (b) once the RPM is upgraded, the database has > > > to be upgraded and the profiles have to be migrated before we can restart > > > the server, otherwise the profile subsystem will not work. See also comments > > > about profile migration tool below. > > > > > Need to have a deeper discussion of the implications. Will create a > > new thread for this topic, or maybe we can discuss in next ipa-cs > > team meeting. > > > > > 2. If the default profile subsystem is changed to LDAP, we shouldn't need to > > > copy the profile files anymore to the instance. Look for > > > "pki_subsystem_profiles_path" in subsystem_layout.py. > > > > > > 3. Are the profile.* properties in CS.cfg still used by any code (other than > > > importProfiles()) if we use LDAP-based profile? If not, we probably should > > > remove them from CS.cfg and move them into the corresponding profile files > > > (just like the raw format), and use file list instead of profile.list during > > > import. > > > > > If we go with option b) above, this will clean things up a bit. If > > we go with option a) we either keep it or make more changes to move > > the classid into the file-based representation. > > > > > I'd suggest we use option (a), change the default to LDAP, and remove > > > profile.* properties after we have the database upgrade and profile > > > migration tool ready. > > > > > > > > > Patch #7-3: > > > > > > 1. By default the subsystems are enabled, so in setSubsystemEnabled() if > > > enabled=true we can actually remove the PROP_ENABLED property to keep it > > > consistent with other subsystems that are enabled by default. > > > > > > Nothing major. ACK. > > > > > > > > > Patch #8-3: > > > 1. There's an unused variable: > > > > > > String className = info.getClassName(); > > > > > Removed it. > > > > > Nothing major. ACK. > > > > > > > > > Patch #9-3: > > > > > > 1. The ca-profile-add --raw works, but the regular ca-profile-add throws an > > > error. If this is an existing problem please open a ticket. > > > > > This is a regression. Will have it fixed in next patch revision. > > > > > 2. The ca-profile-show --raw prepends a message to the raw output, for > > > example: > > > > > > --------------------- > > > Profile "caAdminCert" > > > --------------------- > > > ... > > > > > > I think the message should not be included in the raw output so it can be > > > redirected into a file, for example: > > > > > > pki ca-profile-show caAdminCert --raw > caAdminCert.cfg > > > > > Was included it for consistency with non-raw output. Happy to see > > it go. > > > > > 3. On the server side we should use CMS.debug(e) to log exceptions instead > > > of e.printStackTrace() so that the amount of logging can be controlled. > > > > > OK > > > > > 4. On the CLI side it would be better to throw an exception on execution > > > error (not option error), or just don't catch the original exception, rather > > > than printing an error message. The exception will be caught and displayed > > > properly by the MainCLI, and we can see the full stack trace in verbose > > > mode. > > > > > > 5. The code that reads the raw profile data in ProfileAddCLI and > > > ProfileModifyCLI probably can be refactored into > > > ProfileData.loadRawProfile(). The ProfileCLI.readProfileFromFile() probably > > > can also be moved into ProfileData.loadXMLProfile() for consistency. > > > > > OK > > > > > 6. The ca-profile-edit can also support both formats: XML and raw. > > > > > > I think #1 and #2 should be fixed. The others can be addressed later. > > > > > > > > > Patch #14-3: > > > > > > 1. The ProfileResource interface still uses byte[]. I originally thought it > > > will use Properties as well. Is that possible? > > > > > > 2. Related to #1, the response content-type is application/xml, but we > > > should be returning text/plain or application/octet-stream. I need to take a > > > look at the REST framework to see how we can support it. > > > > > > These issues require further investigation. The code itself is fine. ACK. > > > > > I have found RESTeasy to be anything but. Thanks for ACK; happy to > > revisit later with someone who groks RESTeasy more than I. > > > > > > > > Other comments: > > > > > > 1. For database upgrade, we need to add the profile schema and the profile > > > base entry into the database. We don't have a database upgrade framework > > > yet, but I suppose we can prepare a script that will connect to the database > > > and make the modifications. Later we can convert that script into an proper > > > database upgrade scriptlet. > > > > > > 2. For profile migration, we need to read profile files in the existing > > > instance, add it to LDAP, and remove the profile files and the profile > > > properties in CS.cfg. If it's a one-way migration the migration tool can be > > > implemented as database upgrade scriptlet too. If it's two-way, it would > > > have to be implemented as a stand-alone tool. > > > > > > 3. Since these patches are targeted for 10.3, check with Matt/Ade to make > > > sure the repo is branched properly before you push. > > > > > > 4. The patches can be pushed separately or squashed, it doesn't really > > > matter. Just make sure that each patch pushed contains a complete set of > > > changes so the build doesn't break. This would be important if we need to > > > cherry-pick or revert some patches later. > > > > > > -- > > > Endi S. Dewata > > > > _______________________________________________ > > Pki-devel mailing list > > Pki-devel at redhat.com > > https://www.redhat.com/mailman/listinfo/pki-devel -------------- next part -------------- >From f339c89985c6ef93d4258cbe09ef51364714775e Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Mon, 7 Jul 2014 23:35:35 -0400 Subject: [PATCH 04/10] Add schema for LDAP-based profiles --- base/ca/shared/conf/db.ldif | 5 ++++- base/ca/shared/conf/schema.ldif | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/base/ca/shared/conf/db.ldif b/base/ca/shared/conf/db.ldif index 00fa919b7df38ed97f0bc21b5616a9998845c7d4..8a2e0b07274a83b317fb1ba56e8ef32b96857118 100644 --- a/base/ca/shared/conf/db.ldif +++ b/base/ca/shared/conf/db.ldif @@ -160,4 +160,7 @@ objectClass: top objectClass: organizationalUnit ou: certificateRepository - +dn: ou=certificateProfiles,ou=ca,{rootSuffix} +objectClass: top +objectClass: organizationalUnit +ou: certificateProfiles diff --git a/base/ca/shared/conf/schema.ldif b/base/ca/shared/conf/schema.ldif index 70578e21ce4e102909a1b7b45fa84c184a997bdf..b5f9b709993e0c2f7f9000950e125e975caee4e8 100644 --- a/base/ca/shared/conf/schema.ldif +++ b/base/ca/shared/conf/schema.ldif @@ -487,3 +487,18 @@ dn: cn=schema changetype: modify add: objectClasses objectClasses: ( securityDomainSessionEntry-oid NAME 'securityDomainSessionEntry' DESC 'CMS defined class' SUP top STRUCTURAL MUST ( cn $ host $ uid $ cmsUserGroup $ dateOfCreate ) X-ORIGIN 'user defined' ) + +dn: cn=schema +changetype: modify +add: attributeTypes +attributeTypes: ( classId-oid NAME 'classId' DESC 'Certificate profile class ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' ) + +dn: cn=schema +changetype: modify +add: attributeTypes +attributeTypes: ( certProfileConfig-oid NAME 'certProfileConfig' DESC 'Certificate profile configuration' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' ) + +dn: cn=schema +changetype: modify +add: objectClasses +objectClasses: ( certProfile-oid NAME 'certProfile' DESC 'Certificate profile' SUP top STRUCTURAL MUST cn MAY ( classId $ certProfileConfig ) X-ORIGIN 'user defined' ) -- 1.9.3 -------------- next part -------------- >From ae029d57c6362a4c70057aeb7e8ac015f850cfc5 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Tue, 15 Jul 2014 02:48:35 -0400 Subject: [PATCH 05/10] Add LDAPConfigStore class The LDAPConfigStore class is an IConfigStore that reads and writes its configuration to a given attribute and DN in an LDAP database. --- .../com/netscape/cmscore/base/LDAPConfigStore.java | 195 +++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 base/server/cmscore/src/com/netscape/cmscore/base/LDAPConfigStore.java diff --git a/base/server/cmscore/src/com/netscape/cmscore/base/LDAPConfigStore.java b/base/server/cmscore/src/com/netscape/cmscore/base/LDAPConfigStore.java new file mode 100644 index 0000000000000000000000000000000000000000..788ef6bc9a26f62ed779586818525df56c555cea --- /dev/null +++ b/base/server/cmscore/src/com/netscape/cmscore/base/LDAPConfigStore.java @@ -0,0 +1,195 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007, 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package com.netscape.cmscore.base; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Map; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.ldap.ILdapConnFactory; + +/** + * LDAPConfigStore: + * Extends PropConfigStore with methods to load/save from/to file for + * persistent storage. This is a configuration store agent who + * reads data from an LDAP entry. + *

+ * + * @version $Revision$, $Date$ + * @see PropConfigStore + */ +public class LDAPConfigStore extends PropConfigStore implements IConfigStore { + + private ILdapConnFactory dbFactory; + private String dn; + private String attr; + private LDAPAttribute[] createAttrs; + + /** + * + */ + private static final long serialVersionUID = 3642124526598175633L; + + /** + * Constructs an LDAP configuration store. + *

+ * + * @param dbFactory Database connection factory + * @param dn Distinguished name of record containing config store + * @param attr Name of attribute containing config store + * @param createAttrs Set of initial attributes if creating the entry. Should + * contain cn, objectclass and possibly other attributes. + * + * @exception EBaseException failed to create file configuration + */ + public LDAPConfigStore( + ILdapConnFactory dbFactory, + String dn, LDAPAttribute[] createAttrs, String attr + ) throws EBaseException { + super(null); // top-level store without a name + + this.dbFactory = dbFactory; + this.dn = dn; + this.createAttrs = createAttrs; + this.attr = attr; + + LDAPConnection conn = dbFactory.getConn(); + + String[] readAttrs = {attr}; + try { + LDAPEntry ldapEntry = conn.read(dn, readAttrs); + + Enumeration vals = ldapEntry.getAttribute(attr).getStringValues(); + InputStream data = new ByteArrayInputStream(vals.nextElement().getBytes()); + load(data); + } catch (LDAPException e) { + // if there is no such object, we will create it on commit() + if (e.getLDAPResultCode() != LDAPException.NO_SUCH_OBJECT) { + throw new EBaseException( + "Error reading LDAPConfigStore '" + + dn + "': " + e.toString() + ); + } + } catch (IOException e) { + throw new EBaseException( + "Error reading LDAPConfigStore '" + + dn + "': " + e.toString() + ); + } finally { + dbFactory.returnConn(conn); + } + } + + @Override + public void save(OutputStream out, String header) { + try (PrintWriter writer = new PrintWriter(out)) { + Map map = getProperties(); + for (String k : map.keySet()) { + writer.println(k + "=" + map.get(k)); + } + } + } + + /** + * Commit the configuration to the database. + * + * All uses of LDAPProfileStore at time of writing call with + * createBackup=false, so the argument is ignored. + * + * If backup becomes necessary, the constructor should be + * modified to take a String backupAttr, and the existing + * content be copied to that attribute. + * + * @param createBackup Ignored. + */ + public void commit(boolean createBackup) throws EBaseException { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + save(data, null); + + LDAPAttribute configAttr = new LDAPAttribute(attr, data.toByteArray()); + + LDAPConnection conn = dbFactory.getConn(); + + // first attempt to modify; if modification fails (due + // to no such object), try and add the entry instead. + try { + try { + commitModify(conn, configAttr); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) { + commitAdd(conn, configAttr); + } else { + throw e; + } + } + } catch (LDAPException e) { + throw new EBaseException( + "Error writing LDAPConfigStore '" + + dn + "': " + e.toString() + ); + } finally { + dbFactory.returnConn(conn); + } + } + + /** + * Update the record via an LDAPModification. + * + * @param conn LDAP connection. + * @param configAttr Config store attribute. + * @return true on success, false if the entry does not exist. + */ + private void commitModify(LDAPConnection conn, LDAPAttribute configAttr) + throws LDAPException + { + LDAPModification ldapMod = + new LDAPModification(LDAPModification.REPLACE, configAttr); + conn.modify(dn, ldapMod); + } + + /** + * Add the LDAPEntry via LDAPConnection.add. + * + * @param conn LDAP connection. + * @param configAttr Config store attribute. + * @return true on success, false if the entry already exists. + */ + private void commitAdd(LDAPConnection conn, LDAPAttribute configAttr) + throws LDAPException + { + LDAPAttributeSet attrSet = new LDAPAttributeSet(createAttrs); + attrSet.add(configAttr); + LDAPEntry ldapEntry = new LDAPEntry(dn, attrSet); + conn.add(ldapEntry); + } +} -- 1.9.3 -------------- next part -------------- >From cc1ce829b878e8860e56c05c0d690c05ec8a959e Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 17 Jul 2014 00:24:06 -0400 Subject: [PATCH 06/10] Change ProfileSubsystem to use LDAP database --- .../dogtagpki/server/ca/rest/ProfileService.java | 16 +- .../certsrv/profile/IProfileSubsystem.java | 19 +-- .../cms/servlet/admin/ProfileAdminServlet.java | 30 +--- base/server/cmsbundle/src/UserMessages.properties | 2 + .../com/netscape/cmscore/base/FileConfigStore.java | 4 +- .../netscape/cmscore/profile/ProfileSubsystem.java | 184 ++++++++++++--------- 6 files changed, 114 insertions(+), 141 deletions(-) diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java index 3b2f8a50ebcd18fe0098b2e92e0300645b904fa3..d3f08b270fd66154da880d47be30ea48716b75bd 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java @@ -18,7 +18,6 @@ package org.dogtagpki.server.ca.rest; -import java.io.File; import java.io.IOException; import java.net.URI; import java.security.Principal; @@ -244,7 +243,7 @@ public class ProfileService extends PKIService implements ProfileResource { data.setAuthenticatorId(profile.getAuthenticatorId()); data.setAuthzAcl(profile.getAuthzAcl()); - data.setClassId(cs.getString(profileId + ".class_id")); + data.setClassId(ps.getProfileClassId(profileId)); data.setDescription(profile.getDescription(getLocale(headers))); data.setEnabled(ps.isProfileEnable(profileId)); data.setEnabledBy(ps.getProfileEnableBy(profileId)); @@ -472,18 +471,13 @@ public class ProfileService extends PKIService implements ProfileResource { auditParams.put("description", data.getDescription()); auditParams.put("visible", Boolean.toString(data.isVisible())); - String config = CMS.getConfigStore().getString("instanceRoot") + "/ca/profiles/ca/" + - profileId + ".cfg"; - File configFile = new File(config); - configFile.createNewFile(); IPluginInfo info = registry.getPluginInfo("profile", data.getClassId()); - profile = ps.createProfile(profileId, data.getClassId(), info.getClassName(), config); + profile = ps.createProfile(profileId, data.getClassId(), info.getClassName()); profile.setName(getLocale(headers), data.getName()); profile.setDescription(getLocale(headers), data.getDescription()); profile.setVisible(data.isVisible()); profile.getConfigStore().commit(false); - ps.createProfileConfig(profileId, data.getClassId(), config); if (profile instanceof IProfileEx) { // populates profile specific plugins such as @@ -504,7 +498,7 @@ public class ProfileService extends PKIService implements ProfileResource { return createCreatedResponse(profileData, profileData.getLink().getHref()); - } catch (EBaseException | IOException e) { + } catch (EBaseException e) { CMS.debug("createProfile: error in creating profile: " + e); e.printStackTrace(); @@ -983,9 +977,7 @@ public class ProfileService extends PKIService implements ProfileResource { "`. Profile must be disabled first."); } - String configFile = CMS.getConfigStore().getString("profile." + profileId + ".config"); - - ps.deleteProfile(profileId, configFile); + ps.deleteProfile(profileId); auditProfileChange( ScopeDef.SC_PROFILE_RULES, diff --git a/base/common/src/com/netscape/certsrv/profile/IProfileSubsystem.java b/base/common/src/com/netscape/certsrv/profile/IProfileSubsystem.java index 3238fb2e686cd3f9aa42dac7997cacd8f7c4c06c..b7071fe7526132d7f9ff1945819f0d1f67c18719 100644 --- a/base/common/src/com/netscape/certsrv/profile/IProfileSubsystem.java +++ b/base/common/src/com/netscape/certsrv/profile/IProfileSubsystem.java @@ -61,33 +61,18 @@ public interface IProfileSubsystem extends ISubsystem { * @param id profile id * @param classid implementation id * @param className class Name - * @param configFile configuration file * @exception EProfileException failed to create profile */ - public IProfile createProfile(String id, String classid, - String className, String configFile) + public IProfile createProfile(String id, String classid, String className) throws EProfileException; /** * Deletes profile. * * @param id profile id - * @param configFile configuration file * @exception EProfileException failed to delete profile */ - public void deleteProfile(String id, String configFile) - throws EProfileException; - - /** - * Creates a new profile configuration file. - * - * @param id profile id - * @param classId implementation id - * @param configPath location to create the configuration file - * @exception failed to create profile - */ - public void createProfileConfig(String id, String classId, - String configPath) throws EProfileException; + public void deleteProfile(String id) throws EProfileException; /** * Enables a profile. diff --git a/base/server/cms/src/com/netscape/cms/servlet/admin/ProfileAdminServlet.java b/base/server/cms/src/com/netscape/cms/servlet/admin/ProfileAdminServlet.java index 3d25b8b71bc7f71a7d579aa6d27bcdc623e49e28..b418baf41a0f84ce5d5ac9da56efd191e1a20316 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/admin/ProfileAdminServlet.java +++ b/base/server/cms/src/com/netscape/cms/servlet/admin/ProfileAdminServlet.java @@ -17,7 +17,6 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cms.servlet.admin; -import java.io.File; import java.io.IOException; import java.util.Enumeration; import java.util.StringTokenizer; @@ -2290,26 +2289,8 @@ public class ProfileAdminServlet extends AdminServlet { return; } - String config = null; - try { - config = CMS.getConfigStore().getString("profile." + id + ".config"); - } catch (EBaseException e) { - // store a message in the signed audit log file - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_CONFIG_CERT_PROFILE, - auditSubjectID, - ILogger.FAILURE, - auditParams(req)); - - audit(auditMessage); - - sendResponse(ERROR, null, null, resp); - return; - } - - try { - mProfileSub.deleteProfile(id, config); + mProfileSub.deleteProfile(id); } catch (EProfileException e) { // store a message in the signed audit log file auditMessage = CMS.getLogMessage( @@ -2475,16 +2456,10 @@ public class ProfileAdminServlet extends AdminServlet { IProfile profile = null; - // create configuration file - File configFile = new File(config); - - configFile.createNewFile(); - // create profile try { profile = mProfileSub.createProfile(id, impl, - info.getClassName(), - config); + info.getClassName()); profile.setName(getLocale(req), name); profile.setDescription(getLocale(req), name); if (visible != null && visible.equals("true")) { @@ -2495,7 +2470,6 @@ public class ProfileAdminServlet extends AdminServlet { profile.setAuthenticatorId(auth); profile.getConfigStore().commit(false); - mProfileSub.createProfileConfig(id, impl, config); if (profile instanceof IProfileEx) { // populates profile specific plugins such as // policies, inputs and outputs diff --git a/base/server/cmsbundle/src/UserMessages.properties b/base/server/cmsbundle/src/UserMessages.properties index fe43094e6b2a0531502570bc626da557fc9061ae..cd7fa18bfb1e17bc4ab4aa9e0dac06f815861291 100644 --- a/base/server/cmsbundle/src/UserMessages.properties +++ b/base/server/cmsbundle/src/UserMessages.properties @@ -754,6 +754,8 @@ CMS_PROFILE_CONFIG_KEY_USAGE_EXTENSION_CHECKING=Allow duplicate subject names wi CMS_PROFILE_INTERNAL_ERROR=Profile internal error: {0} CMS_PROFILE_DENY_OPERATION=Not authorized to do this operation. CMS_PROFILE_DELETE_ENABLEPROFILE=Cannot delete enabled profile: {0} +CMS_PROFILE_DELETE_UNKNOWNPROFILE=Cannot delete unknown profile: {0} +CMS_PROFILE_DELETE_DATABASEERROR=Failed to delete profile: {0} CMS_PROFILE_INVALID_REQUEST=Invalid Request CMS_PROFILE_EMPTY_REQUEST_TYPE=Request type is not specified. Check your profile input. CMS_PROFILE_CREATE_POLICY_FAILED=Failed to create profile policy: {0} diff --git a/base/server/cmscore/src/com/netscape/cmscore/base/FileConfigStore.java b/base/server/cmscore/src/com/netscape/cmscore/base/FileConfigStore.java index b77f86d781995e27bb0fe16135fc45a7d6fc4da3..4f8cb2743fdecc354338042a5219a9aaf6e27880 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/base/FileConfigStore.java +++ b/base/server/cmscore/src/com/netscape/cmscore/base/FileConfigStore.java @@ -33,12 +33,10 @@ import com.netscape.cmsutil.util.Utils; /** * FileConfigStore: - * Extends HashConfigStore with methods to load/save from/to file for + * Extends PropConfigStore with methods to load/save from/to file for * persistent storage. This is a configuration store agent who * reads data from a file. *

- * Note that a LdapConfigStore can be implemented so that it reads the configuration stores from the Ldap directory. - *

* * @version $Revision$, $Date$ * @see PropConfigStore diff --git a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java index 27e72352ef22c742b5ea09a180d440d58452dd49..ca0e09b785877da2a98fd2dd54977b8d3ebeaa24 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java +++ b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java @@ -17,26 +17,30 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cmscore.profile; -import java.io.File; import java.util.Enumeration; import java.util.Hashtable; -import java.util.StringTokenizer; import java.util.Vector; +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; + import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ILdapConnFactory; import com.netscape.certsrv.profile.EProfileException; import com.netscape.certsrv.profile.IProfile; import com.netscape.certsrv.profile.IProfileSubsystem; import com.netscape.certsrv.registry.IPluginInfo; import com.netscape.certsrv.registry.IPluginRegistry; +import com.netscape.cmscore.base.LDAPConfigStore; public class ProfileSubsystem implements IProfileSubsystem { - private static final String PROP_LIST = "list"; - private static final String PROP_CLASS_ID = "class_id"; - private static final String PROP_CONFIG = "config"; private static final String PROP_CHECK_OWNER = "checkOwner"; private static final String PROP_ENABLE = "enable"; @@ -45,9 +49,11 @@ public class ProfileSubsystem implements IProfileSubsystem { private IConfigStore mConfig = null; @SuppressWarnings("unused") private ISubsystem mOwner; - private Vector mProfileIds = new Vector(); - private Hashtable mProfiles = new Hashtable(); - private Hashtable mProfileClassIds = new Hashtable(); + private Vector mProfileIds; + private Hashtable mProfiles; + private Hashtable mProfileClassIds; + + private ILdapConnFactory dbFactory; /** * Retrieves the name of this subsystem. @@ -74,9 +80,20 @@ public class ProfileSubsystem implements IProfileSubsystem { public void init(ISubsystem owner, IConfigStore config) throws EBaseException { CMS.debug("ProfileSubsystem: start init"); + + // (re)init member collections + mProfileIds = new Vector(); + mProfiles = new Hashtable(); + mProfileClassIds = new Hashtable(); + IPluginRegistry registry = (IPluginRegistry) CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); + IConfigStore cs = CMS.getConfigStore(); + IConfigStore dbCfg = cs.getSubStore("internaldb"); + dbFactory = CMS.getLdapBoundConnFactory(); + dbFactory.init(dbCfg); + mConfig = config; mOwner = owner; @@ -88,24 +105,46 @@ public class ProfileSubsystem implements IProfileSubsystem { // *.profile2.config=config/profiles/profile2.cfg // read profile id, implementation, and its configuration files - String ids = config.getString(PROP_LIST, ""); - StringTokenizer st = new StringTokenizer(ids, ","); + String basedn = cs.getString("internaldb.basedn"); + String dn = "ou=certificateProfiles,ou=ca," + basedn; + LDAPConnection conn = dbFactory.getConn(); - while (st.hasMoreTokens()) { - String id = st.nextToken(); - IConfigStore subStore = config.getSubStore(id); - String classid = subStore.getString(PROP_CLASS_ID); - IPluginInfo info = registry.getPluginInfo("profile", classid); - if (info == null) { - throw new EBaseException("No plugins for type : profile, with id " + classid); - } - String configPath = subStore.getString(PROP_CONFIG); + String[] attrs = {"cn", "classId"}; + try { + LDAPSearchResults ldapProfiles = conn.search( + dn, LDAPConnection.SCOPE_ONE, "(objectclass=*)", attrs, false); + + while (ldapProfiles.hasMoreElements()) { + String id = ""; + try { + LDAPEntry ldapProfile = ldapProfiles.next(); + + id = (String) + ldapProfile.getAttribute("cn").getStringValues().nextElement(); - CMS.debug("Start Profile Creation - " + id + " " + classid + " " + info.getClassName()); - createProfile(id, classid, info.getClassName(), - configPath); + String classid = (String) + ldapProfile.getAttribute("classId").getStringValues().nextElement(); - CMS.debug("Done Profile Creation - " + id); + IPluginInfo info = registry.getPluginInfo("profile", classid); + if (info == null) { + CMS.debug("Error loading profile: No plugins for type : profile, with id " + classid); + } else { + CMS.debug("Start Profile Creation - " + id + " " + classid + " " + info.getClassName()); + createProfile(id, classid, info.getClassName()); + CMS.debug("Done Profile Creation - " + id); + } + } catch (LDAPException e) { + CMS.debug("Error reading profile '" + id + "'; skipping."); + } + } + } catch (LDAPException e) { + throw new EBaseException("Error reading profiles: " + e.toString()); + } finally { + try { + dbFactory.returnConn(conn); + } catch (Exception e) { + throw new EProfileException("Error releasing the ldap connection" + e.toString()); + } } Enumeration ee = getProfileIds(); @@ -120,16 +159,21 @@ public class ProfileSubsystem implements IProfileSubsystem { /** * Creates a profile instance. */ - public IProfile createProfile(String id, String classid, String className, - String configPath) + public IProfile createProfile(String id, String classid, String className) throws EProfileException { - IProfile profile = null; - try { - profile = (IProfile) Class.forName(className).newInstance(); - IConfigStore subStoreConfig = CMS.createFileConfigStore(configPath); + String[] objectClasses = {"top", "certProfile"}; + LDAPAttribute[] createAttrs = { + new LDAPAttribute("objectclass", objectClasses), + new LDAPAttribute("cn", id), + new LDAPAttribute("classId", classid) + }; + + IConfigStore subStoreConfig = new LDAPConfigStore( + dbFactory, createProfileDN(id), createAttrs, "certProfileConfig"); CMS.debug("ProfileSubsystem: initing " + className); + IProfile profile = (IProfile) Class.forName(className).newInstance(); profile.setId(id); profile.init(this, subStoreConfig); mProfileIds.addElement(id); @@ -144,63 +188,32 @@ public class ProfileSubsystem implements IProfileSubsystem { return null; } - public void deleteProfile(String id, String configPath) throws EProfileException { - + public void deleteProfile(String id) throws EProfileException { if (isProfileEnable(id)) { throw new EProfileException("CMS_PROFILE_DELETE_ENABLEPROFILE"); } - String ids = ""; + LDAPConnection conn; try { - ids = mConfig.getString(PROP_LIST, ""); - } catch (Exception e) { + conn = dbFactory.getConn(); + } catch (ELdapException e) { + throw new EProfileException("Error acquiring the ldap connection" + e.toString()); } - - StringTokenizer tokenizer = new StringTokenizer(ids, ","); - StringBuffer list = new StringBuffer(); - - while (tokenizer.hasMoreTokens()) { - String element = tokenizer.nextToken(); - - if (!element.equals(id)) { - list.append(element + ","); + try { + conn.delete(createProfileDN(id)); + } catch (LDAPException e) { + throw new EProfileException("CMS_PROFILE_DELETE_DATABASEERROR"); + } finally { + try { + dbFactory.returnConn(conn); + } catch (Exception e) { + throw new EProfileException("Error releasing the ldap connection" + e.toString()); } } - if (list.length() != 0) - list.deleteCharAt(list.length() - 1); - mConfig.putString(PROP_LIST, list.toString()); - mConfig.removeSubStore(id); - File file1 = new File(configPath); - - if (!file1.delete()) { - CMS.debug("ProfileSubsystem: deleteProfile: Cannot delete the configuration file : " + configPath); - } mProfileIds.removeElement(id); mProfiles.remove(id); mProfileClassIds.remove(id); - try { - CMS.getConfigStore().commit(false); - } catch (Exception e) { - } - } - - public void createProfileConfig(String id, String classId, - String configPath) - throws EProfileException { - try { - if (mProfiles.size() > 0) { - mConfig.putString(PROP_LIST, - mConfig.getString(PROP_LIST) + "," + id); - } else { - mConfig.putString(PROP_LIST, id); - } - mConfig.putString(id + "." + PROP_CLASS_ID, classId); - mConfig.putString(id + "." + PROP_CONFIG, configPath); - CMS.getConfigStore().commit(true); - } catch (EBaseException e) { - CMS.debug(e.toString()); - } } /** @@ -231,13 +244,6 @@ public class ProfileSubsystem implements IProfileSubsystem { return mConfig; } - /** - * Adds a profile. - */ - public void addProfile(String id, IProfile profile) - throws EProfileException { - } - public boolean isProfileEnable(String id) { IProfile profile = mProfiles.get(id); String enable = null; @@ -326,4 +332,20 @@ public class ProfileSubsystem implements IProfileSubsystem { return false; } } + + /** + * Compute the profile DN given an ID. + */ + private String createProfileDN(String id) throws EProfileException { + if (id == null) { + throw new EProfileException("CMS_PROFILE_DELETE_UNKNOWNPROFILE"); + } + String basedn; + try { + basedn = CMS.getConfigStore().getString("internaldb.basedn"); + } catch (EBaseException e) { + throw new EProfileException("CMS_PROFILE_DELETE_UNKNOWNPROFILE"); + } + return "cn=" + id + ",ou=certificateProfiles,ou=ca," + basedn; + } } -- 1.9.3 -------------- next part -------------- >From 5c9ddc197269e3ef89a199a8e45039838261fb4f Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Tue, 22 Jul 2014 00:03:47 -0400 Subject: [PATCH 07/10] Add ability to enable/disable dynamic subsystems The CA installation process requires starting with the profile subsystem disabled, then enabling it once profiles have been loaded into the database. Accordingly, to avoid hacks with hardcoded offsets, add the "enabled" CS.cfg configuration parameter along with methods to enable or disable a subsystem based on the subsystem ID. Subsystems are enabled by default. This commit also removes an assumption that the subsystem config sub-store names are sequential numbers beginning at `0'. --- base/common/src/com/netscape/certsrv/apps/CMS.java | 22 ++++++++ .../src/com/netscape/certsrv/apps/ICMSEngine.java | 9 +++ .../src/com/netscape/cmscore/apps/CMSEngine.java | 64 ++++++++++++++++------ .../netscape/cmscore/app/CMSEngineDefaultStub.java | 3 + 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/base/common/src/com/netscape/certsrv/apps/CMS.java b/base/common/src/com/netscape/certsrv/apps/CMS.java index 63c1a2cbde2ffdb0ce116c664ab373aeba91f5a3..38a69945be2100cf3538c3924f00fa9e7c409cc7 100644 --- a/base/common/src/com/netscape/certsrv/apps/CMS.java +++ b/base/common/src/com/netscape/certsrv/apps/CMS.java @@ -511,6 +511,28 @@ public final class CMS { } /** + * Enable the subsystem with the given ID. + * + * Does not start the subsystem. + * + * @param id Subsystem ID. + */ + public static void enableSubsystem(String id) throws EBaseException { + _engine.setSubsystemEnabled(id, true); + } + + /** + * Disable the subsystem with the given ID. + * + * Does not stop the subsystem. + * + * @param id Subsystem ID. + */ + public static void disableSubsystem(String id) throws EBaseException { + _engine.setSubsystemEnabled(id, false); + } + + /** * Retrieves the localized user message from UserMessages.properties. * * @param msgID message id defined in UserMessages.properties diff --git a/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java b/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java index 74fa090038b892713c872025470e4c4d9cb87760..5c78a7c0f9c4144df8369a6017876f900e57605d 100644 --- a/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java +++ b/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java @@ -171,6 +171,15 @@ public interface ICMSEngine extends ISubsystem { public Enumeration getSubsystems(); /** + * Set whether the given subsystem is enabled. + * + * @param id The subsystem ID. + * @param enabled Whether the subsystem is enabled + */ + public void setSubsystemEnabled(String id, boolean enabled) + throws EBaseException; + + /** * Retrieves the registered subsytem with the given name. * * @param name subsystem name diff --git a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java index 68c64824e37bcad282a5bbeabf6b943fabf39481..2641057d9ad6d4de751f7215d52374b08eceb1f8 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java +++ b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java @@ -29,6 +29,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; @@ -181,6 +182,7 @@ public class CMSEngine implements ICMSEngine { private static final String PROP_SUBSYSTEM = "subsystem"; private static final String PROP_ID = "id"; private static final String PROP_CLASS = "class"; + private static final String PROP_ENABLED = "enabled"; private static final String SERVER_XML = "server.xml"; public static final SubsystemRegistry mSSReg = SubsystemRegistry.getInstance(); @@ -866,32 +868,34 @@ public class CMSEngine implements ICMSEngine { } } + private ArrayList getDynSubsystemNames() throws EBaseException { + IConfigStore ssconfig = mConfig.getSubStore(PROP_SUBSYSTEM); + Enumeration ssNames = ssconfig.getSubStoreNames(); + ArrayList ssNamesList = new ArrayList(); + while (ssNames.hasMoreElements()) + ssNamesList.add(ssNames.nextElement()); + return ssNamesList; + } + /** * load dynamic subsystems */ private void loadDynSubsystems() throws EBaseException { - IConfigStore ssconfig = mConfig.getSubStore(PROP_SUBSYSTEM); - - // count number of dyn loaded subsystems. - Enumeration ssnames = ssconfig.getSubStoreNames(); - int nsubsystems = 0; - - for (nsubsystems = 0; ssnames.hasMoreElements(); nsubsystems++) - ssnames.nextElement(); + ArrayList ssNames = getDynSubsystemNames(); if (Debug.ON) { - Debug.trace(nsubsystems + " dyn subsystems loading.."); + Debug.trace(ssNames.size() + " dyn subsystems loading.."); } - if (nsubsystems == 0) - return; // load dyn subsystems. - mDynSubsystems = new SubsystemInfo[nsubsystems]; - for (int i = 0; i < mDynSubsystems.length; i++) { - IConfigStore config = - ssconfig.getSubStore(String.valueOf(i)); + IConfigStore ssconfig = mConfig.getSubStore(PROP_SUBSYSTEM); + mDynSubsystems = new SubsystemInfo[ssNames.size()]; + int i = 0; + for (String ssName : ssNames) { + IConfigStore config = ssconfig.getSubStore(ssName); String id = config.getString(PROP_ID); String classname = config.getString(PROP_CLASS); + boolean enabled = config.getBoolean(PROP_ENABLED, true); ISubsystem ss = null; try { @@ -906,11 +910,29 @@ public class CMSEngine implements ICMSEngine { throw new EBaseException( CMS.getUserMessage("CMS_BASE_LOAD_FAILED_1", id, e.toString())); } - mDynSubsystems[i] = new SubsystemInfo(id, ss); + mDynSubsystems[i++] = new SubsystemInfo(id, ss, enabled); Debug.trace("loaded dyn subsystem " + id); } } + /** + * Set whether the given subsystem is enabled. + * + * @param id The subsystem ID. + * @param enabled Whether the subsystem is enabled + */ + public void setSubsystemEnabled(String id, boolean enabled) + throws EBaseException { + IConfigStore ssconfig = mConfig.getSubStore(PROP_SUBSYSTEM); + for (String ssName : getDynSubsystemNames()) { + IConfigStore config = ssconfig.getSubStore(ssName); + if (id.equalsIgnoreCase(config.getString(PROP_ID))) { + config.putBoolean(PROP_ENABLED, enabled); + break; + } + } + } + public LDAPConnection getBoundConnection(String host, int port, int version, LDAPSSLSocketFactoryExt fac, String bindDN, String bindPW) throws LDAPException { @@ -928,6 +950,10 @@ public class CMSEngine implements ICMSEngine { IConfigStore ssConfig = mConfig.getSubStore(id); CMS.debug("CMSEngine: initSubsystem id=" + id); + if (!ssinfo.enabled) { + CMS.debug("CMSEngine: subsystem disabled id=" + id); + return; + } if (doSetId) ss.setId(id); CMS.debug("CMSEngine: ready to init id=" + id); @@ -2000,10 +2026,16 @@ class WarningListener implements ILogEventListener { class SubsystemInfo { public final String mId; public final ISubsystem mInstance; + public final boolean enabled; public SubsystemInfo(String id, ISubsystem ssInstance) { + this(id, ssInstance, true); + } + + public SubsystemInfo(String id, ISubsystem ssInstance, boolean enabled) { mId = id; mInstance = ssInstance; + this.enabled = enabled; } } diff --git a/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java b/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java index db39964f2411d169492cad8dc817a0fe4163b765..0b7518d81c516b2073cc54ae69a9b9fb0262c669 100644 --- a/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java +++ b/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java @@ -135,6 +135,9 @@ public class CMSEngineDefaultStub implements ICMSEngine { return null; } + public void setSubsystemEnabled(String id, boolean enabled) { + }; + public ISubsystem getSubsystem(String name) { return null; } -- 1.9.3 -------------- next part -------------- >From 50514219cad1ca7a5abd4934f7f18f674542d320 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Fri, 18 Jul 2014 02:01:58 -0400 Subject: [PATCH 08/10] Import profiles when spawning CA instance --- base/ca/shared/conf/CS.cfg.in | 1 + .../server/ca/rest/CAInstallerService.java | 114 +++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/base/ca/shared/conf/CS.cfg.in b/base/ca/shared/conf/CS.cfg.in index 4ab8974e6340d81d23bb7f5ea05a07b0936b6463..13ed9c4b9f43f8e76c6f5faffbc78c4a61ccf718 100644 --- a/base/ca/shared/conf/CS.cfg.in +++ b/base/ca/shared/conf/CS.cfg.in @@ -1139,6 +1139,7 @@ subsystem.0.class=com.netscape.ca.CertificateAuthority subsystem.0.id=ca subsystem.1.class=com.netscape.cmscore.profile.ProfileSubsystem subsystem.1.id=profile +subsystem.1.enabled=false subsystem.2.class=com.netscape.cmscore.selftests.SelfTestSubsystem subsystem.2.id=selftests subsystem.3.class=com.netscape.cmscore.cert.CrossCertPairSubsystem diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java index bb823eece4729599b6badd9ca0e24ef560b9f279..00aa7758def2925f346024d0dc5cfc38f3c2b22b 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java @@ -17,13 +17,27 @@ // --- END COPYRIGHT BLOCK --- package org.dogtagpki.server.ca.rest; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.StringTokenizer; + +import netscape.ldap.LDAPAttribute; + import org.dogtagpki.server.rest.SystemConfigService; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ILdapConnFactory; +import com.netscape.certsrv.registry.IPluginInfo; +import com.netscape.certsrv.registry.IPluginRegistry; import com.netscape.certsrv.system.ConfigurationRequest; import com.netscape.cms.servlet.csadmin.ConfigurationUtils; +import com.netscape.cmscore.base.LDAPConfigStore; + /** * @author alee @@ -64,5 +78,105 @@ public class CAInstallerService extends SystemConfigService { CMS.debug(e); throw new PKIException("Errors in determining if security domain host is a master CA"); } + + try { + CMS.enableSubsystem("profile"); + } catch (Exception e) { + CMS.debug(e); + throw new PKIException("Error enabling ProfileSubsystem"); + } + } + + @Override + public void initializeDatabase(ConfigurationRequest data) { + super.initializeDatabase(data); + + if (!data.isClone()) { + try { + importProfiles("/usr/share/pki"); + } catch (Exception e) { + throw new PKIException("Error importing profiles."); + } + } + } + + /** + * Import profiles from the filesystem into the database. + * + * @param configRoot Where to look for the profile files. For a + * fresh installation this should be + * "/usr/share/pki". For existing installations it + * should be CMS.getConfigStore().getString("instanceRoot"). + * + */ + public void importProfiles(String configRoot) + throws EBaseException, ELdapException { + IPluginRegistry registry = (IPluginRegistry) + CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); + IConfigStore profileCfg = cs.getSubStore("profile"); + String profileIds = profileCfg.getString("list", ""); + StringTokenizer st = new StringTokenizer(profileIds, ","); + + IConfigStore dbCfg = cs.getSubStore("internaldb"); + ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory(); + dbFactory.init(dbCfg); + + while (st.hasMoreTokens()) { + String profileId = st.nextToken(); + IConfigStore profileSubCfg = profileCfg.getSubStore(profileId); + String classId = profileSubCfg.getString("class_id", ""); + try { + IPluginInfo info = registry.getPluginInfo("profile", classId); + if (info == null) { + throw new EBaseException("No plugins for type : profile, with id " + classId); + } + + String profilePath = configRoot + "/ca/profiles/ca/" + profileId + ".cfg"; + CMS.debug("Importing profile '" + profileId + "' from " + profilePath); + importProfile(dbFactory, classId, profileId, profilePath); + } catch (EBaseException e) { + CMS.debug("Error importing profile '" + profileId + "': " + e.toString()); + CMS.debug(" Continuing with profile import procedure..."); + } + } + } + + /** + * Import one profile from the filesystem into the database. + * + * @param dbFactory LDAP connection factory. + * @param classId The profile class of the profile to import. + * @param profileId The ID of the profile to import. + * @param profilePath Path to the on-disk profile configuration. + */ + public void importProfile( + ILdapConnFactory dbFactory, String classId, + String profileId, String profilePath) + throws EBaseException { + + String basedn = cs.getString("internaldb.basedn", ""); + + String dn = "cn=" + profileId + ",ou=certificateProfiles,ou=ca," + basedn; + + String[] objectClasses = {"top", "certProfile"}; + LDAPAttribute[] createAttrs = { + new LDAPAttribute("objectclass", objectClasses), + new LDAPAttribute("cn", profileId), + new LDAPAttribute("classId", classId) + }; + + IConfigStore configStore = new LDAPConfigStore( + dbFactory, dn, createAttrs, "certProfileConfig"); + + try { + FileInputStream input = new FileInputStream(profilePath); + configStore.load(input); + } catch (FileNotFoundException e) { + throw new EBaseException("Could not find file for profile: " + profileId); + } catch (IOException e) { + throw new EBaseException("Error loading data for profile: " + profileId); + } + + configStore.commit(false /* no backup */); } } -- 1.9.3 -------------- next part -------------- >From de0e6f17d21048297d53c13bbeaab95d4134344d Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Wed, 23 Jul 2014 02:40:07 -0400 Subject: [PATCH 09/10] Update pki-profile CLI commands to work with "raw" format Update CLI commands for working with the (now LDAP-based) profiles in the same format as was used by the files, by way of the --raw option. Also add the "edit" command to interactively edit a profile. --- .../dogtagpki/server/ca/rest/ProfileService.java | 160 ++++++++++++++++++--- .../netscape/certsrv/profile/ProfileClient.java | 42 ++++++ .../netscape/certsrv/profile/ProfileResource.java | 20 ++- .../netscape/cmstools/profile/ProfileAddCLI.java | 25 +++- .../com/netscape/cmstools/profile/ProfileCLI.java | 15 ++ .../netscape/cmstools/profile/ProfileEditCLI.java | 109 ++++++++++++++ .../cmstools/profile/ProfileModifyCLI.java | 26 +++- .../netscape/cmstools/profile/ProfileShowCLI.java | 34 +++-- 8 files changed, 387 insertions(+), 44 deletions(-) create mode 100644 base/java-tools/src/com/netscape/cmstools/profile/ProfileEditCLI.java diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java index d3f08b270fd66154da880d47be30ea48716b75bd..9de7c7707d2430375cedccf1fd6f5784836df6e8 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/ProfileService.java @@ -18,6 +18,8 @@ package org.dogtagpki.server.ca.rest; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; import java.security.Principal; @@ -27,6 +29,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Properties; import java.util.Vector; import javax.servlet.http.HttpServletRequest; @@ -163,9 +166,7 @@ public class ProfileService extends PKIService implements ProfileResource { return createOKResponse(infos); } - @Override - public Response retrieveProfile(String profileId) throws ProfileNotFoundException { - ProfileData data = null; + private IProfile getProfile(String profileId) throws ProfileNotFoundException { boolean visibleOnly = true; if (profileId == null) { @@ -185,24 +186,12 @@ public class ProfileService extends PKIService implements ProfileResource { visibleOnly = false; } - Enumeration profileIds = ps.getProfileIds(); - - IProfile profile = null; - if (profileIds != null) { - while (profileIds.hasMoreElements()) { - String id = profileIds.nextElement(); - - if (id.equals(profileId)) { - - try { - profile = ps.getProfile(profileId); - } catch (EProfileException e) { - e.printStackTrace(); - throw new ProfileNotFoundException(profileId); - } - break; - } - } + IProfile profile; + try { + profile = ps.getProfile(profileId); + } catch (EProfileException e) { + e.printStackTrace(); + throw new ProfileNotFoundException(profileId); } if (profile == null) { @@ -213,6 +202,14 @@ public class ProfileService extends PKIService implements ProfileResource { throw new ProfileNotFoundException(profileId); } + return profile; + } + + @Override + public Response retrieveProfile(String profileId) throws ProfileNotFoundException { + IProfile profile = getProfile(profileId); + + ProfileData data = null; try { data = createProfileData(profileId); } catch (EBaseException e) { @@ -228,6 +225,19 @@ public class ProfileService extends PKIService implements ProfileResource { return createOKResponse(data); } + @Override + public Response retrieveProfileRaw(String profileId) + throws ProfileNotFoundException { + IProfile profile = getProfile(profileId); + ByteArrayOutputStream data = new ByteArrayOutputStream(); + // add profileId and classId "virtual" properties + profile.getConfigStore().put("profileId", profileId); + profile.getConfigStore().put("classId", ps.getProfileClassId(profileId)); + profile.getConfigStore().save(data, null); + return createOKResponse(data.toByteArray()); + } + + public ProfileData createProfileData(String profileId) throws EBaseException { IProfile profile; @@ -499,6 +509,75 @@ public class ProfileService extends PKIService implements ProfileResource { return createCreatedResponse(profileData, profileData.getLink().getHref()); } catch (EBaseException e) { + CMS.debug("createProfile: error creating profile"); + CMS.debug(e); + + auditProfileChange( + ScopeDef.SC_PROFILE_RULES, + OpDef.OP_ADD, + profileId, + ILogger.FAILURE, + auditParams); + + throw new PKIException("Error in creating profile"); + } + } + + @Override + public Response createProfileRaw(byte[] data) { + if (data == null) { + CMS.debug("createProfileRaw: profile data is null"); + throw new BadRequestException("Unable to create profile: Invalid profile data."); + } + + if (ps == null) { + CMS.debug("createProfile: ps is null"); + throw new PKIException("Error creating profile. Profile Service not available"); + } + + Map auditParams = new LinkedHashMap(); + String profileId = null; + String classId = null; + try { + // load data as properties and read profileId and classId + Properties properties = new Properties(); + properties.load(new ByteArrayInputStream(data)); + profileId = properties.getProperty("profileId"); + classId = properties.getProperty("classId"); + } catch (IOException e) { + throw new BadRequestException("Could not parse raw profile data."); + } + if (profileId == null) { + throw new BadRequestException("Profile data did not contain profileId attribute."); + } + if (classId == null) { + throw new BadRequestException("Profile data did not contain classId attribute."); + } + + try { + IProfile profile = ps.getProfile(profileId); + if (profile != null) { + throw new BadRequestException("Profile already exists"); + } + + auditParams.put("class_id", classId); + + IPluginInfo info = registry.getPluginInfo("profile", classId); + + profile = ps.createProfile(profileId, classId, info.getClassName()); + profile.getConfigStore().commit(false); + profile.getConfigStore().load(new ByteArrayInputStream(data)); + ps.disableProfile(profileId); + + auditProfileChange( + ScopeDef.SC_PROFILE_RULES, + OpDef.OP_ADD, + profileId, + ILogger.SUCCESS, + auditParams); + + return createCreatedResponse(data, uriInfo.getAbsolutePath()); + } catch (EBaseException | IOException e) { CMS.debug("createProfile: error in creating profile: " + e); e.printStackTrace(); @@ -550,6 +629,45 @@ public class ProfileService extends PKIService implements ProfileResource { } } + @Override + public Response modifyProfileRaw(String profileId, byte[] data) { + if (profileId == null) { + CMS.debug("modifyProfile: invalid request. profileId is null"); + throw new BadRequestException("Unable to modify profile: Invalid Profile Id"); + } + + if (data == null) { + CMS.debug("modifyProfile: invalid request. data is null"); + throw new BadRequestException("Unable to modify profile: Invalid profile data"); + } + + if (ps == null) { + CMS.debug("modifyProfile: ps is null"); + throw new PKIException("Error modifying profile. Profile Service not available"); + } + + if (ps.isProfileEnable(profileId)) { + throw new BadRequestException("Cannot change profile data. Profile must be disabled"); + } + + try { + IProfile profile = ps.getProfile(profileId); + if (profile == null) { + throw new ProfileNotFoundException(profileId); + } + + profile.getConfigStore().load(new ByteArrayInputStream(data)); + ps.disableProfile(profileId); + profile.getConfigStore().commit(false); + + return createOKResponse(data); + } catch (EBaseException | IOException e) { + CMS.debug("modifyProfile: error modifying profile " + profileId); + CMS.debug(e); + throw new PKIException("Error modifying profile."); + } + } + private void changeProfileData(ProfileData data, IProfile profile) { String profileId = data.getId(); if (profile == null) { diff --git a/base/common/src/com/netscape/certsrv/profile/ProfileClient.java b/base/common/src/com/netscape/certsrv/profile/ProfileClient.java index 51d159aca687719a2dace939da5b09c4809872ec..7f0b08f0e40d9a5314bcf741c2dc2fc514802ad5 100644 --- a/base/common/src/com/netscape/certsrv/profile/ProfileClient.java +++ b/base/common/src/com/netscape/certsrv/profile/ProfileClient.java @@ -17,10 +17,15 @@ //--- END COPYRIGHT BLOCK --- package com.netscape.certsrv.profile; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.URISyntaxException; +import java.util.Properties; import javax.ws.rs.core.Response; +import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.client.Client; import com.netscape.certsrv.client.PKIClient; @@ -45,6 +50,11 @@ public class ProfileClient extends Client { return client.getEntity(response, ProfileData.class); } + public Properties retrieveProfileRaw(String id) { + Response response = profileClient.retrieveProfileRaw(id); + return byteArrayToProperties(client.getEntity(response, byte[].class)); + } + public ProfileDataInfos listProfiles(Integer start, Integer size) { Response response = profileClient.listProfiles(start, size); return client.getEntity(response, ProfileDataInfos.class); @@ -65,13 +75,45 @@ public class ProfileClient extends Client { return client.getEntity(response, ProfileData.class); } + public Properties createProfileRaw(Properties properties) { + Response response = + profileClient.createProfileRaw(propertiesToByteArray(properties)); + return byteArrayToProperties(client.getEntity(response, byte[].class)); + } + public ProfileData modifyProfile(ProfileData data) { Response response = profileClient.modifyProfile(data.getId(), data); return client.getEntity(response, ProfileData.class); } + public Properties modifyProfileRaw(String profileId, Properties properties) { + Response response = + profileClient.modifyProfileRaw(profileId, propertiesToByteArray(properties)); + return byteArrayToProperties(client.getEntity(response, byte[].class)); + } + public void deleteProfile(String id) { Response response = profileClient.deleteProfile(id); client.getEntity(response, Void.class); } + + private Properties byteArrayToProperties(byte[] data) throws PKIException { + Properties properties = new Properties(); + try { + properties.load(new ByteArrayInputStream(data)); + } catch (IOException e) { + throw new PKIException("Failed to decode profile Properties: " + e.toString()); + } + return properties; + } + + private byte[] propertiesToByteArray(Properties properties) throws PKIException { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + try { + properties.store(data, null); + } catch (IOException e) { + throw new PKIException("Failed to encode profile Properties: " + e.toString()); + } + return data.toByteArray(); + } } diff --git a/base/common/src/com/netscape/certsrv/profile/ProfileResource.java b/base/common/src/com/netscape/certsrv/profile/ProfileResource.java index 87449b27e749c6088f65b53192eb5ac101263f1e..410f98a468dbfca0bf587623935f2a63e97923e9 100644 --- a/base/common/src/com/netscape/certsrv/profile/ProfileResource.java +++ b/base/common/src/com/netscape/certsrv/profile/ProfileResource.java @@ -31,12 +31,24 @@ public interface ProfileResource { @ACLMapping("profiles.read") public Response retrieveProfile(@PathParam("id") String id); + @GET + @Path("{id}/raw") + @ClientResponseType(entityType=byte[].class) + @ACLMapping("profiles.read") + public Response retrieveProfileRaw(@PathParam("id") String id); + @POST @ClientResponseType(entityType=ProfileData.class) @ACLMapping("profiles.create") public Response createProfile(ProfileData data); @POST + @Path("raw") + @ClientResponseType(entityType=byte[].class) + @ACLMapping("profiles.create") + public Response createProfileRaw(byte[] data); + + @POST @Path("{id}") @ClientResponseType(entityType=Void.class) @ACLMapping("profiles.approve") @@ -48,9 +60,15 @@ public interface ProfileResource { @ACLMapping("profiles.modify") public Response modifyProfile(@PathParam("id") String id, ProfileData data); + @PUT + @Path("{id}/raw") + @ClientResponseType(entityType=byte[].class) + @ACLMapping("profiles.modify") + public Response modifyProfileRaw(@PathParam("id") String id, byte[] data); + @DELETE @Path("{id}") @ClientResponseType(entityType=Void.class) @ACLMapping("profiles.delete") public Response deleteProfile(@PathParam("id") String id); -} \ No newline at end of file +} diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileAddCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileAddCLI.java index 62bd145261a798fd89b3b8aaed389d0b2fb886fa..7aaf09d8830fa02d52a5e5956e22fea4e45bf74e 100644 --- a/base/java-tools/src/com/netscape/cmstools/profile/ProfileAddCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileAddCLI.java @@ -1,11 +1,13 @@ package com.netscape.cmstools.profile; -import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Arrays; +import java.util.Properties; import javax.xml.bind.JAXBException; import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; import org.apache.commons.cli.ParseException; import com.netscape.certsrv.profile.ProfileData; @@ -19,6 +21,10 @@ public class ProfileAddCLI extends CLI { public ProfileAddCLI(ProfileCLI profileCLI) { super("add", "Add profiles", profileCLI); this.profileCLI = profileCLI; + + Option optRaw = new Option(null, "raw", false, "Use raw format"); + optRaw.setArgName("raw"); + options.addOption(optRaw); } public void printHelp() { @@ -59,13 +65,20 @@ public class ProfileAddCLI extends CLI { } try { - ProfileData data = ProfileCLI.readProfileFromFile(filename); - data = profileCLI.profileClient.createProfile(data); + if (cmd.hasOption("raw")) { + Properties properties = ProfileCLI.readRawProfileFromFile(filename); + String profileId = properties.getProperty("profileId"); + profileCLI.profileClient.createProfileRaw(properties).store(System.out, null); + MainCLI.printMessage("Added profile " + profileId); + } else { + ProfileData data = ProfileCLI.readProfileFromFile(filename); + data = profileCLI.profileClient.createProfile(data); - MainCLI.printMessage("Added profile " + data.getId()); + MainCLI.printMessage("Added profile " + data.getId()); - ProfileCLI.printProfile(data, profileCLI.getClient().getConfig().getServerURI()); - } catch (FileNotFoundException | JAXBException e) { + ProfileCLI.printProfile(data, profileCLI.getClient().getConfig().getServerURI()); + } + } catch (IOException | JAXBException e) { System.err.println("Error: " + e.getMessage()); System.exit(-1); } diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java index 732b597afd1dbb5b9440d451f34b2f39e20fb904..387593f7884a1cf869e1e3fecfa446001b1edeb4 100644 --- a/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java @@ -3,8 +3,12 @@ package com.netscape.cmstools.profile; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Locale; +import java.util.Properties; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -32,6 +36,7 @@ public class ProfileCLI extends CLI { addModule(new ProfileShowCLI(this)); addModule(new ProfileAddCLI(this)); addModule(new ProfileModifyCLI(this)); + addModule(new ProfileEditCLI(this)); addModule(new ProfileRemoveCLI(this)); addModule(new ProfileEnableCLI(this)); addModule(new ProfileDisableCLI(this)); @@ -130,6 +135,16 @@ public class ProfileCLI extends CLI { return data; } + public static Properties readRawProfileFromFile(String filename) + throws IOException, RuntimeException { + Properties properties = new Properties(); + properties.load(Files.newInputStream(Paths.get(filename))); + String profileId = properties.getProperty("profileId"); + if (profileId == null) + throw new RuntimeException("Error: Missing profileId property in profile data."); + return properties; + } + public static void saveEnrollmentTemplateToFile(String filename, CertEnrollmentRequest request) throws JAXBException, FileNotFoundException { JAXBContext context = JAXBContext.newInstance(CertEnrollmentRequest.class); diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileEditCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileEditCLI.java new file mode 100644 index 0000000000000000000000000000000000000000..f8ce89fb18544f774de48c9027a014bd8d450e02 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileEditCLI.java @@ -0,0 +1,109 @@ +//--- BEGIN COPYRIGHT BLOCK --- +//This program is free software; you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation; version 2 of the License. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. +// +//You should have received a copy of the GNU General Public License along +//with this program; if not, write to the Free Software Foundation, Inc., +//51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +//(C) 2014 Red Hat, Inc. +//All rights reserved. +//--- END COPYRIGHT BLOCK --- + +package com.netscape.cmstools.profile; + +import java.lang.ProcessBuilder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Properties; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.ParseException; + +import com.netscape.cmstools.cli.CLI; + +public class ProfileEditCLI extends CLI { + + public ProfileCLI profileCLI; + + public ProfileEditCLI(ProfileCLI profileCLI) { + super("edit", "Edit profiles (config-store format)", profileCLI); + this.profileCLI = profileCLI; + } + + public void printHelp() { + formatter.printHelp(getFullName() + " [OPTIONS...]", options); + } + + public void execute(String[] args) throws Exception { + // Always check for "--help" prior to parsing + if (Arrays.asList(args).contains("--help")) { + // Display usage + printHelp(); + System.exit(0); + } + + CommandLine cmd = null; + + try { + cmd = parser.parse(options, args); + } catch (ParseException e) { + System.err.println("Error: " + e.getMessage()); + printHelp(); + System.exit(-1); + } + + String[] cmdArgs = cmd.getArgs(); + + if (cmdArgs.length < 1) { + System.err.println("Error: No Profile ID specified."); + printHelp(); + System.exit(-1); + } + + String profileId = cmdArgs[0]; + + // read profile into temporary file + Properties orig = profileCLI.profileClient.retrieveProfileRaw(profileId); + String enabled = orig.getProperty("enable"); + if (enabled == null || !enabled.equalsIgnoreCase("false")) { + System.err.println("Error: Cannot edit profile. Profile must be disabled."); + System.exit(-1); + } + Path tempFile = Files.createTempFile("pki", ".cfg"); + orig.store(Files.newOutputStream(tempFile), null); + + // invoke editor on temporary file + String editor = System.getenv("EDITOR"); + String[] command; + if (editor == null || editor.trim().isEmpty()) { + command = new String[] {"/usr/bin/env", "vi", tempFile.toString()}; + } else { + command = new String[] {editor.trim(), tempFile.toString()}; + } + ProcessBuilder pb = new ProcessBuilder(command); + pb.inheritIO(); + int exitCode = pb.start().waitFor(); + if (exitCode != 0) { + System.err.println("Error: editor exited abnormally."); + System.exit(-1); + } + + // read data from temporary file and modify if changed + Properties cur = new Properties(); + cur.load(Files.newInputStream(tempFile)); + Files.delete(tempFile); + + if (!cur.equals(orig)) { + profileCLI.profileClient.modifyProfileRaw(profileId, cur); + } + cur.store(System.out, null); + } +} diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileModifyCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileModifyCLI.java index bbeb91981e79b6690efa55c8f293b017ea4ace31..cc0f415b78facbaa4cacb7ab4e914717586cfd0e 100644 --- a/base/java-tools/src/com/netscape/cmstools/profile/ProfileModifyCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileModifyCLI.java @@ -1,11 +1,13 @@ package com.netscape.cmstools.profile; -import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Arrays; +import java.util.Properties; import javax.xml.bind.JAXBException; import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; import org.apache.commons.cli.ParseException; import com.netscape.certsrv.profile.ProfileData; @@ -19,6 +21,10 @@ public class ProfileModifyCLI extends CLI { public ProfileModifyCLI(ProfileCLI profileCLI) { super("mod", "Modify profiles", profileCLI); this.profileCLI = profileCLI; + + Option optRaw = new Option(null, "raw", false, "Use raw format"); + optRaw.setArgName("raw"); + options.addOption(optRaw); } public void printHelp() { @@ -59,14 +65,20 @@ public class ProfileModifyCLI extends CLI { } try { - ProfileData data = ProfileCLI.readProfileFromFile(filename); - data = profileCLI.profileClient.modifyProfile(data); + if (cmd.hasOption("raw")) { + Properties properties = ProfileCLI.readRawProfileFromFile(filename); + String profileId = properties.getProperty("profileId"); + profileCLI.profileClient.modifyProfileRaw(profileId, properties).store(System.out, null); + MainCLI.printMessage("Modified profile " + profileId); + } else { + ProfileData data = ProfileCLI.readProfileFromFile(filename); + data = profileCLI.profileClient.modifyProfile(data); - MainCLI.printMessage("Modified profile " + data.getId()); + MainCLI.printMessage("Modified profile " + data.getId()); - ProfileCLI.printProfile(data, profileCLI.getClient().getConfig().getServerURI()); - - } catch (FileNotFoundException | JAXBException e) { + ProfileCLI.printProfile(data, profileCLI.getClient().getConfig().getServerURI()); + } + } catch (IOException | JAXBException e) { System.err.println("Error: " + e.getMessage()); System.exit(-1); } diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileShowCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileShowCLI.java index f5b636c1a8f9dbfe60d29fc07bae373be3ea966e..1dd85f43bf349c4dbca9b4b764f2d40fe7f421aa 100644 --- a/base/java-tools/src/com/netscape/cmstools/profile/ProfileShowCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileShowCLI.java @@ -1,6 +1,8 @@ package com.netscape.cmstools.profile; +import java.io.FileOutputStream; import java.util.Arrays; +import java.util.Properties; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -26,9 +28,13 @@ public class ProfileShowCLI extends CLI { } public void createOptions() { - Option option = new Option(null, "output", true, "Output filename"); - option.setArgName("filename"); - options.addOption(option); + Option optFilename = new Option(null, "output", true, "Output filename"); + optFilename.setArgName("filename"); + options.addOption(optFilename); + + Option optRaw = new Option(null, "raw", false, "Use raw format"); + optRaw.setArgName("raw"); + options.addOption(optRaw); } public void execute(String[] args) throws Exception { @@ -70,14 +76,24 @@ public class ProfileShowCLI extends CLI { } } - ProfileData profileData = profileCLI.profileClient.retrieveProfile(profileId); + if (cmd.hasOption("raw")) { + Properties profileConfig = profileCLI.profileClient.retrieveProfileRaw(profileId); - MainCLI.printMessage("Profile \"" + profileId + "\""); - - if (filename != null) { - ProfileCLI.saveProfileToFile(filename, profileData); + if (filename != null) { + profileConfig.store(new FileOutputStream(filename), null); + MainCLI.printMessage("Saved profile " + profileId + " to " + filename); + } else { + profileConfig.store(System.out, null); + } } else { - ProfileCLI.printProfile(profileData, profileCLI.getClient().getConfig().getServerURI()); + MainCLI.printMessage("Profile \"" + profileId + "\""); + ProfileData profileData = profileCLI.profileClient.retrieveProfile(profileId); + + if (filename != null) { + ProfileCLI.saveProfileToFile(filename, profileData); + } else { + ProfileCLI.printProfile(profileData, profileCLI.getClient().getConfig().getServerURI()); + } } } -- 1.9.3 From ftweedal at redhat.com Thu Nov 20 06:32:41 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 20 Nov 2014 16:32:41 +1000 Subject: [Pki-devel] [PATCH] pki-ftweedal-0015-Monitor-database-for-changes-to-LDAP-profiles.patch In-Reply-To: <5436A001.9050804@redhat.com> References: <20140922073626.GU5346@dhcp-40-8.bne.redhat.com> <20141001064230.GN5346@dhcp-40-8.bne.redhat.com> <5436A001.9050804@redhat.com> Message-ID: <20141120063241.GD8412@dhcp-40-8.bne.redhat.com> Thanks Endi, you picked up several subtle problems in your review. I addressed all the issues in the new patch (attached). Fraser On Thu, Oct 09, 2014 at 09:47:29AM -0500, Endi Sukma Dewata wrote: > On 10/1/2014 1:42 AM, Fraser Tweedale wrote: > >On Mon, Sep 22, 2014 at 05:36:26PM +1000, Fraser Tweedale wrote: > >>This is the first cut of the LDAP profile change monitoring. It > >>depends on patches 0004..0009 and 0014 > >>(https://www.redhat.com/archives/pki-devel/2014-September/msg00052.html). > >> > >>To summarise the implementation: a separate thread carries out a > >>persistent LDAP search and calls back into the ProfileSubsystem when > >>changes occur. I haven't had much experience with persistent > >>searches or multithreaded programming in Java, so eyeballs familiar > >>with those areas are needed. > >> > >>I haven't yet tested with changes replicating between clones (a task > >>for tomorrow) but I wanted to get the patch on list for feedback as > >>early as possible. > >> > >Further to earlier email, I tested profile change replication > >between clones and it is working well. > > > > Some comments: > > 1. The MODDN operation can potentially happen if someone renames the profile > in LDAP directly, and that's easier to do with LDAP-based profiles. I think > we should handle this case too. > > 2. If the LDAP server is restarted the Monitor will stop working and > subsequent changes are not detected. The Monitor should automatically > reconnect in case the LDAP connection is interrupted. > > 3. Since the changesOnly is set to true, there's a possibility that changes > that happens after initial profile loading and before the Monitor starts > will not be detected. To avoid this problem, the changesOnly should be > false, and the initial profile loading should be done in the Monitor as > well. > > 4. The following assignment is redundant because the value is overwritten by > the next line: > > String profileId = ""; > > 5. When receiving a MODIFY operation the Monitor calls forgetProfile(). It's > probably redundant because readProfile() calls createProfile() which calls > forgetProfile() too. > > 6. Generally it's recommended to implement Runnable instead of extending > Thread. The Monitor can be merged into ProfileSubsystem. > > I think #1, #2, #3 are important to fix because there is no mechanism to > refresh the profiles in memory other than by restarting the server, so we > cannot miss any changes in the database. > > > -- > Endi S. Dewata -------------- next part -------------- >From 518841c8527c57f45b1b434685dd0b7276f4f366 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Mon, 22 Sep 2014 03:22:57 -0400 Subject: [PATCH 10/10] Monitor database for changes to LDAP profiles. Use a persistent query to monitor the database for changes to LDAP profiles, and update the contents of the ProfileSubsystem according to the changes (Add/Modify/Delete) that occur. The monitoring occurs within its own thread. --- .../netscape/cmscore/profile/ProfileSubsystem.java | 207 ++++++++++++++++----- 1 file changed, 160 insertions(+), 47 deletions(-) diff --git a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java index ca0e09b785877da2a98fd2dd54977b8d3ebeaa24..2318fcf997801e3a729a7dab7caad3dce182486b 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java +++ b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java @@ -17,15 +17,21 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cmscore.profile; +import java.lang.Thread; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import netscape.ldap.LDAPAttribute; import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPControl; import netscape.ldap.LDAPEntry; import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchConstraints; import netscape.ldap.LDAPSearchResults; +import netscape.ldap.controls.LDAPEntryChangeControl; +import netscape.ldap.controls.LDAPPersistSearchControl; +import netscape.ldap.util.DN; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; @@ -40,7 +46,7 @@ import com.netscape.certsrv.registry.IPluginInfo; import com.netscape.certsrv.registry.IPluginRegistry; import com.netscape.cmscore.base.LDAPConfigStore; -public class ProfileSubsystem implements IProfileSubsystem { +public class ProfileSubsystem implements IProfileSubsystem, Runnable { private static final String PROP_CHECK_OWNER = "checkOwner"; private static final String PROP_ENABLE = "enable"; @@ -52,9 +58,13 @@ public class ProfileSubsystem implements IProfileSubsystem { private Vector mProfileIds; private Hashtable mProfiles; private Hashtable mProfileClassIds; + private String dn; private ILdapConnFactory dbFactory; + private boolean stopped = false; + private Thread monitor; + /** * Retrieves the name of this subsystem. */ @@ -86,9 +96,6 @@ public class ProfileSubsystem implements IProfileSubsystem { mProfiles = new Hashtable(); mProfileClassIds = new Hashtable(); - IPluginRegistry registry = (IPluginRegistry) - CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); - IConfigStore cs = CMS.getConfigStore(); IConfigStore dbCfg = cs.getSubStore("internaldb"); dbFactory = CMS.getLdapBoundConnFactory(); @@ -106,60 +113,47 @@ public class ProfileSubsystem implements IProfileSubsystem { // read profile id, implementation, and its configuration files String basedn = cs.getString("internaldb.basedn"); - String dn = "ou=certificateProfiles,ou=ca," + basedn; - LDAPConnection conn = dbFactory.getConn(); + dn = "ou=certificateProfiles,ou=ca," + basedn; - String[] attrs = {"cn", "classId"}; - try { - LDAPSearchResults ldapProfiles = conn.search( - dn, LDAPConnection.SCOPE_ONE, "(objectclass=*)", attrs, false); + monitor = new Thread(this, "profileChangeMonitor"); + monitor.start(); + } - while (ldapProfiles.hasMoreElements()) { - String id = ""; - try { - LDAPEntry ldapProfile = ldapProfiles.next(); + /** + * Read the given LDAPEntry into the profile subsystem. + */ + private void readProfile(LDAPEntry ldapProfile) { + IPluginRegistry registry = (IPluginRegistry) + CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); - id = (String) - ldapProfile.getAttribute("cn").getStringValues().nextElement(); + String profileId = (String) + ldapProfile.getAttribute("cn").getStringValues().nextElement(); - String classid = (String) - ldapProfile.getAttribute("classId").getStringValues().nextElement(); + String classId = (String) + ldapProfile.getAttribute("classId").getStringValues().nextElement(); - IPluginInfo info = registry.getPluginInfo("profile", classid); - if (info == null) { - CMS.debug("Error loading profile: No plugins for type : profile, with id " + classid); - } else { - CMS.debug("Start Profile Creation - " + id + " " + classid + " " + info.getClassName()); - createProfile(id, classid, info.getClassName()); - CMS.debug("Done Profile Creation - " + id); - } - } catch (LDAPException e) { - CMS.debug("Error reading profile '" + id + "'; skipping."); - } - } - } catch (LDAPException e) { - throw new EBaseException("Error reading profiles: " + e.toString()); - } finally { + IPluginInfo info = registry.getPluginInfo("profile", classId); + if (info == null) { + CMS.debug("Error loading profile: No plugins for type : profile, with classId " + classId); + } else { try { - dbFactory.returnConn(conn); - } catch (Exception e) { - throw new EProfileException("Error releasing the ldap connection" + e.toString()); + CMS.debug("Start Profile Creation - " + profileId + " " + classId + " " + info.getClassName()); + createProfile(profileId, classId, info.getClassName()); + CMS.debug("Done Profile Creation - " + profileId); + } catch (EProfileException e) { + CMS.debug("Error creating profile '" + profileId + "'; skipping."); } } - - Enumeration ee = getProfileIds(); - - while (ee.hasMoreElements()) { - String id = ee.nextElement(); - - CMS.debug("Registered Confirmation - " + id); - } } /** * Creates a profile instance. + * + * createProfile could theoretically be called simultaneously + * with the same profileId from Monitor and ProfileService, + * so the method is synchronized. */ - public IProfile createProfile(String id, String classid, String className) + public synchronized IProfile createProfile(String id, String classid, String className) throws EProfileException { try { String[] objectClasses = {"top", "certProfile"}; @@ -176,7 +170,8 @@ public class ProfileSubsystem implements IProfileSubsystem { IProfile profile = (IProfile) Class.forName(className).newInstance(); profile.setId(id); profile.init(this, subStoreConfig); - mProfileIds.addElement(id); + if (!mProfiles.containsKey(id)) + mProfileIds.addElement(id); mProfiles.put(id, profile); mProfileClassIds.put(id, classid); return profile; @@ -211,11 +206,41 @@ public class ProfileSubsystem implements IProfileSubsystem { } } + forgetProfile(id); + } + + private synchronized void handleMODDN(DN oldDN, LDAPEntry entry) { + DN profilesDN = new DN(dn); + + if (oldDN.isDescendantOf(profilesDN)) + forgetProfile(oldDN.explodeDN(true)[0]); + + if ((new DN(entry.getDN())).isDescendantOf(profilesDN)) + readProfile(entry); + } + + /** + * Forget a profile without deleting it from the database. + * + * This method is used when the profile change monitor receives + * notification that a profile was deleted. + */ + private void forgetProfile(String id) { mProfileIds.removeElement(id); mProfiles.remove(id); mProfileClassIds.remove(id); } + private void forgetProfile(LDAPEntry entry) { + String profileId = (String) + entry.getAttribute("cn").getStringValues().nextElement(); + if (profileId == null) { + CMS.debug("forgetProfile: error retrieving cn (profileId) from LDAPEntry"); + } else { + forgetProfile(profileId); + } + } + /** * Notifies this subsystem if owner is in running mode. */ @@ -229,6 +254,8 @@ public class ProfileSubsystem implements IProfileSubsystem { *

*/ public void shutdown() { + stopped = true; + monitor = null; mProfileIds.clear(); mProfiles.clear(); mProfileClassIds.clear(); @@ -346,6 +373,92 @@ public class ProfileSubsystem implements IProfileSubsystem { } catch (EBaseException e) { throw new EProfileException("CMS_PROFILE_DELETE_UNKNOWNPROFILE"); } - return "cn=" + id + ",ou=certificateProfiles,ou=ca," + basedn; + return "cn=" + id + "," + dn; + } + + public void run() { + int op = LDAPPersistSearchControl.ADD + | LDAPPersistSearchControl.MODIFY + | LDAPPersistSearchControl.DELETE + | LDAPPersistSearchControl.MODDN; + LDAPPersistSearchControl persistCtrl = + new LDAPPersistSearchControl(op, false, true, true); + + LDAPConnection conn; + + CMS.debug("Profile change monitor: starting."); + + while (!stopped) { + try { + conn = dbFactory.getConn(); + } catch (ELdapException e) { + CMS.debug("Profile change monitor: failed to get LDAPConnection. Retrying in 5 seconds."); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + continue; + } + try { + LDAPSearchConstraints cons = conn.getSearchConstraints(); + cons.setServerControls(persistCtrl); + cons.setBatchSize(1); + cons.setServerTimeLimit(0 /* seconds */); + LDAPSearchResults results = conn.search( + dn, LDAPConnection.SCOPE_ONE, "(objectclass=*)", + null, false, cons); + while (!stopped && results.hasMoreElements()) { + LDAPEntry entry = results.next(); + LDAPEntryChangeControl changeControl = null; + LDAPControl[] changeControls = results.getResponseControls(); + if (changeControls != null) { + for (LDAPControl control : changeControls) { + if (control instanceof LDAPEntryChangeControl) { + changeControl = (LDAPEntryChangeControl) control; + break; + } + } + } + CMS.debug("Profile change monitor: Processed change controls."); + if (changeControl != null) { + int changeType = changeControl.getChangeType(); + switch (changeType) { + case LDAPPersistSearchControl.ADD: + CMS.debug("Profile change monitor: ADD"); + readProfile(entry); + break; + case LDAPPersistSearchControl.DELETE: + CMS.debug("Profile change monitor: DELETE"); + forgetProfile(entry); + break; + case LDAPPersistSearchControl.MODIFY: + CMS.debug("Profile change monitor: MODIFY"); + readProfile(entry); + break; + case LDAPPersistSearchControl.MODDN: + CMS.debug("Profile change monitor: MODDN"); + handleMODDN(new DN(changeControl.getPreviousDN()), entry); + break; + default: + CMS.debug("Profile change monitor: unknown change type: " + changeType); + break; + } + } else { + CMS.debug("Profile change monitor: immediate result"); + readProfile(entry); + } + } + } catch (LDAPException e) { + CMS.debug("Profile change monitor: Caught exception: " + e.toString()); + } finally { + try { + dbFactory.returnConn(conn); + } catch (Exception e) { + CMS.debug("Profile change monitor: Error releasing the LDAPConnection" + e.toString()); + } + } + } + CMS.debug("Profile change monitor: stopping."); } } -- 1.9.3 From ftweedal at redhat.com Thu Nov 20 06:58:46 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Thu, 20 Nov 2014 16:58:46 +1000 Subject: [Pki-devel] [PATCH] 0016 - Fix BasicConstraints min/max path length check In-Reply-To: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> Message-ID: <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> Ping. Who wants to review this :) On Thu, Sep 25, 2014 at 04:07:56PM +1000, Fraser Tweedale wrote: > This patch fixes https://fedorahosted.org/pki/ticket/1035 > >From ee503f8fc08243906c130149f41dc8d77442d9c7 Mon Sep 17 00:00:00 2001 > From: Fraser Tweedale > Date: Thu, 25 Sep 2014 01:39:40 -0400 > Subject: [PATCH] Fix BasicConstraints min/max path length check > > The BasicConstraintsExtConstraint min/max path length validity check > ensures that the max length is greater than the min length, however, > when a negative value is used to represent "no max", the check > fails. > > Only compare the min and max length if the max length is > non-negative. > > Ticket #1035 > --- > .../netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java > index ca2668f7db305122f330fca058b27801820a75b4..8fbea435825194d7d31ecda5e65414f1081eeb01 100644 > --- a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java > +++ b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java > @@ -211,7 +211,7 @@ public class BasicConstraintsExtConstraint extends EnrollConstraint { > > int maxLen = getInt(value); > > - if (minLen >= maxLen) { > + if (maxLen >= 0 && minLen >= maxLen) { > CMS.debug("BasicConstraintExt: minPathLen >= maxPathLen!"); > > throw new EPropertyException("bad value"); > -- > 1.9.3 > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From edewata at redhat.com Thu Nov 20 16:06:52 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Thu, 20 Nov 2014 10:06:52 -0600 Subject: [Pki-devel] [PATCH] 0016 - Fix BasicConstraints min/max path length check In-Reply-To: <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> Message-ID: <546E119C.10906@redhat.com> On 11/20/2014 12:58 AM, Fraser Tweedale wrote: > Ping. Who wants to review this :) Looks short enough, I'll bite. :) The changes seem to be fine, so it's ACKed. There are some related issues/questions. Feel free to address them as part of this ticket, or maybe file a separate ticket. 1. The exception message is not very helpful. It probably should have said something like: Max path length cannot be smaller than min path length: 2. According to the ticket the ca-profile-add failed but the profile actually got added. Suppose there's a real constraint violation I think it should display the above exception message, and the profile should not be added. 3. Can we specify any negative value to indicate no min/max, or does it have to be -1? It should be consistent with the documentation. 4. The code seems to assume that the MinPathLen is already added before adding MaxPathLen. Suppose the MinPathLen is missing or added later will this constraint still be validated? Also, maybe we should keep track the list of unreviewed patches on the top of etherpad so we don't miss anything. -- Endi S. Dewata From mharmsen at redhat.com Fri Nov 21 05:53:29 2014 From: mharmsen at redhat.com (Matthew Harmsen) Date: Thu, 20 Nov 2014 22:53:29 -0700 Subject: [Pki-devel] [PATCH] Remove legacy multilib JNI_JAR_DIR logic Message-ID: <546ED359.20606@redhat.com> Please review the attached patch which addresses the following bug: * Bugzilla Bug #1165351 - Errata TPS test fails due to dependent packages not found At one point in time, there was a effort to try to support multilib JNI. This patch removes that legacy code and attempts to add support to allow a user the ability to override this variable. The patch has been successfully tested: * Upgrade * Verify (this used to fail due to the multilib code; now that the problematic code is not present, this test passes) * Downgrade NOTE: Obviously, if 'Verify' is run on the 'pki-base' package after Downgrade, the verification will most likely fail since the downgraded package will probably still contain the problematic 'multilib' code. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pki-core-10.1.2-bz1165351.patch Type: text/x-patch Size: 4368 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: spec.patch Type: text/x-patch Size: 1450 bytes Desc: not available URL: From ftweedal at redhat.com Fri Nov 21 06:40:13 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Fri, 21 Nov 2014 16:40:13 +1000 Subject: [Pki-devel] [PATCH] 0016 - Fix BasicConstraints min/max path length check In-Reply-To: <546E119C.10906@redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> <546E119C.10906@redhat.com> Message-ID: <20141121064013.GM8412@dhcp-40-8.bne.redhat.com> On Thu, Nov 20, 2014 at 10:06:52AM -0600, Endi Sukma Dewata wrote: > On 11/20/2014 12:58 AM, Fraser Tweedale wrote: > >Ping. Who wants to review this :) > > Looks short enough, I'll bite. :) > > The changes seem to be fine, so it's ACKed. > Thanks Endi; I'll reroll the patch to address those points. > There are some related issues/questions. Feel free to address them as part > of this ticket, or maybe file a separate ticket. > > 1. The exception message is not very helpful. It probably should have said > something like: > > Max path length cannot be smaller than min path length: > Yep, I'll do that. > 2. According to the ticket the ca-profile-add failed but the profile > actually got added. Suppose there's a real constraint violation I think it > should display the above exception message, and the profile should not be > added. > > 3. Can we specify any negative value to indicate no min/max, or does it have > to be -1? It should be consistent with the documentation. > Any negative value is considered "no min/max". AFAICT this is consistent with the documentation. If I'm overlooking something please point it out. > 4. The code seems to assume that the MinPathLen is already added before > adding MaxPathLen. Suppose the MinPathLen is missing or added later will > this constraint still be validated? > Good catch. I'll perform the validation each time setConfig is called. > Also, maybe we should keep track the list of unreviewed patches on the top > of etherpad so we don't miss anything. > +1 ; any objections? > -- > Endi S. Dewata From alee at redhat.com Fri Nov 21 14:41:12 2014 From: alee at redhat.com (Ade Lee) Date: Fri, 21 Nov 2014 09:41:12 -0500 Subject: [Pki-devel] [PATCH] Remove legacy multilib JNI_JAR_DIR logic In-Reply-To: <546ED359.20606@redhat.com> References: <546ED359.20606@redhat.com> Message-ID: <1416580872.8797.2.camel@aleeredhat.laptop> ACK On Thu, 2014-11-20 at 22:53 -0700, Matthew Harmsen wrote: > Please review the attached patch which addresses the following bug: > * Bugzilla Bug #1165351 - Errata TPS test fails due to dependent > packages not found > > At one point in time, there was a effort to try to support multilib > JNI. This patch removes that legacy code and attempts to add support > to allow a user the ability to override this variable. > > > The patch has been successfully tested: > > > * Upgrade > * Verify (this used to fail due to the multilib code; now that > the problematic code is not present, this test passes) > * Downgrade > > NOTE: Obviously, if 'Verify' is run on the 'pki-base' package after > Downgrade, the verification will most likely fail > since the downgraded package will probably still contain > the problematic 'multilib' code. > > > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From edewata at redhat.com Fri Nov 21 16:49:46 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Fri, 21 Nov 2014 10:49:46 -0600 Subject: [Pki-devel] [PATCH] 531 Moved web application deployment locations. In-Reply-To: <5450FAB5.1070709@redhat.com> References: <544AD6DE.9030909@redhat.com> <544FA33B.5060003@redhat.com> <20141029073605.GW21514@dhcp-40-8.bne.redhat.com> <5450FAB5.1070709@redhat.com> Message-ID: <546F6D2A.9030200@redhat.com> On 10/29/2014 9:33 AM, Endi Sukma Dewata wrote: > On 10/29/2014 2:36 AM, Fraser Tweedale wrote: >> ACK. >> >> Upgrading existing instance and spawning new instance worked and >> layout was as expected. > > Thanks. Per discussion with alee, we're going to wait until the > following ticket is implemented, at least partially: > https://fedorahosted.org/pki/ticket/1129 > This is needed to support upgrading from a prerelease build (e.g. > 10.2.1-0.1) to the final build (e.g. 10.2.1-1). > > It looks like there's a way to implement it without too much work. Right > now the upgrade folders refer to the version to upgraded from. We're > going to change that to the version we're going to upgrade to. The current upgrade framework actually can upgrade between release numbers without changing the version number. This patch has been revised to change the version/release number to 10.2.1-0.2. I've verified the upgrade from 10.2.0-3 to 10.2.1-0.2 and from 10.2.1-0.1 to 10.2.1-0.2. -- Endi S. Dewata -------------- next part -------------- >From 4fdae39e79659dde9d02bd53c00f945aefa16547 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sun, 12 Oct 2014 00:16:55 -0400 Subject: [PATCH] Moved web application deployment locations. Currently web applications are deployed into Host's appBase (i.e. /webapps). To allow better control of individual subsystem deployments, the web applications have to be moved out of the appBase so that the autoDeploy can work properly later. This patch moves the common web applications to / common/webapps and subsystem web applications to / /webapps. An upgrade script has been added to update existing deployments. The version number has been updated to to 10.2.1-0.2. https://fedorahosted.org/pki/ticket/1183 --- base/common/upgrade/10.2.1/.gitignore | 4 + base/server/etc/default.cfg | 5 +- .../python/pki/server/deployment/pkihelper.py | 35 ++++++ .../deployment/scriptlets/instance_layout.py | 32 +++++- .../deployment/scriptlets/subsystem_layout.py | 6 -- .../deployment/scriptlets/webapp_deployment.py | 49 +++++---- base/server/scripts/operations | 25 ++--- .../01-MoveWebApplicationDeploymentLocations | 119 +++++++++++++++++++++ specs/pki-core.spec | 5 +- 9 files changed, 227 insertions(+), 53 deletions(-) create mode 100644 base/common/upgrade/10.2.1/.gitignore create mode 100755 base/server/upgrade/10.2.1/01-MoveWebApplicationDeploymentLocations diff --git a/base/common/upgrade/10.2.1/.gitignore b/base/common/upgrade/10.2.1/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5e7d2734cfc60289debf74293817c0a8f572ff32 --- /dev/null +++ b/base/common/upgrade/10.2.1/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index ecf436d9f15729ed27e09975ab1f1151e504fe94..98a3628572e78f71525a95cedd0e473be8a14d9d 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -217,6 +217,7 @@ pki_tomcat_common_path=%(pki_instance_path)s/common pki_tomcat_common_lib_path=%(pki_tomcat_common_path)s/lib pki_tomcat_tmpdir_path=%(pki_instance_path)s/temp pki_tomcat_webapps_path=%(pki_instance_path)s/webapps +pki_tomcat_common_webapps_path=%(pki_instance_path)s/common/webapps pki_tomcat_work_path=%(pki_instance_path)s/work pki_tomcat_work_catalina_path=%(pki_tomcat_work_path)s/Catalina pki_tomcat_work_catalina_host_path=%(pki_tomcat_work_catalina_path)s/localhost @@ -231,8 +232,8 @@ pki_instance_lib=%(pki_instance_path)s/lib pki_instance_lib_log4j_properties=%(pki_instance_lib)s/log4j.properties pki_instance_systemd_link=%(pki_instance_path)s/%(pki_instance_name)s pki_subsystem_signed_audit_log_path=%(pki_subsystem_log_path)s/signedAudit -pki_subsystem_tomcat_webapps_link=%(pki_subsystem_path)s/webapps -pki_tomcat_webapps_subsystem_path=%(pki_tomcat_webapps_path)s/%(pki_subsystem_type)s +pki_tomcat_subsystem_webapps_path=%(pki_subsystem_path)s/webapps +pki_tomcat_webapps_subsystem_path=%(pki_tomcat_subsystem_webapps_path)s/%(pki_subsystem_type)s pki_tomcat_webapps_subsystem_webinf_classes_path=%(pki_tomcat_webapps_subsystem_path)s/WEB-INF/classes pki_tomcat_webapps_subsystem_webinf_lib_path=%(pki_tomcat_webapps_subsystem_path)s/WEB-INF/lib pki_certsrv_jar_link=%(pki_tomcat_webapps_subsystem_webinf_lib_path)s/pki-certsrv.jar diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py index 96048bdecafe404225ceedf3c17f6c262f64d093..4fbc4a352a247e2e84c456a24dfc7f79afb9cb46 100644 --- a/base/server/python/pki/server/deployment/pkihelper.py +++ b/base/server/python/pki/server/deployment/pkihelper.py @@ -40,6 +40,7 @@ from grp import getgrnam from pwd import getpwnam from pwd import getpwuid import xml.etree.ElementTree as ET +from lxml import etree import zipfile import selinux if selinux.is_selinux_enabled(): @@ -4171,4 +4172,38 @@ class PKIDeployer: self.tps_connector = TPSConnector(self) self.config_client = ConfigClient(self) + def deploy_webapp(self, name, doc_base, descriptor): + """ + Deploy a web application into a Tomcat instance. + This method will copy the specified deployment descriptor into + /conf/Catalina/localhost/.xml and point the docBase + to the specified location. The web application will become available + under "/" URL path. + + See also: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html + + :param name: Web application name. + :type name: str + :param doc_base: Path to web application content. + :type doc_base: str + :param descriptor: Path to deployment descriptor (context.xml). + :type descriptor: str + """ + new_descriptor = os.path.join( + self.mdict['pki_instance_configuration_path'], + "Catalina", + "localhost", + name + ".xml") + + parser = etree.XMLParser(remove_blank_text=True) + document = etree.parse(descriptor, parser) + + context = document.getroot() + context.set('docBase', doc_base) + + with open(new_descriptor, 'w') as f: + f.write(etree.tostring(document, pretty_print=True)) + + os.chown(new_descriptor, self.mdict['pki_uid'], self.mdict['pki_gid']) + os.chmod(new_descriptor, config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS) diff --git a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py index 9cdecb4f29ccccfe7afdfe11a649e1c2d72a69ba..5079891f92ac498f078cd401f34287fab03e0fd6 100644 --- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py +++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py @@ -55,6 +55,30 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_source_server_path'], deployer.mdict['pki_instance_configuration_path']) + # Deploy ROOT web application + deployer.deploy_webapp( + "ROOT", + os.path.join( + deployer.mdict['pki_tomcat_common_webapps_path'], + "ROOT"), + os.path.join( + deployer.mdict['pki_source_server_path'], + "Catalina", + "localhost", + "ROOT.xml")) + + # Deploy pki web application + deployer.deploy_webapp( + "pki", + os.path.join( + deployer.mdict['pki_tomcat_common_webapps_path'], + "pki"), + os.path.join( + deployer.mdict['pki_source_server_path'], + "Catalina", + "localhost", + "pki.xml")) + # establish Tomcat instance base deployer.directory.create(deployer.mdict['pki_tomcat_common_path']) deployer.directory.create( @@ -74,23 +98,23 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_instance_lib_log4j_properties']) deployer.directory.create(deployer.mdict['pki_tomcat_tmpdir_path']) - # Copy /usr/share/pki/server/webapps to /webapps + # Copy /usr/share/pki/server/webapps to /common/webapps deployer.directory.copy( os.path.join( config.PKI_DEPLOYMENT_SOURCE_ROOT, "server", "webapps"), - deployer.mdict['pki_tomcat_webapps_path']) + deployer.mdict['pki_tomcat_common_webapps_path']) # If desired and available, # copy selected server theme - # to /webapps/pki + # to /common/webapps/pki if config.str2bool(deployer.mdict['pki_theme_enable']) and\ os.path.exists(deployer.mdict['pki_theme_server_dir']): deployer.directory.copy( deployer.mdict['pki_theme_server_dir'], os.path.join( - deployer.mdict['pki_tomcat_webapps_path'], + deployer.mdict['pki_tomcat_common_webapps_path'], "pki"), overwrite_flag=True) diff --git a/base/server/python/pki/server/deployment/scriptlets/subsystem_layout.py b/base/server/python/pki/server/deployment/scriptlets/subsystem_layout.py index 324accad0d6a9230ac15cebd2c67b0eeb1ec756b..c3d06c0796a00d6c5973780706bde7e9e2838bf3 100644 --- a/base/server/python/pki/server/deployment/scriptlets/subsystem_layout.py +++ b/base/server/python/pki/server/deployment/scriptlets/subsystem_layout.py @@ -103,12 +103,6 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.file.copy( deployer.mdict['pki_source_transportcert_profile'], deployer.mdict['pki_target_transportcert_profile']) - # establish instance-based Tomcat PKI subsystem registry - # establish instance-based Tomcat PKI subsystem convenience - # symbolic links - deployer.symlink.create( - deployer.mdict['pki_tomcat_webapps_path'], - deployer.mdict['pki_subsystem_tomcat_webapps_link']) # establish instance-based subsystem convenience symbolic links deployer.symlink.create( deployer.mdict['pki_instance_database_link'], diff --git a/base/server/python/pki/server/deployment/scriptlets/webapp_deployment.py b/base/server/python/pki/server/deployment/scriptlets/webapp_deployment.py index 962de724fcfc034ce0fb389a056928102122679e..dce327ff871f58fb5a954fe76c7ded31867c2af3 100644 --- a/base/server/python/pki/server/deployment/scriptlets/webapp_deployment.py +++ b/base/server/python/pki/server/deployment/scriptlets/webapp_deployment.py @@ -44,29 +44,38 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): config.pki_log.info(log.WEBAPP_DEPLOYMENT_SPAWN_1, __name__, extra=config.PKI_INDENTATION_LEVEL_1) + # Create subsystem webapps folder to store custom webapps: + # //webapps. + deployer.directory.create( + deployer.mdict['pki_tomcat_subsystem_webapps_path']) + + # set ownerships, permissions, and acls + deployer.directory.set_mode( + deployer.mdict['pki_tomcat_subsystem_webapps_path']) + # For TPS, deploy web application directly from /usr/share/pki. if deployer.mdict['pki_subsystem'] == "TPS": - deployer.file.copy( + deployer.deploy_webapp( + "tps", + os.path.join( + config.PKI_DEPLOYMENT_SOURCE_ROOT, + "tps", + "webapps", + "tps"), os.path.join( config.PKI_DEPLOYMENT_SOURCE_ROOT, "tps", "conf", "Catalina", "localhost", - "tps.xml"), - os.path.join( - deployer.mdict['pki_instance_configuration_path'], - "Catalina", - "localhost", "tps.xml")) + return self.rv - # For other subsystems, deploy web application into Tomcat instance. - deployer.directory.create( - deployer.mdict['pki_tomcat_webapps_subsystem_path']) + # For other subsystems, deploy as custom web application. # Copy /usr/share/pki//webapps/ - # to /webapps/ + # to //webapps/ deployer.directory.copy( os.path.join( config.PKI_DEPLOYMENT_SOURCE_ROOT, @@ -77,7 +86,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): overwrite_flag=True) # Copy /usr/share/pki/server/webapps/pki/admin - # to /webapps//admin + # to //webapps//admin # TODO: common templates should be deployed in common webapp deployer.directory.copy( os.path.join( @@ -131,26 +140,16 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_tks_jar'], deployer.mdict['pki_tks_jar_link']) - # set ownerships, permissions, and acls - deployer.directory.set_mode( - deployer.mdict['pki_tomcat_webapps_subsystem_path']) - - # Copy web application context file - # from /usr/share/pki//conf/Catalina/localhost/ - # .xml - # to /conf/Catalina/localhost/.xml - deployer.file.copy( + # Deploy subsystem web application. + deployer.deploy_webapp( + deployer.mdict['pki_subsystem'].lower(), + deployer.mdict['pki_tomcat_webapps_subsystem_path'], os.path.join( config.PKI_DEPLOYMENT_SOURCE_ROOT, deployer.mdict['pki_subsystem'].lower(), "conf", "Catalina", "localhost", - deployer.mdict['pki_subsystem'].lower() + ".xml"), - os.path.join( - deployer.mdict['pki_instance_configuration_path'], - "Catalina", - "localhost", deployer.mdict['pki_subsystem'].lower() + ".xml")) return self.rv diff --git a/base/server/scripts/operations b/base/server/scripts/operations index 37094c037f4a76cfc414a421839c018fdbc4571f..3cd313c6275d69df5ed95f6b5e2e1c2fd9db19e9 100644 --- a/base/server/scripts/operations +++ b/base/server/scripts/operations @@ -1108,11 +1108,11 @@ verify_symlinks() pki_registry_dir="/etc/sysconfig/pki/${PKI_WEB_SERVER_TYPE}/${PKI_INSTANCE_NAME}" pki_systemd_dir="/etc/systemd/system/pki-tomcatd.target.wants" pki_systemd_link="pki-${PKI_WEB_SERVER_TYPE}d@${PKI_INSTANCE_NAME}.service" - pki_ca_jar_dir="${PKI_INSTANCE_PATH}/webapps/ca/WEB-INF/lib" - pki_kra_jar_dir="${PKI_INSTANCE_PATH}/webapps/kra/WEB-INF/lib" - pki_ocsp_jar_dir="${PKI_INSTANCE_PATH}/webapps/ocsp/WEB-INF/lib" - pki_tks_jar_dir="${PKI_INSTANCE_PATH}/webapps/tks/WEB-INF/lib" - pki_tps_jar_dir="${PKI_INSTANCE_PATH}/webapps/tps/WEB-INF/lib" + pki_ca_jar_dir="${PKI_INSTANCE_PATH}/ca/webapps/ca/WEB-INF/lib" + pki_kra_jar_dir="${PKI_INSTANCE_PATH}/kra/webapps/kra/WEB-INF/lib" + pki_ocsp_jar_dir="${PKI_INSTANCE_PATH}/ocsp/webapps/ocsp/WEB-INF/lib" + pki_tks_jar_dir="${PKI_INSTANCE_PATH}/tks/webapps/tks/WEB-INF/lib" + pki_tps_jar_dir="${PKI_INSTANCE_PATH}/tps/webapps/tps/WEB-INF/lib" # '${PKI_INSTANCE_PATH}' symlinks base_symlinks=( @@ -1126,8 +1126,7 @@ verify_symlinks() [alias]=${PKI_INSTANCE_PATH}/alias [conf]=/etc/pki/${PKI_INSTANCE_NAME}/ca [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/ca - [registry]=${pki_registry_dir} - [webapps]=${PKI_INSTANCE_PATH}/webapps) + [registry]=${pki_registry_dir}) # '${pki_ca_jar_dir}' symlinks ca_jar_symlinks=( @@ -1144,8 +1143,7 @@ verify_symlinks() [alias]=${PKI_INSTANCE_PATH}/alias [conf]=/etc/pki/${PKI_INSTANCE_NAME}/kra [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/kra - [registry]=${pki_registry_dir} - [webapps]=${PKI_INSTANCE_PATH}/webapps) + [registry]=${pki_registry_dir}) # '${pki_kra_jar_dir}' symlinks kra_jar_symlinks=( @@ -1162,8 +1160,7 @@ verify_symlinks() [alias]=${PKI_INSTANCE_PATH}/alias [conf]=/etc/pki/${PKI_INSTANCE_NAME}/ocsp [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/ocsp - [registry]=${pki_registry_dir} - [webapps]=${PKI_INSTANCE_PATH}/webapps) + [registry]=${pki_registry_dir}) # '${pki_ocsp_jar_dir}' symlinks ocsp_jar_symlinks=( @@ -1180,8 +1177,7 @@ verify_symlinks() [alias]=${PKI_INSTANCE_PATH}/alias [conf]=/etc/pki/${PKI_INSTANCE_NAME}/tks [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/tks - [registry]=${pki_registry_dir} - [webapps]=${PKI_INSTANCE_PATH}/webapps) + [registry]=${pki_registry_dir}) # '${pki_tks_jar_dir}' symlinks tks_jar_symlinks=( @@ -1198,8 +1194,7 @@ verify_symlinks() [alias]=${PKI_INSTANCE_PATH}/alias [conf]=/etc/pki/${PKI_INSTANCE_NAME}/tps [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/tps - [registry]=${pki_registry_dir} - [webapps]=${PKI_INSTANCE_PATH}/webapps) + [registry]=${pki_registry_dir}) # '${pki_tps_jar_dir}' symlinks tps_jar_symlinks=( diff --git a/base/server/upgrade/10.2.1/01-MoveWebApplicationDeploymentLocations b/base/server/upgrade/10.2.1/01-MoveWebApplicationDeploymentLocations new file mode 100755 index 0000000000000000000000000000000000000000..20f35e837d2dbce7bfee01187b9763d4ff592d40 --- /dev/null +++ b/base/server/upgrade/10.2.1/01-MoveWebApplicationDeploymentLocations @@ -0,0 +1,119 @@ +#!/usr/bin/python +# Authors: +# Endi S. Dewata +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2014 Red Hat, Inc. +# All rights reserved. +# + +import grp +import os +import pwd +import shutil +import signal +import sys +from lxml import etree + +import pki +import pki.server.upgrade + + +class MoveWebApplicationDeploymentLocations(pki.server.upgrade.PKIServerUpgradeScriptlet): + + def __init__(self): + + self.message = 'Move Web application deployment locations' + + self.parser = etree.XMLParser(remove_blank_text=True) + + def upgrade_subsystem(self, instance, subsystem): + + subsystem_webapps = os.path.join(instance.base_dir, subsystem.name, 'webapps') + self.backup(subsystem_webapps) + + # remove old subsystem webapps symlink + if os.path.islink(subsystem_webapps): + os.unlink(subsystem_webapps) + + # create new subsytem webapps folder + if not os.path.exists(subsystem_webapps): + os.mkdir(subsystem_webapps) + + uid = pwd.getpwnam('pkiuser').pw_uid + gid = grp.getgrnam('pkiuser').gr_gid + + os.chown(subsystem_webapps, uid, gid) + os.chmod(subsystem_webapps, 0770) + + # move subsystem webapp + subsystem_old_webapp = os.path.join(instance.base_dir, 'webapps', subsystem.name) + subsystem_new_webapp = os.path.join(subsystem_webapps, subsystem.name) + subsystem_context_xml = os.path.join(instance.conf_dir, 'Catalina', 'localhost', subsystem.name + '.xml') + + self.move_webapp(subsystem_old_webapp, subsystem_new_webapp, subsystem_context_xml) + + def upgrade_instance(self, instance): + + common_webapps = os.path.join(instance.base_dir, 'common', 'webapps') + self.backup(common_webapps) + + # create new common webapps folder + if not os.path.exists(common_webapps): + os.mkdir(common_webapps) + + uid = pwd.getpwnam('pkiuser').pw_uid + gid = grp.getgrnam('pkiuser').gr_gid + + os.chown(common_webapps, uid, gid) + os.chmod(common_webapps, 0770) + + # move ROOT webapp + root_old_webapp = os.path.join(instance.base_dir, 'webapps', 'ROOT') + root_new_webapp = os.path.join(common_webapps, 'ROOT') + root_context_xml = os.path.join(instance.conf_dir, 'Catalina', 'localhost', 'ROOT.xml') + + self.move_webapp(root_old_webapp, root_new_webapp, root_context_xml) + + # move pki webapp + pki_old_webapp = os.path.join(instance.base_dir, 'webapps', 'pki') + pki_new_webapp = os.path.join(common_webapps, 'pki') + pki_context_xml = os.path.join(instance.conf_dir, 'Catalina', 'localhost', 'pki.xml') + + self.move_webapp(pki_old_webapp, pki_new_webapp, pki_context_xml) + + def move_webapp(self, old_webapp, new_webapp, context_xml): + + if not os.path.exists(old_webapp): + return + + # move old webapp to the new webapp + self.backup(old_webapp) + self.backup(new_webapp) + + shutil.move(old_webapp, new_webapp) + + # update docBase in context.xml + self.backup(context_xml) + + document = etree.parse(context_xml, self.parser) + + context = document.getroot() + doc_base = context.get('docBase') + + context.set('docBase', new_webapp) + + with open(context_xml, 'w') as f: + f.write(etree.tostring(document, pretty_print=True)) diff --git a/specs/pki-core.spec b/specs/pki-core.spec index 3c1cd7f803286c96b910e567471a1dc9841903f7..b02dc2e6cc3de16c239b311509120c7b234cc902 100644 --- a/specs/pki-core.spec +++ b/specs/pki-core.spec @@ -5,7 +5,7 @@ distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} Name: pki-core Version: 10.2.1 -Release: 0.1%{?dist} +Release: 0.2%{?dist} Summary: Certificate System - PKI Core Components URL: http://pki.fedoraproject.org/ License: GPLv2 @@ -854,6 +854,9 @@ echo >> /var/log/pki/pki-server-upgrade-%{version}.log 2>&1 %endif # %{with server} %changelog +* Thu Nov 20 2014 Dogtag Team 10.2.1-0.2 +- Moved web application deployment locations. + * Fri Oct 24 2014 Dogtag Team 10.2.1-0.1 - Updated version number to 10.2.1-0.1. - Added CLIs to simplify generating user certificates -- 1.8.4.2 From edewata at redhat.com Fri Nov 21 21:52:40 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Fri, 21 Nov 2014 15:52:40 -0600 Subject: [Pki-devel] [PATCH] 535 Removed profile input/output IDs from CLI output. Message-ID: <546FB428.2070108@redhat.com> The current profile inputs/outputs do not have meaningful IDs (e.g. i1, i2, o1) and are not used by the client so they should not be displayed in the CLI output. In the future the IDs should be renamed into something meaningful (e.g. keygen, sn, cert) and the inputs/outputs should be retrieved by ID. New methods have been added to retrieve by ID. https://fedorahosted.org/pki/ticket/1147 -- Endi S. Dewata -------------- next part -------------- From 98a336c6fc89c5205b3324447800fc2ea326408f Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 21 Nov 2014 16:16:33 -0500 Subject: [PATCH] Removed profile input/output IDs from CLI output. The current profile inputs/outputs do not have meaningful IDs (e.g. i1, i2, o1) and are not used by the client so they should not be displayed in the CLI output. In the future the IDs should be renamed into something meaningful (e.g. keygen, sn, cert) and the inputs/outputs should be retrieved by ID. New methods have been added to retrieve by ID. https://fedorahosted.org/pki/ticket/1147 --- .../certsrv/cert/CertEnrollmentRequest.java | 26 ++++++++++++++++++++++ .../com/netscape/cmstools/profile/ProfileCLI.java | 4 ---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/base/common/src/com/netscape/certsrv/cert/CertEnrollmentRequest.java b/base/common/src/com/netscape/certsrv/cert/CertEnrollmentRequest.java index 732efae0a638a2abd4a330881abef5c2c3b1655d..72aad330fecc63290c9e6d82e576971df499028e 100644 --- a/base/common/src/com/netscape/certsrv/cert/CertEnrollmentRequest.java +++ b/base/common/src/com/netscape/certsrv/cert/CertEnrollmentRequest.java @@ -144,7 +144,12 @@ public class CertEnrollmentRequest { return newInput; } + // TODO: deprecate this method in 10.3 public ProfileInput getInput(String name) { + return getInputByName(name); + } + + public ProfileInput getInputByName(String name) { for (ProfileInput input : inputs) { if (input.getName().equals(name)) return input; @@ -152,6 +157,14 @@ public class CertEnrollmentRequest { return null; } + public ProfileInput getInputByID(String id) { + for (ProfileInput input : inputs) { + if (input.getId().equals(id)) + return input; + } + return null; + } + public void addOutput(ProfileOutput output) { ProfileOutput curOutput = getOutput(output.getName()); if (curOutput != null) { @@ -167,7 +180,12 @@ public class CertEnrollmentRequest { } } + // TODO: deprecate this method in 10.3 public ProfileOutput getOutput(String name) { + return getOutputByName(name); + } + + public ProfileOutput getOutputByName(String name) { for (ProfileOutput output : outputs) { if (output.getName().equals(name)) return output; @@ -175,6 +193,14 @@ public class CertEnrollmentRequest { return null; } + public ProfileOutput getOutputByID(String id) { + for (ProfileOutput output : outputs) { + if (output.getId().equals(id)) + return output; + } + return null; + } + public HashMap toParams() { HashMap ret = new HashMap(); ret.put("isRenewal", Boolean.valueOf(renewal).toString()); diff --git a/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java b/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java index 732b597afd1dbb5b9440d451f34b2f39e20fb904..6d8c6b655ced3807ac01364fe304b422986de567 100644 --- a/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/profile/ProfileCLI.java @@ -79,7 +79,6 @@ public class ProfileCLI extends CLI { for (ProfileInput input: data.getInputs()) { System.out.println(); - System.out.println(" Input ID: " + input.getId()); System.out.println(" Name: " + input.getName()); System.out.println(" Class: " + input.getClassId()); for (ProfileAttribute attr : input.getAttributes()) { @@ -94,7 +93,6 @@ public class ProfileCLI extends CLI { for (ProfileOutput output: data.getOutputs()) { System.out.println(); - System.out.println(" Output ID: " + output.getId()); System.out.println(" Name: " + output.getName()); System.out.println(" Class: " + output.getClassId()); for (ProfileAttribute attr: output.getAttrs()) { @@ -148,7 +146,6 @@ public class ProfileCLI extends CLI { for (ProfileInput input: request.getInputs()) { System.out.println(); - System.out.println(" Input ID: " + input.getId()); System.out.println(" Name: " + input.getName()); System.out.println(" Class: " + input.getClassId()); for (ProfileAttribute attr : input.getAttributes()) { @@ -163,7 +160,6 @@ public class ProfileCLI extends CLI { for (ProfileOutput output: request.getOutputs()) { System.out.println(); - System.out.println(" Output ID: " + output.getId()); System.out.println(" Name: " + output.getName()); System.out.println(" Class: " + output.getClassId()); for (ProfileAttribute attr: output.getAttrs()) { -- 1.8.4.2 From edewata at redhat.com Fri Nov 21 23:58:24 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Fri, 21 Nov 2014 17:58:24 -0600 Subject: [Pki-devel] [PATCH] 536 Improvements for KeyClient.archive_encrypted_data(). Message-ID: <546FD1A0.4070503@redhat.com> The archive_encrypted_data() in KeyClient has been modified to have a default value for the algorithm OID and to take a nonce IV object instead of the base-64 encoded value. https://fedorahosted.org/pki/ticket/1155 https://fedorahosted.org/pki/ticket/1156 The drmtest.py works just fine. -- Endi S. Dewata -------------- next part -------------- From 8b30df22fba68b6c6257d480e8f303fc60ba5d7f Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 21 Nov 2014 18:45:08 -0500 Subject: [PATCH] Improvements for KeyClient.archive_encrypted_data(). The archive_encrypted_data() in KeyClient has been modified to have a default value for the algorithm OID and to take a nonce IV object instead of the base-64 encoded value. https://fedorahosted.org/pki/ticket/1155 https://fedorahosted.org/pki/ticket/1156 --- base/common/python/pki/key.py | 67 ++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py index 1f449955b35b8fffe241669c54745ed9d3286729..bcc56747e48b21c8d666a25fa7023a70ca76f90d 100644 --- a/base/common/python/pki/key.py +++ b/base/common/python/pki/key.py @@ -686,28 +686,34 @@ class KeyClient(object): nonce_iv = self.crypto.generate_nonce_iv() session_key = self.crypto.generate_session_key() - trans_wrapped_session_key = \ - self.crypto.asymmetric_wrap(session_key, self.transport_cert) - wrapped_private_data = self.crypto.symmetric_wrap(private_data, - session_key, - nonce_iv=nonce_iv) - algorithm_oid = self.DES_EDE3_CBC_OID - symkey_params = base64.encodestring(nonce_iv) + wrapped_session_key = self.crypto.asymmetric_wrap(session_key, self.transport_cert) - return self.archive_encrypted_data(client_key_id, data_type, - wrapped_private_data, - trans_wrapped_session_key, - algorithm_oid, - symkey_params, - key_algorithm=key_algorithm, - key_size=key_size) + encrypted_data = self.crypto.symmetric_wrap( + private_data, + session_key, + nonce_iv=nonce_iv) + + return self.archive_encrypted_data( + client_key_id, + data_type, + encrypted_data, + wrapped_session_key, + algorithm_oid=None, + nonce_iv=nonce_iv, + key_algorithm=key_algorithm, + key_size=key_size) @pki.handle_exceptions() - def archive_encrypted_data(self, client_key_id, data_type, - encrypted_data, trans_wrapped_session_key, - algorithm_oid, symkey_params, - key_algorithm=None, key_size=None): + def archive_encrypted_data(self, + client_key_id, + data_type, + encrypted_data, + wrapped_session_key, + algorithm_oid=None, + nonce_iv=None, + key_algorithm=None, + key_size=None): """ Archive a secret (symmetric key or passphrase) on the DRM. @@ -715,12 +721,12 @@ class KeyClient(object): data_type, key_algorithm and key_size. The following parameters are also required: - - wrapped_private_data - which is the secret wrapped by a + - encrypted_data - which is the data encrypted by a session key (168 bit 3DES symmetric key) - - trans_wrapped_session_key - the above session key wrapped by + - wrapped_session_key - the above session key wrapped by the DRM transport certificate public key. - the algorithm_oid string for the symmetric key wrap - - the symkey_params for the symmetric key wrap + - the nonce_iv for the symmetric key wrap This function is useful if the caller wants to do their own wrapping of the secret, or if the secret was generated on a separate client @@ -739,13 +745,22 @@ class KeyClient(object): "For symmetric keys, key algorithm and key size " "must be specified") - if (encrypted_data is None) or (trans_wrapped_session_key is None) or \ - (algorithm_oid is None) or (symkey_params is None): - raise TypeError( - "All data and wrapping parameters must be specified") + if not encrypted_data: + raise TypeError('Missing encrypted data') + + if not wrapped_session_key: + raise TypeError('Missing wrapped session key') + + if not algorithm_oid: + algorithm_oid = KeyClient.DES_EDE3_CBC_OID + + if not nonce_iv: + raise TypeError('Missing nonce IV') - twsk = base64.encodestring(trans_wrapped_session_key) data = base64.encodestring(encrypted_data) + twsk = base64.encodestring(wrapped_session_key) + symkey_params = base64.encodestring(nonce_iv) + request = KeyArchivalRequest(client_key_id=client_key_id, data_type=data_type, wrapped_private_data=data, -- 1.8.4.2 From cfu at redhat.com Sat Nov 22 02:06:34 2014 From: cfu at redhat.com (Christina Fu) Date: Fri, 21 Nov 2014 18:06:34 -0800 Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch Message-ID: <546FEFAA.7000604@redhat.com> attached is the java console part of client side changes for TLS range support: https://fedorahosted.org/pki/ticket/1206 TLS range support: code change needed for cs when acting as client Please review. thanks, Christina -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Ticket-1206-java-console-TLS-range-support-code-chan.patch Type: text/x-patch Size: 3861 bytes Desc: not available URL: From ftweedal at redhat.com Mon Nov 24 06:03:25 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Mon, 24 Nov 2014 16:03:25 +1000 Subject: [Pki-devel] [PATCH] 0016-2 - Fix BasicConstraints min/max path length check In-Reply-To: <546E119C.10906@redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> <546E119C.10906@redhat.com> Message-ID: <20141124060324.GO8412@dhcp-40-8.bne.redhat.com> New patch attached. Re point 1., despite the improved ``EPropertyException`` thrown in the new code, the error reported by the CLI is ``PKIException: Error changing profile data``. I filed https://fedorahosted.org/pki/ticket/1212 to address this later. 3. is not an issue and 2. and 4. have been addressed in the new patch. On Thu, Nov 20, 2014 at 10:06:52AM -0600, Endi Sukma Dewata wrote: > On 11/20/2014 12:58 AM, Fraser Tweedale wrote: > >Ping. Who wants to review this :) > > Looks short enough, I'll bite. :) > > The changes seem to be fine, so it's ACKed. > > There are some related issues/questions. Feel free to address them as part > of this ticket, or maybe file a separate ticket. > > 1. The exception message is not very helpful. It probably should have said > something like: > > Max path length cannot be smaller than min path length: > > 2. According to the ticket the ca-profile-add failed but the profile > actually got added. Suppose there's a real constraint violation I think it > should display the above exception message, and the profile should not be > added. > > 3. Can we specify any negative value to indicate no min/max, or does it have > to be -1? It should be consistent with the documentation. > > 4. The code seems to assume that the MinPathLen is already added before > adding MaxPathLen. Suppose the MinPathLen is missing or added later will > this constraint still be validated? > > Also, maybe we should keep track the list of unreviewed patches on the top > of etherpad so we don't miss anything. > > -- > Endi S. Dewata -------------- next part -------------- >From b4b92d145a8a8b6e6caefaf608114ed034891b53 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 25 Sep 2014 01:39:40 -0400 Subject: [PATCH] Fix BasicConstraints min/max path length check The BasicConstraintsExtConstraint min/max path length validity check ensures that the max length is greater than the min length, however, when a negative value is used to represent "no max", the check fails. Only compare the min and max length if both values are set to non-negative integers. Also run the check when either value is modified, since setting the min path length could invalidate the configuration. Ticket #1035 --- .../constraint/BasicConstraintsExtConstraint.java | 45 +++++++++++++--------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java index ca2668f7db305122f330fca058b27801820a75b4..05ab0e2fdc1609b7dc734ac08ba6d5120c7c617b 100644 --- a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java +++ b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java @@ -195,30 +195,37 @@ public class BasicConstraintsExtConstraint extends EnrollConstraint { public void setConfig(String name, String value) throws EPropertyException { + IConfigStore cs = mConfig.getSubStore("params"); - if (mConfig.getSubStore("params") == null) { + if (cs == null) { CMS.debug("BasicConstraintsExt: mConfig.getSubStore is null"); - // } else { - CMS.debug("BasicConstraintsExt: setConfig name " + name + " value " + value); - - if (name.equals(CONFIG_MAX_PATH_LEN)) { - - String minPathLen = getConfig(CONFIG_MIN_PATH_LEN); - - int minLen = getInt(minPathLen); - - int maxLen = getInt(value); - - if (minLen >= maxLen) { - CMS.debug("BasicConstraintExt: minPathLen >= maxPathLen!"); - - throw new EPropertyException("bad value"); - } - + String origValue = getConfig(name); + cs.putString(name, value); + try { + validateConfig(); + } catch (EPropertyException e) { + cs.putString(name, origValue); + throw e; } - mConfig.getSubStore("params").putString(name, value); + } + } + + private void validateConfig() throws EPropertyException { + int minLen = -1; + int maxLen = -1; + try { + minLen = getConfigInt(CONFIG_MIN_PATH_LEN); + maxLen = getConfigInt(CONFIG_MAX_PATH_LEN); + } catch (NumberFormatException e) { + // one of the path len configs is empty - treat as unset + } + if (maxLen >= 0 && maxLen >= 0 && minLen >= maxLen) { + CMS.debug("BasicConstraintExt: minPathLen >= maxPathLen!"); + throw new EPropertyException( + "Max path length (" + maxLen + + ") must be greater than min path length (" + minLen + ")."); } } } -- 1.9.3 From edewata at redhat.com Mon Nov 24 19:08:26 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 24 Nov 2014 13:08:26 -0600 Subject: [Pki-devel] [PATCH] 0016-2 - Fix BasicConstraints min/max path length check In-Reply-To: <20141124060324.GO8412@dhcp-40-8.bne.redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> <546E119C.10906@redhat.com> <20141124060324.GO8412@dhcp-40-8.bne.redhat.com> Message-ID: <5473822A.1080808@redhat.com> On 11/24/2014 12:03 AM, Fraser Tweedale wrote: > New patch attached. > > Re point 1., despite the improved ``EPropertyException`` thrown in > the new code, the error reported by the CLI is ``PKIException: Error > changing profile data``. I filed > https://fedorahosted.org/pki/ticket/1212 to address this later. > > 3. is not an issue and 2. and 4. have been addressed in the new > patch. I'm not sure if we should do this: String origValue = getConfig(name); cs.putString(name, value); try { validateConfig(); } catch (EPropertyException e) { cs.putString(name, origValue); throw e; } In case validateConfig() throws a different exception, the origValue may not be restored, so the new (possibly invalid) value will remain in the config store. Another thing, in the following code suppose the CONFIG_MIN_PATH_LEN contains an invalid value, the CONFIG_MAX_PATH_LEN will not be read: int minLen = -1; int maxLen = -1; try { minLen = getConfigInt(CONFIG_MIN_PATH_LEN); maxLen = getConfigInt(CONFIG_MAX_PATH_LEN); } catch (NumberFormatException e) { // one of the path len configs is empty - treat as unset } It may not make a difference now since the validation is only done if both variables are not negative, but in case the validation code is expanded to check for other things, the maxLen might not have the right value. Also, I think the original code would throw an error if the value is not a valid integer while here it's swallowed. I think the code should either validate each parameter as it gets added, or validate all parameters after all parameters get added. So in this case the setConfig() might look like this: validateParam(name, value); cs.putString(name, value); and the validateParam() might look like this: // if the new param is minLen if (name == CONFIG_MIN_PATH_LEN) { // if minLen is unset, return if (value is empty) return; // validate minLen is an integer int minLen = getInt(value); // if minLen is negative, return if (minLen < 0) return; // if maxLen is unset, return String maxLenString = getConfig(CONFIG_MAX_PATH_LEN); if (maxLenString is empty) return; // if maxLen is negative, return int maxLen = getInt(maxLenString); if (maxLen < 0) return; // validate minLen is less than maxLen if (minLen >= maxLen) throw exception } Same thing for maxLen. So the code is a bit longer, but I'm not sure it can be simplified without compromising the validation. By the way, this is a separate issue and feel free to open a ticket. I think the validation should check for minLen > maxLen instead of >=. This way the minLen can be equal to maxLen, meaning the parameter has to be a specific length. -- Endi S. Dewata From jmagne at redhat.com Mon Nov 24 19:27:50 2014 From: jmagne at redhat.com (John Magne) Date: Mon, 24 Nov 2014 14:27:50 -0500 (EST) Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch In-Reply-To: <546FEFAA.7000604@redhat.com> References: <546FEFAA.7000604@redhat.com> Message-ID: <158677224.17206310.1416857270113.JavaMail.zimbra@redhat.com> ACK With the caveat that it might have been nice to have these settings to be configurable, but as I understand it, there is no need to because this code is talking to one of our own servers with well known requirements. ----- Original Message ----- From: "Christina Fu" To: pki-devel at redhat.com Sent: Friday, November 21, 2014 6:06:34 PM Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch attached is the java console part of client side changes for TLS range support: https://fedorahosted.org/pki/ticket/1206 TLS range support: code change needed for cs when acting as client Please review. thanks, Christina _______________________________________________ Pki-devel mailing list Pki-devel at redhat.com https://www.redhat.com/mailman/listinfo/pki-devel From cfu at redhat.com Mon Nov 24 19:34:37 2014 From: cfu at redhat.com (Christina Fu) Date: Mon, 24 Nov 2014 11:34:37 -0800 Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch In-Reply-To: <158677224.17206310.1416857270113.JavaMail.zimbra@redhat.com> References: <546FEFAA.7000604@redhat.com> <158677224.17206310.1416857270113.JavaMail.zimbra@redhat.com> Message-ID: <5473884D.3000701@redhat.com> Yes, unlike browsers, who talk to random unknown ssl servers, our cli's and java console clients only talk to Dogtag cs servers. We just depend on the cs server settings for restrictions. The code changes in the areas that affect cli's and java console are only to make sure we can work within whatever tls ranges the cs servers set it to, e.g. tls1_2 - tls1_2. thanks, Christina On 11/24/2014 11:27 AM, John Magne wrote: > ACK > > With the caveat that it might have been nice to have these settings to be configurable, but as I understand it, > there is no need to because this code is talking to one of our own servers with well known requirements. > > ----- Original Message ----- > From: "Christina Fu" > To: pki-devel at redhat.com > Sent: Friday, November 21, 2014 6:06:34 PM > Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch > > attached is the java console part of client side changes for TLS range > support: > https://fedorahosted.org/pki/ticket/1206 TLS range support: code change > needed for cs when acting as client > > Please review. > thanks, > Christina > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From alee at redhat.com Mon Nov 24 21:53:40 2014 From: alee at redhat.com (Ade Lee) Date: Mon, 24 Nov 2014 16:53:40 -0500 Subject: [Pki-devel] [PATCH] 536 Improvements for KeyClient.archive_encrypted_data(). In-Reply-To: <546FD1A0.4070503@redhat.com> References: <546FD1A0.4070503@redhat.com> Message-ID: <1416866020.9069.5.camel@aleeredhat.laptop> ACK On Fri, 2014-11-21 at 17:58 -0600, Endi Sukma Dewata wrote: > The archive_encrypted_data() in KeyClient has been modified to have > a default value for the algorithm OID and to take a nonce IV object > instead of the base-64 encoded value. > > https://fedorahosted.org/pki/ticket/1155 > https://fedorahosted.org/pki/ticket/1156 > > The drmtest.py works just fine. > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From alee at redhat.com Mon Nov 24 22:17:26 2014 From: alee at redhat.com (Ade Lee) Date: Mon, 24 Nov 2014 17:17:26 -0500 Subject: [Pki-devel] [PATCH] 535 Removed profile input/output IDs from CLI output. In-Reply-To: <546FB428.2070108@redhat.com> References: <546FB428.2070108@redhat.com> Message-ID: <1416867446.9069.6.camel@aleeredhat.laptop> ack. On Fri, 2014-11-21 at 15:52 -0600, Endi Sukma Dewata wrote: > The current profile inputs/outputs do not have meaningful IDs > (e.g. i1, i2, o1) and are not used by the client so they should > not be displayed in the CLI output. > > In the future the IDs should be renamed into something meaningful > (e.g. keygen, sn, cert) and the inputs/outputs should be retrieved > by ID. New methods have been added to retrieve by ID. > > https://fedorahosted.org/pki/ticket/1147 > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From ftweedal at redhat.com Tue Nov 25 00:57:55 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Tue, 25 Nov 2014 10:57:55 +1000 Subject: [Pki-devel] [PATCH] 0016-2 - Fix BasicConstraints min/max path length check In-Reply-To: <5473822A.1080808@redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> <546E119C.10906@redhat.com> <20141124060324.GO8412@dhcp-40-8.bne.redhat.com> <5473822A.1080808@redhat.com> Message-ID: <20141125005755.GV8412@dhcp-40-8.bne.redhat.com> On Mon, Nov 24, 2014 at 01:08:26PM -0600, Endi Sukma Dewata wrote: > On 11/24/2014 12:03 AM, Fraser Tweedale wrote: > >New patch attached. > > > >Re point 1., despite the improved ``EPropertyException`` thrown in > >the new code, the error reported by the CLI is ``PKIException: Error > >changing profile data``. I filed > >https://fedorahosted.org/pki/ticket/1212 to address this later. > > > >3. is not an issue and 2. and 4. have been addressed in the new > >patch. > > I'm not sure if we should do this: > > String origValue = getConfig(name); > cs.putString(name, value); > try { > validateConfig(); > } catch (EPropertyException e) { > cs.putString(name, origValue); > throw e; > } > > In case validateConfig() throws a different exception, the origValue may not > be restored, so the new (possibly invalid) value will remain in the config > store. > Good point - I will catch all exceptions. > Another thing, in the following code suppose the CONFIG_MIN_PATH_LEN > contains an invalid value, the CONFIG_MAX_PATH_LEN will not be read: > > int minLen = -1; > int maxLen = -1; > try { > minLen = getConfigInt(CONFIG_MIN_PATH_LEN); > maxLen = getConfigInt(CONFIG_MAX_PATH_LEN); > } catch (NumberFormatException e) { > // one of the path len configs is empty - treat as unset > } > > It may not make a difference now since the validation is only done if both > variables are not negative, but in case the validation code is expanded to > check for other things, the maxLen might not have the right value. Also, I > think the original code would throw an error if the value is not a valid > integer while here it's swallowed. > > I think the code should either validate each parameter as it gets added, or > validate all parameters after all parameters get added. So in this case the > setConfig() might look like this: > > validateParam(name, value); > cs.putString(name, value); > > and the validateParam() might look like this: > > // if the new param is minLen > if (name == CONFIG_MIN_PATH_LEN) { > > // if minLen is unset, return > if (value is empty) return; > > // validate minLen is an integer > int minLen = getInt(value); > > // if minLen is negative, return > if (minLen < 0) return; > > // if maxLen is unset, return > String maxLenString = getConfig(CONFIG_MAX_PATH_LEN); > if (maxLenString is empty) return; > > // if maxLen is negative, return > int maxLen = getInt(maxLenString); > if (maxLen < 0) return; > > // validate minLen is less than maxLen > if (minLen >= maxLen) > throw exception > } > > Same thing for maxLen. So the code is a bit longer, but I'm not sure it can > be simplified without compromising the validation. > Ah, the perils of objects whose initialisation/validity check is not contained in the constructor - or even better, the types! I think that validating each parameter when it is set is too much code, and logic will be duplicated. A single validity check guarded by the condition that all parameters have been set is the approach I will take (the second approach you mention above). > By the way, this is a separate issue and feel free to open a ticket. I think > the validation should check for minLen > maxLen instead of >=. This way the > minLen can be equal to maxLen, meaning the parameter has to be a specific > length. > Yes, I noticed this but have preserved the original semantics. I agree; will file a new ticket. Cheers, Fraser > -- > Endi S. Dewata From cfu at redhat.com Tue Nov 25 03:03:08 2014 From: cfu at redhat.com (Christina Fu) Date: Mon, 24 Nov 2014 19:03:08 -0800 Subject: [Pki-devel] [PATCH] Ticket-1206-java-console-TLS-range-support-code-chan.patch In-Reply-To: <5473884D.3000701@redhat.com> References: <546FEFAA.7000604@redhat.com> <158677224.17206310.1416857270113.JavaMail.zimbra@redhat.com> <5473884D.3000701@redhat.com> Message-ID: <5473F16C.9010200@redhat.com> FYI, fix checked into master, DOGTAG_10_1_BRANCH , and DOGTAG_10_2_0_BRANCH commit info can be found in: https://fedorahosted.org/pki/ticket/1206 Christina On 11/24/2014 11:34 AM, Christina Fu wrote: > Yes, unlike browsers, who talk to random unknown ssl servers, our > cli's and java console clients only talk to Dogtag cs servers. We > just depend on the cs server settings for restrictions. The code > changes in the areas that affect cli's and java console are only to > make sure we can work within whatever tls ranges the cs servers set it > to, e.g. tls1_2 - tls1_2. > > thanks, > Christina > > On 11/24/2014 11:27 AM, John Magne wrote: >> ACK >> >> With the caveat that it might have been nice to have these settings >> to be configurable, but as I understand it, >> there is no need to because this code is talking to one of our own >> servers with well known requirements. >> >> ----- Original Message ----- >> From: "Christina Fu" >> To: pki-devel at redhat.com >> Sent: Friday, November 21, 2014 6:06:34 PM >> Subject: [Pki-devel] [PATCH] >> Ticket-1206-java-console-TLS-range-support-code-chan.patch >> >> attached is the java console part of client side changes for TLS range >> support: >> https://fedorahosted.org/pki/ticket/1206 TLS range support: code change >> needed for cs when acting as client >> >> Please review. >> thanks, >> Christina >> >> _______________________________________________ >> Pki-devel mailing list >> Pki-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/pki-devel > > _______________________________________________ > Pki-devel mailing list > Pki-devel at redhat.com > https://www.redhat.com/mailman/listinfo/pki-devel From edewata at redhat.com Tue Nov 25 15:07:10 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Tue, 25 Nov 2014 09:07:10 -0600 Subject: [Pki-devel] [PATCH] 535 Removed profile input/output IDs from CLI output. In-Reply-To: <1416867446.9069.6.camel@aleeredhat.laptop> References: <546FB428.2070108@redhat.com> <1416867446.9069.6.camel@aleeredhat.laptop> Message-ID: <54749B1E.4070406@redhat.com> On 11/24/2014 4:17 PM, Ade Lee wrote: > ack. Pushed to master. Thanks. -- Endi S. Dewata From edewata at redhat.com Tue Nov 25 15:07:17 2014 From: edewata at redhat.com (Endi Sukma Dewata) Date: Tue, 25 Nov 2014 09:07:17 -0600 Subject: [Pki-devel] [PATCH] 536 Improvements for KeyClient.archive_encrypted_data(). In-Reply-To: <1416866020.9069.5.camel@aleeredhat.laptop> References: <546FD1A0.4070503@redhat.com> <1416866020.9069.5.camel@aleeredhat.laptop> Message-ID: <54749B25.4000200@redhat.com> On 11/24/2014 3:53 PM, Ade Lee wrote: > ACK Pushed to master. Thanks. -- Endi S. Dewata From ftweedal at redhat.com Wed Nov 26 06:02:23 2014 From: ftweedal at redhat.com (Fraser Tweedale) Date: Wed, 26 Nov 2014 16:02:23 +1000 Subject: [Pki-devel] [PATCH] 0016-3 - Fix BasicConstraints min/max path length check In-Reply-To: <5473822A.1080808@redhat.com> References: <20140925060756.GC5346@dhcp-40-8.bne.redhat.com> <20141120065845.GE8412@dhcp-40-8.bne.redhat.com> <546E119C.10906@redhat.com> <20141124060324.GO8412@dhcp-40-8.bne.redhat.com> <5473822A.1080808@redhat.com> Message-ID: <20141126060223.GH8412@dhcp-40-8.bne.redhat.com> v3 patch. In this patch, validateConfig() is short-circuited until all params have been set, to avoid repetition of logic in the validity check. Also, a config that causes validation to fail is now reverted on any exception, not just EPropertyException. Finally, the profile still gets created when there is bad config so I filed another ticket for that: https://fedorahosted.org/pki/ticket/1215 Cheers, Fraser On Mon, Nov 24, 2014 at 01:08:26PM -0600, Endi Sukma Dewata wrote: > On 11/24/2014 12:03 AM, Fraser Tweedale wrote: > >New patch attached. > > > >Re point 1., despite the improved ``EPropertyException`` thrown in > >the new code, the error reported by the CLI is ``PKIException: Error > >changing profile data``. I filed > >https://fedorahosted.org/pki/ticket/1212 to address this later. > > > >3. is not an issue and 2. and 4. have been addressed in the new > >patch. > > I'm not sure if we should do this: > > String origValue = getConfig(name); > cs.putString(name, value); > try { > validateConfig(); > } catch (EPropertyException e) { > cs.putString(name, origValue); > throw e; > } > > In case validateConfig() throws a different exception, the origValue may not > be restored, so the new (possibly invalid) value will remain in the config > store. > > Another thing, in the following code suppose the CONFIG_MIN_PATH_LEN > contains an invalid value, the CONFIG_MAX_PATH_LEN will not be read: > > int minLen = -1; > int maxLen = -1; > try { > minLen = getConfigInt(CONFIG_MIN_PATH_LEN); > maxLen = getConfigInt(CONFIG_MAX_PATH_LEN); > } catch (NumberFormatException e) { > // one of the path len configs is empty - treat as unset > } > > It may not make a difference now since the validation is only done if both > variables are not negative, but in case the validation code is expanded to > check for other things, the maxLen might not have the right value. Also, I > think the original code would throw an error if the value is not a valid > integer while here it's swallowed. > > I think the code should either validate each parameter as it gets added, or > validate all parameters after all parameters get added. So in this case the > setConfig() might look like this: > > validateParam(name, value); > cs.putString(name, value); > > and the validateParam() might look like this: > > // if the new param is minLen > if (name == CONFIG_MIN_PATH_LEN) { > > // if minLen is unset, return > if (value is empty) return; > > // validate minLen is an integer > int minLen = getInt(value); > > // if minLen is negative, return > if (minLen < 0) return; > > // if maxLen is unset, return > String maxLenString = getConfig(CONFIG_MAX_PATH_LEN); > if (maxLenString is empty) return; > > // if maxLen is negative, return > int maxLen = getInt(maxLenString); > if (maxLen < 0) return; > > // validate minLen is less than maxLen > if (minLen >= maxLen) > throw exception > } > > Same thing for maxLen. So the code is a bit longer, but I'm not sure it can > be simplified without compromising the validation. > > By the way, this is a separate issue and feel free to open a ticket. I think > the validation should check for minLen > maxLen instead of >=. This way the > minLen can be equal to maxLen, meaning the parameter has to be a specific > length. > > -- > Endi S. Dewata -------------- next part -------------- >From e0a374e49957bc97a2865153aa7beb63804ed88f Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 25 Sep 2014 01:39:40 -0400 Subject: [PATCH] Fix BasicConstraints min/max path length check The BasicConstraintsExtConstraint min/max path length validity check ensures that the max length is greater than the min length, however, when a negative value is used to represent "no max", the check fails. Only compare the min and max length if all config parameters are set and both the min and max length can be parsed and both are non-negative integers. Also run the check when either value is modified, since setting the min path length could invalidate the configuration. Ticket #1035 --- .../constraint/BasicConstraintsExtConstraint.java | 54 ++++++++++++++-------- .../cms/profile/constraint/EnrollConstraint.java | 17 +++++++ 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java index ca2668f7db305122f330fca058b27801820a75b4..4449a8ddf5e71379f24f8cef3e7f324a6c796ffb 100644 --- a/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java +++ b/base/server/cms/src/com/netscape/cms/profile/constraint/BasicConstraintsExtConstraint.java @@ -195,30 +195,46 @@ public class BasicConstraintsExtConstraint extends EnrollConstraint { public void setConfig(String name, String value) throws EPropertyException { + IConfigStore cs = mConfig.getSubStore("params"); - if (mConfig.getSubStore("params") == null) { + if (cs == null) { CMS.debug("BasicConstraintsExt: mConfig.getSubStore is null"); - // } else { - CMS.debug("BasicConstraintsExt: setConfig name " + name + " value " + value); - - if (name.equals(CONFIG_MAX_PATH_LEN)) { - - String minPathLen = getConfig(CONFIG_MIN_PATH_LEN); - - int minLen = getInt(minPathLen); - - int maxLen = getInt(value); - - if (minLen >= maxLen) { - CMS.debug("BasicConstraintExt: minPathLen >= maxPathLen!"); - - throw new EPropertyException("bad value"); - } - + String origValue = getConfig(name); + cs.putString(name, value); + try { + validateConfig(); + } catch (Exception e) { + cs.putString(name, origValue); + throw e; } - mConfig.getSubStore("params").putString(name, value); + } + } + + private void validateConfig() throws EPropertyException { + if (!allConfigsSet()) + return; + + int minLen = -1; + int maxLen = -1; + try { + minLen = getConfigInt(CONFIG_MIN_PATH_LEN); + } catch (NumberFormatException e) { + CMS.debug("BasicConstraintExt: minPathLen not a number"); + throw new EPropertyException("Min path length is not an integral number."); + } + try { + maxLen = getConfigInt(CONFIG_MAX_PATH_LEN); + } catch (NumberFormatException e) { + CMS.debug("BasicConstraintExt: maxPathLen not a number"); + throw new EPropertyException("Max path length is not an integral number."); + } + if (maxLen >= 0 && maxLen >= 0 && minLen >= maxLen) { + CMS.debug("BasicConstraintExt: minPathLen >= maxPathLen!"); + throw new EPropertyException( + "Max path length (" + maxLen + + ") must be greater than min path length (" + minLen + ")."); } } } diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/EnrollConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/EnrollConstraint.java index eb3eb14f67a6dff5bcd8b048eba316daf6223cb4..a29b9409a27f00662cc8a86c7b38f6c90fd441df 100644 --- a/base/server/cms/src/com/netscape/cms/profile/constraint/EnrollConstraint.java +++ b/base/server/cms/src/com/netscape/cms/profile/constraint/EnrollConstraint.java @@ -102,6 +102,23 @@ public abstract class EnrollConstraint implements IPolicyConstraint { return ""; } + protected boolean allConfigsSet() { + if (mConfig == null) + return false; + IConfigStore cs = mConfig.getSubStore("params"); + if (cs == null) + return false; + + for (String name : mConfigNames) { + try { + cs.getString(name); + } catch (EBaseException e) { + return false; + } + } + return true; + } + public void init(IProfile profile, IConfigStore config) throws EProfileException { mConfig = config; -- 1.9.3