[Pki-devel] [PATCH] 95 Fixes for comments for patches 92-2, 93, 94(CertClient and ProfileClient python implementations)

Fraser Tweedale ftweedal at redhat.com
Fri May 30 06:38:46 UTC 2014


On Thu, May 29, 2014 at 04:03:14PM -0400, Abhishek Koneru wrote:
> Please review the patch which addresses the review comments given by Ade
> and Fraser for patches 92-2, 93, 94.
> 
> 92-2 has been checked in already. 94 has been rebased to fix conflicts
> with Ade's checkins. I removed the changes to account.py and client.py
> form 94 to fix the conflicts. 
> 
> So update the master and apply patches 93, 94, 95.
> 
> Following are the review comments addressed in this patch -
> 
> ** No. 4 in the list is not included in this patch. It will be done in a
> separate patch.
> 
> 1) Copy pasta error in ProfileData.from_json; ``policy_sets`` used
> instead of ``inputs``/``outputs`` when processing
> json_value['Input'] or json_value['Output'] lists.
> 
> 2) Could you please make ProfileDataInfoCollection an iterable?  It
> doesn't make sense to be to have to access the ``profile_data_list``
> attribute just to iterate the ProfileDataInfo objects.
> 

Using generators to implement ``__iter__`` is a fine solution (i.e.
not a show-stopper IMO) but more terse and idiomatic would be::

  def __iter__(self):
   return iter(self._internal_iterable)

Nothing else stood out to me on eyeballing the new patch but I still
have to test it.

Fraser

> 3) Could you please add a ``repr`` method to ProfileData that
> includes at least the profile_id?  Maybe summary information about
> whether it is visible/active as well, but I think just the ID should
> be fine.  Same goes for other classes that show up in lists, please.
> 
> 4) I'm a little concerned about having the properties set/get
> top-level attributes, e.g. ``enabled_by`` sets/gets ``enabledBy``.
> Following this pattern where you wish to use the same name as in the
> JSON would result in infinite loop; indeed you do treat some keys
> different to avoid this, e.g. ``description``.  This inconsistency
> makes me wonder if there's a better pattern.
> 
> My suggestion would be to stash the JSON dict in a top-level
> attribute (the constructor would have to initialise it to an empty
> dict before other attributes are assigned) and then have the
> properties set/get items in that dict.
> 
> You could further cut down boilerplate and duplication by definite a
> descriptor for this purpose, e.g.:
> 
>     class JSONProperty(object):
>         def __init__(self, attr):
>             self.attr
> 
>         def __get__(self, instance, owner)
>             return obj.json_value[self.attr]
> 
>         def __set__(self, instance, value)
>             obj.json_value[self.attr] = value
> 
> Then, most of the assignments in ``from_json`` go away , and
> the corresponding property declarations follow the new pattern:
> 
>     enabled_by = JSONProperty('enabledBy')
> 
> You would still need to treat Input, Output and PolicySets
> differently, but you could also abstract over this pattern with yet
> another class, i.e. a "JSON list property".
> 
> Anyhow, 4) isn't a show-stopper, just a (lengthy) nit-pick.
> 
> *** Will fix this in a separate patch.
> 
> 5. Here you could iterate the (key, value) pairs to save keystrokes and
> a bit of CPU doing ``cert_search_params[param]`` in all those places
> below.  e.g.:
> 
>     for param, value in cert_search_params.viewitems():
>         ...
> 
> ``dict.viewitems()`` returns a *dictview* iterator-like object that
> avoids creating an intermediate list as ``dict.items()`` would.
> 
> 6. Here and in a few other places below it might improve readability to
> test for set membership, e.g.:
> 
>     if param in {
>         'email', 'common_name', 'user'_id', 'org_unit',
>         'org', ...
>     }:
> 
> 7. pycharm appears to be set to 120 columns width by default.  We need
> to set to 80 and reformat accordingly.  Please check in a pycharm
> settings file.  All new code should follow PEP8.
> 
> 8. In CertRevokeRequest, a number of constants are defined for possible
> reason settings.  You should group those, and test for invalid reasons.
> 
> 9.  Do we use CertID anywhere? --- Removed CertID
> 
> 10. list_entrollment_templates has a print r statement in it.  Is that
> supposed to be there?
> 
> 11.  get_enrollment_template should check for None for the profileID.
> 
> 12.  CertRequestInfoCollection has an element - cert_info_list, should
> be
> cert_request_info_list
> 
> 13.  ProfileConstraint -- why is id renamed to name?  Why not just use
> "id"
> -- pycharm and pylint throw a warning when using id as an attribute name
> 
> -- Abhishek
> 

