Merge "Add Project Quota Support for Sub CAs"

This commit is contained in:
Jenkins 2015-09-11 19:31:23 +00:00 committed by Gerrit Code Review
commit 847f0050f6
20 changed files with 331 additions and 47 deletions

View File

@ -18,6 +18,7 @@ from six.moves.urllib import parse
from barbican import api
from barbican.api import controllers
from barbican.common import hrefs
from barbican.common import quota
from barbican.common import resources as res
from barbican.common import utils
from barbican.common import validators
@ -234,6 +235,7 @@ class CertificateAuthoritiesController(controllers.ACLMixin):
self.preferred_ca_repo = repo.get_preferred_ca_repository()
self.project_repo = repo.get_project_repository()
self.validator = validators.NewCAValidator()
self.quota_enforcer = quota.QuotaEnforcer('cas', self.ca_repo)
def __getattr__(self, name):
route_table = {
@ -343,8 +345,7 @@ class CertificateAuthoritiesController(controllers.ACLMixin):
if ctxt: # in authenticated pipeline case, always use auth token user
creator_id = ctxt.user
# TODO(alee) Add quota enforcement
# self.quota_enforcer.enforce(project)
self.quota_enforcer.enforce(project)
new_ca = cert_resources.create_subordinate_ca(
project_model=project,

View File

@ -44,7 +44,9 @@ quota_opts = [
cfg.IntOpt('quota_consumers',
default=-1,
help='Number of consumers allowed per project'),
]
cfg.IntOpt('quota_cas',
default=-1,
help='Number of CAs allowed per project')]
CONF = cfg.CONF
CONF.register_group(quota_opt_group)
@ -59,8 +61,7 @@ class QuotaDriver(object):
def _get_resources(self):
"""List of resources that can be constrained by a quota"""
return ['secrets', 'orders', 'containers',
'consumers']
return ['secrets', 'orders', 'containers', 'consumers', 'cas']
def _get_defaults(self):
"""Return list of default quotas"""
@ -68,7 +69,8 @@ class QuotaDriver(object):
'secrets': CONF.quotas.quota_secrets,
'orders': CONF.quotas.quota_orders,
'containers': CONF.quotas.quota_containers,
'consumers': CONF.quotas.quota_consumers
'consumers': CONF.quotas.quota_consumers,
'cas': CONF.quotas.quota_cas
}
return quotas

View File

@ -865,7 +865,8 @@ class ProjectQuotaValidator(ValidatorBase):
'orders': {'type': 'integer'},
'containers': {'type': 'integer'},
'transport_keys': {'type': 'integer'},
'consumers': {'type': 'integer'}
'consumers': {'type': 'integer'},
'cas': {'type': 'integer'}
},
'additionalProperties': False,
}

View File

@ -0,0 +1,20 @@
"""Add cas column to project quotas table
Revision ID: 4ecde3a3a72a
Revises: 10220ccbe7fa
Create Date: 2015-09-09 09:40:08.540064
"""
# revision identifiers, used by Alembic.
revision = '4ecde3a3a72a'
down_revision = '10220ccbe7fa'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(
'project_quotas',
sa.Column('cas', sa.Integer(), nullable=True))

View File

@ -1281,6 +1281,7 @@ class ProjectQuotas(BASE, ModelBase):
orders = sa.Column(sa.Integer, nullable=True)
containers = sa.Column(sa.Integer, nullable=True)
consumers = sa.Column(sa.Integer, nullable=True)
cas = sa.Column(sa.Integer, nullable=True)
def __init__(self, project_id=None, parsed_project_quotas=None):
"""Creates Project Quotas entity from a project and a dict.
@ -1304,11 +1305,13 @@ class ProjectQuotas(BASE, ModelBase):
self.orders = None
self.containers = None
self.consumers = None
self.cas = None
else:
self.secrets = parsed_project_quotas.get('secrets')
self.orders = parsed_project_quotas.get('orders')
self.containers = parsed_project_quotas.get('containers')
self.consumers = parsed_project_quotas.get('consumers')
self.cas = parsed_project_quotas.get('cas')
def _do_extra_dict_fields(self):
"""Sub-class hook method: return dict of fields."""
@ -1323,4 +1326,6 @@ class ProjectQuotas(BASE, ModelBase):
ret['containers'] = self.containers
if self.consumers:
ret['consumers'] = self.consumers
if self.cas:
ret['cas'] = self.cas
return ret

View File

@ -1475,6 +1475,15 @@ class CertificateAuthorityRepo(BaseRepo):
"""Sub-class hook: validate values."""
pass
def _build_get_project_entities_query(self, project_id, session):
"""Builds query for retrieving CA related to given project.
:param project_id: id of barbican project entity
:param session: existing db session reference.
"""
return session.query(models.CertificateAuthority).filter_by(
project_id=project_id).filter_by(deleted=False)
class CertificateAuthorityMetadatumRepo(BaseRepo):
"""Repository for the CertificateAuthorityMetadatum entity

