Finish Initialization of CA Table when Barbican Starts
Before all CA related commands can succeed, the CA table needs to be populated. This patch invokes code to refresh the table as Barbican starts. In addition, the CA table entries can expire over time, to ensure GET /cas always returns current values, the table is refreshed as necessary at that time too. I also added created a session for this routine to ensure any updates are written immediately to the database. Change-Id: I0e6f9aec073c897f811b0b87a16db3b52753d734 Closes-bug: #1495576
This commit is contained in:
parent
641aa44fe8
commit
91a561dd9a
@ -247,6 +247,8 @@ class CertificateAuthoritiesController(controllers.ACLMixin):
|
||||
self.project_repo = repo.get_project_repository()
|
||||
self.validator = validators.NewCAValidator()
|
||||
self.quota_enforcer = quota.QuotaEnforcer('cas', self.ca_repo)
|
||||
# Populate the CA table at start up
|
||||
cert_resources.refresh_certificate_resources()
|
||||
|
||||
def __getattr__(self, name):
|
||||
route_table = {
|
||||
@ -281,6 +283,9 @@ class CertificateAuthoritiesController(controllers.ACLMixin):
|
||||
if plugin_ca_id is not None:
|
||||
plugin_ca_id = parse.unquote_plus(plugin_ca_id)
|
||||
|
||||
# refresh CA table, in case plugin entries have expired
|
||||
cert_resources.refresh_certificate_resources()
|
||||
|
||||
result = self.ca_repo.get_by_create_date(
|
||||
offset_arg=kw.get('offset', 0),
|
||||
limit_arg=kw.get('limit', None),
|
||||
|
@ -622,18 +622,25 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
|
||||
def refresh_ca_table(self):
|
||||
"""Refreshes the CertificateAuthority table."""
|
||||
session = self.ca_repo.get_session()
|
||||
updates_made = False
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
plugin_name = utils.generate_fullname_for(plugin)
|
||||
cas, offset, limit, total = self.ca_repo.get_by_create_date(
|
||||
plugin_name=plugin_name,
|
||||
session=session,
|
||||
suppress_exception=True)
|
||||
if total < 1:
|
||||
# if no entries are found, then the plugin has not yet been
|
||||
# queried or that plugin's entries have expired.
|
||||
# Most of the time, this will be a no-op for plugins.
|
||||
self.update_ca_info(plugin)
|
||||
self.update_ca_info(plugin, session=session)
|
||||
updates_made = True
|
||||
if updates_made:
|
||||
session.flush()
|
||||
session.commit()
|
||||
|
||||
def update_ca_info(self, cert_plugin):
|
||||
def update_ca_info(self, cert_plugin, session=None):
|
||||
"""Update the CA info for a particular plugin."""
|
||||
|
||||
plugin_name = utils.generate_fullname_for(cert_plugin)
|
||||
@ -642,18 +649,20 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
old_cas, offset, limit, total = self.ca_repo.get_by_create_date(
|
||||
plugin_name=plugin_name,
|
||||
suppress_exception=True,
|
||||
session=session,
|
||||
show_expired=True)
|
||||
|
||||
for old_ca in old_cas:
|
||||
plugin_ca_id = old_ca.plugin_ca_id
|
||||
if plugin_ca_id not in new_ca_infos.keys():
|
||||
# remove CAs that no longer exist
|
||||
self._delete_ca(old_ca)
|
||||
self._delete_ca(old_ca, session=session)
|
||||
else:
|
||||
# update those that still exist
|
||||
self.ca_repo.update_entity(
|
||||
old_ca,
|
||||
new_ca_infos[plugin_ca_id])
|
||||
new_ca_infos[plugin_ca_id],
|
||||
session=session)
|
||||
|
||||
old_ids = set([ca.plugin_ca_id for ca in old_cas])
|
||||
new_ids = set(new_ca_infos.keys())
|
||||
@ -661,17 +670,18 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
# add new CAs
|
||||
add_ids = new_ids - old_ids
|
||||
for add_id in add_ids:
|
||||
self._add_ca(plugin_name, add_id, new_ca_infos[add_id])
|
||||
self._add_ca(plugin_name, add_id, new_ca_infos[add_id],
|
||||
session=session)
|
||||
|
||||
def _add_ca(self, plugin_name, plugin_ca_id, ca_info):
|
||||
def _add_ca(self, plugin_name, plugin_ca_id, ca_info, session=None):
|
||||
parsed_ca = dict(ca_info)
|
||||
parsed_ca['plugin_name'] = plugin_name
|
||||
parsed_ca['plugin_ca_id'] = plugin_ca_id
|
||||
new_ca = models.CertificateAuthority(parsed_ca)
|
||||
self.ca_repo.create_from(new_ca)
|
||||
self.ca_repo.create_from(new_ca, session=session)
|
||||
|
||||
def _delete_ca(self, ca):
|
||||
self.ca_repo.delete_entity_by_id(ca.id, None)
|
||||
def _delete_ca(self, ca, session=None):
|
||||
self.ca_repo.delete_entity_by_id(ca.id, None, session=session)
|
||||
|
||||
|
||||
class _CertificateEventPluginManager(named.NamedExtensionManager,
|
||||
|
@ -65,6 +65,11 @@ ORDER_STATUS_CA_UNAVAIL_FOR_CHECK = models.OrderStatus(
|
||||
)
|
||||
|
||||
|
||||
def refresh_certificate_resources():
|
||||
# Before CA operations can be performed, the CA table must be populated
|
||||
cert.CertificatePluginManager().refresh_ca_table()
|
||||
|
||||
|
||||
def issue_certificate_request(order_model, project_model, result_follow_on):
|
||||
"""Create the initial order with CA.
|
||||
|
||||
|
@ -59,9 +59,11 @@ class WhenTestingCAsResource(utils.BarbicanAPIBaseTestCase):
|
||||
|
||||
def test_response_should_include_total(self):
|
||||
self.create_cas()
|
||||
self.params['plugin_name'] = self.plugin_name
|
||||
resp = self.app.get('/cas/', self.params)
|
||||
self.assertIn('total', resp.namespace)
|
||||
self.assertEqual(self.num_cas, resp.namespace['total'])
|
||||
self.assertEqual(self.num_cas,
|
||||
resp.namespace['total'])
|
||||
|
||||
def test_should_get_list_certificate_authorities_with_params(self):
|
||||
self.create_cas()
|
||||
@ -76,7 +78,7 @@ class WhenTestingCAsResource(utils.BarbicanAPIBaseTestCase):
|
||||
self.assertEqual(resp.namespace['total'], 1)
|
||||
|
||||
def test_should_handle_no_cas(self):
|
||||
self.params = {'offset': 0, 'limit': 2}
|
||||
self.params = {'offset': 0, 'limit': 2, 'plugin_name': 'dummy'}
|
||||
resp = self.app.get('/cas/', self.params)
|
||||
self.assertEqual(resp.namespace.get('cas'), [])
|
||||
self.assertEqual(resp.namespace.get('total'), 0)
|
||||
@ -351,6 +353,7 @@ class WhenTestingCAsResource(utils.BarbicanAPIBaseTestCase):
|
||||
self.plugin_ca_id = 'default_plugin_ca_id_'
|
||||
self.ca_id = "id1"
|
||||
|
||||
self.num_root_cas = 2
|
||||
self.num_cas = 10
|
||||
self.offset = 2
|
||||
self.limit = 4
|
||||
@ -409,7 +412,7 @@ class WhenTestingCAsResource(utils.BarbicanAPIBaseTestCase):
|
||||
|
||||
# create subca for DELETE testing
|
||||
parsed_ca = {
|
||||
'plugin_name': self.plugin_name,
|
||||
'plugin_name': self.plugin_name + '_delete_me',
|
||||
'plugin_ca_id': self.plugin_ca_id + "subca 1",
|
||||
'name': self.plugin_name,
|
||||
'description': 'Sub CA for default plugin',
|
||||
@ -423,8 +426,6 @@ class WhenTestingCAsResource(utils.BarbicanAPIBaseTestCase):
|
||||
ca_repo.save(ca)
|
||||
self.subca = ca
|
||||
|
||||
self.num_cas += 1
|
||||
|
||||
def _create_url(self, external_project_id, offset_arg=None,
|
||||
limit_arg=None):
|
||||
if limit_arg:
|
||||
|
@ -281,9 +281,11 @@ class WhenTestingCertificatePluginManager(utils.BaseTestCase,
|
||||
self.plugin_returned.get_ca_info.assert_called_once_with()
|
||||
self.ca_repo.update_entity.assert_called_once_with(
|
||||
ca1,
|
||||
ca1_modified_info)
|
||||
ca1_modified_info,
|
||||
session=mock.ANY)
|
||||
|
||||
self.ca_repo.delete_entity_by_id.assert_called_once_with(
|
||||
ca2.id,
|
||||
None)
|
||||
None,
|
||||
session=mock.ANY)
|
||||
self.ca_repo.create_from.assert_has_calls([])
|
||||
|
@ -44,16 +44,6 @@ This should provide a response like the following:
|
||||
|
||||
{"cas": ["http://localhost:9311/v1/cas/3a2a533d-ed4d-4c68-a418-2ee79f4c9581"], "total": 1}
|
||||
|
||||
Note: The list of available CAs is currently populated by the plugins the first
|
||||
time a certificate order is initiated. At that time, each plugin is queried
|
||||
for details for each CA with which it is connected. These details have an
|
||||
expiration time set by the plugin, after which the CA data is requested anew
|
||||
from the plugin.
|
||||
|
||||
This means of course that the CA list is empty until the first certificate
|
||||
order is processed. You can order a certificate using instructions in
|
||||
:doc:`Certificates Quick Start <./certificates>`.
|
||||
|
||||
.. _getting_ca_details:
|
||||
|
||||
Getting Details about a CA
|
||||
|
@ -94,10 +94,6 @@ class CATestCommon(base.TestCase):
|
||||
|
||||
self.simple_cmc_data = copy.deepcopy(order_simple_cmc_request_data)
|
||||
|
||||
# we need to prime the pump ie. populate the CA table by sending
|
||||
# in an order (just in case)
|
||||
self.send_test_order()
|
||||
|
||||
def tearDown(self):
|
||||
self.order_behaviors.delete_all_created_orders()
|
||||
self.ca_behaviors.delete_all_created_cas()
|
||||
|
@ -13,12 +13,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import base64
|
||||
import testtools
|
||||
|
||||
from barbican.common import hrefs
|
||||
from barbican.plugin.interface import certificate_manager as cert_interface
|
||||
from barbican.tests import certificate_utils as certutil
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import ca_behaviors
|
||||
@ -284,24 +281,7 @@ class QuotaEnforcementTestCase(base.TestCase):
|
||||
}
|
||||
}
|
||||
|
||||
def send_test_order(self, ca_ref=None):
|
||||
if self.test_order_sent:
|
||||
return
|
||||
self.test_order_sent = True
|
||||
test_model = order_models.OrderModel(
|
||||
**self.get_order_simple_cmc_request_data())
|
||||
test_model.meta['request_data'] = base64.b64encode(
|
||||
certutil.create_good_csr())
|
||||
if ca_ref is not None:
|
||||
ca_id = hrefs.get_ca_id_from_ref(ca_ref)
|
||||
test_model.meta['ca_id'] = ca_id
|
||||
|
||||
create_resp, order_ref = self.order_behaviors.create_order(test_model)
|
||||
self.assertEqual(202, create_resp.status_code)
|
||||
self.assertIsNotNone(order_ref)
|
||||
|
||||
def get_root_ca_ref(self):
|
||||
self.send_test_order()
|
||||
if self.root_ca_ref is not None:
|
||||
return self.root_ca_ref
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user