On Fri, Jun 27, 2014 at 01:08:13PM -0400, Abhishek Koneru wrote:
Please review the patch with fixes for Ade's comments on patch
98-2.
Addressed all the comments except 8 and 10. which will be done
separately.
Patch does seem to address those issues and __main__ still works
nicely (including removing the added profile at end now - thanks).
Switching to serialising the profile as JSON now means that the
upload-xml-profile codepath is no longer exercised by __main__. Not
a blocker I think, but if it's easy to write out a profile as XML so
that codepath is exercised, that would be a good idea.
ACK modulo preceding comment.
Please apply patch 97 before applying 98-3
--Abhishek
On Thu, 2014-06-26 at 00:47 +0800, Ade Lee wrote:
> In profile.py:
>
> 1. Usually, we set @author and so on after the copyright notice.
>
> 2. In PolicyDefault, you have methods to add/remove/get attributes and
> parameters, as in:
> def add_attribute(self, profile_attribute):
> self.policy_attributes.append(profile_attribute)
>
> Change profile_attribute to policy_attribute etc. You should indicate in
> a docstring that the expected type for the policy_attribute etc. is a
> ProfileAttribute.
>
> 3. I'm not a big fan of your _send_profile_request() and
> _send_request_in_file() methods in that they mix create and update
> operations. That might make sense if the operations had a lot of common
> code and were doing the same HTTP operation, but otherwise is just
> confusing.
>
> A better way to split this up might be to define:
>
> def _send_profile_create(self, profile_data)
>
> def _send_profile_modify(self, profile_id, profile_data)
>
> @staticmethod
> def _get_profile_data_from_file(path_to_file, file_format)
>
> and then call these as needed.
>
> So for example, create_profile_using_file_input(self, path_to_file,
> file_format) -> which I would rename to create_profile_from_file(..) ->
> would call:
> profile_data = _get_profile_data_from_file(path_to_file, file_format)
> _send_profile_create(profile_data)
>
> 4. In the test where you add a duplicate profile, you should check for
> the specific exception thrown -- is there a specific exception?
>
> 5. Change the profileID in the test to MySampleProfile instead of
> MySampleCert.
>
> 6. In the test, we are testing file input using original.xml and
> modified.xml. Where do these files come from? It would be better if
> these files were generated as part of the previous tests - which means
> you need a function to save files to xml/json. You should then test xml
> and json.
>
> 7. The policy stuff gets confusing fast, so the example really helps.
> We need more function docstrings though that specify when inputs/outputs
> the functions have - and in particular the class of each input.
>
> 8. We also need more api documentation on the wiki as we have done in
> the past. This is not part of this patch of course.
>
> 9. There are Pycharm errors about unused variables. Most of these will
> be addressed by the refactoring I mention in point 3 above.
>
> 10. profile.py - which is all new code generates probably close to 200
> new messages in pylint. We really should fix these. To keep things
> clear, you should do this in a separate patch.
>
>
>
> On Wed, 2014-06-25 at 16:49 +1000, Fraser Tweedale wrote:
> > On Tue, Jun 24, 2014 at 05:32:25PM -0400, Abhishek Koneru wrote:
> > > Please review the patch with fixes for comments on patch 98.
> > > Issues addressed:
> > > 1. Added a try catch block for issue 1(as noted below) caught by
> > > ftweedal(nice catch!).
> > > 2. Made the data_format mandatory for file_input methods.
> > > 3. Added code to create a profile with PolicyOutput and PolicySets.
> > >
> > > -- Abhishek
> > > Please apply patch 97 before applying this patch.
> > >
> >
> > ACK
> >
> > > On Fri, 2014-06-20 at 12:02 +1000, Fraser Tweedale wrote:
> > > > On Thu, Jun 19, 2014 at 09:58:43AM -0400, Abhishek Koneru wrote:
> > > > > Please review the attached patch which adds methods that allow
users to
> > > > > pass the profile data in a file.
> > > > >
> > > > > Also attached two sample xml files, original.xml and
modified.xml.
> > > > > Place them in /tmp before running the profile.py module.
> > > > >
> > > > > Thanks,
> > > > > Abhishek
> > > > >
> > > >
> > > > Hi Abhishek,
> > > >
> > > > Patch applied and __main__ example run. Seems to do what it says on
> > > > the tin; the usual "haven't used it in anger" caveat
^_^. Review
> > > > comments inline below.
> > > >
> > > > Fraser
> > > >
> > > > > + @pki.handle_exceptions()
> > > > > def modify_profile(self, profile_data):
> > > > > """
> > > > > Modify an existing profile.
> > > > > """
> > > > > - if profile_data is None:
> > > > > - raise ValueError("No ProfileData
specified")
> > > > > + return self._send_profile_request(profile_data,
'modify')
> > > > > +
> > > > > + def _send_request_in_file(self, path_to_file, data_format,
operation):
> > > > > +
> > > > > + if path_to_file is None:
> > > > > + raise ValueError("File path must be
specified.")
> > > > > +
> > > > > + if data_format not in ['xml', 'json']:
> > > > > + raise ValueError("Unsupported data type:
" + str(data_format))
> > > > > +
> > > > > + if operation not in ['create',
'modify']:
> > > > > + raise ValueError("Invalid operation specified:
" + str(operation))
> > > > > +
> > > > > + data = None
> > > > > + with open(path_to_file) as input_file:
> > > > > + data = input_file.read()
> > > > > +
> > > > > + if data_format == 'xml':
> > > > > + self.headers['Content-type'] =
'application/xml'
> > > >
> > > > 1)
> > > >
> > > > Overwriting self.headers['Content-type'] is problematic.
For
> > > > example, if the data cannot be parsed or lacks an 'id' key,
an
> > > > exception will be raised and the ProfileClient will be stuck with
> > > > the wrong Content-Type header.
> > > >
> > > > Possible solutions:
> > > >
> > > > - use try/finally or a context manager to ensure the header gets
> > > > reset to 'application/json' even if an exception is
raised.
> > > > - Modify the _put and _post methods to include an optional argument
> > > > to override the content-type header. To avoid special-cases too
> > > > many things, this arg could even be a dict that can be merged with
> > > > the default headers, e.g.:
> > > >
> > > > def _post(self, url, payload=None, headers=None):
> > > > self.account_client.login()
> > > > headers = dict(self.headers, **(headers or {}))
> > > > r = self.connection.post(url, payload, headers,
query_params)
> > > > ...
> > > >
> > > > Then callers can supply custom headers for a single request
> > > > without potentially affecting other requests.
> > > >
> > > > > +
> > > > > + r = None
> > > > > + # Sending the data to the server.
> > > > > + if operation == 'create':
> > > > > + r = self._post(self.profiles_url, data)
> > > > > + else:
> > > > > + profile_id = None
> > > > > + if data_format == 'xml':
> > > > > + profile_id =
etree.fromstring(data).get('id')
> > > > > + else:
> > > > > + profile_id = json.loads(data)['id']
> > > > > +
> > > > > + if profile_id is None:
> > > > > + raise ValueError('Profile Id is not
specified.')
> > > > > + url = self.profiles_url + '/' + profile_id
> > > > > + r = self._put(url, data)
> > > > > +
> > > > > + # Reset the Content-type header to json(As used by
other methods).
> > > > > + if data_format == 'xml':
> > > > > + self.headers['Content-type'] =
'application/json'
> > > > >
> > > > > - url = self.profiles_url + '/' +
str(profile_data.profile_id)
> > > > > - profile_object = json.dumps(profile_data,
cls=encoder.CustomTypeEncoder,
> > > > > - sort_keys=True)
> > > > > - r = self._put(url, profile_object)
> > > > > return Profile.from_json(r.json())
> > > > >
> > > > > + @pki.handle_exceptions()
> > > > > + def create_profile_using_file_input(self, path_to_file,
data_format="xml"):
> > > > > + """
> > > > > + Create a new profile from a profile object stored in a
file.
> > > > > + Acceptable data formats - json, xml.
> > > > > + """
> > > > > + return self._send_request_in_file(path_to_file,
data_format, 'create')
> > > > > +
> > > >
> > > > 2)
> > > >
> > > > Default ``data_format="xml"`` makes it too easy for people
to misuse
> > > > the API, e.g. a path to a JSON file, but no ``data_format`` kwarg
> > > > given, resulting in default "xml" being used. I suggest
either
> > > > making it a compulsory argument, or making its default ``None`` and,
> > > > if no explicit ``data_format`` arg given, using the file extension.
> > > >
> > > >
> > > > > + @pki.handle_exceptions()
> > > > > + def modify_profile_using_file_input(self, path_to_file,
data_format="xml"):
> > > > > + """
> > > > > + Modify a profile from a profile object stored in a
file.
> > > > > + Acceptable data formats - json, xml.
> > > > > + """
> > > > > + return self._send_request_in_file(path_to_file,
data_format, 'modify')
> > > > > +
> > > > > + @pki.handle_exceptions()
> > > > > def delete_profile(self, profile_id):
> > > > > """
> > > > > Delete a profile.
> > > > > @@ -1185,6 +1253,23 @@ def main():
> > > > > # pylint: disable-msg=W0703
> > > > > except Exception as e:
> > > > > print str(e)
> > > > > + print
> > > > > +
> > > > > + # Creating a profile from file
> > > > > + print('Creating a profile using file input.')
> > > > > + print('------------------------------------')
> > > > > + original = profile_client.create_profile_using_file_input(
> > > > > + '/tmp/original.xml')
> > > > > + print(original)
> > > > > + print
> > > > > +
> > > > > + # Modifying a profile from file
> > > > > + print('Modifying a profile using file input.')
> > > > > + print('------------------------------------')
> > > > > + modified = profile_client.modify_profile_using_file_input(
> > > > > + '/tmp/modified.xml')
> > > > > + print(modified)
> > > > > + print
> > > >
> > > > 3)
> > > >
> > > > Nit-pick: could you delete the created profile here so that the
> > > > example can be run multiple times?
> > >
> >
> > > From 3a031e463e6fb0b5658ad9e1bc37a018c99f32de Mon Sep 17 00:00:00 2001
> > > From: Abhishek Koneru <akoneru(a)redhat.com>
> > > Date: Thu, 19 Jun 2014 00:10:13 -0400
> > > Subject: [PATCH] Added methods for providing file input for profile
request.
> > >
> > > Added new methods to allow user to provide file input to perform
> > > operations like create profile/modify profile.
> > > The supported file formats a re xml and json.
> > > ---
> > > base/common/python/pki/__init__.py | 4 +-
> > > base/common/python/pki/profile.py | 265
++++++++++++++++++++++++++++++++++---
> > > 2 files changed, 252 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/base/common/python/pki/__init__.py
b/base/common/python/pki/__init__.py
> > > index
e9b726cf763785b4a700ef314ff27774b13aba40..891d6ea6364b037f132ff3754b73b372c638b0f7 100644
> > > --- a/base/common/python/pki/__init__.py
> > > +++ b/base/common/python/pki/__init__.py
> > > @@ -168,7 +168,7 @@ class PKIException(Exception, ResourceMessage):
> > > ret = cls(json_value['Message'],
json_value['Code'],
> > > json_value['ClassName'])
> > > for attr in
json_value['Attributes']['Attribute']:
> > > - print(str(attr))
> > > + print str(attr)
> > > ret.add_attribute(attr["name"],
attr["value"])
> > > return ret
> > >
> > > @@ -299,7 +299,7 @@ class PropertyFile(object):
> > > def show(self):
> > > """ Show contents of property
file."""
> > > for line in self.lines:
> > > - print(line)
> > > + print line
> > >
> > > def insert_line(self, index, line):
> > > """ Insert line in property file
"""
> > > diff --git a/base/common/python/pki/profile.py
b/base/common/python/pki/profile.py
> > > index
4f08ee5ba9527855f63b2785c32f9aaddc8d1289..2d4ce5ace11bc6417032fb89ecf6b18707b6fca0 100644
> > > --- a/base/common/python/pki/profile.py
> > > +++ b/base/common/python/pki/profile.py
> > > @@ -1,12 +1,27 @@
> > > #!/usr/bin/python
> > > """
> > > -Created on May 13,, 2014
> > > +@author: Abhishek Koneru <akoneru(a)redhat.com>
> > >
> > > -@author: akoneru
> > > + This program is free software; you can redistribute it and/or modify
> > > + it under the terms of the GNU General Public License as published by
> > > + the Free Software Foundation; version 2 of the License.
> > > +
> > > + This program is distributed in the hope that it will be useful,
> > > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + GNU General Public License for more details.
> > > +
> > > + You should have received a copy of the GNU General Public License along
> > > + with this program; if not, write to the Free Software Foundation, Inc.,
> > > + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> > > +
> > > + Copyright (C) 2014 Red Hat, Inc.
> > > + All rights reserved.
> > > """
> > >
> > > import json
> > > import types
> > > +from lxml import etree
> > >
> > > import pki
> > > import pki.client as client
> > > @@ -433,6 +448,38 @@ class PolicyDefault(object):
> > > def policy_params(self, value):
> > > setattr(self, 'params', value)
> > >
> > > + def add_attribute(self, profile_attribute):
> > > + self.policy_attributes.append(profile_attribute)
> > > +
> > > + def remove_attribute(self, profile_attribute_name):
> > > + for attr in self.policy_attributes:
> > > + if attr.name == profile_attribute_name:
> > > + self.policy_attributes.remove(attr)
> > > + break
> > > +
> > > + def get_attribute(self, profile_attribute_name):
> > > + for attr in self.policy_attributes:
> > > + if attr.name == profile_attribute_name:
> > > + return attr
> > > +
> > > + return None
> > > +
> > > + def add_parameter(self, profile_parameter):
> > > + self.policy_params.append(profile_parameter)
> > > +
> > > + def remove_parameter(self, profile_parameter_name):
> > > + for param in self.policy_params:
> > > + if param.name == profile_parameter_name:
> > > + self.policy_params.remove(param)
> > > + break
> > > +
> > > + def get_parameter(self, profile_parameter_name):
> > > + for param in self.policy_params:
> > > + if param.name == profile_parameter_name:
> > > + return param
> > > +
> > > + return None
> > > +
> > > @classmethod
> > > def from_json(cls, json_value):
> > > policy_def = cls()
> > > @@ -531,6 +578,22 @@ class PolicyConstraint(object):
> > > def policy_constraint_values(self, value):
> > > setattr(self, 'constraint', value)
> > >
> > > + def add_constraint_value(self, policy_constraint_value):
> > > + self.policy_constraint_values.append(policy_constraint_value)
> > > +
> > > + def remove_attribute(self, policy_constraint_value_name):
> > > + for attr in self.policy_constraint_values:
> > > + if attr.name == policy_constraint_value_name:
> > > + self.policy_constraint_values.remove(attr)
> > > + break
> > > +
> > > + def get_attribute(self, policy_constraint_value_name):
> > > + for constraint in self.policy_constraint_values:
> > > + if constraint.name == policy_constraint_value_name:
> > > + return constraint
> > > +
> > > + return None
> > > +
> > > @classmethod
> > > def from_json(cls, json_value):
> > > policy_constraint = cls()
> > > @@ -995,31 +1058,99 @@ class ProfileClient(object):
> > > """
> > > return self._modify_profile_state(profile_id, 'disable')
> > >
> > > + def _send_profile_request(self, profile_data, operation):
> > > +
> > > + if profile_data is None:
> > > + raise ValueError("No ProfileData specified")
> > > +
> > > + if operation not in ['create', 'modify']:
> > > + raise ValueError("Invalid operation specified: " +
str(operation))
> > > +
> > > + profile_object = json.dumps(profile_data,
cls=encoder.CustomTypeEncoder,
> > > + sort_keys=True)
> > > + r = None
> > > + if operation == 'create':
> > > + r = self._post(self.profiles_url, profile_object)
> > > + else:
> > > + if profile_data.profile_id is None:
> > > + raise ValueError("Profile Id is not
specified.")
> > > + url = self.profiles_url + '/' +
str(profile_data.profile_id)
> > > + r = self._put(url, profile_object)
> > > +
> > > + return Profile.from_json(r.json())
> > > +
> > > + @pki.handle_exceptions()
> > > def create_profile(self, profile_data):
> > > """
> > > Create a new profile for the given ProfileData object.
> > > """
> > > - if profile_data is None:
> > > - raise ValueError("No ProfileData specified")
> > > -
> > > - profile_object = json.dumps(profile_data,
cls=encoder.CustomTypeEncoder,
> > > - sort_keys=True)
> > > - r = self._post(self.profiles_url, profile_object)
> > > - return Profile.from_json(r.json())
> > > + return self._send_profile_request(profile_data,
'create')
> > >
> > > + @pki.handle_exceptions()
> > > def modify_profile(self, profile_data):
> > > """
> > > Modify an existing profile.
> > > """
> > > - if profile_data is None:
> > > - raise ValueError("No ProfileData specified")
> > > + return self._send_profile_request(profile_data,
'modify')
> > > +
> > > + def _send_request_in_file(self, path_to_file, data_format,
operation):
> > > +
> > > + if path_to_file is None:
> > > + raise ValueError("File path must be specified.")
> > > +
> > > + if data_format not in ['xml', 'json']:
> > > + raise ValueError("Unsupported data type: " +
str(data_format))
> > > +
> > > + if operation not in ['create', 'modify']:
> > > + raise ValueError("Invalid operation specified: " +
str(operation))
> > > +
> > > + data = None
> > > + with open(path_to_file) as input_file:
> > > + data = input_file.read()
> > > +
> > > + if data_format == 'xml':
> > > + self.headers['Content-type'] =
'application/xml'
> > > +
> > > + r = None
> > > + try:
> > > + # Sending the data to the server.
> > > + if operation == 'create':
> > > + r = self._post(self.profiles_url, data)
> > > + else:
> > > + profile_id = None
> > > + if data_format == 'xml':
> > > + profile_id =
etree.fromstring(data).get('id')
> > > + else:
> > > + profile_id = json.loads(data)['id']
> > > +
> > > + if profile_id is None:
> > > + raise ValueError('Profile Id is not
specified.')
> > > + url = self.profiles_url + '/' + profile_id
> > > + r = self._put(url, data)
> > > + finally:
> > > + # Reset the Content-type header to json(As used by other
methods).
> > > + if data_format == 'xml':
> > > + self.headers['Content-type'] =
'application/json'
> > >
> > > - url = self.profiles_url + '/' +
str(profile_data.profile_id)
> > > - profile_object = json.dumps(profile_data,
cls=encoder.CustomTypeEncoder,
> > > - sort_keys=True)
> > > - r = self._put(url, profile_object)
> > > return Profile.from_json(r.json())
> > >
> > > + @pki.handle_exceptions()
> > > + def create_profile_using_file_input(self, path_to_file,
data_format):
> > > + """
> > > + Create a new profile from a profile object stored in a file.
> > > + Acceptable data formats - json, xml.
> > > + """
> > > + return self._send_request_in_file(path_to_file, data_format,
'create')
> > > +
> > > + @pki.handle_exceptions()
> > > + def modify_profile_using_file_input(self, path_to_file,
data_format):
> > > + """
> > > + Modify a profile from a profile object stored in a file.
> > > + Acceptable data formats - json, xml.
> > > + """
> > > + return self._send_request_in_file(path_to_file, data_format,
'modify')
> > > +
> > > + @pki.handle_exceptions()
> > > def delete_profile(self, profile_id):
> > > """
> > > Delete a profile.
> > > @@ -1108,6 +1239,7 @@ def main():
> > > renewal=False, xml_output=False,
> > > authorization_acl="")
> > >
> > > + # Adding a profile input
> > > profile_input = ProfileInput("i1",
"subjectNameInputImpl")
> > > profile_input.add_attribute(ProfileAttribute("sn_uid"))
> > > profile_input.add_attribute(ProfileAttribute("sn_e"))
> > > @@ -1121,6 +1253,92 @@ def main():
> > >
> > > profile_data.add_input(profile_input)
> > >
> > > + # Adding a profile output
> > > + profile_output = ProfileOutput("o1", name="Certificate
Output",
> > > + class_id="certOutputImpl")
> > > +
profile_output.add_attribute(ProfileAttribute("pretty_cert"))
> > > + profile_output.add_attribute(ProfileAttribute("b64_cert"))
> > > +
> > > + profile_data.add_output(profile_output)
> > > +
> > > + # Create a Policy set with a list of profile policies
> > > + policy_list = []
> > > +
> > > + # Creating profile policy
> > > + policy_default = PolicyDefault("Subject Name Default",
> > > +
"userSubjectNameDefaultImpl",
> > > + "This default populates a
User-Supplied "
> > > + "Certificate Subject Name to the
request.")
> > > +
> > > + attr_descriptor = Descriptor(syntax="string",
description="Subject Name")
> > > + policy_attribute = ProfileAttribute("name",
descriptor=attr_descriptor)
> > > + policy_default.add_attribute(policy_attribute)
> > > +
> > > + policy_constraint = PolicyConstraint("Subject Name
Constraint",
> > > + "This constraint accepts
the subject "
> > > + "name that matches
UID=.*",
> > > +
"subjectNameConstraintImpl")
> > > + constraint_descriptor = Descriptor(syntax="string",
> > > + description="Subject Name
Pattern")
> > > + policy_constraint_value = PolicyConstraintValue("pattern",
> > > + "UID=.*",
> > > +
constraint_descriptor)
> > > + policy_constraint.add_constraint_value(policy_constraint_value)
> > > +
> > > + policy_list.append(ProfilePolicy("1", policy_default,
policy_constraint))
> > > +
> > > + # Creating another profile policy
> > > + # Defining the policy default
> > > + policy_default = PolicyDefault("Validity Default",
"validityDefaultImpl",
> > > + "This default populates a
Certificate "
> > > + "Validity to the request. The
default "
> > > + "values are Range=180 in
days")
> > > + attr_descriptor = Descriptor(syntax="string",
description="Not Before")
> > > + policy_attribute = ProfileAttribute("notBefore",
descriptor=attr_descriptor)
> > > + policy_default.add_attribute(policy_attribute)
> > > +
> > > + attr_descriptor = Descriptor(syntax="string",
description="Not After")
> > > + policy_attribute = ProfileAttribute("notAfter",
descriptor=attr_descriptor)
> > > + policy_default.add_attribute(policy_attribute)
> > > +
> > > + profile_param = ProfileParameter("range", 180)
> > > + profile_param2 = ProfileParameter("startTime", 0)
> > > + policy_default.add_parameter(profile_param)
> > > + policy_default.add_parameter(profile_param2)
> > > +
> > > + #Defining the policy constraint
> > > + policy_constraint = PolicyConstraint("Validity
Constraint",
> > > + "This constraint rejects
the validity "
> > > + "that is not between 365
days.",
> > > +
"validityConstraintImpl")
> > > + constraint_descriptor = Descriptor(syntax="integer",
> > > + description="Validity Range
(in days)",
> > > + default_value=365)
> > > + policy_constraint_value = PolicyConstraintValue("range",
365,
> > > +
constraint_descriptor)
> > > + policy_constraint.add_constraint_value(policy_constraint_value)
> > > +
> > > + constraint_descriptor = Descriptor(syntax="boolean",
default_value=False,
> > > + description="Check Not Before
against"
> > > + " current
time")
> > > + policy_constraint_value =
PolicyConstraintValue("notBeforeCheck", False,
> > > +
constraint_descriptor)
> > > + policy_constraint.add_constraint_value(policy_constraint_value)
> > > +
> > > + constraint_descriptor = Descriptor(syntax="boolean",
default_value=False,
> > > + description="Check Not After
against"
> > > + " Not
Before")
> > > + policy_constraint_value =
PolicyConstraintValue("notAfterCheck", False,
> > > +
constraint_descriptor)
> > > + policy_constraint.add_constraint_value(policy_constraint_value)
> > > +
> > > + policy_list.append(ProfilePolicy("2", policy_default,
policy_constraint))
> > > +
> > > + policy_set = PolicySet("userCertSet", policy_list)
> > > +
> > > + profile_data.add_policy_set(policy_set)
> > > +
> > > + # Create a new profile
> > > created_profile = profile_client.create_profile(profile_data)
> > > print(created_profile)
> > > print
> > > @@ -1185,6 +1403,23 @@ def main():
> > > # pylint: disable-msg=W0703
> > > except Exception as e:
> > > print str(e)
> > > + print
> > > +
> > > + # Creating a profile from file
> > > + print('Creating a profile using file input.')
> > > + print('------------------------------------')
> > > + original = profile_client.create_profile_using_file_input(
> > > + '/tmp/original.xml', 'xml')
> > > + print(original)
> > > + print
> > > +
> > > + # Modifying a profile from file
> > > + print('Modifying a profile using file input.')
> > > + print('------------------------------------')
> > > + modified = profile_client.modify_profile_using_file_input(
> > > + '/tmp/modified.xml', 'xml')
> > > + print(modified)
> > > + print
> > >
> > >
> > > if __name__ == "__main__":
> > > --
> > > 1.8.5.3
> > >
> >
> > _______________________________________________
> > Pki-devel mailing list
> > Pki-devel(a)redhat.com
> >
https://www.redhat.com/mailman/listinfo/pki-devel
>
>
From 6f0acdb884fa8306be8d3478a22becb301cb6d79 Mon Sep 17 00:00:00
2001
From: Abhishek Koneru <akoneru(a)redhat.com>
Date: Thu, 19 Jun 2014 00:10:13 -0400
Subject: [PATCH] Added methods for providing file input for profile request.
Added new methods to allow user to provide file input to perform
operations like create profile/modify profile.
The supported file formats a re xml and json.
---
base/common/python/pki/__init__.py | 7 +-
base/common/python/pki/profile.py | 636 +++++++++++++++++++++++++++++++------
2 files changed, 541 insertions(+), 102 deletions(-)
diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index e9b726cf763785b4a700ef314ff27774b13aba40..b610a9f8d9dc13dcdaf1e7cc920a204d0277d3f3
100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -168,7 +168,7 @@ class PKIException(Exception, ResourceMessage):
ret = cls(json_value['Message'], json_value['Code'],
json_value['ClassName'])
for attr in json_value['Attributes']['Attribute']:
- print(str(attr))
+ print str(attr)
ret.add_attribute(attr["name"], attr["value"])
return ret
@@ -299,7 +299,7 @@ class PropertyFile(object):
def show(self):
""" Show contents of property file."""
for line in self.lines:
- print(line)
+ print line
def insert_line(self, index, line):
""" Insert line in property file """
@@ -403,6 +403,9 @@ class Link:
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
link = cls()
for attr in attr_list:
setattr(link, attr, attr_list[attr])
diff --git a/base/common/python/pki/profile.py b/base/common/python/pki/profile.py
index 4f08ee5ba9527855f63b2785c32f9aaddc8d1289..f8caf10e0a89773f3287899c68a9ce60da5cee0e
100644
--- a/base/common/python/pki/profile.py
+++ b/base/common/python/pki/profile.py
@@ -1,13 +1,29 @@
#!/usr/bin/python
"""
-Created on May 13,, 2014
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
-@author: akoneru
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Copyright (C) 2014 Red Hat, Inc.
+ All rights reserved.
+
+ @author: Abhishek Koneru <akoneru(a)redhat.com>
"""
import json
+import os
import types
+
import pki
import pki.client as client
import pki.account as account
@@ -36,6 +52,9 @@ class ProfileDataInfo(object):
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
profile_data_info = cls()
profile_data_info.profile_id = attr_list['profileId']
profile_data_info.profile_name = attr_list['profileName']
@@ -59,9 +78,9 @@ class ProfileDataInfoCollection(object):
return iter(self.profile_data_list)
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
ret = cls()
- profile_data_infos = json_value['entries']
+ profile_data_infos = attr_list['entries']
if not isinstance(profile_data_infos, types.ListType):
ret.profile_data_list.append(
ProfileDataInfo.from_json(profile_data_infos))
@@ -70,7 +89,7 @@ class ProfileDataInfoCollection(object):
ret.profile_data_list.append(
ProfileDataInfo.from_json(profile_info))
- links = json_value['Link']
+ links = attr_list['Link']
if not isinstance(links, types.ListType):
ret.links.append(pki.Link.from_json(links))
else:
@@ -128,6 +147,9 @@ class Descriptor(object):
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
descriptor = cls()
for attr in attr_list:
setattr(descriptor, attr, attr_list[attr])
@@ -163,6 +185,9 @@ class ProfileAttribute(object):
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
attribute = cls()
attribute.name = attr_list['name']
if 'Value' in attr_list:
@@ -241,15 +266,28 @@ class ProfileInput(object):
setattr(self, 'ConfigAttribute', value)
def add_attribute(self, profile_attribute):
+ """
+ Add a ProfileAttribute object to the attributes list.
+ """
+ if not isinstance(profile_attribute, ProfileAttribute):
+ raise ValueError("Object passed is not a ProfileAttribute.")
self.attributes.append(profile_attribute)
def remove_attribute(self, profile_attribute_name):
+ """
+ Remove a ProfileAttribute object with the given name from the attributes
+ list.
+ """
for attr in self.attributes:
if attr.name == profile_attribute_name:
self.attributes.remove(attr)
break
def get_attribute(self, profile_attribute_name):
+ """
+ Returns a ProfileAttribute object for the given name.
+ None, if no match.
+ """
for attr in self.attributes:
if attr.name == profile_attribute_name:
return attr
@@ -257,31 +295,46 @@ class ProfileInput(object):
return None
def add_config_attribute(self, profile_attribute):
- self.attributes.append(profile_attribute)
+ """
+ Add a ProfileAttribute object to the config_attributes list.
+ """
+ if not isinstance(profile_attribute, ProfileAttribute):
+ raise ValueError("Object passed is not a ProfileAttribute.")
+ self.config_attributes.append(profile_attribute)
def remove_config_attribute(self, config_attribute_name):
+ """
+ Remove a ProfileAttribute object with the given name from the
+ config_attributes list.
+ """
for attr in self.config_attributes:
if attr.name == config_attribute_name:
- self.attributes.remove(attr)
+ self.config_attributes.remove(attr)
break
def get_config_attribute(self, config_attribute_name):
- for attr in self.attributes:
+ """
+ Returns a ProfileAttribute object with the given name.
+ None, if there is no match in the config_attributes list.
+ """
+ for attr in self.config_attributes:
if attr.name == config_attribute_name:
return attr
return None
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
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']
+ profile_input.profile_input_id = attr_list['id']
+ profile_input.class_id = attr_list['ClassID']
+ profile_input.name = attr_list['Name']
+ if 'Text' in attr_list:
+ profile_input.text = attr_list['Text']
- attributes = json_value['Attribute']
+ attributes = attr_list['Attribute']
if not isinstance(attributes, types.ListType):
profile_input.attributes.append(
ProfileAttribute.from_json(attributes))
@@ -290,7 +343,7 @@ class ProfileInput(object):
profile_input.attributes.append(
ProfileAttribute.from_json(profile_info))
- config_attributes = json_value['ConfigAttribute']
+ config_attributes = attr_list['ConfigAttribute']
if not isinstance(config_attributes, types.ListType):
profile_input.config_attributes.append(
ProfileAttribute.from_json(config_attributes))
@@ -334,15 +387,28 @@ class ProfileOutput(object):
setattr(self, 'classId', value)
def add_attribute(self, profile_attribute):
+ """
+ Add a ProfileAttribute object to the attributes list.
+ """
+ if not isinstance(profile_attribute, ProfileAttribute):
+ raise ValueError("Object passed is not a ProfileAttribute.")
self.attributes.append(profile_attribute)
def remove_attribute(self, profile_attribute_name):
+ """
+ Remove a ProfileAttribute object with the given name from the attributes
+ list.
+ """
for attr in self.attributes:
if attr.name == profile_attribute_name:
self.attributes.remove(attr)
break
def get_attribute(self, profile_attribute_name):
+ """
+ Returns a ProfileAttribute object for the given name.
+ None, if no match.
+ """
for attr in self.attributes:
if attr.name == profile_attribute_name:
return attr
@@ -350,14 +416,17 @@ class ProfileOutput(object):
return None
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
profile_output = cls()
- profile_output.profile_output_id = json_value['id']
- profile_output.name = json_value['name']
- if 'text' in json_value:
- profile_output.text = json_value['text']
- profile_output.class_id = json_value['classId']
- attributes = json_value['attributes']
+ profile_output.profile_output_id = attr_list['id']
+ profile_output.name = attr_list['name']
+ if 'text' in attr_list:
+ profile_output.text = attr_list['text']
+ profile_output.class_id = attr_list['classId']
+ attributes = attr_list['attributes']
if not isinstance(attributes, types.ListType):
profile_output.attributes.append(
ProfileAttribute.from_json(attributes))
@@ -375,6 +444,9 @@ class ProfileParameter(object):
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
param = cls()
for attr in attr_list:
setattr(param, attr, attr_list[attr])
@@ -433,17 +505,76 @@ class PolicyDefault(object):
def policy_params(self, value):
setattr(self, 'params', value)
+ def add_attribute(self, policy_attribute):
+ """
+ Add a policy attribute to the attribute list.
+ @param policy_attribute - A ProfileAttribute object
+ """
+ if not isinstance(policy_attribute, ProfileAttribute):
+ raise ValueError("Object passed is not a ProfileAttribute.")
+ self.policy_attributes.append(policy_attribute)
+
+ def remove_attribute(self, policy_attribute_name):
+ """
+ Remove a policy attribute with the given name from the attributes list.
+ """
+ for attr in self.policy_attributes:
+ if attr.name == policy_attribute_name:
+ self.policy_attributes.remove(attr)
+ break
+
+ def get_attribute(self, policy_attribute_name):
+ """
+ Fetch the policy attribute with the given name from the attributes list.
+ """
+ for attr in self.policy_attributes:
+ if attr.name == policy_attribute_name:
+ return attr
+
+ return None
+
+ def add_parameter(self, policy_parameter):
+ """
+ Add a profile parameter to the parameters list.
+ @param policy_parameter - A ProfileParameter object.
+ """
+ if not isinstance(policy_parameter, ProfileParameter):
+ raise ValueError("Object passed is not a ProfileParameter.")
+ self.policy_params.append(policy_parameter)
+
+ def remove_parameter(self, profile_parameter_name):
+ """
+ Remove a profile parameter with the given name from the parameters list.
+ """
+ for param in self.policy_params:
+ if param.name == profile_parameter_name:
+ self.policy_params.remove(param)
+ break
+
+ def get_parameter(self, profile_parameter_name):
+ """
+ Fetch a profile parameter with the given name from the parameters list.
+ """
+ for param in self.policy_params:
+ if param.name == profile_parameter_name:
+ return param
+
+ return None
+
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
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 'id' in attr_list:
+ policy_def.name = attr_list['id']
+ if 'classId' in attr_list:
+ policy_def.class_id = attr_list['classId']
+ if 'description' in attr_list:
+ policy_def.description = attr_list['description']
+ if 'policyAttribute' in attr_list:
+ attributes = attr_list['policyAttribute']
if not isinstance(attributes, types.ListType):
policy_def.policy_attributes.append(
ProfileAttribute.from_json(attributes))
@@ -452,8 +583,8 @@ class PolicyDefault(object):
policy_def.policy_attributes.append(
ProfileAttribute.from_json(attr))
- if 'params' in json_value:
- params = json_value['params']
+ if 'params' in attr_list:
+ params = attr_list['params']
if not isinstance(params, types.ListType):
policy_def.policy_params.append(
ProfileParameter.from_json(params))
@@ -480,13 +611,16 @@ class PolicyConstraintValue(object):
setattr(self, 'id', value)
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
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'])
+ ret.name = attr_list['id']
+ ret.value = attr_list['value']
+ if 'descriptor' in attr_list:
+ ret.descriptor = Descriptor.from_json(attr_list['descriptor'])
return ret
@@ -531,17 +665,49 @@ class PolicyConstraint(object):
def policy_constraint_values(self, value):
setattr(self, 'constraint', value)
+ def add_constraint_value(self, policy_constraint_value):
+ """
+ Add a ProfileConstraintValue to the policy_constraint_values list.
+ """
+ if not isinstance(policy_constraint_value, PolicyConstraintValue):
+ raise ValueError("Object passed not of type
PolicyConstraintValue")
+ self.policy_constraint_values.append(policy_constraint_value)
+
+ def remove_constraint_value(self, policy_constraint_value_name):
+ """
+ Removes a PolicyConstraintValue with the given name form the
+ policy_constraint_values list.
+ """
+ for attr in self.policy_constraint_values:
+ if attr.name == policy_constraint_value_name:
+ self.policy_constraint_values.remove(attr)
+ break
+
+ def get_constraint_value(self, policy_constraint_value_name):
+ """
+ Returns a PolicyConstraintValue object with the given name.
+ None, if there is no match.
+ """
+ for constraint in self.policy_constraint_values:
+ if constraint.name == policy_constraint_value_name:
+ return constraint
+
+ return None
+
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
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 'id' in attr_list:
+ policy_constraint.name = attr_list['id']
+ if 'description' in attr_list:
+ policy_constraint.description = attr_list['description']
+ if 'classId' in attr_list:
+ policy_constraint.class_id = attr_list['classId']
+ if 'constraint' in attr_list:
+ constraints = attr_list['constraint']
if not isinstance(constraints, types.ListType):
policy_constraint.policy_constraint_values.append(
PolicyConstraintValue.from_json(constraints))
@@ -592,9 +758,12 @@ class ProfilePolicy(object):
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']))
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
+ return cls(attr_list['id'],
PolicyDefault.from_json(attr_list['def']),
+ PolicyConstraint.from_json(attr_list['constraint']))
class ProfilePolicySet(object):
@@ -607,6 +776,9 @@ class ProfilePolicySet(object):
@classmethod
def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
policy_set = cls()
policies = attr_list['policies']
@@ -649,19 +821,40 @@ class PolicySet(object):
setattr(self, 'value', value)
def add_policy(self, profile_policy):
+ """
+ Add a ProfilePolicy object to the policy_list
+ """
+ if not isinstance(profile_policy, ProfilePolicy):
+ raise ValueError("Object passed is not a ProfilePolicy.")
self.policy_list.append(profile_policy)
def remove_policy(self, policy_id):
+ """
+ Removes a ProfilePolicy with the given ID from the PolicySet.
+ """
for policy in self.policy_list:
if policy.policy_id == policy_id:
- self.policy_list.pop(policy)
+ self.policy_list.remove(policy)
+ break
+
+ def get_policy(self, policy_id):
+ """
+ Returns a ProfilePolicy object with the given profile id.
+ """
+ for policy in self.policy_list:
+ if policy.policy_id == policy_id:
+ return policy
+ return None
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
policy_set = cls()
- policy_set.name = json_value['id']
- policies = json_value['value']
+ policy_set.name = attr_list['id']
+ policies = attr_list['value']
if not isinstance(policies, types.ListType):
policy_set.policy_list.append(ProfilePolicy.from_json(policies))
else:
@@ -694,17 +887,39 @@ class PolicySetList(object):
setattr(self, 'PolicySet', value)
def add_policy_set(self, policy_set):
+ """
+ Add a PolicySet object to the policy_sets list.
+ """
+ if not isinstance(policy_set, PolicySet):
+ raise ValueError("Object passed is not a PolicySet.")
self.policy_sets.append(policy_set)
def remove_policy_set(self, policy_set_name):
+ """
+ Remove a PolicySet object with the given name from the policy_sets list.
+ """
for policy_set in self.policy_sets:
if policy_set.name == policy_set_name:
- self.policy_sets.pop(policy_set)
+ self.policy_sets.remove(policy_set)
+ break
+
+ def get_policy_set(self, policy_set_name):
+ """
+ Fetch the PolicySet object for the given name.
+ Returns None, if not found.
+ """
+ for policy_set in self.policy_sets:
+ if policy_set.name == policy_set_name:
+ return policy_set
+ return None
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
+ if attr_list is None:
+ return None
+
policy_set_list = cls()
- policy_sets = json_value['PolicySet']
+ policy_sets = attr_list['PolicySet']
if not isinstance(policy_sets, types.ListType):
policy_set_list.policy_sets.append(PolicySet.from_json(policy_sets))
else:
@@ -824,51 +1039,101 @@ class Profile(object):
setattr(self, 'PolicySets', value)
def add_input(self, profile_input):
+ """
+ Add a ProfileInput object to the inputs list of the Profile.
+ """
+ if not isinstance(profile_input, ProfileInput):
+ raise ValueError("Object passed is not a PolicyInput.")
if profile_input is None:
raise ValueError("No ProfileInput object provided.")
self.inputs.append(profile_input)
def remove_input(self, profile_input_id):
+ """
+ Remove a ProfileInput from the inputs list of the Profile.
+ """
for profile_input in self.inputs:
if profile_input_id == profile_input.profile_input_id:
- self.inputs.pop(profile_input)
+ self.inputs.remove(profile_input)
+ break
+
+ def get_input(self, profile_input_id):
+ """
+ Fetches a ProfileInput with the given ProfileInput id.
+ Returns None, if there is no matching input.
+ """
+ for profile_input in self.inputs:
+ if profile_input_id == profile_input.profile_input_id:
+ return profile_input
+ return None
def add_output(self, profile_output):
+ """
+ Add a ProfileOutput object to the outputs list of the Profile.
+ """
+ if not isinstance(profile_output, ProfileOutput):
+ raise ValueError("Object passed is not a PolicyOutput.")
if profile_output is None:
raise ValueError("No ProfileOutput object provided.")
self.outputs.append(profile_output)
def remove_output(self, profile_output_id):
+ """
+ Remove a ProfileOutput from the outputs list of the Profile.
+ """
for profile_output in self.outputs:
if profile_output_id == profile_output.profile_output_id:
- self.inputs.pop(profile_output)
+ self.inputs.remove(profile_output)
+
+ def get_output(self, profile_output_id):
+ """
+ Fetches a ProfileOutput with the given ProfileOutput id.
+ Returns None, if there is no matching output.
+ """
+ for profile_input in self.inputs:
+ if profile_output_id == profile_input.profile_input_id:
+ return profile_input
+ return None
def add_policy_set(self, policy_set):
+ """
+ Add a PolicySet object to the policy_sets list of the Profile.
+ """
if policy_set is None:
raise ValueError("No PolicySet object provided.")
self.policy_set_list.add_policy_set(policy_set)
def remove_policy_set(self, policy_set_name):
+ """
+ Remove a PolicySet from the policy_sets list of the Profile.
+ """
self.policy_set_list.remove_policy_set(policy_set_name)
+ def get_policy_set(self, policy_set_name):
+ """
+ Fetches a ProfileInput with the given ProfileInput id.
+ Returns None, if there is no matching input.
+ """
+ return self.policy_set_list.get_policy_set(policy_set_name)
+
@classmethod
- def from_json(cls, json_value):
+ def from_json(cls, attr_list):
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_data.profile_id = attr_list['id']
+ profile_data.class_id = attr_list['classId']
+ profile_data.name = attr_list['name']
+ profile_data.description = attr_list['description']
+ profile_data.enabled = attr_list['enabled']
+ profile_data.visible = attr_list['visible']
+ if 'enabledBy' in attr_list:
+ profile_data.enabled_by = attr_list['enabledBy']
+ if 'authenticatorId' in attr_list:
+ profile_data.authenticator_id = attr_list['authenticatorId']
+ profile_data.authorization_acl = attr_list['authzAcl']
+ profile_data.renewal = attr_list['renewal']
+ profile_data.xml_output = attr_list['xmlOutput']
- profile_inputs = json_value['Input']
+ profile_inputs = attr_list['Input']
if not isinstance(profile_inputs, types.ListType):
profile_data.inputs.append(ProfileInput.from_json(profile_inputs))
else:
@@ -876,7 +1141,7 @@ class Profile(object):
profile_data.inputs.append(
ProfileInput.from_json(profile_input))
- profile_outputs = json_value['Output']
+ profile_outputs = attr_list['Output']
if not isinstance(profile_outputs, types.ListType):
profile_data.outputs.append(
ProfileOutput.from_json(profile_outputs))
@@ -886,9 +1151,9 @@ class Profile(object):
ProfileOutput.from_json(profile_output))
profile_data.policy_set_list = \
- PolicySetList.from_json(json_value['PolicySets'])
+ PolicySetList.from_json(attr_list['PolicySets'])
- profile_data.link = pki.Link.from_json(json_value['link'])
+ profile_data.link = pki.Link.from_json(attr_list['link'])
return profile_data
@@ -904,6 +1169,20 @@ class Profile(object):
}
return str(attributes)
+ @staticmethod
+ def get_profile_data_from_file(path_to_file):
+ """
+ Reads the file for the serialized Profile object.
+ Currently supports only data format in json.
+ """
+ if path_to_file is None:
+ raise ValueError("File path must be specified.")
+ with open(path_to_file) as input_file:
+ data = input_file.read()
+ if data is not None:
+ return Profile.from_json(json.loads(data))
+ return None
+
class ProfileClient(object):
"""
@@ -995,34 +1274,67 @@ class ProfileClient(object):
"""
return self._modify_profile_state(profile_id, 'disable')
- def create_profile(self, profile_data):
- """
- Create a new profile for the given ProfileData object.
- """
+ def _send_profile_create(self, profile_data):
+
if profile_data is None:
raise ValueError("No ProfileData specified")
profile_object = json.dumps(profile_data, cls=encoder.CustomTypeEncoder,
sort_keys=True)
+
r = self._post(self.profiles_url, profile_object)
+
return Profile.from_json(r.json())
- def modify_profile(self, profile_data):
- """
- Modify an existing profile.
- """
+ def _send_profile_modify(self, profile_data):
if profile_data is None:
raise ValueError("No ProfileData specified")
+ profile_object = json.dumps(profile_data, cls=encoder.CustomTypeEncoder,
+ sort_keys=True)
+ if profile_data.profile_id is None:
+ raise ValueError("Profile Id is not specified.")
url = self.profiles_url + '/' + str(profile_data.profile_id)
- profile_object = json.dumps(profile_data, cls=encoder.CustomTypeEncoder,
- sort_keys=True)
r = self._put(url, profile_object)
+
return Profile.from_json(r.json())
+ @pki.handle_exceptions()
+ def create_profile(self, profile_data):
+ """
+ Create a new profile for the given Profile object.
+ """
+ return self._send_profile_create(profile_data)
+
+ @pki.handle_exceptions()
+ def modify_profile(self, profile_data):
+ """
+ Modify an existing profile with the given Profile object.
+ """
+ return self._send_profile_modify(profile_data)
+
+ def create_profile_from_file(self, path_to_file):
+ """
+ Reads the file for the serialized Profile object.
+ Performs the profile create operation.
+ Currently supports only data format in json.
+ """
+ profile_data = Profile.get_profile_data_from_file(path_to_file)
+ return self._send_profile_create(profile_data)
+
+ def modify_profile_from_file(self, path_to_file):
+ """
+ Reads the file for the serialized Profile object.
+ Performs the profile modify operation.
+ Currently supports only data format in json.
+ """
+ profile_data = Profile.get_profile_data_from_file(path_to_file)
+ return self._send_profile_modify(profile_data)
+
+ @pki.handle_exceptions()
def delete_profile(self, profile_id):
"""
- Delete a profile.
+ Delete a profile with the given Profile Id.
"""
if profile_id is None:
raise ValueError("Profile Id must be specified.")
@@ -1058,6 +1370,11 @@ def main():
#Initialize the ProfileClient class
profile_client = ProfileClient(connection)
+ # Folder to store the files generated during test
+ file_path = '/tmp/profile_client_test/'
+ if not os.path.exists(file_path):
+ os.makedirs(file_path)
+
#Fetching a list of profiles
profile_data_infos = profile_client.list_profiles()
print('List of profiles:')
@@ -1102,12 +1419,14 @@ def main():
print('-----------------------')
profile_data = Profile(name="My Sample User Cert Enrollment",
- profile_id="MySampleCert",
class_id="caEnrollImpl",
+ profile_id="MySampleProfile",
+ class_id="caEnrollImpl",
description="Example User Cert Enroll Impl",
enabled_by='admin', enabled=False, visible=False,
renewal=False, xml_output=False,
authorization_acl="")
+ # Adding a profile input
profile_input = ProfileInput("i1", "subjectNameInputImpl")
profile_input.add_attribute(ProfileAttribute("sn_uid"))
profile_input.add_attribute(ProfileAttribute("sn_e"))
@@ -1121,6 +1440,97 @@ def main():
profile_data.add_input(profile_input)
+ # Adding a profile output
+ profile_output = ProfileOutput("o1", name="Certificate Output",
+ class_id="certOutputImpl")
+ profile_output.add_attribute(ProfileAttribute("pretty_cert"))
+ profile_output.add_attribute(ProfileAttribute("b64_cert"))
+
+ profile_data.add_output(profile_output)
+
+ # Create a Policy set with a list of profile policies
+ policy_list = []
+
+ # Creating profile policy
+ policy_default = PolicyDefault("Subject Name Default",
+ "userSubjectNameDefaultImpl",
+ "This default populates a User-Supplied "
+ "Certificate Subject Name to the
request.")
+
+ attr_descriptor = Descriptor(syntax="string", description="Subject
Name")
+ policy_attribute = ProfileAttribute("name", descriptor=attr_descriptor)
+ policy_default.add_attribute(policy_attribute)
+
+ policy_constraint = PolicyConstraint("Subject Name Constraint",
+ "This constraint accepts the subject
"
+ "name that matches UID=.*",
+ "subjectNameConstraintImpl")
+ constraint_descriptor = Descriptor(syntax="string",
+ description="Subject Name Pattern")
+ policy_constraint_value = PolicyConstraintValue("pattern",
+ "UID=.*",
+ constraint_descriptor)
+ policy_constraint.add_constraint_value(policy_constraint_value)
+
+ policy_list.append(ProfilePolicy("1", policy_default, policy_constraint))
+
+ # Creating another profile policy
+ # Defining the policy default
+ policy_default = PolicyDefault("Validity Default",
"validityDefaultImpl",
+ "This default populates a Certificate "
+ "Validity to the request. The default "
+ "values are Range=180 in days")
+ attr_descriptor = Descriptor(syntax="string", description="Not
Before")
+ policy_attribute = ProfileAttribute("notBefore",
descriptor=attr_descriptor)
+ policy_default.add_attribute(policy_attribute)
+
+ attr_descriptor = Descriptor(syntax="string", description="Not
After")
+ policy_attribute = ProfileAttribute("notAfter",
descriptor=attr_descriptor)
+ policy_default.add_attribute(policy_attribute)
+
+ profile_param = ProfileParameter("range", 180)
+ profile_param2 = ProfileParameter("startTime", 0)
+ policy_default.add_parameter(profile_param)
+ policy_default.add_parameter(profile_param2)
+
+ #Defining the policy constraint
+ policy_constraint = PolicyConstraint("Validity Constraint",
+ "This constraint rejects the validity
"
+ "that is not between 365 days.",
+ "validityConstraintImpl")
+ constraint_descriptor = Descriptor(syntax="integer",
+ description="Validity Range (in
days)",
+ default_value=365)
+ policy_constraint_value = PolicyConstraintValue("range", 365,
+ constraint_descriptor)
+ policy_constraint.add_constraint_value(policy_constraint_value)
+
+ constraint_descriptor = Descriptor(syntax="boolean", default_value=False,
+ description="Check Not Before against"
+ " current time")
+ policy_constraint_value = PolicyConstraintValue("notBeforeCheck", False,
+ constraint_descriptor)
+ policy_constraint.add_constraint_value(policy_constraint_value)
+
+ constraint_descriptor = Descriptor(syntax="boolean", default_value=False,
+ description="Check Not After against"
+ " Not Before")
+ policy_constraint_value = PolicyConstraintValue("notAfterCheck", False,
+ constraint_descriptor)
+ policy_constraint.add_constraint_value(policy_constraint_value)
+
+ policy_list.append(ProfilePolicy("2", policy_default, policy_constraint))
+
+ policy_set = PolicySet("userCertSet", policy_list)
+
+ profile_data.add_policy_set(policy_set)
+
+ # Write the profile data object to a file for testing a file input
+ with open(file_path+'/original.json', 'w') as output_file:
+ output_file.write(json.dumps(profile_data,
+ cls=encoder.CustomTypeEncoder,
+ sort_keys=True, indent=4))
+ # Create a new profile
created_profile = profile_client.create_profile(profile_data)
print(created_profile)
print
@@ -1131,7 +1541,7 @@ def main():
try:
profile_data = Profile(name="My Sample User Cert Enrollment",
- profile_id="MySampleCert",
+ profile_id="MySampleProfile",
class_id="caEnrollImpl",
description="Example User Cert Enroll Impl",
enabled_by='admin', enabled=False,
visible=False,
@@ -1151,40 +1561,66 @@ def main():
profile_data.add_input(profile_input)
profile_client.create_profile(profile_data)
# pylint: disable-msg=W0703
- except Exception as e:
- print str(e)
+ except pki.BadRequestException as e:
+ print 'MySampleProfile ' + str(e)
print
# Modify the above created profile
- print('Modifying the profile MySampleCert.')
+ print('Modifying the profile MySampleProfile.')
print('-----------------------------------')
- fetch = profile_client.get_profile('MySampleCert')
+ fetch = profile_client.get_profile('MySampleProfile')
profile_input2 = ProfileInput("i2", "keyGenInputImpl")
profile_input2.add_attribute(ProfileAttribute("cert_request_type"))
profile_input2.add_attribute(ProfileAttribute("cert_request"))
fetch.add_input(profile_input2)
fetch.name += " (Modified)"
+ with open(file_path+'modified.json', 'w') as output_file:
+ output_file.write(json.dumps(fetch, cls=encoder.CustomTypeEncoder,
+ sort_keys=True, indent=4))
modified_profile = profile_client.modify_profile(fetch)
print(modified_profile)
print
# Delete a profile
- print ("Deleting the profile MySampleCert.")
+ print ("Deleting the profile MySampleProfile.")
print ("----------------------------------")
- profile_client.delete_profile('MySampleCert')
- print ("Deleted profile MySampleCert.")
+ profile_client.delete_profile('MySampleProfile')
+ print ("Deleted profile MySampleProfile.")
print
# Testing deletion of a profile
print('Test profile deletion.')
print('----------------------')
try:
- profile_client.get_profile('MySampleCert')
+ profile_client.get_profile('MySampleProfile')
# pylint: disable-msg=W0703
- except Exception as e:
+ except pki.ProfileNotFoundException as e:
print str(e)
+ print
+
+ # Creating a profile from file
+ print('Creating a profile using file input.')
+ print('------------------------------------')
+ original = profile_client.create_profile_from_file(
+ file_path + 'original.json')
+ print(original)
+ print
+
+ # Modifying a profile from file
+ print('Modifying a profile using file input.')
+ print('------------------------------------')
+ modified = profile_client.modify_profile_from_file(
+ file_path + 'modified.json')
+ print(modified)
+ print
+
+ # Test clean up
+ profile_client.delete_profile('MySampleProfile')
+ os.remove(file_path+'original.json')
+ os.remove(file_path+'modified.json')
+ os.removedirs(file_path)
if __name__ == "__main__":
--
1.8.5.3