diff --git a/.gitignore b/.gitignore index 7bb58e6..bcf84e4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/ *.lock bk/ site/ +update.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 12a9c7a..0b24b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +v 0.6.0 [17-Dec-2023] +--------------------- + - Add control styles. + v 0.5.0 [07-Dec-2023] --------------------- - Add acctions to controls. diff --git a/VERSION b/VERSION index 8f0916f..a918a2a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0 +0.6.0 diff --git a/docs/en/docs/img/tools_msg_01.png b/docs/en/docs/img/tools_msg_01.png new file mode 100644 index 0000000..764b754 Binary files /dev/null and b/docs/en/docs/img/tools_msg_01.png differ diff --git a/docs/en/docs/img/tools_msg_02.png b/docs/en/docs/img/tools_msg_02.png new file mode 100644 index 0000000..699c501 Binary files /dev/null and b/docs/en/docs/img/tools_msg_02.png differ diff --git a/docs/en/docs/img/tools_msg_03.png b/docs/en/docs/img/tools_msg_03.png new file mode 100644 index 0000000..0addf55 Binary files /dev/null and b/docs/en/docs/img/tools_msg_03.png differ diff --git a/docs/en/docs/img/tools_msg_04.png b/docs/en/docs/img/tools_msg_04.png new file mode 100644 index 0000000..5092179 Binary files /dev/null and b/docs/en/docs/img/tools_msg_04.png differ diff --git a/docs/en/docs/img/tools_msg_05.png b/docs/en/docs/img/tools_msg_05.png new file mode 100644 index 0000000..b1e3489 Binary files /dev/null and b/docs/en/docs/img/tools_msg_05.png differ diff --git a/docs/en/docs/img/tools_msg_06.png b/docs/en/docs/img/tools_msg_06.png new file mode 100644 index 0000000..972ce4c Binary files /dev/null and b/docs/en/docs/img/tools_msg_06.png differ diff --git a/docs/en/docs/tools/datetime.md b/docs/en/docs/tools/datetime.md new file mode 100644 index 0000000..6d66fe4 --- /dev/null +++ b/docs/en/docs/tools/datetime.md @@ -0,0 +1,177 @@ + +!!! tip "Atención" + + La fecha inicial en Calc y en Python son diferentes. + +
+ +### **today** + +Obtener la fecha actual. + +```py + d = app.dates + app.msgbox(d.today) +``` + +
+ +### **now** + +Obtener la fecha y hora actuales. + +```py + d = app.dates + app.msgbox(d.now) +``` + +
+ +### **time** + +Obtener la hora actual. + +```py + d = app.dates + app.msgbox(d.now.time()) +``` + +
+ +### **epoch** + +Obtener el [tiempo Unix][1] + +```py + d = app.dates + app.msgbox(d.epoch) +``` + +
+ +### **date** + +Devolver una fecha + +```py + d = app.dates + + date = d.date(1974, 1, 15) + app.msgbox(date) +``` + +
+ +### **time** + +Devolver una hora + +```py + d = app.dates + + time = d.time(10, 20, 15) + app.msgbox(time) +``` + +
+ +### **datetime** + +Devolver fecha y hora + +```py + d = app.dates + + dt = d.datetime(1974, 1, 15, 10, 11, 12) + app.msgbox(dt) +``` + +
+ +### **str_to_date** + +Convertir una cadena en fecha. Mira este [excelente recurso][2] + +```py + d = app.dates + + cadena = '1974-01-15' + plantilla = '%Y-%m-%d' + fecha = d.str_to_date(cadena, plantilla) + app.msgbox(fecha) + app.msgbox(type(fecha)) +``` + +Para obtener un valor válido para establecer en una celda de Calc. + +```py + d = app.dates + + cadena = '1974-01-15' + plantilla = '%Y-%m-%d' + fecha = d.str_to_date(cadena, plantilla, True) + app.msgbox(fecha) + app.msgbox(type(fecha)) +``` + +
+ +### **calc_to_date** + +Convierte el valor de una celda en una fecha Python, por ejemplo, la fecha inicial configurada en Calc. + +```py + d = app.dates + + valor_en_celda = 0 + fecha = d.calc_to_date(valor_en_celda) + app.msgbox(fecha) + app.msgbox(type(fecha)) +``` + +
+ +### **sleep** + +Pausar la ejecución por X segundos. + +!!! tip inline end "Atención" + + La pausa es bloqueante. + +```py + d = app.dates + + app.sleep(3) + app.msgbox('Fin') +``` + +
+ +### **start** y **end** + +Medir tiempo en segundos. + +```py + d = app.dates + + d.start() + app.sleep(5) + seconds = d.end() + app.msgbox(seconds) +``` + +Regresar timedelta en vez de segundos. + +```py + d = app.dates + + d.start() + app.sleep(5) + td = d.end(False) + app.msgbox(td) +``` + + +[1]: https://es.wikipedia.org/wiki/Tiempo_Unix +[2]: https://strftime.org diff --git a/docs/en/docs/tools/index.md b/docs/en/docs/tools/index.md new file mode 100644 index 0000000..6ed32ea --- /dev/null +++ b/docs/en/docs/tools/index.md @@ -0,0 +1,123 @@ +--- +title: Information +--- + +Remember, import first the library. + +```py +import easymacro as app +``` + +
+ +## About PC + +
+ +### **OS** + +Get Operate System. +```py +app.msgbox(app.OS) +``` + +
+ +### **DESKTOP** + +Get desktop type, only GNU/Linux. +```py +app.msgbox(app.DESKTOP) +``` + +
+ +### **PC** + +Get PC name. +```py +app.msgbox(app.PC) +``` + +
+ +### **USER** + +Get user name. +```py +app.msgbox(app.USER) +``` + +
+ +### **IS_WIN** + +If OS is Windows. +```py +app.msgbox(app.IS_WIN) +``` + +
+ +### **IS_MAC** + +IF OS is MAC +```py +app.msgbox(app.IS_MAC) +``` + +
+ +## About LibreOffice + +### **NAME** + +Application name. +```py +app.msgbox(app.NAME) +``` + +
+ +### **VERSION** + +Version. +```py +app.msgbox(app.VERSION) +``` + +
+ +### **LANG** + +Language +```py +app.msgbox(app.LANG) +``` + +
+ +### **LANGUAGE** + +Language with variant. +```py +app.msgbox(app.LANGUAGE) +``` + +
+ +### **IS_APPIMAGE** + +If LibreOffice use by AppImage. +```py +app.msgbox(app.IS_APPIMAGE) +``` + +
+ +### **IS_FLATPAK** + +If LibreOffice is use by FlatPak. +```py +app.msgbox(app.IS_FLATPAK) +``` diff --git a/docs/en/docs/tools/messages.md b/docs/en/docs/tools/messages.md new file mode 100644 index 0000000..6fd464e --- /dev/null +++ b/docs/en/docs/tools/messages.md @@ -0,0 +1,75 @@ +## Message Box + +### **msgbox** + +Show standard message. +```py + message = 'Fucking World' + title = 'My Macro' + app.msgbox(message, title) +``` + +![msgbox](../img/tools_msg_01.png) + +
+ +### **warning** + +Show message with warning icon. +```py + message = 'Caution, this action is dangerous' + title = 'My Macro' + app.warning(message, title) +``` + +![warning](../img/tools_msg_02.png) + +
+ +### **errorbox** + +Show message with error icon. +```py + message = 'ERROR: contact support' + title = 'My Macro' + app.errorbox(message, title) +``` + +![error](../img/tools_msg_03.png) + +
+ +### **question** + +Ask a question by showing the interrogation icon and displaying the command buttons `Yes` and `No`. The answer is always True if user select `yes` and False otherwise. +```py + message = 'Python is easy?' + title = 'My Macro' + result = app.question(message, title) + app.msgbox(result) +``` + +![question](../img/tools_msg_04.png) + +
+ +### **inputbox** + +Shows a message to user, allowing to capture an answer. +```py + message = 'Capture your name' + name = app.inputbox(message) + app.msgbox(name) +``` + +![inputbox](../img/tools_msg_05.png) + +To hide on screen what the user typing, util for request passwords. +```py + message = 'Type your password' + echochar = '*' + password = app.inputbox(message, echochar=echochar) + app.msgbox(password) +``` + +![inputbox](../img/tools_msg_06.png) \ No newline at end of file diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 00d4ccf..edbc449 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -5,6 +5,10 @@ nav: - Home: index.md - Install: install.md - Debug: debug.md + - Tools: + - tools/index.md + - Messages: tools/messages.md + - Dates and time: tools/datetime.md theme: name: material locale: en @@ -33,8 +37,8 @@ markdown_extensions: extra: alternate: - name: English - link: / + link: /easymacro lang: en - name: Español - link: /langs/es + link: /easymacro/langs/es lang: es diff --git a/docs/es/mkdocs.yml b/docs/es/mkdocs.yml index 2392800..dfcf9e4 100644 --- a/docs/es/mkdocs.yml +++ b/docs/es/mkdocs.yml @@ -69,8 +69,8 @@ markdown_extensions: extra: alternate: - name: English - link: / + link: /easymacro lang: en - name: Español - link: /langs/es + link: /easymacro/langs/es lang: es diff --git a/source/easymacro/constants.py b/source/easymacro/constants.py new file mode 100644 index 0000000..bef7b98 --- /dev/null +++ b/source/easymacro/constants.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +# ~ Is de sum of: +# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html + +from com.sun.star.sheet import CellFlags + +ONLY_DATA = 31 + +ALL = 1023 diff --git a/source/easymacro/easycalc.py b/source/easymacro/easycalc.py index 4dab252..5153660 100644 --- a/source/easymacro/easycalc.py +++ b/source/easymacro/easycalc.py @@ -16,75 +16,13 @@ from .easyevents import EventsRangeSelectionListener, LOEvents from .easyshape import LOShapes, LOShape from .easydrawpage import LODrawPage from .easyforms import LOForms +from .easystyles import LOStyleFamilies SECONDS_DAY = 60 * 60 * 24 ONLY_VALUES = CellFlags.VALUE + CellFlags.DATETIME + CellFlags.STRING -class LOCellStyle(): - - def __init__(self, obj): - self._obj = obj - - def __str__(self): - return f'CellStyle: {self.name}' - - @property - def obj(self): - return self._obj - - @property - def name(self): - return self.obj.Name - - @property - def properties(self): - properties = self.obj.PropertySetInfo.Properties - data = {p.Name: getattr(self.obj, p.Name) for p in properties} - return data - @properties.setter - def properties(self, values): - set_properties(self.obj, values) - - -class LOCellStyles(): - - def __init__(self, obj, doc): - self._obj = obj - self._doc = doc - - def __len__(self): - return len(self.obj) - - def __getitem__(self, index): - return LOCellStyle(self.obj[index]) - - def __setitem__(self, key, value): - self.obj[key] = value - - def __delitem__(self, key): - if not isinstance(key, str): - key = key.Name - del self.obj[key] - - def __contains__(self, item): - return item in self.obj - - @property - def obj(self): - return self._obj - - @property - def names(self): - return self.obj.ElementNames - - def new(self, name: str): - obj = self._doc.create_instance('com.sun.star.style.CellStyle') - self.obj[name] = obj - return LOCellStyle(obj) - - # ~ IsFiltered, # ~ IsManualPageBreak, # ~ IsStartOfNewPage @@ -939,6 +877,11 @@ class LOCalc(LODocument): """Get class events""" return LOEvents(self.obj.Events) + @property + def styles(self): + ci = self.obj.createInstance + return LOStyleFamilies(self.obj.StyleFamilies, ci) + def ranges(self): """Create ranges container""" obj = self._create_instance('com.sun.star.sheet.SheetCellRanges') @@ -1111,5 +1054,4 @@ class LOCalc(LODocument): return self.cell_styles @property def cell_styles(self): - obj = self.obj.StyleFamilies['CellStyles'] - return LOCellStyles(obj, self) + return self.styles['CellStyles'] diff --git a/source/easymacro/easymain.py b/source/easymacro/easymain.py index 6765153..9b89b75 100644 --- a/source/easymacro/easymain.py +++ b/source/easymacro/easymain.py @@ -24,6 +24,7 @@ from com.sun.star.beans import PropertyValue, NamedValue, StringPair from com.sun.star.datatransfer import XTransferable, DataFlavor from com.sun.star.ui.dialogs import TemplateDescription +from .constants import ALL from .messages import MESSAGES @@ -31,6 +32,8 @@ __all__ = [ 'ALL', 'DESKTOP', 'INFO_DEBUG', + 'IS_APPIMAGE', + 'IS_FLATPAK', 'IS_MAC', 'IS_WIN', 'LANG', @@ -63,13 +66,12 @@ PC = platform.node() USER = getpass.getuser() IS_WIN = OS == 'Windows' IS_MAC = OS == 'Darwin' - - -ALL = 1023 +IS_FLATPAK = bool(os.getenv("FLATPAK_ID", "")) +IS_APPIMAGE = bool(os.getenv("APPIMAGE", "")) LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' -LOG_DATE = '%d/%m/%Y %H:%M:%S' +LOG_DATE = '%Y/%m/%d %H:%M:%S' if IS_WIN: logging.addLevelName(logging.ERROR, 'ERROR') logging.addLevelName(logging.DEBUG, 'DEBUG') @@ -149,7 +151,6 @@ day = get_app_config(node, 'DD') DATE_OFFSET = datetime.date(year, month, day).toordinal() _info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path) -# ~ doc INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}" @@ -221,6 +222,12 @@ def set_properties(model, properties): return +def get_properties(obj): + properties = obj.PropertySetInfo.Properties + values = {p.Name: getattr(obj, p.Name) for p in properties} + return values + + # ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61 class classproperty: diff --git a/source/easymacro/easystyles.py b/source/easymacro/easystyles.py new file mode 100644 index 0000000..5ea2621 --- /dev/null +++ b/source/easymacro/easystyles.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python + + +from .easymain import log, BaseObject, set_properties, get_properties + + +STYLE_FAMILIES = 'StyleFamilies' + + +class LOBaseStyles(BaseObject): + + def __init__(self, obj, create_instance=None): + super().__init__(obj) + self._create_intance = create_instance + + def __len__(self): + return self.obj.Count + + def __contains__(self, item): + return self.obj.hasByName(item) + + def __getitem__(self, index): + if index in self: + style = self.obj.getByName(index) + else: + raise IndexError + + if self.NAME == STYLE_FAMILIES: + s = LOStyles(style, index, self._create_intance) + else: + s = LOStyle(style) + return s + + def __iter__(self): + self._index = 0 + return self + + def __next__(self): + if self._index < self.obj.Count: + style = self[self.names[self._index]] + else: + raise StopIteration + + self._index += 1 + return style + + @property + def names(self): + return self.obj.ElementNames + + +class LOStyle(): + NAME = 'Style' + + def __init__(self, obj): + self._obj = obj + + def __str__(self): + return f'Style: {self.name}' + + def __contains__(self, item): + return hasattr(self.obj, item) + + def __setattr__(self, name, value): + if name != '_obj': + self.obj.setPropertyValue(name, value) + else: + super().__setattr__(name, value) + + def __getattr__(self, name): + return self.obj.getPropertyValue(name) + + @property + def obj(self): + return self._obj + + @property + def name(self): + return self.obj.Name + + @property + def is_in_use(self): + return self.obj.isInUse() + + @property + def is_user_defined(self): + return self.obj.isUserDefined() + + @property + def properties(self): + return get_properties(self.obj) + @properties.setter + def properties(self, values): + set_properties(self.obj, values) + + +class LOStyles(LOBaseStyles): + NAME = 'Styles' + + def __init__(self, obj, type_style, create_instance): + super().__init__(obj) + self._type_style = type_style + self._create_instance = create_instance + + def __str__(self): + return f'Styles: {self._type_style}' + + def __setitem__(self, key, value): + if key in self: + style = self.obj.getByName(key) + else: + name = f'com.sun.star.style.{self._type_style[:-1]}' + style = self._create_instance(name) + self.obj.insertByName(key, style) + set_properties(style, value) + + def __delitem__(self, key): + self.obj.removeByName(key) + + +class LOStyleFamilies(LOBaseStyles): + NAME = STYLE_FAMILIES + + def __init__(self, obj, create_instance): + super().__init__(obj, create_instance) + + def __str__(self): + return f'Style Families: {self.names}' + + def _validate_name(self, name): + if not name.endswith('Styles'): + name = f'{name}Styles' + return name + + def __contains__(self, item): + return self.obj.hasByName(self._validate_name(item)) + + def __getitem__(self, index): + if isinstance(index, int): + index = self.names[index] + else: + index = self._validate_name(index) + return super().__getitem__(index) diff --git a/source/easymacro/easywriter.py b/source/easymacro/easywriter.py index 1fce072..22e20df 100644 --- a/source/easymacro/easywriter.py +++ b/source/easymacro/easywriter.py @@ -1,11 +1,123 @@ #!/usr/bin/env python3 +from .easymain import log, BaseObject from .easydoc import LODocument +class LOWriterTextPortion(BaseObject): + + def __init__(self, obj): + super().__init__(obj) + + def __str__(self): + return 'Writer: TextPortion' + + @property + def string(self): + return self.obj.String + + +class LOWriterParagraph(BaseObject): + TEXT_PORTION = 'SwXTextPortion' + + def __init__(self, obj): + super().__init__(obj) + + def __str__(self): + return 'Writer: Paragraph' + + def __iter__(self): + self._iter = iter(self.obj) + return self + + def __next__(self): + obj = next(self._iter) + type_obj = obj.ImplementationName + if type_obj == self.TEXT_PORTION: + obj = LOWriterTextPortion(obj) + return obj + + @property + def string(self): + return self.obj.String + + @property + def cursor(self): + return self.obj.Text.createTextCursorByRange(self.obj) + + +class LOWriterTextRange(BaseObject): + PARAGRAPH = 'SwXParagraph' + + def __init__(self, obj): + super().__init__(obj) + + def __str__(self): + return 'Writer: TextRange' + + def __getitem__(self, index): + for i, v in enumerate(self): + if index == i: + return v + if index > i: + raise IndexError + + def __iter__(self): + self._enum = self.obj.createEnumeration() + return self + + def __next__(self): + if self._enum.hasMoreElements(): + obj = self._enum.nextElement() + type_obj = obj.ImplementationName + if type_obj == self.PARAGRAPH: + obj = LOWriterParagraph(obj) + else: + raise StopIteration + return obj + + @property + def string(self): + return self.obj.String + + @property + def cursor(self): + return self.obj.Text.createTextCursorByRange(self.obj) + + +class LOWriterTextRanges(BaseObject): + + def __init__(self, obj): + super().__init__(obj) + # ~ self._doc = doc + # ~ self._paragraphs = [LOWriterTextRange(p, doc) for p in obj] + + def __str__(self): + return 'Writer: TextRanges' + + def __len__(self): + return self.obj.Count + + def __getitem__(self, index): + return LOWriterTextRange(self.obj[index]) + + def __iter__(self): + self._index = 0 + return self + + def __next__(self): + try: + obj = LOWriterTextRange(self.obj[self._index]) + except IndexError: + raise StopIteration + + self._index += 1 + return obj + + class LOWriter(LODocument): - _type = 'writer' TEXT_RANGES = 'SwXTextRanges' + _type = 'writer' def __init__(self, obj): super().__init__(obj) @@ -17,3 +129,25 @@ class LOWriter(LODocument): @zoom.setter def zoom(self, value): self._view_settings.ZoomValue = value + + @property + def selection(self): + """Get current seleccion""" + sel = None + selection = self.obj.CurrentSelection + type_obj = selection.ImplementationName + + if type_obj == self.TEXT_RANGES: + sel = LOWriterTextRanges(selection) + if len(sel) == 1: + sel = sel[0] + else: + log.debug(type_obj) + log.debug(selection) + sel = selection + + return sel + + @property + def string(self): + return self._obj.Text.String diff --git a/source/easymacro/utils.py b/source/easymacro/utils.py new file mode 100644 index 0000000..e75677a --- /dev/null +++ b/source/easymacro/utils.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python + +# ~ https://github.com/pyca/cryptography/blob/main/src/cryptography/fernet.py#L27 + +import base64 +import binascii +import os +import time +import typing + + +_MAX_CLOCK_SKEW = 60 + + +class InvalidSignature(Exception): + pass + + +class InvalidToken(Exception): + pass + + +class Fernet: + def __init__( + self, + key: bytes | str, + backend: typing.Any = None, + ) -> None: + try: + key = base64.urlsafe_b64decode(key) + except binascii.Error as exc: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) from exc + if len(key) != 32: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) + + + self._signing_key = key[:16] + self._encryption_key = key[16:] + + + @classmethod + def generate_key(cls) -> bytes: + return base64.urlsafe_b64encode(os.urandom(32)) + + + def encrypt(self, data: bytes) -> bytes: + return self.encrypt_at_time(data, int(time.time())) + + + def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: + iv = os.urandom(16) + return self._encrypt_from_parts(data, current_time, iv) + + + def _encrypt_from_parts( + self, data: bytes, current_time: int, iv: bytes + ) -> bytes: + utils._check_bytes("data", data) + + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_data = padder.update(data) + padder.finalize() + encryptor = Cipher( + algorithms.AES(self._encryption_key), + modes.CBC(iv), + ).encryptor() + ciphertext = encryptor.update(padded_data) + encryptor.finalize() + + + basic_parts = ( + b"\x80" + + current_time.to_bytes(length=8, byteorder="big") + + iv + + ciphertext + ) + + + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(basic_parts) + hmac = h.finalize() + return base64.urlsafe_b64encode(basic_parts + hmac) + + + def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(token) + if ttl is None: + time_info = None + else: + time_info = (ttl, int(time.time())) + return self._decrypt_data(data, timestamp, time_info) + + + def decrypt_at_time( + self, token: bytes | str, ttl: int, current_time: int + ) -> bytes: + if ttl is None: + raise ValueError( + "decrypt_at_time() can only be used with a non-None ttl" + ) + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, (ttl, current_time)) + + + def extract_timestamp(self, token: bytes | str) -> int: + timestamp, data = Fernet._get_unverified_token_data(token) + # Verify the token was not tampered with. + self._verify_signature(data) + return timestamp + + + @staticmethod + def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]: + if not isinstance(token, (str, bytes)): + raise TypeError("token must be bytes or str") + + + try: + data = base64.urlsafe_b64decode(token) + except (TypeError, binascii.Error): + raise InvalidToken + + + if not data or data[0] != 0x80: + raise InvalidToken + + + if len(data) < 9: + raise InvalidToken + + + timestamp = int.from_bytes(data[1:9], byteorder="big") + return timestamp, data + + + def _verify_signature(self, data: bytes) -> None: + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(data[:-32]) + try: + h.verify(data[-32:]) + except InvalidSignature: + raise InvalidToken + + + def _decrypt_data( + self, + data: bytes, + timestamp: int, + time_info: tuple[int, int] | None, + ) -> bytes: + if time_info is not None: + ttl, current_time = time_info + if timestamp + ttl < current_time: + raise InvalidToken + + + if current_time + _MAX_CLOCK_SKEW < timestamp: + raise InvalidToken + + + self._verify_signature(data) + + + iv = data[9:25] + ciphertext = data[25:-32] + decryptor = Cipher( + algorithms.AES(self._encryption_key), modes.CBC(iv) + ).decryptor() + plaintext_padded = decryptor.update(ciphertext) + try: + plaintext_padded += decryptor.finalize() + except ValueError: + raise InvalidToken + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + + + unpadded = unpadder.update(plaintext_padded) + try: + unpadded += unpadder.finalize() + except ValueError: + raise InvalidToken + return unpadded diff --git a/source/tests/test_config.py b/source/tests/test_config.py index 1cc6e69..fa9579d 100644 --- a/source/tests/test_config.py +++ b/source/tests/test_config.py @@ -9,6 +9,6 @@ USER = 'elmau' IS_WIN = False IS_MAC = False -LIBO_VERSION = '7.4' +LIBO_VERSION = '7.6' LANGUAGE = 'en-US' LANG = 'en'