Cixo Develop 6 miesięcy temu
rodzic
commit
10e15ca4f9

+ 4 - 0
source/cx_apikey/__init__.py

@@ -0,0 +1,4 @@
+from .apikey import apikey
+from .apikey_exceptions import apikey_syntax_error
+from .apikey_validator import apikey_validator
+from .apikey_factory import apikey_factory

+ 44 - 0
source/cx_apikey/apikey.py

@@ -0,0 +1,44 @@
+class apikey:
+    def __init__(
+        self, 
+        content: str, 
+        size: int, 
+        prefix: str, 
+        prefix_separator: str
+    ) -> None:
+        self.__content = content
+        self.__size = size
+        self.__prefix = prefix
+        self.__prefix_separator = prefix_separator
+
+    @property
+    def content(self) -> str:
+        return self.__content
+    
+    @property
+    def size(self) -> int:
+        return self.__size
+
+    @property
+    def prefix(self) -> str:
+        return self.__prefix
+
+    @property
+    def prefix_separator(self) -> str:
+        return self.__prefix_separator
+
+    @property
+    def key(self) -> str:
+        return self.__content
+
+    def compare(self, target: str | object) -> bool:
+        return self.__content == str(target)
+
+    def __eq__(self, target: str | object) -> bool:
+        return self.compare(target)
+
+    def __str__(self) -> str:
+        return self.__content
+
+    def __repr__(self) -> str:
+        return "API key: \"" + self.__content + "\""

+ 3 - 0
source/cx_apikey/apikey_exceptions.py

@@ -0,0 +1,3 @@
+class apikey_syntax_error(Exception):
+    def __init__(self) -> None:
+        super().__init__("That is not valid API key.")

+ 90 - 0
source/cx_apikey/apikey_factory.py

@@ -0,0 +1,90 @@
+import os
+import functools 
+
+from .apikey import apikey
+from .apikey_validator import apikey_validator
+from .apikey_exceptions import apikey_syntax_error
+
+class apikey_factory:
+    def __init__(self, prefix: str = "key") -> None:
+        self.__size = 256
+        self.__prefix = prefix
+
+    def set_size(self, size: int) -> object:
+        self.__size = size
+        return self
+
+    def set_prefix(self, prefix: str) -> object:
+        self.__prefix = prefix
+        return self
+
+    @property
+    def prefix_separator(self) -> str:  
+        return "_"
+
+    @property
+    def prefix(self) -> str:
+        return self.__prefix
+
+    @property
+    def size(self) -> int:
+        return self.__size
+
+    @functools.lru_cache
+    def __get_random_token_size(self, token_size: int) -> int:
+        random_size = token_size / 2 + token_size % 2
+        random_size = int(random_size)
+
+        return random_size
+
+    @functools.lru_cache
+    def __get_token_size(self, full_size: int, prefix_size: int) -> int:
+        return full_size - prefix_size - len(self.prefix_separator)
+
+    def __get_random(self) -> str:
+        token_size = self.__get_token_size(self.size, len(self.prefix))
+        random_size = self.__get_random_token_size(token_size)
+
+        return os.urandom(random_size).hex()[0:token_size]
+    
+    def __get_new_token(self) -> str:
+        return self.prefix + self.prefix_separator + self.__get_random()
+
+    def generate(self) -> apikey:
+        return apikey(
+            self.__get_new_token(),
+            self.size,
+            self.prefix,
+            self.prefix_separator
+        )
+    
+    def load(self, token: str) -> apikey:
+        if not self.get_validator().validate(token):
+            raise apikey_syntax_error()
+
+        return apikey(
+            token,
+            self.size,
+            self.prefix,
+            self.prefix_separator
+        )
+
+    def get_validator(self) -> apikey_validator:
+        return self.__get_validator_cache(
+            self.size,
+            self.prefix,
+            self.prefix_separator
+        )
+
+    @functools.lru_cache
+    def __get_validator_cache(
+        self, 
+        size: int, 
+        prefix: str, 
+        prefix_separator: str
+    ) -> apikey_validator:
+        return apikey_validator(
+            size,
+            prefix,
+            prefix_separator
+        )

+ 50 - 0
source/cx_apikey/apikey_validator.py

@@ -0,0 +1,50 @@
+from .apikey import apikey
+
+class apikey_validator:
+    def __init__(
+        self,
+        size: int,
+        prefix: str,
+        prefix_separator: str
+    ) -> None:
+        self.__size = size
+        self.__prefix = prefix
+        self.__prefix_separator = prefix_separator
+
+    @property
+    def size(self) -> int:
+        return self.__size
+
+    @property
+    def prefix(self) -> str:
+        return self.__prefix
+
+    @property
+    def prefix_separator(self) -> str:
+        return self.__prefix_separator
+
+    def validate(self, content: str) -> bool:
+        parts = content.split(self.prefix_separator)
+
+        if len(parts) != 2:
+            return False
+
+        prefix = parts[0]
+        token = parts[1]
+
+        if prefix != self.prefix:
+            return False
+
+        if len(content) != self.size:
+            return False
+
+        if len(token) % 2 != 0:
+            token = token + "0"
+
+        try:
+            bytes_token = bytes.fromhex(token)
+        except:
+            return False
+
+        return True
+

+ 29 - 0
tests/000-default.py

@@ -0,0 +1,29 @@
+import pathlib
+import sys
+
+current = pathlib.Path(__file__)
+test_dir = current.parent
+project_dir = test_dir.parent
+source_dir = project_dir / pathlib.Path("source")
+
+sys.path.append(str(source_dir))
+
+import cx_apikey as apikey
+
+test_count = 0
+
+def test(tag: str, result: bool) -> None:
+    global test_count
+    test_count += 1
+
+    print("Running test " + tag + " (" + str(test_count) + "):", end = " ")
+
+    if result:
+        print("Valid")
+        return
+
+    print("FAIL!!!")
+    exit(-1)
+
+
+

+ 27 - 0
tests/001-apikey_factory.py

@@ -0,0 +1,27 @@
+import pathlib
+import sys
+
+current = pathlib.Path(__file__)
+test_dir = current.parent
+project_dir = test_dir.parent
+source_dir = project_dir / pathlib.Path("source")
+
+sys.path.append(str(source_dir))
+
+import cx_apikey as apikey
+
+factory = apikey \
+    .apikey_factory() \
+    .set_size(64) \
+    .set_prefix("test")
+
+key = factory.generate()
+print(repr(key))
+
+factory_2 = apikey \
+    .apikey_factory("SeCond") \
+    .set_prefix("SEcond") \
+    .set_size(128)
+
+print(repr(factory_2.generate()))
+

+ 40 - 0
tests/002-apikey.py

@@ -0,0 +1,40 @@
+import pathlib
+import sys
+
+current = pathlib.Path(__file__)
+test_dir = current.parent
+project_dir = test_dir.parent
+source_dir = project_dir / pathlib.Path("source")
+
+sys.path.append(str(source_dir))
+
+import cx_apikey as apikey
+
+test_count = 0
+
+def test(tag: str, result: bool) -> None:
+    global test_count
+    test_count += 1
+
+    print("Running test " + tag + " (" + str(test_count) + "):", end = " ")
+
+    if result:
+        print("Valid")
+        return
+
+    print("FAIL!!!")
+    exit(-1)
+
+
+factory = apikey.apikey_factory("test").set_size(64)
+sample_1 = factory.generate()
+sample_2 = factory.generate()
+
+sample_1_string = sample_1.content
+
+test("compare different", sample_1 != sample_2)
+test("compare different string", sample_1 != str(sample_2))
+test("compare self", sample_1 == sample_1)
+test("compare self as content", sample_1 == sample_1_string)
+test("compare self as string", sample_1 == str(sample_1))
+

+ 34 - 0
tests/003-validator.py

@@ -0,0 +1,34 @@
+import pathlib
+import sys
+
+current = pathlib.Path(__file__)
+test_dir = current.parent
+project_dir = test_dir.parent
+source_dir = project_dir / pathlib.Path("source")
+
+sys.path.append(str(source_dir))
+
+import cx_apikey as apikey
+
+test_count = 0
+
+def test(tag: str, result: bool) -> None:
+    global test_count
+    test_count += 1
+
+    print("Running test " + tag + " (" + str(test_count) + "):", end = " ")
+
+    if result:
+        print("Valid")
+        return
+
+    print("FAIL!!!")
+    exit(-1)
+
+factory = apikey.apikey_factory("test").set_size(10)
+sample = factory.generate()
+sample_manual = "test_1234567890"
+bad_sample = "no_1234567890"
+other_bad = "test_uuuuuuuuuu"
+
+factory.get_validator()