2019-11-25 08:45:53 +00:00
|
|
|
import click
|
|
|
|
from django.db import transaction
|
|
|
|
|
|
|
|
from funkwhale_api.federation import models as federation_models
|
2022-11-23 11:11:36 +00:00
|
|
|
from funkwhale_api.users import models, serializers, tasks
|
2019-11-25 08:45:53 +00:00
|
|
|
|
2022-11-23 11:11:36 +00:00
|
|
|
from . import base, utils
|
2019-11-25 08:45:53 +00:00
|
|
|
|
|
|
|
|
2022-11-23 21:36:56 +00:00
|
|
|
class FakeRequest:
|
2019-11-25 08:45:53 +00:00
|
|
|
def __init__(self, session={}):
|
|
|
|
self.session = session
|
|
|
|
|
|
|
|
|
|
|
|
@transaction.atomic
|
|
|
|
def handler_create_user(
|
|
|
|
username,
|
|
|
|
password,
|
|
|
|
email,
|
|
|
|
is_superuser=False,
|
|
|
|
is_staff=False,
|
|
|
|
permissions=[],
|
|
|
|
upload_quota=None,
|
|
|
|
):
|
|
|
|
serializer = serializers.RS(
|
|
|
|
data={
|
|
|
|
"username": username,
|
|
|
|
"email": email,
|
|
|
|
"password1": password,
|
|
|
|
"password2": password,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
utils.logger.debug("Validating user data…")
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
|
2021-06-17 15:55:12 +00:00
|
|
|
# Override e-mail validation, we assume accounts created from CLI have a valid e-mail
|
2019-11-25 08:45:53 +00:00
|
|
|
request = FakeRequest(session={"account_verified_email": email})
|
|
|
|
utils.logger.debug("Creating user…")
|
|
|
|
user = serializer.save(request=request)
|
|
|
|
utils.logger.debug("Setting permissions and other attributes…")
|
|
|
|
user.is_staff = is_staff
|
|
|
|
user.upload_quota = upload_quota
|
|
|
|
user.is_superuser = is_superuser
|
|
|
|
for permission in permissions:
|
|
|
|
if permission in models.PERMISSIONS:
|
|
|
|
utils.logger.debug("Setting %s permission to True", permission)
|
2022-11-23 21:36:56 +00:00
|
|
|
setattr(user, f"permission_{permission}", True)
|
2019-11-25 08:45:53 +00:00
|
|
|
else:
|
|
|
|
utils.logger.warn("Unknown permission %s", permission)
|
|
|
|
utils.logger.debug("Creating actor…")
|
|
|
|
user.actor = models.create_actor(user)
|
|
|
|
user.save()
|
|
|
|
return user
|
|
|
|
|
|
|
|
|
|
|
|
@transaction.atomic
|
|
|
|
def handler_delete_user(usernames, soft=True):
|
|
|
|
for username in usernames:
|
2022-11-23 21:36:56 +00:00
|
|
|
click.echo(f"Deleting {username}…")
|
2019-11-25 08:45:53 +00:00
|
|
|
actor = None
|
|
|
|
user = None
|
|
|
|
try:
|
|
|
|
user = models.User.objects.get(username=username)
|
|
|
|
except models.User.DoesNotExist:
|
|
|
|
try:
|
|
|
|
actor = federation_models.Actor.objects.local().get(
|
|
|
|
preferred_username=username
|
|
|
|
)
|
|
|
|
except federation_models.Actor.DoesNotExist:
|
|
|
|
click.echo(" Not found, skipping")
|
|
|
|
continue
|
|
|
|
|
|
|
|
actor = actor or user.actor
|
|
|
|
if user:
|
|
|
|
tasks.delete_account(user_id=user.pk)
|
|
|
|
if not soft:
|
|
|
|
click.echo(" Hard delete, removing actor")
|
|
|
|
actor.delete()
|
|
|
|
click.echo(" Done")
|
|
|
|
|
|
|
|
|
|
|
|
@transaction.atomic
|
|
|
|
def handler_update_user(usernames, kwargs):
|
|
|
|
users = models.User.objects.filter(username__in=usernames)
|
|
|
|
total = users.count()
|
|
|
|
if not total:
|
|
|
|
click.echo("No matching users")
|
|
|
|
return
|
|
|
|
|
|
|
|
final_kwargs = {}
|
|
|
|
supported_fields = [
|
|
|
|
"is_active",
|
|
|
|
"permission_moderation",
|
|
|
|
"permission_library",
|
|
|
|
"permission_settings",
|
|
|
|
"is_staff",
|
|
|
|
"is_superuser",
|
|
|
|
"upload_quota",
|
|
|
|
"password",
|
|
|
|
]
|
|
|
|
for field in supported_fields:
|
|
|
|
try:
|
|
|
|
value = kwargs[field]
|
|
|
|
except KeyError:
|
|
|
|
continue
|
|
|
|
final_kwargs[field] = value
|
|
|
|
|
|
|
|
click.echo(
|
|
|
|
"Updating {} on {} matching users…".format(
|
|
|
|
", ".join(final_kwargs.keys()), total
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if "password" in final_kwargs:
|
|
|
|
new_password = final_kwargs.pop("password")
|
|
|
|
for user in users:
|
|
|
|
user.set_password(new_password)
|
|
|
|
models.User.objects.bulk_update(users, ["password"])
|
|
|
|
if final_kwargs:
|
|
|
|
users.update(**final_kwargs)
|
|
|
|
click.echo("Done!")
|
|
|
|
|
|
|
|
|
|
|
|
@base.cli.group()
|
|
|
|
def users():
|
|
|
|
"""Manage users"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@users.command()
|
|
|
|
@click.option("--username", "-u", prompt=True, required=True)
|
|
|
|
@click.option(
|
|
|
|
"-p",
|
|
|
|
"--password",
|
|
|
|
prompt="Password (leave empty to have a random one generated)",
|
|
|
|
hide_input=True,
|
|
|
|
envvar="FUNKWHALE_CLI_USER_PASSWORD",
|
|
|
|
default="",
|
|
|
|
help="If empty, a random password will be generated and displayed in console output",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-e",
|
|
|
|
"--email",
|
|
|
|
prompt=True,
|
|
|
|
help="Email address to associate with the account",
|
|
|
|
required=True,
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-q",
|
|
|
|
"--upload-quota",
|
|
|
|
help="Upload quota (leave empty to use default pod quota)",
|
|
|
|
required=False,
|
|
|
|
default=None,
|
|
|
|
type=click.INT,
|
|
|
|
)
|
|
|
|
@click.option(
|
2022-01-09 11:40:49 +00:00
|
|
|
"--superuser/--no-superuser",
|
|
|
|
default=False,
|
2019-11-25 08:45:53 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
2022-01-09 11:40:49 +00:00
|
|
|
"--staff/--no-staff",
|
|
|
|
default=False,
|
2019-11-25 08:45:53 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
2022-01-09 11:40:49 +00:00
|
|
|
"--permission",
|
|
|
|
multiple=True,
|
2019-11-25 08:45:53 +00:00
|
|
|
)
|
|
|
|
def create(username, password, email, superuser, staff, permission, upload_quota):
|
|
|
|
"""Create a new user"""
|
|
|
|
generated_password = None
|
|
|
|
if password == "":
|
|
|
|
generated_password = models.User.objects.make_random_password()
|
|
|
|
user = handler_create_user(
|
|
|
|
username=username,
|
|
|
|
password=password or generated_password,
|
|
|
|
email=email,
|
|
|
|
is_superuser=superuser,
|
|
|
|
is_staff=staff,
|
|
|
|
permissions=permission,
|
|
|
|
upload_quota=upload_quota,
|
|
|
|
)
|
2022-11-23 21:36:56 +00:00
|
|
|
click.echo(f"User {user.username} created!")
|
2019-11-25 08:45:53 +00:00
|
|
|
if generated_password:
|
2022-11-23 21:36:56 +00:00
|
|
|
click.echo(f" Generated password: {generated_password}")
|
2019-11-25 08:45:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
@base.delete_command(group=users, id_var="username")
|
|
|
|
@click.argument("username", nargs=-1)
|
|
|
|
@click.option(
|
|
|
|
"--hard/--no-hard",
|
|
|
|
default=False,
|
|
|
|
help="Purge all user-related info (allow recreating a user with the same username)",
|
|
|
|
)
|
|
|
|
def delete(username, hard):
|
|
|
|
"""Delete given users"""
|
|
|
|
handler_delete_user(usernames=username, soft=not hard)
|
|
|
|
|
|
|
|
|
|
|
|
@base.update_command(group=users, id_var="username")
|
|
|
|
@click.argument("username", nargs=-1)
|
|
|
|
@click.option(
|
|
|
|
"--active/--inactive",
|
|
|
|
help="Mark as active or inactive (inactive users cannot login or use the service)",
|
|
|
|
default=None,
|
|
|
|
)
|
|
|
|
@click.option("--superuser/--no-superuser", default=None)
|
|
|
|
@click.option("--staff/--no-staff", default=None)
|
|
|
|
@click.option("--permission-library/--no-permission-library", default=None)
|
|
|
|
@click.option("--permission-moderation/--no-permission-moderation", default=None)
|
|
|
|
@click.option("--permission-settings/--no-permission-settings", default=None)
|
|
|
|
@click.option("--password", default=None, envvar="FUNKWHALE_CLI_USER_UPDATE_PASSWORD")
|
|
|
|
@click.option(
|
2022-01-09 11:40:49 +00:00
|
|
|
"-q",
|
|
|
|
"--upload-quota",
|
|
|
|
type=click.INT,
|
2019-11-25 08:45:53 +00:00
|
|
|
)
|
|
|
|
def update(username, **kwargs):
|
|
|
|
"""Update attributes for given users"""
|
|
|
|
field_mapping = {
|
|
|
|
"active": "is_active",
|
|
|
|
"superuser": "is_superuser",
|
|
|
|
"staff": "is_staff",
|
|
|
|
}
|
|
|
|
final_kwargs = {}
|
|
|
|
for cli_field, value in kwargs.items():
|
|
|
|
if value is None:
|
|
|
|
continue
|
|
|
|
model_field = (
|
|
|
|
field_mapping[cli_field] if cli_field in field_mapping else cli_field
|
|
|
|
)
|
|
|
|
final_kwargs[model_field] = value
|
|
|
|
|
|
|
|
if not final_kwargs:
|
|
|
|
raise click.BadArgumentUsage("You need to update at least one attribute")
|
|
|
|
|
|
|
|
handler_update_user(usernames=username, kwargs=final_kwargs)
|