>From 7c207b246666dcd1b1d30802d8ee83e8316bd399 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Mon, 27 Oct 2014 09:37:48 -0400
Subject: [PATCH 238/238] Updates to some python client classes for prettier
 API docs.

Added missing .rst annotations and missing docstrings.
---
 base/common/python/pki/__init__.py   | 145 +++++++++++++++++++++++++++++++----
 base/common/python/pki/account.py    |  30 ++++++++
 base/common/python/pki/client.py     |  99 ++++++++++++++++++++++++
 base/common/python/pki/profile.py    |  36 ++++-----
 base/common/python/pki/system.py     | 105 +++++++++++++++++++++++++
 base/common/python/pki/systemcert.py |   6 +-
 6 files changed, 385 insertions(+), 36 deletions(-)

diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 4ca1e92e0ccc372d7845363a8a5614bb1b9623cf..62d87a01c454878d5d0c1dc14b5f5e143326b4d8 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 0916ec7cccf25357b75161d08fea32626fdf9486..c8c7cd78b662e69ebb4294dbc3241d71cfa22071 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 5a17fd890da9bdade5a352c1af5492647b6d7380..90939930a7e2526122f5184f3d47dde2e86a0300 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 4addee618d014a5929263df0e70b0b9a57128e0f..42efec1c693dedfb43e4933f4aa7fdd27b72705c 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 a4b5c2feea265ebe49a6ba7ebacdb3c4d2e86405..1cc2a7a5a2fbf0527643f89e461ed427c6303ac6 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 6986ba0720b5389db8b6871a07500da8c82b5515..d59e07b3dae1633b655af8fa790f98f4e2d20226 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())
-- 
1.9.3