View File

@ -26,7 +26,7 @@ class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
self.assertEqual(200, resp.status_int)
quotas_list = resp.json.get('quotas')
self.assertEqual({'consumers': -1, 'containers': -1, 'orders': -1,
'secrets': -1},
'secrets': -1, 'cas': -1},
quotas_list)
def test_should_get_specific_project_quotas(self):
@ -38,7 +38,7 @@ class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
self.assertEqual(200, resp.status_int)
project_quotas = resp.json.get('project_quotas')
self.assertEqual({'consumers': 105, 'containers': 103, 'orders': 102,
'secrets': 101},
'secrets': 101, 'cas': 106},
project_quotas)
def test_should_return_not_found_get_specific_project_quotas(self):
@ -164,7 +164,8 @@ class WhenTestingQuotas(utils.BarbicanAPIBaseTestCase):
'secrets': index * 100 + 1,
'orders': index * 100 + 2,
'containers': index * 100 + 3,
'consumers': index * 100 + 5}
'consumers': index * 100 + 5,
'cas': index * 100 + 6}
request = {'project_quotas': parsed_project_quotas}
resp = self.app.put_json(
'/project-quotas/{0}'.format(project_id), request)

View File

@ -34,21 +34,26 @@ class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
self.assertEqual(-1, quotas['orders'])
self.assertEqual(-1, quotas['containers'])
self.assertEqual(-1, quotas['consumers'])
self.assertEqual(-1, quotas['cas'])
def test_compute_effective_quotas_using_some_defaults(self):
configured_quotas = {'consumers': None, 'containers': 66,
'orders': None, 'secrets': 55}
'orders': None, 'secrets': 55,
'cas': None}
quotas = self.quota_driver._compute_effective_quotas(configured_quotas)
expected_quotas = {'consumers': -1, 'containers': 66,
'orders': -1, 'secrets': 55}
'orders': -1, 'secrets': 55,
'cas': -1}
self.assertEqual(expected_quotas, quotas)
def test_compute_effective_quotas_using_all_defaults(self):
configured_quotas = {'consumers': None, 'containers': None,
'orders': None, 'secrets': None}
'orders': None, 'secrets': None,
'cas': None}
quotas = self.quota_driver._compute_effective_quotas(configured_quotas)
expected_quotas = {'consumers': -1, 'containers': -1,
'orders': -1, 'secrets': -1}
'orders': -1, 'secrets': -1,
'cas': -1}
self.assertEqual(expected_quotas, quotas)
def test_is_unlimited_true(self):
@ -83,7 +88,8 @@ class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
'project_quotas': {'consumers': 105,
'containers': 103,
'orders': 102,
'secrets': 101}}], 'total': 1},
'secrets': 101,
'cas': 106}}], 'total': 1},
project_quotas)
def test_should_get_empty_project_quotas_list(self):
@ -122,13 +128,15 @@ class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
quotas = self.quota_driver.get_quotas(
self.get_test_project_id('partial'))
expected_quotas = {'quotas': {'consumers': -1, 'containers': 66,
'orders': -1, 'secrets': 55}}
'orders': -1, 'secrets': 55,
'cas': -1}}
self.assertEqual(expected_quotas, quotas)
def test_get_quotas_using_all_defaults(self):
quotas = self.quota_driver.get_quotas('not_configured')
expected_quotas = {'quotas': {'consumers': -1, 'containers': -1,
'orders': -1, 'secrets': -1}}
'orders': -1, 'secrets': -1,
'cas': -1}}
self.assertEqual(expected_quotas, quotas)
# ----------------------- Helper Functions ---------------------------
@ -152,7 +160,8 @@ class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
'secrets': index * 100 + 1,
'orders': index * 100 + 2,
'containers': index * 100 + 3,
'consumers': index * 100 + 5}
'consumers': index * 100 + 5,
'cas': index * 100 + 6}
return parsed_project_quotas
def get_test_response_project_quotas(self, index=1):
@ -161,19 +170,22 @@ class WhenTestingQuotaDriverFunctions(database_utils.RepositoryTestCase):
'secrets': 55,
'orders': None,
'containers': 66,
'consumers': None}
'consumers': None,
'cas': None}
elif index == 'none':
response_project_quotas = {
'secrets': None,
'orders': None,
'containers': None,
'consumers': None}
'consumers': None,
'cas': None}
else:
response_project_quotas = {
'secrets': index * 100 + 1,
'orders': index * 100 + 2,
'containers': index * 100 + 3,
'consumers': index * 100 + 5}
'consumers': index * 100 + 5,
'cas': index * 100 + 6}
return response_project_quotas
def create_a_test_project_quotas(self, index=1):
@ -213,7 +225,8 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
test_repo = DummyRepoForTestingQuotaEnforcement(0)
quota_enforcer = quota.QuotaEnforcer('secrets', test_repo)
disabled_project_quotas = {'consumers': 0, 'containers': 0,
'orders': 0, 'secrets': 0}
'orders': 0, 'secrets': 0,
'cas': 0}
self.quota_driver.set_project_quotas(self.project.external_id,
disabled_project_quotas)
exception = self.assertRaises(
@ -230,7 +243,8 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
test_repo = DummyRepoForTestingQuotaEnforcement(4)
quota_enforcer = quota.QuotaEnforcer('secrets', test_repo)
five_project_quotas = {'consumers': 5, 'containers': 5,
'orders': 5, 'secrets': 5}
'orders': 5, 'secrets': 5,
'cas': 5}
self.quota_driver.set_project_quotas(self.project.external_id,
five_project_quotas)
quota_enforcer.enforce(self.project)
@ -239,7 +253,8 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
test_repo = DummyRepoForTestingQuotaEnforcement(5)
quota_enforcer = quota.QuotaEnforcer('secrets', test_repo)
five_project_quotas = {'consumers': 5, 'containers': 5,
'orders': 5, 'secrets': 5}
'orders': 5, 'secrets': 5,
'cas': 5}
self.quota_driver.set_project_quotas(self.project.external_id,
five_project_quotas)
exception = self.assertRaises(
@ -256,7 +271,8 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
test_repo = DummyRepoForTestingQuotaEnforcement(6)
quota_enforcer = quota.QuotaEnforcer('secrets', test_repo)
five_project_quotas = {'consumers': 5, 'containers': 5,
'orders': 5, 'secrets': 5}
'orders': 5, 'secrets': 5,
'cas': 5}
self.quota_driver.set_project_quotas(self.project.external_id,
five_project_quotas)
exception = self.assertRaises(

View File

@ -1372,7 +1372,8 @@ class WhenTestingProjectQuotasValidator(utils.BaseTestCase):
self.good_project_quotas = {"project_quotas":
{"secrets": 50,
"orders": 10,
"containers": 20}}
"containers": 20,
"cas": 30}}
self.bad_project_quotas = {"bad key": "bad value"}
self.validator = validators.ProjectQuotaValidator()

