Example #1
0
async def find(req):
    """
    Find files based on an optional text query that is matched against file names. Only ready, unreserved files are
    returned.

    """
    db = req.app["db"]

    base_query = {"ready": True, "reserved": False}

    file_type = req.query.get("type", None)

    db_query = dict()

    if file_type:
        base_query["type"] = file_type

    data = await paginate(db.files,
                          db_query,
                          req.query,
                          sort=[("uploaded_at", pymongo.DESCENDING)],
                          projection=virtool.files.db.PROJECTION,
                          base_query=base_query)

    return json_response(data)
Example #2
0
async def update_permissions(req):
    """
    Updates the permissions of a given group.

    """
    db = req.app["db"]
    data = req["data"]

    group_id = req.match_info["group_id"]

    old_document = await db.groups.find_one({"_id": group_id}, ["permissions"])

    if not old_document:
        return not_found()

    old_document["permissions"].update(data["permissions"])

    # Get the current permissions dict for the passed group id.
    document = await db.groups.find_one_and_update(
        {"_id": group_id},
        {"$set": {
            "permissions": old_document["permissions"]
        }})

    await virtool.groups.db.update_member_users(db, group_id)

    return json_response(virtool.utils.base_processor(document))
Example #3
0
async def edit_user(req):
    db = req.app["db"]
    data = req["data"]
    ref_id = req.match_info["ref_id"]
    user_id = req.match_info["user_id"]

    document = await db.references.find_one(
        {
            "_id": ref_id,
            "users.id": user_id
        }, ["groups", "users"])

    if document is None:
        return not_found()

    if not await virtool.references.db.check_right(req, ref_id, "modify"):
        return insufficient_rights()

    subdocument = await virtool.references.db.edit_group_or_user(
        db, ref_id, user_id, "users", data)

    if subdocument is None:
        return not_found()

    subdocument = await virtool.users.db.attach_identicons(db, subdocument)

    return json_response(subdocument)
Example #4
0
async def set_rights(req):
    """
    Change rights settings for the specified sample document.

    """
    db = req.app["db"]
    data = req["data"]

    sample_id = req.match_info["sample_id"]

    if not await db.samples.count({"_id": sample_id}):
        return not_found()

    user_id = req["client"].user_id

    # Only update the document if the connected user owns the samples or is an administrator.
    if not req[
            "client"].administrator and user_id != await virtool.samples.db.get_sample_owner(
                db, sample_id):
        return insufficient_rights("Must be administrator or sample owner")

    group = data.get("group", None)

    if group:
        existing_group_ids = await db.groups.distinct("_id") + ["none"]

        if group not in existing_group_ids:
            return bad_request("Group does not exist")

    # Update the sample document with the new rights.
    document = await db.samples.find_one_and_update(
        {"_id": sample_id}, {"$set": data},
        projection=virtool.samples.db.RIGHTS_PROJECTION)

    return json_response(document)
Example #5
0
async def add_user(req):
    db = req.app["db"]
    data = req["data"]
    ref_id = req.match_info["ref_id"]

    document = await db.references.find_one(ref_id, ["groups", "users"])

    if document is None:
        return not_found()

    if not await virtool.references.db.check_right(req, ref_id, "modify"):
        return insufficient_rights()

    try:
        subdocument = await virtool.references.db.add_group_or_user(
            db, ref_id, "users", data)
    except virtool.errors.DatabaseError as err:
        if "already exists" in str(err):
            return bad_request("User already exists")

        if "does not exist" in str(err):
            return bad_request("User does not exist")

        raise

    headers = {"Location": f"/api/refs/{ref_id}/users/{subdocument['id']}"}

    subdocument = await virtool.users.db.attach_identicons(db, subdocument)

    return json_response(subdocument, headers=headers, status=201)
Example #6
0
async def install(req):
    db = req.app["db"]

    releases = await virtool.db.utils.get_one_field(db.status, "releases",
                                                    "software")

    try:
        latest_release = releases[0]
    except IndexError:
        return not_found("Could not find latest uninstalled release")

    process = await virtool.processes.db.register(
        db, "update_software", context={"file_size": latest_release["size"]})

    await db.status.update_one(
        {"_id": "software"}, {"$set": {
            "process": process,
            "updating": True
        }})

    update = virtool.github.create_update_subdocument(
        latest_release, False, req["client"].user_id,
        virtool.utils.timestamp())

    await aiojobs.aiohttp.spawn(
        req, virtool.software.db.install(req.app, latest_release,
                                         process["id"]))

    return json_response(update)
