aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenz Diener <[email protected]>2019-10-12 18:58:46 +0200
committerLorenz Diener <[email protected]>2019-10-12 18:58:46 +0200
commitca45cd65aa1e404164d318f1b5453de6e8b5cc6d (patch)
tree606a65e4b5585a778d9d1a4df9118dc02b403f11
parent5c4916bd813e205d7fc5cd7f44324b0cecdc4940 (diff)
downloadmastodon.py-ca45cd65aa1e404164d318f1b5453de6e8b5cc6d.tar.gz
Add ability to persist base urls with clientid/secret/token (fixes #200)
-rw-r--r--mastodon/Mastodon.py52
-rw-r--r--tests/test_auth.py30
-rw-r--r--tests/test_create_app.py2
3 files changed, 65 insertions, 19 deletions
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py
index 465beb7..c84ac6a 100644
--- a/mastodon/Mastodon.py
+++ b/mastodon/Mastodon.py
@@ -267,16 +267,17 @@ class Mastodon:
267 267
268 if to_file is not None: 268 if to_file is not None:
269 with open(to_file, 'w') as secret_file: 269 with open(to_file, 'w') as secret_file:
270 secret_file.write(response['client_id'] + '\n') 270 secret_file.write(response['client_id'] + "\n")
271 secret_file.write(response['client_secret'] + '\n') 271 secret_file.write(response['client_secret'] + "\n")
272 272 secret_file.write(api_base_url + "\n")
273
273 return (response['client_id'], response['client_secret']) 274 return (response['client_id'], response['client_secret'])
274 275
275 ### 276 ###
276 # Authentication, including constructor 277 # Authentication, including constructor
277 ### 278 ###
278 def __init__(self, client_id=None, client_secret=None, access_token=None, 279 def __init__(self, client_id=None, client_secret=None, access_token=None,
279 api_base_url=__DEFAULT_BASE_URL, debug_requests=False, 280 api_base_url=None, debug_requests=False,
280 ratelimit_method="wait", ratelimit_pacefactor=1.1, 281 ratelimit_method="wait", ratelimit_pacefactor=1.1,
281 request_timeout=__DEFAULT_TIMEOUT, mastodon_version=None, 282 request_timeout=__DEFAULT_TIMEOUT, mastodon_version=None,
282 version_check_mode = "created", session=None): 283 version_check_mode = "created", session=None):
@@ -285,9 +286,12 @@ class Mastodon:
285 give a `client_id` and it is not a file, you must also give a secret. If you specify an 286 give a `client_id` and it is not a file, you must also give a secret. If you specify an
286 `access_token` then you don't need to specify a `client_id`. It is allowed to specify 287 `access_token` then you don't need to specify a `client_id`. It is allowed to specify
287 neither - in this case, you will be restricted to only using endpoints that do not 288 neither - in this case, you will be restricted to only using endpoints that do not
288 require authentication. 289 require authentication. If a file is given as `client_id`, client ID, secret and
290 base url are read from that file.
289 291
290 You can also specify an `access_token`, directly or as a file (as written by `log_in()`_). 292 You can also specify an `access_token`, directly or as a file (as written by `log_in()`_). If
293 a file is given, Mastodon.py also tries to load the base URL from this file, if present. A
294 client id and secret are not required in this case.
291 295
292 Mastodon.py can try to respect rate limits in several ways, controlled by `ratelimit_method`. 296 Mastodon.py can try to respect rate limits in several ways, controlled by `ratelimit_method`.
293 "throw" makes functions throw a `MastodonRatelimitError` when the rate 297 "throw" makes functions throw a `MastodonRatelimitError` when the rate
@@ -298,8 +302,9 @@ class Mastodon:
298 even in "wait" and "pace" mode, requests can still fail due to network or other problems! Also 302 even in "wait" and "pace" mode, requests can still fail due to network or other problems! Also
299 note that "pace" and "wait" are NOT thread safe. 303 note that "pace" and "wait" are NOT thread safe.
300 304
301 Specify `api_base_url` if you wish to talk to an instance other than the flagship one. 305 Specify `api_base_url` if you wish to talk to an instance other than the flagship one. When
302 If a file is given as `client_id`, client ID and secret are read from that file. 306 reading from client id or access token files as written by Mastodon.py 1.5.0 or larger,
307 this can be omitted.
303 308
304 By default, a timeout of 300 seconds is used for all requests. If you wish to change this, 309 By default, a timeout of 300 seconds is used for all requests. If you wish to change this,
305 pass the desired timeout (in seconds) as `request_timeout`. 310 pass the desired timeout (in seconds) as `request_timeout`.
@@ -317,7 +322,10 @@ class Mastodon:
317 changed after the version of Mastodon that is connected has been released. If it is set to "none", 322 changed after the version of Mastodon that is connected has been released. If it is set to "none",
318 version checking is disabled. 323 version checking is disabled.
319 """ 324 """
320 self.api_base_url = Mastodon.__protocolize(api_base_url) 325 self.api_base_url = None
326 if not api_base_url is None:
327 self.api_base_url = Mastodon.__protocolize(api_base_url)
328
321 self.client_id = client_id 329 self.client_id = client_id
322 self.client_secret = client_secret 330 self.client_secret = client_secret
323 self.access_token = access_token 331 self.access_token = access_token
@@ -364,6 +372,13 @@ class Mastodon:
364 with open(self.client_id, 'r') as secret_file: 372 with open(self.client_id, 'r') as secret_file:
365 self.client_id = secret_file.readline().rstrip() 373 self.client_id = secret_file.readline().rstrip()
366 self.client_secret = secret_file.readline().rstrip() 374 self.client_secret = secret_file.readline().rstrip()
375
376 try_base_url = secret_file.readline().rstrip()
377 if (not try_base_url is None) and len(try_base_url) != 0:
378 try_base_url = Mastodon.__protocolize(try_base_url)
379 if not (self.api_base_url is None or try_base_url == self.api_base_url):
380 raise MastodonIllegalArgumentError('Mismatch in base URLs between files and/or specified')
381 self.api_base_url = try_base_url
367 else: 382 else:
368 if self.client_secret is None: 383 if self.client_secret is None:
369 raise MastodonIllegalArgumentError('Specified client id directly, but did not supply secret') 384 raise MastodonIllegalArgumentError('Specified client id directly, but did not supply secret')
@@ -371,7 +386,14 @@ class Mastodon:
371 if self.access_token is not None and os.path.isfile(self.access_token): 386 if self.access_token is not None and os.path.isfile(self.access_token):
372 with open(self.access_token, 'r') as token_file: 387 with open(self.access_token, 'r') as token_file:
373 self.access_token = token_file.readline().rstrip() 388 self.access_token = token_file.readline().rstrip()
374 389
390 try_base_url = token_file.readline().rstrip()
391 if (not try_base_url is None) and len(try_base_url) != 0:
392 try_base_url = Mastodon.__protocolize(try_base_url)
393 if not (self.api_base_url is None or try_base_url == self.api_base_url):
394 raise MastodonIllegalArgumentError('Mismatch in base URLs between files and/or specified')
395 self.api_base_url = try_base_url
396
375 def retrieve_mastodon_version(self): 397 def retrieve_mastodon_version(self):
376 """ 398 """
377 Determine installed mastodon version and set major, minor and patch (not including RC info) accordingly. 399 Determine installed mastodon version and set major, minor and patch (not including RC info) accordingly.
@@ -508,8 +530,9 @@ class Mastodon:
508 530
509 if to_file is not None: 531 if to_file is not None:
510 with open(to_file, 'w') as token_file: 532 with open(to_file, 'w') as token_file:
511 token_file.write(response['access_token'] + '\n') 533 token_file.write(response['access_token'] + "\n")
512 534 token_file.write(self.api_base_url + "\n")
535
513 self.__logged_in_id = None 536 self.__logged_in_id = None
514 537
515 return response['access_token'] 538 return response['access_token']
@@ -572,8 +595,9 @@ class Mastodon:
572 595
573 if to_file is not None: 596 if to_file is not None:
574 with open(to_file, 'w') as token_file: 597 with open(to_file, 'w') as token_file:
575 token_file.write(response['access_token'] + '\n') 598 token_file.write(response['access_token'] + "\n")
576 599 token_file.write(self.api_base_url + "\n")
600
577 self.__logged_in_id = None 601 self.__logged_in_id = None
578 602
579 return response['access_token'] 603 return response['access_token']
diff --git a/tests/test_auth.py b/tests/test_auth.py
index b4a004e..fbf8974 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -30,7 +30,6 @@ def test_log_in_password(api_anonymous):
30 password='mastodonadmin') 30 password='mastodonadmin')
31 assert token 31 assert token
32 32
33
34@pytest.mark.vcr() 33@pytest.mark.vcr()
35def test_log_in_password_incorrect(api_anonymous): 34def test_log_in_password_incorrect(api_anonymous):
36 with pytest.raises(MastodonIllegalArgumentError): 35 with pytest.raises(MastodonIllegalArgumentError):
@@ -38,7 +37,6 @@ def test_log_in_password_incorrect(api_anonymous):
38 username='admin@localhost', 37 username='admin@localhost',
39 password='hunter2') 38 password='hunter2')
40 39
41
42@pytest.mark.vcr() 40@pytest.mark.vcr()
43def test_log_in_password_to_file(api_anonymous, tmpdir): 41def test_log_in_password_to_file(api_anonymous, tmpdir):
44 filepath = tmpdir.join('token') 42 filepath = tmpdir.join('token')
@@ -46,18 +44,42 @@ def test_log_in_password_to_file(api_anonymous, tmpdir):
46 username='admin@localhost', 44 username='admin@localhost',
47 password='mastodonadmin', 45 password='mastodonadmin',
48 to_file=str(filepath)) 46 to_file=str(filepath))
49 token = filepath.read_text('UTF-8').rstrip() 47 token = filepath.read_text('UTF-8').rstrip().split("\n")[0]
50 assert token 48 assert token
51 api = api_anonymous 49 api = api_anonymous
52 api.access_token = token 50 api.access_token = token
53 assert api.account_verify_credentials() 51 assert api.account_verify_credentials()
54 52
53@pytest.mark.vcr()
54def test_url_errors(tmpdir):
55 clientid_good = tmpdir.join("clientid")
56 token_good = tmpdir.join("token")
57 clientid_bad = tmpdir.join("clientid_bad")
58 token_bad = tmpdir.join("token_bad")
59
60 clientid_good.write_text("foo\nbar\nhttps://zombo.com\n", "UTF-8")
61 token_good.write_text("foo\nhttps://zombo.com\n", "UTF-8")
62 clientid_bad.write_text("foo\nbar\nhttps://evil.org\n", "UTF-8")
63 token_bad.write_text("foo\nhttps://evil.org\n", "UTF-8")
64
65 api = Mastodon(client_id = clientid_good, access_token = token_good)
66 assert api
67 assert api.api_base_url == "https://zombo.com"
68 assert Mastodon(client_id = clientid_good, access_token = token_good, api_base_url = "zombo.com")
69
70 with pytest.raises(MastodonIllegalArgumentError):
71 Mastodon(client_id = clientid_good, access_token = token_bad, api_base_url = "zombo.com")
72
73 with pytest.raises(MastodonIllegalArgumentError):
74 Mastodon(client_id = clientid_bad, access_token = token_good, api_base_url = "zombo.com")
75
76 with pytest.raises(MastodonIllegalArgumentError):
77 Mastodon(client_id = clientid_bad, access_token = token_bad, api_base_url = "zombo.com")
55 78
56@pytest.mark.skip(reason="Not sure how to test this without setting up selenium or a similar browser automation suite to click on the allow button") 79@pytest.mark.skip(reason="Not sure how to test this without setting up selenium or a similar browser automation suite to click on the allow button")
57def test_log_in_code(api_anonymous): 80def test_log_in_code(api_anonymous):
58 pass 81 pass
59 82
60
61@pytest.mark.skip(reason="Not supported by Mastodon >:@ (yet?)") 83@pytest.mark.skip(reason="Not supported by Mastodon >:@ (yet?)")
62def test_log_in_refresh(api_anonymous): 84def test_log_in_refresh(api_anonymous):
63 pass 85 pass
diff --git a/tests/test_create_app.py b/tests/test_create_app.py
index b9de298..8260751 100644
--- a/tests/test_create_app.py
+++ b/tests/test_create_app.py
@@ -31,7 +31,7 @@ def test_create_app(mocker, to_file=None, redirect_uris=None, website=None):
31def test_create_app_to_file(mocker, tmpdir): 31def test_create_app_to_file(mocker, tmpdir):
32 filepath = tmpdir.join('credentials') 32 filepath = tmpdir.join('credentials')
33 test_create_app(mocker, to_file=str(filepath)) 33 test_create_app(mocker, to_file=str(filepath))
34 assert filepath.read_text('UTF-8') == "foo\nbar\n" 34 assert filepath.read_text('UTF-8') == "foo\nbar\nhttps://example.com\n"
35 35
36def test_create_app_redirect_uris(mocker): 36def test_create_app_redirect_uris(mocker):
37 test_create_app(mocker, redirect_uris='http://example.net') 37 test_create_app(mocker, redirect_uris='http://example.net')
Powered by cgit v1.2.3 (git 2.41.0)