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)
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))
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)
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)
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)
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)
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)
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)
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)
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)
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} })
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))
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)
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)
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)
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)
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)
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)
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)
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])
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)
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)
async def unavailable(req): return json_response( { "id": "requires_setup", "message": "Server is not configured" }, status=503, headers={"Location": "/setup"})
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))
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)
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)
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))
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
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)
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)