From 082ce35dda3ece598e215723435ef2a46ef573b7 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Mon, 4 Mar 2019 17:29:20 +0100
Subject: [PATCH] More flexible config

---
 funkwhale_cli/api.py        | 12 ++++++++
 funkwhale_cli/cli.py        | 56 ++++++++++++++++++++++++++++++++-----
 funkwhale_cli/config.py     | 10 +++++++
 funkwhale_cli/exceptions.py |  4 +++
 setup.cfg                   |  3 ++
 tests/test_config.py        | 10 +++++++
 6 files changed, 88 insertions(+), 7 deletions(-)
 create mode 100644 funkwhale_cli/config.py
 create mode 100644 tests/test_config.py

diff --git a/funkwhale_cli/api.py b/funkwhale_cli/api.py
index 3d9e92d..1227802 100644
--- a/funkwhale_cli/api.py
+++ b/funkwhale_cli/api.py
@@ -43,3 +43,15 @@ def clean_nodeinfo(data):
     schema = schemas.NodeInfo2Schema()
     result = schema.load(data)
     return result.data
+
+
+async def get_jwt_token(session, url, username, password):
+    url = f"{url}/api/v1/token/"
+    response = await session.post(
+        url, data={"username": username, "password": password}
+    )
+    if response.status == 400:
+        raise exceptions.AuthenticationError(
+            "Unable to log in with provided credentials"
+        )
+    return (await response.json())["token"]
diff --git a/funkwhale_cli/cli.py b/funkwhale_cli/cli.py
index a5eb15c..0026ca1 100644
--- a/funkwhale_cli/cli.py
+++ b/funkwhale_cli/cli.py
@@ -3,6 +3,7 @@ import aiohttp
 import click
 import click_log
 import functools
+import keyring
 import logging
 import urllib.parse
 import json
@@ -36,11 +37,10 @@ def async_command(f):
     return functools.update_wrapper(wrapper, f)
 
 
-@click.group()
-@click.option("-H", "--url", envvar="FUNKWHALE_SERVER_URL", type=URL)
-@click_log.simple_verbosity_option(logs.logger, expose_value=True)
-@click.pass_context
-def cli(ctx, url, verbosity):
+SERVER_DECORATOR = click.option("-H", "--url", envvar="FUNKWHALE_SERVER_URL", type=URL)
+
+
+def set_server(ctx, url):
     ctx.ensure_object(dict)
     ctx.obj["SERVER_URL"] = url
     parsed = urllib.parse.urlparse(url)
@@ -48,17 +48,59 @@ def cli(ctx, url, verbosity):
     ctx.obj["SERVER_PROTOCOL"] = parsed.scheme
 
 
+@click.group()
+@SERVER_DECORATOR
+@click_log.simple_verbosity_option(logs.logger, expose_value=True)
+@click.pass_context
+def cli(ctx, url, verbosity):
+    set_server(ctx, url)
+
+
+@cli.command()
+@SERVER_DECORATOR
+@click.option("-u", "--username", envvar="FUNKWHALE_USERNAME", prompt=True)
+@click.option(
+    "-p", "--password", envvar="FUNKWHALE_PASSWORD", prompt=True, hide_input=True
+)
+@click.pass_context
+@async_command
+async def login(ctx, url, username, password):
+    set_server(ctx, url)
+    async with api.get_session() as session:
+        token = await api.get_jwt_token(
+            session, ctx.obj["SERVER_URL"], username=username, password=password
+        )
+
+    keyring.set_password(ctx.obj["SERVER_URL"], username, token)
+    click.echo("Login successfull!")
+
+
+@cli.command()
+@SERVER_DECORATOR
+@click.option("-u", "--username", envvar="FUNKWHALE_USERNAME", prompt=True)
+@click.pass_context
+@async_command
+async def logout(ctx, url, username):
+    set_server(ctx, url)
+    keyring.delete_password(ctx.obj["SERVER_URL"], username)
+    click.echo("Logout successfull!")
+
+
 @cli.group()
+@SERVER_DECORATOR
 @click.pass_context
-def server(ctx):
+def server(ctx, url):
+    set_server(ctx, url)
     ctx.ensure_object(dict)
 
 
 @server.command()
+@SERVER_DECORATOR
 @click.option("--raw", is_flag=True)
 @click.pass_context
 @async_command
-async def info(ctx, raw):
+async def info(ctx, url, raw):
+    set_server(ctx, url)
     async with api.get_session() as session:
         nodeinfo = await api.fetch_nodeinfo(
             session,
diff --git a/funkwhale_cli/config.py b/funkwhale_cli/config.py
new file mode 100644
index 0000000..1107491
--- /dev/null
+++ b/funkwhale_cli/config.py
@@ -0,0 +1,10 @@
+import appdirs
+import keyring
+
+
+def get_app_dirs():
+    return appdirs.AppDirs("funkwhale", "funkwhale")
+
+
+def set_password(key, user, password):
+    keyring.set_password(key, user, password)
diff --git a/funkwhale_cli/exceptions.py b/funkwhale_cli/exceptions.py
index 4a4096f..6a775f6 100644
--- a/funkwhale_cli/exceptions.py
+++ b/funkwhale_cli/exceptions.py
@@ -4,3 +4,7 @@ class FunkwhaleError(Exception):
 
 class NoNodeInfo(FunkwhaleError):
     pass
+
+
+class AuthenticationError(FunkwhaleError):
+    pass
diff --git a/setup.cfg b/setup.cfg
index 96330ec..16402bc 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -21,9 +21,12 @@ packages = find:
 install_requires =
     aiofiles
     aiohttp
+    appdirs
     click
     click-log
+    keyring
     marshmallow
+    python-dotenv
     semver
 
 [options.entry_points]
diff --git a/tests/test_config.py b/tests/test_config.py
new file mode 100644
index 0000000..d54cf7d
--- /dev/null
+++ b/tests/test_config.py
@@ -0,0 +1,10 @@
+import appdirs
+
+from funkwhale_cli import config
+
+
+def test_get_app_dir():
+    assert (
+        config.get_app_dirs().user_config_dir
+        == appdirs.AppDirs("funkwhale", "funkwhale").user_config_dir
+    )
-- 
GitLab