| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- import express from "express";
- import internalUser from "../internal/user.js";
- import Access from "../lib/access.js";
- import { isCI } from "../lib/config.js";
- import errs from "../lib/error.js";
- import jwtdecode from "../lib/express/jwt-decode.js";
- import userIdFromMe from "../lib/express/user-id-from-me.js";
- import apiValidator from "../lib/validator/api.js";
- import validator from "../lib/validator/index.js";
- import { express as logger } from "../logger.js";
- import { getValidationSchema } from "../schema/index.js";
- import { isSetup } from "../setup.js";
- const router = express.Router({
- caseSensitive: true,
- strict: true,
- mergeParams: true,
- });
- /**
- * /api/users
- */
- router
- .route("/")
- .options((_, res) => {
- res.sendStatus(204);
- })
- .all(jwtdecode())
- /**
- * GET /api/users
- *
- * Retrieve all users
- */
- .get(async (req, res, next) => {
- try {
- const data = await validator(
- {
- additionalProperties: false,
- properties: {
- expand: {
- $ref: "common#/properties/expand",
- },
- query: {
- $ref: "common#/properties/query",
- },
- },
- },
- {
- expand:
- typeof req.query.expand === "string"
- ? req.query.expand.split(",")
- : null,
- query: typeof req.query.query === "string" ? req.query.query : null,
- },
- );
- const users = await internalUser.getAll(
- res.locals.access,
- data.expand,
- data.query,
- );
- res.status(200).send(users);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- })
- /**
- * POST /api/users
- *
- * Create a new User
- */
- .post(async (req, res, next) => {
- const body = req.body;
- try {
- // If we are in setup mode, we don't check access for current user
- const setup = await isSetup();
- if (!setup) {
- logger.info("Creating a new user in setup mode");
- const access = new Access(null);
- await access.load(true);
- res.locals.access = access;
- // We are in setup mode, set some defaults for this first new user, such as making
- // them an admin.
- body.is_disabled = false;
- if (typeof body.roles !== "object" || body.roles === null) {
- body.roles = [];
- }
- if (body.roles.indexOf("admin") === -1) {
- body.roles.push("admin");
- }
- }
- const payload = await apiValidator(
- getValidationSchema("/users", "post"),
- body,
- );
- const user = await internalUser.create(res.locals.access, payload);
- res.status(201).send(user);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- })
- /**
- * DELETE /api/users
- *
- * Deletes ALL users. This is NOT GENERALLY AVAILABLE!
- * (!) It is NOT an authenticated endpoint.
- * (!) Only CI should be able to call this endpoint. As a result,
- *
- * it will only work when the env vars DEBUG=true and CI=true
- *
- * Do NOT set those env vars in a production environment!
- */
- .delete(async (_, res, next) => {
- if (isCI()) {
- try {
- logger.warn("Deleting all users - CI environment detected, allowing this operation");
- await internalUser.deleteAll();
- res.status(200).send(true);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- return;
- }
- next(new errs.ItemNotFoundError());
- });
- /**
- * Specific user
- *
- * /api/users/123
- */
- router
- .route("/:user_id")
- .options((_, res) => {
- res.sendStatus(204);
- })
- .all(jwtdecode())
- .all(userIdFromMe)
- /**
- * GET /users/123 or /users/me
- *
- * Retrieve a specific user
- */
- .get(async (req, res, next) => {
- try {
- const data = await validator(
- {
- required: ["user_id"],
- additionalProperties: false,
- properties: {
- user_id: {
- $ref: "common#/properties/id",
- },
- expand: {
- $ref: "common#/properties/expand",
- },
- },
- },
- {
- user_id: req.params.user_id,
- expand:
- typeof req.query.expand === "string"
- ? req.query.expand.split(",")
- : null,
- },
- );
- const user = await internalUser.get(res.locals.access, {
- id: data.user_id,
- expand: data.expand,
- omit: internalUser.getUserOmisionsByAccess(
- res.locals.access,
- data.user_id,
- ),
- });
- res.status(200).send(user);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- })
- /**
- * PUT /api/users/123
- *
- * Update and existing user
- */
- .put(async (req, res, next) => {
- try {
- const payload = await apiValidator(
- getValidationSchema("/users/{userID}", "put"),
- req.body,
- );
- payload.id = req.params.user_id;
- const result = await internalUser.update(res.locals.access, payload);
- res.status(200).send(result);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- })
- /**
- * DELETE /api/users/123
- *
- * Update and existing user
- */
- .delete(async (req, res, next) => {
- try {
- const result = await internalUser.delete(res.locals.access, {
- id: req.params.user_id,
- });
- res.status(200).send(result);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- });
- /**
- * Specific user auth
- *
- * /api/users/123/auth
- */
- router
- .route("/:user_id/auth")
- .options((_, res) => {
- res.sendStatus(204);
- })
- .all(jwtdecode())
- .all(userIdFromMe)
- /**
- * PUT /api/users/123/auth
- *
- * Update password for a user
- */
- .put(async (req, res, next) => {
- try {
- const payload = await apiValidator(
- getValidationSchema("/users/{userID}/auth", "put"),
- req.body,
- );
- payload.id = req.params.user_id;
- const result = await internalUser.setPassword(res.locals.access, payload);
- res.status(200).send(result);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- });
- /**
- * Specific user permissions
- *
- * /api/users/123/permissions
- */
- router
- .route("/:user_id/permissions")
- .options((_, res) => {
- res.sendStatus(204);
- })
- .all(jwtdecode())
- .all(userIdFromMe)
- /**
- * PUT /api/users/123/permissions
- *
- * Set some or all permissions for a user
- */
- .put(async (req, res, next) => {
- try {
- const payload = await apiValidator(
- getValidationSchema("/users/{userID}/permissions", "put"),
- req.body,
- );
- payload.id = req.params.user_id;
- const result = await internalUser.setPermissions(
- res.locals.access,
- payload,
- );
- res.status(200).send(result);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- });
- /**
- * Specific user login as
- *
- * /api/users/123/login
- */
- router
- .route("/:user_id/login")
- .options((_, res) => {
- res.sendStatus(204);
- })
- .all(jwtdecode())
- /**
- * POST /api/users/123/login
- *
- * Log in as a user
- */
- .post(async (req, res, next) => {
- try {
- const result = await internalUser.loginAs(res.locals.access, {
- id: Number.parseInt(req.params.user_id, 10),
- });
- res.status(200).send(result);
- } catch (err) {
- logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
- next(err);
- }
- });
- export default router;
|