def test_construct_raises_exception(self): with self.assertRaises(EnvironmentError): InfrastructureFactory.construct( Environment(env={ InfrastructureFactory.PERSISTENCE_MODULE: "invalid topic" })) with self.assertRaises(AssertionError): InfrastructureFactory.construct( Environment(env={ InfrastructureFactory.PERSISTENCE_MODULE: get_topic(object) }))
def test_encrypt_and_decrypt(self): environment = Environment() key = AESCipher.create_key(16) environment["CIPHER_KEY"] = key # Check plain text can be encrypted and recovered. plain_text = b"some text" cipher = AESCipher(environment) cipher_text = cipher.encrypt(plain_text) cipher = AESCipher(environment) recovered_text = cipher.decrypt(cipher_text) self.assertEqual(recovered_text, plain_text) # Check raises on invalid nonce. with self.assertRaises(ValueError): cipher.decrypt(cipher_text[:10]) # Check raises on invalid tag. with self.assertRaises(ValueError): cipher.decrypt(cipher_text[:20]) # Check raises on invalid data. with self.assertRaises(ValueError): cipher.decrypt(cipher_text[:30]) # Check raises on invalid key. key = AESCipher.create_key(16) environment["CIPHER_KEY"] = key cipher = AESCipher(environment) with self.assertRaises(ValueError): cipher.decrypt(cipher_text)
def construct(cls: Type[TF], env: Environment) -> TF: """ Constructs concrete infrastructure factory for given named application. Reads and resolves persistence topic from environment variable 'PERSISTENCE_MODULE'. """ factory_cls: Type[TF] # noinspection SpellCheckingInspection topic = ( env.get( "INFRASTRUCTURE_FACTORY", # Legacy. "", ) or env.get( "FACTORY_TOPIC", # Legacy. "", ) or env.get( cls.PERSISTENCE_MODULE, "eventsourcing.popo:Factory", )) try: obj: Union[Type[TF], ModuleType] = resolve_topic(topic) except TopicError as e: raise EnvironmentError( "Failed to resolve persistence module topic: " f"'{topic}' from environment " f"variable '{cls.PERSISTENCE_MODULE}'") from e if isinstance(obj, ModuleType): # Find the factory in the module. factory_classes: List[Type[TF]] = [] for member in obj.__dict__.values(): if (isinstance(member, type) and issubclass(member, InfrastructureFactory) and member != InfrastructureFactory): factory_classes.append(cast(Type[TF], member)) if len(factory_classes) == 1: factory_cls = factory_classes[0] else: raise AssertionError( f"Found {len(factory_classes)} infrastructure factory classes in" f" '{topic}', expected 1.") elif isinstance(obj, type) and issubclass(obj, InfrastructureFactory): factory_cls = obj else: raise AssertionError( f"Not an infrastructure factory class or module: {topic}") return factory_cls(env=env)
def construct_env(self, name: str, env: Optional[EnvType] = None) -> Environment: """ Constructs environment from which application will be configured. """ _env = dict(type(self).env) if type(self).is_snapshotting_enabled or type(self).snapshotting_intervals: _env["IS_SNAPSHOTTING_ENABLED"] = "y" _env.update(os.environ) if env is not None: _env.update(env) return Environment(name, _env)
def __init__(self, environment: Environment): """ Initialises AES cipher with ``cipher_key``. :param str cipher_key: 16, 24, or 32 bytes encoded as base64 """ cipher_key = environment.get(self.CIPHER_KEY) if not cipher_key: raise EnvironmentError(f"'{self.CIPHER_KEY}' not set in env") key = b64decode(cipher_key.encode("utf8")) AESCipher.check_key_size(len(key)) self.key = key
def test_createkey(self): environment = Environment() # Valid key lengths. key = AESCipher.create_key(16) environment["CIPHER_KEY"] = key AESCipher(environment) key = AESCipher.create_key(24) environment["CIPHER_KEY"] = key AESCipher(environment) key = AESCipher.create_key(32) environment["CIPHER_KEY"] = key AESCipher(environment) # Non-valid key lengths (on generate key). with self.assertRaises(ValueError): AESCipher.create_key(12) with self.assertRaises(ValueError): AESCipher.create_key(20) with self.assertRaises(ValueError): AESCipher.create_key(28) with self.assertRaises(ValueError): AESCipher.create_key(36) # Non-valid key lengths (on construction). def create_key(num_bytes): return b64encode(AESCipher.random_bytes(num_bytes)).decode("utf8") key = create_key(12) environment["CIPHER_KEY"] = key with self.assertRaises(ValueError): AESCipher(environment) key = create_key(20) environment["CIPHER_KEY"] = key with self.assertRaises(ValueError): AESCipher(environment) key = create_key(28) environment["CIPHER_KEY"] = key with self.assertRaises(ValueError): AESCipher(environment) key = create_key(36) environment["CIPHER_KEY"] = key with self.assertRaises(ValueError): AESCipher(environment)
def setUp(self) -> None: self.env = Environment("TestCase") super().setUp()
def setUp(self) -> None: self.env = Environment("TestCase") self.env[InfrastructureFactory.PERSISTENCE_MODULE] = Factory.__module__ self.env[Factory.SQLITE_DBNAME] = ":memory:" super().setUp()
def test(self): # Construct transcoder. transcoder = JSONTranscoder() transcoder.register(UUIDAsHex()) transcoder.register(DecimalAsStr()) transcoder.register(DatetimeAsISO()) # Construct cipher. environment = Environment() environment[AESCipher.CIPHER_KEY] = AESCipher.create_key(16) cipher = AESCipher(environment) # Construct compressor. compressor = ZlibCompressor() # Construct mapper with cipher. mapper = Mapper(transcoder=transcoder, cipher=cipher) # Create a domain event. domain_event = BankAccount.TransactionAppended( originator_id=uuid4(), originator_version=123456, timestamp=BankAccount.TransactionAppended.create_timestamp(), amount=Decimal("10.00"), ) # Map from domain event. stored_event = mapper.from_domain_event(domain_event) # Map to domain event. copy = mapper.to_domain_event(stored_event) # Check values are not visible. assert "Alice" not in str(stored_event.state) # Check decrypted copy has correct values. assert copy.originator_id == domain_event.originator_id assert copy.originator_version == domain_event.originator_version assert copy.timestamp == domain_event.timestamp, copy.timestamp assert copy.originator_version == domain_event.originator_version assert len(stored_event.state) == 162, len(stored_event.state) # Construct mapper with cipher and compressor. mapper = Mapper( transcoder=transcoder, cipher=cipher, compressor=compressor, ) # Map from domain event. stored_event = mapper.from_domain_event(domain_event) # Map to domain event. copy = mapper.to_domain_event(stored_event) # Check decompressed copy has correct values. assert copy.originator_id == domain_event.originator_id assert copy.originator_version == domain_event.originator_version assert len(stored_event.state) in ( 135, 136, 137, 138, 139, 140, 141, 142, 143, ), len(stored_event.state)