<commit_before>import virtool.utils

from pymongo import ReturnDocument
from cerberus import Validator

from virtool.handlers.utils import protected, bad_request, invalid_input, unpack_json_request, json_response, not_found
from virtool.permissions import PERMISSIONS
from virtool.groups import merge_group_permissions
from virtool.users import projection, processor, invalidate_session, user_exists, hash_password


@protected("manage_users")
async def find(req):
    """
    Get a list of the existing ``user_ids`` in the database.
     
    """
    data = [processor(document) for document in await req.app["db"].users.find({}, projection).to_list(None)]
    return json_response(data)


@protected("manage_users")
async def get(req):
    """
    Get a near-complete user document. Password data are removed.
     
    """
    document = await req.app["db"].users.find_one(req.match_info["user_id"], projection)

    if not document:
        return not_found()

    return json_response(processor(document))


@protected("manage_users")
async def create(req):
    """
    Add a new user to the user database.

    """
    db, data = await unpack_json_request(req)

    v = Validator({
        "user_id": {"type": "string", "required": True},
        "password": {"type": "string", "required": True},
        "force_reset": {"type": "boolean"}
    })

    if not v(data):
        return invalid_input(v.errors)

    # Check if the username is already taken. Fail if it does.
    if await user_exists(db, data["user_id"]):
        return bad_request("User already exists")

    document = {
        "_id": data["user_id"],
        # A list of group _ids the user is associated with.
        "groups": list(),
        "settings": {
            "skip_quick_analyze_dialog": True,
            "show_ids": True,
            "show_versions": True,
            "quick_analyze_algorithm": "pathoscope_bowtie"
        },
        "sessions": [],
        "permissions": {permission: False for permission in PERMISSIONS},
        "password": <PASSWORD>(data["password"]),
        "primary_group": "",
        # Should the user be forced to reset their password on their next login?
        "force_reset": data.get("force_reset", True),
        # A timestamp taken at the last password change.
        "last_password_change": virtool.utils.timestamp,
        # Should all of the user's sessions be invalidated so that they are forced to login next time they
        # download the client.
        "invalidate_sessions": False
    }

    await db.users.insert(document)
    
    return json_response(processor({key: document[key] for key in projection}))


async def set_password(req):
    """
    Used by users with the *modify_options* permission to change other users passwords.

    """
    data = await req.json()

    user_id = req.match_info["user_id"]

    if not await user_exists(req.app["db"], user_id):
        return not_found()

    document = await req.app["db"].users.find_one_and_update({"_id": user_id}, {
        "$set": {
            "password": <PASSWORD>(data["password"]),
            "last_password_change": virtool.utils.timestamp(),
            "invalidate_sessions": True
        }
    }, return_document=ReturnDocument.AFTER)

    return json_response(document)


async def set_force_reset(req):
    """
    Used by users with the *modify_options* permission to Set a users password. Can take a "reset" property, which
    when True will force the user to reset their password on next login. To be called by an connection with
    administrative privileges.

    """
    user_id = req.match_info["user_id"]

    data = await req.json()

    if not await user_exists(req.app["db"], user_id):<commit_msg>Add better response data for set_password<commit_after>import virtool.utils

from pymongo import ReturnDocument
from cerberus import Validator

from virtool.handlers.utils import protected, bad_request, invalid_input, unpack_json_request, json_response, not_found
from virtool.permissions import PERMISSIONS
from virtool.groups import merge_group_permissions
from virtool.users import projection, processor, invalidate_session, user_exists, hash_password


@protected("manage_users")
async def find(req):
    """
    Get a list of the existing ``user_ids`` in the database.
     
    """
    data = [processor(document) for document in await req.app["db"].users.find({}, projection).to_list(None)]
    return json_response(data)


@protected("manage_users")
async def get(req):
    """
    Get a near-complete user document. Password data are removed.
     
    """
    document = await req.app["db"].users.find_one(req.match_info["user_id"], projection)

    if not document:
        return not_found()

    return json_response(processor(document))


@protected("manage_users")
async def create(req):
    """
    Add a new user to the user database.

    """
    db, data = await unpack_json_request(req)

    v = Validator({
        "user_id": {"type": "string", "required": True},
        "password": {"type": "string", "required": True},
        "force_reset": {"type": "boolean"}
    })

    if not v(data):
        return invalid_input(v.errors)

    # Check if the username is already taken. Fail if it does.
    if await user_exists(db, data["user_id"]):
        return bad_request("User already exists")

    document = {
        "_id": data["user_id"],
        # A list of group _ids the user is associated with.
        "groups": list(),
        "settings": {
            "skip_quick_analyze_dialog": True,
            "show_ids": True,
            "show_versions": True,
            "quick_analyze_algorithm": "pathoscope_bowtie"
        },
        "sessions": [],
        "permissions": {permission: False for permission in PERMISSIONS},
        "password": <PASSWORD>(data["password"]),
        "primary_group": "",
        # Should the user be forced to reset their password on their next login?
        "force_reset": data.get("force_reset", True),
        # A timestamp taken at the last password change.
        "last_password_change": virtool.utils.timestamp(),
        # Should all of the user's sessions be invalidated so that they are forced to login next time they
        # download the client.
        "invalidate_sessions": False
    }

    await db.users.insert(document)
    
    return json_response(processor({key: document[key] for key in projection}))


async def set_password(req):
    """
    Used by users with the *modify_options* permission to change other users passwords.

    """
    data = await req.json()

    user_id = req.match_info["user_id"]

    if not await user_exists(req.app["db"], user_id):
        return not_found()

    document = await req.app["db"].users.find_one_and_update({"_id": user_id}, {
        "$set": {
            "password": <PASSWORD>(data["password"]),
            "last_password_change": virtool.utils.timestamp(),
            "invalidate_sessions": True
        }
    }, return_document=ReturnDocument.AFTER, projection=["force_reset", "last_password_change"])

    print(document)

    document["user_id"] = document.pop("_id")

    return json_response(document)


async def set_force_reset(req):
    """
    Used by users with the *modify_options* permission to Set a users password. Can take a "reset" property, which
    when True will force the user to reset their password on next login. To be called by an connection with
    administrative privileges.

    """
    user_id = req.match_info["user_id"]

    data = await req.json()

    if not await user_exists(req.app["db"], user_id):
{'fraction_non_alphanumeric': 0.11323490640653835, 'fraction_numerical': 0.0, 'mean_word_length': 3.1618211738891935, 'pattern_counts': {'":': 54, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4480517', 'n_tokens_mistral': 2194, 'n_tokens_neox': 2129, 'n_words': 659}