diff options
author | Ade Lee <alee@redhat.com> | 2014-10-27 09:37:48 -0400 |
---|---|---|
committer | Ade Lee <alee@redhat.com> | 2014-10-27 14:26:39 -0400 |
commit | 8d8e33bc282d8ceae4ea9da4546c9c7d25984fd9 (patch) | |
tree | 8332a9230ba521b80b02572c5c893f8db67247b6 /base/common/python | |
parent | ae9494b4af8e4b9f39e7d478b1f95f5d40f55add (diff) | |
download | pki-8d8e33bc282d8ceae4ea9da4546c9c7d25984fd9.tar.gz pki-8d8e33bc282d8ceae4ea9da4546c9c7d25984fd9.tar.xz pki-8d8e33bc282d8ceae4ea9da4546c9c7d25984fd9.zip |
Updates to some python client classes for prettier API docs.
Added missing .rst annotations and missing docstrings.
Added log file for sphinx runs.
Diffstat (limited to 'base/common/python')
-rw-r--r-- | base/common/python/docs/CMakeLists.txt | 8 | ||||
-rw-r--r-- | base/common/python/docs/conf.py.in | 6 | ||||
-rw-r--r-- | base/common/python/docs/index.rst | 4 | ||||
-rw-r--r-- | base/common/python/pki/__init__.py | 145 | ||||
-rw-r--r-- | base/common/python/pki/account.py | 30 | ||||
-rw-r--r-- | base/common/python/pki/client.py | 99 | ||||
-rw-r--r-- | base/common/python/pki/profile.py | 36 | ||||
-rw-r--r-- | base/common/python/pki/system.py | 105 | ||||
-rw-r--r-- | base/common/python/pki/systemcert.py | 6 |
9 files changed, 398 insertions, 41 deletions
diff --git a/base/common/python/docs/CMakeLists.txt b/base/common/python/docs/CMakeLists.txt index 51e8c287a..86bb590f3 100644 --- a/base/common/python/docs/CMakeLists.txt +++ b/base/common/python/docs/CMakeLists.txt @@ -20,6 +20,12 @@ set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../../../../base/common/html # MAN output directory set(SPHINX_MAN_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../../../../base/common/man/man1") +# HTML log file +set(SPHINX_HTML_LOG_FILE "${CMAKE_CURRENT_BINARY_DIR}/../../../../../../../pki-common-sphinx-http.log") + +# MAN log file +set(SPHINX_MAN_LOG_FILE "${CMAKE_CURRENT_BINARY_DIR}/../../../../../../../pki-common-sphinx-man.log") + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" "${BINARY_BUILD_DIR}/conf.py" @@ -30,6 +36,7 @@ add_custom_target(dogtag_python_client_docs ALL -b html -c "${BINARY_BUILD_DIR}" -d "${SPHINX_CACHE_DIR}" + -w "${SPHINX_HTML_LOG_FILE}" -a "${CMAKE_CURRENT_SOURCE_DIR}" "${SPHINX_HTML_DIR}" @@ -40,6 +47,7 @@ add_custom_target(dogtag_python_client_man_docs ALL -b man -c "${BINARY_BUILD_DIR}" -d "${SPHINX_CACHE_DIR}" + -w "${SPHINX_MAN_LOG_FILE}" -a "${CMAKE_CURRENT_SOURCE_DIR}" "${SPHINX_MAN_DIR}" diff --git a/base/common/python/docs/conf.py.in b/base/common/python/docs/conf.py.in index eca3b1a0b..d6f55e465 100644 --- a/base/common/python/docs/conf.py.in +++ b/base/common/python/docs/conf.py.in @@ -183,7 +183,7 @@ latex_elements = { # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'dogtag.tex', u'Dogtag Certificate Server Python Client Documentation', + ('index', 'dogtag.tex', u'Dogtag Python Client API', u'Author', 'manual'), ] @@ -213,7 +213,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pki-python-client', u'Dogtag Python Client Documentation', + ('index', 'pki-python-client', u'Dogtag Python Client API', [u'Dogtag PKI Project Team'], 1) ] @@ -227,7 +227,7 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Dogtag', u'Dogtag Certificate Server Python Client Documentation', + ('index', 'Dogtag', u'Dogtag Python Client API', u'Author', 'Dogtag PKI Team', 'Dogtag is an enterprise software system designed to manage enterprise Public Key Infrastructure (PKI) deployments.', 'Miscellaneous'), ] diff --git a/base/common/python/docs/index.rst b/base/common/python/docs/index.rst index 8422a78bd..eca286ac9 100644 --- a/base/common/python/docs/index.rst +++ b/base/common/python/docs/index.rst @@ -1,5 +1,5 @@ -Welcome to Dogtag's Python Client documentation! -================================================ +Dogtag Python Client API +======================== Dogtag is an enterprise software system designed to manage enterprise Public Key Infrastructure (PKI) deployments. diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py index 4ca1e92e0..62d87a01c 100644 --- a/base/common/python/pki/__init__.py +++ b/base/common/python/pki/__init__.py @@ -40,7 +40,24 @@ CERT_FOOTER = "-----END CERTIFICATE-----" def read_text(message, options=None, default=None, delimiter=':', allow_empty=True, case_sensitive=True): - """ get an input from the user. """ + """ + Get an input from the user. This is used, for example, in + pkispawn and pkidestroy to obtain user input. + + :param message: prompt to display to the user + :type message: str + :param options: list of possible inputs by the user. + :type options: list + :param default: default value of parameter being prompted. + :type default: str + :param delimiter: delimiter to be used at the end of the prompt. + :type delimiter: str + :param allow_empty: Allow input to be empty. + :type allow_empty: boolean -- True/False + :param case_sensitive: Allow input to be case sensitive. + :type case_sensitive: boolean -- True/False + :returns: str -- value obtained from user input. + """ if default: message = message + ' [' + default + ']' message = message + delimiter + ' ' @@ -74,7 +91,11 @@ def read_text(message, def implementation_version(): - """ Return implementation version """ + """ + Return implementation version. + + :returns: str --implementation version + """ with open(PACKAGE_VERSION, 'r') as input_file: for line in input_file: line = line.strip('\n') @@ -137,12 +158,26 @@ class ResourceMessage(object): self.ClassName = class_name def add_attribute(self, name, value): - """ Add an attribute to the list. """ + """ + Add an attribute to the list. + + :param name: name of attribute to add + :type name: str + :param value: value to add + :type value: str + :returns: None + """ attr = Attribute(name, value) self.Attributes.Attribute.append(attr) def get_attribute_value(self, name): - """ Get the value of a given attribute """ + """ + Get the value of a given attribute. + + :param name: name of attribute to retrieve + :type name: str + :returns: str -- value of parameter + """ for attr in self.Attributes.Attribute: if attr.name == name: return attr.value @@ -164,7 +199,12 @@ class PKIException(Exception, ResourceMessage): @classmethod def from_json(cls, json_value): - """ Construct exception from JSON """ + """ + Construct PKIException from JSON. + :param json_value: JSON representation of the exception. + :type json_value: str + :return: pki.PKIException + """ ret = cls(json_value['Message'], json_value['Code'], json_value['ClassName']) for attr in json_value['Attributes']['Attribute']: @@ -220,7 +260,9 @@ class RequestNotFoundException(ResourceNotFoundException): class UserNotFoundException(ResourceNotFoundException): """ User Not Found Exception: return code = 404 """ - +""" +Mapping from Java Server exception classes to python exception classes +""" EXCEPTION_MAPPINGS = { "com.netscape.certsrv.base.BadRequestException": BadRequestException, "com.netscape.certsrv.base.ConflictingOperationException": @@ -267,7 +309,13 @@ def handle_exceptions(): class PropertyFile(object): - """ Class to manage property files """ + """ + Class to manage property files The contents of the property file + are maintained internally as a list of properties. + + Properties are strings of the format <name> <delimiter> <value> where + '=' is the default delimiter. + """ def __init__(self, filename, delimiter='='): """ Constructor """ @@ -277,7 +325,12 @@ class PropertyFile(object): self.lines = [] def read(self): - """ Read from property file """ + """ + Read from property file into the list of properties + maintained by this object. + + :return: None + """ self.lines = [] if not os.path.exists(self.filename): @@ -290,27 +343,58 @@ class PropertyFile(object): self.lines.append(line) def write(self): - """ Write to property file """ + """ + Write the list of properties maintained by this object + to the property file. + + :return: None + """ # write all lines in the original order with open(self.filename, 'w') as f_out: for line in self.lines: f_out.write(line + '\n') def show(self): - """ Show contents of property file.""" + """ + Print the contents of the list of properties maintained by this object + to STDOUT. + + :return: None + """ for line in self.lines: print line def insert_line(self, index, line): - """ Insert line in property file """ + """ + Insert property into the list of properties maintained by this object + at the specified location (index). + + :param index: point at which to insert value. + :type index: int + :param line: value to be inserted. + :type line; str + :return: None + """ self.lines.insert(index, line) def remove_line(self, index): - """ Remove line from property file """ + """ + Remove property at specified index from the properties list. + + :param index: location of property to be removed. + :type index: int + :return: None + """ self.lines.pop(index) def index(self, name): - """ Find the index (position) of a property in a property file """ + """ + Find the index (position) of a property in a property file. + + :param name: name of property + :type name: str + :return: int -- index of property. + """ for i, line in enumerate(self.lines): # parse <key> <delimiter> <value> @@ -328,7 +412,13 @@ class PropertyFile(object): return -1 def get(self, name): - """ Get value for specified property """ + """ + Get the value of the specified property. + + :param name: name of property to be fetched. + :type name: str + :return: str -- value of property + """ result = None for line in self.lines: @@ -349,7 +439,17 @@ class PropertyFile(object): return result def set(self, name, value, index=None): - """ Set value for specified property """ + """ + Set value of specified property. + + :param name: name of property to set. + :type name: str + :param value: value to set + :type value: str + :param index: (optional) position of property + :type index: int + :return: None + """ for i, line in enumerate(self.lines): # parse <key> <delimiter> <value> @@ -372,7 +472,13 @@ class PropertyFile(object): self.insert_line(index, name + self.delimiter + value) def remove(self, name): - """ Remove property from property file """ + """ + Remove property from list of properties maintained by this object. + + :param name: name of property to be removed. + :type name: str + :returns: None + """ for i, line in enumerate(self.lines): # parse <key> <delimiter> <value> @@ -403,6 +509,13 @@ class Link(object): @classmethod def from_json(cls, attr_list): + """ + Generate Link from JSON + + :param attr_list: JSON representation of Link + :type attr_list: str + :return: pki.Link + """ if attr_list is None: return None diff --git a/base/common/python/pki/account.py b/base/common/python/pki/account.py index 0916ec7cc..c8c7cd78b 100644 --- a/base/common/python/pki/account.py +++ b/base/common/python/pki/account.py @@ -22,14 +22,44 @@ import pki class AccountClient: + """ + Class used to associate an authentication session variable with a + connection. + + To use this class: + * set the authentication credentials with the connection, + * create an AccountClient and then call login(). + * further operations in this session will use the same authentication + credentials without re-authentication. + * call logout() to invalidate the session. + """ def __init__(self, connection): + """ + Creates an AccountClient for the connection. + + :param connection: connection to be associated with the AccountClient + :type connection: pki.PKIConnection + :returns: AccountClient + """ self.connection = connection @pki.handle_exceptions() def login(self): + """ + Login to account REST interface. If login is successful, + an authentication session variable is associated with the connection. + + :returns: None + """ self.connection.get('/rest/account/login') @pki.handle_exceptions() def logout(self): + """ + Logs out of the session. Authentication session variables are + invalidated for the connection + + :returns: None + """ self.connection.get('/rest/account/logout') diff --git a/base/common/python/pki/client.py b/base/common/python/pki/client.py index 5a17fd890..90939930a 100644 --- a/base/common/python/pki/client.py +++ b/base/common/python/pki/client.py @@ -23,9 +23,29 @@ import requests class PKIConnection: + """ + Class to encapsulate the connection between the client and a Dogtag + subsystem. + """ def __init__(self, protocol='http', hostname='localhost', port='8080', subsystem='ca', accept='application/json'): + """ + Set the parameters for a python-requests based connection to a + Dogtag subsystem. + :param protocol: http or https + :type protocol: str + :param hostname: hostname of server + :type hostname: str + :param port: port of server + :type port: str + :param subsystem: ca, kra, ocsp, tks or tps + :type subsystem: str + :param accept: value of accept header. Supported values are usually + 'application/json' or 'application/xml' + :type accept: str + :return: PKIConnection object. + """ self.protocol = protocol self.hostname = hostname @@ -41,10 +61,30 @@ class PKIConnection: self.session.headers.update({'Accept': accept}) def authenticate(self, username=None, password=None): + """ + Set the parameters used for authentication if username/password is to + be used. Both username and password must not be None. + Note that this method only sets the parameters. Actual authentication + occurs when the connection is attempted, + + :param username: username to authenticate connection + :param password: password to authenticate connection + :return: None + """ if username is not None and password is not None: self.session.auth = (username, password) def set_authentication_cert(self, pem_cert_path): + """ + Set the path to the PEM file containing the certificate and private key + for the client certificate to be used for authentication to the server, + when client certificate authentication is required. + + :param pem_cert_path: path to the PEM file + :type pem_cert_path: str + :return: None + :raises: Exception if path is empty or None. + """ if pem_cert_path is None: raise Exception("No path for the certificate specified.") if len(str(pem_cert_path)) == 0: @@ -52,6 +92,21 @@ class PKIConnection: self.session.cert = pem_cert_path def get(self, path, headers=None, params=None, payload=None): + """ + Uses python-requests to issue a GET request to the server. + + :param path: path URI for the GET request + :type path: str + :param headers: headers for the GET request + :type headers: dict + :param params: Query parameters for the GET request + :type params: dict or bytes + :param payload: data to be sent in the body of the request + :type payload: dict, bytes, file-like object + :returns: request.response -- response from the server + :raises: Exception from python-requests in case the GET was not + successful, or returns an error code. + """ r = self.session.get( self.serverURI + path, verify=False, @@ -62,6 +117,21 @@ class PKIConnection: return r def post(self, path, payload, headers=None, params=None): + """ + Uses python-requests to issue a POST request to the server. + + :param path: path URI for the POST request + :type path: str + :param payload: data to be sent in the body of the request + :type payload: dict, bytes, file-like object + :param headers: headers for the POST request + :type headers: dict + :param params: Query parameters for the POST request + :type params: dict or bytes + :returns: request.response -- response from the server + :raises: Exception from python-requests in case the POST was not + successful, or returns an error code. + """ r = self.session.post( self.serverURI + path, verify=False, @@ -72,16 +142,45 @@ class PKIConnection: return r def put(self, path, payload, headers=None): + """ + Uses python-requests to issue a PUT request to the server. + + :param path: path URI for the PUT request + :type path: str + :param payload: data to be sent in the body of the request + :type payload: dict, bytes, file-like object + :param headers: headers for the PUT request + :type headers: dict + :returns: request.response -- response from the server + :raises: Exception from python-requests in case the PUT was not + successful, or returns an error code. + """ r = self.session.put(self.serverURI + path, payload, headers=headers) r.raise_for_status() return r def delete(self, path, headers=None): + """ + Uses python-requests to issue a DEL request to the server. + + :param path: path URI for the DEL request + :type path: str + :param headers: headers for the DEL request + :type headers: dict + :returns: request.response -- response from the server + :raises: Exception from python-requests in case the DEL was not + successful, or returns an error code. + """ r = self.session.delete(self.serverURI + path, headers=headers) r.raise_for_status() return r + def main(): + """ + Test code for the PKIConnection class. + :return: None + """ conn = PKIConnection() headers = {'Content-type': 'application/json', 'Accept': 'application/json'} diff --git a/base/common/python/pki/profile.py b/base/common/python/pki/profile.py index 4addee618..42efec1c6 100644 --- a/base/common/python/pki/profile.py +++ b/base/common/python/pki/profile.py @@ -1,23 +1,21 @@ #!/usr/bin/python -""" - 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. - - @author: Abhishek Koneru <akoneru@redhat.com> -""" +# 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. +# +# @author: Abhishek Koneru <akoneru@redhat.com> import json import os diff --git a/base/common/python/pki/system.py b/base/common/python/pki/system.py index a4b5c2fee..1cc2a7a5a 100644 --- a/base/common/python/pki/system.py +++ b/base/common/python/pki/system.py @@ -29,6 +29,10 @@ if os.path.exists("/etc/debian_version"): class SecurityDomainHost(object): + """ + Class representing a security domain host. + """ + def __init__(self): """ constructor """ self.id = None @@ -44,6 +48,13 @@ class SecurityDomainHost(object): @classmethod def from_json(cls, json_value): + """ + Constructs a SecurityDomainHost object from JSON. + + :param json_value: JSON string representing a security domain host. + :type json_value: str + :returns: SecurityDomainHost + """ host = cls() host.admin_port = json_value['SecureAdminPort'] host.agent_port = json_value['SecureAgentPort'] @@ -59,12 +70,25 @@ class SecurityDomainHost(object): class SecurityDomainSubsystem(object): + """ + Class representing a security domain subsystem. + This is essentially a list of SecurityDomainHost objects of a + particular subsystem type (ca, kra, tps, tks, ocsp). + """ + def __init__(self): self.name = None self.hosts = {} @classmethod def from_json(cls, json_value): + """ + Constructs a SecurityDomainSubsystem from a JSON representation. + + :param json_value: JSON representation of the Security Domain Subsystem + :type json_value: str + :returns: SecurityDomainSubsystem + """ ret = cls() ret.name = json_value['id'] for host in json_value['Host']: @@ -73,12 +97,24 @@ class SecurityDomainSubsystem(object): class SecurityDomainInfo(object): + """ + Class representing the entire security domain. + This is essentially a list of SecurityDomainSubsystem components. + """ + def __init__(self): self.name = None self.systems = {} @classmethod def from_json(cls, json_value): + """ + Create a SecurityDomainInfo object from JSON. + + :param json_value: JSON representation of a security domain. + :type json_value: str + :returns: SecurityDomainInfo + """ ret = cls() ret.name = json_value['id'] for slist in json_value['Subsystem']: @@ -88,15 +124,36 @@ class SecurityDomainInfo(object): class SecurityDomainClient(object): + """ + Client used to get the security domain from a security domain CA. + The connection details for the security domain CA are specified in + a PKIConnection object used to construct this client. + """ + def __init__(self, connection): self.connection = connection def get_security_domain_info(self): + """ + Contact the security domain CA specified in the connection object + used to construct this client and get the security domain using the + REST API. + + :returns: pki.system.SecurityDomainInfo + """ response = self.connection.get('/rest/securityDomain/domainInfo') info = SecurityDomainInfo.from_json(response.json()) return info def get_old_security_domain_info(self): + """ + Contact the security domain CA specified in the connection object + used to construct this client and get the security domain using the + old servlet-based interface. This method is useful when contacting + old servers which do not provide the REST API. + + :returns: pki.system.SecurityDomainInfo + """ response = self.connection.get('/admin/ca/getDomainXML') root = ETree.fromstring(response.text) domaininfo = ETree.fromstring(root.find("DomainInfo").text) @@ -106,6 +163,13 @@ class SecurityDomainClient(object): class ConfigurationRequest(object): + """ + Class used to represent a configuration request to be submitted to the + Java installation servlet during the execution of pkispawn. + + This class is the python equivalent of the Java class: + com.netscape.certsrv.system.ConfigurationRequest + """ def __init__(self): self.token = "Internal Key Storage Token" self.isClone = "false" @@ -115,20 +179,51 @@ class ConfigurationRequest(object): class ConfigurationResponse(object): + """ + Class used to represent the response from the Java configuration + servlet during the execution of pkispawn. + + This class is the python equivalent of the Java class: + com.netscape.certsrv.system.ConfigurationRequest + """ def __init__(self): pass class SystemCertData(object): + """ + Class used to represent the data for a system certificate, which is + used in the data passed into and returned from the Java installation + servlet during the execution of pkispawn. + + This class is the python equivalent of the Java class: + com.netscape.certsrv.system.SystemCertData + """ def __init__(self): pass class SystemConfigClient(object): + """ + Client used to interact with the Java configuration servlet to configure + a Dogtag subsystem during the execution of pkispawn. + + The connection details for the system being configured are passed in + the PKIConnection object used when constructing this object. + """ def __init__(self, connection): self.connection = connection def configure(self, data): + """ + Contacts the server and invokes the Java configuration REST API to + configure a Dogtag subsystem. + + :param data: Configuration request containing all the input needed to + configure the subsystem + :type data: ConfigurationRequest + :return: ConfigurationResponse -- response from configuration servlet. + """ headers = {'Content-type': 'application/json', 'Accept': 'application/json'} response = self.connection.post('/rest/installer/configure', data, @@ -137,10 +232,20 @@ class SystemConfigClient(object): class SystemStatusClient(object): + """ + Client used to check the status of a Dogtag subsystem. + """ def __init__(self, connection): self.connection = connection def get_status(self): + """ + Checks the status of the subsystem by calling the getStatus() + servlet. This is used to determine if the server is up and ready to + receive and process requests. + + :return: str - getStatus response + """ response = self.connection.get('/admin/' + self.connection.subsystem + '/getStatus') return response.text diff --git a/base/common/python/pki/systemcert.py b/base/common/python/pki/systemcert.py index 6986ba072..d59e07b3d 100644 --- a/base/common/python/pki/systemcert.py +++ b/base/common/python/pki/systemcert.py @@ -43,7 +43,11 @@ class SystemCertClient(object): @pki.handle_exceptions() def get_transport_cert(self): - """ Return transport certificate """ + """ + Return transport certificate. + + :return: pki.cert.CertData -- transport certificate data + """ url = self.cert_url + '/transport' response = self.connection.get(url, self.headers) cert_data = CertData.from_json(response.json()) |