View File

@ -120,6 +120,60 @@ class WhenTestingCertificateAuthorityRepo(database_utils.RepositoryTestCase):
session=session,
suppress_exception=False)
def test_get_count_should_return_zero(self):
session = self.ca_repo.get_session()
project = models.Project()
project.external_id = "my keystone id"
project.save(session=session)
session.commit()
count = self.ca_repo.get_count(project.id, session=session)
self.assertEqual(count, 0)
def test_get_count_should_return_one(self):
session = self.ca_repo.get_session()
project = models.Project()
project.external_id = "my keystone id"
project.save(session=session)
ca_model = models.CertificateAuthority(self.parsed_ca)
ca_model.project_id = project.id
self.ca_repo.create_from(ca_model, session=session)
session.commit()
count = self.ca_repo.get_count(project.id, session=session)
self.assertEqual(count, 1)
def test_get_count_should_return_one_after_delete(self):
session = self.ca_repo.get_session()
project = models.Project()
project.external_id = "my keystone id"
project.save(session=session)
ca_model = models.CertificateAuthority(self.parsed_ca)
ca_model.project_id = project.id
self.ca_repo.create_from(ca_model, session=session)
ca_model = models.CertificateAuthority(self.parsed_ca)
ca_model.project_id = project.id
self.ca_repo.create_from(ca_model, session=session)
session.commit()
count = self.ca_repo.get_count(project.id, session=session)
self.assertEqual(count, 2)
self.ca_repo.delete_entity_by_id(ca_model.id, "my keystone id",
session=session)
session.commit()
count = self.ca_repo.get_count(project.id, session=session)
self.assertEqual(count, 1)
class WhenTestingProjectCARepo(database_utils.RepositoryTestCase):

View File

@ -45,12 +45,14 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
'secrets': 101,
'orders': 102,
'containers': 103,
'consumers': 105}
'consumers': 105,
'cas': 106}
self.parsed_project_quotas_2 = {
'secrets': 201,
'orders': 202,
'containers': 203,
'consumers': 205}
'consumers': 205,
'cas': 206}
self.parsed_project_quotas_3 = {
'secrets': 301,
'containers': 303,
@ -80,6 +82,8 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
[s.containers for s in retrieved_project_quotas])
self.assertEqual([105],
[s.consumers for s in retrieved_project_quotas])
self.assertEqual([106],
[s.cas for s in retrieved_project_quotas])
def test_get_list_of_two_project_quotas(self):
self.project_quotas_repo.create_or_update_by_project_id(
@ -110,6 +114,8 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
[s.containers for s in retrieved_project_quotas])
self.assertItemsEqual([105, 205],
[s.consumers for s in retrieved_project_quotas])
self.assertItemsEqual([106, 206],
[s.cas for s in retrieved_project_quotas])
def test_should_raise_get_list_of_zero_project_quotas(self):
self.assertRaises(
@ -143,6 +149,7 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
self.assertEqual(102, retrieved_project_quotas.orders)
self.assertEqual(103, retrieved_project_quotas.containers)
self.assertEqual(105, retrieved_project_quotas.consumers)
self.assertEqual(106, retrieved_project_quotas.cas)
def test_project_quotas_with_some_defaults(self):
self.project_quotas_repo.create_or_update_by_project_id(
@ -161,6 +168,7 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
self.assertIsNone(retrieved_project_quotas.orders)
self.assertEqual(303, retrieved_project_quotas.containers)
self.assertEqual(305, retrieved_project_quotas.consumers)
self.assertIsNone(retrieved_project_quotas.cas)
def test_update_specific_project_quotas(self):
self.project_quotas_repo.create_or_update_by_project_id(
@ -184,6 +192,7 @@ class WhenTestingProjectQuotasRepo(database_utils.RepositoryTestCase):
self.assertEqual(202, retrieved_project_quotas.orders)
self.assertEqual(203, retrieved_project_quotas.containers)
self.assertEqual(205, retrieved_project_quotas.consumers)
self.assertEqual(206, retrieved_project_quotas.cas)
def test_should_raise_get_missing_specific_project_quotas(self):
self.assertRaises(

View File

@ -530,7 +530,8 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
'secrets': 101,
'orders': 102,
'containers': 103,
'consumers': 105}
'consumers': 105,
'cas': 106}
project_quotas = models.ProjectQuotas(project.id,
parsed_project_quotas)
@ -539,6 +540,7 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
self.assertEqual(102, project_quotas.orders)
self.assertEqual(103, project_quotas.containers)
self.assertEqual(105, project_quotas.consumers)
self.assertEqual(106, project_quotas.cas)
def test_create_new_project_quotas_with_all_default_quotas(self):
project = models.Project()
@ -552,6 +554,7 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
self.assertEqual(None, project_quotas.orders)
self.assertEqual(None, project_quotas.containers)
self.assertEqual(None, project_quotas.consumers)
self.assertEqual(None, project_quotas.cas)
def test_create_new_project_quotas_with_some_default_quotas(self):
project = models.Project()
@ -569,6 +572,7 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
self.assertEqual(None, project_quotas.orders)
self.assertEqual(103, project_quotas.containers)
self.assertEqual(105, project_quotas.consumers)
self.assertEqual(None, project_quotas.cas)
def test_should_throw_exception_missing_project_id(self):
self.assertRaises(exception.MissingArgumentError,
@ -582,7 +586,8 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
'secrets': 101,
'orders': 102,
'containers': 103,
'consumers': 105}
'consumers': 105,
'cas': 106}
project_quotas = models.ProjectQuotas(project.id,
parsed_project_quotas)
self.assertEqual(project.id,
@ -595,7 +600,8 @@ class WhenCreatingNewProjectQuotas(utils.BaseTestCase):
project_quotas.to_dict_fields()['containers'])
self.assertEqual(105,
project_quotas.to_dict_fields()['consumers'])
self.assertEqual(106,
project_quotas.to_dict_fields()['cas'])
if __name__ == '__main__':
unittest.main()

View File

@ -33,7 +33,7 @@ role. The service administrator's role is defined in Barbican's policy.json fil
The default role for a service admin is "key-manager:service-admin".
Quotas can be enforced for the following Barbican resources: secrets, containers,
orders, and consumers. The configured quota value can be None (use the default),
orders, consumers, and CAs. The configured quota value can be None (use the default),
-1 (unlimited), 0 (disabled), or a positive integer defining the maximum number
allowed for a project.
@ -61,6 +61,8 @@ in the standard configuration file are as follows.
# default number of consumers allowed per project
quota_consumers = -1
# default number of CAs allowed per project
quota_cas = -1
The default quotas are returned via a **GET** on the **quotas** resource when no
explicit project quotas have been set for the current project.
@ -92,7 +94,8 @@ with the request.
{"secrets": -1,
"orders": -1,
"containers": -1,
"consumers": -1
"consumers": -1,
"cas": -1
}
}
@ -119,7 +122,8 @@ To set or replace the quotas for the project with the ID 1234:
curl -i -X PUT -H "content-type:application/json" \
-H "X-Auth-Token:$TOKEN" \
-d '{"project_quotas": {"secrets": 500,
"orders": 100, "containers": -1, "consumers": 100}}' \
"orders": 100, "containers": -1, "consumers": 100,
"cas": 50}}' \
http://localhost:9311/v1/project-quotas/1234
Response:
@ -156,10 +160,11 @@ To get project quota information for a single project:
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"project_quotas":
{"secrets": 500,
"orders": 100,
"containers": -1,
"consumers": 100}}
{"secrets": 500,
"orders": 100,
"containers": -1,
"consumers": 100,
"cas": 50}}
The project quota information defined for all projects can be retrieved by using
@ -185,13 +190,15 @@ The returned response contains a list with all project quota data.
{"secrets": 500,
"orders": 100,
"containers": -1,
"consumers": 100}},
"consumers": 100,
"cas": 50}},
{"project_id": "5678",
"project_quotas":
{"secrets": 500,
"orders": 100,
"containers": -1,
"consumers": 100}}]}
"consumers": 100,
"cas": 50}}]}
To get more details on project quota lookup APIs you can reference

View File

@ -33,7 +33,8 @@ Request/Response:
"secrets": 10,
"orders": 20,
"containers": 10,
"consumers": -1
"consumers": -1,
"cas": 5
}
}
@ -60,6 +61,9 @@ Response Attributes
| consumers | integer | Contains the effective quota value of the current project |
| | | for the consumers resource. |
+------------+---------+--------------------------------------------------------------+
| cas | integer | Contains the effective quota value of the current project |
| | | for the CAs resource. |
+------------+---------+--------------------------------------------------------------+
Effective quota values are interpreted as follows:
@ -122,7 +126,8 @@ Request/Response:
"secrets": 2000,
"orders": 0,
"containers": -1,
"consumers": null
"consumers": null,
"cas": null
}
},
{
@ -131,7 +136,8 @@ Request/Response:
"secrets": 200,
"orders": 100,
"containers": -1,
"consumers": null
"consumers": null,
"cas": null
}
},
],
@ -177,6 +183,9 @@ Response Attributes
| consumers | integer | Contains the effective quota value of the current project |
| | | for the consumers resource. |
+----------------+---------+--------------------------------------------------------------+
| cas | integer | Contains the effective quota value of the current project |
| | | for the CAs resource. |
+----------------+---------+--------------------------------------------------------------+
| total | integer | The total number of configured project quotas records. |
+----------------+---------+--------------------------------------------------------------+
| next | string | A HATEOS url to retrieve the next set of quotas based on |
@ -250,7 +259,8 @@ Request/Response:
"secrets": 10,
"orders": 20,
"containers": -1,
"consumers": 10
"consumers": 10,
"cas": 5
}
}
@ -277,6 +287,9 @@ Response Attributes
| consumers | integer | Contains the configured quota value of the requested project |
| | | for the consumers resource. |
+----------------+---------+--------------------------------------------------------------+
| cas | integer | Contains the configured quota value of the requested project |
| | | for the CAs resource. |
+----------------+---------+--------------------------------------------------------------+
.. _get_project_quotas_uuid_status_codes:
@ -351,6 +364,9 @@ Request Attributes
| consumers | integer | The value to set for this project's |
| | | consumer quota. |
+----------------+---------+----------------------------------------------+
| cas | integer | The value to set for this project's |
| | | CA quota. |
+----------------+---------+----------------------------------------------+
Configured project quota values are specified as follows:

View File

@ -216,6 +216,8 @@ quota_transport_keys = -1
# default number of consumers allowed per project
quota_consumers = -1
# default number of CAs allowed per project
quota_cas = -1
# ================= Keystone Notification Options - Application ===============

View File

@ -98,3 +98,30 @@ class CABehaviors(base_behaviors.BaseBehaviors):
admin = user_name
self.created_entities.append((ca_ref, admin))
return resp, ca_ref
def delete_ca(self, ca_ref, extra_headers=None,
expected_fail=False, use_auth=True, user_name=None):
"""Delete a secret.
:param ca_ref: HATEOS ref of the secret to be deleted
:param extra_headers: Optional HTTP headers to add to the request
:param expected_fail: If test is expected to fail the deletion
:param use_auth: Boolean for whether to send authentication headers
:param user_name: The user name used to delete the entity
:return A request response object
"""
resp = self.client.delete(ca_ref, extra_headers=extra_headers,
use_auth=use_auth, user_name=user_name)
if not expected_fail:
for item in self.created_entities:
if item[0] == ca_ref:
self.created_entities.remove(item)
return resp
def delete_all_created_cas(self):
"""Delete all of the cas that we have created."""
entities = list(self.created_entities)
for (ca_ref, admin) in entities:
self.delete_ca(ca_ref, user_name=admin)

View File

@ -90,6 +90,8 @@ class CertificateAuthoritiesTestCase(base.TestCase):
self.send_test_order()
def tearDown(self):
self.order_behaviors.delete_all_created_orders()
self.ca_behaviors.delete_all_created_cas()
super(CertificateAuthoritiesTestCase, self).tearDown()
def get_signing_cert(self, ca_ref):

View File

@ -54,6 +54,7 @@ class QuotasTestCase(base.TestCase):
self.assertEqual(-1, resp.model.quotas.orders)
self.assertEqual(-1, resp.model.quotas.containers)
self.assertEqual(-1, resp.model.quotas.consumers)
self.assertEqual(-1, resp.model.quotas.cas)
def test_get_project_quotas_by_project_id(self):
"""Get project quota information for specific project"""
@ -73,6 +74,7 @@ class QuotasTestCase(base.TestCase):
self.assertEqual(10, resp.model.project_quotas.orders)
self.assertEqual(20, resp.model.project_quotas.containers)
self.assertIsNone(resp.model.project_quotas.consumers)
self.assertIsNone(resp.model.project_quotas.cas)
def test_get_project_quotas_by_project_id_not_found(self):
"""Get project quota information for specific project"""

View File

@ -13,14 +13,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from testtools import testcase
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
from functionaltests.api.v1.behaviors import consumer_behaviors
from functionaltests.api.v1.behaviors import container_behaviors
from functionaltests.api.v1.behaviors import order_behaviors
from functionaltests.api.v1.behaviors import quota_behaviors
from functionaltests.api.v1.behaviors import secret_behaviors
from functionaltests.api.v1.models import ca_models
from functionaltests.api.v1.models import consumer_model
from functionaltests.api.v1.models import container_models
from functionaltests.api.v1.models import order_models
@ -34,7 +41,12 @@ admin_b = CONF.rbac_users.admin_b
service_admin = CONF.identity.service_admin
@testcase.attr('no_parallel')
def is_ca_backend_snakeoil():
return 'snakeoil_ca' in\
cert_interface.CONF.certificate.enabled_certificate_plugins
@testtools.testcase.attr('no_parallel')
class QuotaEnforcementTestCase(base.TestCase):
def setUp(self):
@ -46,18 +58,22 @@ class QuotaEnforcementTestCase(base.TestCase):
self.order_behaviors = order_behaviors.OrderBehaviors(self.client)
self.consumer_behaviors = consumer_behaviors.ConsumerBehaviors(
self.client)
self.ca_behaviors = ca_behaviors.CABehaviors(self.client)
self.secret_data = self.get_default_secret_data()
self.quota_data = self.get_default_quota_data()
self.project_id = self.quota_behaviors.get_project_id_from_name(
admin_b)
self.order_secrets = []
self.root_ca_ref = None
self.test_order_sent = False
def tearDown(self):
self.quota_behaviors.delete_all_created_quotas()
self.consumer_behaviors.delete_all_created_consumers()
self.container_behaviors.delete_all_created_containers()
self.secret_behaviors.delete_all_created_secrets()
self.ca_behaviors.delete_all_created_cas()
for secret_ref in self.order_secrets:
resp = self.secret_behaviors.delete_secret(
secret_ref, user_name=admin_b)
@ -137,6 +153,32 @@ class QuotaEnforcementTestCase(base.TestCase):
self.create_consumers(count=5)
self.create_consumers(expected_return=403)
@testtools.skipIf(not is_ca_backend_snakeoil(),
"This test is only usable with snakeoil")
def test_snakeoil_cas_unlimited(self):
self.set_quotas('cas', -1)
self.create_snakeoil_cas(count=5)
@testtools.skipIf(not is_ca_backend_snakeoil(),
"This test is only usable with snakeoil")
def test_snakeoil_cas_disabled(self):
self.set_quotas('cas', 0)
self.create_snakeoil_cas(expected_return=403)
@testtools.skipIf(not is_ca_backend_snakeoil(),
"This test is only usable with snakeoil")
def test_snakeoil_cas_limited_one(self):
self.set_quotas('cas', 1)
self.create_snakeoil_cas(count=1)
self.create_snakeoil_cas(expected_return=403)
@testtools.skipIf(not is_ca_backend_snakeoil(),
"This test is only usable with snakeoil")
def test_snakeoil_cas_limited_five(self):
self.set_quotas('cas', 5)
self.create_snakeoil_cas(count=5)
self.create_snakeoil_cas(expected_return=403)
# ----------------------- Helper Functions ---------------------------
def get_default_quota_data(self):
@ -230,3 +272,63 @@ class QuotaEnforcementTestCase(base.TestCase):
resp, consumer_dat = self.consumer_behaviors.create_consumer(
model, container_ref, user_name=admin_b)
self.assertEqual(expected_return, resp.status_code)
def get_order_simple_cmc_request_data(self):
return {
'type': 'certificate',
'meta': {
'request_type': 'simple-cmc',
'requestor_name': 'Barbican User',
'requestor_email': 'user@example.com',
'requestor_phone': '555-1212'
}
}
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
(resp, cas, total, next_ref, prev_ref) = self.ca_behaviors.get_cas()
snake_name = 'barbican.plugin.snakeoil_ca.SnakeoilCACertificatePlugin'
snake_plugin_ca_id = "Snakeoil CA"
for item in cas:
ca = self.ca_behaviors.get_ca(item)
if ca.model.plugin_name == snake_name:
if ca.model.plugin_ca_id == snake_plugin_ca_id:
return item
return None
def get_snakeoil_subca_model(self):
parent_ca_ref = self.get_root_ca_ref()
return ca_models.CAModel(
parent_ca_ref=parent_ca_ref,
description="Test Snake Oil Subordinate CA",
name="Subordinate CA",
subject_dn="CN=Subordinate CA, O=example.com"
)
def create_snakeoil_cas(self, count=1, expected_return=201):
"""Utility function to create snakeoil cas"""
for _ in range(count):
ca_model = self.get_snakeoil_subca_model()
resp, ca_ref = self.ca_behaviors.create_ca(ca_model,
user_name=admin_b)
self.assertEqual(expected_return, resp.status_code)

View File

@ -20,12 +20,13 @@ from functionaltests.api.v1.models.base_models import BaseModel
class QuotasModel(BaseModel):
def __init__(self, secrets=None, orders=None, containers=None,
consumers=None):
consumers=None, cas=None):
super(QuotasModel, self).__init__()
self.secrets = secrets
self.orders = orders
self.containers = containers
self.consumers = consumers
self.cas = cas
class QuotasResponseModel(BaseModel):