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                                      42x       10x 10x 10x     42x       16x 16x 16x         16x     16x 2x     14x         14x     42x       393x 393x     42x       9x   9x       9x   9x     42x       30x 30x 1x         1x             29x 29x       29x    
import { Request, Response } from "express";
import { PostUpdateUserEmailAddressRequestSchema } from "shared/types/api/login";
import {
  GetUserBySerialRequest,
  PostUpdateUserPasswordRequest,
  PostUserRequest,
} from "shared/types/api/users";
import { UserGroup } from "shared/types/models/user";
import { verifyUpdateUserEmailAddress } from "server/features/kompassi-login/kompassiLoginService";
import {
  fetchUserBySerialOrUsername,
  fetchUserByUsername,
  storeUser,
  storeUserPassword,
} from "server/features/user/userService";
import { getAuthUsername } from "server/middleware/requireAuth";
import { getAuthorizedUserGroup } from "server/utils/authHeader";
import { logger } from "server/utils/logger";
 
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`, {
        cause: 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);
};