Commit ca74a8ea authored by Rafael Monnerat's avatar Rafael Monnerat

Support multiple certificates per user

See merge request nexedi/erp5!1811
parents 63254fe4 e80c4d7e
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
</item> </item>
<item> <item>
<key> <string>condition</string> </key> <key> <string>condition</string> </key>
<value> <string></string> </value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
...@@ -56,7 +58,7 @@ ...@@ -56,7 +58,7 @@
</item> </item>
<item> <item>
<key> <string>priority</string> </key> <key> <string>priority</string> </key>
<value> <float>10.0</float> </value> <value> <float>11.0</float> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
...@@ -77,7 +79,20 @@ ...@@ -77,7 +79,20 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>text</string> </key> <key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_getCertificate</string> </value> <value> <string>string:${object_url}/CertificateLogin_getCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: here.getDestinationReference() is None</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
</item> </item>
<item> <item>
<key> <string>condition</string> </key> <key> <string>condition</string> </key>
<value> <string></string> </value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
...@@ -77,7 +79,20 @@ ...@@ -77,7 +79,20 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>text</string> </key> <key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_revokeCertificate</string> </value> <value> <string>string:${object_url}/CertificateLogin_revokeCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: here.getDestinationReference() is not None</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -5,30 +5,7 @@ from Products.ERP5Type import Permissions ...@@ -5,30 +5,7 @@ from Products.ERP5Type import Permissions
class Person(ERP5Person): class Person(ERP5Person):
security = ClassSecurityInfo() security = ClassSecurityInfo()
def _getCertificateLoginDocument(self): def checkCertificateRequest(self):
for _erp5_login in self.objectValues(
portal_type=["ERP5 Login"]):
if _erp5_login.getValidationState() == "validated" and \
_erp5_login.getReference() == self.getUserId():
# The user already created a Login document as UserId, so
# So just use this one.
return _erp5_login
for _certificate_login in self.objectValues(
portal_type=["Certificate Login"]):
if _certificate_login.getValidationState() == "validated":
return _certificate_login
certificate_login = self.newContent(
portal_type="Certificate Login",
# For now use UserId as easy way.
reference=self.getUserId()
)
certificate_login.validate()
return certificate_login
def _checkCertificateRequest(self):
try: try:
self.checkUserCanChangePassword() self.checkUserCanChangePassword()
except Unauthorized: except Unauthorized:
...@@ -41,25 +18,20 @@ class Person(ERP5Person): ...@@ -41,25 +18,20 @@ class Person(ERP5Person):
if getSecurityManager().getUser().getId() != user_id: if getSecurityManager().getUser().getId() != user_id:
raise raise
def _getCertificate(self): def _generateCertificate(self):
return self.getPortalObject().portal_certificate_authority\ certificate_login = self.newContent(
.getNewCertificate(self._getCertificateLoginDocument().getReference()) portal_type="Certificate Login",
)
def _revokeCertificate(self): certificate_dict = certificate_login.getCertificate()
return self.getPortalObject().portal_certificate_authority\ certificate_login.validate()
.revokeCertificateByCommonName(self._getCertificateLoginDocument().getReference()) return certificate_dict
security.declarePublic('getCertificate') security.declarePublic('generateCertificate')
def getCertificate(self): def generateCertificate(self):
"""Returns new SSL certificate""" """Returns new SSL certificate
self._checkCertificateRequest() This API was kept for backward compatibility"""
return self._getCertificate() self.checkCertificateRequest()
return self._generateCertificate()
security.declarePublic('revokeCertificate')
def revokeCertificate(self):
"""Revokes existing certificate"""
self._checkCertificateRequest()
self._revokeCertificate()
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getTitle') 'getTitle')
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2023 Nexedi SA and Contributors. All Rights Reserved.
# Rafael Monnerat <rafael@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# 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; either version 2
# of the License, or (at your option) any later version.
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
class CertificateLoginMixin:
security = ClassSecurityInfo()
def _getCertificate(self):
portal = self.getPortalObject()
_id = self._generateRandomId()
reference = 'CERTLOGIN-%i-%s' % (
portal.portal_ids.generateNewId(
id_group='certificate_login',
id_generator='non_continuous_integer_increasing',
), _id
)
self.setReference(reference)
certificate_dict = self.getPortalObject().portal_certificate_authority\
.getNewCertificate(self.getReference())
self.setDestinationReference(certificate_dict['id'])
return certificate_dict
def _revokeCertificate(self):
if self.getDestinationReference() is not None:
certificate_dict = self.getPortalObject().portal_certificate_authority\
.revokeCertificate(self.getDestinationReference())
self.setDestinationReference(None)
return certificate_dict
elif self.getReference() is not None:
# Backward compatibility whenever the serial wast set
certificate_dict = self.getPortalObject().portal_certificate_authority\
.revokeCertificateByCommonName(self.getReference())
# Ensure it is None
self.setDestinationReference(None)
return certificate_dict
else:
raise ValueError("No certificate found to revoke!")
security.declarePublic('getCertificate')
def getCertificate(self):
"""Returns new SSL certificate"""
if self.getDestinationReference() is not None:
raise ValueError("Certificate was already issued, please revoke first.")
return self._getCertificate()
security.declarePublic('revokeCertificate')
def revokeCertificate(self):
"""Revokes existing certificate"""
self._revokeCertificate()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Mixin Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>CertificateLoginMixin</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>mixin.erp5.CertificateLoginMixin</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -10,22 +10,16 @@ ...@@ -10,22 +10,16 @@
<key> <string>_property_domain_dict</string> </key> <key> <string>_property_domain_dict</string> </key>
<value> <value>
<dictionary> <dictionary>
<item>
<key> <string>description</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>short_title</string> </key> <key> <string>short_title</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -45,9 +39,7 @@ ...@@ -45,9 +39,7 @@
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <value> <string>Certificate Authority Tool contains Certificate Authority.</string> </value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>factory</string> </key> <key> <string>factory</string> </key>
...@@ -79,9 +71,15 @@ ...@@ -79,9 +71,15 @@
<none/> <none/>
</value> </value>
</item> </item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Contribution Tool</string> </value> <value> <string></string> </value>
</item> </item>
<item> <item>
<key> <string>type_class</string> </key> <key> <string>type_class</string> </key>
...@@ -104,28 +102,7 @@ ...@@ -104,28 +102,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>domain_name</string> </key> <key> <string>domain_name</string> </key>
<value> <value> <string>erp5_ui</string> </value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>description</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>property_name</string> </key> <key> <string>property_name</string> </key>
...@@ -134,7 +111,7 @@ ...@@ -134,7 +111,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/> <global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle> </pickle>
...@@ -142,9 +119,7 @@ ...@@ -142,9 +119,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>domain_name</string> </key> <key> <string>domain_name</string> </key>
<value> <value> <string>erp5_ui</string> </value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>property_name</string> </key> <key> <string>property_name</string> </key>
......
<type_mixin>
<portal_type id="Certificate Login">
<item>CertificateLoginMixin</item>
</portal_type>
</type_mixin>
\ No newline at end of file
parent = context.getParentValue()
if parent.getPortalType() == "Person":
parent.checkCertificateRequest()
certificate = context.getCertificate() certificate = context.getCertificate()
request = context.REQUEST request = context.REQUEST
request.set('your_certificate', certificate['certificate']) request.set('your_certificate', certificate['certificate'])
request.set('your_key', certificate['key']) request.set('your_key', certificate['key'])
return context.Person_viewCertificateDialog() return context.CertificateLogin_viewCertificateDialog(
keep_items = {'portal_status_message' : 'Certificate generated.'}
)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_revokeCertificate</string> </value> <value> <string>CertificateLogin_getCertificate</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
parent = context.getParentValue()
if parent.getPortalType() == "Person":
parent.checkCertificateRequest()
context.revokeCertificate() context.revokeCertificate()
return context.Base_redirect(form_id, keep_items = {'portal_status_message' : 'Certificate revoked.'}, **kw) return context.Base_redirect(form_id, keep_items = {'portal_status_message' : 'Certificate revoked.'}, **kw)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_getCertificate</string> </value> <value> <string>CertificateLogin_revokeCertificate</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -106,6 +106,7 @@ ...@@ -106,6 +106,7 @@
<key> <string>right</string> </key> <key> <string>right</string> </key>
<value> <value>
<list> <list>
<string>my_destination_reference</string>
<string>my_translated_validation_state_title</string> <string>my_translated_validation_state_title</string>
</list> </list>
</value> </value>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_destination_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_read_only_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Authorisation Identity</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
</item> </item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value> <value> <string>my_view_mode_read_only_reference</string> </value>
</item> </item>
<item> <item>
<key> <string>form_id</string> </key> <key> <string>form_id</string> </key>
......
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
<key> <string>action</string> </key> <key> <string>action</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -115,7 +119,7 @@ ...@@ -115,7 +119,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_viewCertificateDialog</string> </value> <value> <string>CertificateLogin_viewCertificateDialog</string> </value>
</item> </item>
<item> <item>
<key> <string>method</string> </key> <key> <string>method</string> </key>
...@@ -123,7 +127,7 @@ ...@@ -123,7 +127,7 @@
</item> </item>
<item> <item>
<key> <string>name</string> </key> <key> <string>name</string> </key>
<value> <string>Person_viewCertificateDialog</string> </value> <value> <string>CertificateLogin_viewCertificateDialog</string> </value>
</item> </item>
<item> <item>
<key> <string>pt</string> </key> <key> <string>pt</string> </key>
...@@ -139,7 +143,7 @@ ...@@ -139,7 +143,7 @@
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Certificate Request</string> </value> <value> <string>Request Certificate</string> </value>
</item> </item>
<item> <item>
<key> <string>unicode_mode</string> </key> <key> <string>unicode_mode</string> </key>
......
...@@ -211,7 +211,9 @@ ...@@ -211,7 +211,9 @@
<key> <string>default</string> </key> <key> <string>default</string> </key>
<value> <string>Please copy both key and certificate.\n <value> <string>Please copy both key and certificate.\n
\n \n
They are NOT stored anywhere for security reason.</string> </value> They are NOT stored anywhere for security reason.\n
\n
To activate the certificate, you still have to validate the Certificate Login</string> </value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>testCertificateAuthorityTool</string> </value> <value> <string>testCertificateAuthorityPerson</string> </value>
</item> </item>
<item> <item>
<key> <string>default_source_reference</string> </key> <key> <string>default_source_reference</string> </key>
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>test.erp5_certificate_authority.testCertificateAuthorityTool</string> </value> <value> <string>test.erp5.testCertificateAuthorityPerson</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
</item> </item>
<item> <item>
<key> <string>version</string> </key> <key> <string>version</string> </key>
<value> <string>erp5_certificate_authority</string> </value> <value> <string>erp5</string> </value>
</item> </item>
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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; either version 2
# of the License, or (at your option) any later version.
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import random
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from erp5.component.tool.CertificateAuthorityTool import CertificateAuthorityBusy
#from AccessControl import Unauthorized
class TestCertificateAuthorityTool(ERP5TypeTestCase):
def afterSetUp(self):
if "TEST_CA_PATH" in os.environ:
self.portal.portal_certificate_authority.certificate_authority_path = \
os.environ['TEST_CA_PATH']
def getBusinessTemplateList(self):
return ('erp5_base', 'erp5_certificate_authority')
def test_lock_unlock(self):
certificate_authority_tool = self.portal.portal_certificate_authority
certificate_authority_tool._checkCertificateAuthority()
try:
certificate_authority_tool._lockCertificateAuthority()
certificate_authority_tool._unlockCertificateAuthority()
certificate_authority_tool._lockCertificateAuthority()
self.assertRaises(CertificateAuthorityBusy, certificate_authority_tool._lockCertificateAuthority)
finally:
certificate_authority_tool._unlockCertificateAuthority()
def test_getNewCertificate(self):
certificate_authority_tool = self.portal.portal_certificate_authority
common_name = str(random.random())
certificate_dict = certificate_authority_tool.getNewCertificate(common_name)
self.assertEqual(common_name, certificate_dict['common_name'])
self.assertNotEqual(None, certificate_dict['id'])
self.assertNotEqual(None, certificate_dict['key'])
self.assertNotEqual(None, certificate_dict['certificate'])
self.assertIn('CN=%s' % common_name, certificate_dict['certificate'])
# Check serial
serial = certificate_authority_tool._getValidSerial(common_name)
self.assertEqual(serial, [certificate_dict['id'].upper()])
self.assertRaises(ValueError,
certificate_authority_tool.getNewCertificate, common_name)
def test_getNewCertificate_locked(self):
certificate_authority_tool = self.portal.portal_certificate_authority
certificate_authority_tool._checkCertificateAuthority()
try:
certificate_authority_tool._lockCertificateAuthority()
common_name = str(random.random())
self.assertRaises(CertificateAuthorityBusy,
certificate_authority_tool.getNewCertificate, common_name)
certificate_authority_tool._unlockCertificateAuthority()
certificate_dict = certificate_authority_tool.getNewCertificate(common_name)
self.assertEqual(common_name, certificate_dict['common_name'])
finally:
certificate_authority_tool._unlockCertificateAuthority()
def test_revokeCertificate_raise(self):
certificate_authority_tool = self.portal.portal_certificate_authority
common_name = str(random.random())
self.assertRaises(ValueError,
certificate_authority_tool.revokeCertificate, common_name)
def test_revokeCertificate(self):
certificate_authority_tool = self.portal.portal_certificate_authority
common_name = str(random.random())
certificate_dict = certificate_authority_tool.getNewCertificate(common_name)
self.assertEqual(common_name, certificate_dict['common_name'])
self.assertNotEqual(None, certificate_dict['id'])
self.assertIn('CN=%s' % common_name, certificate_dict['certificate'])
# Check serial
serial_list = certificate_authority_tool._getValidSerial(common_name)
self.assertEqual(len(serial_list), 1)
self.assertEqual(serial_list[0], certificate_dict['id'].upper())
revoke_dict = certificate_authority_tool.revokeCertificate(serial_list[0])
self.assertNotEqual(revoke_dict['crl'], None)
# No valid certificate anymore
self.assertRaises(ValueError, certificate_authority_tool._getValidSerial, common_name)
def test_revokeCertificateByName(self):
certificate_authority_tool = self.portal.portal_certificate_authority
common_name = str(random.random())
certificate_dict = certificate_authority_tool.getNewCertificate(common_name)
self.assertEqual(common_name, certificate_dict['common_name'])
self.assertNotEqual(None, certificate_dict['id'])
self.assertIn('CN=%s' % common_name, certificate_dict['certificate'])
serial_list = certificate_authority_tool._getValidSerial(common_name)
self.assertEqual(len(serial_list), 1)
self.assertEqual(serial_list[0], certificate_dict['id'].upper())
response = certificate_authority_tool.revokeCertificateByCommonName(common_name)
self.assertEqual(None, response)
# No valid certificate anymore
self.assertRaises(ValueError, certificate_authority_tool._getValidSerial, common_name)
def test_revokeCertificate_locked(self):
certificate_authority_tool = self.portal.portal_certificate_authority
common_name = str(random.random())
certificate_dict = certificate_authority_tool.getNewCertificate(common_name)
self.assertEqual(common_name, certificate_dict['common_name'])
try:
certificate_authority_tool._lockCertificateAuthority()
self.assertRaises(CertificateAuthorityBusy,
certificate_authority_tool.revokeCertificateByCommonName, common_name)
certificate_authority_tool._unlockCertificateAuthority()
response = certificate_authority_tool.revokeCertificateByCommonName(common_name)
self.assertEqual(None, response)
# No valid certificate anymore
self.assertRaises(ValueError, certificate_authority_tool._getValidSerial, common_name)
finally:
certificate_authority_tool._unlockCertificateAuthority()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testCertificateAuthorityTool</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testCertificateAuthorityTool</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -281,15 +281,15 @@ class CertificateAuthorityTool(BaseTool): ...@@ -281,15 +281,15 @@ class CertificateAuthorityTool(BaseTool):
index = open(self.index).read().splitlines() index = open(self.index).read().splitlines()
valid_line_list = [q for q in index if q.startswith('V') and valid_line_list = [q for q in index if q.startswith('V') and
('CN=%s/' % common_name in q)] ('CN=%s/' % common_name in q)]
if len(valid_line_list) != 1: if len(valid_line_list) < 1:
raise ValueError('No certificate for %r' % common_name) raise ValueError('No certificate for %r' % common_name)
return valid_line_list[0].split('\t')[3] return [l.split('\t')[3] for l in valid_line_list]
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'revokeCertificate') 'revokeCertificateByCommonName')
def revokeCertificateByCommonName(self, common_name): def revokeCertificateByCommonName(self, common_name):
self._checkCertificateAuthority() self._checkCertificateAuthority()
serial = self._getValidSerial(common_name) for serial in self._getValidSerial(common_name):
self.revokeCertificate(serial) self.revokeCertificate(serial)
InitializeClass(CertificateAuthorityTool) InitializeClass(CertificateAuthorityTool)
Certificate Login | view Certificate Login | get_certificate
Person | get_certificate Certificate Login | revoke_certificate
Person | revoke_certificate Certificate Login | view
\ No newline at end of file \ No newline at end of file
mixin.erp5.CertificateLoginMixin
\ No newline at end of file
Certificate Login | CertificateLoginMixin
\ No newline at end of file
test.erp5_certificate_authority.testCertificateAuthorityTool test.erp5.testCertificateAuthorityPerson
\ No newline at end of file test.erp5.testCertificateAuthorityTool
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment