From 8254828e6445878bf73427e1d8184773ae36a8c2 Mon Sep 17 00:00:00 2001 From: Kaitlin Farr Date: Fri, 11 Nov 2016 16:45:32 -0500 Subject: [PATCH] Add client list filter functionality Exposes the secret_type, created, updated, expiration, and sort as filters for listing secrets. Change-Id: I583f27f91cb3c6bdb23438dff6b539407b4005ed Depends-On: I95b5390ef24a754f66ccdb9ccde34cd0820b02fd --- barbicanclient/secrets.py | 32 +++- .../client/v1/functional/test_secrets.py | 181 ++++++++++++++++++ 2 files changed, 211 insertions(+), 2 deletions(-) diff --git a/barbicanclient/secrets.py b/barbicanclient/secrets.py index f373007..0f9de79 100644 --- a/barbicanclient/secrets.py +++ b/barbicanclient/secrets.py @@ -537,19 +537,37 @@ class SecretManager(base.BaseEntityManager): raise ValueError('secret_ref is required.') self._api.delete(secret_ref) - def list(self, limit=10, offset=0, name=None, algorithm=None, - mode=None, bits=0): + def list(self, limit=10, offset=0, name=None, algorithm=None, mode=None, + bits=0, secret_type=None, created=None, updated=None, + expiration=None, sort=None): """List Secrets for the project This method uses the limit and offset parameters for paging, and also supports filtering. + The time filters (created, updated, and expiration) are expected to + be an ISO 8601 formatted string, which can be prefixed with comparison + operators: 'gt:' (greater-than), 'gte:' (greater-than-or-equal), 'lt:' + (less-than), or 'lte': (less-than-or-equal). + :param limit: Max number of secrets returned :param offset: Offset secrets to begin list :param name: Name filter for the list :param algorithm: Algorithm filter for the list :param mode: Mode filter for the list :param bits: Bits filter for the list + :param secret_type: Secret type filter for the list + :param created: Created time filter for the list, an ISO 8601 format + string, optionally prefixed with 'gt:', 'gte:', 'lt:', or 'lte:' + :param updated: Updated time filter for the list, an ISO 8601 format + string, optionally prefixed with 'gt:', 'gte:', 'lt:', or 'lte:' + :param expiration: Expiration time filter for the list, an ISO 8601 + format string, optionally prefixed with 'gt:', 'gte:', 'lt:', + or 'lte:' + :param sort: Determines the sorted order of the returned list, a + string of comma-separated sort keys ('created', 'expiration', + 'mode', 'name', 'secret_type', 'status', or 'updated') with a + direction appended (':asc' or ':desc') to each key :returns: list of Secret objects that satisfy the provided filter criteria. :rtype: list @@ -568,6 +586,16 @@ class SecretManager(base.BaseEntityManager): params['mode'] = mode if bits > 0: params['bits'] = bits + if secret_type: + params['secret_type'] = secret_type + if created: + params['created'] = created + if updated: + params['updated'] = updated + if expiration: + params['expiration'] = expiration + if sort: + params['sort'] = sort response = self._api.get(self._entity, params=params) diff --git a/functionaltests/client/v1/functional/test_secrets.py b/functionaltests/client/v1/functional/test_secrets.py index 0c8abed..7832154 100644 --- a/functionaltests/client/v1/functional/test_secrets.py +++ b/functionaltests/client/v1/functional/test_secrets.py @@ -14,7 +14,9 @@ # limitations under the License. import base64 +import datetime import sys +import time from barbicanclient import exceptions from functionaltests.client import base @@ -767,3 +769,182 @@ class SecretsTestCase(base.TestCase): resp = self.barbicanclient.secrets.get(secret_ref) self.assertEqual("ACTIVE", resp.status) self.assertEqual(secret_type, resp.secret_type) + + @utils.parameterized_dataset({ + 'query_by_name': { + 'secret_1_dict': dict(name="name1"), + 'secret_2_dict': dict(name="name2"), + 'query_dict': dict(name="name1") + }, + 'query_by_algorithm': { + 'secret_1_dict': dict(algorithm="algorithm1"), + 'secret_2_dict': dict(algorithm="algorithm2"), + 'query_dict': dict(algorithm="algorithm1") + }, + 'query_by_mode': { + 'secret_1_dict': dict(mode="mode1"), + 'secret_2_dict': dict(mode="mode2"), + 'query_dict': dict(mode="mode1") + }, + 'query_by_bit_length': { + 'secret_1_dict': dict(bit_length=1024), + 'secret_2_dict': dict(bit_length=2048), + 'query_dict': dict(bits=1024) + }, + 'query_by_secret_type': { + 'secret_1_dict': dict(secret_type='opaque'), + 'secret_2_dict': dict(secret_type='symmetric'), + 'query_dict': dict(secret_type='opaque') + }, + }) + @testcase.attr('positive') + def test_secret_list_with_filter(self, secret_1_dict, secret_2_dict, + query_dict): + secret_1 = self.barbicanclient.secrets.create(**secret_1_dict) + secret_1_ref = self.cleanup.add_entity(secret_1) + self.assertIsNotNone(secret_1_ref) + secret_2 = self.barbicanclient.secrets.create(**secret_2_dict) + secret_2_ref = self.cleanup.add_entity(secret_2) + self.assertIsNotNone(secret_2_ref) + + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(1, len(secret_list)) + + @utils.parameterized_dataset({ + 'query_by_name': { + 'secret_1_dict': dict(name="name1"), + 'secret_2_dict': dict(name="name2"), + 'sort': 'name' + }, + 'query_by_algorithm': { + 'secret_1_dict': dict(algorithm="algorithm1"), + 'secret_2_dict': dict(algorithm="algorithm2"), + 'sort': 'algorithm' + }, + 'query_by_mode': { + 'secret_1_dict': dict(mode="mode1"), + 'secret_2_dict': dict(mode="mode2"), + 'sort': 'mode' + }, + 'query_by_bit_length': { + 'secret_1_dict': dict(bit_length=1024), + 'secret_2_dict': dict(bit_length=2048), + 'sort': 'bit_length' + }, + 'query_by_secret_type': { + 'secret_1_dict': dict(secret_type='opaque'), + 'secret_2_dict': dict(secret_type='symmetric'), + 'sort': 'secret_type' + }, + }) + @testcase.attr('positive') + def test_secret_list_with_sort(self, secret_1_dict, secret_2_dict, sort): + secret_1 = self.barbicanclient.secrets.create(**secret_1_dict) + secret_1_ref = self.cleanup.add_entity(secret_1) + self.assertIsNotNone(secret_1_ref) + secret_2 = self.barbicanclient.secrets.create(**secret_2_dict) + secret_2_ref = self.cleanup.add_entity(secret_2) + self.assertIsNotNone(secret_2_ref) + + query_dict = {'sort': sort + ":asc"} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + self.assertEqual(secret_1_ref, secret_list[0].secret_ref) + + query_dict = {'sort': sort + ":desc"} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + self.assertEqual(secret_2_ref, secret_list[0].secret_ref) + + @utils.parameterized_dataset({ + 'created': { + 'date_type': 'created', + }, + 'updated': { + 'date_type': 'updated', + }, + 'expiration': { + 'date_type': 'expiration', + }, + }) + @testcase.attr('positive') + def test_secret_list_with_date_filter(self, date_type): + now = datetime.datetime.utcnow() + expiration_1 = (now + datetime.timedelta(days=3)).isoformat() + expiration_2 = (now + datetime.timedelta(days=5)).isoformat() + secret_1 = self.barbicanclient.secrets.create(expiration=expiration_1) + secret_1_ref = self.cleanup.add_entity(secret_1) + self.assertIsNotNone(secret_1_ref) + payload = "gF6+lLoF3ohA9aPRpt+6bQ==" + self.barbicanclient.secrets.update(secret_1_ref, payload) + + time.sleep(1) + + secret_2 = self.barbicanclient.secrets.create(expiration=expiration_2) + secret_2_ref = self.cleanup.add_entity(secret_2) + self.assertIsNotNone(secret_2_ref) + + time_to_search_1 = getattr(secret_1, date_type).isoformat() + time_to_search_2 = getattr(secret_2, date_type).isoformat() + + # Search for secrets with secret 1's time + query_dict = {date_type: time_to_search_1} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(1, len(secret_list)) + self.assertEqual(secret_1_ref, secret_list[0].secret_ref) + + # Search for secrets with time < secret 2, i.e. secret 1 + query_dict = {date_type: 'lt:' + time_to_search_2} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(1, len(secret_list)) + self.assertEqual(secret_1_ref, secret_list[0].secret_ref) + + # Search for secrets with time < secret 1, i.e. none + query_dict = {date_type: 'lt:' + time_to_search_1} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(0, len(secret_list)) + + # Search for secrets with time <= secret 2, i.e. both secrets + query_dict = {date_type: 'lte:' + time_to_search_2} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + + # Search for secrets with time > secret 1, i.e. secret 2 + query_dict = {date_type: 'gt:' + time_to_search_1} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(1, len(secret_list)) + self.assertEqual(secret_2_ref, secret_list[0].secret_ref) + + # Search for secrets with time > secret 2, i.e. none + query_dict = {date_type: 'gt:' + time_to_search_2} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(0, len(secret_list)) + + # Search for secrets with time >= secret 1, i.e. both secrets + query_dict = {date_type: 'gte:' + time_to_search_1} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + + # Sort secrets by date + query_dict = {'sort': date_type + ":asc"} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + self.assertEqual(secret_1_ref, secret_list[0].secret_ref) + + # Sort secrets by date + query_dict = {'sort': date_type + ":desc"} + secret_list = self.barbicanclient.secrets.list(**query_dict) + + self.assertEqual(2, len(secret_list)) + self.assertEqual(secret_2_ref, secret_list[0].secret_ref)