> >From d3c10a6ff46bfc2f8afea849c2d3a297e47e305d Mon Sep 17 00:00:00 2001
> From: Abhishek Koneru <akoneru at redhat.com>
> Date: Fri, 9 May 2014 10:16:44 -0400
> Subject: [PATCH] Added methods in CertClient for CertRequestResource
> 
> Adds the methods for fetching the enrollment templates,
> creating the enrollment requests, submitting the requests,
> performing actions(approve, reject, cancel etc.) on the requests.
> 
> Also defined the classes needed for representing data used to
> perform the above mentioned operations.
> ---
>  base/common/python/pki/cert.py    | 694 +++++++++++++++++++++++++++++++++++++-
>  base/common/python/pki/profile.py | 577 +++++++++++++++++++++++++++++++
>  2 files changed, 1261 insertions(+), 10 deletions(-)
>  create mode 100644 base/common/python/pki/profile.py
> 
> diff --git a/base/common/python/pki/cert.py b/base/common/python/pki/cert.py
> index c0141048586c5fdbdf84cd0f1c204009e5cac715..b22307ad1b2c2456257dc9416208e6725234bf9c 100644
> --- a/base/common/python/pki/cert.py
> +++ b/base/common/python/pki/cert.py
> @@ -4,11 +4,14 @@ Created on Feb 13, 2014
>  
>  @author: akoneru
>  """
> +import copy
>  import json
> +import types
> +
>  import pki
>  import pki.client as client
>  import pki.encoder as encoder
> -import types
> +import pki.profile as profile
>  
>  
>  class CertId(object):
> @@ -104,8 +107,8 @@ class CertDataInfo(object):
>  
>  class CertDataInfoCollection(object):
>      """
> -    Class containing lists of CertDataInfo objects.
> -    This data is returned when searching/listing certificate records on the CA.
> +    Class containing list of CertDataInfo objects and their respective link objects.
> +    This data is returned when searching/listing certificate records in the CA.
>      """
>  
>      def __init__(self):
> @@ -162,7 +165,7 @@ class CertRequestInfo(object):
>          cert_request_info.request_id = \
>              str(cert_request_info.request_url)[(str(cert_request_info.request_url).rfind("/") + 1):]
>          #Optional parameters
> -        if 'certID' in attr_list:
> +        if 'certId' in attr_list:
>              cert_request_info.cert_id = attr_list['certId']
>          if 'certURL' in attr_list:
>              cert_request_info.cert_url = attr_list['certURL']
> @@ -174,6 +177,37 @@ class CertRequestInfo(object):
>          return cert_request_info
>  
>  
> +class CertRequestInfoCollection(object):
> +    """
> +    Class containing list of CertRequestInfo objects.
> +    This data is returned when listing certificate request records in the CA.
> +    """
> +
> +    def __init__(self):
> +        self.cert_info_list = []
> +        self.links = []
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        """ Populate object from JSON input """
> +        ret = cls()
> +        cert_req_infos = json_value['entries']
> +        if not isinstance(cert_req_infos, types.ListType):
> +            ret.cert_info_list.append(CertRequestInfo.from_json(cert_req_infos))
> +        else:
> +            for cert_info in cert_req_infos:
> +                ret.cert_info_list.append(CertRequestInfo.from_json(cert_info))
> +
> +        links = json_value['Link']
> +        if not isinstance(links, types.ListType):
> +            ret.links.append(pki.Link.from_json(links))
> +        else:
> +            for link in links:
> +                ret.links.append(pki.Link.from_json(link))
> +
> +        return ret
> +
> +
>  class CertSearchRequest(object):
>      """
>          An object of this class is used to store the search parameters
> @@ -285,6 +319,330 @@ class CertRevokeRequest(object):
>              setattr(self, "Comments", comments)
>  
>  
> +class CertEnrollmentRequest(object):
> +    """
> +    This class encapsulates the parameters required for a certificate enrollment request.
> +    """
> +
> +    def __init__(self, profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None,
> +                 inputs=None, outputs=None):
> +        """ Constructor """
> +        self.profile_id = profile_id
> +        self.renewal = renewal
> +        self.serial_number = serial_number
> +        self.remote_host = remote_host
> +        self.remote_address = remote_address
> +        if inputs is None:
> +            self.inputs = []
> +        if outputs is None:
> +            self.outputs = []
> +
> +    @property
> +    def profile_id(self):
> +        return getattr(self, 'ProfileID', None)
> +
> +    @profile_id.setter
> +    def profile_id(self, value):
> +        setattr(self, 'ProfileID', value)
> +
> +    @property
> +    def renewal(self):
> +        return getattr(self, 'Renewal', False)
> +
> +    @renewal.setter
> +    def renewal(self, value):
> +        setattr(self, 'Renewal', value)
> +
> +    @property
> +    def serial_number(self):
> +        return getattr(self, 'SerialNumber', None)
> +
> +    @serial_number.setter
> +    def serial_number(self, value):
> +        setattr(self, 'SerialNumber', value)
> +
> +    @property
> +    def remote_host(self):
> +        return getattr(self, 'RemoteHost', None)
> +
> +    @remote_host.setter
> +    def remote_host(self, value):
> +        setattr(self, 'RemoteHost', value)
> +
> +    @property
> +    def remote_address(self):
> +        return getattr(self, 'RemoteAddress', None)
> +
> +    @remote_address.setter
> +    def remote_address(self, value):
> +        setattr(self, 'RemoteAddress', value)
> +
> +    @property
> +    def inputs(self):
> +        return getattr(self, 'Input')
> +
> +    @inputs.setter
> +    def inputs(self, value):
> +        setattr(self, 'Input', value)
> +
> +    @property
> +    def outputs(self):
> +        return getattr(self, 'Output')
> +
> +    @outputs.setter
> +    def outputs(self, value):
> +        setattr(self, 'Output', value)
> +
> +    def add_input(self, profile_input):
> +        self.inputs.append(profile_input)
> +
> +    def remove_input(self, profile_input_name):
> +        for profile_input in self.inputs:
> +            if profile_input_name == profile_input.name:
> +                self.inputs.pop(profile_input)
> +                break
> +
> +    def get_input(self, profile_input_name):
> +        for profile_input in self.inputs:
> +            if profile_input_name == profile_input.name:
> +                return profile_input
> +
> +        return None
> +
> +    def add_output(self, profile_output):
> +        self.outputs.append(profile_output)
> +
> +    def remove_output(self, profile_output_name):
> +        for output in self.outputs:
> +            if profile_output_name == output.name:
> +                self.outputs.pop(output)
> +                break
> +
> +    def get_output(self, profile_output_name):
> +        for output in self.outputs:
> +            if profile_output_name == output.name:
> +                return output
> +
> +        return None
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        enroll_request = cls()
> +
> +        enroll_request.profile_id = json_value['ProfileID']
> +        enroll_request.renewal = json_value['Renewal']
> +        if 'SerialNumber' in json_value:
> +            enroll_request.serial_number = json_value['SerialNumber']
> +        if 'RemoteHost' in json_value:
> +            enroll_request.remote_host = json_value['RemoteHost']
> +        if 'RemoteAddress' in json_value:
> +            enroll_request.remote_address = json_value['RemoteAddress']
> +
> +        inputs = json_value['Input']
> +        if not isinstance(inputs, types.ListType):
> +            enroll_request.inputs.append(profile.ProfileInput.from_json(inputs))
> +        else:
> +            for profile_input in inputs:
> +                enroll_request.inputs.append(profile.ProfileInput.from_json(profile_input))
> +
> +        outputs = json_value['Output']
> +        if not isinstance(outputs, types.ListType):
> +            enroll_request.outputs.append(profile.ProfileOutput.from_json(outputs))
> +        else:
> +            for profile_output in outputs:
> +                enroll_request.outputs.append(profile.ProfileOutput.from_json(profile_output))
> +
> +        return enroll_request
> +
> +
> +class CertReviewResponse(CertEnrollmentRequest):
> +    """
> +    An object of this class represent the response from the server when
> +    reviewing a certificate enrollment request.
> +    It contains a nonce required to perform action on the request.
> +    """
> +
> +    def __init__(self, profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None,
> +                 inputs=None, outputs=None, nonce=None, request_id=None, request_type=None, request_status=None,
> +                 request_owner=None, request_creation_time=None, request_modification_time=None, request_notes=None,
> +                 profile_approval_by=None, profile_set_id=None, profile_is_visible=None, profile_name=None,
> +                 profile_description=None, profile_remote_host=None, profile_remote_address=None, policy_sets=None):
> +
> +        super(CertReviewResponse, self).__init__(profile_id, renewal, serial_number, remote_host,
> +                                                 remote_address, inputs, outputs)
> +        self.nonce = nonce
> +        self.request_id = request_id
> +        self.request_type = request_type
> +        self.request_status = request_status
> +        self.request_owner = request_owner
> +        self.request_creation_time = request_creation_time
> +        self.request_modification_time = request_modification_time
> +        self.request_notes = request_notes
> +        self.profile_approved_by = profile_approval_by
> +        self.profile_set_id = profile_set_id
> +        self.profile_is_visible = profile_is_visible
> +        self.profile_name = profile_name
> +        self.profile_description = profile_description
> +        self.profile_remote_host = profile_remote_host
> +        self.profile_remote_address = profile_remote_address
> +
> +        if policy_sets is None:
> +            self.policy_sets = []
> +        else:
> +            self.policy_sets = policy_sets
> +
> +    @property
> +    def request_id(self):
> +        return getattr(self, 'requestId')
> +
> +    @request_id.setter
> +    def request_id(self, value):
> +        setattr(self, 'requestId', value)
> +
> +    @property
> +    def request_type(self):
> +        return getattr(self, 'requestType')
> +
> +    @request_type.setter
> +    def request_type(self, value):
> +        setattr(self, 'requestType', value)
> +
> +    @property
> +    def request_status(self):
> +        return getattr(self, 'requestStatus')
> +
> +    @request_status.setter
> +    def request_status(self, value):
> +        setattr(self, 'requestStatus', value)
> +
> +    @property
> +    def request_owner(self):
> +        return getattr(self, 'requestOwner')
> +
> +    @request_owner.setter
> +    def request_owner(self, value):
> +        setattr(self, 'requestOwner', value)
> +
> +    @property
> +    def request_creation_time(self):
> +        return getattr(self, 'requestCreationTime')
> +
> +    @request_creation_time.setter
> +    def request_creation_time(self, value):
> +        setattr(self, 'requestCreationTime', value)
> +
> +    @property
> +    def request_modification_time(self):
> +        return getattr(self, 'requestModificationTime')
> +
> +    @request_modification_time.setter
> +    def request_modification_time(self, value):
> +        setattr(self, 'requestModificationTime', value)
> +
> +    @property
> +    def request_notes(self):
> +        return getattr(self, 'requestNotes')
> +
> +    @request_notes.setter
> +    def request_notes(self, value):
> +        setattr(self, 'requestNotes', value)
> +
> +    @property
> +    def profile_approved_by(self):
> +        return getattr(self, 'profileApprovedBy')
> +
> +    @profile_approved_by.setter
> +    def profile_approved_by(self, value):
> +        setattr(self, 'profileApprovedBy', value)
> +
> +    @property
> +    def profile_set_id(self):
> +        return getattr(self, 'profileSetId')
> +
> +    @profile_set_id.setter
> +    def profile_set_id(self, value):
> +        setattr(self, 'profileSetId', value)
> +
> +    @property
> +    def profile_is_visible(self):
> +        return getattr(self, 'profileIsVisible')
> +
> +    @profile_is_visible.setter
> +    def profile_is_visible(self, value):
> +        setattr(self, 'profileIsVisible', value)
> +
> +    @property
> +    def profile_name(self):
> +        return getattr(self, 'profileName')
> +
> +    @profile_name.setter
> +    def profile_name(self, value):
> +        setattr(self, 'profileName', value)
> +
> +    @property
> +    def profile_description(self):
> +        return getattr(self, 'profileDescription')
> +
> +    @profile_description.setter
> +    def profile_description(self, value):
> +        setattr(self, 'profileDescription', value)
> +
> +    @property
> +    def profile_remote_host(self):
> +        return getattr(self, 'profileRemoteHost')
> +
> +    @profile_remote_host.setter
> +    def profile_remote_host(self, value):
> +        setattr(self, 'profileRemoteHost', value)
> +
> +    @property
> +    def profile_remote_address(self):
> +        return getattr(self, 'profileRemoteAddr')
> +
> +    @profile_remote_address.setter
> +    def profile_remote_address(self, value):
> +        setattr(self, 'profileRemoteAddr', value)
> +
> +    @property
> +    def policy_sets(self):
> +        return getattr(self, 'ProfilePolicySet')
> +
> +    @policy_sets.setter
> +    def policy_sets(self, value):
> +        setattr(self, 'ProfilePolicySet', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +
> +        #First read the values for attributes defined in CertEnrollmentRequest
> +        review_response = super(CertReviewResponse, cls).from_json(json_value)
> +
> +        review_response.nonce = json_value['nonce']
> +        review_response.request_id = json_value['requestId']
> +        review_response.request_type = json_value['requestType']
> +        review_response.request_status = json_value['requestStatus']
> +        review_response.request_owner = json_value['requestOwner']
> +        review_response.request_creation_time = json_value['requestCreationTime']
> +        review_response.request_modification_time = json_value['requestModificationTime']
> +        review_response.request_notes = json_value['requestNotes']
> +        review_response.profile_approved_by = json_value['profileApprovedBy']
> +        review_response.profile_set_id = json_value['profileSetId']
> +        review_response.profile_is_visible = json_value['profileIsVisible']
> +        review_response.profile_name = json_value['profileName']
> +        review_response.profile_description = json_value['profileDescription']
> +        review_response.profile_remote_host = json_value['profileRemoteHost']
> +        review_response.profile_remote_address = json_value['profileRemoteAddr']
> +
> +        profile_policy_sets = json_value['ProfilePolicySet']
> +        if not isinstance(profile_policy_sets, types.ListType):
> +            review_response.policy_sets.append(profile.ProfilePolicySet.from_json(profile_policy_sets))
> +        else:
> +            for policy_set in profile_policy_sets:
> +                review_response.policy_sets.append(profile.ProfilePolicySet.from_json(policy_set))
> +
> +        return review_response
> +
> +
>  class CertClient(object):
>      """
>      Class encapsulating and mirroring the functionality in the CertResource Java interface class
> @@ -298,6 +656,8 @@ class CertClient(object):
>                          'Accept': 'application/json'}
>          self.cert_url = '/rest/certs'
>          self.agent_cert_url = '/rest/agent/certs'
> +        self.cert_requests_url = '/rest/certrequests'
> +        self.agent_cert_requests_url = '/rest/agent/certrequests'
>          self.enrollment_templates = {}
>  
>      @pki.handle_exceptions()
> @@ -399,10 +759,251 @@ class CertClient(object):
>          r = self.connection.post(url, None, headers=self.headers)
>          return CertRequestInfo.from_json(r.json())
>  
> +    @pki.handle_exceptions()
> +    def get_request(self, request_id):
> +        """
> +        Get information of a certificate request with the given request ID.
> +        Returns a CertRequestInfo object.
> +        """
> +
> +        if request_id is None:
> +            raise ValueError("Request ID must be specified")
> +        url = self.cert_requests_url + '/' + str(request_id)
> +        r = self.connection.get(url, headers=self.headers)
> +
> +        return CertRequestInfo.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def list_requests(self, request_status=None, request_type=None, from_request_id=None, size=None,
> +                      max_results=None, max_time=None):
> +        """
> +        Query for a list of certificates using the arguments passed.
> +        Returns a CertRequestInfoCollection object.
> +        """
> +
> +        query_params = {
> +            'requestStatus': request_status,
> +            'requestType': request_type,
> +            'start': from_request_id,
> +            'pageSize': size,
> +            'maxResults': max_results,
> +            'maxTime': max_time
> +        }
> +        r = self.connection.get(self.agent_cert_requests_url, self.headers, query_params)
> +        return CertRequestInfoCollection.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def review_request(self, request_id):
> +        """
> +        Reviews a certificate enrollment request.
> +        Returns a CertReviewResponse object which contains the nonce
> +        from the server needed to perform an action on the request.
> +        """
> +        if request_id is None:
> +            raise ValueError("Request Id must be specified.")
> +
> +        url = self.agent_cert_requests_url + '/' + str(request_id)
> +        r = self.connection.get(url, headers=self.headers)
> +        return CertReviewResponse.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def _perform_action(self, request_id, cert_review_response, action):
> +        """
> +        An internal method used by all the action methods to perform
> +        an action on a certificate request.
> +        The parameter cert_review_response
> +        """
> +        if request_id is None:
> +            raise ValueError("Request Id must be specified.")
> +        if cert_review_response is None:
> +            cert_review_response = self.review_request(request_id)
> +
> +        url = self.agent_cert_requests_url + '/' + request_id + '/' + action
> +        review_response = json.dumps(cert_review_response, cls=encoder.CustomTypeEncoder, sort_keys=True)
> +        r = self.connection.post(url, review_response, headers=self.headers)
> +        return r
> +
> +    def approve_request(self, request_id, cert_review_response=None):
> +        """
> +        Approves a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'approve')
> +
> +    def cancel_request(self, request_id, cert_review_response=None):
> +        """
> +        Cancels a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'cancel')
> +
> +    def reject_request(self, request_id,  cert_review_response=None):
> +        """
> +        Rejects a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'reject')
> +
> +    def validate_request(self, request_id, cert_review_response):
> +        """
> +        Validates a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'validate')
> +
> +    def update_request(self, request_id, cert_review_response):
> +        """
> +        Updates a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'update')
> +
> +    def assign_request(self, request_id, cert_review_response):
> +        """
> +        Assigns a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'assign')
> +
> +    def unassign_request(self, request_id, cert_review_response):
> +        """
> +        Un-assigns a certificate enrollment request.
> +        If cert_review_response is None, a review request operation is performed to fetch the
> +        CertReviewResponse object.
> +        Requires as agent level authentication.
> +        """
> +        return self._perform_action(request_id, cert_review_response, 'unassign')
> +
> +    @pki.handle_exceptions()
> +    def list_enrollment_templates(self, start=None, size=None):
> +        """
> +        Gets the list of profile templates supported by the CA.
> +        The values for start and size arguments determine the starting point and the length of the list.
> +        Returns a ProfileDataInfoCollection object.
> +        """
> +
> +        url = self.cert_requests_url + '/profiles'
> +        query_params = {
> +            'start': start,
> +            'size': size
> +        }
> +        r = self.connection.get(url, self.headers, query_params)
> +        print r
> +        return profile.ProfileDataInfoCollection.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def get_enrollment_template(self, profile_id):
> +        """
> +        Fetch the enrollment template for the given profile id.
> +        For the first time, the request is sent to the server.
> +        The retrieved CertEnrollmentRequest object is then cached locally for future requests.
> +        Returns a CerEnrollmentRequest object.
> +        """
> +
> +        if profile_id in self.enrollment_templates:
> +            return copy.deepcopy(self.enrollment_templates[profile_id])
> +        url = self.cert_requests_url + '/profiles/' + str(profile_id)
> +        r = self.connection.get(url, self.headers)
> +
> +        #Caching the enrollment template object in-memory for future use.
> +        enrollment_template = CertEnrollmentRequest.from_json(r.json())
> +        self.enrollment_templates[profile_id] = enrollment_template
> +
> +        return copy.deepcopy(enrollment_template)
> +
> +    @pki.handle_exceptions()
> +    def create_enrollment_request(self, profile_id, inputs):
> +        """
> +        Fetches the enrollment request object for the given profile and
> +        sets values to its attributes using the values provided in the inputs dictionary.
> +        Returns the CertEnrollmentRequest object, which can be submitted to enroll a certificate.
> +        """
> +        if inputs is None or len(inputs) == 0:
> +            raise ValueError("No inputs provided.")
> +
> +        enrollment_template = self.get_enrollment_template(profile_id)
> +        for profile_input in enrollment_template.inputs:
> +            for attribute in profile_input.attributes:
> +                if attribute.name in inputs:
> +                    attribute.value = inputs[attribute.name]
> +
> +        return enrollment_template
> +
> +    @pki.handle_exceptions()
> +    def submit_enrollment_request(self, enrollment_request):
> +        """
> +        Submits the CertEnrollmentRequest object to the server.
> +        Returns a CertRequestInfoCollection object with information about the certificate requests
> +        enrolled at the CA.
> +        """
> +        request_object = json.dumps(enrollment_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
> +        r = self.connection.post(self.cert_requests_url, request_object, self.headers)
> +        return CertRequestInfoCollection.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def enroll_cert(self, profile_id, inputs):
> +        """
> +        A convenience method for enrolling a certificate for a given profile id.
> +        The inputs parameter should be a dictionary with values for the profile attributes
> +        for an enrollment request.
> +
> +        Calling this method with valid arguments, creates an enrollment request, submits it
> +        to the server, approves the certificate requests generated for the enrollment and
> +        returns a list of CertData objects for all the certificates generated as part of this
> +        enrollment.
> +
> +        Note: This method supports only certificate enrollment where only one agent approval
> +        is sufficient.
> +
> +        Requires an agent level authentication.
> +        """
> +
> +        # Create a CertEnrollmentRequest object using the inputs for the given profile id.
> +        enroll_request = self.create_enrollment_request(profile_id, inputs)
> +
> +        # Submit the enrollment request
> +        cert_request_infos = self.submit_enrollment_request(enroll_request)
> +
> +        # Approve the requests generated for the certificate enrollment.
> +        # Fetch the CertData objects for all the certificates created and return to the caller.
> +
> +        certificates = []
> +        for cert_request_info in cert_request_infos.cert_info_list:
> +            request_id = cert_request_info.request_id
> +            self.approve_request(request_id)
> +            cert_id = self.get_request(request_id).cert_id
> +            certificates.append(self.get_cert(cert_id))
> +
> +        return certificates
> +
>  
>  encoder.NOTYPES['CertData'] = CertData
>  encoder.NOTYPES['CertSearchRequest'] = CertSearchRequest
>  encoder.NOTYPES['CertRevokeRequest'] = CertRevokeRequest
> +encoder.NOTYPES['CertEnrollmentRequest'] = CertEnrollmentRequest
> +encoder.NOTYPES['ProfileInput'] = profile.ProfileInput
> +encoder.NOTYPES['ProfileAttribute'] = profile.ProfileAttribute
> +encoder.NOTYPES['Descriptor'] = profile.Descriptor
> +encoder.NOTYPES['ProfileOutput'] = profile.ProfileOutput
> +encoder.NOTYPES['CertReviewResponse'] = CertReviewResponse
> +encoder.NOTYPES['ProfilePolicySet'] = profile.ProfilePolicySet
> +encoder.NOTYPES['ProfilePolicy'] = profile.ProfilePolicy
> +encoder.NOTYPES['PolicyDefault'] = profile.PolicyDefault
> +encoder.NOTYPES['PolicyConstraint'] = profile.PolicyConstraint
> +encoder.NOTYPES['PolicyConstraintValue'] = profile.PolicyConstraintValue
> +encoder.NOTYPES['ProfileParameter'] = profile.ProfileParameter
>  
>  
>  def main():
> @@ -416,16 +1017,76 @@ def main():
>      #Instantiate the CertClient
>      cert_client = CertClient(connection)
>  
> +    cert_client.get_enrollment_template('caUserCert')
> +
> +    #Enrolling an user certificate
> +    print('Enrolling an user certificate')
> +    print('-----------------------------')
> +
> +    inputs = dict()
> +    inputs['cert_request_type'] = 'crmf'
> +    inputs['cert_request'] = "MIIBpDCCAaAwggEGAgUA5n9VYTCBx4ABAqUOMAwxCjAIBgNVBAMTAXimgZ8wDQYJKoZIhvcNAQEBBQAD" \
> +                             "gY0AMIGJAoGBAK/SmUVoUjBtqHNw/e3OoCSXw42pdQSR53/eYJWpf7nyTbZ9UuIhGfXOtxy5vRetmDHE" \
> +                             "9u0AopmuJbr1rL17/tSnDakpkE9umQ2lMOReLloSdX32w2xOeulUwh5BGbFpq10S0SvW1H93Vn0eCy2a" \
> +                             "a4UtILNEsp7JJ3FnYJibfuMPAgMBAAGpEDAOBgNVHQ8BAf8EBAMCBeAwMzAVBgkrBgEFBQcFAQEMCHJl" \
> +                             "Z1Rva2VuMBoGCSsGAQUFBwUBAgwNYXV0aGVudGljYXRvcqGBkzANBgkqhkiG9w0BAQUFAAOBgQCuywnr" \
> +                             "Dk/wGwfbguw9oVs9gzFQwM4zeFbk+z82G5CWoG/4mVOT5LPL5Q8iF+KfnaU9Qcu6zZPxW6ZmDd8WpPJ+" \
> +                             "MTPyQl3Q5BfiKa4l5ra1NeqxMOlMiiupwINmm7jd1KaA2eIjuyC8/gTaO4b14R6aRaOj+Scp9cNYbthA7REhJw=="
> +    inputs['sn_uid'] = 'test12345'
> +    inputs['sn_e'] = 'example at redhat.com'
> +    inputs['sn_cn'] = 'TestUser'
> +
> +    cert_data_infos = cert_client.enroll_cert('caUserCert', inputs)
> +
> +    for data in cert_data_infos:
> +        print('Serial Number: ' + data.serial_number)
> +        print('Issuer: ' + data.issuer_dn)
> +        print('Subject: ' + data.subject_dn)
> +        print('Pretty Print:')
> +        print(data.pretty_repr)
> +
> +    print
> +
> +    # Enrolling a server certificate
> +    print("Enrolling a server certificate")
> +    print('------------------------------')
> +
> +    inputs = dict()
> +    inputs['cert_request_type'] = 'pkcs10'
> +    inputs['cert_request'] = "MIIBmDCCAQECAQAwWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5DMRAwDgYDVQQHDAdSYWxlaWdoMRUwE" \
> +                             "wYDVQQKDAxSZWQgSGF0IEluYy4xEzARBgNVBAMMClRlc3RTZXJ2ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY" \
> +                             "0AMIGJAoGBAMJpWz92dSYCvWxllrQCY5atPKCswUwyppRNGPnKmJ77AdHBBI4dFyET+h/+69jQMTLZMa8" \
> +                             "FX7SbyHvgbgLBP4Q/RzCSE2S87qFNjriOqiQCqJmcrzDzdncJQiP+O7T6MSpLo3smLP7dK1Vd7vK0Vy8y" \
> +                             "HwV0eBx7DgYedv2slBPHAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQBvkxAGKwkfK3TKwLc5Mg0IWp8zG" \
> +                             "RVwxdIlghAL8DugNocCNNgmZazglJOOehLuk0/NkLX1ZM5RrVgM09W6kcfWZtIwr5Uje2K/+6tW2ZTGrb" \
> +                             "izs7CNOTMzA/9H8CkHb4H9P/qRT275zHIocYj4smUnXLwWGsBMeGs+OMMbGvSrHg=="
> +
> +    inputs['requestor_name'] = 'Tester'
> +    inputs['requestor_email'] = 'example at redhat.com'
> +
> +    cert_data_infos_2 = cert_client.enroll_cert('caServerCert', inputs)
> +    for data in cert_data_infos_2:
> +        print('Serial Number: ' + data.serial_number)
> +        print('Issuer: ' + data.issuer_dn)
> +        print('Subject: ' + data.subject_dn)
> +        print('Pretty Print:')
> +        print(data.pretty_repr)
> +
> +    print
> +
>      # List all the VALID certs
> +    print('An example listing all VALID certs')
> +    print('----------------------------------')
> +
>      search_params = {'status': 'VALID'}
> -    cert_data_infos = cert_client.list_certs(**search_params)
> -    for cert_data_info in cert_data_infos.cert_info_list:
> +    cert_data_list = cert_client.list_certs(**search_params)
> +    for cert_data_info in cert_data_list.cert_info_list:
>          print("Serial Number: " + cert_data_info.cert_id)
>          print("Subject DN: " + cert_data_info.subject_dn)
>          print("Status: " + cert_data_info.status)
>      print
>  
> -    #Trying an invalid get cert
> +    #Trying to get a non-existing cert
>      #Assuming that there is no certificate with serial number = 100
>      try:
>          cert_data = cert_client.get_cert(100)
> @@ -437,10 +1098,14 @@ def main():
>          print
>  
>      # Certificate Serial Number used for CertClient methods.
> -    # 7 and '0x7' are also valid values
> -    cert_id = 0x7
> +    # 7, 0x7 and '0x7' are also valid values
> +    # Following examples use the serial number of the user certificate enrolled before.
> +    cert_id = cert_data_infos[0].serial_number
>  
>      #Get certificate data
> +    print('Getting information of a certificate')
> +    print('------------------------------------')
> +
>      cert_data = cert_client.get_cert(cert_id)
>      # Print the certificate information
>      print('Serial Number: ' + cert_data.serial_number)
> @@ -456,6 +1121,9 @@ def main():
>      print
>  
>      # Review a certificate - used to get a nonce for revoke request.
> +    print('Reviewing a certificate')
> +    print('-----------------------')
> +
>      cert_data = cert_client.review_cert(cert_id)
>      print('Serial Number: ' + cert_data.serial_number)
>      print('Issuer: ' + cert_data.issuer_dn)
> @@ -465,6 +1133,9 @@ def main():
>      print
>  
>      #Revoke a certificate
> +    print('Revoking a certificate')
> +    print('----------------------')
> +
>      cert_request_info = cert_client.revoke_cert(cert_data.serial_number,
>                                                  revocation_reason=CertRevokeRequest.REASON_CERTIFICATE_HOLD,
>                                                  comments="Test revoking a cert", nonce=cert_data.nonce)
> @@ -474,6 +1145,9 @@ def main():
>      print
>  
>      #Un-revoke a certificate
> +    print('Un-revoking a certificate')
> +    print('-------------------------')
> +
>      cert_request_info = cert_client.unrevoke_cert(cert_data.serial_number)
>      print('Request ID: ' + cert_request_info.request_id)
>      print('Request Type: ' + cert_request_info.request_type)
> @@ -482,4 +1156,4 @@ def main():
>  
>  
>  if __name__ == "__main__":
> -    main()
> +    main()
> \ No newline at end of file
> diff --git a/base/common/python/pki/profile.py b/base/common/python/pki/profile.py
> new file mode 100644
> index 0000000000000000000000000000000000000000..490db0994cacf927ced96b5ed43790fda4327894
> --- /dev/null
> +++ b/base/common/python/pki/profile.py
> @@ -0,0 +1,577 @@
> +#!/usr/bin/python
> +"""
> +Created on May 13,, 2014
> +
> + at author: akoneru
> +"""
> +
> +import types
> +
> +import pki
> +
> +
> +class ProfileDataInfo(object):
> +    """Stores information about a profile"""
> +    def __init__(self):
> +        self.profile_id = None
> +        self.profile_name = None
> +        self.profile_description = None
> +        self.profile_url = None
> +
> +    @classmethod
> +    def from_json(cls, attr_list):
> +        profile_data_info = cls()
> +        profile_data_info.profile_id = attr_list['profileId']
> +        profile_data_info.profile_name = attr_list['profileName']
> +        profile_data_info.profile_description = attr_list['profileDescription']
> +        profile_data_info.profile_url = attr_list['profileURL']
> +
> +        return profile_data_info
> +
> +
> +class ProfileDataInfoCollection(object):
> +    """
> +    Represents a collection of ProfileDataInfo objects.
> +    Also encapsulates the links for the list of the objects stored.
> +    """
> +
> +    def __init__(self):
> +        self.profile_data_list = []
> +        self.links = []
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        ret = cls()
> +        profile_data_infos = json_value['entries']
> +        if not isinstance(profile_data_infos, types.ListType):
> +            ret.profile_data_list.append(ProfileDataInfo.from_json(profile_data_infos))
> +        else:
> +            for profile_info in profile_data_infos:
> +                ret.profile_data_list.append(ProfileDataInfo.from_json(profile_info))
> +
> +        links = json_value['Link']
> +        if not isinstance(links, types.ListType):
> +            ret.links.append(pki.Link.from_json(links))
> +        else:
> +            for link in links:
> +                ret.links.append(pki.Link.from_json(link))
> +
> +        return ret
> +
> +
> +class Descriptor(object):
> +    """
> +    This class represents the description of a ProfileAttribute.
> +    It stores information such as the syntax, constraint and default value of a profile attribute.
> +    """
> +
> +    def __init__(self, syntax=None, constraint=None, description=None, default_value=None):
> +        self.syntax = syntax
> +        self.constraint = constraint
> +        self.description = description
> +        self.default_value = default_value
> +
> +    @property
> +    def syntax(self):
> +        return getattr(self, 'Syntax', None)
> +
> +    @syntax.setter
> +    def syntax(self, value):
> +        setattr(self, 'Syntax', value)
> +
> +    @property
> +    def constraint(self):
> +        return getattr(self, 'Constraint', None)
> +
> +    @constraint.setter
> +    def constraint(self, value):
> +        setattr(self, 'Constraint', value)
> +
> +    @property
> +    def description(self):
> +        return getattr(self, 'Description', None)
> +
> +    @description.setter
> +    def description(self, value):
> +        setattr(self, 'Description', value)
> +
> +    @property
> +    def default_value(self):
> +        return getattr(self, 'DefaultValue', None)
> +
> +    @default_value.setter
> +    def default_value(self, value):
> +        setattr(self, 'DefaultValue', value)
> +
> +    @classmethod
> +    def from_json(cls, attr_list):
> +        descriptor = cls()
> +        for attr in attr_list:
> +            setattr(descriptor, attr, attr_list[attr])
> +
> +        return descriptor
> +
> +
> +class ProfileAttribute(object):
> +    """
> +    Represents a profile attribute of a ProfileInput.
> +    """
> +    def __init__(self, name=None, value=None, descriptor=None):
> +        self.name = name
> +        self.value = value
> +        self.descriptor = descriptor
> +
> +    @property
> +    def descriptor(self):
> +        return getattr(self, 'Descriptor')
> +
> +    @descriptor.setter
> +    def descriptor(self, value):
> +        setattr(self, 'Descriptor', value)
> +
> +    @property
> +    def value(self):
> +        return getattr(self, 'Value')
> +
> +    @value.setter
> +    def value(self, value):
> +        setattr(self, 'Value', value)
> +
> +    @classmethod
> +    def from_json(cls, attr_list):
> +        attribute = cls()
> +        attribute.name = attr_list['name']
> +        if 'Value' in attr_list:
> +            attribute.value = attr_list['Value']
> +        if 'Descriptor' in attr_list:
> +            attribute.descriptor = Descriptor.from_json(attr_list['Descriptor'])
> +
> +        return attribute
> +
> +
> +class ProfileInput(object):
> +    """
> +    This class encapsulates all the attributes of a profile to generate a
> +    specific property of a certificate.
> +    Ex. Subject name, Requestor Information etc.
> +    """
> +
> +    def __init__(self, profile_input_id=None, class_id=None, name=None, text=None, attributes=None,
> +                 config_attributes=None):
> +
> +        self.profile_input_id = profile_input_id
> +        self.class_id = class_id
> +        self.name = name
> +        self.text = text
> +        if attributes is None:
> +            self.attributes = []
> +        if config_attributes is None:
> +            self.config_attributes = []
> +
> +    @property
> +    def profile_input_id(self):
> +        return getattr(self, 'id')
> +
> +    @profile_input_id.setter
> +    def profile_input_id(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def class_id(self):
> +        return getattr(self, 'ClassID', None)
> +
> +    @class_id.setter
> +    def class_id(self, value):
> +        setattr(self, 'ClassID', value)
> +
> +    @property
> +    def name(self):
> +        return getattr(self, 'Name', None)
> +
> +    @name.setter
> +    def name(self, value):
> +        setattr(self, 'Name', value)
> +
> +    @property
> +    def text(self):
> +        return getattr(self, 'Text', None)
> +
> +    @text.setter
> +    def text(self, value):
> +        setattr(self, 'Text', value)
> +
> +    @property
> +    def attributes(self):
> +        return getattr(self, 'Attribute')
> +
> +    @attributes.setter
> +    def attributes(self, value):
> +        setattr(self, 'Attribute', value)
> +
> +    @property
> +    def config_attributes(self):
> +        return getattr(self, 'ConfigAttribute')
> +
> +    @config_attributes.setter
> +    def config_attributes(self, value):
> +        setattr(self, 'ConfigAttribute', value)
> +
> +    def add_attribute(self, profile_attribute):
> +        self.attributes.append(profile_attribute)
> +
> +    def remove_attribute(self, profile_attribute_name):
> +        for attr in self.attributes:
> +            if attr.name == profile_attribute_name:
> +                self.attributes.remove(attr)
> +                break
> +
> +    def get_attribute(self, profile_attribute_name):
> +        for attr in self.attributes:
> +            if attr.name == profile_attribute_name:
> +                return attr
> +
> +        return None
> +
> +    def add_config_attribute(self, profile_attribute):
> +        self.attributes.append(profile_attribute)
> +
> +    def remove_config_attribute(self, config_attribute_name):
> +        for attr in self.config_attributes:
> +            if attr.name == config_attribute_name:
> +                self.attributes.remove(attr)
> +                break
> +
> +    def get_config_attribute(self, config_attribute_name):
> +        for attr in self.attributes:
> +            if attr.name == config_attribute_name:
> +                return attr
> +
> +        return None
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        profile_input = cls()
> +        profile_input.profile_input_id = json_value['id']
> +        profile_input.class_id = json_value['ClassID']
> +        profile_input.name = json_value['Name']
> +        if 'Text' in json_value:
> +            profile_input.text = json_value['Text']
> +
> +        attributes = json_value['Attribute']
> +        if not isinstance(attributes, types.ListType):
> +            profile_input.attributes.append(ProfileAttribute.from_json(attributes))
> +        else:
> +            for profile_info in attributes:
> +                profile_input.attributes.append(ProfileAttribute.from_json(profile_info))
> +
> +        config_attributes = json_value['ConfigAttribute']
> +        if not isinstance(config_attributes, types.ListType):
> +            profile_input.config_attributes.append(ProfileAttribute.from_json(config_attributes))
> +        else:
> +            for config_attribute in config_attributes:
> +                profile_input.config_attributes.append(ProfileAttribute.from_json(config_attribute))
> +
> +        return profile_input
> +
> +
> +class ProfileOutput(object):
> +    """
> +    This class defines the output of a certificate enrollment request
> +    using a profile.
> +    """
> +
> +    def __init__(self, profile_output_id=None, name=None, text=None, class_id=None, attributes=None):
> +        self.profile_output_id = profile_output_id
> +        self.name = name
> +        self.text = text
> +        self.class_id = class_id
> +        if attributes is None:
> +            self.attributes = []
> +
> +    @property
> +    def profile_output_id(self):
> +        return getattr(self, 'id')
> +
> +    @profile_output_id.setter
> +    def profile_output_id(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def class_id(self):
> +        return getattr(self, 'classId', None)
> +
> +    @class_id.setter
> +    def class_id(self, value):
> +        setattr(self, 'classId', value)
> +
> +    def add_attribute(self, profile_attribute):
> +        self.attributes.append(profile_attribute)
> +
> +    def remove_attribute(self, profile_attribute_name):
> +        for attr in self.attributes:
> +            if attr.name == profile_attribute_name:
> +                self.attributes.remove(attr)
> +                break
> +
> +    def get_attribute(self, profile_attribute_name):
> +        for attr in self.attributes:
> +            if attr.name == profile_attribute_name:
> +                return attr
> +
> +        return None
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        profile_output = cls()
> +        profile_output.profile_output_id = json_value['id']
> +        profile_output.name = json_value['name']
> +        profile_output.text = json_value['text']
> +        profile_output.class_id = json_value['classId']
> +        attributes = json_value['attributes']
> +        if not isinstance(attributes, types.ListType):
> +            profile_output.attributes.append(ProfileAttribute.from_json(attributes))
> +        else:
> +            for profile_info in attributes:
> +                profile_output.attributes.append(ProfileAttribute.from_json(profile_info))
> +        return profile_output
> +
> +
> +class ProfileParameter(object):
> +
> +    def __init__(self, name=None, value=None):
> +        self.name = name
> +        self.value = value
> +
> +    @classmethod
> +    def from_json(cls, attr_list):
> +        param = cls()
> +        for attr in attr_list:
> +            setattr(param, attr, attr_list[attr])
> +        return param
> +
> +
> +class PolicyDefault(object):
> +    """
> +    An object of this class contains information of the default usage of a specific ProfileInput.
> +    """
> +
> +    def __init__(self, name=None, class_id=None, description=None, policy_attributes=None, policy_params=None):
> +        self.name = name
> +        self.class_id = class_id
> +        self.description = description
> +        if policy_attributes is None:
> +            self.policy_attributes = []
> +        else:
> +            self.policy_attributes = policy_attributes
> +        if policy_params is None:
> +            self.policy_params = []
> +        else:
> +            self.policy_params = policy_params
> +
> +    @property
> +    def name(self):
> +        return getattr(self, 'id')
> +
> +    @name.setter
> +    def name(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def class_id(self):
> +        return getattr(self, 'classId')
> +
> +    @class_id.setter
> +    def class_id(self, value):
> +        setattr(self, 'classId', value)
> +
> +    @property
> +    def policy_attributes(self):
> +        return getattr(self, 'policyAttribute')
> +
> +    @policy_attributes.setter
> +    def policy_attributes(self, value):
> +        setattr(self, 'policyAttribute', value)
> +
> +    @property
> +    def policy_params(self):
> +        return getattr(self, 'params')
> +
> +    @policy_params.setter
> +    def policy_params(self, value):
> +        setattr(self, 'params', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        policy_def = cls()
> +        if 'id' in json_value:
> +            policy_def.name = json_value['id']
> +        if 'classId' in json_value:
> +            policy_def.class_id = json_value['classId']
> +        if 'description' in json_value:
> +            policy_def.description = json_value['description']
> +        if 'policyAttribute' in json_value:
> +            attributes = json_value['policyAttribute']
> +            if not isinstance(attributes, types.ListType):
> +                policy_def.policy_attributes.append(ProfileAttribute.from_json(attributes))
> +            else:
> +                for attr in attributes:
> +                    policy_def.policy_attributes.append(ProfileAttribute.from_json(attr))
> +
> +        if 'params' in json_value:
> +            params = json_value['params']
> +            if not isinstance(params, types.ListType):
> +                policy_def.policy_params.append(ProfileParameter.from_json(params))
> +            else:
> +                for param in params:
> +                    policy_def.policy_params.append(ProfileParameter.from_json(param))
> +
> +        return policy_def
> +
> +
> +class PolicyConstraintValue(object):
> +
> +    def __init__(self, name=None, value=None, descriptor=None):
> +        self.name = name
> +        self.value = value
> +        self.descriptor = descriptor
> +
> +    @property
> +    def name(self):
> +        return getattr(self, 'id')
> +
> +    @name.setter
> +    def name(self, value):
> +        setattr(self, 'id', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        ret = cls()
> +
> +        ret.name = json_value['id']
> +        ret.value = json_value['value']
> +        if 'descriptor' in json_value:
> +            ret.descriptor = Descriptor.from_json(json_value['descriptor'])
> +
> +        return ret
> +
> +
> +class PolicyConstraint(object):
> +    """
> +    An object of this class contains the policy constraints applied to a ProfileInput
> +    used by a certificate enrollment request.
> +    """
> +
> +    def __init__(self, name=None, description=None, class_id=None, policy_constraint_values=None):
> +        self.name = name
> +        self.description = description
> +        self.class_id = class_id
> +        if policy_constraint_values is None:
> +            self.policy_constraint_values = []
> +        else:
> +            self.policy_constraint_values = policy_constraint_values
> +
> +    @property
> +    def name(self):
> +        return getattr(self, 'id')
> +
> +    @name.setter
> +    def name(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def class_id(self):
> +        return getattr(self, 'classId')
> +
> +    @class_id.setter
> +    def class_id(self, value):
> +        setattr(self, 'classId', value)
> +
> +    @property
> +    def policy_constraint_values(self):
> +        return getattr(self, 'constraint')
> +
> +    @policy_constraint_values.setter
> +    def policy_constraint_values(self, value):
> +        setattr(self, 'constraint', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        policy_constraint = cls()
> +        if 'id' in json_value:
> +            policy_constraint.name = json_value['id']
> +        if 'description' in json_value:
> +            policy_constraint.description = json_value['description']
> +        if 'classId' in json_value:
> +            policy_constraint.class_id = json_value['classId']
> +        if 'constraint' in json_value:
> +            constraints = json_value['constraint']
> +            if not isinstance(constraints, types.ListType):
> +                policy_constraint.policy_constraint_values.append(PolicyConstraintValue.from_json(constraints))
> +            else:
> +                for constraint in constraints:
> +                    policy_constraint.policy_constraint_values.append(PolicyConstraintValue.from_json(constraint))
> +
> +        return policy_constraint
> +
> +
> +class ProfilePolicy(object):
> +    """
> +    This class represents the policy a profile adheres to.
> +    An object of this class stores the default values for profile and the constraints present on the
> +    values of the attributes of the profile submitted for an enrollment request.
> +    """
> +
> +    def __init__(self, policy_id=None, policy_default=None, policy_constraint=None):
> +        self.policy_id = policy_id
> +        self.policy_default = policy_default
> +        self.policy_constraint = policy_constraint
> +
> +    @property
> +    def policy_id(self):
> +        return getattr(self, 'id')
> +
> +    @policy_id.setter
> +    def policy_id(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def policy_default(self):
> +        return getattr(self, 'def')
> +
> +    @policy_default.setter
> +    def policy_default(self, value):
> +        setattr(self, 'def', value)
> +
> +    @property
> +    def policy_constraint(self):
> +        return getattr(self, 'constraint')
> +
> +    @policy_constraint.setter
> +    def policy_constraint(self, value):
> +        setattr(self, 'constraint', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        return cls(json_value['id'], PolicyDefault.from_json(json_value['def']),
> +                   PolicyConstraint.from_json(json_value['constraint']))
> +
> +
> +class ProfilePolicySet(object):
> +    """
> +    Stores a list of ProfilePolicy objects.
> +    """
> +    def __init__(self):
> +        self.policies = []
> +
> +    @classmethod
> +    def from_json(cls, attr_list):
> +        policy_set = cls()
> +
> +        policies = attr_list['policies']
> +        if not isinstance(policies, types.ListType):
> +            policy_set.policies.append(ProfilePolicy.from_json(policies))
> +        else:
> +            for policy in policies:
> +                policy_set.policies.append(ProfilePolicy.from_json(policy))
> +
> +        return policy_set
> -- 
> 1.8.5.3
> 

> >From ae8c2c3bbe0c4ac6ab62b6a58c71ce1899fe8c74 Mon Sep 17 00:00:00 2001
> From: Abhishek Koneru <akoneru at redhat.com>
> Date: Tue, 20 May 2014 21:01:24 -0400
> Subject: [PATCH] Initial patch for ProfileClient implementation
> 
> This patch adds methods for listing profiles, retrieving aprofile,
> enabling a profile and disabling a profile.
> It also contains few cosmetic changes in account.py and
> client.py(pycharm PEP8 warnings addressed)
> ---
>  base/common/python/pki/profile.py | 357 +++++++++++++++++++++++++++++++++++++-
>  1 file changed, 356 insertions(+), 1 deletion(-)
> 
> diff --git a/base/common/python/pki/profile.py b/base/common/python/pki/profile.py
> index 490db0994cacf927ced96b5ed43790fda4327894..83cd8bcca0d9ad3841b0b6b74e2aa4a6724e5bbc 100644
> --- a/base/common/python/pki/profile.py
> +++ b/base/common/python/pki/profile.py
> @@ -8,6 +8,8 @@ Created on May 13,, 2014
>  import types
>  
>  import pki
> +import pki.client as client
> +import pki.account as account
>  
>  
>  class ProfileDataInfo(object):
> @@ -325,7 +327,8 @@ class ProfileOutput(object):
>          profile_output = cls()
>          profile_output.profile_output_id = json_value['id']
>          profile_output.name = json_value['name']
> -        profile_output.text = json_value['text']
> +        if 'text' in json_value:
> +            profile_output.text = json_value['text']
>          profile_output.class_id = json_value['classId']
>          attributes = json_value['attributes']
>          if not isinstance(attributes, types.ListType):
> @@ -575,3 +578,355 @@ class ProfilePolicySet(object):
>                  policy_set.policies.append(ProfilePolicy.from_json(policy))
>  
>          return policy_set
> +
> +
> +class PolicySet(object):
> +    """
> +    An object of this class contains a name value pair of the
> +    policy name and the ProfilePolicy object.
> +    """
> +    def __init__(self, name=None, policy_list=None):
> +        self.name = name
> +        if policy_list is None:
> +            self.policy_list = []
> +        else:
> +            self.policy_list = policy_list
> +
> +    @property
> +    def name(self):
> +        return getattr(self, 'id')
> +
> +    @name.setter
> +    def name(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def policy_list(self):
> +        return getattr(self, 'value')
> +
> +    @policy_list.setter
> +    def policy_list(self, value):
> +        setattr(self, 'value', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        policy_set = cls()
> +
> +        policy_set.name = json_value['id']
> +        policies = json_value['value']
> +        if not isinstance(policies, types.ListType):
> +            policy_set.policy_list.append(ProfilePolicy.from_json(policies))
> +        else:
> +            for policy in policies:
> +                policy_set.policy_list.append(ProfilePolicy.from_json(policy))
> +
> +
> +class PolicySetList(object):
> +    """
> +    An object of this class stores a list of ProfileSet objects.
> +    """
> +
> +    def __init__(self, policy_sets=None):
> +        if policy_sets is None:
> +            self.policy_sets = []
> +        else:
> +            self.policy_sets = policy_sets
> +
> +    @property
> +    def policy_sets(self):
> +        return getattr(self, 'PolicySet')
> +
> +    @policy_sets.setter
> +    def policy_sets(self, value):
> +        setattr(self, 'PolicySet', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        policy_set_list = cls()
> +        policy_sets = json_value['PolicySet']
> +        if not isinstance(policy_sets, types.ListType):
> +            policy_set_list.policy_sets.append(PolicySet.from_json(policy_sets))
> +        else:
> +            for policy_set in policy_sets:
> +                policy_set_list.policy_sets.append(PolicySet.from_json(policy_set))
> +
> +
> +class ProfileData(object):
> +    """
> +    This class represents an enrollment profile.
> +    """
> +
> +    def __init__(self, profile_id=None, class_id=None, name=None, description=None, enabled=None, visible=None,
> +                 enabled_by=None, authenticator_id=None, authorization_acl=None, renewal=None, xml_output=None,
> +                 inputs=None, outputs=None, policy_sets=None, link=None):
> +
> +        self.profile_id = profile_id
> +        self.name = name
> +        self.class_id = class_id
> +        self.description = description
> +        self.enabled = enabled
> +        self.visible = visible
> +        self.enabled_by = enabled_by
> +        self.authenticator_id = authenticator_id
> +        self.authorization_acl = authorization_acl
> +        self.renewal = renewal
> +        self.xml_output = xml_output
> +        if inputs is None:
> +            self.inputs = []
> +        else:
> +            self.inputs = inputs
> +        if outputs is None:
> +            self.outputs = []
> +        else:
> +            self.outputs = outputs
> +        if policy_sets is None:
> +            self.policy_sets = []
> +        else:
> +            self.policy_sets = policy_sets
> +        self.link = link
> +
> +    @property
> +    def profile_id(self):
> +        return getattr(self, 'id')
> +
> +    @profile_id.setter
> +    def profile_id(self, value):
> +        setattr(self, 'id', value)
> +
> +    @property
> +    def class_id(self):
> +        return getattr(self, 'classId')
> +
> +    @class_id.setter
> +    def class_id(self, value):
> +        setattr(self, 'classId', value)
> +
> +    @property
> +    def enabled_by(self):
> +        return getattr(self, 'enabledBy')
> +
> +    @enabled_by.setter
> +    def enabled_by(self, value):
> +        setattr(self, 'enabledBy', value)
> +
> +    @property
> +    def authenticator_id(self):
> +        return getattr(self, 'authenticatorId')
> +
> +    @authenticator_id.setter
> +    def authenticator_id(self, value):
> +        setattr(self, 'authenticatorId', value)
> +
> +    @property
> +    def authorization_acl(self):
> +        return getattr(self, 'authzAcl')
> +
> +    @authorization_acl.setter
> +    def authorization_acl(self, value):
> +        setattr(self, 'authzAcl', value)
> +
> +    @property
> +    def xml_output(self):
> +        return getattr(self, 'xmlOutput')
> +
> +    @xml_output.setter
> +    def xml_output(self, value):
> +        setattr(self, 'xmlOutput', value)
> +
> +    @property
> +    def inputs(self):
> +        return getattr(self, 'Input')
> +
> +    @inputs.setter
> +    def inputs(self, value):
> +        setattr(self, 'Input', value)
> +
> +    @property
> +    def outputs(self):
> +        return getattr(self, 'Output')
> +
> +    @outputs.setter
> +    def outputs(self, value):
> +        setattr(self, 'Output', value)
> +
> +    @property
> +    def policy_sets(self):
> +        return getattr(self, 'PolicySets')
> +
> +    @policy_sets.setter
> +    def policy_sets(self, value):
> +        setattr(self, 'PolicySets', value)
> +
> +    @classmethod
> +    def from_json(cls, json_value):
> +        profile_data = cls()
> +        profile_data.profile_id = json_value['id']
> +        profile_data.class_id = json_value['classId']
> +        profile_data.name = json_value['name']
> +        profile_data.description = json_value['description']
> +        profile_data.enabled = json_value['enabled']
> +        profile_data.visible = json_value['visible']
> +        if 'enabledBy' in json_value:
> +            profile_data.enabled_by = json_value['enabledBy']
> +        if 'authenticatorId' in json_value:
> +            profile_data.authenticator_id = json_value['authenticatorId']
> +        profile_data.authorization_acl = json_value['authzAcl']
> +        profile_data.renewal = json_value['renewal']
> +        profile_data.xml_output = json_value['xmlOutput']
> +
> +        profile_inputs = json_value['Input']
> +        if not isinstance(profile_inputs, types.ListType):
> +            profile_data.inputs.append(ProfileInput.from_json(profile_inputs))
> +        else:
> +            for profile_input in profile_inputs:
> +                profile_data.policy_sets.append(ProfileInput.from_json(profile_input))
> +
> +        profile_outputs = json_value['Output']
> +        if not isinstance(profile_outputs, types.ListType):
> +            profile_data.outputs.append(ProfileOutput.from_json(profile_outputs))
> +        else:
> +            for profile_output in profile_outputs:
> +                profile_data.policy_sets.append(ProfileOutput.from_json(profile_output))
> +
> +        policy_sets = json_value['PolicySets']
> +        if not isinstance(policy_sets, types.ListType):
> +            profile_data.policy_sets.append(PolicySetList.from_json(policy_sets))
> +        else:
> +            for policy_set in policy_sets:
> +                profile_data.policy_sets.append(PolicySetList.from_json(policy_set))
> +
> +        profile_data.link = pki.Link.from_json(json_value['link'])
> +
> +        return profile_data
> +
> +
> +class ProfileClient(object):
> +    """
> +    This class consists of methods for accessing the ProfileResource.
> +    """
> +    def __init__(self, connection):
> +        self.connection = connection
> +        self.headers = {'Content-type': 'application/json',
> +                        'Accept': 'application/json'}
> +        self.profiles_url = '/rest/profiles'
> +        self.account_client = account.AccountClient(connection)
> +
> +    def _get(self, url, query_params=None, payload=None):
> +        self.account_client.login()
> +        r = self.connection.get(url, self.headers, query_params, payload)
> +        self.account_client.logout()
> +        return r
> +
> +    def _post(self, url, payload=None, query_params=None):
> +        self.account_client.login()
> +        r = self.connection.post(url, payload, self.headers, query_params)
> +        self.account_client.logout()
> +        return r
> +
> +    @pki.handle_exceptions()
> +    def list_profiles(self, start=None, size=None):
> +        """
> +        Fetches the list of profiles.
> +        The start and size arguments provide pagination support.
> +        Returns a ProfileDataInfoCollection object.
> +        """
> +        query_params = {
> +            'start': start,
> +            'size': size
> +        }
> +        r = self._get(self.profiles_url, query_params)
> +        return ProfileDataInfoCollection.from_json(r.json())
> +
> +    @pki.handle_exceptions()
> +    def get_profile(self, profile_id):
> +        """
> +        Fetches information for the profile for the given profile id.
> +        Returns a ProfileData object.
> +        """
> +        if profile_id is None:
> +            raise ValueError("Profile ID must be specified.")
> +        url = self.profiles_url + '/' + str(profile_id)
> +        r = self._get(url)
> +        return ProfileData.from_json(r.json())
> +
> +    def _modify_profile_state(self, profile_id, action):
> +        """
> +        Internal method used to modify the profile state.
> +        """
> +        if profile_id is None:
> +            raise ValueError("Profile ID must be specified.")
> +        if action is None:
> +            raise ValueError("A valid action(enable/disable) must be specified.")
> +
> +        url = self.profiles_url + '/' + str(profile_id)
> +        params = {'action': action}
> +        self._post(url, query_params=params)
> +
> +    @pki.handle_exceptions()
> +    def enable_profile(self, profile_id):
> +        """
> +        Enables a profile.
> +        """
> +        return self._modify_profile_state(profile_id, 'enable')
> +
> +    @pki.handle_exceptions()
> +    def disable_profile(self, profile_id):
> +        """
> +        Disables a profile.
> +        """
> +        return self._modify_profile_state(profile_id, 'disable')
> +
> +
> +def main():
> +    # Initialize a PKIConnection object for the CA
> +    connection = client.PKIConnection('https', 'localhost', '8443', 'ca')
> +
> +    # The pem file used for authentication. Created from a p12 file using the command -
> +    # openssl pkcs12 -in <p12_file_path> -out /tmp/auth.pem -nodes
> +    connection.set_authentication_cert("/tmp/auth.pem")
> +
> +    #Initialize the ProfileClient class
> +    profile_client = ProfileClient(connection)
> +
> +    #Fetching a list of profiles
> +    profile_data_infos = profile_client.list_profiles()
> +    print('List of profiles:')
> +    print('-----------------')
> +    for profile_data_info in profile_data_infos.profile_data_list:
> +        print('  Profile ID: ' + profile_data_info.profile_id)
> +        print('  Profile Name: ' + profile_data_info.profile_name)
> +        print('  Profile Description: ' + profile_data_info.profile_description)
> +    print
> +
> +    # Get a specific profile
> +    profile_data = profile_client.get_profile('caUserCert')
> +    print('Profile Data for caUserCert:')
> +    print('----------------------------')
> +    print('  Profile ID: ' + profile_data.profile_id)
> +    print('  Profile Name: ' + profile_data.name)
> +    print('  Profile Description: ' + profile_data.description)
> +    print('  Is profile enabled? ' + str(profile_data.enabled))
> +    print('  Is profile visible? ' + str(profile_data.visible))
> +    print
> +
> +    # Disabling a profile
> +    print('Disabling a profile:')
> +    print('--------------------')
> +    profile_client.disable_profile('caUserCert')
> +    profile = profile_client.get_profile('caUserCert')
> +    print('  Profile ID: ' + profile.profile_id)
> +    print('  Is profile enabled? ' + str(profile.enabled))
> +    print
> +
> +    # Disabling a profile
> +    print('Enabling a profile:')
> +    print('-------------------')
> +    profile_client.enable_profile('caUserCert')
> +    profile = profile_client.get_profile('caUserCert')
> +    print('  Profile ID: ' + profile_data.profile_id)
> +    print('  Is profile enabled? ' + str(profile.enabled))
> +    print
> +
> +
> +if __name__ == "__main__":
> +    main()
> \ No newline at end of file
> -- 
> 1.8.5.3
> 

> >From 4a237ad0edd25ddf378d204e26b835951aaaa3b2 Mon Sep 17 00:00:00 2001
> From: Abhishek Koneru <akoneru at redhat.com>
> Date: Fri, 23 May 2014 12:17:38 -0400
> Subject: [PATCH] Addressed comments given for patches 92-2, 93, 94.
> 
> Addressed review comments for the patches that
> implement the CertClient and a part of ProfileClient.
> 
> Also includes the pycharm project files in pki/.idea.
> ---
>  .idea/.name                       |   1 +
>  .idea/codeStyleSettings.xml       |  14 +
>  .idea/encodings.xml               |   5 +
>  .idea/misc.xml                    |   5 +
>  .idea/modules.xml                 |   9 +
>  .idea/pki.iml                     |   9 +
>  .idea/scopes/scope_settings.xml   |   5 +
>  .idea/vcs.xml                     |   7 +
>  .idea/workspace.xml               | 780 ++++++++++++++++++++++++++++++++++++++
>  base/common/python/pki/account.py |   3 +
>  base/common/python/pki/cert.py    | 480 ++++++++++++++---------
>  base/common/python/pki/profile.py | 137 +++++--
>  12 files changed, 1242 insertions(+), 213 deletions(-)
>  create mode 100644 .idea/.name
>  create mode 100644 .idea/codeStyleSettings.xml
>  create mode 100644 .idea/encodings.xml
>  create mode 100644 .idea/misc.xml
>  create mode 100644 .idea/modules.xml
>  create mode 100644 .idea/pki.iml
>  create mode 100644 .idea/scopes/scope_settings.xml
>  create mode 100644 .idea/vcs.xml
>  create mode 100644 .idea/workspace.xml
> 
> diff --git a/.idea/.name b/.idea/.name
> new file mode 100644
> index 0000000000000000000000000000000000000000..a1cbd55fe0d55b799879256c4def7367783c2877
> --- /dev/null
> +++ b/.idea/.name
> @@ -0,0 +1 @@
> +pki
> \ No newline at end of file
> diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..f99399bf8bc3e5c2de6d4ff649dd30eb634a18b7
> --- /dev/null
> +++ b/.idea/codeStyleSettings.xml
> @@ -0,0 +1,14 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="ProjectCodeStyleSettingsManager">
> +    <option name="PER_PROJECT_SETTINGS">
> +      <value>
> +        <XML>
> +          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
> +        </XML>
> +      </value>
> +    </option>
> +    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Dogtag_Code_Style" />
> +  </component>
> +</project>
> +
> diff --git a/.idea/encodings.xml b/.idea/encodings.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..e206d70d8595e2a50675ba11de48efcfa012497d
> --- /dev/null
> +++ b/.idea/encodings.xml
> @@ -0,0 +1,5 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
> +</project>
> +
> diff --git a/.idea/misc.xml b/.idea/misc.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..2e6cdab0109625e557c43478dd443d0b3bc05fac
> --- /dev/null
> +++ b/.idea/misc.xml
> @@ -0,0 +1,5 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.5 (/usr/bin/python2.7)" project-jdk-type="Python SDK" />
> +</project>
> +
> diff --git a/.idea/modules.xml b/.idea/modules.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..34a47cab36485cf5819e2ebf3e1f27488da1ff38
> --- /dev/null
> +++ b/.idea/modules.xml
> @@ -0,0 +1,9 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="ProjectModuleManager">
> +    <modules>
> +      <module fileurl="file://$PROJECT_DIR$/.idea/pki.iml" filepath="$PROJECT_DIR$/.idea/pki.iml" />
> +    </modules>
> +  </component>
> +</project>
> +
> diff --git a/.idea/pki.iml b/.idea/pki.iml
> new file mode 100644
> index 0000000000000000000000000000000000000000..a34a85703f063917904e9e958b91cf9f4a2504e1
> --- /dev/null
> +++ b/.idea/pki.iml
> @@ -0,0 +1,9 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<module type="PYTHON_MODULE" version="4">
> +  <component name="NewModuleRootManager">
> +    <content url="file://$MODULE_DIR$" />
> +    <orderEntry type="inheritedJdk" />
> +    <orderEntry type="sourceFolder" forTests="false" />
> +  </component>
> +</module>
> +
> diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..922003b8433bcad6ce9778a37628d738faa26389
> --- /dev/null
> +++ b/.idea/scopes/scope_settings.xml
> @@ -0,0 +1,5 @@
> +<component name="DependencyValidationManager">
> +  <state>
> +    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
> +  </state>
> +</component>
> \ No newline at end of file
> diff --git a/.idea/vcs.xml b/.idea/vcs.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..c80f2198b5f6863fd489ec8ac6c40a50ac1f7b30
> --- /dev/null
> +++ b/.idea/vcs.xml
> @@ -0,0 +1,7 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="VcsDirectoryMappings">
> +    <mapping directory="$PROJECT_DIR$" vcs="Git" />
> +  </component>
> +</project>
> +
> diff --git a/.idea/workspace.xml b/.idea/workspace.xml
> new file mode 100644
> index 0000000000000000000000000000000000000000..17337d1f91ac4a118650061cd97d58b25b0171a6
> --- /dev/null
> +++ b/.idea/workspace.xml
> @@ -0,0 +1,780 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<project version="4">
> +  <component name="ChangeListManager">
> +    <list default="true" id="c7272e4c-4c26-4a55-9c50-4b1b35d87164" name="Default" comment="">
> +      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/base/common/python/pki/cert.py" afterPath="$PROJECT_DIR$/base/common/python/pki/cert.py" />
> +      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/base/common/python/pki/profile.py" afterPath="$PROJECT_DIR$/base/common/python/pki/profile.py" />
> +      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
> +    </list>
> +    <ignored path="pki.iws" />
> +    <ignored path=".idea/workspace.xml" />
> +    <option name="TRACKING_ENABLED" value="true" />
> +    <option name="SHOW_DIALOG" value="false" />
> +    <option name="HIGHLIGHT_CONFLICTS" value="true" />
> +    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
> +    <option name="LAST_RESOLUTION" value="IGNORE" />
> +  </component>
> +  <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
> +  <component name="CreatePatchCommitExecutor">
> +    <option name="PATCH_PATH" value="" />
> +  </component>
> +  <component name="DaemonCodeAnalyzer">
> +    <disable_hints />
> +  </component>
> +  <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
> +  <component name="FavoritesManager">
> +    <favorites_list name="pki" />
> +  </component>
> +  <component name="FileEditorManager">
> +    <leaf>
> +      <file leaf-file-name="cert.py" pinned="false" current="false" current-in-tab="false">
> +        <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +          <provider selected="true" editor-type-id="text-editor">
> +            <state line="1108" column="9" selection-start="41945" selection-end="41945" vertical-scroll-proportion="0.0" vertical-offset="16500" max-vertical-offset="19335">
> +              <folding />
> +            </state>
> +          </provider>
> +        </entry>
> +      </file>
> +      <file leaf-file-name="encoder.py" pinned="false" current="false" current-in-tab="false">
> +        <entry file="file://$PROJECT_DIR$/base/common/python/pki/encoder.py">
> +          <provider selected="true" editor-type-id="text-editor">
> +            <state line="33" column="34" selection-start="1482" selection-end="1482" vertical-scroll-proportion="0.0" vertical-offset="495" max-vertical-offset="810">
> +              <folding />
> +            </state>
> +          </provider>
> +        </entry>
> +      </file>
> +      <file leaf-file-name="profile.py" pinned="false" current="true" current-in-tab="true">
> +        <entry file="file://$PROJECT_DIR$/base/common/python/pki/profile.py">
> +          <provider selected="true" editor-type-id="text-editor">
> +            <state line="961" column="41" selection-start="28774" selection-end="28774" vertical-scroll-proportion="0.71965814" vertical-offset="13934" max-vertical-offset="14880">
> +              <folding />
> +            </state>
> +          </provider>
> +        </entry>
> +      </file>
> +    </leaf>
> +  </component>
> +  <component name="FindManager">
> +    <FindUsagesManager>
> +      <setting name="OPEN_NEW_TAB" value="false" />
> +    </FindUsagesManager>
> +  </component>
> +  <component name="Git.Settings">
> +    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
> +  </component>
> +  <component name="IdeDocumentHistory">
> +    <option name="changedFiles">
> +      <list>
> +        <option value="$PROJECT_DIR$/base/common/python/pki/system.py" />
> +        <option value="/usr/lib/python2.7/site-packages/pki/__init__.py" />
> +        <option value="/usr/lib/python2.7/site-packages/pki/profile.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/__init__.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/client.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/key.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/account.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/cert.py" />
> +        <option value="$PROJECT_DIR$/base/common/python/pki/profile.py" />
> +      </list>
> +    </option>
> +  </component>
> +  <component name="ProjectFrameBounds">
> +    <option name="width" value="1920" />
> +    <option name="height" value="1055" />
> +  </component>
> +  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
> +    <OptionsSetting value="true" id="Add" />
> +    <OptionsSetting value="true" id="Remove" />
> +    <OptionsSetting value="true" id="Checkout" />
> +    <OptionsSetting value="true" id="Update" />
> +    <OptionsSetting value="true" id="Status" />
> +    <OptionsSetting value="true" id="Edit" />
> +    <ConfirmationsSetting value="0" id="Add" />
> +    <ConfirmationsSetting value="0" id="Remove" />
> +  </component>
> +  <component name="ProjectReloadState">
> +    <option name="STATE" value="0" />
> +  </component>
> +  <component name="ProjectView">
> +    <navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5">
> +      <flattenPackages />
> +      <showMembers />
> +      <showModules />
> +      <showLibraryContents />
> +      <hideEmptyPackages />
> +      <abbreviatePackageNames />
> +      <autoscrollToSource />
> +      <autoscrollFromSource />
> +      <sortByType />
> +    </navigator>
> +    <panes>
> +      <pane id="Scope" />
> +      <pane id="ProjectPane">
> +        <subPane>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="base" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="base" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="kra" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="base" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="kra" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="functional" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="base" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="common" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +          <PATH>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="base" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="common" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="python" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +            <PATH_ELEMENT>
> +              <option name="myItemId" value="pki" />
> +              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
> +            </PATH_ELEMENT>
> +          </PATH>
> +        </subPane>
> +      </pane>
> +    </panes>
> +  </component>
> +  <component name="PropertiesComponent">
> +    <property name="options.lastSelected" value="preferences.sourceCode.General" />
> +    <property name="options.splitter.main.proportions" value="0.3" />
> +    <property name="options.splitter.details.proportions" value="0.2" />
> +    <property name="options.searchVisible" value="true" />
> +    <property name="LayoutCode.rearrangeEntriesPython" value="false" />
> +    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
> +    <property name="recentsLimit" value="5" />
> +    <property name="FullScreen" value="false" />
> +  </component>
> +  <component name="PyConsoleOptionsProvider">
> +    <option name="myPythonConsoleState">
> +      <console-settings />
> +    </option>
> +  </component>
> +  <component name="RunManager" selected="Python.cert">
> +    <configuration default="false" name="drmtest" type="PythonConfigurationType" factoryName="Python" temporary="true">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs>
> +        <env name="PYTHONUNBUFFERED" value="1" />
> +      </envs>
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/base/kra/functional" />
> +      <option name="IS_MODULE_SDK" value="true" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/base/kra/functional/drmtest.py" />
> +      <option name="PARAMETERS" value="" />
> +      <RunnerSettings RunnerId="PythonRunner" />
> +      <ConfigurationWrapper RunnerId="PythonRunner" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="tests" factoryName="py.test">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs />
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="CLASS_NAME" value="" />
> +      <option name="METHOD_NAME" value="" />
> +      <option name="FOLDER_NAME" value="" />
> +      <option name="TEST_TYPE" value="TEST_SCRIPT" />
> +      <option name="PATTERN" value="" />
> +      <option name="USE_PATTERN" value="false" />
> +      <option name="testToRun" value="" />
> +      <option name="keywords" value="" />
> +      <option name="params" value="" />
> +      <option name="USE_PARAM" value="false" />
> +      <option name="USE_KEYWORD" value="false" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="tests" factoryName="Nosetests">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs />
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="CLASS_NAME" value="" />
> +      <option name="METHOD_NAME" value="" />
> +      <option name="FOLDER_NAME" value="" />
> +      <option name="TEST_TYPE" value="TEST_SCRIPT" />
> +      <option name="PATTERN" value="" />
> +      <option name="USE_PATTERN" value="false" />
> +      <option name="PARAMS" value="" />
> +      <option name="USE_PARAM" value="false" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="PythonConfigurationType" factoryName="Python">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs>
> +        <env name="PYTHONUNBUFFERED" value="1" />
> +      </envs>
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="PARAMETERS" value="" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="tests" factoryName="Unittests">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs />
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="CLASS_NAME" value="" />
> +      <option name="METHOD_NAME" value="" />
> +      <option name="FOLDER_NAME" value="" />
> +      <option name="TEST_TYPE" value="TEST_SCRIPT" />
> +      <option name="PATTERN" value="" />
> +      <option name="USE_PATTERN" value="false" />
> +      <option name="PUREUNITTEST" value="true" />
> +      <option name="PARAMS" value="" />
> +      <option name="USE_PARAM" value="false" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="tests" factoryName="Doctests">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs />
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="CLASS_NAME" value="" />
> +      <option name="METHOD_NAME" value="" />
> +      <option name="FOLDER_NAME" value="" />
> +      <option name="TEST_TYPE" value="TEST_SCRIPT" />
> +      <option name="PATTERN" value="" />
> +      <option name="USE_PATTERN" value="false" />
> +      <method />
> +    </configuration>
> +    <configuration default="true" type="tests" factoryName="Attests">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs />
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="" />
> +      <option name="IS_MODULE_SDK" value="false" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="" />
> +      <option name="CLASS_NAME" value="" />
> +      <option name="METHOD_NAME" value="" />
> +      <option name="FOLDER_NAME" value="" />
> +      <option name="TEST_TYPE" value="TEST_SCRIPT" />
> +      <option name="PATTERN" value="" />
> +      <option name="USE_PATTERN" value="false" />
> +      <method />
> +    </configuration>
> +    <configuration default="false" name="cert" type="PythonConfigurationType" factoryName="Python">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs>
> +        <env name="PYTHONUNBUFFERED" value="1" />
> +      </envs>
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/base/common/python/pki" />
> +      <option name="IS_MODULE_SDK" value="true" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/base/common/python/pki/cert.py" />
> +      <option name="PARAMETERS" value="" />
> +      <RunnerSettings RunnerId="PythonRunner" />
> +      <ConfigurationWrapper RunnerId="PythonRunner" />
> +      <method />
> +    </configuration>
> +    <configuration default="false" name="profile" type="PythonConfigurationType" factoryName="Python">
> +      <option name="INTERPRETER_OPTIONS" value="" />
> +      <option name="PARENT_ENVS" value="true" />
> +      <envs>
> +        <env name="PYTHONUNBUFFERED" value="1" />
> +      </envs>
> +      <option name="SDK_HOME" value="" />
> +      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/base/common/python/pki" />
> +      <option name="IS_MODULE_SDK" value="true" />
> +      <option name="ADD_CONTENT_ROOTS" value="true" />
> +      <option name="ADD_SOURCE_ROOTS" value="true" />
> +      <module name="pki" />
> +      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/base/common/python/pki/profile.py" />
> +      <option name="PARAMETERS" value="" />
> +      <RunnerSettings RunnerId="PythonRunner" />
> +      <ConfigurationWrapper RunnerId="PythonRunner" />
> +      <method />
> +    </configuration>
> +    <list size="3">
> +      <item index="0" class="java.lang.String" itemvalue="Python.cert" />
> +      <item index="1" class="java.lang.String" itemvalue="Python.profile" />
> +      <item index="2" class="java.lang.String" itemvalue="Python.drmtest" />
> +    </list>
> +    <recent_temporary>
> +      <list size="1">
> +        <item index="0" class="java.lang.String" itemvalue="Python.drmtest" />
> +      </list>
> +    </recent_temporary>
> +  </component>
> +  <component name="ShelveChangesManager" show_recycled="false" />
> +  <component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false" SSL_PROTOCOLS="all">
> +    <option name="USER" value="" />
> +    <option name="PASSWORD" value="" />
> +    <option name="mySSHConnectionTimeout" value="30000" />
> +    <option name="mySSHReadTimeout" value="30000" />
> +    <option name="LAST_MERGED_REVISION" />
> +    <option name="MERGE_DRY_RUN" value="false" />
> +    <option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
> +    <option name="UPDATE_LOCK_ON_DEMAND" value="false" />
> +    <option name="IGNORE_SPACES_IN_MERGE" value="false" />
> +    <option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" />
> +    <option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
> +    <option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
> +    <option name="FORCE_UPDATE" value="false" />
> +    <option name="IGNORE_EXTERNALS" value="false" />
> +    <myIsUseDefaultProxy>false</myIsUseDefaultProxy>
> +  </component>
> +  <component name="TaskManager">
> +    <task active="true" id="Default" summary="Default task">
> +      <changelist id="c7272e4c-4c26-4a55-9c50-4b1b35d87164" name="Default" comment="" />
> +      <created>1399489373098</created>
> +      <updated>1399489373098</updated>
> +    </task>
> +    <servers />
> +  </component>
> +  <component name="ToolWindowManager">
> +    <frame x="0" y="0" width="1920" height="1055" extended-state="0" />
> +    <editor active="true" />
> +    <layout>
> +      <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
> +      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
> +      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
> +      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
> +      <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.18071012" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
> +      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
> +      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
> +      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32911393" sideWeight="0.5" order="8" side_tool="true" content_ui="tabs" />
> +      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.32826087" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
> +      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
> +      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
> +      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
> +      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
> +      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32911393" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
> +      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
> +      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
> +      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
> +    </layout>
> +  </component>
> +  <component name="Vcs.Log.UiProperties">
> +    <option name="RECENTLY_FILTERED_USER_GROUPS">
> +      <collection />
> +    </option>
> +  </component>
> +  <component name="VcsContentAnnotationSettings">
> +    <option name="myLimit" value="2678400000" />
> +  </component>
> +  <component name="VcsManagerConfiguration">
> +    <option name="myTodoPanelSettings">
> +      <TodoPanelSettings />
> +    </option>
> +  </component>
> +  <component name="XDebuggerManager">
> +    <breakpoint-manager />
> +  </component>
> +  <component name="editorHistoryManager">
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="71" column="11" selection-start="2182" selection-end="2182" vertical-scroll-proportion="0.0" vertical-offset="1065" max-vertical-offset="5505" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="186" column="19" selection-start="5179" selection-end="5332" vertical-scroll-proportion="0.0" vertical-offset="12090" max-vertical-offset="13005" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/kra/functional/drmtest.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="63" column="62" selection-start="2479" selection-end="2479" vertical-scroll-proportion="0.0" vertical-offset="945" max-vertical-offset="3645" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="50" selection-start="1722" selection-end="1722" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="795" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="94" column="0" selection-start="2610" selection-end="2610" vertical-scroll-proportion="0.0" vertical-offset="1210" max-vertical-offset="3330">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/client.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="55" column="0" selection-start="1874" selection-end="1874" vertical-scroll-proportion="0.0" vertical-offset="825" max-vertical-offset="1335" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/system.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="97" column="23" selection-start="3204" selection-end="3204" vertical-scroll-proportion="0.0" vertical-offset="1455" max-vertical-offset="2175" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="71" column="11" selection-start="2182" selection-end="2182" vertical-scroll-proportion="0.0" vertical-offset="1065" max-vertical-offset="5505" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="186" column="19" selection-start="5179" selection-end="5332" vertical-scroll-proportion="0.0" vertical-offset="12090" max-vertical-offset="13005" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/kra/functional/drmtest.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="63" column="62" selection-start="2479" selection-end="2479" vertical-scroll-proportion="0.0" vertical-offset="945" max-vertical-offset="3645" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="50" selection-start="1722" selection-end="1722" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="795" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="189" column="0" selection-start="6042" selection-end="6647" vertical-scroll-proportion="0.0" vertical-offset="2635" max-vertical-offset="8925">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/client.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="56" column="8" selection-start="1883" selection-end="1883" vertical-scroll-proportion="0.0" vertical-offset="840" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="0" selection-start="965" selection-end="965" vertical-scroll-proportion="0.0" vertical-offset="435" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="376" column="0" selection-start="11538" selection-end="11538" vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="5730" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="12705" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="50" selection-start="1722" selection-end="1722" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="795" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/json/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="164" column="8" selection-start="6162" selection-end="6162" vertical-scroll-proportion="0.0" vertical-offset="2155" max-vertical-offset="6810" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="433" column="13" selection-start="18082" selection-end="18082" vertical-scroll-proportion="0.0" vertical-offset="60" max-vertical-offset="7320">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="0" selection-start="965" selection-end="965" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="376" column="0" selection-start="11538" selection-end="11538" vertical-scroll-proportion="0.0" vertical-offset="5640" max-vertical-offset="5730" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="8820" max-vertical-offset="12705" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="410" column="0" selection-start="17165" selection-end="17165" vertical-scroll-proportion="0.0" vertical-offset="6285" max-vertical-offset="7200">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="0" selection-start="965" selection-end="965" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="376" column="0" selection-start="11538" selection-end="11538" vertical-scroll-proportion="0.0" vertical-offset="5640" max-vertical-offset="5730" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="8820" max-vertical-offset="12705" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="410" column="0" selection-start="17165" selection-end="17165" vertical-scroll-proportion="0.0" vertical-offset="6582" max-vertical-offset="7200">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="0" selection-start="965" selection-end="965" vertical-scroll-proportion="0.0" vertical-offset="570" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="376" column="0" selection-start="11538" selection-end="11538" vertical-scroll-proportion="0.0" vertical-offset="5640" max-vertical-offset="5730" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="8820" max-vertical-offset="12705" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="186" column="40" selection-start="6757" selection-end="6757" vertical-scroll-proportion="0.0" vertical-offset="2790" max-vertical-offset="6735">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python3.3/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="-0.75409836" vertical-offset="690" max-vertical-offset="1605" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/json/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="164" column="8" selection-start="6162" selection-end="6162" vertical-scroll-proportion="0.33333334" vertical-offset="2155" max-vertical-offset="6810" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/types.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="38" column="0" selection-start="965" selection-end="965" vertical-scroll-proportion="0.14754099" vertical-offset="435" max-vertical-offset="1350" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/eq">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="78" column="17" selection-start="3367" selection-end="3367" vertical-scroll-proportion="0.3322034" vertical-offset="974" max-vertical-offset="389175" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/kra/functional/drmtest.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="98" column="52" selection-start="3798" selection-end="3798" vertical-scroll-proportion="0.0" vertical-offset="945" max-vertical-offset="3645" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/site-packages/simplejson/decoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="392" column="0" selection-start="14386" selection-end="14386" vertical-scroll-proportion="0.8037383" vertical-offset="5375" max-vertical-offset="5910" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib64/python2.7/copy.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="144" column="4" selection-start="3993" selection-end="3993" vertical-scroll-proportion="-0.03382187" vertical-offset="2160" max-vertical-offset="6555" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib/python2.7/site-packages/requests/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1020" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$USER_HOME$/.PyCharm30/system/python_stubs/-1247972723/__builtin__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="659" column="6" selection-start="21724" selection-end="21724" vertical-scroll-proportion="-0.9447576" vertical-offset="10723" max-vertical-offset="77265">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/system.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="12" column="0" selection-start="517" selection-end="517" vertical-scroll-proportion="-0.3697858" vertical-offset="508" max-vertical-offset="1560" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/key.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="427" column="0" selection-start="15300" selection-end="15300" vertical-scroll-proportion="0.524239" vertical-offset="5865" max-vertical-offset="12630" />
> +      </provider>
> +    </entry>
> +    <entry file="file:///usr/lib/python2.7/site-packages/pki/client.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="48" column="0" selection-start="1562" selection-end="1562" vertical-scroll-proportion="0.30665162" vertical-offset="448" max-vertical-offset="1335" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/kraclient.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="945" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/__init__.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="218" column="0" selection-start="7097" selection-end="7097" vertical-scroll-proportion="-0.03382187" vertical-offset="3270" max-vertical-offset="5700" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cryptoutil.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="4020" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/client.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="83" column="0" selection-start="2671" selection-end="2671" vertical-scroll-proportion="0.89853436" vertical-offset="448" max-vertical-offset="1335" />
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/encoder.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="33" column="34" selection-start="1482" selection-end="1482" vertical-scroll-proportion="0.0" vertical-offset="495" max-vertical-offset="810">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/cert.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="1108" column="9" selection-start="41945" selection-end="41945" vertical-scroll-proportion="0.0" vertical-offset="16500" max-vertical-offset="19335">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/account.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="22" column="0" selection-start="803" selection-end="803" vertical-scroll-proportion="0.5641026" vertical-offset="0" max-vertical-offset="615">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +    <entry file="file://$PROJECT_DIR$/base/common/python/pki/profile.py">
> +      <provider selected="true" editor-type-id="text-editor">
> +        <state line="961" column="41" selection-start="28774" selection-end="28774" vertical-scroll-proportion="0.71965814" vertical-offset="13934" max-vertical-offset="14880">
> +          <folding />
> +        </state>
> +      </provider>
> +    </entry>
> +  </component>
> +</project>
> +
> diff --git a/base/common/python/pki/account.py b/base/common/python/pki/account.py
> index 1ab5b2ddb47804771d6cc89cd11f7f1d9b043856..0916ec7cccf25357b75161d08fea32626fdf9486 100644
> --- a/base/common/python/pki/account.py
> +++ b/base/common/python/pki/account.py
> @@ -18,6 +18,7 @@
>  # Copyright (C) 2013 Red Hat, Inc.
>  # All rights reserved.
>  #
> +import pki
>  
>  
>  class AccountClient:
> @@ -25,8 +26,10 @@ class AccountClient:
>      def __init__(self, connection):
>          self.connection = connection
>  
> +    @pki.handle_exceptions()
>      def login(self):
>          self.connection.get('/rest/account/login')
>  
> +    @pki.handle_exceptions()
>      def logout(self):
>          self.connection.get('/rest/account/logout')
> diff --git a/base/common/python/pki/cert.py b/base/common/python/pki/cert.py
> index b22307ad1b2c2456257dc9416208e6725234bf9c..c3ed435d1c395060690f8c15a5b83bfac34059f4 100644
> --- a/base/common/python/pki/cert.py
> +++ b/base/common/python/pki/cert.py
> @@ -14,16 +14,6 @@ import pki.encoder as encoder
>  import pki.profile as profile
>  
>  
> -class CertId(object):
> -    """
> -    Class encapsulating a certificate serial number
> -    """
> -
> -    def __init__(self, cert_id):
> -        """ Constructor """
> -        self.value = cert_id
> -
> -
>  class CertData(object):
>      """
>      Class containing certificate data as returned from getCert()
> @@ -43,6 +33,16 @@ class CertData(object):
>          self.nonce = None
>          self.link = None
>  
> +    def __repr__(self):
> +        attributes = {
> +            "CertData": {
> +                "serial_number": self.serial_number,
> +                "subject_dn": self.subject_dn,
> +                "status": self.status
> +            }
> +        }
> +        return str(attributes)
> +
>      @classmethod
>      def from_json(cls, attr_list):
>          """ Return CertData object from JSON dict """
> @@ -72,7 +72,7 @@ class CertDataInfo(object):
>  
>      def __init__(self):
>          """ Constructor """
> -        self.cert_id = None
> +        self.serial_number = None
>          self.subject_dn = None
>          self.status = None
>          self.type = None
> @@ -85,11 +85,21 @@ class CertDataInfo(object):
>          self.issued_by = None
>          self.link = None
>  
> +    def __repr__(self):
> +        obj = {
> +            "CertDataInfo": {
> +                'serial_number': self.serial_number,
> +                'subject_dn': self.subject_dn,
> +                'type': self.type,
> +                'status': self.status
> +            }}
> +        return str(obj)
> +
>      @classmethod
>      def from_json(cls, attr_list):
>          """ Return CertDataInfo object from JSON dict """
>          cert_data_info = cls()
> -        cert_data_info.cert_id = attr_list['id']
> +        cert_data_info.serial_number = attr_list['id']
>          cert_data_info.subject_dn = attr_list['SubjectDN']
>          cert_data_info.status = attr_list['Status']
>          cert_data_info.type = attr_list['Type']
> @@ -107,25 +117,31 @@ class CertDataInfo(object):
>  
>  class CertDataInfoCollection(object):
>      """
> -    Class containing list of CertDataInfo objects and their respective link objects.
> +    Class containing list of CertDataInfo objects and their respective link
> +    objects.
>      This data is returned when searching/listing certificate records in the CA.
>      """
>  
>      def __init__(self):
>          """ Constructor """
> -        self.cert_info_list = []
> +        self.cert_data_info_list = []
>          self.links = []
>  
> +    def __iter__(self):
> +        for cert_data_info in self.cert_data_info_list:
> +            yield cert_data_info
> +
>      @classmethod
>      def from_json(cls, json_value):
>          """ Populate object from JSON input """
>          ret = cls()
>          cert_infos = json_value['entries']
>          if not isinstance(cert_infos, types.ListType):
> -            ret.cert_info_list.append(CertDataInfo.from_json(cert_infos))
> +            ret.cert_data_info_list.append(CertDataInfo.from_json(cert_infos))
>          else:
>              for cert_info in cert_infos:
> -                ret.cert_info_list.append(CertDataInfo.from_json(cert_info))
> +                ret.cert_data_info_list.append(
> +                    CertDataInfo.from_json(cert_info))
>  
>          links = json_value['Link']
>          if not isinstance(links, types.ListType):
> @@ -155,6 +171,17 @@ class CertRequestInfo(object):
>          self.cert_url = None
>          self.error_message = None
>  
> +    def __repr__(self):
> +        obj = {
> +            'CertRequestInfo': {
> +                'request_id': self.request_id,
> +                'request_type': self.request_type,
> +                'request_status': self.request_status,
> +                'request_url': self.request_url
> +            }
> +        }
> +        return str(obj)
> +
>      @classmethod
>      def from_json(cls, attr_list):
>          cert_request_info = cls()
> @@ -163,7 +190,8 @@ class CertRequestInfo(object):
>          cert_request_info.request_status = attr_list['requestStatus']
>          cert_request_info.operation_result = attr_list['operationResult']
>          cert_request_info.request_id = \
> -            str(cert_request_info.request_url)[(str(cert_request_info.request_url).rfind("/") + 1):]
> +            str(cert_request_info.request_url)[(str(
> +                cert_request_info.request_url).rfind("/") + 1):]
>          #Optional parameters
>          if 'certId' in attr_list:
>              cert_request_info.cert_id = attr_list['certId']
> @@ -184,19 +212,25 @@ class CertRequestInfoCollection(object):
>      """
>  
>      def __init__(self):
> -        self.cert_info_list = []
> +        self.cert_request_info_list = []
>          self.links = []
>  
> +    def __iter__(self):
> +        for cert_request_info in self.cert_request_info_list:
> +            yield cert_request_info
> +
>      @classmethod
>      def from_json(cls, json_value):
>          """ Populate object from JSON input """
>          ret = cls()
>          cert_req_infos = json_value['entries']
>          if not isinstance(cert_req_infos, types.ListType):
> -            ret.cert_info_list.append(CertRequestInfo.from_json(cert_req_infos))
> +            ret.cert_request_info_list.append(
> +                CertRequestInfo.from_json(cert_req_infos))
>          else:
>              for cert_info in cert_req_infos:
> -                ret.cert_info_list.append(CertRequestInfo.from_json(cert_info))
> +                ret.cert_request_info_list.append(
> +                    CertRequestInfo.from_json(cert_info))
>  
>          links = json_value['Link']
>          if not isinstance(links, types.ListType):
> @@ -215,18 +249,28 @@ class CertSearchRequest(object):
>      """
>  
>      search_params = {'serial_to': 'serialTo', 'serial_from': 'serialFrom',
> -                     'email': 'eMail', 'common_name': 'commonName', 'user_id': 'userID',
> -                     'org_unit': 'orgUnit', 'org': 'org', 'locality': 'locality',
> -                     'state': 'state', 'country': 'country', 'match_exactly': 'matchExactly',
> -                     'status': 'status', 'revoked_by': 'revokedBy', 'revoked_on_from': 'revokedOnFrom',
> -                     'revoked_on_to': 'revokedOnTo', 'revocation_reason': 'revocationReason',
> -                     'issued_by': 'issuedBy', 'issued_on_from': 'issuedOnFrom', 'issued_on_to': 'issuedOnTo',
> -                     'valid_not_before_from': 'validNotBeforeFrom', 'valid_not_before_to': 'validNotBeforeTo',
> -                     'valid_not_after_from': 'validNotAfterFrom', 'valid_not_after_to': 'validNotAfterTo',
> -                     'validity_operation': 'validityOperation', 'validity_count': 'validityCount',
> -                     'validity_unit': 'validityUnit', 'cert_type_sub_email_ca': 'certTypeSubEmailCA',
> -                     'cert_type_sub_ssl_ca': 'certTypeSubSSLCA', 'cert_type_secure_email': 'certTypeSecureEmail',
> -                     'cert_type_ssl_client': 'certTypeSSLClient', 'cert_type_ssl_server': 'certTypeSSLServer'}
> +                     'email': 'eMail', 'common_name': 'commonName',
> +                     'user_id': 'userID', 'org_unit': 'orgUnit', 'org': 'org',
> +                     'locality': 'locality', 'state': 'state',
> +                     'country': 'country', 'match_exactly': 'matchExactly',
> +                     'status': 'status', 'revoked_by': 'revokedBy',
> +                     'revoked_on_from': 'revokedOnFrom',
> +                     'revoked_on_to': 'revokedOnTo',
> +                     'revocation_reason': 'revocationReason',
> +                     'issued_by': 'issuedBy', 'issued_on_from': 'issuedOnFrom',
> +                     'issued_on_to': 'issuedOnTo',
> +                     'valid_not_before_from': 'validNotBeforeFrom',
> +                     'valid_not_before_to': 'validNotBeforeTo',
> +                     'valid_not_after_from': 'validNotAfterFrom',
> +                     'valid_not_after_to': 'validNotAfterTo',
> +                     'validity_operation': 'validityOperation',
> +                     'validity_count': 'validityCount',
> +                     'validity_unit': 'validityUnit',
> +                     'cert_type_sub_email_ca': 'certTypeSubEmailCA',
> +                     'cert_type_sub_ssl_ca': 'certTypeSubSSLCA',
> +                     'cert_type_secure_email': 'certTypeSecureEmail',
> +                     'cert_type_ssl_client': 'certTypeSSLClient',
> +                     'cert_type_ssl_server': 'certTypeSSLServer'}
>  
>      def __init__(self, **cert_search_params):
>          """ Constructor """
> @@ -234,59 +278,64 @@ class CertSearchRequest(object):
>          if len(cert_search_params) == 0:
>              setattr(self, 'serialNumberRangeInUse', True)
>  
> -        for param in cert_search_params:
> +        for param, value in cert_search_params.viewitems():
>              if not param in CertSearchRequest.search_params:
>                  raise ValueError('Invalid search parameter: ' + param)
>  
> -            if param == 'serial_to' or param == 'serial_from':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {'serial_to', 'serial_from'}:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'serialNumberRangeInUse', True)
>  
> -            if param == 'email' or param == 'common_name' or param == 'user_id' or param == 'org_unit' \
> -                    or param == 'org' or param == 'locality' or param == 'state' or param == 'country' \
> -                    or param == 'match_exactly':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {
> +                    'email', 'common_name', 'user_id', 'org_unit', 'org',
> +                    'locality', 'state', 'country', 'match_exactly'
> +            }:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'subjectInUse', True)
>  
>              if param == 'status':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +                setattr(self, CertSearchRequest.search_params[param], value)
>  
>              if param == 'revoked_by':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'revokedByInUse', True)
>  
> -            if param == 'revoked_on_from' or param == 'revoked_on_to':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {'revoked_on_from', 'revoked_on_to'}:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'revokedOnInUse', True)
>  
>              if param == 'revocation_reason':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'revocationReasonInUse', True)
>  
>              if param == 'issued_by':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'issuedByInUse', True)
>  
> -            if param == 'issued_on_from' or param == 'issued_on_to':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {'issued_on_from', 'issued_on_to'}:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'issuedOnInUse', True)
>  
> -            if param == 'valid_not_before_from' or param == 'valid_not_before_to':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {'valid_not_before_from', 'valid_not_before_to'}:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'validNotBeforeInUse', True)
>  
> -            if param == 'valid_not_after_from' or param == 'valid_not_after_to':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {'valid_not_after_from', 'valid_not_after_to'}:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'validNotAfterInUse', True)
>  
> -            if param == 'validity_operation' or param == 'validity_count' or param == 'validity_unit':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {
> +                'validity_operation', 'validity_count', 'validity_unit'
> +            }:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'validityLengthInUse', True)
>  
> -            if param == 'cert_type_sub_email_ca' or param == 'cert_type_sub_ssl_ca' \
> -                    or param == 'cert_type_secure_email' or param == 'cert_type_ssl_client' \
> -                    or param == 'cert_type_ssl_server':
> -                setattr(self, CertSearchRequest.search_params[param], cert_search_params[param])
> +            if param in {
> +                'cert_type_sub_email_ca', 'cert_type_sub_ssl_ca',
> +                'cert_type_secure_email', 'cert_type_ssl_client',
> +                'cert_type_ssl_server'
> +            }:
> +                setattr(self, CertSearchRequest.search_params[param], value)
>                  setattr(self, 'certTypeInUse', True)
>  
>  
> @@ -309,9 +358,25 @@ class CertRevokeRequest(object):
>  
>      def __init__(self, nonce, reason=None, invalidity_date=None, comments=None):
>          """ Constructor """
> +
>          setattr(self, "Nonce", nonce)
> +
>          if reason is None:
>              reason = self.REASON_UNSPECIFIED
> +        else:
> +            if reason not in {
> +                CertRevokeRequest.REASON_AA_COMPROMISE,
> +                CertRevokeRequest.REASON_AFFILIATION_CHANGED,
> +                CertRevokeRequest.REASON_CA_COMPROMISE,
> +                CertRevokeRequest.REASON_CERTIFICATE_HOLD,
> +                CertRevokeRequest.REASON_CESSATION_OF_OPERATION,
> +                CertRevokeRequest.REASON_KEY_COMPROMISE,
> +                CertRevokeRequest.REASON_PRIVILEGE_WITHDRAWN,
> +                CertRevokeRequest.REASON_REMOVE_FROM_CRL,
> +                CertRevokeRequest.REASON_SUPERSEDED,
> +                CertRevokeRequest.REASON_UNSPECIFIED
> +            }:
> +                raise ValueError('Invalid revocation reason specified.')
>          setattr(self, "Reason", reason)
>          if invalidity_date is not None:
>              setattr(self, "InvalidityDate", invalidity_date)
> @@ -321,11 +386,13 @@ class CertRevokeRequest(object):
>  
>  class CertEnrollmentRequest(object):
>      """
> -    This class encapsulates the parameters required for a certificate enrollment request.
> +    This class encapsulates the parameters required for a certificate
> +     enrollment request.
>      """
>  
> -    def __init__(self, profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None,
> -                 inputs=None, outputs=None):
> +    def __init__(self, profile_id=None, renewal=False, serial_number=None,
> +                 remote_host=None, remote_address=None, inputs=None,
> +                 outputs=None):
>          """ Constructor """
>          self.profile_id = profile_id
>          self.renewal = renewal
> @@ -443,14 +510,17 @@ class CertEnrollmentRequest(object):
>              enroll_request.inputs.append(profile.ProfileInput.from_json(inputs))
>          else:
>              for profile_input in inputs:
> -                enroll_request.inputs.append(profile.ProfileInput.from_json(profile_input))
> +                enroll_request.inputs.append(
> +                    profile.ProfileInput.from_json(profile_input))
>  
>          outputs = json_value['Output']
>          if not isinstance(outputs, types.ListType):
> -            enroll_request.outputs.append(profile.ProfileOutput.from_json(outputs))
> +            enroll_request.outputs.append(
> +                profile.ProfileOutput.from_json(outputs))
>          else:
>              for profile_output in outputs:
> -                enroll_request.outputs.append(profile.ProfileOutput.from_json(profile_output))
> +                enroll_request.outputs.append(
> +                    profile.ProfileOutput.from_json(profile_output))
>  
>          return enroll_request
>  
> @@ -462,14 +532,21 @@ class CertReviewResponse(CertEnrollmentRequest):
>      It contains a nonce required to perform action on the request.
>      """
>  
> -    def __init__(self, profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None,
> -                 inputs=None, outputs=None, nonce=None, request_id=None, request_type=None, request_status=None,
> -                 request_owner=None, request_creation_time=None, request_modification_time=None, request_notes=None,
> -                 profile_approval_by=None, profile_set_id=None, profile_is_visible=None, profile_name=None,
> -                 profile_description=None, profile_remote_host=None, profile_remote_address=None, policy_sets=None):
> +    def __init__(self, profile_id=None, renewal=False, serial_number=None,
> +                 remote_host=None, remote_address=None, inputs=None,
> +                 outputs=None, nonce=None, request_id=None, request_type=None,
> +                 request_status=None, request_owner=None,
> +                 request_creation_time=None, request_modification_time=None,
> +                 request_notes=None, profile_approval_by=None,
> +                 profile_set_id=None, profile_is_visible=None,
> +                 profile_name=None, profile_description=None,
> +                 profile_remote_host=None, profile_remote_address=None,
> +                 policy_sets=None):
>  
> -        super(CertReviewResponse, self).__init__(profile_id, renewal, serial_number, remote_host,
> -                                                 remote_address, inputs, outputs)
> +        super(CertReviewResponse, self).__init__(profile_id, renewal,
> +                                                 serial_number, remote_host,
> +                                                 remote_address, inputs,
> +                                                 outputs)
>          self.nonce = nonce
>          self.request_id = request_id
>          self.request_type = request_type
> @@ -622,8 +699,10 @@ class CertReviewResponse(CertEnrollmentRequest):
>          review_response.request_type = json_value['requestType']
>          review_response.request_status = json_value['requestStatus']
>          review_response.request_owner = json_value['requestOwner']
> -        review_response.request_creation_time = json_value['requestCreationTime']
> -        review_response.request_modification_time = json_value['requestModificationTime']
> +        review_response.request_creation_time = \
> +            json_value['requestCreationTime']
> +        review_response.request_modification_time = \
> +            json_value['requestModificationTime']
>          review_response.request_notes = json_value['requestNotes']
>          review_response.profile_approved_by = json_value['profileApprovedBy']
>          review_response.profile_set_id = json_value['profileSetId']
> @@ -635,18 +714,20 @@ class CertReviewResponse(CertEnrollmentRequest):
>  
>          profile_policy_sets = json_value['ProfilePolicySet']
>          if not isinstance(profile_policy_sets, types.ListType):
> -            review_response.policy_sets.append(profile.ProfilePolicySet.from_json(profile_policy_sets))
> +            review_response.policy_sets.append(
> +                profile.ProfilePolicySet.from_json(profile_policy_sets))
>          else:
>              for policy_set in profile_policy_sets:
> -                review_response.policy_sets.append(profile.ProfilePolicySet.from_json(policy_set))
> +                review_response.policy_sets.append(
> +                    profile.ProfilePolicySet.from_json(policy_set))
>  
>          return review_response
>  
>  
>  class CertClient(object):
>      """
> -    Class encapsulating and mirroring the functionality in the CertResource Java interface class
> -    defining the REST API for Certificate resources.
> +    Class encapsulating and mirroring the functionality in the CertResource
> +    Java interface class defining the REST API for Certificate resources.
>      """
>  
>      def __init__(self, connection):
> @@ -661,101 +742,123 @@ class CertClient(object):
>          self.enrollment_templates = {}
>  
>      @pki.handle_exceptions()
> -    def get_cert(self, cert_id):
> +    def get_cert(self, cert_serial_number):
>          """ Return a CertData object for a particular certificate. """
> -        if cert_id is None:
> +        if cert_serial_number is None:
>              raise ValueError("Certificate ID must be specified")
>  
> -        url = self.cert_url + '/' + str(cert_id)
> +        url = self.cert_url + '/' + str(cert_serial_number)
>          r = self.connection.get(url, self.headers)
>          return CertData.from_json(r.json())
>  
>      @pki.handle_exceptions()
> -    def list_certs(self, max_results=None, max_time=None, start=None, size=None, **cert_search_params):
> -        """ Return a CertDataInfoCollection object with a information about all the
> -            certificates that satisfy the search criteria.
> +    def list_certs(self, max_results=None, max_time=None, start=None, size=None,
> +                   **cert_search_params):
> +        """ Return a CertDataInfoCollection object with a information about all
> +            the certificates that satisfy the search criteria.
>              If cert_search_request=None, returns all the certificates.
>          """
>          url = self.cert_url + '/search'
> -        query_params = {"maxResults": max_results, "maxTime": max_time, "start": start, "size": size}
> +        query_params = {"maxResults": max_results, "maxTime": max_time,
> +                        "start": start, "size": size}
>          cert_search_request = CertSearchRequest(**cert_search_params)
> -        search_request = json.dumps(cert_search_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
> -        response = self.connection.post(url, search_request, self.headers, query_params)
> +        search_request = json.dumps(cert_search_request,
> +                                    cls=encoder.CustomTypeEncoder,
> +                                    sort_keys=True)
> +        response = self.connection.post(url, search_request, self.headers,
> +                                        query_params)
>          return CertDataInfoCollection.from_json(response.json())
>  
>      @pki.handle_exceptions()
> -    def review_cert(self, cert_id):
> +    def review_cert(self, cert_serial_number):
>          """ Reviews a certificate. Returns a CertData object with a nonce.
> -            This method requires an agent's authentication cert in the connection object.
> +            This method requires an agent's authentication cert in the
> +            connection object.
>          """
> -        if cert_id is None:
> +        if cert_serial_number is None:
>              raise ValueError("Certificate ID must be specified")
>  
> -        url = self.agent_cert_url + '/' + str(cert_id)
> +        url = self.agent_cert_url + '/' + str(cert_serial_number)
>          r = self.connection.get(url, self.headers)
>          return CertData.from_json(r.json())
>  
> -    def _submit_revoke_request(self, url, cert_id, revocation_reason=None, invalidity_date=None, comments=None,
> -                               nonce=None):
> +    def _submit_revoke_request(self, url, cert_serial_number,
> +                               revocation_reason=None, invalidity_date=None,
> +                               comments=None, nonce=None):
>          """
>          Submits a certificate revocation request.
>          Expects the URL for submitting the request.
>          Creates a CertRevokeRequest object using the arguments provided.
> -        If nonce is passed as an argument, reviews the cert to get a nonce from the server
> -        and passes it in the request.
> +        If nonce is passed as an argument, reviews the cert to get a nonce
> +        from the server and passes it in the request.
>          Returns a CertRequestInfo object.
>          """
> -        if cert_id is None:
> +        if cert_serial_number is None:
>              raise ValueError("Certificate ID must be specified")
>  
>          if url is None:
>              raise ValueError("URL not specified")
>  
>          if nonce is None:
> -            cert_data = self.review_cert(cert_id)
> +            cert_data = self.review_cert(cert_serial_number)
>              nonce = cert_data.nonce
> -        request = CertRevokeRequest(nonce, revocation_reason, invalidity_date, comments)
> -        revoke_request = json.dumps(request, cls=encoder.CustomTypeEncoder, sort_keys=True)
> +        request = CertRevokeRequest(nonce, revocation_reason, invalidity_date,
> +                                    comments)
> +        revoke_request = json.dumps(request, cls=encoder.CustomTypeEncoder,
> +                                    sort_keys=True)
>          r = self.connection.post(url, revoke_request, headers=self.headers)
>          return CertRequestInfo.from_json(r.json())
>  
>      @pki.handle_exceptions()
> -    def revoke_cert(self, cert_id, revocation_reason=None, invalidity_date=None, comments=None, nonce=None):
> +    def revoke_cert(self, cert_serial_number, revocation_reason=None,
> +                    invalidity_date=None, comments=None, nonce=None):
>          """ Revokes a certificate.
>              Returns a CertRequestInfo object with information about the request.
> -            This method requires an agent's authentication cert in the connection object.
> +            This method requires an agent's authentication cert in the
> +            connection object.
>          """
> -        url = self.agent_cert_url + '/' + str(cert_id) + '/revoke'
> -        return self._submit_revoke_request(url, cert_id, revocation_reason, invalidity_date, comments, nonce)
> +        url = self.agent_cert_url + '/' + str(cert_serial_number) + '/revoke'
> +        return self._submit_revoke_request(url, cert_serial_number,
> +                                           revocation_reason, invalidity_date,
> +                                           comments, nonce)
>  
>      @pki.handle_exceptions()
> -    def revoke_ca_cert(self, cert_id, revocation_reason=None, invalidity_date=None, comments=None, nonce=None):
> +    def revoke_ca_cert(self, cert_serial_number, revocation_reason=None,
> +                       invalidity_date=None, comments=None, nonce=None):
>          """ Revokes a CA certificate.
>              Returns a CertRequestInfo object with information about the request.
> -            This method requires an agent's authentication cert in the connection object.
> +            This method requires an agent's authentication cert in the
> +            connection object.
>          """
> -        url = self.agent_cert_url + '/' + str(cert_id) + '/revoke-ca'
> -        return self._submit_revoke_request(url, cert_id, revocation_reason, invalidity_date, comments, nonce)
> +        url = self.agent_cert_url + '/' + str(cert_serial_number) + '/revoke-ca'
> +        return self._submit_revoke_request(url, cert_serial_number,
> +                                           revocation_reason, invalidity_date,
> +                                           comments, nonce)
>  
>      @pki.handle_exceptions()
> -    def hold_cert(self, cert_id, comments=None):
> +    def hold_cert(self, cert_serial_number, comments=None):
>          """ Places a certificate on-hold.
> -            Calls the revoke_cert method with reason - CertRevokeRequest.REASON_CERTIFICATE_HOLD.
> +            Calls the revoke_cert method with reason -
> +            CertRevokeRequest.REASON_CERTIFICATE_HOLD.
>              Returns a CertRequestInfo object.
> -            This method requires an agent's authentication cert in the connection object.
> +            This method requires an agent's authentication cert in the
> +            connection object.
>          """
> -        return self.revoke_cert(cert_id, CertRevokeRequest.REASON_CERTIFICATE_HOLD, comments=comments)
> +        return self.revoke_cert(cert_serial_number,
> +                                CertRevokeRequest.REASON_CERTIFICATE_HOLD,
> +                                comments=comments)
>  
>      @pki.handle_exceptions()
> -    def unrevoke_cert(self, cert_id):
> +    def unrevoke_cert(self, cert_serial_number):
>          """ Un-revokes a revoked certificate.
>              Returns a CertRequestInfo object.
> -            This method requires an agent's authentication cert in the connection object.
> +            This method requires an agent's authentication cert in the
> +            connection object.
>          """
> -        if cert_id is None:
> +        if cert_serial_number is None:
>              raise ValueError("Certificate ID must be specified")
>  
> -        url = self.agent_cert_url + '/' + str(cert_id) + '/unrevoke'
> +        url = self.agent_cert_url + '/' + str(cert_serial_number) + '/unrevoke'
>          r = self.connection.post(url, None, headers=self.headers)
>          return CertRequestInfo.from_json(r.json())
>  
> @@ -774,8 +877,9 @@ class CertClient(object):
>          return CertRequestInfo.from_json(r.json())
>  
>      @pki.handle_exceptions()
> -    def list_requests(self, request_status=None, request_type=None, from_request_id=None, size=None,
> -                      max_results=None, max_time=None):
> +    def list_requests(self, request_status=None, request_type=None,
> +                      from_request_id=None, size=None, max_results=None,
> +                      max_time=None):
>          """
>          Query for a list of certificates using the arguments passed.
>          Returns a CertRequestInfoCollection object.
> @@ -789,7 +893,8 @@ class CertClient(object):
>              'maxResults': max_results,
>              'maxTime': max_time
>          }
> -        r = self.connection.get(self.agent_cert_requests_url, self.headers, query_params)
> +        r = self.connection.get(self.agent_cert_requests_url, self.headers,
> +                                query_params)
>          return CertRequestInfoCollection.from_json(r.json())
>  
>      @pki.handle_exceptions()
> @@ -819,15 +924,17 @@ class CertClient(object):
>              cert_review_response = self.review_request(request_id)
>  
>          url = self.agent_cert_requests_url + '/' + request_id + '/' + action
> -        review_response = json.dumps(cert_review_response, cls=encoder.CustomTypeEncoder, sort_keys=True)
> +        review_response = json.dumps(cert_review_response,
> +                                     cls=encoder. CustomTypeEncoder,
> +                                     sort_keys=True)
>          r = self.connection.post(url, review_response, headers=self.headers)
>          return r
>  
>      def approve_request(self, request_id, cert_review_response=None):
>          """
>          Approves a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
>          return self._perform_action(request_id, cert_review_response, 'approve')
> @@ -835,8 +942,8 @@ class CertClient(object):
>      def cancel_request(self, request_id, cert_review_response=None):
>          """
>          Cancels a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
>          return self._perform_action(request_id, cert_review_response, 'cancel')
> @@ -844,8 +951,8 @@ class CertClient(object):
>      def reject_request(self, request_id,  cert_review_response=None):
>          """
>          Rejects a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
>          return self._perform_action(request_id, cert_review_response, 'reject')
> @@ -853,17 +960,18 @@ class CertClient(object):
>      def validate_request(self, request_id, cert_review_response):
>          """
>          Validates a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
> -        return self._perform_action(request_id, cert_review_response, 'validate')
> +        return self._perform_action(request_id, cert_review_response,
> +                                    'validate')
>  
>      def update_request(self, request_id, cert_review_response):
>          """
>          Updates a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
>          return self._perform_action(request_id, cert_review_response, 'update')
> @@ -871,8 +979,8 @@ class CertClient(object):
>      def assign_request(self, request_id, cert_review_response):
>          """
>          Assigns a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
>          return self._perform_action(request_id, cert_review_response, 'assign')
> @@ -880,17 +988,19 @@ class CertClient(object):
>      def unassign_request(self, request_id, cert_review_response):
>          """
>          Un-assigns a certificate enrollment request.
> -        If cert_review_response is None, a review request operation is performed to fetch the
> -        CertReviewResponse object.
> +        If cert_review_response is None, a review request operation is performed
> +        to fetch the CertReviewResponse object.
>          Requires as agent level authentication.
>          """
> -        return self._perform_action(request_id, cert_review_response, 'unassign')
> +        return self._perform_action(request_id, cert_review_response,
> +                                    'unassign')
>  
>      @pki.handle_exceptions()
>      def list_enrollment_templates(self, start=None, size=None):
>          """
>          Gets the list of profile templates supported by the CA.
> -        The values for start and size arguments determine the starting point and the length of the list.
> +        The values for start and size arguments determine the starting point and
> +        the length of the list.
>          Returns a ProfileDataInfoCollection object.
>          """
>  
> @@ -900,7 +1010,6 @@ class CertClient(object):
>              'size': size
>          }
>          r = self.connection.get(url, self.headers, query_params)
> -        print r
>          return profile.ProfileDataInfoCollection.from_json(r.json())
>  
>      @pki.handle_exceptions()
> @@ -908,10 +1017,13 @@ class CertClient(object):
>          """
>          Fetch the enrollment template for the given profile id.
>          For the first time, the request is sent to the server.
> -        The retrieved CertEnrollmentRequest object is then cached locally for future requests.
> +        The retrieved CertEnrollmentRequest object is then cached locally for
> +        future requests.
>          Returns a CerEnrollmentRequest object.
>          """
>  
> +        if profile_id is None:
> +            raise ValueError("Profile ID must be specified.")
>          if profile_id in self.enrollment_templates:
>              return copy.deepcopy(self.enrollment_templates[profile_id])
>          url = self.cert_requests_url + '/profiles/' + str(profile_id)
> @@ -927,8 +1039,10 @@ class CertClient(object):
>      def create_enrollment_request(self, profile_id, inputs):
>          """
>          Fetches the enrollment request object for the given profile and
> -        sets values to its attributes using the values provided in the inputs dictionary.
> -        Returns the CertEnrollmentRequest object, which can be submitted to enroll a certificate.
> +        sets values to its attributes using the values provided in the inputs
> +        dictionary.
> +        Returns the CertEnrollmentRequest object, which can be submitted to
> +        enroll a certificate.
>          """
>          if inputs is None or len(inputs) == 0:
>              raise ValueError("No inputs provided.")
> @@ -945,42 +1059,48 @@ class CertClient(object):
>      def submit_enrollment_request(self, enrollment_request):
>          """
>          Submits the CertEnrollmentRequest object to the server.
> -        Returns a CertRequestInfoCollection object with information about the certificate requests
> -        enrolled at the CA.
> +        Returns a CertRequestInfoCollection object with information about the
> +        certificate requests enrolled at the CA.
>          """
> -        request_object = json.dumps(enrollment_request, cls=encoder.CustomTypeEncoder, sort_keys=True)
> -        r = self.connection.post(self.cert_requests_url, request_object, self.headers)
> +        request_object = json.dumps(enrollment_request,
> +                                    cls=encoder.CustomTypeEncoder,
> +                                    sort_keys=True)
> +        r = self.connection.post(self.cert_requests_url, request_object,
> +                                 self.headers)
>          return CertRequestInfoCollection.from_json(r.json())
>  
>      @pki.handle_exceptions()
>      def enroll_cert(self, profile_id, inputs):
>          """
>          A convenience method for enrolling a certificate for a given profile id.
> -        The inputs parameter should be a dictionary with values for the profile attributes
> -        for an enrollment request.
> +        The inputs parameter should be a dictionary with values for the profile
> +        attributes for an enrollment request.
>  
> -        Calling this method with valid arguments, creates an enrollment request, submits it
> -        to the server, approves the certificate requests generated for the enrollment and
> -        returns a list of CertData objects for all the certificates generated as part of this
> -        enrollment.
> +        Calling this method with valid arguments, creates an enrollment request,
> +        submits it to the server, approves the certificate requests generated
> +        for the enrollment and returns a list of CertData objects for all the
> +        certificates generated as part of this enrollment.
>  
> -        Note: This method supports only certificate enrollment where only one agent approval
> -        is sufficient.
> +        Note: This method supports only certificate enrollment where only one
> +        agent approval is sufficient.
>  
>          Requires an agent level authentication.
> +        Returns a list of CertData objects.
>          """
>  
> -        # Create a CertEnrollmentRequest object using the inputs for the given profile id.
> +        # Create a CertEnrollmentRequest object using the inputs for the given
> +        #  profile id.
>          enroll_request = self.create_enrollment_request(profile_id, inputs)
>  
>          # Submit the enrollment request
>          cert_request_infos = self.submit_enrollment_request(enroll_request)
>  
>          # Approve the requests generated for the certificate enrollment.
> -        # Fetch the CertData objects for all the certificates created and return to the caller.
> +        # Fetch the CertData objects for all the certificates created and
> +        # return to the caller.
>  
>          certificates = []
> -        for cert_request_info in cert_request_infos.cert_info_list:
> +        for cert_request_info in cert_request_infos.cert_request_info_list:
>              request_id = cert_request_info.request_id
>              self.approve_request(request_id)
>              cert_id = self.get_request(request_id).cert_id
> @@ -1010,7 +1130,8 @@ def main():
>      # Create a PKIConnection object that stores the details of the CA.
>      connection = client.PKIConnection('https', 'localhost', '8443', 'ca')
>  
> -    # The pem file used for authentication. Created from a p12 file using the command -
> +    # The pem file used for authentication. Created from a p12 file using the
> +    # command -
>      # openssl pkcs12 -in <p12_file_path> -out /tmp/auth.pem -nodes
>      connection.set_authentication_cert("/tmp/auth.pem")
>  
> @@ -1025,13 +1146,19 @@ def main():
>  
>      inputs = dict()
>      inputs['cert_request_type'] = 'crmf'
> -    inputs['cert_request'] = "MIIBpDCCAaAwggEGAgUA5n9VYTCBx4ABAqUOMAwxCjAIBgNVBAMTAXimgZ8wDQYJKoZIhvcNAQEBBQAD" \
> -                             "gY0AMIGJAoGBAK/SmUVoUjBtqHNw/e3OoCSXw42pdQSR53/eYJWpf7nyTbZ9UuIhGfXOtxy5vRetmDHE" \
> -                             "9u0AopmuJbr1rL17/tSnDakpkE9umQ2lMOReLloSdX32w2xOeulUwh5BGbFpq10S0SvW1H93Vn0eCy2a" \
> -                             "a4UtILNEsp7JJ3FnYJibfuMPAgMBAAGpEDAOBgNVHQ8BAf8EBAMCBeAwMzAVBgkrBgEFBQcFAQEMCHJl" \
> -                             "Z1Rva2VuMBoGCSsGAQUFBwUBAgwNYXV0aGVudGljYXRvcqGBkzANBgkqhkiG9w0BAQUFAAOBgQCuywnr" \
> -                             "Dk/wGwfbguw9oVs9gzFQwM4zeFbk+z82G5CWoG/4mVOT5LPL5Q8iF+KfnaU9Qcu6zZPxW6ZmDd8WpPJ+" \
> -                             "MTPyQl3Q5BfiKa4l5ra1NeqxMOlMiiupwINmm7jd1KaA2eIjuyC8/gTaO4b14R6aRaOj+Scp9cNYbthA7REhJw=="
> +    inputs['cert_request'] = "MIIBpDCCAaAwggEGAgUA5n9VYTCBx4ABAqUOMAwxCjAIBgN" \
> +                             "VBAMTAXimgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK" \
> +                             "/SmUVoUjBtqHNw/e3OoCSXw42pdQSR53/eYJWpf7nyTbZ9U" \
> +                             "uIhGfXOtxy5vRetmDHE9u0AopmuJbr1rL17/tSnDakpkE9u" \
> +                             "mQ2lMOReLloSdX32w2xOeulUwh5BGbFpq10S0SvW1H93Vn0" \
> +                             "eCy2aa4UtILNEsp7JJ3FnYJibfuMPAgMBAAGpEDAOBgNVHQ" \
> +                             "8BAf8EBAMCBeAwMzAVBgkrBgEFBQcFAQEMCHJlZ1Rva2VuM" \
> +                             "BoGCSsGAQUFBwUBAgwNYXV0aGVudGljYXRvcqGBkzANBgkq" \
> +                             "hkiG9w0BAQUFAAOBgQCuywnrDk/wGwfbguw9oVs9gzFQwM4" \
> +                             "zeFbk+z82G5CWoG/4mVOT5LPL5Q8iF+KfnaU9Qcu6zZPxW6" \
> +                             "ZmDd8WpPJ+MTPyQl3Q5BfiKa4l5ra1NeqxMOlMiiupwINmm" \
> +                             "7jd1KaA2eIjuyC8/gTaO4b14R6aRaOj+Scp9cNYbthA7REh" \
> +                             "Jw=="
>      inputs['sn_uid'] = 'test12345'
>      inputs['sn_e'] = 'example at redhat.com'
>      inputs['sn_cn'] = 'TestUser'
> @@ -1053,13 +1180,18 @@ def main():
>  
>      inputs = dict()
>      inputs['cert_request_type'] = 'pkcs10'
> -    inputs['cert_request'] = "MIIBmDCCAQECAQAwWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5DMRAwDgYDVQQHDAdSYWxlaWdoMRUwE" \
> -                             "wYDVQQKDAxSZWQgSGF0IEluYy4xEzARBgNVBAMMClRlc3RTZXJ2ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY" \
> -                             "0AMIGJAoGBAMJpWz92dSYCvWxllrQCY5atPKCswUwyppRNGPnKmJ77AdHBBI4dFyET+h/+69jQMTLZMa8" \
> -                             "FX7SbyHvgbgLBP4Q/RzCSE2S87qFNjriOqiQCqJmcrzDzdncJQiP+O7T6MSpLo3smLP7dK1Vd7vK0Vy8y" \
> -                             "HwV0eBx7DgYedv2slBPHAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQBvkxAGKwkfK3TKwLc5Mg0IWp8zG" \
> -                             "RVwxdIlghAL8DugNocCNNgmZazglJOOehLuk0/NkLX1ZM5RrVgM09W6kcfWZtIwr5Uje2K/+6tW2ZTGrb" \
> -                             "izs7CNOTMzA/9H8CkHb4H9P/qRT275zHIocYj4smUnXLwWGsBMeGs+OMMbGvSrHg=="
> +    inputs['cert_request'] = "MIIBmDCCAQECAQAwWDELMAkGA1UEBhMCVVMxCzAJBgNVBAg" \
> +                             "MAk5DMRAwDgYDVQQHDAdSYWxlaWdoMRUwEwYDVQQKDAxSZW" \
> +                             "QgSGF0IEluYy4xEzARBgNVBAMMClRlc3RTZXJ2ZXIwgZ8wD" \
> +                             "QYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMJpWz92dSYCvWxl" \
> +                             "lrQCY5atPKCswUwyppRNGPnKmJ77AdHBBI4dFyET+h/+69j" \
> +                             "QMTLZMa8FX7SbyHvgbgLBP4Q/RzCSE2S87qFNjriOqiQCqJ" \
> +                             "mcrzDzdncJQiP+O7T6MSpLo3smLP7dK1Vd7vK0Vy8yHwV0e" \
> +                             "Bx7DgYedv2slBPHAgMBAAGgADANBgkqhkiG9w0BAQUFAAOB" \
> +                             "gQBvkxAGKwkfK3TKwLc5Mg0IWp8zGRVwxdIlghAL8DugNoc" \
> +                             "CNNgmZazglJOOehLuk0/NkLX1ZM5RrVgM09W6kcfWZtIwr5" \
> +                             "Uje2K/+6tW2ZTGrbizs7CNOTMzA/9H8CkHb4H9P/qRT275z" \
> +                             "HIocYj4smUnXLwWGsBMeGs+OMMbGvSrHg=="
>  
>      inputs['requestor_name'] = 'Tester'
>      inputs['requestor_email'] = 'example at redhat.com'
> @@ -1080,8 +1212,8 @@ def main():
>  
>      search_params = {'status': 'VALID'}
>      cert_data_list = cert_client.list_certs(**search_params)
> -    for cert_data_info in cert_data_list.cert_info_list:
> -        print("Serial Number: " + cert_data_info.cert_id)
> +    for cert_data_info in cert_data_list:
> +        print("Serial Number: " + cert_data_info.serial_number)
>          print("Subject DN: " + cert_data_info.subject_dn)
>          print("Status: " + cert_data_info.status)
>      print
> @@ -1099,7 +1231,8 @@ def main():
>  
>      # Certificate Serial Number used for CertClient methods.
>      # 7, 0x7 and '0x7' are also valid values
> -    # Following examples use the serial number of the user certificate enrolled before.
> +    # Following examples use the serial number of the user certificate enrolled
> +    #  before.
>      cert_id = cert_data_infos[0].serial_number
>  
>      #Get certificate data
> @@ -1136,9 +1269,8 @@ def main():
>      print('Revoking a certificate')
>      print('----------------------')
>  
> -    cert_request_info = cert_client.revoke_cert(cert_data.serial_number,
> -                                                revocation_reason=CertRevokeRequest.REASON_CERTIFICATE_HOLD,
> -                                                comments="Test revoking a cert", nonce=cert_data.nonce)
> +    cert_request_info = cert_client.hold_cert(cert_data.serial_number,
> +                                              comments="Test revoking a cert")
>      print('Request ID: ' + cert_request_info.request_id)
>      print('Request Type: ' + cert_request_info.request_type)
>      print('Request Status: ' + cert_request_info.request_status)
> diff --git a/base/common/python/pki/profile.py b/base/common/python/pki/profile.py
> index 83cd8bcca0d9ad3841b0b6b74e2aa4a6724e5bbc..0f2b4e3106260807433bf870706a6334d62c637c 100644
> --- a/base/common/python/pki/profile.py
> +++ b/base/common/python/pki/profile.py
> @@ -20,6 +20,17 @@ class ProfileDataInfo(object):
>          self.profile_description = None
>          self.profile_url = None
>  
> +    def __repr__(self):
> +        attributes = {
> +            "ProfileDataInfo": {
> +                'profile_id': self.profile_id,
> +                'name': self.profile_name,
> +                'description': self.profile_description,
> +                'url': self.profile_url
> +            }
> +        }
> +        return str(attributes)
> +
>      @classmethod
>      def from_json(cls, attr_list):
>          profile_data_info = cls()
> @@ -41,15 +52,21 @@ class ProfileDataInfoCollection(object):
>          self.profile_data_list = []
>          self.links = []
>  
> +    def __iter__(self):
> +        for profile_data_info in self.profile_data_list:
> +            yield profile_data_info
> +
>      @classmethod
>      def from_json(cls, json_value):
>          ret = cls()
>          profile_data_infos = json_value['entries']
>          if not isinstance(profile_data_infos, types.ListType):
> -            ret.profile_data_list.append(ProfileDataInfo.from_json(profile_data_infos))
> +            ret.profile_data_list.append(
> +                ProfileDataInfo.from_json(profile_data_infos))
>          else:
>              for profile_info in profile_data_infos:
> -                ret.profile_data_list.append(ProfileDataInfo.from_json(profile_info))
> +                ret.profile_data_list.append(
> +                    ProfileDataInfo.from_json(profile_info))
>  
>          links = json_value['Link']
>          if not isinstance(links, types.ListType):
> @@ -64,10 +81,12 @@ class ProfileDataInfoCollection(object):
>  class Descriptor(object):
>      """
>      This class represents the description of a ProfileAttribute.
> -    It stores information such as the syntax, constraint and default value of a profile attribute.
> +    It stores information such as the syntax, constraint and default value of
> +    a profile attribute.
>      """
>  
> -    def __init__(self, syntax=None, constraint=None, description=None, default_value=None):
> +    def __init__(self, syntax=None, constraint=None, description=None,
> +                 default_value=None):
>          self.syntax = syntax
>          self.constraint = constraint
>          self.description = description
> @@ -158,8 +177,8 @@ class ProfileInput(object):
>      Ex. Subject name, Requestor Information etc.
>      """
>  
> -    def __init__(self, profile_input_id=None, class_id=None, name=None, text=None, attributes=None,
> -                 config_attributes=None):
> +    def __init__(self, profile_input_id=None, class_id=None, name=None,
> +                 text=None, attributes=None, config_attributes=None):
>  
>          self.profile_input_id = profile_input_id
>          self.class_id = class_id
> @@ -261,17 +280,21 @@ class ProfileInput(object):
>  
>          attributes = json_value['Attribute']
>          if not isinstance(attributes, types.ListType):
> -            profile_input.attributes.append(ProfileAttribute.from_json(attributes))
> +            profile_input.attributes.append(
> +                ProfileAttribute.from_json(attributes))
>          else:
>              for profile_info in attributes:
> -                profile_input.attributes.append(ProfileAttribute.from_json(profile_info))
> +                profile_input.attributes.append(
> +                    ProfileAttribute.from_json(profile_info))
>  
>          config_attributes = json_value['ConfigAttribute']
>          if not isinstance(config_attributes, types.ListType):
> -            profile_input.config_attributes.append(ProfileAttribute.from_json(config_attributes))
> +            profile_input.config_attributes.append(
> +                ProfileAttribute.from_json(config_attributes))
>          else:
>              for config_attribute in config_attributes:
> -                profile_input.config_attributes.append(ProfileAttribute.from_json(config_attribute))
> +                profile_input.config_attributes.append(
> +                    ProfileAttribute.from_json(config_attribute))
>  
>          return profile_input
>  
> @@ -282,7 +305,8 @@ class ProfileOutput(object):
>      using a profile.
>      """
>  
> -    def __init__(self, profile_output_id=None, name=None, text=None, class_id=None, attributes=None):
> +    def __init__(self, profile_output_id=None, name=None, text=None,
> +                 class_id=None, attributes=None):
>          self.profile_output_id = profile_output_id
>          self.name = name
>          self.text = text
> @@ -332,10 +356,12 @@ class ProfileOutput(object):
>          profile_output.class_id = json_value['classId']
>          attributes = json_value['attributes']
>          if not isinstance(attributes, types.ListType):
> -            profile_output.attributes.append(ProfileAttribute.from_json(attributes))
> +            profile_output.attributes.append(
> +                ProfileAttribute.from_json(attributes))
>          else:
>              for profile_info in attributes:
> -                profile_output.attributes.append(ProfileAttribute.from_json(profile_info))
> +                profile_output.attributes.append(
> +                    ProfileAttribute.from_json(profile_info))
>          return profile_output
>  
>  
> @@ -355,10 +381,12 @@ class ProfileParameter(object):
>  
>  class PolicyDefault(object):
>      """
> -    An object of this class contains information of the default usage of a specific ProfileInput.
> +    An object of this class contains information of the default usage of a
> +    specific ProfileInput.
>      """
>  
> -    def __init__(self, name=None, class_id=None, description=None, policy_attributes=None, policy_params=None):
> +    def __init__(self, name=None, class_id=None, description=None,
> +                 policy_attributes=None, policy_params=None):
>          self.name = name
>          self.class_id = class_id
>          self.description = description
> @@ -415,18 +443,22 @@ class PolicyDefault(object):
>          if 'policyAttribute' in json_value:
>              attributes = json_value['policyAttribute']
>              if not isinstance(attributes, types.ListType):
> -                policy_def.policy_attributes.append(ProfileAttribute.from_json(attributes))
> +                policy_def.policy_attributes.append(
> +                    ProfileAttribute.from_json(attributes))
>              else:
>                  for attr in attributes:
> -                    policy_def.policy_attributes.append(ProfileAttribute.from_json(attr))
> +                    policy_def.policy_attributes.append(
> +                        ProfileAttribute.from_json(attr))
>  
>          if 'params' in json_value:
>              params = json_value['params']
>              if not isinstance(params, types.ListType):
> -                policy_def.policy_params.append(ProfileParameter.from_json(params))
> +                policy_def.policy_params.append(
> +                    ProfileParameter.from_json(params))
>              else:
>                  for param in params:
> -                    policy_def.policy_params.append(ProfileParameter.from_json(param))
> +                    policy_def.policy_params.append(
> +                        ProfileParameter.from_json(param))
>  
>          return policy_def
>  
> @@ -460,11 +492,12 @@ class PolicyConstraintValue(object):
>  
>  class PolicyConstraint(object):
>      """
> -    An object of this class contains the policy constraints applied to a ProfileInput
> -    used by a certificate enrollment request.
> +    An object of this class contains the policy constraints applied to a
> +    ProfileInput used by a certificate enrollment request.
>      """
>  
> -    def __init__(self, name=None, description=None, class_id=None, policy_constraint_values=None):
> +    def __init__(self, name=None, description=None, class_id=None,
> +                 policy_constraint_values=None):
>          self.name = name
>          self.description = description
>          self.class_id = class_id
> @@ -509,10 +542,12 @@ class PolicyConstraint(object):
>          if 'constraint' in json_value:
>              constraints = json_value['constraint']
>              if not isinstance(constraints, types.ListType):
> -                policy_constraint.policy_constraint_values.append(PolicyConstraintValue.from_json(constraints))
> +                policy_constraint.policy_constraint_values.append(
> +                    PolicyConstraintValue.from_json(constraints))
>              else:
>                  for constraint in constraints:
> -                    policy_constraint.policy_constraint_values.append(PolicyConstraintValue.from_json(constraint))
> +                    policy_constraint.policy_constraint_values.append(
> +                        PolicyConstraintValue.from_json(constraint))
>  
>          return policy_constraint
>  
> @@ -520,11 +555,13 @@ class PolicyConstraint(object):
>  class ProfilePolicy(object):
>      """
>      This class represents the policy a profile adheres to.
> -    An object of this class stores the default values for profile and the constraints present on the
> -    values of the attributes of the profile submitted for an enrollment request.
> +    An object of this class stores the default values for profile and the
> +    constraints present on the values of the attributes of the profile submitted
> +    for an enrollment request.
>      """
>  
> -    def __init__(self, policy_id=None, policy_default=None, policy_constraint=None):
> +    def __init__(self, policy_id=None, policy_default=None,
> +                 policy_constraint=None):
>          self.policy_id = policy_id
>          self.policy_default = policy_default
>          self.policy_constraint = policy_constraint
> @@ -648,7 +685,8 @@ class PolicySetList(object):
>              policy_set_list.policy_sets.append(PolicySet.from_json(policy_sets))
>          else:
>              for policy_set in policy_sets:
> -                policy_set_list.policy_sets.append(PolicySet.from_json(policy_set))
> +                policy_set_list.policy_sets.append(
> +                    PolicySet.from_json(policy_set))
>  
>  
>  class ProfileData(object):
> @@ -656,9 +694,11 @@ class ProfileData(object):
>      This class represents an enrollment profile.
>      """
>  
> -    def __init__(self, profile_id=None, class_id=None, name=None, description=None, enabled=None, visible=None,
> -                 enabled_by=None, authenticator_id=None, authorization_acl=None, renewal=None, xml_output=None,
> -                 inputs=None, outputs=None, policy_sets=None, link=None):
> +    def __init__(self, profile_id=None, class_id=None, name=None,
> +                 description=None, enabled=None, visible=None, enabled_by=None,
> +                 authenticator_id=None, authorization_acl=None, renewal=None,
> +                 xml_output=None, inputs=None, outputs=None, policy_sets=None,
> +                 link=None):
>  
>          self.profile_id = profile_id
>          self.name = name
> @@ -779,26 +819,43 @@ class ProfileData(object):
>              profile_data.inputs.append(ProfileInput.from_json(profile_inputs))
>          else:
>              for profile_input in profile_inputs:
> -                profile_data.policy_sets.append(ProfileInput.from_json(profile_input))
> +                profile_data.inputs.append(
> +                    ProfileInput.from_json(profile_input))
>  
>          profile_outputs = json_value['Output']
>          if not isinstance(profile_outputs, types.ListType):
> -            profile_data.outputs.append(ProfileOutput.from_json(profile_outputs))
> +            profile_data.outputs.append(
> +                ProfileOutput.from_json(profile_outputs))
>          else:
>              for profile_output in profile_outputs:
> -                profile_data.policy_sets.append(ProfileOutput.from_json(profile_output))
> +                profile_data.outputs.append(
> +                    ProfileOutput.from_json(profile_output))
>  
>          policy_sets = json_value['PolicySets']
>          if not isinstance(policy_sets, types.ListType):
> -            profile_data.policy_sets.append(PolicySetList.from_json(policy_sets))
> +            profile_data.policy_sets.append(
> +                PolicySetList.from_json(policy_sets))
>          else:
>              for policy_set in policy_sets:
> -                profile_data.policy_sets.append(PolicySetList.from_json(policy_set))
> +                profile_data.policy_sets.append(
> +                    PolicySetList.from_json(policy_set))
>  
>          profile_data.link = pki.Link.from_json(json_value['link'])
>  
>          return profile_data
>  
> +    def __repr__(self):
> +        attributes = {
> +            "ProfileData": {
> +                'profile_id': self.profile_id,
> +                'name': self.name,
> +                'description': self.description,
> +                'status': ('enabled' if self.enabled else 'disabled'),
> +                'visible': self.visible
> +            }
> +        }
> +        return str(attributes)
> +
>  
>  class ProfileClient(object):
>      """
> @@ -856,7 +913,8 @@ class ProfileClient(object):
>          if profile_id is None:
>              raise ValueError("Profile ID must be specified.")
>          if action is None:
> -            raise ValueError("A valid action(enable/disable) must be specified.")
> +            raise ValueError("A valid action(enable/disable) must be "
> +                             "specified.")
>  
>          url = self.profiles_url + '/' + str(profile_id)
>          params = {'action': action}
> @@ -881,7 +939,8 @@ def main():
>      # Initialize a PKIConnection object for the CA
>      connection = client.PKIConnection('https', 'localhost', '8443', 'ca')
>  
> -    # The pem file used for authentication. Created from a p12 file using the command -
> +    # The pem file used for authentication. Created from a p12 file using the
> +    # command -
>      # openssl pkcs12 -in <p12_file_path> -out /tmp/auth.pem -nodes
>      connection.set_authentication_cert("/tmp/auth.pem")
>  
> @@ -892,7 +951,7 @@ def main():
>      profile_data_infos = profile_client.list_profiles()
>      print('List of profiles:')
>      print('-----------------')
> -    for profile_data_info in profile_data_infos.profile_data_list:
> +    for profile_data_info in profile_data_infos:
>          print('  Profile ID: ' + profile_data_info.profile_id)
>          print('  Profile Name: ' + profile_data_info.profile_name)
>          print('  Profile Description: ' + profile_data_info.profile_description)
> -- 
> 1.8.5.3
> 

> _______________________________________________
> Pki-devel mailing list
> Pki-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/pki-devel




More information about the Pki-devel mailing list