Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-py-vapid for openSUSE:Factory
checked in at 2025-10-21 11:15:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-py-vapid (Old)
and /work/SRC/openSUSE:Factory/.python-py-vapid.new.18484 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-py-vapid"
Tue Oct 21 11:15:23 2025 rev:7 rq:1312273 version:1.9.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-py-vapid/python-py-vapid.changes
2025-06-10 09:10:47.669099994 +0200
+++
/work/SRC/openSUSE:Factory/.python-py-vapid.new.18484/python-py-vapid.changes
2025-10-21 11:16:24.046743509 +0200
@@ -1,0 +2,10 @@
+Mon Oct 20 03:08:23 UTC 2025 - Steve Kowalik <[email protected]>
+
+- Update to 1.9.2:
+ * Move project metadata to pyproject.toml
+- Add patch remove-mock.patch:
+ * Prefer built-in unittest.mock
+- Add patch support-new-cryptography.patch:
+ * Support new cryprtography changes.
+
+-------------------------------------------------------------------
Old:
----
py-vapid-1.9.0.tar.gz
New:
----
py_vapid-1.9.2.tar.gz
remove-mock.patch
support-new-cryptography.patch
----------(New B)----------
New: * Move project metadata to pyproject.toml
- Add patch remove-mock.patch:
* Prefer built-in unittest.mock
New: * Prefer built-in unittest.mock
- Add patch support-new-cryptography.patch:
* Support new cryprtography changes.
----------(New E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-py-vapid.spec ++++++
--- /var/tmp/diff_new_pack.vyJviQ/_old 2025-10-21 11:16:25.186791420 +0200
+++ /var/tmp/diff_new_pack.vyJviQ/_new 2025-10-21 11:16:25.190791588 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-py-vapid
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2025 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,12 +18,16 @@
%bcond_without libalternatives
Name: python-py-vapid
-Version: 1.9.0
+Version: 1.9.2
Release: 0
Summary: VAPID header generation library
License: MPL-2.0
URL: https://github.com/mozilla-services/vapid
-Source:
https://files.pythonhosted.org/packages/source/p/py-vapid/py-vapid-%{version}.tar.gz
+Source:
https://files.pythonhosted.org/packages/source/p/py-vapid/py_vapid-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM Based on gh#web-push-libs/vapid#108
+Patch0: remove-mock.patch
+# PATCH-FIX-UPSTREAM Based on gh#web-push-libs/vapid#110
+Patch1: support-new-cryptography.patch
BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module wheel}
@@ -43,8 +47,7 @@
VAPID header generation library.
%prep
-%setup -q -n py-vapid-%{version}
-sed -i 's/from mock import/from unittest.mock import/'
py_vapid/tests/test_vapid.py
+%autosetup -p1 -n py_vapid-%{version}
%build
%pyproject_wheel
@@ -65,5 +68,5 @@
%license LICENSE
%python_alternative %{_bindir}/vapid
%{python_sitelib}/py[-_]vapid
-%{python_sitelib}/py[-_]vapid-%{version}*-info
+%{python_sitelib}/py[-_]vapid-%{version}.dist-info
++++++ remove-mock.patch ++++++
>From 87e8fa3b251145f8f996e64974134921c8d06bb3 Mon Sep 17 00:00:00 2001
From: Alexandre Detiste <[email protected]>
Date: Sun, 30 Mar 2025 12:11:35 +0200
Subject: [PATCH] prefer newer unittest.mock from standard library
---
python/py_vapid/tests/test_vapid.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
Index: py_vapid-1.9.2/py_vapid/tests/test_vapid.py
===================================================================
--- py_vapid-1.9.2.orig/py_vapid/tests/test_vapid.py
+++ py_vapid-1.9.2/py_vapid/tests/test_vapid.py
@@ -5,7 +5,10 @@ import os
import json
import unittest
from cryptography.hazmat.primitives import serialization
-from mock import patch, Mock
+try:
+ from unittest.mock import patch, Mock
+except ImportError:
+ from mock import patch, Mock
from py_vapid import Vapid01, Vapid02, VapidException, _check_sub
from py_vapid.jwt import decode
++++++ support-new-cryptography.patch ++++++
>From 4898bb4d418c4b3b3a0bb3620cb4cb13a481e0ba Mon Sep 17 00:00:00 2001
From: jrconlin <[email protected]>
Date: Thu, 2 Oct 2025 10:29:35 -0700
Subject: [PATCH 1/2] feat: Update to latest EC recommendations
Closes #105
---
python/py_vapid/__init__.py | 116 +++++++++++++++++-------------------
python/pyproject.toml | 2 +-
2 files changed, 57 insertions(+), 61 deletions(-)
diff --git a/py_vapid/__init__.py b/py_vapid/__init__.py
index 4a6eff2..94d1113 100644
--- a/py_vapid/__init__.py
+++ b/py_vapid/__init__.py
@@ -25,6 +25,7 @@
class VapidException(Exception):
"""An exception wrapper for Vapid."""
+
pass
@@ -34,6 +35,7 @@ class Vapid01(object):
https://tools.ietf.org/html/draft-ietf-webpush-vapid-01
"""
+
_private_key = None
_public_key = None
_schema = "WebPush"
@@ -65,14 +67,14 @@ def from_raw(cls, private_raw):
key = ec.derive_private_key(
int(binascii.hexlify(b64urldecode(private_raw)), 16),
curve=ec.SECP256R1(),
- backend=default_backend())
+ backend=default_backend(),
+ )
return cls(key)
@classmethod
def from_raw_public(cls, public_raw):
key = ec.EllipticCurvePublicKey.from_encoded_point(
- curve=ec.SECP256R1(),
- data=b64urldecode(public_raw)
+ curve=ec.SECP256R1(), data=b64urldecode(public_raw)
)
ss = cls()
ss._public_key = key
@@ -87,8 +89,7 @@ def from_pem(cls, private_key):
"""
# not sure why, but load_pem_private_key fails to deserialize
- return cls.from_der(
- b''.join(private_key.splitlines()[1:-1]))
+ return cls.from_der(b"".join(private_key.splitlines()[1:-1]))
@classmethod
def from_der(cls, private_key):
@@ -98,9 +99,9 @@ def from_der(cls, private_key):
:type private_key: bytes
"""
- key = serialization.load_der_private_key(b64urldecode(private_key),
- password=None,
- backend=default_backend())
+ key = serialization.load_der_private_key(
+ b64urldecode(private_key), password=None, backend=default_backend()
+ )
return cls(key)
@classmethod
@@ -118,13 +119,13 @@ def from_file(cls, private_key_file=None):
vapid.generate_keys()
vapid.save_key(private_key_file)
return vapid
- with open(private_key_file, 'r') as file:
+ with open(private_key_file, "r") as file:
private_key = file.read()
try:
if "-----BEGIN" in private_key:
- vapid = cls.from_pem(private_key.encode('utf8'))
+ vapid = cls.from_pem(private_key.encode("utf8"))
else:
- vapid = cls.from_der(private_key.encode('utf8'))
+ vapid = cls.from_der(private_key.encode("utf8"))
return vapid
except Exception as exc:
logging.error("Could not open private key file: %s", repr(exc))
@@ -156,11 +157,10 @@ def verify(cls, key, auth):
type key: str
"""
- tokens = auth.rsplit(' ', 1)[1].rsplit('.', 1)
+ tokens = auth.rsplit(" ", 1)[1].rsplit(".", 1)
kp = cls().from_raw_public(key.encode())
return kp.verify_token(
- validation_token=tokens[0].encode(),
- verification_token=tokens[1]
+ validation_token=tokens[0].encode(), verification_token=tokens[1]
)
@property
@@ -197,20 +197,19 @@ def public_key(self):
def generate_keys(self):
"""Generate a valid ECDSA Key Pair."""
- self.private_key = ec.generate_private_key(ec.SECP256R1,
- default_backend())
+ self.private_key = ec.generate_private_key(ec.SECP256R1(),
default_backend())
def private_pem(self):
return self.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
- encryption_algorithm=serialization.NoEncryption()
+ encryption_algorithm=serialization.NoEncryption(),
)
def public_pem(self):
return self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
- format=serialization.PublicFormat.SubjectPublicKeyInfo
+ format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
def save_key(self, key_file):
@@ -245,14 +244,14 @@ def verify_token(self, validation_token,
verification_token):
:rtype: boolean
"""
- hsig = b64urldecode(verification_token.encode('utf8'))
+ hsig = b64urldecode(verification_token.encode("utf8"))
r = int(binascii.hexlify(hsig[:32]), 16)
s = int(binascii.hexlify(hsig[32:]), 16)
try:
self.public_key.verify(
ecutils.encode_dss_signature(r, s),
validation_token,
- signature_algorithm=ec.ECDSA(hashes.SHA256())
+ signature_algorithm=ec.ECDSA(hashes.SHA256()),
)
return True
except InvalidSignature:
@@ -260,23 +259,25 @@ def verify_token(self, validation_token,
verification_token):
def _base_sign(self, claims):
cclaims = copy.deepcopy(claims)
- if not cclaims.get('exp'):
- cclaims['exp'] = int(time.time()) + 86400
- if not self.conf.get('no-strict', False):
- valid = _check_sub(cclaims.get('sub', ''))
+ if not cclaims.get("exp"):
+ cclaims["exp"] = int(time.time()) + 86400
+ if not self.conf.get("no-strict", False):
+ valid = _check_sub(cclaims.get("sub", ""))
else:
- valid = cclaims.get('sub') is not None
+ valid = cclaims.get("sub") is not None
if not valid:
raise VapidException(
"Missing 'sub' from claims. "
- "'sub' is your admin email as a mailto: link.")
- if not re.match(r"^https?://[^/:]+(:\d+)?$",
- cclaims.get("aud", ""),
- re.IGNORECASE):
+ "'sub' is your admin email as a mailto: link."
+ )
+ if not re.match(
+ r"^https?://[^/:]+(:\d+)?$", cclaims.get("aud", ""), re.IGNORECASE
+ ):
raise VapidException(
"Missing 'aud' from claims. "
"'aud' is the scheme, host and optional port for this "
- "transaction e.g. https://example.com:8080")
+ "transaction e.g. https://example.com:8080"
+ )
return cclaims
def sign(self, claims, crypto_key=None):
@@ -292,19 +293,22 @@ def sign(self, claims, crypto_key=None):
"""
sig = sign(self._base_sign(claims), self.private_key)
- pkey = 'p256ecdsa='
+ pkey = "p256ecdsa="
pkey += b64urlencode(
self.public_key.public_bytes(
serialization.Encoding.X962,
- serialization.PublicFormat.UncompressedPoint
- ))
+ serialization.PublicFormat.UncompressedPoint,
+ )
+ )
if crypto_key:
- crypto_key = crypto_key + ';' + pkey
+ crypto_key = crypto_key + ";" + pkey
else:
crypto_key = pkey
- return {"Authorization": "{} {}".format(self._schema, sig.strip('=')),
- "Crypto-Key": crypto_key}
+ return {
+ "Authorization": "{} {}".format(self._schema, sig.strip("=")),
+ "Crypto-Key": crypto_key,
+ }
class Vapid02(Vapid01):
@@ -313,6 +317,7 @@ class Vapid02(Vapid01):
https://tools.ietf.org/html/rfc8292
"""
+
_schema = "vapid"
def sign(self, claims, crypto_key=None):
@@ -329,14 +334,11 @@ def sign(self, claims, crypto_key=None):
"""
sig = sign(self._base_sign(claims), self.private_key)
pkey = self.public_key.public_bytes(
- serialization.Encoding.X962,
- serialization.PublicFormat.UncompressedPoint
- )
- return{
+ serialization.Encoding.X962,
serialization.PublicFormat.UncompressedPoint
+ )
+ return {
"Authorization": "{schema} t={t},k={k}".format(
- schema=self._schema,
- t=sig,
- k=b64urlencode(pkey)
+ schema=self._schema, t=sig, k=b64urlencode(pkey)
)
}
@@ -349,27 +351,23 @@ def verify(cls, auth):
:rtype: bool
"""
- pref_tok = auth.rsplit(' ', 1)
- assert pref_tok[0].lower() == cls._schema, (
- "Incorrect schema specified")
+ pref_tok = auth.rsplit(" ", 1)
+ assert pref_tok[0].lower() == cls._schema, "Incorrect schema specified"
parts = {}
- for tok in pref_tok[1].split(','):
- kv = tok.split('=', 1)
+ for tok in pref_tok[1].split(","):
+ kv = tok.split("=", 1)
parts[kv[0]] = kv[1]
- assert 'k' in parts.keys(), (
- "Auth missing public key 'k' value")
- assert 't' in parts.keys(), (
- "Auth missing token set 't' value")
- kp = cls().from_raw_public(parts['k'].encode())
- tokens = parts['t'].rsplit('.', 1)
+ assert "k" in parts.keys(), "Auth missing public key 'k' value"
+ assert "t" in parts.keys(), "Auth missing token set 't' value"
+ kp = cls().from_raw_public(parts["k"].encode())
+ tokens = parts["t"].rsplit(".", 1)
return kp.verify_token(
- validation_token=tokens[0].encode(),
- verification_token=tokens[1]
+ validation_token=tokens[0].encode(), verification_token=tokens[1]
)
def _check_sub(sub):
- """ Check to see if the `sub` is a properly formatted `mailto:`
+ """Check to see if the `sub` is a properly formatted `mailto:`
a `mailto:` should be a SMTP mail address. Mind you, since I run
YouFailAtEmail.com, you have every right to yell about how terrible
@@ -382,9 +380,7 @@ def _check_sub(sub):
:rtype: bool
"""
- pattern = (
-
r"^(mailto:.+@((localhost|[%\w-]+(\.[%\w-]+)+|([0-9a-f]{1,4}):+([0-9a-f]{1,4})?)))|https:\/\/(localhost|[\w-]+\.[\w\.-]+|([0-9a-f]{1,4}:+)+([0-9a-f]{1,4})?)$"
# noqa
- )
+ pattern =
r"^(mailto:.+@((localhost|[%\w-]+(\.[%\w-]+)+|([0-9a-f]{1,4}):+([0-9a-f]{1,4})?)))|https:\/\/(localhost|[\w-]+\.[\w\.-]+|([0-9a-f]{1,4}:+)+([0-9a-f]{1,4})?)$"
# noqa
return re.match(pattern, sub, re.IGNORECASE) is not None