passphera-core 0.30.1__py3-none-any.whl → 0.31.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,12 @@
1
1
  from dataclasses import dataclass, field
2
2
  from datetime import datetime, timezone
3
- from uuid import UUID, uuid4
4
-
5
- from cryptography.fernet import Fernet
6
3
 
7
4
  from cipherspy.cipher import *
8
5
  from cipherspy.exceptions import InvalidAlgorithmException
9
- from cipherspy.utilities import generate_salt, derive_key
10
-
11
- from passphera_core.exceptions import InvalidPropertyNameException
12
6
 
7
+ from passphera_core.utilities import check_property_name
13
8
 
14
- _cipher_registry: dict[str, BaseCipherAlgorithm] = {
9
+ _cipher_registry: dict = {
15
10
  'caesar': CaesarCipherAlgorithm,
16
11
  'affine': AffineCipherAlgorithm,
17
12
  'playfair': PlayfairCipherAlgorithm,
@@ -27,63 +22,8 @@ _default_properties: dict[str, str] = {
27
22
  }
28
23
 
29
24
 
30
- @dataclass
31
- class Password:
32
- id: UUID = field(default_factory=uuid4)
33
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
34
- updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
35
- context: str = field(default_factory=str)
36
- text: str = field(default_factory=str)
37
- password: str = field(default_factory=str)
38
- salt: bytes = field(default_factory=lambda: bytes)
39
-
40
- def encrypt(self) -> None:
41
- """
42
- Encrypts the password using Fernet symmetric encryption.
43
-
44
- This method generates a new salt, derives an encryption key using the password
45
- and salt, and then encrypts the password using Fernet encryption. The encrypted
46
- password is stored back in the password field as a base64-encoded string.
47
- :return: None
48
- """
49
- self.salt = generate_salt()
50
- key = derive_key(self.password, self.salt)
51
- self.password = Fernet(key).encrypt(self.password.encode()).decode()
52
-
53
- def decrypt(self) -> str:
54
- """
55
- Decrypts the encrypted password using Fernet symmetric decryption.
56
-
57
- This method uses the stored salt to derive the encryption key and then
58
- decrypts the stored encrypted password using Fernet decryption.
59
- :return: str: The decrypted original password
60
- """
61
- key = derive_key(self.password, self.salt)
62
- return Fernet(key).decrypt(self.password.encode()).decode()
63
-
64
- def to_dict(self) -> dict:
65
- """Convert the Password entity to a dictionary."""
66
- return {
67
- "id": self.id,
68
- "created_at": self.created_at,
69
- "updated_at": self.updated_at,
70
- "context": self.context,
71
- "text": self.text,
72
- "password": self.password,
73
- "salt": self.salt,
74
- }
75
-
76
- def from_dict(self, data: dict) -> None:
77
- """Convert a dictionary to a Password entity."""
78
- for key, value in data.items():
79
- setattr(self, key, value)
80
-
81
-
82
25
  @dataclass
83
26
  class Generator:
84
- id: UUID = field(default_factory=uuid4)
85
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
86
- updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
87
27
  shift: int = field(default=_default_properties["shift"])
88
28
  multiplier: int = field(default=_default_properties["multiplier"])
89
29
  key: str = field(default=_default_properties["key"])
@@ -97,9 +37,19 @@ class Generator:
97
37
  Get the primary algorithm used to cipher the password
98
38
  :return: BaseCipherAlgorithm: The primary algorithm used for the cipher
99
39
  """
100
- if self.algorithm.lower() not in _cipher_registry:
40
+ algo_name = self.algorithm.lower()
41
+ if algo_name not in _cipher_registry:
101
42
  raise InvalidAlgorithmException(self.algorithm)
102
- return _cipher_registry[self.algorithm.lower()]
43
+ AlgoClass = _cipher_registry[algo_name]
44
+ if algo_name == "caesar":
45
+ return AlgoClass(self.shift)
46
+ elif algo_name == "affine":
47
+ return AlgoClass(self.shift, self.multiplier)
48
+ elif algo_name == "playfair":
49
+ return AlgoClass(self.key)
50
+ elif algo_name == "hill":
51
+ return AlgoClass(self.key)
52
+ raise InvalidAlgorithmException(self.algorithm)
103
53
 
104
54
  def get_properties(self) -> dict:
105
55
  """
@@ -123,6 +73,17 @@ class Generator:
123
73
  "characters_replacements": self.characters_replacements,
124
74
  }
125
75
 
76
+ @check_property_name
77
+ def get_property(self, prop: str):
78
+ """
79
+ Get the value of a specific generator property
80
+ :param prop: The property name to retrieve; must be one of: shift, multiplier, key, algorithm, prefix, postfix
81
+ :raises InvalidPropertyNameException: If the property name is not one of the allowed properties
82
+ :return: The value of the requested property
83
+ """
84
+ return getattr(self, prop)
85
+
86
+ @check_property_name
126
87
  def set_property(self, prop: str, value: str):
127
88
  """
128
89
  Update a generator property with a new value
@@ -131,24 +92,21 @@ class Generator:
131
92
  :raises ValueError: If the property name is not one of the allowed properties
132
93
  :return: None
133
94
  """
134
- if prop not in {"id", "created_at", "updated_at", "shift", "multiplier", "key", "algorithm", "prefix", "postfix"}:
135
- raise InvalidPropertyNameException(prop)
136
95
  if prop in ["shift", "multiplier"]:
137
96
  value = int(value)
138
97
  setattr(self, prop, value)
139
98
  if prop == "algorithm":
140
99
  self.get_algorithm()
141
100
  self.updated_at = datetime.now(timezone.utc)
142
-
143
- def reset_property(self, prop: str):
101
+
102
+ @check_property_name
103
+ def reset_property(self, prop: str) -> None:
144
104
  """
145
105
  Reset a generator property to its default value
146
106
  :param prop: The property name to reset, it must be one of: shift, multiplier, key, algorithm, prefix, postfix
147
107
  :raises ValueError: If the property name is not one of the allowed properties
148
108
  :return: None
149
109
  """
150
- if prop not in {"id", "created_at", "updated_at", "shift", "multiplier", "key", "algorithm", "prefix", "postfix"}:
151
- raise InvalidPropertyNameException(prop)
152
110
  setattr(self, prop, _default_properties[prop])
153
111
  if prop == "algorithm":
154
112
  self.get_algorithm()
@@ -162,7 +120,7 @@ class Generator:
162
120
  """
163
121
  return self.characters_replacements.get(character, character)
164
122
 
165
- def replace_character(self, character: str, replacement: str) -> None:
123
+ def set_character_replacement(self, character: str, replacement: str) -> None:
166
124
  """
167
125
  Replace a character with another character or set of characters
168
126
  Eg: pg.replace_character('a', '@1')
@@ -173,7 +131,7 @@ class Generator:
173
131
  self.characters_replacements[character[0]] = replacement
174
132
  self.updated_at = datetime.now(timezone.utc)
175
133
 
176
- def reset_character(self, character: str) -> None:
134
+ def reset_character_replacement(self, character: str) -> None:
177
135
  """
178
136
  Reset a character to its original value (remove its replacement from characters_replacements)
179
137
  :param character: The character to be reset to its original value
@@ -198,15 +156,13 @@ class Generator:
198
156
  def to_dict(self) -> dict:
199
157
  """Convert the Generator entity to a dictionary."""
200
158
  return {
201
- "id": self.id,
202
- "created_at": self.created_at,
203
- "updated_at": self.updated_at,
204
159
  "shift": self.shift,
205
160
  "multiplier": self.multiplier,
206
161
  "key": self.key,
207
162
  "algorithm": self.algorithm,
208
163
  "prefix": self.prefix,
209
164
  "postfix": self.postfix,
165
+ "character_replacements": self.characters_replacements,
210
166
  }
211
167
 
212
168
  def from_dict(self, data: dict) -> None:
@@ -1,21 +1,3 @@
1
- class PasswordNotFoundException(Exception):
2
- def __init__(self) -> None:
3
- super().__init__("Password not found")
4
-
5
-
6
- class DuplicatePasswordException(Exception):
7
- def __init__(self, password) -> None:
8
- self.password = password
9
- message = self._build_message(password)
10
- super().__init__(message)
11
-
12
- @staticmethod
13
- def _build_message(password) -> str:
14
- if hasattr(password, 'context') and password.context:
15
- return f"Password for context '{password.context}' already exists"
16
- return "Duplicate password detected"
17
-
18
-
19
1
  class InvalidPropertyNameException(Exception):
20
2
  def __init__(self, property_name: str) -> None:
21
3
  self.property_name = property_name
@@ -1,20 +1,9 @@
1
- from datetime import datetime, timezone
1
+ from passphera_core.exceptions import InvalidPropertyNameException
2
2
 
3
- from passphera_core.entities import Password, Generator
4
- from passphera_core.exceptions import DuplicatePasswordException
5
- from passphera_core.interfaces import VaultRepository
6
3
 
7
-
8
- def generate_password(entity: Password, generator: Generator) -> None:
9
- entity.text = entity.text
10
- entity.context = entity.context
11
- entity.password = generator.generate_password(entity.text)
12
- entity.updated_at = datetime.now(timezone.utc)
13
- entity.encrypt()
14
-
15
-
16
- def get_password(password_repository: VaultRepository, context: str) -> Password:
17
- password_entity: Password = password_repository.get(context)
18
- if password_entity:
19
- raise DuplicatePasswordException(password_entity)
20
- return password_entity
4
+ def check_property_name(func):
5
+ def wrapper(self, prop, value):
6
+ if prop not in {"shift", "multiplier", "key", "algorithm", "prefix", "postfix", "character_replacements"}:
7
+ raise InvalidPropertyNameException(prop)
8
+ return func(self, prop, value)
9
+ return wrapper
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: passphera-core
3
- Version: 0.30.1
4
- Summary: The core system of passphera project
3
+ Version: 0.31.0
4
+ Summary: The core of passphera project
5
5
  Home-page: https://github.com/passphera/core
6
6
  Author: Fathi Abdelmalek
7
- Author-email: abdelmalek.fathi.2001@gmail.com
7
+ Author-email: passphera@imfathi.com
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
@@ -0,0 +1,8 @@
1
+ passphera_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ passphera_core/entities.py,sha256=RXoSnJgLpij3zYSWN9JeybiujTz1IZgHIZh3MxW3Z58,6987
3
+ passphera_core/exceptions.py,sha256=nb0iWLaFpFHEUjCa5dQ8UnIn3tZs8gN5dy2URgqJf0g,211
4
+ passphera_core/utilities.py,sha256=O15lTSzT-kNUnPkx9Eufc_qET7uK-9cccPKzx2gHz0s,362
5
+ passphera_core-0.31.0.dist-info/METADATA,sha256=EFYOC1RNysf0UPMaYqjzNZBXdOjuHQz0b08_iLA9vVs,851
6
+ passphera_core-0.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ passphera_core-0.31.0.dist-info/top_level.txt,sha256=aDUX2iWGOyfzyf6XakLWTbgeWqNrypMHO074Qratyds,15
8
+ passphera_core-0.31.0.dist-info/RECORD,,
File without changes
@@ -1,70 +0,0 @@
1
- from passphera_core.entities import Generator
2
- from passphera_core.interfaces import GeneratorRepository
3
-
4
-
5
- class GetGeneratorUseCase:
6
- def __init__(self, generator_repository: GeneratorRepository):
7
- self.generator_repository: GeneratorRepository = generator_repository
8
-
9
- def __call__(self) -> Generator:
10
- return self.generator_repository.get()
11
-
12
-
13
- class GetPropertiesUseCase:
14
- def __init__(self, generator_repository: GeneratorRepository):
15
- self.generator_repository: GeneratorRepository = generator_repository
16
-
17
- def __call__(self) -> dict:
18
- return self.generator_repository.get().get_properties()
19
-
20
-
21
- class SetPropertyUseCase:
22
- def __init__(self, generator_repository: GeneratorRepository):
23
- self.generator_repository: GeneratorRepository = generator_repository
24
-
25
- def __call__(self, prop: str, value: str) -> Generator:
26
- generator_entity: Generator = self.generator_repository.get()
27
- generator_entity.set_property(prop, value)
28
- self.generator_repository.update(generator_entity)
29
- return generator_entity
30
-
31
-
32
- class ResetPropertyUseCase:
33
- def __init__(self, generator_repository: GeneratorRepository):
34
- self.generator_repository: GeneratorRepository = generator_repository
35
-
36
- def __call__(self, prop: str) -> Generator:
37
- generator_entity: Generator = self.generator_repository.get()
38
- generator_entity.reset_property(prop)
39
- self.generator_repository.update(generator_entity)
40
- return generator_entity
41
-
42
-
43
- class GetCharacterReplacementUseCase:
44
- def __init__(self, generator_repository: GeneratorRepository):
45
- self.generator_repository: GeneratorRepository = generator_repository
46
-
47
- def __call__(self, character: str) -> str:
48
- return self.generator_repository.get().get_character_replacement(character)
49
-
50
-
51
- class SetCharacterReplacementUseCase:
52
- def __init__(self, generator_repository: GeneratorRepository):
53
- self.generator_repository: GeneratorRepository = generator_repository
54
-
55
- def __call__(self, character: str, replacement: str) -> Generator:
56
- generator_entity: Generator = self.generator_repository.get()
57
- generator_entity.replace_character(character, replacement)
58
- self.generator_repository.update(generator_entity)
59
- return generator_entity
60
-
61
-
62
- class ResetCharacterReplacementUseCase:
63
- def __init__(self, generator_repository: GeneratorRepository,):
64
- self.generator_repository: GeneratorRepository = generator_repository
65
-
66
- def __call__(self, character: str) -> Generator:
67
- generator_entity: Generator = self.generator_repository.get()
68
- generator_entity.reset_character(character)
69
- self.generator_repository.update(generator_entity)
70
- return generator_entity
@@ -1,73 +0,0 @@
1
- from passphera_core.entities import Password, Generator
2
- from passphera_core.exceptions import DuplicatePasswordException
3
- from passphera_core.interfaces import VaultRepository, GeneratorRepository
4
- from passphera_core.utilities import generate_password, get_password
5
-
6
-
7
- class GeneratePasswordUseCase:
8
- def __init__(
9
- self,
10
- password_repository: VaultRepository,
11
- generator_repository: GeneratorRepository,
12
- ):
13
- self.password_repository: VaultRepository = password_repository
14
- self.generator_repository: GeneratorRepository = generator_repository
15
-
16
- def __call__(self, context: str, text: str) -> Password:
17
- password_entity: Password = self.password_repository.get(context)
18
- if password_entity:
19
- raise DuplicatePasswordException(password_entity)
20
- password_entity: Password = Password(context=context, text=text)
21
- generator_entity: Generator = self.generator_repository.get()
22
- generate_password(password_entity, generator_entity)
23
- self.password_repository.save(password_entity)
24
- return password_entity
25
-
26
-
27
- class GetPasswordUseCase:
28
- def __init__(self, password_repository: VaultRepository):
29
- self.password_repository: VaultRepository = password_repository
30
-
31
- def __call__(self, context: str) -> Password:
32
- return get_password(self.password_repository, context)
33
-
34
-
35
- class UpdatePasswordUseCase:
36
- def __init__(
37
- self,
38
- password_repository: VaultRepository,
39
- generator_repository: GeneratorRepository,
40
- ):
41
- self.password_repository: VaultRepository = password_repository
42
- self.generator_repository: GeneratorRepository = generator_repository
43
-
44
- def __call__(self, context: str, text: str) -> Password:
45
- password_entity: Password = get_password(self.password_repository, context)
46
- generator_entity: Generator = self.generator_repository.get()
47
- generate_password(password_entity, generator_entity)
48
- self.password_repository.update(password_entity)
49
- return password_entity
50
-
51
-
52
- class DeletePasswordUseCase:
53
- def __init__(self, password_repository: VaultRepository):
54
- self.password_repository: VaultRepository = password_repository
55
-
56
- def __call__(self, context: str) -> None:
57
- self.password_repository.delete(get_password(self.password_repository, context))
58
-
59
-
60
- class ListPasswordsUseCase:
61
- def __init__(self, password_repository: VaultRepository):
62
- self.password_repository: VaultRepository = password_repository
63
-
64
- def __call__(self) -> list[Password]:
65
- return self.password_repository.list()
66
-
67
-
68
- class FlushPasswordsUseCase:
69
- def __init__(self, password_repository: VaultRepository):
70
- self.password_repository: VaultRepository = password_repository
71
-
72
- def __call__(self) -> None:
73
- self.password_repository.flush()
@@ -1,43 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from passphera_core.entities import Password, Generator
4
-
5
-
6
- class VaultRepository(ABC):
7
- @abstractmethod
8
- def save(self, password: Password) -> None:
9
- pass
10
-
11
- @abstractmethod
12
- def get(self, context: str) -> Password:
13
- pass
14
-
15
- @abstractmethod
16
- def update(self, password: Password) -> None:
17
- pass
18
-
19
- @abstractmethod
20
- def delete(self, password: Password) -> None:
21
- pass
22
-
23
- @abstractmethod
24
- def list(self) -> list[Password]:
25
- pass
26
-
27
- @abstractmethod
28
- def flush(self) -> None:
29
- pass
30
-
31
-
32
- class GeneratorRepository(ABC):
33
- @abstractmethod
34
- def save(self, generator: Generator) -> None:
35
- pass
36
-
37
- @abstractmethod
38
- def get(self) -> Generator:
39
- pass
40
-
41
- @abstractmethod
42
- def update(self, generator: Generator) -> None:
43
- pass
@@ -1,12 +0,0 @@
1
- passphera_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- passphera_core/entities.py,sha256=fUCjf7D9FCKEB2tMoNUr9RJLn_50qcggTDOrBhGEHVI,8892
3
- passphera_core/exceptions.py,sha256=Tqb-FKJ1_JfX5gSC8Wc0CP84AhwouvnP2gkg83xAlUY,786
4
- passphera_core/interfaces.py,sha256=DpHFh_vnamORf69P1dwLrZ8AYZPcYIol0Lq_VKRBkXc,855
5
- passphera_core/utilities.py,sha256=n7cAVox-yGd60595RjLvrGKSGqFQRm279YqKS3-R1X0,748
6
- passphera_core/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- passphera_core/application/generator.py,sha256=hBSk6vktJ2KPxzKSRnH_WVnCvg7vOHVzN-TQ5GdgXEQ,2769
8
- passphera_core/application/password.py,sha256=9wsSz3uMWEZCgfnX5V7WYiWp2OqjSmsn6OSWK4HIIfo,2876
9
- passphera_core-0.30.1.dist-info/METADATA,sha256=yQf5woOolmMgufQYpwSqlJN_KcdMthXzM1nPaF93vfI,868
10
- passphera_core-0.30.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- passphera_core-0.30.1.dist-info/top_level.txt,sha256=aDUX2iWGOyfzyf6XakLWTbgeWqNrypMHO074Qratyds,15
12
- passphera_core-0.30.1.dist-info/RECORD,,