def create_user(app, access_token): # Username is not yet known at this point, so fetch it from Mastodon user = User(app.instance, None, access_token) creds = api.verify_credentials(app, user) user = User(app.instance, creds['username'], access_token) config.save_user(user, activate=True) print_out("Access token saved to config at: <green>{}</green>".format( config.get_config_file_path())) return user
def create_user(app, email, access_token): user = User(app.instance, email, access_token) path = config.save_user(user) print_out("Access token saved to: <green>{}</green>".format(path)) return user
def load_user(user_id, throw=False): config = load_config() if user_id in config['users']: return User(**config['users'][user_id]) if throw: raise ConsoleError("User '{}' not found".format(user_id))
def extract_user_app(config, user_id): if user_id not in config['users']: return None, None user_data = config['users'][user_id] instance = user_data['instance'] if instance not in config['apps']: return None, None app_data = config['apps'][instance] return User(**user_data), App(**app_data)
def two_factor_login_interactive(app): """Hacky implementation of two factor authentication""" print_out("Log in to {}".format(app.instance)) email = input('Email: ') password = getpass('Password: '******'/auth/sign_in' session = requests.Session() # Fetch sign in form response = session.get(sign_in_url) response.raise_for_status() soup = BeautifulSoup(response.content, "html.parser") form = soup.find('form') inputs = form.find_all('input') data = {i.attrs.get('name'): i.attrs.get('value') for i in inputs} data['user[email]'] = email data['user[password]'] = password # Submit form, get 2FA entry form response = session.post(sign_in_url, data) response.raise_for_status() soup = BeautifulSoup(response.content, "html.parser") form = soup.find('form') inputs = form.find_all('input') data = {i.attrs.get('name'): i.attrs.get('value') for i in inputs} data['user[otp_attempt]'] = input("2FA Token: ") # Submit token response = session.post(sign_in_url, data) response.raise_for_status() # Extract access token from response soup = BeautifulSoup(response.content, "html.parser") initial_state = soup.find('script', id='initial-state') if not initial_state: raise ConsoleError("Login failed: Invalid 2FA token?") data = json.loads(initial_state.get_text()) access_token = data['meta']['access_token'] user = User(app.instance, email, access_token) path = config.save_user(user) print_out("Access token saved to: <green>{}</green>".format(path))
def login(app, username, password): url = app.base_url + '/oauth/token' response = requests.post(url, { 'grant_type': 'password', 'client_id': app.client_id, 'client_secret': app.client_secret, 'username': username, 'password': password, 'scope': SCOPES, }) response.raise_for_status() data = response.json() access_token = data.get('access_token') return User(username, access_token)
def login_interactive(app): print("\nLog in to " + green(app.instance)) email = input('Email: ') password = getpass('Password: '******'access_token']) path = config.save_user(user) print("Access token saved to: " + green(path)) return user
# -*- coding: utf-8 -*- import io import pytest import re from collections import namedtuple from unittest import mock from toot import console, User, App, http from toot.exceptions import ConsoleError from tests.utils import MockResponse app = App('habunek.com', 'https://habunek.com', 'foo', 'bar') user = User('habunek.com', '*****@*****.**', 'xxx') MockUuid = namedtuple("MockUuid", ["hex"]) def uncolorize(text): """Remove ANSI color sequences from a string""" return re.sub(r'\x1b[^m]*m', '', text) def test_print_usage(capsys): console.print_usage() out, err = capsys.readouterr() assert "toot - a Mastodon CLI client" in out @mock.patch('uuid.uuid4')