1
0
mirror of https://github.com/ihabunek/toot synced 2024-12-23 23:52:40 +01:00
Toot-Mastodon-CLI-TUI-clien.../toot/cli/auth.py
2023-12-14 11:35:52 +01:00

159 lines
4.9 KiB
Python

import click
import platform
import sys
import webbrowser
from click.shell_completion import CompletionItem
from click.types import StringParamType
from toot import api, config, __version__
from toot.auth import get_or_create_app, login_auth_code, login_username_password
from toot.cli import cli
from toot.cli.validators import validate_instance
instance_option = click.option(
"--instance", "-i", "base_url",
prompt="Enter instance URL",
default="https://mastodon.social",
callback=validate_instance,
help="""Domain or base URL of the instance to log into,
e.g. 'mastodon.social' or 'https://mastodon.social'""",
)
class AccountParamType(StringParamType):
"""Custom type to add shell completion for account names"""
def shell_complete(self, ctx, param, incomplete: str):
accounts = config.load_config()["users"].keys()
return [
CompletionItem(a)
for a in accounts
if a.lower().startswith(incomplete.lower())
]
@cli.command()
def auth():
"""Show logged in accounts and instances"""
config_data = config.load_config()
if not config_data["users"]:
click.echo("You are not logged in to any accounts")
return
active_user = config_data["active_user"]
click.echo("Authenticated accounts:")
for uid, u in config_data["users"].items():
active_label = "ACTIVE" if active_user == uid else ""
uid = click.style(uid, fg="green")
active_label = click.style(active_label, fg="yellow")
click.echo(f"* {uid} {active_label}")
path = config.get_config_file_path()
path = click.style(path, "blue")
click.echo(f"\nAuth tokens are stored in: {path}")
@cli.command()
def env():
"""Print environment information for inclusion in bug reports."""
click.echo(f"toot {__version__}")
click.echo(f"Python {sys.version}")
click.echo(platform.platform())
@cli.command(name="login_cli")
@instance_option
@click.option("--email", "-e", help="Email address to log in with", prompt=True)
@click.option("--password", "-p", hidden=True, prompt=True, hide_input=True)
def login_cli(base_url: str, email: str, password: str):
"""
Log into an instance from the console (not recommended)
Does NOT support two factor authentication, may not work on instances
other than Mastodon, mostly useful for scripting.
"""
app = get_or_create_app(base_url)
login_username_password(app, email, password)
click.secho("✓ Successfully logged in.", fg="green")
click.echo("Access token saved to config at: ", nl=False)
click.secho(config.get_config_file_path(), fg="green")
LOGIN_EXPLANATION = """This authentication method requires you to log into your
Mastodon instance in your browser, where you will be asked to authorize toot to
access your account. When you do, you will be given an authorization code which
you need to paste here.""".replace("\n", " ")
@cli.command()
@instance_option
def login(base_url: str):
"""Log into an instance using your browser (recommended)"""
app = get_or_create_app(base_url)
url = api.get_browser_login_url(app)
click.echo(click.wrap_text(LOGIN_EXPLANATION))
click.echo("\nLogin URL:")
click.echo(url)
yesno = click.prompt("Open link in default browser? [Y/n]", default="Y", show_default=False)
if not yesno or yesno.lower() == 'y':
webbrowser.open(url)
authorization_code = ""
while not authorization_code:
authorization_code = click.prompt("Authorization code")
login_auth_code(app, authorization_code)
click.echo()
click.secho("✓ Successfully logged in.", fg="green")
@cli.command()
@click.argument("account", type=AccountParamType(), required=False)
def logout(account: str):
"""Log out of ACCOUNT, delete stored access keys"""
accounts = _get_accounts_list()
if not account:
raise click.ClickException(f"Specify account to log out:\n{accounts}")
user = config.load_user(account)
if not user:
raise click.ClickException(f"Account not found. Logged in accounts:\n{accounts}")
config.delete_user(user)
click.secho(f"✓ Account {account} logged out", fg="green")
@cli.command()
@click.argument("account", type=AccountParamType(), required=False)
def activate(account: str):
"""Switch to logged in ACCOUNT."""
accounts = _get_accounts_list()
if not account:
raise click.ClickException(f"Specify account to activate:\n{accounts}")
user = config.load_user(account)
if not user:
raise click.ClickException(f"Account not found. Logged in accounts:\n{accounts}")
config.activate_user(user)
click.secho(f"✓ Account {account} activated", fg="green")
def _get_accounts_list() -> str:
accounts = config.load_config()["users"].keys()
if not accounts:
raise click.ClickException("You're not logged into any accounts")
return "\n".join([f"* {acct}" for acct in accounts])