Example #7
0
async def install(req):
    """
    Install the latest official HMM database from GitHub.

    """
    db = req.app["db"]

    user_id = req["client"].user_id

    if await db.status.count({"_id": "hmm", "updates.ready": False}):
        return conflict("Install already in progress")

    process = await virtool.processes.db.register(db, "install_hmms")

    document = await db.status.find_one_and_update(
        {"_id": "hmm"}, {"$set": {
            "process": {
                "id": process["id"]
            }
        }})

    release = document.get("release", None)

    if release is None:
        return bad_request("Target release does not exist")

    update = virtool.github.create_update_subdocument(release, False, user_id)

    await db.status.update_one({"_id": "hmm"}, {"$push": {"updates": update}})

    await aiojobs.aiohttp.spawn(
        req, virtool.hmm.db.install(req.app, process["id"], release, user_id))

    return json_response(update)
Example #8
0
async def find(req):
    db = req.app["db"]

    term = req.query.get("find", None)

    db_query = dict()

    if term:
        db_query = compose_regex_query(term, ["name", "data_type"])

    base_query = virtool.references.db.compose_base_find_query(
        req["client"].user_id, req["client"].administrator,
        req["client"].groups)

    data = await paginate(db.references,
                          db_query,
                          req.query,
                          sort="name",
                          base_query=base_query,
                          processor=virtool.utils.base_processor,
                          projection=virtool.references.db.PROJECTION)

    for d in data["documents"]:
        latest_build, otu_count, unbuilt_count = await asyncio.gather(
            virtool.references.db.get_latest_build(db, d["id"]),
            virtool.references.db.get_otu_count(db, d["id"]),
            virtool.references.db.get_unbuilt_count(db, d["id"]))

        d.update({
            "latest_build": latest_build,
            "otu_count": otu_count,
            "unbuilt_change_count": unbuilt_count
        })

    return json_response(data)
Example #9
0
async def find_history(req):
    """
    Find history changes for a specific index.

    """
    db = req.app["db"]

    index_id = req.match_info["index_id"]

    if not await db.indexes.count({"_id": index_id}):
        return not_found()

    term = req.query.get("term", None)

    db_query = {"index.id": index_id}

    if term:
        db_query.update(compose_regex_query(term, ["otu.name", "user.id"]))

    data = await paginate(db.history,
                          db_query,
                          req.query,
                          sort=[("otu.name", 1), ("otu.version", -1)],
                          projection=virtool.history.db.LIST_PROJECTION,
                          reverse=True)

    return json_response(data)
Example #10
0
async def edit(req):
    """
    Update specific fields in the sample document.

    """
    db = req.app["db"]
    data = req["data"]

    sample_id = req.match_info["sample_id"]

    if not await virtool.samples.db.check_rights(db, sample_id, req["client"]):
        return insufficient_rights()

    message = await virtool.samples.db.check_name(db,
                                                  req.app["settings"],
                                                  data["name"],
                                                  sample_id=sample_id)

    if message:
        return bad_request(message)

    document = await db.samples.find_one_and_update(
        {"_id": sample_id}, {"$set": data},
        projection=virtool.samples.db.LIST_PROJECTION)

    processed = virtool.utils.base_processor(document)

    return json_response(processed)
Example #11
0
async def get(req):
    settings = await virtool.settings.db.get(req.app["db"])

    return json_response({
        **settings,
        **{key: req.app["settings"][key] for key in virtool.settings.db.CONFIG_PROJECTION}
    })
Example #12
0
async def get(req):
    """
    Get a complete analysis document.

    """
    db = req.app["db"]

    analysis_id = req.match_info["analysis_id"]

    document = await db.analyses.find_one(analysis_id)

    if document is None:
        return not_found()

    sample = await db.samples.find_one({"_id": document["sample"]["id"]}, {"quality": False})

    if not sample:
        return bad_request("Parent sample does not exist")

    read, _ = virtool.samples.utils.get_sample_rights(sample, req["client"])

    if not read:
        return insufficient_rights()

    if document["ready"]:
        document = await virtool.analyses.format.format_analysis(db, req.app["settings"], document)

    document["subtraction"] = {
        "id": sample["subtraction"]["id"]
    }

    return json_response(virtool.utils.base_processor(document))
