All files / server/src/features/user userController.ts

96.66% Statements 29/30
80% Branches 8/10
100% Functions 5/5
96.66% Lines 29/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                                      39x       10x 10x 10x     39x       16x 16x 16x         16x     16x 2x     14x         14x     39x       279x 279x     39x       9x   9x       9x   9x     39x       27x 27x 1x         1x             26x 26x       26x    
import { Request, Response } from "express";
import {
  fetchUserByUsername,
  storeUser,
  fetchUserBySerialOrUsername,
  storeUserPassword,
} from "server/features/user/userService";
import { getAuthUsername } from "server/middleware/requireAuth";
import { getAuthorizedUserGroup } from "server/utils/authHeader";
import { logger } from "server/utils/logger";
import { UserGroup } from "shared/types/models/user";
import {
  GetUserBySerialRequest,
  PostUpdateUserPasswordRequest,
  PostUserRequest,
} from "shared/types/api/users";
import { PostUpdateUserEmailAddressRequestSchema } from "shared/types/api/login";
import { verifyUpdateUserEmailAddress } from "server/features/kompassi-login/kompassiLoginService";
 
export const postUser = async (
  req: Request<unknown, unknown, PostUserRequest>,
  res: Response,
): Promise<Response> => {
  const { username, password, serial } = req.body;
  const response = await storeUser(username, password, serial);
  return res.json(response);
};
 
export const postUserPassword = async (
  req: Request<unknown, unknown, PostUpdateUserPasswordRequest>,
  res: Response,
): Promise<Response> => {
  const requesterUsername = getAuthUsername(req);
  const requesterUserGroup = getAuthorizedUserGroup(req.headers.authorization);
  const { usernameToUpdate, password } = req.body;
 
  // Only the account owner, helpers, or admins may change a password — decided by the JWT
  // userGroup claim, never by the requester's username
  const isAdminOrHelper =
    requesterUserGroup === UserGroup.ADMIN ||
    requesterUserGroup === UserGroup.HELPER;
 
  if (requesterUsername !== usernameToUpdate && !isAdminOrHelper) {
    return res.sendStatus(401);
  }
 
  const response = await storeUserPassword(
    usernameToUpdate,
    password,
    requesterUserGroup,
  );
  return res.json(response);
};
 
export const getUser = async (
  req: Request,
  res: Response,
): Promise<Response> => {
  const response = await fetchUserByUsername(getAuthUsername(req));
  return res.json(response);
};
 
export const getUserBySerialOrUsername = async (
  req: Request<unknown, unknown, unknown, GetUserBySerialRequest>,
  res: Response,
): Promise<Response> => {
  const { searchTerm } = req.query;
 
  if (!searchTerm) {
    return res.sendStatus(422);
  }
 
  const response = await fetchUserBySerialOrUsername(searchTerm);
 
  return res.json(response);
};
 
export const postUpdateUserEmailAddress = async (
  req: Request,
  res: Response,
): Promise<Response> => {
  const result = PostUpdateUserEmailAddressRequestSchema.safeParse(req.body);
  if (!result.success) {
    logger.error(
      new Error(
        `Error validating postUpdateUserEmailAddress body: ${JSON.stringify(result.error)}`,
      ),
    );
    return res.status(422).json({
      message: "Invalid email format",
      status: "error",
      errorId: "invalidEmail",
    });
  }
 
  const { email } = result.data;
  const response = await verifyUpdateUserEmailAddress(
    getAuthUsername(req),
    email,
  );
  return res.json(response);
};