write OAuth token to cache by default (#616)
This commit is contained in:
parent
ddc253cf9a
commit
dfcf2a2c91
@ -910,6 +910,16 @@ Description Controls how a user is directed to an OAuth authorization site.
|
|||||||
=========== =====
|
=========== =====
|
||||||
|
|
||||||
|
|
||||||
|
extractor.oauth.cache
|
||||||
|
---------------------
|
||||||
|
=========== =====
|
||||||
|
Type ``bool``
|
||||||
|
Default ``true``
|
||||||
|
Description Store tokens received during OAuth authorizations
|
||||||
|
in `cache <cache.file_>`__.
|
||||||
|
=========== =====
|
||||||
|
|
||||||
|
|
||||||
extractor.oauth.port
|
extractor.oauth.port
|
||||||
--------------------
|
--------------------
|
||||||
=========== =====
|
=========== =====
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
},
|
},
|
||||||
"deviantart":
|
"deviantart":
|
||||||
{
|
{
|
||||||
"refresh-token": null,
|
|
||||||
"extra": false,
|
"extra": false,
|
||||||
"flat": true,
|
"flat": true,
|
||||||
"folders": false,
|
"folders": false,
|
||||||
@ -51,8 +50,6 @@
|
|||||||
},
|
},
|
||||||
"flickr":
|
"flickr":
|
||||||
{
|
{
|
||||||
"access-token": null,
|
|
||||||
"access-token-secret": null,
|
|
||||||
"videos": true,
|
"videos": true,
|
||||||
"size-max": null
|
"size-max": null
|
||||||
},
|
},
|
||||||
@ -96,6 +93,7 @@
|
|||||||
"oauth":
|
"oauth":
|
||||||
{
|
{
|
||||||
"browser": true,
|
"browser": true,
|
||||||
|
"cache": true,
|
||||||
"port": 6414
|
"port": 6414
|
||||||
},
|
},
|
||||||
"pixiv":
|
"pixiv":
|
||||||
@ -120,7 +118,6 @@
|
|||||||
},
|
},
|
||||||
"reddit":
|
"reddit":
|
||||||
{
|
{
|
||||||
"refresh-token": null,
|
|
||||||
"comments": 0,
|
"comments": 0,
|
||||||
"morecomments": false,
|
"morecomments": false,
|
||||||
"date-min": 0,
|
"date-min": 0,
|
||||||
|
@ -850,9 +850,12 @@ class DeviantartOAuthAPI():
|
|||||||
self.client_secret = extractor.config(
|
self.client_secret = extractor.config(
|
||||||
"client-secret", self.CLIENT_SECRET)
|
"client-secret", self.CLIENT_SECRET)
|
||||||
|
|
||||||
self.refresh_token = extractor.config("refresh-token")
|
token = extractor.config("refresh-token")
|
||||||
if self.refresh_token == "cache":
|
if token is None or token == "cache":
|
||||||
self.refresh_token = "#" + str(self.client_id)
|
token = "#" + str(self.client_id)
|
||||||
|
if not _refresh_token_cache(token):
|
||||||
|
token = None
|
||||||
|
self.refresh_token_key = token
|
||||||
|
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"Using %s API credentials (client-id %s)",
|
"Using %s API credentials (client-id %s)",
|
||||||
@ -952,18 +955,19 @@ class DeviantartOAuthAPI():
|
|||||||
endpoint = "user/profile/" + username
|
endpoint = "user/profile/" + username
|
||||||
return self._call(endpoint, fatal=False)
|
return self._call(endpoint, fatal=False)
|
||||||
|
|
||||||
def authenticate(self, refresh_token):
|
def authenticate(self, refresh_token_key):
|
||||||
"""Authenticate the application by requesting an access token"""
|
"""Authenticate the application by requesting an access token"""
|
||||||
self.headers["Authorization"] = self._authenticate_impl(refresh_token)
|
self.headers["Authorization"] = \
|
||||||
|
self._authenticate_impl(refresh_token_key)
|
||||||
|
|
||||||
@cache(maxage=3600, keyarg=1)
|
@cache(maxage=3600, keyarg=1)
|
||||||
def _authenticate_impl(self, refresh_token):
|
def _authenticate_impl(self, refresh_token_key):
|
||||||
"""Actual authenticate implementation"""
|
"""Actual authenticate implementation"""
|
||||||
url = "https://www.deviantart.com/oauth2/token"
|
url = "https://www.deviantart.com/oauth2/token"
|
||||||
if refresh_token:
|
if refresh_token_key:
|
||||||
self.log.info("Refreshing private access token")
|
self.log.info("Refreshing private access token")
|
||||||
data = {"grant_type": "refresh_token",
|
data = {"grant_type": "refresh_token",
|
||||||
"refresh_token": _refresh_token_cache(refresh_token)}
|
"refresh_token": _refresh_token_cache(refresh_token_key)}
|
||||||
else:
|
else:
|
||||||
self.log.info("Requesting public access token")
|
self.log.info("Requesting public access token")
|
||||||
data = {"grant_type": "client_credentials"}
|
data = {"grant_type": "client_credentials"}
|
||||||
@ -977,8 +981,9 @@ class DeviantartOAuthAPI():
|
|||||||
self.log.debug("Server response: %s", data)
|
self.log.debug("Server response: %s", data)
|
||||||
raise exception.AuthenticationError('"{}" ({})'.format(
|
raise exception.AuthenticationError('"{}" ({})'.format(
|
||||||
data.get("error_description"), data.get("error")))
|
data.get("error_description"), data.get("error")))
|
||||||
if refresh_token:
|
if refresh_token_key:
|
||||||
_refresh_token_cache.update(refresh_token, data["refresh_token"])
|
_refresh_token_cache.update(
|
||||||
|
refresh_token_key, data["refresh_token"])
|
||||||
return "Bearer " + data["access_token"]
|
return "Bearer " + data["access_token"]
|
||||||
|
|
||||||
def _call(self, endpoint, params=None, fatal=True, public=True):
|
def _call(self, endpoint, params=None, fatal=True, public=True):
|
||||||
@ -988,7 +993,7 @@ class DeviantartOAuthAPI():
|
|||||||
if self.delay >= 0:
|
if self.delay >= 0:
|
||||||
time.sleep(2 ** self.delay)
|
time.sleep(2 ** self.delay)
|
||||||
|
|
||||||
self.authenticate(None if public else self.refresh_token)
|
self.authenticate(None if public else self.refresh_token_key)
|
||||||
response = self.extractor.request(
|
response = self.extractor.request(
|
||||||
url, headers=self.headers, params=params, fatal=None)
|
url, headers=self.headers, params=params, fatal=None)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@ -1024,7 +1029,7 @@ class DeviantartOAuthAPI():
|
|||||||
|
|
||||||
if extend:
|
if extend:
|
||||||
if public and len(data["results"]) < params["limit"]:
|
if public and len(data["results"]) < params["limit"]:
|
||||||
if self.refresh_token:
|
if self.refresh_token_key:
|
||||||
self.log.debug("Switching to private access token")
|
self.log.debug("Switching to private access token")
|
||||||
public = False
|
public = False
|
||||||
continue
|
continue
|
||||||
@ -1155,9 +1160,11 @@ class DeviantartEclipseAPI():
|
|||||||
return text.rextract(page, '\\"id\\":', ',', pos)[0].strip('" ')
|
return text.rextract(page, '\\"id\\":', ',', pos)[0].strip('" ')
|
||||||
|
|
||||||
|
|
||||||
@cache(maxage=10*365*24*3600, keyarg=0)
|
@cache(maxage=100*365*24*3600, keyarg=0)
|
||||||
def _refresh_token_cache(original_token, new_token=None):
|
def _refresh_token_cache(token):
|
||||||
return new_token or original_token
|
if token and token[0] == "#":
|
||||||
|
return None
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -26,6 +26,7 @@ class OAuthBase(Extractor):
|
|||||||
def __init__(self, match):
|
def __init__(self, match):
|
||||||
Extractor.__init__(self, match)
|
Extractor.__init__(self, match)
|
||||||
self.client = None
|
self.client = None
|
||||||
|
self.cache = config.get(("extractor", self.category), "cache", True)
|
||||||
|
|
||||||
def oauth_config(self, key, default=None):
|
def oauth_config(self, key, default=None):
|
||||||
return config.interpolate(
|
return config.interpolate(
|
||||||
@ -94,6 +95,13 @@ class OAuthBase(Extractor):
|
|||||||
token_secret=data["oauth_token_secret"],
|
token_secret=data["oauth_token_secret"],
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# write to cache
|
||||||
|
if self.cache:
|
||||||
|
key = (self.subcategory, self.session.auth.consumer_key)
|
||||||
|
tokens = (data["oauth_token"], data["oauth_token_secret"])
|
||||||
|
oauth._token_cache.update(key, tokens)
|
||||||
|
self.log.info("Writing tokens to cache")
|
||||||
|
|
||||||
def _oauth2_authorization_code_grant(
|
def _oauth2_authorization_code_grant(
|
||||||
self, client_id, client_secret, auth_url, token_url,
|
self, client_id, client_secret, auth_url, token_url,
|
||||||
scope="read", key="refresh_token", auth=True,
|
scope="read", key="refresh_token", auth=True,
|
||||||
@ -162,7 +170,7 @@ class OAuthBase(Extractor):
|
|||||||
))
|
))
|
||||||
|
|
||||||
# write to cache
|
# write to cache
|
||||||
if cache and config.get(("extractor", self.category), "cache"):
|
if self.cache and cache:
|
||||||
cache.update("#" + str(client_id), data[key])
|
cache.update("#" + str(client_id), data[key])
|
||||||
self.log.info("Writing 'refresh-token' to cache")
|
self.log.info("Writing 'refresh-token' to cache")
|
||||||
|
|
||||||
@ -223,6 +231,7 @@ class OAuthReddit(OAuthBase):
|
|||||||
"https://www.reddit.com/api/v1/authorize",
|
"https://www.reddit.com/api/v1/authorize",
|
||||||
"https://www.reddit.com/api/v1/access_token",
|
"https://www.reddit.com/api/v1/access_token",
|
||||||
scope="read history",
|
scope="read history",
|
||||||
|
cache=reddit._refresh_token_cache,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,7 +222,6 @@ class RedditAPI():
|
|||||||
self.extractor = extractor
|
self.extractor = extractor
|
||||||
self.comments = text.parse_int(extractor.config("comments", 0))
|
self.comments = text.parse_int(extractor.config("comments", 0))
|
||||||
self.morecomments = extractor.config("morecomments", False)
|
self.morecomments = extractor.config("morecomments", False)
|
||||||
self.refresh_token = extractor.config("refresh-token")
|
|
||||||
self.log = extractor.log
|
self.log = extractor.log
|
||||||
|
|
||||||
client_id = extractor.config("client-id", self.CLIENT_ID)
|
client_id = extractor.config("client-id", self.CLIENT_ID)
|
||||||
@ -236,6 +235,13 @@ class RedditAPI():
|
|||||||
self.client_id = client_id
|
self.client_id = client_id
|
||||||
self.headers = {"User-Agent": user_agent}
|
self.headers = {"User-Agent": user_agent}
|
||||||
|
|
||||||
|
token = extractor.config("refresh-token")
|
||||||
|
if token is None or token == "cache":
|
||||||
|
key = "#" + self.client_id
|
||||||
|
self.refresh_token = _refresh_token_cache(key)
|
||||||
|
else:
|
||||||
|
self.refresh_token = token
|
||||||
|
|
||||||
def submission(self, submission_id):
|
def submission(self, submission_id):
|
||||||
"""Fetch the (submission, comments)=-tuple for a submission id"""
|
"""Fetch the (submission, comments)=-tuple for a submission id"""
|
||||||
endpoint = "/comments/" + submission_id + "/.json"
|
endpoint = "/comments/" + submission_id + "/.json"
|
||||||
@ -382,3 +388,10 @@ class RedditAPI():
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _decode(sid):
|
def _decode(sid):
|
||||||
return util.bdecode(sid, "0123456789abcdefghijklmnopqrstuvwxyz")
|
return util.bdecode(sid, "0123456789abcdefghijklmnopqrstuvwxyz")
|
||||||
|
|
||||||
|
|
||||||
|
@cache(maxage=100*365*24*3600, keyarg=0)
|
||||||
|
def _refresh_token_cache(token):
|
||||||
|
if token and token[0] == "#":
|
||||||
|
return None
|
||||||
|
return token
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright 2018-2019 Mike Fährmann
|
# Copyright 2018-2020 Mike Fährmann
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License version 2 as
|
# it under the terms of the GNU General Public License version 2 as
|
||||||
@ -20,6 +20,7 @@ import requests
|
|||||||
import requests.auth
|
import requests.auth
|
||||||
|
|
||||||
from . import text
|
from . import text
|
||||||
|
from .cache import cache
|
||||||
|
|
||||||
|
|
||||||
def nonce(size, alphabet=string.ascii_letters):
|
def nonce(size, alphabet=string.ascii_letters):
|
||||||
@ -117,6 +118,10 @@ class OAuth1API():
|
|||||||
token_secret = extractor.config("access-token-secret")
|
token_secret = extractor.config("access-token-secret")
|
||||||
key_type = "default" if api_key == self.API_KEY else "custom"
|
key_type = "default" if api_key == self.API_KEY else "custom"
|
||||||
|
|
||||||
|
if token is None or token == "cache":
|
||||||
|
key = (extractor.category, api_key)
|
||||||
|
token, token_secret = _token_cache(key)
|
||||||
|
|
||||||
if api_key and api_secret and token and token_secret:
|
if api_key and api_secret and token and token_secret:
|
||||||
self.log.debug("Using %s OAuth1.0 authentication", key_type)
|
self.log.debug("Using %s OAuth1.0 authentication", key_type)
|
||||||
self.session = OAuth1Session(
|
self.session = OAuth1Session(
|
||||||
@ -131,3 +136,8 @@ class OAuth1API():
|
|||||||
kwargs["fatal"] = None
|
kwargs["fatal"] = None
|
||||||
kwargs["session"] = self.session
|
kwargs["session"] = self.session
|
||||||
return self.extractor.request(url, **kwargs)
|
return self.extractor.request(url, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@cache(maxage=100*365*24*3600, keyarg=0)
|
||||||
|
def _token_cache(key):
|
||||||
|
return None, None
|
||||||
|
@ -22,8 +22,8 @@ config.set(("cache",), "file", dbpath)
|
|||||||
from gallery_dl import cache # noqa E402
|
from gallery_dl import cache # noqa E402
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
# def tearDownModule():
|
||||||
util.remove_file(dbpath)
|
# util.remove_file(dbpath)
|
||||||
|
|
||||||
|
|
||||||
class TestCache(unittest.TestCase):
|
class TestCache(unittest.TestCase):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user