Example #13
0
async def list_releases(req):
    try:
        releases = await virtool.software.db.fetch_and_update_releases(req.app)
    except aiohttp.ClientConnectorError:
        return bad_gateway("Could not connection to www.virtool.ca")

    return json_response(releases)
Example #14
0
async def remove(req):
    """
    Remove a reference and its otus, history, and indexes.

    """
    db = req.app["db"]

    ref_id = req.match_info["ref_id"]

    if not await virtool.db.utils.id_exists(db.references, ref_id):
        return not_found()

    if not await virtool.references.db.check_right(req, ref_id, "remove"):
        return insufficient_rights()

    user_id = req["client"].user_id

    context = {"ref_id": ref_id, "user_id": user_id}

    process = await virtool.processes.db.register(db,
                                                  "delete_reference",
                                                  context=context)

    await db.references.delete_one({"_id": ref_id})

    p = virtool.references.db.RemoveReferenceProcess(req.app, process["id"])

    await aiojobs.aiohttp.spawn(req, p.run())

    headers = {"Content-Location": f"/api/processes/{process['id']}"}

    return json_response(process, 202, headers)
Example #15
0
async def create(req):
    """
    Add a new user to the user database.

    """
    db = req.app["db"]
    data = await req.json()

    if data["user_id"] == "virtool":
        return bad_request("Reserved user name: virtool")

    error = await virtool.users.checks.check_password_length(req)

    if error:
        return bad_request(error)

    user_id = data["user_id"]

    try:
        document = await virtool.users.db.create(db, user_id, data["password"],
                                                 data["force_reset"])
    except virtool.errors.DatabaseError:
        return bad_request("User already exists")

    headers = {"Location": f"/api/users/{user_id}"}

    return json_response(virtool.utils.base_processor(
        {key: document[key]
         for key in virtool.users.db.PROJECTION}),
                         headers=headers,
                         status=201)
Example #16
0
async def upload(req):
    db = req.app["db"]

    file_type = req.match_info["file_type"]

    if file_type not in FILE_TYPES:
        return not_found()

    errors = naive_validator(req)

    if errors:
        return invalid_query(errors)

    filename = req.query["name"]

    document = await virtool.files.db.create(db,
                                             filename,
                                             file_type,
                                             user_id=req["client"].user_id)

    file_id = document["id"]

    await naive_writer(req, file_id)

    headers = {"Location": f"/api/files/{file_id}"}

    return json_response(document, status=201, headers=headers)
Example #17
0
async def create(req):
    """
    Add a new subtraction. Starts an :class:`.CreateSubtraction` job process.

    """
    db = req.app["db"]
    data = req["data"]

    subtraction_id = data["subtraction_id"]

    if await db.subtraction.count({"_id": subtraction_id}):
        return bad_request("Subtraction name already exists")

    file_id = data["file_id"]

    file = await db.files.find_one(file_id, ["name"])

    if file is None:
        return bad_request("File does not exist")

    job_id = await virtool.db.utils.get_new_id(db.jobs)

    user_id = req["client"].user_id

    document = {
        "_id": data["subtraction_id"],
        "nickname": data["nickname"],
        "ready": False,
        "is_host": True,
        "file": {
            "id": file_id,
            "name": file["name"]
        },
        "user": {
            "id": user_id
        },
        "job": {
            "id": job_id
        }
    }

    await db.subtraction.insert_one(document)

    task_args = {"subtraction_id": subtraction_id, "file_id": file_id}

    await virtool.jobs.db.create(db,
                                 req.app["settings"],
                                 "create_subtraction",
                                 task_args,
                                 user_id,
                                 job_id=job_id)

    await req.app["jobs"].enqueue(job_id)

    headers = {"Location": f"/api/account/keys/{subtraction_id}"}

    return json_response(virtool.utils.base_processor(document),
                         headers=headers,
                         status=201)
Example #18
0
async def get_api_keys(req):
    db = req.app["db"]

    user_id = req["client"].user_id

    cursor = db.keys.find({"user.id": user_id}, API_KEY_PROJECTION)

    return json_response([d async for d in cursor], status=200)
