Skip to content

Commit

Permalink
Merge 038e995 into c1f211d
Browse files Browse the repository at this point in the history
  • Loading branch information
barseghyanartur committed Aug 17, 2021
2 parents c1f211d + 038e995 commit f3f50b0
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 27 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.

1.9
-----
2021-08-18

- Add `value_dumper` to most of the functions/methods related to signature
generation/validation. It's aimed to make signatures generated in languages
better compatible with `ska`.
- Add `quoter` to most of the functions/methods related to signature
generation/validation. It's aimed to make signatures generated in languages
better compatible with `ska`.

1.8.2
-----
2021-06-18
Expand Down
11 changes: 11 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.

1.9
-----
2021-08-18

- Add `value_dumper` to most of the functions/methods related to signature
generation/validation. It's aimed to make signatures generated in languages
better compatible with `ska`.
- Add `quoter` to most of the functions/methods related to signature
generation/validation. It's aimed to make signatures generated in languages
better compatible with `ska`.

1.8.2
-----
2021-06-18
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
except:
readme = ""

version = "1.8.2"
version = "1.9"

exec_dirs = [
"src/ska/bin/",
Expand Down
2 changes: 1 addition & 1 deletion src/ska/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .utils import RequestHelper

__title__ = "ska"
__version__ = "1.8.2"
__version__ = "1.9"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2013-2021 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
Expand Down
35 changes: 32 additions & 3 deletions src/ska/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from base64 import b64encode
from datetime import datetime, timedelta
import time
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional, Union, Callable

from . import error_codes
from .defaults import SIGNATURE_LIFETIME, TIMESTAMP_FORMAT
Expand Down Expand Up @@ -129,6 +129,8 @@ def validate_signature(
valid_until: Union[str, float],
extra: Optional[Dict[str, Union[bytes, str, float, int]]] = None,
return_object: bool = False,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> Union[SignatureValidationResult, bool]:
"""Validates the signature.
Expand All @@ -139,6 +141,8 @@ def validate_signature(
:param extra: Extra arguments to be validated.
:param return_object: If set to True, an instance of
``SignatureValidationResult`` is returned.
:param value_dumper:
:param quoter:
:return:
:example:
Expand All @@ -162,6 +166,8 @@ def validate_signature(
secret_key=secret_key,
valid_until=valid_until,
extra=extra,
value_dumper=value_dumper,
quoter=quoter,
)

if not return_object:
Expand Down Expand Up @@ -205,6 +211,8 @@ def get_base(
auth_user: str,
timestamp: Union[float, str],
extra: Optional[Dict[str, Union[bytes, str, float, int]]] = None,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> bytes:
"""Get base string.
Expand All @@ -214,14 +222,20 @@ def get_base(
:param auth_user:
:param timestamp:
:param extra:
:param value_dumper:
:param quoter:
"""
if not extra:
extra = {}

_base = [str(timestamp), auth_user]

if extra:
urlencoded_extra = sorted_urlencode(extra)
urlencoded_extra = sorted_urlencode(
extra,
value_dumper=value_dumper,
quoter=quoter,
)
if urlencoded_extra:
_base.append(urlencoded_extra)

Expand All @@ -243,6 +257,8 @@ def make_hash(
secret_key: str,
valid_until: Union[str, float] = None,
extra: Optional[Dict[str, Union[bytes, str, float, int]]] = None,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> bytes:
"""Make hash.
Expand All @@ -252,6 +268,8 @@ def make_hash(
:param secret_key:
:param valid_until: Unix timestamp, valid until.
:param extra: Additional variables to be added.
:param value_dumper:
:param quoter:
:return:
"""
raise NotImplementedError("You should implement this method!")
Expand All @@ -264,6 +282,8 @@ def generate_signature(
valid_until: Optional[Union[float, str]] = None,
lifetime: int = SIGNATURE_LIFETIME,
extra: Optional[Dict[str, Union[bytes, str, float, int]]] = None,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> "AbstractSignature":
"""Generates the signature.
Expand All @@ -275,6 +295,8 @@ def generate_signature(
:param valid_until: Unix timestamp, valid until.
:param lifetime: Lifetime of the signature in seconds.
:param extra: Additional variables to be added.
:param value_dumper:
:param quoter:
:return:
:example:
Expand All @@ -295,7 +317,14 @@ def generate_signature(
return None # Something went wrong

signature = b64encode(
cls.make_hash(auth_user, secret_key, valid_until, extra)
cls.make_hash(
auth_user,
secret_key,
valid_until,
extra,
value_dumper=value_dumper,
quoter=quoter,
)
)

return cls(
Expand Down
66 changes: 61 additions & 5 deletions src/ska/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime, timedelta
from importlib import import_module
import json
import time
from typing import Callable, Dict, List, Tuple, Union, Optional
from urllib.parse import quote
Expand All @@ -10,12 +11,16 @@
__copyright__ = "2013-2021 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = (
"get_callback_func",
"default_value_dumper",
"dict_keys",
"dict_to_ordered_list",
"sorted_urlencode",
"extract_signed_data",
"get_callback_func",
"javascript_value_dumper",
"make_valid_until",
"sorted_urlencode",
"default_quoter",
"javascript_quoter",
)


Expand Down Expand Up @@ -87,20 +92,71 @@ def dict_to_ordered_list(
return items


def dict_to_ordered_dict(obj):
if isinstance(obj, dict):
obj = dict(sorted(obj.items()))
for k, v in obj.items():
if isinstance(v, dict) or isinstance(v, list):
obj[k] = dict_to_ordered_dict(v)

if isinstance(obj, list):
for i, v in enumerate(obj):
if isinstance(v, dict) or isinstance(v, list):
obj[i] = dict_to_ordered_dict(v)
# obj = sorted(obj, key=lambda x: json.dumps(x))

return obj


def default_value_dumper(value):
return value


def javascript_value_dumper(value):
if isinstance(value, (int, float, str)):
return value
# elif isinstance(value, UUID):
# return str(value)
else:
return json.dumps(value, separators=(",", ":"))


def default_quoter(value):
return quote(value)


def javascript_quoter(value):
return quote(value, safe="~()*!.'")


def sorted_urlencode(
data: Dict[str, Union[bytes, str, float, int]], quoted: bool = True
data: Dict[str, Union[bytes, str, float, int]],
quoted: bool = True,
value_dumper: Optional[Callable] = default_value_dumper,
quoter: Optional[Callable] = default_quoter,
) -> str:
"""Similar to built-in ``urlencode``, but always puts data in a sorted
constant way that stays the same between various python versions.
:param data:
:param quoted:
:param value_dumper:
:param quoter:
:return:
"""
_sorted = [f"{k}={v}" for k, v in dict_to_ordered_list(data)]
if not value_dumper:
value_dumper = default_value_dumper

if not quoter:
quoter = default_quoter

# _sorted = [f"{k}={value_dumper(v)}" for k, v in dict_to_ordered_list(data)]
_sorted = [
f"{k}={value_dumper(v)}" for k, v in dict_to_ordered_dict(data).items()
]
res = "&".join(_sorted)
if quoted:
res = quote(res)
res = quoter(res)
return res


Expand Down
26 changes: 24 additions & 2 deletions src/ska/shortcuts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Optional, Type, Union
from typing import Dict, Optional, Type, Union, Callable

from .base import SignatureValidationResult, AbstractSignature
from .defaults import (
Expand Down Expand Up @@ -43,6 +43,7 @@ def sign_url(
extra: Optional[Dict[str, Union[bytes, str, float, int]]] = None,
extra_param: str = DEFAULT_EXTRA_PARAM,
signature_cls: Type[AbstractSignature] = Signature,
value_dumper: Optional[Callable] = None,
) -> str:
"""Sign the URL.
Expand All @@ -64,6 +65,7 @@ def sign_url(
:param extra_param: Name of the GET param name which would hold the
``extra_keys`` value.
:param signature_cls:
:param value_dumper:
:return:
:example:
Expand Down Expand Up @@ -101,6 +103,7 @@ def sign_url(
valid_until=valid_until,
lifetime=lifetime,
extra=extra,
value_dumper=value_dumper,
)

request_helper = RequestHelper(
Expand Down Expand Up @@ -131,6 +134,8 @@ def signature_to_dict(
extra: Optional[Dict[str, Union[str, int]]] = None,
extra_param: str = DEFAULT_EXTRA_PARAM,
signature_cls: Type[AbstractSignature] = Signature,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> Dict[str, Union[bytes, str, float, int]]:
"""Return a dictionary containing the signature data params.
Expand All @@ -149,6 +154,8 @@ def signature_to_dict(
:param extra_param: Name of the (for example POST) param name which
would hold the ``extra`` keys value.
:param signature_cls:
:param value_dumper:
:param quoter:
:return:
:example:
Expand Down Expand Up @@ -184,6 +191,8 @@ def signature_to_dict(
valid_until=valid_until,
lifetime=lifetime,
extra=extra,
value_dumper=value_dumper,
quoter=quoter,
)

request_helper = RequestHelper(
Expand All @@ -207,6 +216,8 @@ def validate_signed_request_data(
valid_until_param: str = DEFAULT_VALID_UNTIL_PARAM,
extra_param: str = DEFAULT_EXTRA_PARAM,
signature_cls: Type[AbstractSignature] = Signature,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> SignatureValidationResult:
"""Validate the signed request data.
Expand All @@ -222,6 +233,8 @@ def validate_signed_request_data(
:param extra_param: Name of the (foe example GET or POST) param
name which holds the ``extra`` keys value.
:param signature_cls:
:param value_dumper:
:param quoter:
:return: A ``ska.SignatureValidationResult``
object with the following properties:
- `result` (bool): True if data is valid. False otherwise.
Expand All @@ -237,7 +250,10 @@ def validate_signed_request_data(
)

validation_result = request_helper.validate_request_data(
data=data, secret_key=secret_key
data=data,
secret_key=secret_key,
value_dumper=value_dumper,
quoter=quoter,
)

return validation_result
Expand All @@ -253,6 +269,8 @@ def extract_signed_request_data(
validate: bool = False,
fail_silently: bool = False,
signature_cls: Type[AbstractSignature] = Signature,
value_dumper: Optional[Callable] = None,
quoter: Optional[Callable] = None,
) -> Dict[str, Union[bytes, str, float, int]]:
"""Validate the signed request data.
Expand All @@ -271,6 +289,8 @@ def extract_signed_request_data(
returning the result.
:param fail_silently: If set to True, exceptions are omitted.
:param signature_cls:
:param value_dumper:
:param quoter:
:return: Dictionary with signed request data.
"""
request_helper = RequestHelper(
Expand All @@ -286,4 +306,6 @@ def extract_signed_request_data(
secret_key=secret_key,
validate=validate,
fail_silently=fail_silently,
value_dumper=value_dumper,
quoter=quoter,
)

0 comments on commit f3f50b0

Please sign in to comment.