mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
CRL generation master: new utility to enable|disable
Implement a new command ipa-clrgen-manage to enable, disable, or check the status of CRL generation on the localhost. The command automates the manual steps described in the wiki https://www.freeipa.org/page/Howto/Promote_CA_to_Renewal_and_CRL_Master Fixes: https://pagure.io/freeipa/issue/5803 Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Francois Cami <fcami@redhat.com>
This commit is contained in:
parent
f90a4b9554
commit
0d23fa9278
@ -997,6 +997,7 @@ fi
|
||||
%{_sbindir}/ipa-cacert-manage
|
||||
%{_sbindir}/ipa-winsync-migrate
|
||||
%{_sbindir}/ipa-pkinit-manage
|
||||
%{_sbindir}/ipa-crlgen-manage
|
||||
%{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
|
||||
%{_libexecdir}/certmonger/ipa-server-guard
|
||||
%dir %{_libexecdir}/ipa
|
||||
@ -1055,6 +1056,7 @@ fi
|
||||
%{_mandir}/man1/ipa-cacert-manage.1*
|
||||
%{_mandir}/man1/ipa-winsync-migrate.1*
|
||||
%{_mandir}/man1/ipa-pkinit-manage.1*
|
||||
%{_mandir}/man1/ipa-crlgen-manage.1*
|
||||
|
||||
|
||||
%files -n python3-ipaserver
|
||||
|
@ -28,6 +28,7 @@ dist_noinst_DATA = \
|
||||
ipa-cacert-manage.in \
|
||||
ipa-winsync-migrate.in \
|
||||
ipa-pkinit-manage.in \
|
||||
ipa-crlgen-manage.in \
|
||||
ipa-custodia.in \
|
||||
ipa-custodia-check.in \
|
||||
ipa-httpd-kdcproxy.in \
|
||||
@ -58,6 +59,7 @@ nodist_sbin_SCRIPTS = \
|
||||
ipa-cacert-manage \
|
||||
ipa-winsync-migrate \
|
||||
ipa-pkinit-manage \
|
||||
ipa-crlgen-manage \
|
||||
$(NULL)
|
||||
|
||||
appdir = $(libexecdir)/ipa/
|
||||
|
8
install/tools/ipa-crlgen-manage.in
Normal file
8
install/tools/ipa-crlgen-manage.in
Normal file
@ -0,0 +1,8 @@
|
||||
@PYTHONSHEBANG@
|
||||
#
|
||||
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipaserver.install.ipa_crlgen_manage import CRLGenManage
|
||||
|
||||
CRLGenManage.run_cli()
|
@ -27,6 +27,7 @@ dist_man1_MANS = \
|
||||
ipa-cacert-manage.1 \
|
||||
ipa-winsync-migrate.1 \
|
||||
ipa-pkinit-manage.1 \
|
||||
ipa-crlgen-manage.1 \
|
||||
$(NULL)
|
||||
|
||||
dist_man8_MANS = \
|
||||
|
47
install/tools/man/ipa-crlgen-manage.1
Normal file
47
install/tools/man/ipa-crlgen-manage.1
Normal file
@ -0,0 +1,47 @@
|
||||
.\"
|
||||
.\" Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
||||
.\"
|
||||
.TH "ipa-crlgen-manage" "1" "Feb 12 2019" "FreeIPA" "FreeIPA Manual Pages"
|
||||
.SH "NAME"
|
||||
ipa\-crlgen\-manage \- Enables or disables CRL generation
|
||||
.SH "SYNOPSIS"
|
||||
ipa\-crlgen\-manage [options] <enable|disable|status>
|
||||
.SH "DESCRIPTION"
|
||||
Run the command with the \fBenable\fR option to enable CRL generation on the
|
||||
local host. This requires that the IPA server is already installed and
|
||||
configured, including a CA. The command will restart Dogtag and Apache.
|
||||
|
||||
Run the command with the \fBdisable\fR option to disable CRL generation on the
|
||||
local host. The command will restart Dogtag and Apache.
|
||||
|
||||
Run the command with the \fBstatus\fR option to determine the current status
|
||||
of CRL generation. If the local host is configured for CRL generation, the
|
||||
command also prints the last CRL generation date and number.
|
||||
|
||||
Important: the administrator must ensure that there is only one IPA server
|
||||
generating CRLs. In order to transfer the CRL generation from one server to
|
||||
another, please run \fBipa-crlgen-manage disable\fR on the current CRL
|
||||
generation master, followed by \fBipa-crlgen-manage enable\fR on the new
|
||||
CRL generation master.
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Show the program's version and exit.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Show the help for this program.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debugging information.
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
Output only errors.
|
||||
.TP
|
||||
\fB\-\-log\-file\fR=\fIFILE\fR
|
||||
Log to the given file.
|
||||
.SH "EXIT STATUS"
|
||||
0 if the command was successful
|
||||
|
||||
1 if an error occurred
|
||||
|
||||
2 if the local host is not an IPA server
|
@ -256,6 +256,10 @@ def is_ca_installed_locally():
|
||||
return os.path.exists(paths.CA_CS_CFG_PATH)
|
||||
|
||||
|
||||
class InconsistentCRLGenConfigException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CAInstance(DogtagInstance):
|
||||
"""
|
||||
When using a dogtag CA the DS database contains just the
|
||||
@ -278,6 +282,14 @@ class CAInstance(DogtagInstance):
|
||||
'subsystemCert cert-pki-ca',
|
||||
'caSigningCert cert-pki-ca')
|
||||
server_cert_name = 'Server-Cert cert-pki-ca'
|
||||
# The following must be aligned with the RewriteRule defined in
|
||||
# install/share/ipa-pki-proxy.conf.template
|
||||
crl_rewrite_pattern = r"^\s*(RewriteRule\s+\^/ipa/crl/MasterCRL.bin\s.*)$"
|
||||
crl_rewrite_comment = r"^#\s*RewriteRule\s+\^/ipa/crl/MasterCRL.bin\s.*$"
|
||||
crl_rewriterule = "\nRewriteRule ^/ipa/crl/MasterCRL.bin " \
|
||||
"http://{}/ca/ee/ca/getCRL?" \
|
||||
"op=getCRL&crlIssuingPoint=MasterCRL " \
|
||||
"[L,R=301,NC]"
|
||||
|
||||
def __init__(self, realm=None, host_name=None, custodia=None):
|
||||
super(CAInstance, self).__init__(
|
||||
@ -1386,6 +1398,155 @@ class CAInstance(DogtagInstance):
|
||||
'50-dogtag10-migration.update')]
|
||||
)
|
||||
|
||||
def is_crlgen_enabled(self):
|
||||
"""Check if the local CA instance is generating CRL
|
||||
|
||||
Three conditions must be met to consider that the local CA is CRL
|
||||
generation master:
|
||||
- in CS.cfg ca.crl.MasterCRL.enableCRLCache=true
|
||||
- in CS.cfg ca.crl.MasterCRL.enableCRLUpdates=true
|
||||
- in /etc/httpd/conf.d/ipa-pki-proxy.conf the RewriteRule
|
||||
^/ipa/crl/MasterCRL.bin is disabled (commented or removed)
|
||||
|
||||
If the values are inconsistent, an exception is raised
|
||||
:returns: True/False
|
||||
:raises: InconsistentCRLGenConfigException if the config is
|
||||
inconsistent
|
||||
"""
|
||||
try:
|
||||
cache = directivesetter.get_directive(
|
||||
self.config, 'ca.crl.MasterCRL.enableCRLCache', '=')
|
||||
enableCRLCache = cache.lower() == 'true'
|
||||
updates = directivesetter.get_directive(
|
||||
self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
||||
enableCRLUpdates = updates.lower() == 'true'
|
||||
|
||||
# If the values are different, the config is inconsistent
|
||||
if enableCRLCache != enableCRLUpdates:
|
||||
raise InconsistentCRLGenConfigException(
|
||||
"Configuration is inconsistent, please check "
|
||||
"ca.crl.MasterCRL.enableCRLCache and "
|
||||
"ca.crl.MasterCRL.enableCRLUpdates in {} and "
|
||||
"run ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
self.config))
|
||||
except IOError:
|
||||
raise RuntimeError(
|
||||
"Unable to read {}".format(self.config))
|
||||
|
||||
# At this point enableCRLCache and enableCRLUpdates have the same value
|
||||
try:
|
||||
rewriteRuleDisabled = True
|
||||
p = re.compile(self.crl_rewrite_pattern)
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF) as f:
|
||||
for line in f.readlines():
|
||||
if p.search(line):
|
||||
rewriteRuleDisabled = False
|
||||
break
|
||||
except IOError:
|
||||
raise RuntimeError(
|
||||
"Unable to read {}".format(paths.HTTPD_IPA_PKI_PROXY_CONF))
|
||||
|
||||
# if enableCRLUpdates and rewriteRuleDisabled are different, the config
|
||||
# is inconsistent
|
||||
if enableCRLUpdates != rewriteRuleDisabled:
|
||||
raise InconsistentCRLGenConfigException(
|
||||
"Configuration is inconsistent, please check "
|
||||
"ca.crl.MasterCRL.enableCRLCache in {} and the "
|
||||
"RewriteRule ^/ipa/crl/MasterCRL.bin in {} and "
|
||||
"run ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
self.config, paths.HTTPD_IPA_PKI_PROXY_CONF))
|
||||
return enableCRLUpdates
|
||||
|
||||
def setup_crlgen(self, setup_crlgen):
|
||||
"""Configure the local host for CRL generation
|
||||
|
||||
:param setup_crlgen: if True enable CRL generation, if False, disable
|
||||
"""
|
||||
try:
|
||||
crlgen_enabled = self.is_crlgen_enabled()
|
||||
if crlgen_enabled == setup_crlgen:
|
||||
logger.info(
|
||||
"Nothing to do, CRL generation already %s",
|
||||
"enabled" if crlgen_enabled else "disabled")
|
||||
return
|
||||
except InconsistentCRLGenConfigException:
|
||||
logger.warning("CRL generation is partially enabled, repairing...")
|
||||
|
||||
# Stop PKI
|
||||
logger.info("Stopping %s", self.service_name)
|
||||
self.stop_instance()
|
||||
logger.debug("%s successfully stopped", self.service_name)
|
||||
|
||||
# Edit the CS.cfg directives
|
||||
logger.info("Editing %s", self.config)
|
||||
with directivesetter.DirectiveSetter(
|
||||
self.config, quotes=False, separator='=') as ds:
|
||||
# Convert the bool setup_crlgen to a lowercase string
|
||||
str_value = str(setup_crlgen).lower()
|
||||
ds.set('ca.crl.MasterCRL.enableCRLCache', str_value)
|
||||
ds.set('ca.crl.MasterCRL.enableCRLUpdates', str_value)
|
||||
|
||||
# Start pki-tomcat
|
||||
logger.info("Starting %s", self.service_name)
|
||||
self.start_instance()
|
||||
logger.debug("%s successfully started", self.service_name)
|
||||
|
||||
# Edit the RewriteRule
|
||||
def comment_rewriterule():
|
||||
logger.info("Editing %s", paths.HTTPD_IPA_PKI_PROXY_CONF)
|
||||
# look for the pattern RewriteRule ^/ipa/crl/MasterCRL.bin ..
|
||||
# and comment out
|
||||
p = re.compile(self.crl_rewrite_pattern, re.MULTILINE)
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF) as f:
|
||||
content = f.read()
|
||||
new_content = p.sub(r"#\1", content)
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF, 'w') as f:
|
||||
f.write(new_content)
|
||||
|
||||
def uncomment_rewriterule():
|
||||
logger.info("Editing %s", paths.HTTPD_IPA_PKI_PROXY_CONF)
|
||||
# check if the pattern RewriteRule ^/ipa/crl/MasterCRL.bin ..
|
||||
# is already present
|
||||
present = False
|
||||
p = re.compile(self.crl_rewrite_pattern, re.MULTILINE)
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF) as f:
|
||||
content = f.read()
|
||||
present = p.search(content)
|
||||
# Remove the comment
|
||||
p_comment = re.compile(self.crl_rewrite_comment, re.MULTILINE)
|
||||
new_content = p_comment.sub("", content)
|
||||
# If not already present, add RewriteRule
|
||||
if not present:
|
||||
new_content += self.crl_rewriterule.format(api.env.host)
|
||||
# Finally write the file
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF, 'w') as f:
|
||||
f.write(new_content)
|
||||
|
||||
try:
|
||||
if setup_crlgen:
|
||||
comment_rewriterule()
|
||||
else:
|
||||
uncomment_rewriterule()
|
||||
|
||||
except IOError:
|
||||
raise RuntimeError(
|
||||
"Unable to access {}".format(paths.HTTPD_IPA_PKI_PROXY_CONF))
|
||||
|
||||
# Restart httpd
|
||||
http_service = services.knownservices.httpd
|
||||
logger.info("Restarting %s", http_service.service_name)
|
||||
http_service.restart()
|
||||
logger.debug("%s successfully restarted", http_service.service_name)
|
||||
|
||||
# make sure a CRL is generated if setup_crl is True
|
||||
if setup_crlgen:
|
||||
logger.info("Forcing CRL update")
|
||||
api.Backend.ra.override_port = 8443
|
||||
result = api.Backend.ra.updateCRL(wait='true')
|
||||
if result.get('crlUpdate', 'Failure') == 'Success':
|
||||
logger.debug("Successfully updated CRL")
|
||||
api.Backend.ra.override_port = None
|
||||
|
||||
|
||||
def __update_entry_from_cert(make_filter, make_entry, cert):
|
||||
"""
|
||||
@ -1466,7 +1627,6 @@ def __update_entry_from_cert(make_filter, make_entry, cert):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def update_people_entry(cert):
|
||||
"""
|
||||
Update the userCerticate for an entry in the dogtag ou=People. This
|
||||
|
118
ipaserver/install/ipa_crlgen_manage.py
Normal file
118
ipaserver/install/ipa_crlgen_manage.py
Normal file
@ -0,0 +1,118 @@
|
||||
#
|
||||
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from __future__ import print_function, absolute_import
|
||||
|
||||
import os
|
||||
import logging
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography import x509
|
||||
|
||||
from ipalib import api
|
||||
from ipalib.errors import NetworkError
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.admintool import AdminTool
|
||||
from ipaserver.install import cainstance
|
||||
from ipaserver.install import installutils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CRLGenManage(AdminTool):
|
||||
command_name = "ipa-crlgen-manage"
|
||||
usage = "%prog <enable|disable|status>"
|
||||
description = "Manage CRL Generation Master."
|
||||
|
||||
def validate_options(self):
|
||||
super(CRLGenManage, self).validate_options(needs_root=True)
|
||||
installutils.check_server_configuration()
|
||||
|
||||
option_parser = self.option_parser
|
||||
|
||||
if not self.args:
|
||||
option_parser.error("action not specified")
|
||||
elif len(self.args) > 1:
|
||||
option_parser.error("too many arguments")
|
||||
|
||||
action = self.args[0]
|
||||
if action not in {'enable', 'disable', 'status'}:
|
||||
option_parser.error("unrecognized action '{}'".format(action))
|
||||
|
||||
def run(self):
|
||||
api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
|
||||
api.finalize()
|
||||
|
||||
try:
|
||||
api.Backend.ldap2.connect()
|
||||
except NetworkError as e:
|
||||
logger.debug("Unable to connect to the local instance: %s", e)
|
||||
raise RuntimeError("IPA must be running, please run ipactl start")
|
||||
ca = cainstance.CAInstance(api.env.realm)
|
||||
|
||||
try:
|
||||
action = self.args[0]
|
||||
if action == 'enable':
|
||||
self.enable(ca)
|
||||
elif action == 'disable':
|
||||
self.disable(ca)
|
||||
elif action == 'status':
|
||||
self.status(ca)
|
||||
finally:
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
||||
return 0
|
||||
|
||||
def check_local_ca_instance(self, raiseOnErr=False):
|
||||
if not api.Command.ca_is_enabled()['result'] or \
|
||||
not cainstance.is_ca_installed_locally():
|
||||
if raiseOnErr:
|
||||
raise RuntimeError("Dogtag CA is not installed. "
|
||||
"Please install a CA first with the "
|
||||
"`ipa-ca-install` command.")
|
||||
else:
|
||||
logger.warning(
|
||||
"Warning: Dogtag CA is not installed on this server.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def enable(self, ca):
|
||||
# When the local node is not a CA, raise an Exception
|
||||
self.check_local_ca_instance(raiseOnErr=True)
|
||||
ca.setup_crlgen(True)
|
||||
logger.info("CRL generation enabled on the local host. "
|
||||
"Please make sure to have only a single CRL generation "
|
||||
"master.")
|
||||
|
||||
def disable(self, ca):
|
||||
# When the local node is not a CA, nothing to do
|
||||
if not self.check_local_ca_instance():
|
||||
return
|
||||
ca.setup_crlgen(False)
|
||||
logger.info("CRL generation disabled on the local host. "
|
||||
"Please make sure to configure CRL generation on another "
|
||||
"master with %s enable", self.command_name)
|
||||
|
||||
def status(self, ca):
|
||||
# When the local node is not a CA, return "disabled"
|
||||
if not self.check_local_ca_instance():
|
||||
print("CRL generation: disabled")
|
||||
return
|
||||
|
||||
# Local node is a CA, check its configuration
|
||||
if ca.is_crlgen_enabled():
|
||||
print("CRL generation: enabled")
|
||||
try:
|
||||
crl_filename = os.path.join(paths.PKI_CA_PUBLISH_DIR,
|
||||
'MasterCRL.bin')
|
||||
with open(crl_filename, 'rb') as f:
|
||||
crl = x509.load_der_x509_crl(f.read(), default_backend())
|
||||
print("Last CRL update: {}".format(crl.last_update))
|
||||
for ext in crl.extensions:
|
||||
if ext.oid == x509.oid.ExtensionOID.CRL_NUMBER:
|
||||
print("Last CRL Number: {}".format(
|
||||
ext.value.crl_number))
|
||||
except IOError:
|
||||
logger.error("Unable to find last CRL")
|
||||
else:
|
||||
print("CRL generation: disabled")
|
@ -1148,6 +1148,67 @@ def parse_unrevoke_cert_xml(doc):
|
||||
return response
|
||||
|
||||
|
||||
def parse_updateCRL_xml(doc):
|
||||
'''
|
||||
:param doc: The root node of the xml document to parse
|
||||
:returns: result dict
|
||||
:except ValueError:
|
||||
|
||||
After parsing the results are returned in a result dict. The following
|
||||
table illustrates the mapping from the CMS data item to what may be found
|
||||
in the result dict. If a CMS data item is absent it will also be absent in
|
||||
the result dict.
|
||||
|
||||
If the requestStatus is not SUCCESS then the response dict will have the
|
||||
contents described in `parse_error_template_xml`.
|
||||
|
||||
+-----------------+-------------+-----------------------+---------------+
|
||||
|cms name |cms type |result name |result type |
|
||||
+=================+=============+=======================+===============+
|
||||
|crlIssuingPoint |string |crl_issuing_point |unicode |
|
||||
+-----------------+-------------+-----------------------+---------------+
|
||||
|crlUpdate |string |crl_update [1] |unicode |
|
||||
+-----------------+-------------+-----------------------+---------------+
|
||||
|
||||
.. [1] crlUpdate may be one of:
|
||||
|
||||
- "Success"
|
||||
- "Failure"
|
||||
- "missingParameters"
|
||||
- "testingNotEnabled"
|
||||
- "testingInProgress"
|
||||
- "Scheduled"
|
||||
- "inProgress"
|
||||
- "disabled"
|
||||
- "notInitialized"
|
||||
|
||||
'''
|
||||
|
||||
request_status = get_request_status_xml(doc)
|
||||
|
||||
if request_status != CMS_STATUS_SUCCESS:
|
||||
response = parse_error_template_xml(doc)
|
||||
return response
|
||||
|
||||
response = {}
|
||||
response['request_status'] = request_status
|
||||
|
||||
crl_issuing_point = doc.xpath('//xml/header/crlIssuingPoint[1]')
|
||||
if len(crl_issuing_point) == 1:
|
||||
crl_issuing_point = etree.tostring(
|
||||
crl_issuing_point[0], method='text',
|
||||
encoding=unicode).strip()
|
||||
response['crl_issuing_point'] = crl_issuing_point
|
||||
|
||||
crl_update = doc.xpath('//xml/header/crlUpdate[1]')
|
||||
if len(crl_update) == 1:
|
||||
crl_update = etree.tostring(crl_update[0], method='text',
|
||||
encoding=unicode).strip()
|
||||
response['crl_update'] = crl_update
|
||||
|
||||
return response
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
from ipalib import Registry, errors, SkipPluginModule
|
||||
@ -1923,6 +1984,47 @@ class ra(rabase.rabase, RestClient):
|
||||
|
||||
return results
|
||||
|
||||
def updateCRL(self, wait='false'):
|
||||
"""
|
||||
Force update of the CRL
|
||||
|
||||
:param wait: if true, the call will be synchronous and return only
|
||||
when the CRL has been generated
|
||||
"""
|
||||
logger.debug('%s.updateCRL()', type(self).__name__)
|
||||
# Call CMS
|
||||
http_status, _http_headers, http_body = (
|
||||
self._sslget('/ca/agent/ca/updateCRL',
|
||||
self.override_port or self.env.ca_agent_port,
|
||||
crlIssuingPoint='MasterCRL',
|
||||
waitForUpdate=wait,
|
||||
xml='true')
|
||||
)
|
||||
|
||||
# Parse and handle errors
|
||||
if http_status != 200:
|
||||
self.raise_certificate_operation_error('updateCRL',
|
||||
detail=http_status)
|
||||
|
||||
parse_result = self.get_parse_result_xml(http_body,
|
||||
parse_updateCRL_xml)
|
||||
request_status = parse_result['request_status']
|
||||
if request_status != CMS_STATUS_SUCCESS:
|
||||
self.raise_certificate_operation_error(
|
||||
'updateCRL',
|
||||
cms_request_status_to_string(request_status),
|
||||
parse_result.get('error_string'))
|
||||
|
||||
# Return command result
|
||||
cmd_result = {}
|
||||
|
||||
if 'crl_issuing_point' in parse_result:
|
||||
cmd_result['crlIssuingPoint'] = parse_result['crl_issuing_point']
|
||||
if 'crl_update' in parse_result:
|
||||
cmd_result['crlUpdate'] = parse_result['crl_update']
|
||||
|
||||
return cmd_result
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
@register()
|
||||
|
@ -123,3 +123,12 @@ class rabase(Backend):
|
||||
:param options: dictionary of search options
|
||||
"""
|
||||
raise errors.NotImplementedError(name='%s.find' % self.name)
|
||||
|
||||
def updateCRL(self, wait='false'):
|
||||
"""
|
||||
Force update of the CRL
|
||||
|
||||
:param wait: if true, the call will be synchronous and return only
|
||||
when the CRL has been generated
|
||||
"""
|
||||
raise errors.NotImplementedError(name='%s.updateCRL' % self.name)
|
||||
|
Loading…
Reference in New Issue
Block a user