freeipa

FreeIPA is an integrated Identity and Authentication solution for Linux/UNIX networked environments.  |  http://www.freeipa.org/

Commit 39a5d9c csrgen: Automate full cert request flow

2 files Authored by benlipton a year ago , Committed by jcholast a year ago ,
csrgen: Automate full cert request flow

Allows the `ipa cert-request` command to generate its own CSR. It no
longer requires a CSR passed on the command line, instead it creates a
config (bash script) with `cert-get-requestdata`, then runs it to build
a CSR, and submits that CSR.

Example usage (NSS database):
$ ipa cert-request --principal host/test.example.com --profile-id caIPAserviceCert --database /tmp/certs

Example usage (PEM private key file):
$ ipa cert-request --principal host/test.example.com --profile-id caIPAserviceCert --private-key /tmp/key.pem

https://fedorahosted.org/freeipa/ticket/4899

Reviewed-By: Jan Cholasta <jcholast@redhat.com>

    
  1 @@ -19,6 +19,11 @@
  2   # You should have received a copy of the GNU General Public License
  3   # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  4   
  5 + import subprocess
  6 + from tempfile import NamedTemporaryFile as NTF
  7 + 
  8 + import six
  9 + 
 10   from ipaclient.frontend import MethodOverride
 11   from ipalib import errors
 12   from ipalib import x509
 13 @@ -27,17 +32,86 @@
 14   from ipalib.plugable import Registry
 15   from ipalib.text import _
 16   
 17 + if six.PY3:
 18 +     unicode = str
 19 + 
 20   register = Registry()
 21   
 22   
 23   @register(override=True, no_fail=True)
 24   class cert_request(MethodOverride):
 25 +     takes_options = (
 26 +         Str(
 27 +             'database?',
 28 +             label=_('Path to NSS database'),
 29 +             doc=_('Path to NSS database to use for private key'),
 30 +         ),
 31 +         Str(
 32 +             'private_key?',
 33 +             label=_('Path to private key file'),
 34 +             doc=_('Path to PEM file containing a private key'),
 35 +         ),
 36 +     )
 37 + 
 38       def get_args(self):
 39           for arg in super(cert_request, self).get_args():
 40               if arg.name == 'csr':
 41 -                 arg = arg.clone_retype(arg.name, File)
 42 +                 arg = arg.clone_retype(arg.name, File, required=False)
 43               yield arg
 44   
 45 +     def forward(self, csr=None, **options):
 46 +         database = options.pop('database', None)
 47 +         private_key = options.pop('private_key', None)
 48 + 
 49 +         if csr is None:
 50 +             if database:
 51 +                 helper = u'certutil'
 52 +                 helper_args = ['-d', database]
 53 +             elif private_key:
 54 +                 helper = u'openssl'
 55 +                 helper_args = [private_key]
 56 +             else:
 57 +                 raise errors.InvocationError(
 58 +                     message=u"One of 'database' or 'private_key' is required")
 59 + 
 60 +             with NTF() as scriptfile, NTF() as csrfile:
 61 +                 profile_id = options.get('profile_id')
 62 + 
 63 +                 self.api.Command.cert_get_requestdata(
 64 +                     profile_id=profile_id,
 65 +                     principal=options.get('principal'),
 66 +                     out=unicode(scriptfile.name),
 67 +                     helper=helper)
 68 + 
 69 +                 helper_cmd = [
 70 +                     'bash', '-e', scriptfile.name, csrfile.name] + helper_args
 71 + 
 72 +                 try:
 73 +                     subprocess.check_output(helper_cmd)
 74 +                 except subprocess.CalledProcessError as e:
 75 +                     raise errors.CertificateOperationError(
 76 +                         error=(
 77 +                             _('Error running "%(cmd)s" to generate CSR:'
 78 +                               ' %(err)s') %
 79 +                             {'cmd': ' '.join(helper_cmd), 'err': e.output}))
 80 + 
 81 +                 try:
 82 +                     csr = unicode(csrfile.read())
 83 +                 except IOError as e:
 84 +                     raise errors.CertificateOperationError(
 85 +                         error=(_('Unable to read generated CSR file: %(err)s')
 86 +                                % {'err': e}))
 87 +                 if not csr:
 88 +                     raise errors.CertificateOperationError(
 89 +                         error=(_('Generated CSR was empty')))
 90 +         else:
 91 +             if database is not None or private_key is not None:
 92 +                 raise errors.MutuallyExclusiveError(reason=_(
 93 +                     "Options 'database' and 'private_key' are not compatible"
 94 +                     " with 'csr'"))
 95 + 
 96 +         return super(cert_request, self).forward(csr, **options)
 97 + 
 98   
 99   @register(override=True, no_fail=True)
100   class cert_show(MethodOverride):
 1 @@ -13,6 +13,7 @@
 2   from ipalib.parameters import Principal
 3   from ipalib.plugable import Registry
 4   from ipalib.text import _
 5 + from ipapython import dogtag
 6   
 7   if six.PY3:
 8       unicode = str
 9 @@ -36,7 +37,7 @@
10                     ' HTTP/test.example.com)'),
11           ),
12           Str(
13 -             'profile_id',
14 +             'profile_id?',
15               label=_('Profile ID'),
16               doc=_('CSR Generation Profile to use'),
17           ),
18 @@ -73,6 +74,8 @@
19   
20           principal = options.get('principal')
21           profile_id = options.get('profile_id')
22 +         if profile_id is None:
23 +             profile_id = dogtag.DEFAULT_PROFILE
24           helper = options.get('helper')
25   
26           if self.api.env.in_server: