Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-proton-vpn-api-core for
openSUSE:Factory checked in at 2024-05-22 21:33:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-proton-vpn-api-core (Old)
and /work/SRC/openSUSE:Factory/.python-proton-vpn-api-core.new.1880 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-proton-vpn-api-core"
Wed May 22 21:33:12 2024 rev:4 rq:1175848 version:0.24.4
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-proton-vpn-api-core/python-proton-vpn-api-core.changes
2024-05-08 11:42:33.274945909 +0200
+++
/work/SRC/openSUSE:Factory/.python-proton-vpn-api-core.new.1880/python-proton-vpn-api-core.changes
2024-05-22 21:33:37.436496114 +0200
@@ -1,0 +2,11 @@
+Wed May 22 11:28:37 UTC 2024 - Alexandre Vicenzi <[email protected]>
+
+- Update to 0.24.4
+ * Filter OSError not just FileNotFound error in sentry.
+ * Set the sentry user id based on a hash of /etc/machine-id
+ * Fix deprecation warning when calculatin WireGuard certificate validity
period.
+ * Fix error saving cache file when parent directory does not exist
+ * Only initialize sentry on first enable.
+ * Forward SSL_CERT_FILE environment variable to sentry.
+
+-------------------------------------------------------------------
Old:
----
v0.23.1.tar.gz
New:
----
v0.24.4.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-proton-vpn-api-core.spec ++++++
--- /var/tmp/diff_new_pack.9e7YbH/_old 2024-05-22 21:33:39.024554198 +0200
+++ /var/tmp/diff_new_pack.9e7YbH/_new 2024-05-22 21:33:39.024554198 +0200
@@ -18,20 +18,21 @@
%{?sle15_python_module_pythons}
Name: python-proton-vpn-api-core
-Version: 0.23.1
+Version: 0.24.4
Release: 0
Summary: Proton VPN API library
License: GPL-3.0-or-later
Group: Development/Languages/Python
URL: https://github.com/ProtonVPN/python-proton-vpn-api-core
Source:
https://github.com/ProtonVPN/python-proton-vpn-api-core/archive/refs/tags/v%{version}.tar.gz
+BuildRequires: %{python_module PyNaCl}
+BuildRequires: %{python_module cryptography}
BuildRequires: %{python_module distro}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module proton-core}
BuildRequires: %{python_module proton-vpn-connection}
BuildRequires: %{python_module proton-vpn-killswitch}
BuildRequires: %{python_module proton-vpn-logger}
-BuildRequires: %{python_module proton-vpn-session}
BuildRequires: %{python_module pytest-asyncio}
BuildRequires: %{python_module pytest-cov}
BuildRequires: %{python_module pytest}
@@ -40,12 +41,13 @@
BuildRequires: fdupes
BuildRequires: pkgconfig
BuildRequires: python-rpm-macros
+Requires: python-PyNaCl
+Requires: python-cryptography
Requires: python-distro
Requires: python-proton-core
Requires: python-proton-vpn-connection
Requires: python-proton-vpn-killswitch
Requires: python-proton-vpn-logger
-Requires: python-proton-vpn-session
Requires: python-sentry-sdk
BuildArch: noarch
%python_subpackages
++++++ v0.23.1.tar.gz -> v0.24.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-proton-vpn-api-core-0.23.1/debian/changelog
new/python-proton-vpn-api-core-0.24.4/debian/changelog
--- old/python-proton-vpn-api-core-0.23.1/debian/changelog 2024-04-23
17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/debian/changelog 2024-05-07
10:00:23.000000000 +0200
@@ -1,3 +1,34 @@
+proton-vpn-api-core (0.24.4) unstable; urgency=medium
+
+ * Filter OSError not just FileNotFound error in sentry.
+
+ -- Luke Titley <[email protected]> Tue, 07 May 2024 11:00:00 +0100
+
+proton-vpn-api-core (0.24.3) unstable; urgency=medium
+
+ * Set the sentry user id based on a hash of /etc/machine-id
+
+ -- Luke Titley <[email protected]> Fri, 03 May 2024 11:00:00 +0100
+
+proton-vpn-api-core (0.24.2) unstable; urgency=medium
+
+ * Fix deprecation warning when calculatin WireGuard certificate validity
period.
+
+ -- Alexandru Cheltuitor <[email protected]> Thu, 02 May 2024
16:00:00 +0100
+
+proton-vpn-api-core (0.24.1) unstable; urgency=medium
+
+ * Fix error saving cache file when parent directory does not exist
+
+ -- Josep Llaneras <[email protected]> Tue, 30 Apr 2024 17:58:40 +0200
+
+proton-vpn-api-core (0.24.0) unstable; urgency=medium
+
+ * Only initialize sentry on first enable.
+ * Forward SSL_CERT_FILE environment variable to sentry.
+
+ -- Luke Titley <[email protected]> Tue, 30 Apr 2024 17:00:00 +0100
+
proton-vpn-api-core (0.23.1) unstable; urgency=medium
* Added missing pip dependencies.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-proton-vpn-api-core-0.23.1/debian/control
new/python-proton-vpn-api-core-0.24.4/debian/control
--- old/python-proton-vpn-api-core-0.23.1/debian/control 2024-04-23
17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/debian/control 2024-05-07
10:00:23.000000000 +0200
@@ -14,6 +14,6 @@
python3-proton-vpn-connection,
python3-distro, python3-proton-vpn-logger, python3-proton-vpn-killswitch,
python3-sentry-sdk, python3-proton-core, python3-nacl
-Breaks: proton-vpn-gtk-app (<< 4.3.1~rc1)
+Breaks: proton-vpn-gtk-app (<< 4.3.2~rc2)
Replaces: python3-proton-vpn-session
Description: Python3 ProtonVPN Core API
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/api.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/api.py
--- old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/api.py
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/api.py
2024-05-07 10:00:23.000000000 +0200
@@ -30,7 +30,7 @@
from proton.vpn.core.session import ClientConfig, LoginResult, BugReportForm
from proton.vpn.core.session.account import VPNAccount
-from proton.vpn.core.usage import UsageReporting, usage_reporting
+from proton.vpn.core.usage import UsageReporting
class ProtonVPNAPI: # pylint: disable=too-many-public-methods
@@ -41,6 +41,8 @@
)
self._settings_persistence = SettingsPersistence()
self._vpn_connector = None
+ self._usage_reporting = UsageReporting(
+ client_type_metadata=client_type_metadata)
async def get_vpn_connector(self):
"""Returns an object that wraps around the raw VPN connection object.
@@ -69,7 +71,7 @@
settings = await loop.run_in_executor(
None, self._settings_persistence.get, user_tier
)
- self.usage_reporting.enabled = settings.anonymous_crash_reports
+ self._usage_reporting.enabled = settings.anonymous_crash_reports
return settings
async def save_settings(self, settings: Settings):
@@ -82,7 +84,7 @@
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, self._settings_persistence.save,
settings)
await self._vpn_connector.apply_settings(settings)
- self.usage_reporting.enabled = settings.anonymous_crash_reports
+ self._usage_reporting.enabled = settings.anonymous_crash_reports
async def login(self, username: str, password: str) -> LoginResult:
"""
@@ -209,7 +211,7 @@
@property
def usage_reporting(self) -> UsageReporting:
"""Returns the usage reporting instance to send anonymous crash
reports."""
- return usage_reporting
+ return self._usage_reporting
def _get_features(self):
settings = self._settings_persistence.get(user_tier=0,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/cache_handler.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/cache_handler.py
--- old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/cache_handler.py
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/cache_handler.py
2024-05-07 10:00:23.000000000 +0200
@@ -22,21 +22,28 @@
import json
import os
+from pathlib import Path
class CacheHandler:
"""Used to save, load, and remove cache files."""
def __init__(self, filepath: str):
- self._fp = filepath
+ self._fp = Path(filepath)
+
+ @property
+ def exists(self):
+ """True if the cache file exists and False otherwise."""
+ return self._fp.is_file()
def save(self, newdata: dict):
"""Save data to cache file."""
+ self._fp.parent.mkdir(parents=True, exist_ok=True)
with open(self._fp, "w") as f: # pylint: disable=W1514, C0103
json.dump(newdata, f, indent=4) # pylint: disable=C0103
def load(self):
"""Load data from cache file, if it exists."""
- if not os.path.isfile(self._fp):
+ if not self.exists:
return None
with open(self._fp, "r") as f: # pylint: disable=W1514, C0103
@@ -44,5 +51,5 @@
def remove(self):
""" Remove cache from disk."""
- if os.path.exists(self._fp):
+ if self.exists:
os.remove(self._fp)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/cache.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/cache.py
--- old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/cache.py
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/cache.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,74 +0,0 @@
-"""
-Copyright (c) 2023 Proton AG
-
-This file is part of Proton VPN.
-
-Proton VPN is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Proton VPN is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with ProtonVPN. If not, see <https://www.gnu.org/licenses/>.
-"""
-
-import json
-from pathlib import Path
-
-
-class CacheFile:
- """
- Persists/loads a python dictionary to disk.
- """
- def __init__(self, file_path: Path):
- """
- :param file_path: Path from/to which load/persist the cache data.
- """
- self.file_path = file_path
-
- @property
- def exists(self):
- """True if the cache file exists and False otherwise."""
- return self.file_path.is_file()
-
- def save(self, data: dict) -> None:
- """Persists the dictionary to the current file path."""
- CacheFile.to_path(data, self.file_path)
-
- @staticmethod
- def to_path(data: dict, file_path: Path) -> None:
- """Persists the dictionary to the given path."""
- with open(file_path, "w", encoding="utf-8") as file:
- json.dump(obj=data, fp=file)
-
- def load(self) -> dict:
- """
- Loads the dictionary from the current file path.
-
- :returns: the loaded dictionary.
- :raises ValueError: if the file content is invalid.
- :raises FileNotFoundError: if the file was not found.
- """
- return CacheFile.from_path(self.file_path)
-
- @staticmethod
- def from_path(file_path: Path) -> dict:
- """
- Loads a dictionary from a given file path.
-
- :param: file_path: path to the file to load as dictionary.
- :returns: the loaded dictionary.
- :raises ValueError: if the file content is invalid.
- :raises FileNotFoundError: if the file was not found.
- """
- with open(file_path, "r", encoding="utf-8") as file:
- return json.load(file)
-
- def remove(self) -> None:
- """Removes the current persistence file, if exists."""
- self.file_path.unlink(missing_ok=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/certificates.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/certificates.py
---
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/certificates.py
2024-04-23 17:03:26.000000000 +0200
+++
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/certificates.py
2024-05-07 10:00:23.000000000 +0200
@@ -278,7 +278,20 @@
""" remaining time the certificate is valid,
in seconds. < 0 : certificate is not valid anymore.
"""
- return self._cert.not_valid_after.timestamp() -
datetime.datetime.utcnow().timestamp()
+ # cryptography >= v42.0.0 added `not_valid_after_utc` and deprecated
`not_valid_after`.
+ if hasattr(self._cert, "not_valid_after_utc"):
+ not_valid_after_timestamp =
self._cert.not_valid_after_utc.timestamp()
+ else:
+ # Because `not_valid_after` returns a naive utc
+ # datetime object (without time zone info), we add it manually
+ # to then be able to compare it to the current time.
+ not_valid_after_timestamp = self._cert.not_valid_after.replace(
+ tzinfo=datetime.timezone.utc
+ ).timestamp()
+
+ now_timestamp =
datetime.datetime.now(datetime.timezone.utc).timestamp()
+
+ return not_valid_after_timestamp - now_timestamp
@property
def validity_date(self) -> datetime.datetime: # pylint:
disable=missing-function-docstring
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/client_config.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/client_config.py
---
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/client_config.py
2024-04-23 17:03:26.000000000 +0200
+++
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/client_config.py
2024-05-07 10:00:23.000000000 +0200
@@ -25,7 +25,7 @@
from proton.utils.environment import VPNExecutionEnvironment
-from proton.vpn.core.session.cache import CacheFile
+from proton.vpn.core.cache_handler import CacheHandler
from proton.vpn.core.session.exceptions import ClientConfigDecodeError
from proton.vpn.core.session.utils import rest_api_request
@@ -237,7 +237,7 @@
"""
self._session = session
self._client_config = None
- self._cache_file = CacheFile(self.CACHE_PATH)
+ self._cache_file = CacheHandler(self.CACHE_PATH)
def clear_cache(self):
"""Discards the cache, if existing."""
@@ -265,6 +265,6 @@
was found then the default client configuration is returned.
"""
- cache = self._cache_file.load() if self._cache_file.exists else None
+ cache = self._cache_file.load()
self._client_config = ClientConfig.from_dict(cache) if cache else
ClientConfig.default()
return self._client_config
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/servers/fetcher.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/servers/fetcher.py
---
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/servers/fetcher.py
2024-04-23 17:03:26.000000000 +0200
+++
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/servers/fetcher.py
2024-05-07 10:00:23.000000000 +0200
@@ -22,7 +22,7 @@
from proton.utils.environment import VPNExecutionEnvironment
-from proton.vpn.core.session.cache import CacheFile
+from proton.vpn.core.cache_handler import CacheHandler
from proton.vpn.core.session.exceptions import ServerListDecodeError
from proton.vpn.core.session.servers.types import ServerLoad
from proton.vpn.core.session.servers.logicals import ServerList,
PersistenceKeys
@@ -44,11 +44,11 @@
self,
session: "VPNSession",
server_list: Optional[ServerList] = None,
- cache_file: Optional[CacheFile] = None
+ cache_file: Optional[CacheHandler] = None
):
self._session = session
self._server_list = server_list
- self._cache_file = cache_file or CacheFile(self.CACHE_PATH)
+ self._cache_file = cache_file or CacheHandler(self.CACHE_PATH)
def clear_cache(self):
"""Discards the cache, if existing."""
@@ -103,10 +103,10 @@
:raises ServerListDecodeError: if the cache is not found or if the
data stored in the cache is not valid.
"""
- try:
- cache = self._cache_file.load()
- except FileNotFoundError as error:
- raise ServerListDecodeError("Cached server list was not found")
from error
+ cache = self._cache_file.load()
+
+ if not cache:
+ raise ServerListDecodeError("Cached server list was not found")
self._server_list = ServerList.from_dict(cache)
return self._server_list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/session.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/session.py
--- old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/session/session.py
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/session/session.py
2024-05-07 10:00:23.000000000 +0200
@@ -89,7 +89,7 @@
self._server_list = self._fetcher.load_server_list_from_cache()
self._client_config =
self._fetcher.load_client_config_from_cache()
except ValueError:
- logger.exception("Error deserializing VPN session.")
+ logger.warning("VPN session could not be deserialized.",
exc_info=True)
super().__setstate__(data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/usage.py
new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/usage.py
--- old/python-proton-vpn-api-core-0.23.1/proton/vpn/core/usage.py
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/proton/vpn/core/usage.py
2024-05-07 10:00:23.000000000 +0200
@@ -16,27 +16,159 @@
You should have received a copy of the GNU General Public License
along with ProtonVPN. If not, see <https://www.gnu.org/licenses/>.
"""
-import sentry_sdk
-from sentry_sdk.integrations.dedupe import DedupeIntegration
-from sentry_sdk.integrations.stdlib import StdlibIntegration
-from sentry_sdk.integrations.modules import ModulesIntegration
+import logging
+import os
+import hashlib
+import getpass
from proton.vpn.core.session_holder import ClientTypeMetadata,
DISTRIBUTION_VERSION, DISTRIBUTION_ID
from proton.vpn.core.session.dataclasses import get_desktop_environment
DSN =
"https://[email protected]/core/v4/reports/sentry/25"
+SSL_CERT_FILE = "SSL_CERT_FILE"
+MACHINE_ID = "/etc/machine-id"
+PROTON_VPN = "protonvpn"
+
+log = logging.getLogger(__name__)
class UsageReporting:
"""Sends anonymous usage reports to Proton."""
- def __init__(self):
- self.enabled = False
+ def __init__(self, client_type_metadata: ClientTypeMetadata):
+ self._enabled = False
self._capture_exception = None
+ self._client_type_metadata = client_type_metadata
+ self._user_id = None
+ self._desktop_environment = get_desktop_environment()
+
+ @property
+ def enabled(self):
+ """Returns whether anonymous usage reporting is enabled."""
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value: bool):
+ """
+ Sets whether usage reporting is enabled/disabled.
+
+ On unsupported platforms, this may fail, in which case UsageReporting
+ will be disabled and an exception will be logged.
+ """
+
+ try:
+ self._enabled = value and self._start_sentry()
+ except Exception: # pylint: disable=broad-except
+ self._enabled = False
+ log.exception("Failed to enabled usage reporting")
+
+ def report_error(self, error):
+ """
+ Send an error to sentry if anonymous usage reporting is enabled.
- def init(self, client_type_metadata: ClientTypeMetadata, enabled: bool):
- """This method should be called before reporting, otherwise reports
will be ignored."""
+ On unsupported platforms, this may fail, in which case the error will
+ will not be reported and an exception will be logged.
+ """
+
+ try:
+ if self._enabled:
+ self._add_scope_metadata()
+ sanitized_error = self._sanitize_error(error)
+ self._capture_exception(sanitized_error)
+
+ except Exception: # pylint: disable=broad-except
+ log.exception("Failed to report error '%s'", str(error))
+
+ @staticmethod
+ def _get_user_id(machine_id_filepath=MACHINE_ID, user_name=None):
+ """
+ Returns a unique identifier for the user.
+
+ :param machine_id_filepath: The path to the machine id file,
+ defaults to /etc/machine-id. This can be overrided for testing.
+
+ :param user_name: The username to include in the hash, if None is
+ provided, the current user is obtained from the environment.
+ """
+
+ if not os.path.exists(machine_id_filepath):
+ return None
+
+ # We include the username in the hash to avoid collisions on machines
+ # with multiple users.
+ if not user_name:
+ user_name = getpass.getuser()
+
+ # We use the machine id to uniquely identify the machine, we combine it
+ # with the application name and the username. All three are hashed to
+ # avoid leaking any personal information.
+ with open(machine_id_filepath, "r", encoding="utf-8") as
machine_id_file:
+ machine_id = machine_id_file.read().strip()
+
+ combined = hashlib.sha256(machine_id.encode('utf-8'))
+ combined.update(hashlib.sha256(PROTON_VPN.encode('utf-8')).digest())
+ combined.update(hashlib.sha256(user_name.encode('utf-8')).digest())
+
+ return str(combined.hexdigest())
+
+ @staticmethod
+ def _sanitize_error(error_info):
+ """
+ This is where we remove personal information from the error before
+ sending it to sentry.
+ """
+
+ def _sanitize(error):
+ if isinstance(error, OSError):
+ # We dont have a lot of files we need to read, so it's safer to
+ # not include the full file path in the report.
+ _, filename = os.path.split(error.filename)
+ error.filename = filename
+ return error
+
+ if isinstance(error_info, tuple):
+ return (error_info[0], _sanitize(error_info[1]), error_info[2])
+
+ return _sanitize(error_info)
+
+ def _add_scope_metadata(self):
+ """
+ Unfortunately, we cannot set the user and tags on the isolation scope
+ on startup because this is lost by the time we report an error.
+ So we have to set the user and tags on the current scope just before
+ reporting an error.
+ """
+ import sentry_sdk # pylint: disable=import-outside-toplevel
+ # Using configure_scope to set a tag works with older versions of
+ # sentry (0.12.2) and so works on ubuntu 20.
+ with sentry_sdk.configure_scope() as scope:
+ scope.set_tag("distro_name", DISTRIBUTION_ID)
+ scope.set_tag("distro_version", DISTRIBUTION_VERSION)
+ scope.set_tag("desktop_environment", self._desktop_environment)
+ if self._user_id and hasattr(scope, "set_user"):
+ scope.set_user({"id": self._user_id})
+
+ def _start_sentry(self):
+ """Starts the sentry SDK with the appropriate configuration."""
+
+ if self._capture_exception:
+ return True
+
+ if not self._client_type_metadata:
+ raise ValueError("Client type metadata is not set, "
+ "UsageReporting.init() must be called first.")
+
+ import sentry_sdk # pylint: disable=import-outside-toplevel
+
+ from sentry_sdk.integrations.dedupe import DedupeIntegration #
pylint: disable=import-outside-toplevel
+ from sentry_sdk.integrations.stdlib import StdlibIntegration #
pylint: disable=import-outside-toplevel
+ from sentry_sdk.integrations.modules import ModulesIntegration #
pylint: disable=import-outside-toplevel
+
+ # Read from SSL_CERT_FILE from environment variable, this allows us to
+ # use an http proxy if we want to.
+ ca_certs = os.environ.get(SSL_CERT_FILE, None)
+ client_type_metadata = self._client_type_metadata
sentry_sdk.init(
dsn=DSN,
release=f"{client_type_metadata.type}-{client_type_metadata.version}",
@@ -46,34 +178,14 @@
DedupeIntegration(), # Yes we want to avoid event
duplication
StdlibIntegration(), # Yes we want info from the standard
lib objects
ModulesIntegration() # Yes we want to know what python
modules are installed
- ]
+ ],
+ ca_certs=ca_certs
)
- # Using configure_scope to set a tag works with older versions of
- # sentry (0.12.2) and so works on ubuntu 20.
- with sentry_sdk.configure_scope() as scope:
- scope.set_tag("distro_name", DISTRIBUTION_ID)
- scope.set_tag("distro_version", DISTRIBUTION_VERSION)
- scope.set_tag("desktop_environment", get_desktop_environment())
-
- self.enabled = enabled
+ # Store the user id so we don't have to calculate it again.
+ self._user_id = self._get_user_id()
# Store _capture_exception as a member, so it's easier to test.
self._capture_exception = sentry_sdk.capture_exception
- def disable(self):
- """Disables anonymous usage reporting. """
- self.enabled = False
-
- def enable(self):
- """Enables anonymous usage reporting."""
- self.enabled = True
-
- def report_error(self, error):
- """Reports an error if anonymous usage reporting is enabled."""
-
- if self.enabled:
- self._capture_exception(error)
-
-
-usage_reporting = UsageReporting()
+ return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/rpmbuild/SPECS/package.spec
new/python-proton-vpn-api-core-0.24.4/rpmbuild/SPECS/package.spec
--- old/python-proton-vpn-api-core-0.23.1/rpmbuild/SPECS/package.spec
2024-04-23 17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/rpmbuild/SPECS/package.spec
2024-05-07 10:00:23.000000000 +0200
@@ -1,5 +1,5 @@
%define unmangled_name proton-vpn-api-core
-%define version 0.23.1
+%define version 0.24.4
%define release 1
Prefix: %{_prefix}
@@ -34,7 +34,7 @@
Requires: python3-sentry-sdk
Requires: python3-pynacl
-Conflicts: proton-vpn-gtk-app < 4.3.1~rc1
+Conflicts: proton-vpn-gtk-app < 4.3.2~rc2
Obsoletes: python3-proton-vpn-session
%{?python_disable_dependency_generator}
@@ -59,6 +59,22 @@
%defattr(-,root,root)
%changelog
+* Fri May 03 2024 Luke Titley <[email protected]> 0.24.4
+- Filter OSError not just FileNotFound error in sentry.
+
+* Fri May 03 2024 Luke Titley <[email protected]> 0.24.3
+- Set the sentry user id based on a hash of /etc/machine-id
+
+* Thu May 02 2024 Alexandru Cheltuitor <[email protected]> 0.24.2
+- Fix deprecation warning when calculatin WireGuard certificate validity
period.
+
+* Tue Apr 30 2024 Josep Llaneras <[email protected]> 0.24.1
+- Fix error saving cache file when parent directory does not exist
+
+* Tue Apr 30 2024 Luke Titley <[email protected]> 0.24.0
+- Only initialize sentry on first enable.
+- Forward SSL_CERT_FILE environment variable to sentry.
+
* Mon Apr 23 2024 Luke Titley <[email protected]> 0.23.1
- Added missing pip dependencies.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-proton-vpn-api-core-0.23.1/setup.py
new/python-proton-vpn-api-core-0.24.4/setup.py
--- old/python-proton-vpn-api-core-0.23.1/setup.py 2024-04-23
17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/setup.py 2024-05-07
10:00:23.000000000 +0200
@@ -4,7 +4,7 @@
setup(
name="proton-vpn-api-core",
- version="0.23.1",
+ version="0.24.4",
description="Proton AG VPN Core API",
author="Proton AG",
author_email="[email protected]",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-proton-vpn-api-core-0.23.1/tests/test_usage.py
new/python-proton-vpn-api-core-0.24.4/tests/test_usage.py
--- old/python-proton-vpn-api-core-0.23.1/tests/test_usage.py 2024-04-23
17:03:26.000000000 +0200
+++ new/python-proton-vpn-api-core-0.24.4/tests/test_usage.py 2024-05-07
10:00:23.000000000 +0200
@@ -16,16 +16,26 @@
You should have received a copy of the GNU General Public License
along with ProtonVPN. If not, see <https://www.gnu.org/licenses/>.
"""
+import os
import pytest
from types import SimpleNamespace
+import tempfile
-from proton.vpn.core.usage import usage_reporting
+from proton.vpn.core.session_holder import ClientTypeMetadata
+from proton.vpn.core.usage import UsageReporting
+SECRET_FILE = "secret.txt"
+SECRET_PATH = os.path.join("/home/wozniak/5nkfiudfmk/.cache", SECRET_FILE)
+MACHINE_ID = "bg77t2rmpjhgt9zim5gkz4t78jfur39f"
+SENTRY_USER_ID =
"70cf75689cecae78ec588316320d76477c71031f7fd172dd5577ac95934d4499"
+USERNAME = "tester"
@pytest.mark.parametrize("enabled", [True, False])
def test_usage_report_enabled(enabled):
report_error = SimpleNamespace(invoked=False)
+ usage_reporting = UsageReporting(ClientTypeMetadata("test_usage.py",
"none"))
+
def capture_exception(error):
report_error.invoked = True
@@ -36,3 +46,33 @@
usage_reporting.report_error(EMPTY_ERROR)
assert report_error.invoked == enabled, "UsageReporting enable state does
not match the error reporting"
+
+
[email protected]("enabled", [True, False])
+def test_sanitize_simple_error(enabled):
+
+ error = FileNotFoundError("File not found")
+ error.filename = SECRET_PATH
+
+ assert UsageReporting._sanitize_error(error).filename == SECRET_FILE,
"Error sanitization failed"
+
+
[email protected]("enabled", [True, False])
+def test_sanitize_traceback_error(enabled):
+
+ try:
+ open(SECRET_PATH, "r")
+ except FileNotFoundError as exception:
+ error = (type(exception), exception, exception.__traceback__)
+
+ assert UsageReporting._sanitize_error(error)[1].filename == SECRET_FILE,
"Error sanitization failed"
+
+
+def test_userid_calaculation():
+ with tempfile.NamedTemporaryFile() as file:
+ file.write(MACHINE_ID.encode('utf-8'))
+ file.seek(0)
+
+ assert UsageReporting._get_user_id(
+ machine_id_filepath=file.name,
+ user_name=USERNAME) == SENTRY_USER_ID, "Error hashing does not
match the expected value"