passphera-core 0.30.2__py3-none-any.whl → 0.32.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,23 +1,18 @@
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,
18
13
  'hill': HillCipherAlgorithm,
19
14
  }
20
- _default_properties: dict[str, str] = {
15
+ _default_properties: dict[str, object] = {
21
16
  "shift": 3,
22
17
  "multiplier": 3,
23
18
  "key": "hill",
@@ -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"])
@@ -133,6 +73,17 @@ class Generator:
133
73
  "characters_replacements": self.characters_replacements,
134
74
  }
135
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
136
87
  def set_property(self, prop: str, value: str):
137
88
  """
138
89
  Update a generator property with a new value
@@ -141,28 +92,23 @@ class Generator:
141
92
  :raises ValueError: If the property name is not one of the allowed properties
142
93
  :return: None
143
94
  """
144
- if prop not in {"id", "created_at", "updated_at", "shift", "multiplier", "key", "algorithm", "prefix", "postfix"}:
145
- raise InvalidPropertyNameException(prop)
146
95
  if prop in ["shift", "multiplier"]:
147
96
  value = int(value)
148
97
  setattr(self, prop, value)
149
98
  if prop == "algorithm":
150
99
  self.get_algorithm()
151
- self.updated_at = datetime.now(timezone.utc)
152
-
153
- def reset_property(self, prop: str):
100
+
101
+ @check_property_name
102
+ def reset_property(self, prop: str) -> None:
154
103
  """
155
104
  Reset a generator property to its default value
156
105
  :param prop: The property name to reset, it must be one of: shift, multiplier, key, algorithm, prefix, postfix
157
106
  :raises ValueError: If the property name is not one of the allowed properties
158
107
  :return: None
159
108
  """
160
- if prop not in {"id", "created_at", "updated_at", "shift", "multiplier", "key", "algorithm", "prefix", "postfix"}:
161
- raise InvalidPropertyNameException(prop)
162
109
  setattr(self, prop, _default_properties[prop])
163
110
  if prop == "algorithm":
164
111
  self.get_algorithm()
165
- self.updated_at = datetime.now(timezone.utc)
166
112
 
167
113
  def get_character_replacement(self, character: str) -> str:
168
114
  """
@@ -172,7 +118,7 @@ class Generator:
172
118
  """
173
119
  return self.characters_replacements.get(character, character)
174
120
 
175
- def replace_character(self, character: str, replacement: str) -> None:
121
+ def set_character_replacement(self, character: str, replacement: str) -> None:
176
122
  """
177
123
  Replace a character with another character or set of characters
178
124
  Eg: pg.replace_character('a', '@1')
@@ -181,16 +127,14 @@ class Generator:
181
127
  :return: None
182
128
  """
183
129
  self.characters_replacements[character[0]] = replacement
184
- self.updated_at = datetime.now(timezone.utc)
185
130
 
186
- def reset_character(self, character: str) -> None:
131
+ def reset_character_replacement(self, character: str) -> None:
187
132
  """
188
133
  Reset a character to its original value (remove its replacement from characters_replacements)
189
134
  :param character: The character to be reset to its original value
190
135
  :return: None
191
136
  """
192
137
  self.characters_replacements.pop(character, None)
193
- self.updated_at = datetime.now(timezone.utc)
194
138
 
195
139
  def generate_password(self, text: str) -> str:
196
140
  """
@@ -201,25 +145,25 @@ class Generator:
201
145
  main_algorithm: BaseCipherAlgorithm = self.get_algorithm()
202
146
  secondary_algorithm: AffineCipherAlgorithm = AffineCipherAlgorithm(self.shift, self.multiplier)
203
147
  intermediate: str = secondary_algorithm.encrypt(f"{self.prefix}{text}{self.postfix}")
204
- password: str = main_algorithm.encrypt(f"{self.prefix}{intermediate}{self.postfix}")
205
- password = password.translate(str.maketrans(self.characters_replacements))
148
+ password: str = main_algorithm.encrypt(intermediate)
149
+ for char, repl in self.characters_replacements.items():
150
+ password = password.replace(char, repl)
206
151
  return ''.join(c.upper() if c in text else c for c in password)
207
152
 
208
153
  def to_dict(self) -> dict:
209
154
  """Convert the Generator entity to a dictionary."""
210
155
  return {
211
- "id": self.id,
212
- "created_at": self.created_at,
213
- "updated_at": self.updated_at,
214
156
  "shift": self.shift,
215
157
  "multiplier": self.multiplier,
216
158
  "key": self.key,
217
159
  "algorithm": self.algorithm,
218
160
  "prefix": self.prefix,
219
161
  "postfix": self.postfix,
162
+ "character_replacements": self.characters_replacements,
220
163
  }
221
164
 
222
165
  def from_dict(self, data: dict) -> None:
223
166
  """Convert a dictionary to a Generator entity."""
224
167
  for key, value in data.items():
225
- setattr(self, key, value)
168
+ if key in _default_properties or key == "characters_replacements":
169
+ setattr(self, key, value)
@@ -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, *args, **kwargs):
6
+ if prop not in {"shift", "multiplier", "key", "algorithm", "prefix", "postfix", "character_replacements"}:
7
+ raise InvalidPropertyNameException(prop)
8
+ return func(self, prop, *args, **kwargs)
9
+ return wrapper
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: passphera-core
3
- Version: 0.30.2
4
- Summary: The core system of passphera project
3
+ Version: 0.32.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=uGKOMknwmHYb2zx26WS81bVMVeTZCRnP8A4NlL41GHM,6894
3
+ passphera_core/exceptions.py,sha256=nb0iWLaFpFHEUjCa5dQ8UnIn3tZs8gN5dy2URgqJf0g,211
4
+ passphera_core/utilities.py,sha256=2cu6qWu3iseCejStxk4vD35bhQfgvb1WWx0DzXEMp_w,382
5
+ passphera_core-0.32.0.dist-info/METADATA,sha256=LirSsl3UaKonN1mnaHLTef9j-2esOAoSIZbDL6HdBjs,851
6
+ passphera_core-0.32.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ passphera_core-0.32.0.dist-info/top_level.txt,sha256=aDUX2iWGOyfzyf6XakLWTbgeWqNrypMHO074Qratyds,15
8
+ passphera_core-0.32.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=sZYUQC7yGwsQ4ughai_QG_-TN-sVEMCpaBi8SJ0uyts,9321
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.2.dist-info/METADATA,sha256=5-jCeM8j_Yx0Q0EaWtc_knE22pK78rv7o9xIRc5Szc4,868
10
- passphera_core-0.30.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- passphera_core-0.30.2.dist-info/top_level.txt,sha256=aDUX2iWGOyfzyf6XakLWTbgeWqNrypMHO074Qratyds,15
12
- passphera_core-0.30.2.dist-info/RECORD,,