Add account creation

This commit is contained in:
Lorenz Diener 2019-04-28 17:56:20 +02:00
pare a29d278bf9
commit b6692f0b16
S'han modificat 3 arxius amb 206 adicions i 3 eliminacions

Veure arxiu

@ -477,6 +477,70 @@ class Mastodon:
return response['access_token'] return response['access_token']
@api_version("2.7.0", "2.7.0", "2.7.0")
def create_account(self, username, password, email, agreement=False, locale="en", scopes=__DEFAULT_SCOPES, to_file=None):
"""
Creates a new user account with the given username, password and email. "agreement"
must be set to true (after showing the user the instances user agreement and having
them agree to it), "locale" specifies the language for the confirmation e-mail as an
ISO 639-1 (two-letter) language code.
Does not require an access token, but does require a client grant.
By default, this method is rate-limited by IP to 5 requests per 30 minutes.
Returns an access token (just like log_in), which it can also persist to to_file,
and sets it internally so that the user is now logged in. Note that this token
can only be used after the user has confirmed their e-mail.
"""
params = self.__generate_params(locals(), ['to_file', 'scopes'])
params['client_id'] = self.client_id
params['client_secret'] = self.client_secret
if agreement == False:
del params_initial['agreement']
# Step 1: Get a user-free token via oauth
try:
oauth_params = {}
oauth_params['scope'] = " ".join(scopes)
oauth_params['client_id'] = self.client_id
oauth_params['client_secret'] = self.client_secret
oauth_params['grant_type'] = 'client_credentials'
response = self.__api_request('POST', '/oauth/token', oauth_params, do_ratelimiting=False)
temp_access_token = response['access_token']
except Exception as e:
raise MastodonIllegalArgumentError('Invalid request during oauth phase: %s' % e)
# Step 2: Use that to create a user
try:
response = self.__api_request('POST', '/api/v1/accounts', params, do_ratelimiting=False,
access_token_override = temp_access_token)
self.access_token = response['access_token']
self.__set_refresh_token(response.get('refresh_token'))
self.__set_token_expired(int(response.get('expires_in', 0)))
except Exception as e:
raise MastodonIllegalArgumentError('Invalid request: %s' % e)
# Step 3: Check scopes, persist, et cetera
received_scopes = response["scope"].split(" ")
for scope_set in self.__SCOPE_SETS.keys():
if scope_set in received_scopes:
received_scopes += self.__SCOPE_SETS[scope_set]
if not set(scopes) <= set(received_scopes):
raise MastodonAPIError(
'Granted scopes "' + " ".join(received_scopes) + '" do not contain all of the requested scopes "' + " ".join(scopes) + '".')
if to_file is not None:
with open(to_file, 'w') as token_file:
token_file.write(response['access_token'] + '\n')
self.__logged_in_id = None
return response['access_token']
### ###
# Reading data: Instances # Reading data: Instances
### ###
@ -2287,7 +2351,7 @@ class Mastodon:
json_object = Mastodon.__json_allow_dict_attrs(json_object) json_object = Mastodon.__json_allow_dict_attrs(json_object)
return json_object return json_object
def __api_request(self, method, endpoint, params={}, files={}, headers={}, do_ratelimiting=True): def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, do_ratelimiting=True):
""" """
Internal API request helper. Internal API request helper.
""" """
@ -2314,8 +2378,10 @@ class Mastodon:
# Generate request headers # Generate request headers
headers = copy.deepcopy(headers) headers = copy.deepcopy(headers)
if self.access_token is not None: if not self.access_token is None:
headers['Authorization'] = 'Bearer ' + self.access_token headers['Authorization'] = 'Bearer ' + self.access_token
if not access_token_override is None:
headers['Authorization'] = 'Bearer ' + access_token_override
if self.debug_requests: if self.debug_requests:
print('Mastodon: Request to endpoint "' + endpoint + '" using method "' + method + '".') print('Mastodon: Request to endpoint "' + endpoint + '" using method "' + method + '".')

Veure arxiu

