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)
                }))
示例#2
0
    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)
示例#3
0
    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)
示例#4
0
 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)
示例#5
0
    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
示例#6
0
    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)
示例#7
0
 def setUp(self) -> None:
     self.env = Environment("TestCase")
     super().setUp()
示例#8
0
 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)