Skip to content

Commit

Permalink
Merge pull request #129 from JOJ0/raw_cmd_and_common_opts
Browse files Browse the repository at this point in the history
Add `raw` command, a new module for common CLI options, and improve `matrix raw` help
  • Loading branch information
JOJ0 authored Oct 14, 2023
2 parents be77cef + 6e52699 commit 7da00fa
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 44 deletions.
1 change: 1 addition & 0 deletions doc/source/index_cli_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Command Line Reference
synadm.cli.matrix
synadm.cli.regtok
synadm.cli.notice
synadm.cli.raw
6 changes: 6 additions & 0 deletions doc/source/synadm.cli.raw.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Raw
====

.. click:: synadm.cli.raw:raw_request_cmd
:prog: synadm raw
:nested: full
14 changes: 14 additions & 0 deletions synadm/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1370,3 +1370,17 @@ def notice_send(self, receivers, content_plain, content_html, paginate,
else:
data["user_id"] = receivers
return [self.query("post", "v1/send_server_notice", data=data)]

def raw_request(self, endpoint, method, data):
data_dict = {}
if method != "get":
self.log.debug("The data we are trying to parse and submit:")
self.log.debug(data)
try: # user provided json might be crap
data_dict = json.loads(data)
except Exception as error:
self.log.error("loading data: %s: %s",
type(error).__name__, error)
return None

return self.query(method, endpoint, data=data_dict)
2 changes: 1 addition & 1 deletion synadm/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,4 +505,4 @@ def version(helper):


# Import additional commands
from synadm.cli import room, user, media, group, history, matrix, regtok, notice # noqa: F401, E402, E501
from synadm.cli import room, user, media, group, history, matrix, regtok, notice, raw # noqa: F401, E402, E501
57 changes: 57 additions & 0 deletions synadm/cli/_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# synadm
# Copyright (C) 2020-2023 Johannes Tiefenbacher
#
# synadm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# synadm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"Common CLI options, option groups, helpers and utilities."

import click

from click_option_group import MutuallyExclusiveOptionGroup


def common_opts_raw_command(function):
return click.argument(
"endpoint", type=str
)(
click.option(
"--method", "-m",
type=click.Choice(["get", "post", "put", "delete"]),
help="The HTTP method used for the request.",
default="get", show_default=True
)(function)
)


data_group_raw_command = MutuallyExclusiveOptionGroup(
"Data",
help=""
)


def data_opts_raw_command(function):
return data_group_raw_command.option(
"--data", "-d", type=str, default='{}', show_default=True,
help="""The JSON string sent in the body of post, put and delete
requests - provided as a string. Make sure to escape it from shell
interpretation by using single quotes. E.g '{"key1": "value1",
"key2": 123}'"""
)(
data_group_raw_command.option(
"--data-file", "-f", type=click.File("rt"),
show_default=True,
help="""Read JSON data from file. To read from stdin use "-" as the
filename argument."""
)(function)
)
71 changes: 28 additions & 43 deletions synadm/cli/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import click

from synadm import cli
from synadm.cli._common import common_opts_raw_command, data_opts_raw_command
from click_option_group import optgroup, MutuallyExclusiveOptionGroup


Expand Down Expand Up @@ -72,72 +73,56 @@ def login_cmd(helper, user_id, password):


@matrix.command(name="raw")
@click.argument(
"endpoint", type=str)
@click.option(
"--method", "-m", type=click.Choice(["get", "post", "put", "delete"]),
help="""The HTTP method used for the request.""",
default="get", show_default=True)
@optgroup.group(
"Data input",
cls=MutuallyExclusiveOptionGroup,
help="")
@optgroup.option(
"--data", "-d", type=str, default='{}', show_default=True,
help="""The JSON string sent in the body of post, put and delete requests -
provided as a string. Make sure to escape it from shell interpretation by
using single quotes. E.g '{"key1": "value1", "key2": 123}'
""")
@optgroup.option(
"--data-file", "-f", type=click.File("rt"),
show_default=True,
help="""Read JSON data from file. To read from stdin use "-" as the
filename argument.
""")
@common_opts_raw_command
@data_opts_raw_command
@optgroup.group(
"Matrix token",
cls=MutuallyExclusiveOptionGroup,
help="")
@optgroup.option(
"--token", "-t", type=str, envvar='MTOKEN', show_default=True,
help="""Token used for Matrix authentication instead of the configured
admin user's token. If --token (and --prompt) option is missing, the token
is read from environment variable $MTOKEN instead. To make sure a user's
token does not show up in system logs, don't provide it on the shell
directly but set $MTOKEN with shell command `read MTOKEN`.""")
admin user's token. If ``--token`` (and ``--prompt``) option is missing,
the token is read from environment variable ``$MTOKEN`` instead. To make
sure a user's token does not show up in system logs, don't provide it on
the shell directly but set ``$MTOKEN`` with shell command ``read
MTOKEN``.""")
@optgroup.option(
"--prompt", "-p", is_flag=True, show_default=True,
help="""Prompt for the token used for Matrix authentication. This option
always overrides $MTOKEN.""")
@click.pass_obj
def raw_request_cmd(helper, endpoint, method, data, data_file, token, prompt):
""" Execute a raw request to the Matrix API.
""" Execute a custom request to the Matrix API.
The endpoint argument is the part of the URL _after_ the configured base
URL and Matrix path (see `synadm config`). A simple get request would e.g
look like this: `synadm matrix raw client/versions`
URL (actually "Synapse base URL") and "Matrix API path" (see ``synadm
config``). A get request could look like this: ``synadm matrix raw
client/versions`` URL encoding must be handled at this point. Consider
enabling debug outputs via synadm's global flag ``-vv``
Use either --token or --prompt to provide a user's token and execute Matrix
commands on their behalf. Respect the privacy of others! Be responsible!
Use either ``--token`` or ``--prompt`` to provide a user's token and
execute Matrix commands on their behalf. Respect the privacy of others!
Act responsible!
\b
The precedence rules for token reading are:
1. Interactive input using --prompt; 2. Set on CLI via --token string;
3. Read from environment variable $MTOKEN; 4. Preconfigured admin token
set in synadm's config file.
1. Interactive input using ``--prompt``;
2. Set on CLI via ``--token``
3. Read from environment variable ``$MTOKEN``;
4. Preconfigured admin token set via ``synadm config``.
Caution: Passing secrets as CLI arguments or via environment variables is
not considered secure. Know what you are doing!
"""
if prompt:
token = click.prompt("Matrix token", type=str)

if data_file:
raw_request = helper.matrix_api.raw_request(
endpoint,
method,
data_file.read(),
token=token
)
else:
raw_request = helper.matrix_api.raw_request(endpoint, method, data,
token=token)
data = data_file.read()

raw_request = helper.matrix_api.raw_request(endpoint, method, data,
token=token)

if helper.no_confirm:
if raw_request is None:
Expand Down
55 changes: 55 additions & 0 deletions synadm/cli/raw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# synadm
# Copyright (C) 2020-2023 Johannes Tiefenbacher
#
# synadm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# synadm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""This module holds the `raw` command only."""

import click

from synadm import cli
from synadm.cli._common import common_opts_raw_command, data_opts_raw_command


@cli.root.command(name="raw")
@common_opts_raw_command
@data_opts_raw_command
@click.pass_obj
def raw_request_cmd(helper, endpoint, method, data, data_file):
""" Issue a custom request to the Synapse Admin API.
The endpoint argument is the part of the URL _after_ the configured
"Synapse base URL" and "Synapse Admin API path" (see ``synadm config``).
A get request to the "Query User Account API" would look like this:
``synadm raw v2/users/%40testuser%3Aexample.org``. URL encoding must be
handled at this point. Consider enabling debug outputs via synadm's global
flag ``-vv``
"""
if data_file:
data = data_file.read()

raw_request = helper.api.raw_request(endpoint, method, data)

if helper.no_confirm:
if raw_request is None:
raise SystemExit(1)
helper.output(raw_request)
else:
if raw_request is None:
click.echo("The Admin API's response was empty or JSON data "
"could not be loaded.")
raise SystemExit(1)
else:
helper.output(raw_request)

0 comments on commit 7da00fa

Please sign in to comment.