@ -0,0 +1,116 @@
interactions:
- request:
body: client_name=mastodon.py+generated+test+app&scopes=read+write+follow+push&redirect_uris=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [python-requests/2.18.4]
method: POST
uri: http://localhost:3000/api/v1/apps
response:
body: {string: '{"id":"17","name":"mastodon.py generated test app","website":null,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":"6b8b07698f3f57d73b0fd779fac2fcc64d6e852d3334425a6b50b53bc32db986","client_secret":"df903d79cc8a27d8d4f9aa8213cf65a9681fea679a56643fcb3e5a3f66c4f9c7","vapid_key":"BCryMB_mKFcSpmXE3kJ1Ri3ZFVdBLjRsX54VYhE21BMyftx8k67qWxFs2OCuQCtj0k1ILESkQhGuOKJcQnodx4g="}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Type: [application/json; charset=utf-8]
ETag: [W/"3a06e1b620ce8b2c3ce4045d1e2c179b"]
Referrer-Policy: [strict-origin-when-cross-origin]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Content-Type-Options: [nosniff]
X-Download-Options: [noopen]
X-Frame-Options: [SAMEORIGIN]
X-Permitted-Cross-Domain-Policies: [none]
X-Request-Id: [258ec6bb-4a82-41c3-b55b-560fdfb5ad13]
X-Runtime: ['0.025005']
X-XSS-Protection: [1; mode=block]
content-length: ['374']
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.18.4]
method: GET
uri: http://localhost:3000/api/v1/instance/
response:
body: {string: '{"uri":"localhost","title":"Mastodon","description":"","email":"","version":"2.8.0","urls":{"streaming_api":"ws://localhost:4000"},"stats":{"user_count":2,"status_count":15,"domain_count":0},"thumbnail":"http://localhost/packs/media/images/preview-9a17d32fc48369e8ccd910a75260e67d.jpg","languages":["en"],"registrations":true,"contact_account":null}'}
headers:
Cache-Control: ['max-age=300, public']
Content-Type: [application/json; charset=utf-8]
Date: ['Sun, 28 Apr 2019 15:56:05 GMT']
ETag: [W/"4ba18203af6a9d9402c05e8ffc21ac45"]
Referrer-Policy: [strict-origin-when-cross-origin]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Content-Type-Options: [nosniff]
X-Download-Options: [noopen]
X-Frame-Options: [SAMEORIGIN]
X-Permitted-Cross-Domain-Policies: [none]
X-Request-Id: [bfb330d1-53c3-4fcc-b79e-30e256a2f845]
X-Runtime: ['0.025590']
X-XSS-Protection: [1; mode=block]
content-length: ['349']
status: {code: 200, message: OK}
- request:
body: scope=read+write+follow+push&client_id=6b8b07698f3f57d73b0fd779fac2fcc64d6e852d3334425a6b50b53bc32db986&client_secret=df903d79cc8a27d8d4f9aa8213cf65a9681fea679a56643fcb3e5a3f66c4f9c7&grant_type=client_credentials
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['212']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [python-requests/2.18.4]
method: POST
uri: http://localhost:3000/oauth/token
response:
body: {string: '{"access_token":"756276912d1d5d74cfbf2e275517a234cee584891ca1f87a3fb2bdbd1fed1ca1","token_type":"Bearer","scope":"read
write follow push","created_at":1556466966}'}
headers:
Cache-Control: ['private, no-store']
Content-Type: [application/json; charset=utf-8]
ETag: [W/"851cc791f71c2ce9bf821e023e26b361"]
Pragma: [no-cache]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Request-Id: [0205c26b-9c77-4c32-bb59-a2d69eafde33]
X-Runtime: ['0.039194']
content-length: ['162']
status: {code: 200, message: OK}
- request:
body: locale=en&agreement=1&email=email%40localhost11707&password=swordfish&username=coolguy11707&client_id=6b8b07698f3f57d73b0fd779fac2fcc64d6e852d3334425a6b50b53bc32db986&client_secret=df903d79cc8a27d8d4f9aa8213cf65a9681fea679a56643fcb3e5a3f66c4f9c7
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: [Bearer 756276912d1d5d74cfbf2e275517a234cee584891ca1f87a3fb2bdbd1fed1ca1]
Connection: [keep-alive]
Content-Length: ['245']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [python-requests/2.18.4]
method: POST
uri: http://localhost:3000/api/v1/accounts
response:
body: {string: '{"access_token":"b20f513163b154065d17f5aff37b779f51d13c152fe6e7d1be366d64d2e74e39","token_type":"Bearer","scope":"read
write follow push","created_at":1556466966}'}
headers:
Cache-Control: ['private, no-store']
Content-Type: [application/json; charset=utf-8]
ETag: [W/"7ba50c22bb330ba5a0936c19bea78093"]
Pragma: [no-cache]
Referrer-Policy: [strict-origin-when-cross-origin]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Content-Type-Options: [nosniff]
X-Download-Options: [noopen]
X-Frame-Options: [SAMEORIGIN]
X-Permitted-Cross-Domain-Policies: [none]
X-Request-Id: [51ac5a4c-e850-46be-89c6-681e46f891dd]
X-Runtime: ['0.208778']
X-XSS-Protection: [1; mode=block]
content-length: ['162']
status: {code: 200, message: OK}
version: 1

Veure arxiu

@ -1,6 +1,8 @@
from mastodon import Mastodon from mastodon import Mastodon
import pytest import pytest
import requests import requests
import time
try: try:
from mock import Mock from mock import Mock
except ImportError: except ImportError:
@ -46,3 +48,22 @@ def test_app_verify_credentials(api):
app = api.app_verify_credentials() app = api.app_verify_credentials()
assert app assert app
assert app.name == 'Mastodon.py test suite' assert app.name == 'Mastodon.py test suite'
@pytest.mark.vcr()
def test_app_account_create():
# This leaves behind stuff on the test server, which is unfortunate, but eh.
suffix = str(time.time()).replace(".", "")[-5:]
test_app = test_app = Mastodon.create_app(
"mastodon.py generated test app",
api_base_url="http://localhost:3000/"
)
test_app_api = Mastodon(
test_app[0],
test_app[1],
api_base_url="http://localhost:3000/"
)
test_token = test_app_api.create_account("coolguy" + suffix, "swordfish", "email@localhost" + suffix, agreement=True)
assert test_token