2022-09-15 13:14:55 +02:00
|
|
|
import logging
|
2022-04-23 13:48:29 +02:00
|
|
|
from typing import Optional
|
|
|
|
|
2022-04-23 11:20:43 +02:00
|
|
|
import click
|
2023-03-04 15:57:38 +01:00
|
|
|
from humanize import naturalsize
|
2022-04-23 11:20:43 +02:00
|
|
|
|
2023-04-12 09:57:41 +02:00
|
|
|
from fittrackee import db
|
2022-04-23 11:20:43 +02:00
|
|
|
from fittrackee.cli.app import app
|
|
|
|
from fittrackee.users.exceptions import UserNotFoundException
|
2023-03-04 15:57:38 +01:00
|
|
|
from fittrackee.users.export_data import (
|
|
|
|
clean_user_data_export,
|
|
|
|
generate_user_data_archives,
|
|
|
|
)
|
2022-04-23 13:48:29 +02:00
|
|
|
from fittrackee.users.utils.admin import UserManagerService
|
2022-09-15 13:14:55 +02:00
|
|
|
from fittrackee.users.utils.token import clean_blacklisted_tokens
|
|
|
|
|
|
|
|
handler = logging.StreamHandler()
|
2023-03-04 17:04:15 +01:00
|
|
|
logger = logging.getLogger('fittrackee_users_cli')
|
2022-09-15 13:14:55 +02:00
|
|
|
logger.setLevel(logging.INFO)
|
|
|
|
logger.addHandler(handler)
|
2022-04-23 11:20:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
@click.group(name='users')
|
|
|
|
def users_cli() -> None:
|
|
|
|
"""Manage users."""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2023-04-12 09:57:41 +02:00
|
|
|
@users_cli.command('create')
|
|
|
|
@click.argument('username')
|
|
|
|
@click.option('--email', type=str, required=True, help='User email.')
|
|
|
|
@click.option(
|
|
|
|
'--password',
|
|
|
|
type=str,
|
|
|
|
help='User password. If not provided, a random password is generated.',
|
|
|
|
)
|
|
|
|
def create_user(username: str, email: str, password: Optional[str]) -> None:
|
|
|
|
"""Create an active user account."""
|
|
|
|
with app.app_context():
|
|
|
|
try:
|
|
|
|
user_manager_service = UserManagerService(username)
|
|
|
|
user, user_password = user_manager_service.create_user(
|
|
|
|
email=email, password=password
|
|
|
|
)
|
|
|
|
db.session.add(user)
|
|
|
|
db.session.commit()
|
|
|
|
user_manager_service.update(activate=True)
|
|
|
|
click.echo(f"User '{username}' created.")
|
|
|
|
if not password:
|
|
|
|
click.echo(f"The user password is: {user_password}")
|
|
|
|
except Exception as e:
|
|
|
|
click.echo(f'Error(s) occurred:\n{e}', err=True)
|
|
|
|
|
|
|
|
|
2022-04-23 13:48:29 +02:00
|
|
|
@users_cli.command('update')
|
2022-04-23 11:20:43 +02:00
|
|
|
@click.argument('username')
|
2022-04-23 13:48:29 +02:00
|
|
|
@click.option(
|
|
|
|
'--set-admin',
|
|
|
|
type=bool,
|
|
|
|
help='Add/remove admin rights (when adding admin rights, '
|
|
|
|
'it also activates user account if not active).',
|
|
|
|
)
|
|
|
|
@click.option('--activate', is_flag=True, help='Activate user account.')
|
|
|
|
@click.option(
|
|
|
|
'--reset-password',
|
|
|
|
is_flag=True,
|
|
|
|
help='Reset user password (a new password will be displayed).',
|
|
|
|
)
|
|
|
|
@click.option('--update-email', type=str, help='Update user email.')
|
|
|
|
def manage_user(
|
|
|
|
username: str,
|
|
|
|
set_admin: Optional[bool],
|
|
|
|
activate: bool,
|
|
|
|
reset_password: bool,
|
|
|
|
update_email: Optional[str],
|
|
|
|
) -> None:
|
2022-04-24 11:05:01 +02:00
|
|
|
"""Manage given user account."""
|
2022-04-23 11:20:43 +02:00
|
|
|
with app.app_context():
|
|
|
|
try:
|
2022-04-23 13:48:29 +02:00
|
|
|
user_manager_service = UserManagerService(username)
|
|
|
|
_, is_user_updated, password = user_manager_service.update(
|
|
|
|
is_admin=set_admin,
|
|
|
|
with_confirmation=False,
|
|
|
|
activate=activate,
|
|
|
|
reset_password=reset_password,
|
|
|
|
new_email=update_email,
|
|
|
|
)
|
|
|
|
if is_user_updated:
|
|
|
|
click.echo(f"User '{username}' updated.")
|
|
|
|
if password:
|
|
|
|
click.echo(f"The new password is: {password}")
|
|
|
|
else:
|
|
|
|
click.echo("No updates.")
|
2022-04-23 11:20:43 +02:00
|
|
|
except UserNotFoundException:
|
2022-04-23 13:48:29 +02:00
|
|
|
click.echo(
|
|
|
|
f"User '{username}' not found.\n"
|
|
|
|
"Check the provided user name (case sensitive).",
|
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
click.echo(f'An error occurred: {e}', err=True)
|
2022-09-15 13:14:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
@users_cli.command('clean_tokens')
|
|
|
|
@click.option('--days', type=int, required=True, help='Number of days.')
|
|
|
|
def clean(
|
|
|
|
days: int,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Clean blacklisted tokens expired for more than provided number of days.
|
|
|
|
"""
|
|
|
|
with app.app_context():
|
|
|
|
deleted_rows = clean_blacklisted_tokens(days)
|
|
|
|
logger.info(f'Blacklisted tokens deleted: {deleted_rows}.')
|
2023-03-04 15:57:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
@users_cli.command('clean_archives')
|
|
|
|
@click.option('--days', type=int, required=True, help='Number of days.')
|
|
|
|
def clean_export_archives(
|
|
|
|
days: int,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Clean user export archives created for more than provided number of days.
|
|
|
|
"""
|
|
|
|
with app.app_context():
|
|
|
|
counts = clean_user_data_export(days)
|
|
|
|
logger.info(
|
|
|
|
f'Deleted data export requests: {counts["deleted_requests"]}.'
|
|
|
|
)
|
2023-03-04 17:04:15 +01:00
|
|
|
logger.info(
|
|
|
|
f'Deleted data export archives: {counts["deleted_archives"]}.'
|
|
|
|
)
|
2023-03-04 15:57:38 +01:00
|
|
|
logger.info(f'Freed space: {naturalsize(counts["freed_space"])}.')
|
|
|
|
|
|
|
|
|
|
|
|
@users_cli.command('export_archives')
|
|
|
|
@click.option(
|
|
|
|
'--max',
|
|
|
|
type=int,
|
|
|
|
required=True,
|
2023-03-04 17:49:02 +01:00
|
|
|
help='Maximum number of export requests to process.',
|
2023-03-04 15:57:38 +01:00
|
|
|
)
|
|
|
|
def export_archives(
|
|
|
|
max: int,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Export user data in zip archive if incomplete requests exist.
|
|
|
|
To use in case redis is not set.
|
|
|
|
"""
|
|
|
|
with app.app_context():
|
|
|
|
count = generate_user_data_archives(max)
|
2023-03-04 17:04:15 +01:00
|
|
|
logger.info(f'Generated data export archives: {count}.')
|