Pārlūkot izejas kodu

Start workin on secret app part.

Cixo Develop 7 mēneši atpakaļ
vecāks
revīzija
20b0e674a5

+ 2 - 0
assets/__init__.py

@@ -3,6 +3,7 @@ from .user import user
 from .user import user_builder
 from .user_loader import user_loader
 from .apikey import apikey
+from .apikey import bad_apikey_exception
 from .secret import secret
 from .secret import secret_builder
 from .secret_loader import secret_loader
@@ -15,6 +16,7 @@ from .code_key import code_key_manager
 from .settings import settings
 from .application_part import application_part
 from .application_user import application_user
+from .application_secret import application_secret
 from .validators import validator
 from .validators import validator_dumper
 from .validators import validator_result

+ 6 - 0
assets/apikey.py

@@ -1,5 +1,11 @@
 import os
 
+class bad_apikey_exception(Exception):
+    """ This error would be raised, when bad apikey occur. """
+    
+    def __init__(self) -> None:
+        super("Provided ApiKey not exists in database.")
+
 class apikey:
     """
     This create new random API key.

+ 117 - 0
assets/application_secret.py

@@ -0,0 +1,117 @@
+import typing
+
+from .user import user
+from .user import user_builder
+from .user_loader import user_loader
+from .secret import secret
+from .secret import secret_builder
+from .secret_coder import secret_coder
+from .secret_loader import secret_loader
+from .application_part import application_part
+from .apikey import bad_apikey_exception
+from .validators import name_validator
+from .validators import domain_validator
+
+class secret_response:
+    def __init__(self, target: secret | secret_builder) -> None:
+        if isinstance(target, secret):
+            self.__target = target
+            return
+
+        self.__target = target.result
+
+    @property
+    def response(self) -> dict:
+        return {
+            "name": self.target.name,
+            "domain": self.target.domain,
+            "coded" : self.target.coded
+        }
+
+    @property
+    def target(self) -> secret:
+        return self.__target
+
+    @property
+    def builder(self) -> secret_builder:
+        return secret_builder(self.__target)
+
+class secret_collection_response:
+    def __init__(self, target: typing.Iterable[secret] | None = None) -> None:
+        self.__collection = []
+
+        if target is None:
+            return
+
+        if type(target) is list:
+            self.__collection = target.copy()
+            return
+
+        for count in target:
+            self.__collection.append(count)
+
+    @property
+    def response(self) -> dict:
+        return [ count.response for count in self.collection ]
+
+    @property
+    def collection(self) -> list:
+        return self.__collection.copy()
+
+    def append(self, target: secret | secret_builder) -> object:
+        if isinstance(target, secret_builder):
+            target = target.result
+
+        self.__collection.append(target)
+        return self
+
+class application_secret(application_part):
+    def get(self, apikey: str, name: str) -> dict:
+        with self.__secret_database(apikey) as loader:
+            target = loader.load_by_name(name)
+
+            if target is None:
+                return self.__not_found_response()
+
+            return secret_response(target).response
+
+    def create(self, apikey: str, name: str, domain: str, coded: str) -> dict:
+        validation = self._validation("name", name_validator(name))
+        validation = validation or self._validation(
+            "domain", 
+            domain_validator(domain)
+        )
+
+        if validation is not None:
+            return validation
+
+        if not secret_coder.validate(coded):
+            return self._fail_response(cause = "Invalid coded secret")
+
+        with self.__secret_database(apikey) as loader:
+            builder = secret_builder()
+            builder.name = name
+            builder.domain = domain
+            builder.coded = coded
+            builder.owner = loader.owner
+
+            loader.append(builder.result)     
+
+            return self._success_response()
+
+    @property
+    def __not_found_response(self) -> dict:
+        return self._fail_response(cause = "Secret not found.")
+
+    @property
+    def __user_database(self) -> user_loader:
+        return user_loader(self._connector)
+
+    def __secret_database(self, apikey: str) -> secret_loader:
+        with self.__user_database as loader:
+            target = loader.get_by_apikey(apikey)
+
+            if target is None:
+                raise bad_apikey_exception()
+
+            return secret_loader(self._connector, target)

+ 6 - 0
assets/code_key.py

@@ -24,6 +24,12 @@ class code_key_manager:
 
         return self.__key
 
+    @property
+    def coder(self) -> secret_coder:
+        """ This return secret coder manager. """
+        
+        return secret_coder(self.decrypted)
+
     @property
     def decrypted(self) -> str:
         """ This return decrypted key. """

+ 18 - 0
assets/user.py

@@ -6,6 +6,7 @@ from .builder import builder
 from .code_key import code_key
 from .code_key import code_key_manager
 from .secret_coder import bad_password
+from .secret_coder import secret_coder
 
 class user(sqlmodel.SQLModel, table = True):
     """
@@ -38,6 +39,23 @@ class user(sqlmodel.SQLModel, table = True):
 
         return code_key(password = password, crypted_key = self.code_key)
 
+    def coder(self, password: str) -> secret_coder:
+        """
+        This return secret coder to work with secrets. It require password
+        to decrypt master key, and it could raise bad_password exception when 
+        password is not correct.
+
+        Parameters:
+            password (str): Password used to decrypt crypto key
+
+        Returns:
+            (secret_coder): Crypto secrets manager
+        """
+
+        key = self.key(password).decrypted
+
+        return secret_coder(key)
+
     @property
     def in_database(self) -> bool:
         """ True when user exists in database. """

+ 37 - 1
assets/validators.py

@@ -1,5 +1,6 @@
 import re
 import enum
+import urllib.parse
 
 class validator_result(enum.IntEnum):
     """
@@ -251,7 +252,7 @@ class nick_validator(validator):
 
     @property
     def max_lenght(self) -> int:
-        return 256
+        return 64
 
     @property
     def must_contain(self) -> list:
@@ -284,3 +285,38 @@ class nick_validator(validator):
 
         return validator_result.valid
 
+class name_validator(nick_validator):
+    """
+    This is validator for secrets names.
+    """
+
+    pass
+
+class domain_validator(validator):
+    """
+    This is validator for domain names. 
+    """
+
+    @property
+    def max_lenght(self) -> int:
+        return 253
+
+    @property
+    def min_lenght(self) -> int:
+        return 1
+
+    @property
+    def invalid_chars(self) -> list:
+        return []
+
+    @property
+    def must_contain(self) -> list:
+        return []
+    
+    def _end_final(self, content: str) -> int:
+        try:
+            urllib.parse.urlparse(content)
+        except ValueError:
+            return validator_result.contain_invalid_char
+
+        return validator_result.valid

+ 48 - 0
tests/008-application_secret.py

@@ -0,0 +1,48 @@
+import pathlib
+
+current = pathlib.Path(__file__).parent
+root = current.parent
+
+import sys
+sys.path.append(str(root))
+
+import assets
+
+import assets
+import sqlmodel
+
+def drop_db() -> None:
+    db = pathlib.Path("./008-application_secret.db")
+
+    if db.is_file():
+        db.unlink()
+
+drop_db()
+
+connection = sqlmodel.create_engine("sqlite:///008-application_secret.db")
+user_app = assets.application_user(connection)
+secret_app = assets.application_secret(connection)
+
+sqlmodel.SQLModel.metadata.create_all(connection)
+
+user_app.register("test", "password")
+apikey = user_app.login("test", "password")["apikey"]
+
+print("Create test user, apikey: " + apikey)
+
+code_key = user_app.get(apikey)["code_key"]
+coder = assets.code_key_manager("password", code_key).coder
+coded = coder.encrypt("sample")
+
+secret_app.create(apikey, "sample", "https://xyz.com", coded)
+
+print()
+print("Created secret.")
+print("Result:")
+print(secret_app.get(apikey, "sample"))
+
+print()
+print("Decoding:")
+print(coder.decrypt(secret_app.get(apikey, "sample")["coded"]))
+
+drop_db()