def get_all(collection_name: str, last_key: str, limit: int): is_system = DynamoPlusService.is_system(collection_name) last_evaluated_key = None if is_system: logger.info("Get {} metadata from system".format(collection_name)) if collection_name == 'collection': last_collection_metadata = None collections, last_evaluated_key = SystemService.get_all_collections( limit, last_key) documents = list( map(lambda c: from_collection_to_dict(c), collections)) return documents, last_evaluated_key else: raise HandlerException(HandlerExceptionErrorCodes.BAD_REQUEST, "{} not valid", collection_name) else: logger.info("get all {} collection limit = {} last_key = {} ".format( collection_name, limit, last_key)) collection_metadata = SystemService.get_collection_by_name( collection_name) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) domain_service = DomainService(collection_metadata) logger.info("Query all {}".format(collection_name)) documents, last_evaluated_key = domain_service.find_all( limit, last_key) return documents, last_evaluated_key
def update(collection_name: str, document: dict, document_id: str = None): is_system = DynamoPlusService.is_system(collection_name) if is_system: if collection_name == "client_authorization": if document_id: document["client_id"] = document_id validate_client_authorization(document) client_authorization = from_dict_to_client_authorization(document) client_authorization = SystemService.update_authorization( client_authorization) logging.info("updated client_authorization {}".format( client_authorization.__str__)) return from_client_authorization_to_dict(client_authorization) else: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "updating {} is not supported ".format(collection_name)) else: logger.info("update {} document {}".format(collection_name, document)) collection_metadata = SystemService.get_collection_by_name( collection_name) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) if document_id: document[collection_metadata.id_key] = document_id validate_document(document, collection_metadata) timestamp = datetime.utcnow() document["update_date_time"] = timestamp.isoformat() d = DomainService(collection_metadata).update_document(document) logger.info("updated document {}".format(d)) return d
def update_indexes(collection_name: str, new_record: dict): system_service = SystemService() collection_metadata = system_service.get_collection_by_name( collection_name) if collection_metadata: __indexing(lambda r: r.update(new_record), system_service, collection_name, collection_metadata, new_record) else: logger.debug( 'Skipping creating indexes on record of type {}: collection not found' .format(collection_name))
def get(collection_name: str, document_id: str): is_system = DynamoPlusService.is_system(collection_name) if is_system: logger.info("Get {} metadata from system".format(collection_name)) if collection_name == 'collection': collection_metadata = SystemService.get_collection_by_name( document_id) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.NOT_FOUND, "{} not found with name {}".format(collection_name, document_id)) logger.info("Found collection {}".format( collection_metadata.__str__)) return collection_metadata.__dict__ elif collection_name == 'index': index_metadata = SystemService.get_index(document_id, collection_name) if index_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.NOT_FOUND, "{} not found with name {}".format(collection_name, document_id)) logger.info("Found index {}".format(index_metadata.__str__)) return index_metadata.__dict__ elif collection_name == 'client_authorization': client_authorization = SystemService.get_client_authorization( document_id) if client_authorization is None: raise HandlerException( HandlerExceptionErrorCodes.NOT_FOUND, "{} not found with name {}".format(collection_name, document_id)) logger.info("Found client_authorization {}".format( client_authorization.__str__)) return from_client_authorization_to_dict(client_authorization) else: raise HandlerException(HandlerExceptionErrorCodes.BAD_REQUEST, "{} not valid", collection_name) else: logger.info("Get {} document".format(collection_name)) collection_metadata = SystemService.get_collection_by_name( collection_name) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) document = DomainService(collection_metadata).get_document(document_id) if document is None: raise HandlerException( HandlerExceptionErrorCodes.NOT_FOUND, "{} not found with id {}".format(collection_name, document_id)) return document
def query(collection_name: str, query: dict = None, start_from: str = None, limit: int = None): is_system = DynamoPlusService.is_system(collection_name) documents = [] last_evaluated_key = None if is_system: if collection_name == 'collection': collections, last_key = SystemService.get_all_collections( limit, start_from) documents = list( map(lambda c: from_collection_to_dict(c), collections)) last_evaluated_key = last_key elif collection_name == 'index' and "matches" in query and "eq" in query[ "matches"] and "value" in query["matches"]["eq"]: target_collection_name = query["matches"]["eq"]["value"] index_metadata_list, last_key = SystemService.find_indexes_from_collection_name( target_collection_name, limit, start_from) documents = list( map(lambda i: from_index_to_dict(i), index_metadata_list)) last_evaluated_key = last_key else: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) else: if "matches" in query: predicate: Predicate = from_dict_to_predicate(query["matches"]) else: raise HandlerException(HandlerExceptionErrorCodes.BAD_REQUEST, "invalid predicate") logger.info("query {} collection by {} ".format( collection_name, predicate)) collection_metadata = SystemService.get_collection_by_name( collection_name) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) domain_service = DomainService(collection_metadata) query_id = "__".join(predicate.get_fields()) index_metadata = SystemService.get_index(query_id, collection_name) if index_metadata is None: raise HandlerException(HandlerExceptionErrorCodes.BAD_REQUEST, "no index {} found".format(query_id)) documents, last_evaluated_key = domain_service.query( predicate, limit, start_from) return documents, last_evaluated_key
def create(collection_name: str, document: dict) -> dict: is_system = DynamoPlusService.is_system(collection_name) if is_system: logger.info("Creating {} metadata {}".format(collection_name, document)) if collection_name == 'collection': validate_collection(document) collection_metadata = from_dict_to_collection(document) collection_metadata = SystemService.create_collection( collection_metadata) logger.info("Created collection {}".format( collection_metadata.__str__)) return from_collection_to_dict(collection_metadata) elif collection_name == 'index': validate_index(document) index_metadata = from_dict_to_index(document) index_metadata = SystemService.create_index(index_metadata) logger.info("Created index {}".format(index_metadata.__str__)) return from_index_to_dict(index_metadata) elif collection_name == 'client_authorization': validate_client_authorization(document) client_authorization = from_dict_to_client_authorization(document) client_authorization = SystemService.create_client_authorization( client_authorization) logging.info("created client_authorization {}".format( client_authorization.__str__())) return from_client_authorization_to_dict(client_authorization) else: logger.info("Create {} document {}".format(collection_name, document)) collection_metadata = SystemService.get_collection_by_name( collection_name) validate_document(document, collection_metadata) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) timestamp = datetime.utcnow() ## TODO: key generator if collection_metadata.auto_generate_id: document[collection_metadata.id_key] = str(uuid.uuid1()) document["creation_date_time"] = timestamp.isoformat() document["order_unique"] = str(int(time.time() * 1000.0)) d = DomainService(collection_metadata).create_document(document) logger.info("Created document {}".format(d)) return d
def get_client_authorization_by_api_key(headers: dict): authorization_value = AuthorizationService.get_authorization_value( headers, API_KEY_PREFIX) logger.info("authorization value = {}".format(authorization_value)) if authorization_value and CLIENT_ID_HEADER in headers: client_id = headers[CLIENT_ID_HEADER] logger.info("client_id = {}".format(client_id)) client_authorization = SystemService.get_client_authorization( client_id) logger.info( "found client authorization {}".format(client_authorization)) if client_authorization.api_key == authorization_value: return client_authorization
def get_client_authorization_using_http_signature_authorized( headers: dict, method: str, path: str): headers = dict((k.lower(), v) for k, v in headers.items()) if "authorization" in headers: authorization_header = headers["authorization"] if authorization_header.startswith("Signature"): authorization_value = authorization_header.replace( "Signature", "") signature_components = {} if authorization_value: for v in authorization_value.split(","): key, value = v.split("=", 1) signature_components[key.strip()] = value.replace( "\"", "") if "keyId" in signature_components: key_id = signature_components["keyId"] logging.info("client {}".format(key_id)) client_authorization = SystemService.get_client_authorization( key_id) if client_authorization: signatory_message = "(request-target): {} {}".format( method, path) for h in filter( lambda header: header.lower() != 'authorization', headers): signatory_message += "\n{}: {}".format( h.lower(), headers[h]) rsakey = RSA.importKey( client_authorization.client_public_key) signer = PKCS1_v1_5.new(rsakey) ## digest = SHA256.new() signature = signature_components[ "signature"].replace("\"", "") ## digest.update(b64decode(signatory_message)) hash = SHA256.new( signatory_message.encode("utf-8")) if signer.verify(hash, b64decode(signature)): return client_authorization else: logging.error( "signature not verified for key id {}". format(key_id)) else: logging.error( "client authorization not found for key {}". format(key_id)) else: logging.error("missing key id") else: logging.error("missing authorization value ")
def __indexing(repository_action: Callable[[DynamoPlusRepository], None], system_service: SystemService, collection_name: str, collection_metadata: Collection, new_record: dict): #logger.info("indexing {} record {}", collection_name, str(new_record)) is_system = DynamoPlusService.is_system(collection_name) if not is_system: logger.debug("{} is not system".format(collection_name)) indexes_by_collection_name, last_evaluated_key = system_service.find_indexes_from_collection_name( collection_name) for index in indexes_by_collection_name: logger.debug("found index {}".format(str(index))) repository = IndexDynamoPlusRepository(collection_metadata, index, False) index_model = IndexModel(collection_metadata, new_record, index) if index_model.data(): repository_action(repository)
def delete(collection_name: str, id: str): is_system = DynamoPlusService.is_system(collection_name) if is_system: logger.info("delete {} metadata from system".format(collection_name)) if collection_name == 'collection': SystemService.delete_collection(id) elif collection_name == 'index': index_metadata = SystemService.delete_index(id) elif collection_name == 'client_authorization': SystemService.delete_authorization(id) else: raise NotImplementedError( "collection_name {} not handled".format(collection_name)) else: logger.info("delete {} document {}".format(collection_name, id)) collection_metadata = SystemService.get_collection_by_name( collection_name) if collection_metadata is None: raise HandlerException( HandlerExceptionErrorCodes.BAD_REQUEST, "{} is not a valid collection".format(collection_name)) DomainService(collection_metadata).delete_document(id)
def setUp(self): self.systemService = SystemService()
class TestSystemService(unittest.TestCase): def setUp(self): self.systemService = SystemService() @patch.object(DynamoPlusRepository, "update") @patch.object(DynamoPlusRepository, "__init__") def test_update_authorization_http_signature(self, mock_repository, mock_update): expected_client_id = "test" client_authorization = ClientAuthorizationHttpSignature( expected_client_id, [Scope("example", ScopesType.CREATE)], "my-public-key") client_authorization_metadata = Collection("client_authorization", "client_id") mock_repository.return_value = None document = { "type": "http_signature", "client_id": expected_client_id, "client_scopes": [{ "collection_name": "example", "scope_type": "CREATE" }], "public_key": "my-public-key" } mock_update.return_value = Model(client_authorization_metadata, document) result = self.systemService.update_authorization(client_authorization) self.assertEqual(result.client_id, client_authorization.client_id) self.assertTrue(isinstance(result, ClientAuthorizationHttpSignature)) self.assertEqual(call(document), mock_update.call_args_list[0]) @patch.object(DynamoPlusRepository, "delete") @patch.object(DynamoPlusRepository, "__init__") def test_delete_authorization_http_signature(self, mock_repository, mock_delete): expected_client_id = "test" client_authorization_metadata = Collection("client_authorization", "client_id") mock_repository.return_value = None document = { "type": "http_signature", "client_id": expected_client_id, "client_scopes": [{ "collection_name": "example", "scope_type": "CREATE" }], "public_key": "my-public-key" } mock_delete.return_value = Model(client_authorization_metadata, document) self.systemService.delete_authorization(expected_client_id) self.assertEqual(call(expected_client_id), mock_delete.call_args_list[0]) @patch.object(DynamoPlusRepository, "create") @patch.object(DynamoPlusRepository, "__init__") def test_create_authorization_http_signature(self, mock_repository, mock_create): expected_client_id = "test" client_authorization = ClientAuthorizationHttpSignature( expected_client_id, [Scope("example", ScopesType.CREATE)], "my-public-key") client_authorization_metadata = Collection("client_authorization", "client_id") mock_repository.return_value = None document = { "type": "http_signature", "client_id": expected_client_id, "client_scopes": [{ "collection_name": "example", "scope_type": "CREATE" }], "public_key": "my-public-key" } mock_create.return_value = Model(client_authorization_metadata, document) result = self.systemService.create_client_authorization( client_authorization) self.assertEqual(result.client_id, client_authorization.client_id) self.assertTrue(isinstance(result, ClientAuthorizationHttpSignature)) self.assertEqual(call(document), mock_create.call_args_list[0]) @patch.object(DynamoPlusRepository, "create") @patch.object(DynamoPlusRepository, "__init__") def test_create_authorization_api_key(self, mock_repository, mock_create): expected_client_id = "test" client_authorization = ClientAuthorizationApiKey( expected_client_id, [Scope("example", ScopesType.CREATE)], "my-api-key", []) client_authorization_metadata = Collection("client_authorization", "client_id") mock_repository.return_value = None document = { "type": "api_key", "client_id": expected_client_id, "client_scopes": [{ "collection_name": "example", "scope_type": "CREATE" }], "api_key": "my-api-key" } mock_create.return_value = Model(client_authorization_metadata, document) result = self.systemService.create_client_authorization( client_authorization) self.assertEqual(result.client_id, client_authorization.client_id) self.assertTrue(isinstance(result, ClientAuthorizationApiKey)) self.assertEqual(call(document), mock_create.call_args_list[0]) @patch.object(DynamoPlusRepository, "get") @patch.object(DynamoPlusRepository, "__init__") def test_get_client_authorization_http_signature(self, mock_repository, mock_get): expected_client_id = 'my-client-id' mock_repository.return_value = None client_authorization_metadata = Collection("client_authorization", "client_id") document = { "client_id": expected_client_id, "type": "http_signature", "public_key": "my-public-key", "client_scopes": [{ "collection_name": "example", "scope_type": "GET" }] } expected_model = Model(client_authorization_metadata, document) mock_get.return_value = expected_model result = self.systemService.get_client_authorization( expected_client_id) self.assertTrue(mock_get.called_with(expected_client_id)) self.assertEqual(expected_client_id, result.client_id) self.assertIsInstance(result, ClientAuthorizationHttpSignature) self.assertEqual("my-public-key", result.client_public_key) self.assertEqual(1, len(result.client_scopes)) self.assertEqual("example", result.client_scopes[0].collection_name) self.assertEqual("GET", result.client_scopes[0].scope_type.name) @patch.object(DynamoPlusRepository, "get") @patch.object(DynamoPlusRepository, "__init__") def test_get_client_authorization_api_key(self, mock_repository, mock_get): expected_client_id = 'my-client-id' mock_repository.return_value = None client_authorization_metadata = Collection("client_authorization", "client_id") document = { "client_id": expected_client_id, "type": "api_key", "api_key": "my_api_key", "whitelist_hosts": ["*"], "client_scopes": [{ "collection_name": "example", "scope_type": "GET" }] } expected_model = Model(client_authorization_metadata, document) mock_get.return_value = expected_model result = self.systemService.get_client_authorization( expected_client_id) self.assertTrue(mock_get.called_with(expected_client_id)) self.assertEqual(expected_client_id, result.client_id) self.assertIsInstance(result, ClientAuthorizationApiKey) @patch.object(DynamoPlusRepository, "create") @patch.object(DynamoPlusRepository, "__init__") def test_createCollection(self, mock_repository, mock_create): expected_id = 'example' target_collection = { "name": "example", "id_key": "id", "ordering": None, "auto_generate_id": False } document = {"name": expected_id, "id_key": "id"} collection_metadata = Collection("collection", "name") expected_model = Model(collection_metadata, document) mock_repository.return_value = None mock_create.return_value = expected_model target_metadata = Collection("example", "id") created_collection = self.systemService.create_collection( target_metadata) collection_id = created_collection.name self.assertEqual(collection_id, expected_id) self.assertEqual(call(target_collection), mock_create.call_args_list[0]) @patch.object(DynamoPlusRepository, "delete") @patch.object(DynamoPlusRepository, "__init__") def test_deleteCollection(self, mock_repository, mock_delete): expected_id = 'example' mock_repository.return_value = None self.systemService.delete_collection(expected_id) self.assertTrue(mock_delete.called_with(expected_id)) @patch.object(DynamoPlusRepository, "get") @patch.object(DynamoPlusRepository, "__init__") def test_getCollection(self, mock_repository, mock_get): expected_id = 'example' mock_repository.return_value = None collection_metadata = Collection("collection", "name") document = { "name": expected_id, "id_key": expected_id, "fields": [{ "field1": "string" }] } expected_model = Model(collection_metadata, document) mock_get.return_value = expected_model result = self.systemService.get_collection_by_name(expected_id) # self.assertIn("fields",result) self.assertTrue(mock_get.called_with(expected_id)) @patch.object(DynamoPlusRepository, "create") @patch.object(DynamoPlusRepository, "__init__") def test_createIndexWithOrdering(self, mock_repository, mock_create): expected_id = 'field1__field2.field21__ORDER_BY__field2.field21' expected_conditions = ["field1", "field2.field21"] target_index = { "uid": "1", "name": expected_id, "collection": { "name": "example" }, "conditions": expected_conditions, "ordering_key": "field2.field21" } index_metadata = Collection("index", "name") expected_model = Model(index_metadata, target_index) mock_repository.return_value = None mock_create.return_value = expected_model index = Index("1", "example", expected_conditions, "field2.field21") created_index = self.systemService.create_index(index) index_name = created_index.index_name self.assertEqual(index_name, expected_id) self.assertEqual(call(target_index), mock_create.call_args_list[0]) @patch.object(DynamoPlusRepository, "create") @patch.object(DynamoPlusRepository, "__init__") def test_createIndexWithNoOrdering(self, mock_repository, mock_create): expected_id = 'field1__field2.field21' expected_conditions = ["field1", "field2.field21"] target_index = { "uid": "1", "name": expected_id, "collection": { "name": "example" }, "conditions": expected_conditions, "ordering_key": None } index_metadata = Collection("index", "name") expected_model = Model(index_metadata, target_index) mock_repository.return_value = None mock_create.return_value = expected_model index = Index("1", "example", expected_conditions) created_index = self.systemService.create_index(index) index_name = created_index.index_name self.assertEqual(index_name, expected_id) self.assertEqual(call(target_index), mock_create.call_args_list[0]) @patch.object(DynamoPlusRepository, "query_v2") @patch.object(DynamoPlusRepository, "__init__") def test_queryCollectionByName(self, mock_index_dynamoplus_repository, mock_find): collection_metadata = Collection("example", "name") expected_query = Query(Eq("name", "example"), collection_metadata) mock_index_dynamoplus_repository.return_value = None mock_find.return_value = QueryResult([ Model(Collection("example", "id"), { "name": "example", "id_key": "id" }) ]) collections = self.systemService.find_collections_by_example( collection_metadata) self.assertTrue(len(collections) == 1) self.assertEqual(collections[0].name, "example") self.assertEqual(call(expected_query), mock_find.call_args_list[0]) @patch.object(DynamoPlusRepository, "query_v2") @patch.object(DynamoPlusRepository, "__init__") def test_queryIndex_by_CollectionByName(self, mock_index_dynamoplus_repository, mock_find): expected_query = Query(Eq("collection.name", "example"), Collection("index", "uid")) mock_index_dynamoplus_repository.return_value = None mock_find.return_value = QueryResult([ Model( Collection("index", "name"), { "uid": "1", "name": "collection.name", "collection": { "name": "example" }, "conditions": ["collection.name"] }) ]) indexes, last_key = self.systemService.find_indexes_from_collection_name( "example") self.assertEqual(1, len(indexes)) self.assertEqual(indexes[0].uid, "1") self.assertEqual(indexes[0].index_name, "collection.name") self.assertEqual(call(expected_query), mock_find.call_args_list[0]) @patch.object(DynamoPlusRepository, "query_v2") @patch.object(DynamoPlusRepository, "__init__") def test_queryIndex_by_CollectionByName_generator( self, mock_index_dynamoplus_repository, mock_find): expected_query = Query(Eq("collection.name", "example"), Collection("index", "uid"), 2) mock_index_dynamoplus_repository.return_value = None mock_find.side_effect = [ self.fake_query_result("1", "2"), self.fake_query_result("2", "3"), self.fake_query_result("3", "4"), self.fake_query_result("4", "5"), self.fake_query_result("5"), ] indexes = self.systemService.get_indexes_from_collection_name_generator( "example", 2) uids = list(map(lambda i: i.uid, indexes)) self.assertEqual(5, len(uids)) self.assertEqual(["1", "2", "3", "4", "5"], uids) self.assertEqual(call(expected_query), mock_find.call_args_list[0]) def fake_query_result(self, uid, next=None): return QueryResult([ Model( Collection("index", "name"), { "uid": uid, "name": "collection.name", "collection": { "name": "example" + uid }, "conditions": ["collection.name"] }) ], next)