freeipa/ipaserver/plugins/ca.py
Fraser Tweedale 3d4db834ca Add 'ca' plugin
This commit adds the 'ca' plugin for creating and managing
lightweight CAs.  The initial implementation supports a single level
of sub-CAs underneath the IPA CA.

This commit also:

- adds the container for FreeIPA CA objects

- adds schema for the FreeIPA CA objects

- updates ipa-pki-proxy.conf to allow access to the Dogtag
  lightweight CAs REST API.

Part of: https://fedorahosted.org/freeipa/ticket/4559

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
2016-06-15 07:13:38 +02:00

218 lines
6.5 KiB
Python

#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
from ipalib import api, errors, DNParam, Str
from ipalib.constants import IPA_CA_CN
from ipalib.plugable import Registry
from ipaserver.plugins.baseldap import (
LDAPObject, LDAPSearch, LDAPCreate, LDAPDelete,
LDAPUpdate, LDAPRetrieve)
from ipaserver.plugins.cert import ca_enabled_check
from ipalib import _, ngettext
__doc__ = _("""
Manage Certificate Authorities
Subordinate Certificate Authorities (Sub-CAs) can be added for scoped issuance
of X.509 certificates.
EXAMPLES:
Create new CA, subordinate to the IPA CA.
ipa ca-add puppet --desc "Puppet" \\
--subject "CN=Puppet CA,O=EXAMPLE.COM"
""")
register = Registry()
@register()
class ca(LDAPObject):
"""
Lightweight CA Object
"""
container_dn = api.env.container_ca
object_name = _('Certificate Authority')
object_name_plural = _('Certificate Authorities')
object_class = ['ipaca']
permission_filter_objectclasses = ['ipaca']
default_attributes = [
'cn', 'description', 'ipacaid', 'ipacaissuerdn', 'ipacasubjectdn',
]
rdn_attribute = 'cn'
rdn_is_primary_key = True
label = _('Certificate Authorities')
label_singular = _('Certificate Authority')
takes_params = (
Str('cn',
primary_key=True,
cli_name='name',
label=_('Name'),
doc=_('Name for referencing the CA'),
),
Str('description?',
cli_name='desc',
label=_('Description'),
doc=_('Description of the purpose of the CA'),
),
Str('ipacaid',
cli_name='id',
label=_('Authority ID'),
doc=_('Dogtag Authority ID'),
flags=['no_create', 'no_update'],
),
DNParam('ipacasubjectdn',
cli_name='subject',
label=_('Subject DN'),
doc=_('Subject Distinguished Name'),
flags=['no_update'],
),
DNParam('ipacaissuerdn',
cli_name='issuer',
label=_('Issuer DN'),
doc=_('Issuer Distinguished Name'),
flags=['no_create', 'no_update'],
),
)
permission_filter_objectclasses = ['ipaca']
managed_permissions = {
'System: Read CAs': {
'replaces_global_anonymous_aci': True,
'ipapermbindruletype': 'all',
'ipapermright': {'read', 'search', 'compare'},
'ipapermdefaultattr': {
'cn',
'description',
'ipacaid',
'ipacaissuerdn',
'ipacasubjectdn',
'objectclass',
},
},
'System: Add CA': {
'ipapermright': {'add'},
'replaces': [
'(target = "ldap:///cn=*,cn=cas,cn=ca,$SUFFIX")(version 3.0;acl "permission:Add CA";allow (add) groupdn = "ldap:///cn=Add CA,cn=permissions,cn=pbac,$SUFFIX";)',
],
'default_privileges': {'CA Administrator'},
},
'System: Delete CA': {
'ipapermright': {'delete'},
'replaces': [
'(target = "ldap:///cn=*,cn=cas,cn=ca,$SUFFIX")(version 3.0;acl "permission:Delete CA";allow (delete) groupdn = "ldap:///cn=Delete CA,cn=permissions,cn=pbac,$SUFFIX";)',
],
'default_privileges': {'CA Administrator'},
},
'System: Modify CA': {
'ipapermright': {'write'},
'ipapermdefaultattr': {
'cn',
'description',
},
'replaces': [
'(targetattr = "cn || description")(target = "ldap:///cn=*,cn=cas,cn=ca,$SUFFIX")(version 3.0;acl "permission:Modify CA";allow (write) groupdn = "ldap:///cn=Modify CA,cn=permissions,cn=pbac,$SUFFIX";)',
],
'default_privileges': {'CA Administrator'},
},
}
@register()
class ca_find(LDAPSearch):
__doc__ = _("Search for CAs.")
msg_summary = ngettext(
'%(count)d CA matched', '%(count)d CAs matched', 0
)
def execute(self, *keys, **options):
ca_enabled_check()
return super(ca_find, self).execute(*keys, **options)
@register()
class ca_show(LDAPRetrieve):
__doc__ = _("Display the properties of a CA.")
def execute(self, *args, **kwargs):
ca_enabled_check()
return super(ca_show, self).execute(*args, **kwargs)
@register()
class ca_add(LDAPCreate):
__doc__ = _("Create a CA.")
msg_summary = _('Created CA "%(value)s"')
def pre_callback(self, ldap, dn, entry, entry_attrs, *keys, **options):
ca_enabled_check()
if not ldap.can_add(dn[1:]):
raise errors.ACIError(
info=_("Insufficient 'add' privilege for entry '%s'.") % dn)
# check for name collision before creating CA in Dogtag
try:
api.Object.ca.get_dn_if_exists(keys[-1])
self.obj.handle_duplicate_entry(*keys)
except errors.NotFound:
pass
# Create the CA in Dogtag.
with self.api.Backend.ra_lightweight_ca as ca_api:
resp = ca_api.create_ca(options['ipacasubjectdn'])
entry['ipacaid'] = [resp['id']]
entry['ipacaissuerdn'] = [resp['issuerDN']]
# In the event that the issued certificate's subject DN
# differs from what was requested, record the actual DN.
#
entry['ipacasubjectdn'] = [resp['dn']]
return dn
@register()
class ca_del(LDAPDelete):
__doc__ = _('Delete a CA.')
msg_summary = _('Deleted CA "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
ca_enabled_check()
if keys[0] == IPA_CA_CN:
raise errors.ProtectedEntryError(
label=_("CA"),
key=keys[0],
reason=_("IPA CA cannot be deleted"))
ca_id = self.api.Command.ca_show(keys[0])['result']['ipacaid'][0]
with self.api.Backend.ra_lightweight_ca as ca_api:
ca_api.disable_ca(ca_id)
ca_api.delete_ca(ca_id)
return dn
@register()
class ca_mod(LDAPUpdate):
__doc__ = _("Modify CA configuration.")
msg_summary = _('Modified CA "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
ca_enabled_check()
if 'rename' in options or 'cn' in entry_attrs:
if keys[0] == IPA_CA_CN:
raise errors.ProtectedEntryError(
label=_("CA"),
key=keys[0],
reason=u'IPA CA cannot be renamed')
return dn