summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2015-04-30 04:55:29 -0400
committerJan Cholasta <jcholast@redhat.com>2015-06-04 08:27:33 +0000
commit300b74fc7fb2a5ce540b2d21189794a5b2db88b1 (patch)
treecfa27d8e5ce6eec66e0c47e38ab6813fef6f6d43 /ipaserver
parent35af0d6d66e623012755acca44bd77186067d156 (diff)
downloadfreeipa-300b74fc7fb2a5ce540b2d21189794a5b2db88b1.tar.gz
freeipa-300b74fc7fb2a5ce540b2d21189794a5b2db88b1.tar.xz
freeipa-300b74fc7fb2a5ce540b2d21189794a5b2db88b1.zip
Add certprofile plugin
Add the 'certprofile' plugin which defines the commands for managing certificate profiles and associated permissions. Also update Dogtag network code in 'ipapython.dogtag' to support headers and arbitrary request bodies, to facilitate use of the Dogtag profiles REST API. Part of: https://fedorahosted.org/freeipa/ticket/57 Reviewed-By: Martin Basti <mbasti@redhat.com>
Diffstat (limited to 'ipaserver')
-rw-r--r--ipaserver/plugins/dogtag.py176
1 files changed, 172 insertions, 4 deletions
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 52bdb0d42..9654123b1 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -4,8 +4,9 @@
# Jason Gerard DeRose <jderose@redhat.com>
# Rob Crittenden <rcritten@@redhat.com>
# John Dennis <jdennis@redhat.com>
+# Fraser Tweedale <ftweedal@redhat.com>
#
-# Copyright (C) 2014 Red Hat
+# Copyright (C) 2014, 2015 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
@@ -238,17 +239,21 @@ digits and nothing else follows.
'''
import datetime
+import json
from lxml import etree
+import os
import tempfile
import time
import urllib2
+import pki
from pki.client import PKIConnection
import pki.crypto as cryptoutil
from pki.kra import KRAClient
from ipalib import Backend
from ipapython.dn import DN
+import ipapython.cookie
import ipapython.dogtag
from ipapython import ipautil
from ipaserver.install.certs import CertDB
@@ -1262,13 +1267,12 @@ def select_any_master(ldap2, service='CA'):
#-------------------------------------------------------------------------------
-from ipalib import api, SkipPluginModule
+from ipalib import api, errors, SkipPluginModule
if api.env.ra_plugin != 'dogtag':
# In this case, abort loading this plugin module...
raise SkipPluginModule(reason='dogtag not selected as RA plugin')
import os, random
from ipaserver.plugins import rabase
-from ipalib.errors import CertificateOperationError
from ipalib.constants import TYPE_ERROR
from ipalib.util import cachedproperty
from ipapython import dogtag
@@ -1318,7 +1322,7 @@ class ra(rabase.rabase):
err_msg = u'%s (%s)' % (err_msg, detail)
self.error('%s.%s(): %s', self.fullname, func_name, err_msg)
- raise CertificateOperationError(error=err_msg)
+ raise errors.CertificateOperationError(error=err_msg)
@cachedproperty
def ca_host(self):
@@ -1923,3 +1927,167 @@ class kra(Backend):
return KRAClient(connection, crypto)
api.register(kra)
+
+
+class RestClient(Backend):
+ """Simple Dogtag REST client to be subclassed by other backends.
+
+ This class is a context manager. Authenticated calls must be
+ executed in a ``with`` suite::
+
+ class ra_certprofile(RestClient):
+ path = 'profile'
+ ...
+
+ api.register(ra_certprofile)
+
+ with api.Backend.ra_certprofile as profile_api:
+ # REST client is now logged in
+ profile_api.create_profile(...)
+
+ """
+ path = None
+
+ @staticmethod
+ def _parse_dogtag_error(body):
+ try:
+ return pki.PKIException.from_json(json.loads(body))
+ except:
+ return None
+
+ def __init__(self):
+ if api.env.in_tree:
+ self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
+ self.pwd_file = self.sec_dir + os.sep + '.pwd'
+ else:
+ self.sec_dir = paths.HTTPD_ALIAS_DIR
+ self.pwd_file = paths.ALIAS_PWDFILE_TXT
+ self.noise_file = self.sec_dir + os.sep + '.noise'
+ self.ipa_key_size = "2048"
+ self.ipa_certificate_nickname = "ipaCert"
+ self.ca_certificate_nickname = "caCert"
+ try:
+ f = open(self.pwd_file, "r")
+ self.password = f.readline().strip()
+ f.close()
+ except IOError:
+ self.password = ''
+ super(RestClient, self).__init__()
+
+ # session cookie
+ self.cookie = None
+
+ @cachedproperty
+ def ca_host(self):
+ """
+ :return: host
+ as str
+
+ Select our CA host.
+ """
+ ldap2 = self.api.Backend.ldap2
+ if host_has_service(api.env.ca_host, ldap2, "CA"):
+ return api.env.ca_host
+ if api.env.host != api.env.ca_host:
+ if host_has_service(api.env.host, ldap2, "CA"):
+ return api.env.host
+ host = select_any_master(ldap2)
+ if host:
+ return host
+ else:
+ return api.env.ca_host
+
+ def __enter__(self):
+ """Log into the REST API"""
+ if self.cookie is not None:
+ return
+ status, status_text, resp_headers, resp_body = dogtag.https_request(
+ self.ca_host, self.env.ca_agent_port, '/ca/rest/account/login',
+ self.sec_dir, self.password, self.ipa_certificate_nickname,
+ method='GET'
+ )
+ cookies = ipapython.cookie.Cookie.parse(resp_headers.get('set-cookie', ''))
+ if status != 200 or len(cookies) == 0:
+ raise errors.RemoteRetrieveError(reason=_('Failed to authenticate to CA REST API'))
+ self.cookie = str(cookies[0])
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """Log out of the REST API"""
+ dogtag.https_request(
+ self.ca_host, self.env.ca_agent_port, '/ca/rest/account/logout',
+ self.sec_dir, self.password, self.ipa_certificate_nickname,
+ method='GET'
+ )
+ self.cookie = None
+
+ def _ssldo(self, method, path, headers=None, body=None):
+ """
+ :param url: The URL to post to.
+ :param kw: Keyword arguments to encode into POST body.
+ :return: (http_status, http_reason_phrase, http_headers, http_body)
+ as (integer, unicode, dict, str)
+
+ Perform an HTTPS request
+ """
+ if self.cookie is None:
+ raise errors.RemoteRetrieveError(
+ reason=_("REST API is not logged in."))
+
+ headers = headers or {}
+ headers['Cookie'] = self.cookie
+
+ resource = os.path.join('/ca/rest', self.path, path)
+
+ # perform main request
+ status, status_text, resp_headers, resp_body = dogtag.https_request(
+ self.ca_host, self.env.ca_agent_port, resource,
+ self.sec_dir, self.password, self.ipa_certificate_nickname,
+ method=method, headers=headers, body=body
+ )
+ if status < 200 or status >= 300:
+ explanation = self._parse_dogtag_error(resp_body) or ''
+ raise errors.RemoteRetrieveError(
+ reason=_('Non-2xx response from CA REST API: %(status)d %(status_text)s. %(explanation)s')
+ % {'status': status, 'status_text': status_text, 'explanation': explanation}
+ )
+ return (status, status_text, resp_headers, resp_body)
+
+
+class ra_certprofile(RestClient):
+ """
+ Profile management backend plugin.
+ """
+ path = 'profiles'
+
+ def create_profile(self, profile_data):
+ """
+ Import the profile into Dogtag
+ """
+ self._ssldo('POST', 'raw',
+ headers={
+ 'Content-type': 'application/xml',
+ 'Accept': 'application/json',
+ },
+ body=profile_data
+ )
+
+ def enable_profile(self, profile_id):
+ """
+ Enable the profile in Dogtag
+ """
+ self._ssldo('POST', profile_id + '?action=enable')
+
+ def disable_profile(self, profile_id):
+ """
+ Enable the profile in Dogtag
+ """
+ self._ssldo('POST', profile_id + '?action=disable')
+
+ def delete_profile(self, profile_id):
+ """
+ Delete the profile from Dogtag
+ """
+ self._ssldo('DELETE', profile_id, headers={'Accept': 'application/json'})
+
+api.register(ra_certprofile)