From aca71c6bc713f243099f41a76247e7baa5fbd8d1 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Wed, 22 Nov 2023 11:38:00 +0100 Subject: [PATCH] [wallabag] Improved OAuth flow integration. If the user provided `username` and `password` in the plugin configuration, then we should use those credentials to refresh the OAuth token when expired. --- platypush/plugins/wallabag/__init__.py | 93 ++++++++++++++++++++------ 1 file changed, 71 insertions(+), 22 deletions(-) diff --git a/platypush/plugins/wallabag/__init__.py b/platypush/plugins/wallabag/__init__.py index 776e3afe6..c7e4b7835 100644 --- a/platypush/plugins/wallabag/__init__.py +++ b/platypush/plugins/wallabag/__init__.py @@ -1,13 +1,13 @@ import json import os import pathlib -import requests import time - from datetime import datetime, timedelta from typing import Iterable, List, Optional from urllib.parse import urljoin +import requests + from platypush.config import Config from platypush.plugins import Plugin, action from platypush.schemas.wallabag import WallabagEntrySchema @@ -23,6 +23,8 @@ class WallabagPlugin(Plugin): str(Config.get('workdir')), 'wallabag', 'credentials.json' ) + _api_timeout = 15 + def __init__( self, client_id: str, @@ -39,12 +41,24 @@ class WallabagPlugin(Plugin): :param client_secret: Client secret for your application - you can create one at ``/developer``. :param server_url: Base URL of the Wallabag server (default: ``https://wallabag.it``). - :param username: Wallabag username. Only needed for the first login, - you can remove it afterwards. Alternatively, you can provide it - on the :meth:`.login` method. - :param password: Wallabag password. Only needed for the first login, - you can remove it afterwards. Alternatively, you can provide it - on the :meth:`.login` method. + :param username: Wallabag username. This configuration attribute is + required for the first login. You can delete afterwards. However, + note that, without username and password, the session is valid only + as long as the ``refresh_token`` returned by the API is valid + (that's usually a couple of months by default). When the + ``refresh_token`` expires, you will have to either add again this + attribute to the configuration and restart the app (so the service + can create a new session), or manually provide your credentials + through the :meth:`.login` action. + :param password: Wallabag password. This configuration attribute is + required for the first login. You can delete afterwards. However, + note that, without username and password, the session is valid only + as long as the ``refresh_token`` returned by the API is valid + (that's usually a couple of months by default). When the + ``refresh_token`` expires, you will have to either add again this + attribute to the configuration and restart the app (so the service + can create a new session), or manually provide your credentials + through the :meth:`.login` action. :param credentials_file: Path to the file where the OAuth session parameters will be stored (default: ``/wallabag/credentials.json``). @@ -80,18 +94,41 @@ class WallabagPlugin(Plugin): def _oauth_refresh_token(self): url = urljoin(self._server_url, '/oauth/v2/token') + payload = { + 'grant_type': 'refresh_token', + 'client_id': self._client_id, + 'client_secret': self._client_secret, + 'access_token': self._session['access_token'], + 'refresh_token': self._session['refresh_token'], + } + + if self._username and self._password: + payload['username'] = self._username + payload['password'] = self._password + rs = requests.post( url, - json={ - 'grant_type': 'refresh_token', - 'client_id': self._client_id, - 'client_secret': self._client_secret, - 'access_token': self._session['access_token'], - 'refresh_token': self._session['refresh_token'], - }, + timeout=self._api_timeout, + json=payload, ) - rs.raise_for_status() + try: + rs.raise_for_status() + except Exception as e: + self.logger.warning( + 'Failure occurred when refreshing the OAuth token, creating a new session: %s', + e, + ) + + assert self._username and self._password, ( + "Can't refresh the OAuth session: username and password have not been provided. " + "Please either provide them in the configuration and restart " + "the service, or call the `wallabag.login` action" + ) + + self._oauth_create_new_session(self._username, self._password) + return + rs = rs.json() self._session.update( { @@ -115,6 +152,7 @@ class WallabagPlugin(Plugin): url = urljoin(self._server_url, '/oauth/v2/token') rs = requests.post( url, + timeout=self._api_timeout, json={ 'grant_type': 'password', 'client_id': self._client_id, @@ -260,7 +298,7 @@ class WallabagPlugin(Plugin): ) @action - def get(self, id: int) -> Optional[dict]: + def get(self, id: int) -> Optional[dict]: # pylint: disable=redefined-builtin """ Get the content and metadata of a link by ID. @@ -268,10 +306,18 @@ class WallabagPlugin(Plugin): :return: .. schema:: wallabag.WallabagEntrySchema """ rs = self._request(f'/entries/{id}.json', method='get') - return WallabagEntrySchema().dump(rs) # type: ignore + if not rs: + return None + + return dict(WallabagEntrySchema().dump(rs)) @action - def export(self, id: int, file: str, format: str = 'txt'): + def export( + self, + id: int, # pylint: disable=redefined-builtin + file: str, + format: str = 'txt', # pylint: disable=redefined-builtin + ): """ Export a saved entry to a file in the specified format. @@ -345,7 +391,7 @@ class WallabagPlugin(Plugin): @action def update( self, - id: int, + id: int, # pylint: disable=redefined-builtin title: Optional[str] = None, content: Optional[str] = None, tags: Optional[Iterable[str]] = None, @@ -387,10 +433,13 @@ class WallabagPlugin(Plugin): }, ) - return WallabagEntrySchema().dump(rs) # type: ignore + if not rs: + return None + + return dict(WallabagEntrySchema().dump(rs)) @action - def delete(self, id: int) -> Optional[dict]: + def delete(self, id: int) -> Optional[dict]: # pylint: disable=redefined-builtin """ Delete an entry by ID.