passphera-core 0.30.2__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"])
@@ -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,24 +92,21 @@ 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
100
  self.updated_at = datetime.now(timezone.utc)
152
-
153
- def reset_property(self, prop: str):
101
+
102
+ @check_property_name
103
+ def reset_property(self, prop: str) -> None:
154
104
  """
155
105
  Reset a generator property to its default value
156
106
  :param prop: The property name to reset, it must be one of: shift, multiplier, key, algorithm, prefix, postfix
157
107
  :raises ValueError: If the property name is not one of the allowed properties
158
108
  :return: None
159
109
  """
160
- if prop not in {"id", "created_at", "updated_at", "shift", "multiplier", "key", "algorithm", "prefix", "postfix"}:
161
- raise InvalidPropertyNameException(prop)
162
110
  setattr(self, prop, _default_properties[prop])
163
111
  if prop == "algorithm":
164
112
  self.get_algorithm()
@@ -172,7 +120,7 @@ class Generator:
172
120
  """
173
121
  return self.characters_replacements.get(character, character)
174
122
 
175
- def replace_character(self, character: str, replacement: str) -> None:
123
+ def set_character_replacement(self, character: str, replacement: str) -> None:
176
124
  """
177
125
  Replace a character with another character or set of characters
178
126
  Eg: pg.replace_character('a', '@1')
@@ -183,7 +131,7 @@ class Generator:
183
131
  self.characters_replacements[character[0]] = replacement
184
132
  self.updated_at = datetime.now(timezone.utc)
185
133
 
186
- def reset_character(self, character: str) -> None:
134
+ def reset_character_replacement(self, character: str) -> None:
187
135
  """
188
136
  Reset a character to its original value (remove its replacement from characters_replacements)
189
137
  :param character: The character to be reset to its original value
@@ -201,22 +149,20 @@ class Generator:
201
149
  main_algorithm: BaseCipherAlgorithm = self.get_algorithm()
202
150
  secondary_algorithm: AffineCipherAlgorithm = AffineCipherAlgorithm(self.shift, self.multiplier)
203
151
  intermediate: str = secondary_algorithm.encrypt(f"{self.prefix}{text}{self.postfix}")
204
- password: str = main_algorithm.encrypt(f"{self.prefix}{intermediate}{self.postfix}")
152
+ password: str = main_algorithm.encrypt(intermediate)
205
153
  password = password.translate(str.maketrans(self.characters_replacements))
206
154
  return ''.join(c.upper() if c in text else c for c in password)
207
155
 
208
156
  def to_dict(self) -> dict:
209
157
  """Convert the Generator entity to a dictionary."""
210
158
  return {
211
- "id": self.id,
212
- "created_at": self.created_at,
213
- "updated_at": self.updated_at,
214
159
  "shift": self.shift,
215
160
  "multiplier": self.multiplier,
216
161
  "key": self.key,
217
162
  "algorithm": self.algorithm,
218
163
  "prefix": self.prefix,
219
164
  "postfix": self.postfix,
165
+ "character_replacements": self.characters_replacements,
220
166
  }
221
167
 
222
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.2
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=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,,