Example #19
0
        async def wrapped(req):

            if not public and not req["client"].user_id:
                return json_response(
                    {
                        "id": "requires_authorization",
                        "message": "Requires authorization"
                    },
                    status=401)

            if not req["client"].administrator:
                if admin:
                    return json_response(
                        {
                            "id": "not_permitted",
                            "message": "Requires administrative privilege"
                        },
                        status=403)

                if permission and not req["client"].permissions[permission]:
                    return json_response(
                        {
                            "id": "not_permitted",
                            "message": "Not permitted"
                        },
                        status=403)

            content_type = req.headers.get("Content-type", "")

            if "multipart/form-data" not in content_type:
                try:
                    data = await req.json()
                except (json.decoder.JSONDecodeError, UnicodeDecodeError):
                    data = dict()

                if schema:
                    v = Validator(schema, purge_unknown=True)

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

                    data = v.document

                req["data"] = data

            return await handler(req)
Example #20
0
async def find(req):
    """
    Get a list of all existing group documents.

    """
    cursor = req.app["db"].groups.find()
    return json_response(
        [virtool.utils.base_processor(d) async for d in cursor])
Example #21
0
async def get_resources(req):
    """
    Get a object describing compute resource usage on the server.

    """
    resources = virtool.resources.get()
    req.app["resources"].update(resources)
    return json_response(resources)
Example #22
0
async def find(req):
    db = req.app["db"]

    documents = [
        virtool.utils.base_processor(d) async for d in db.processes.find()
    ]

    return json_response(documents)
Example #23
0
async def unavailable(req):
    return json_response(
        {
            "id": "requires_setup",
            "message": "Server is not configured"
        },
        status=503,
        headers={"Location": "/setup"})
Example #24
0
async def get(req):
    db = req.app["db"]

    await virtool.software.db.fetch_and_update_releases(req.app,
                                                        ignore_errors=True)

    document = await db.status.find_one("software")

    return json_response(virtool.utils.base_processor(document))
Example #25
0
async def middleware(req, handler):
    try:
        return await handler(req)

    except virtool.errors.ProxyError as err:
        return json_response({
            "id": "proxy_error",
            "message": str(err)
        },
                             status=500)

    except aiohttp.ClientProxyConnectionError:
        return json_response(
            {
                "id": "proxy_error",
                "message": "Could not connect to proxy"
            },
            status=500)
Example #26
0
async def find(req):
    """
    Get a list of change documents.

    """
    db = req.app["db"]

    data = await virtool.history.db.find(db, req.query)

    return json_response(data)
Example #27
0
async def replace(req):
    sample_id = req.match_info["sample_id"]

    await virtool.samples.db.attempt_file_replacement(req.app, sample_id,
                                                      req["client"].user_id)

    document = await req.app["db"].samples.find_one(
        sample_id, virtool.samples.db.PROJECTION)

    return json_response(virtool.utils.base_processor(document))
Example #28
0
async def login(req):
    """
    Create a new session for the user with `username`.

    """
    db = req.app["db"]
    data = req["data"]

    user_id = data["username"]
    password = data["password"]

    # When this value is set, the session will last for 1 month instead of the 1 hour default.
    remember = data["remember"]

    # Re-render the login page with an error message if the username and/or password are invalid.
    if not await virtool.users.db.validate_credentials(db, user_id, password):
        return bad_request("Invalid username or password")

    session_id = req.cookies.get("session_id")

    # If the user's password needs to be reset, redirect to the reset page without authorizing the session. A one-time
    # reset code is generated and added to the query string.
    if await virtool.db.utils.get_one_field(db.users, "force_reset", user_id):
        return json_response({
            "reset": True,
            "reset_code": await virtool.users.sessions.create_reset_code(db, session_id, user_id, remember)
        }, status=200)

    session, token = await virtool.users.sessions.replace_session(
        db,
        session_id,
        virtool.http.auth.get_ip(req),
        user_id,
        remember
    )

    resp = json_response({"reset": False}, status=201)

    resp.set_cookie("session_id", session["_id"], httponly=True)
    resp.set_cookie("session_token", token, httponly=True)

    return resp
Example #29
0
async def list_groups(req):
    db = req.app["db"]
    ref_id = req.match_info["ref_id"]

    if not await db.references.count({"_id": ref_id}):
        return not_found()

    groups = await virtool.db.utils.get_one_field(db.references, "groups",
                                                  ref_id)

    return json_response(groups)
Example #30
0
async def find_indexes(req):
    db = req.app["db"]

    ref_id = req.match_info["ref_id"]

    if not await virtool.db.utils.id_exists(db.references, ref_id):
        return not_found()

    data = await virtool.indexes.db.find(db, req.query, ref_id=ref_id)

    return json_response(data)