Skip to main content

Overview

User account management in ARCA follows a strict hierarchical permission model where users can only manage accounts with equal or lower privilege levels. This page covers CRUD operations for user accounts and their business rules.
All user management operations are protected by the JwtAuthGuard and enforce role-based access control.

User Model

Database Schema

apps/backend/prisma/schema.prisma
model Usuario {
  id_User   String  @id @default(uuid()) @map("ID_User") @db.Uuid
  nome      String  @db.VarChar(50)
  email     String  @unique @db.VarChar(100)
  senhaHash String  @map("SenhaHash") @db.VarChar(255)
  roleId    Int     @map("ID_Role") @db.SmallInt
  isActive  Boolean @default(true)

  role                      Role               @relation(fields: [roleId], references: [id_Role])
  documentosUsuario         DocumentoUsuario[] @relation("UsuarioDocumentos")
  logsExecutados            LogAuditoria[]     @relation("UsuarioExecutorLogs")
  atendimentoComoEstagiario Atendimento[]      @relation("EstagiarioAtendimentos")
  atendimentoComoSupervisor Atendimento[]      @relation("SupervisorAtendimentos")
  relatorioComoEstagiario   RelatorioAlta[]    @relation("EstagiarioRelatorios")
  relatorioComoSupervisor   RelatorioAlta[]    @relation("SupervisorRelatorios")
  pacientesComoEstagiario   Paciente[]         @relation("EstagiarioResponsavel")
  pacientesComoSupervisor   Paciente[]         @relation("SupervisorResponsavel")

  @@map("USUARIOS")
}
User IDs are UUIDs for enhanced security and global uniqueness. Emails must be unique across the system.

Creating Users

API Endpoint

curl -X POST http://localhost:3000/users \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "nome": "João Silva",
    "email": "[email protected]",
    "senha": "SecurePassword123!",
    "roleId": 4
  }'

Request Body (CreateUserDto)

nome
string
required
User’s full name (max 50 characters)
email
string
required
Valid email address (max 100 characters, must be unique)
senha
string
required
Password (min 8 characters, max 255 characters)
roleId
number
required
Role ID: 1 (Admin), 2 (Secretário), 3 (Supervisor), or 4 (Estagiário)

Validation Rules

The CreateUserDto enforces strict validation:
apps/backend/src/users/dto/create-user.dto.ts
import { IsEmail, IsInt, IsNotEmpty, IsString, MaxLength, Min, MinLength } from 'class-validator';

export class CreateUserDto {
    @IsString()
    @IsNotEmpty()
    @MaxLength(50)
    nome: string;

    @IsEmail()
    @IsNotEmpty()
    @MaxLength(100)
    email: string;

    @IsString()
    @IsNotEmpty()
    @MinLength(8)
    @MaxLength(255)
    senha: string;

    @IsInt()
    @Min(1)
    roleId: number;
}

Permission Rules

apps/backend/src/users/users.service.ts
async create(createUserDto: CreateUserDto, creator: TokenDto) {
  // Verifica se o criador tem permissão para criar usuários
  if (
    // 1. A regra geral: proíbe se o novo usuário tiver acesso igual ou superior
    createUserDto.roleId <= creator.access &&
    // 2. A exceção: a regra acima NÃO se aplica se for um Admin (1) criando outro Admin (1)
    !(creator.access === 1 && createUserDto.roleId === 1)
  ) {
    throw new ForbiddenException(
      'Você não tem permissão para criar um usuário com nível de acesso igual ou superior ao seu.',
    );
  }

  // Verifica se o email já existe
  const existingUser = await this.prisma.usuario.findUnique({
    where: { email: createUserDto.email },
  });
  if (existingUser) {
    throw new BadRequestException('E-mail já cadastrado.');
  }

  const passwordHash = await this.HashingService.hash(createUserDto.senha);

  return this.prisma.usuario.create({
    data: {
      nome: createUserDto.nome,
      email: createUserDto.email,
      senhaHash: passwordHash,
      roleId: createUserDto.roleId,
    },
    select: {
      id_User: true,
      nome: true,
      email: true,
      senhaHash: false, // Never return password hash
      roleId: true,
    },
  });
}
Key Rules:
  • Users cannot create accounts with higher or equal privilege (except Admins creating Admins)
  • Email addresses must be unique
  • Passwords are automatically hashed with bcrypt before storage
  • Password hashes are never returned in API responses

Response

{
  "id_User": "550e8400-e29b-41d4-a716-446655440000",
  "nome": "João Silva",
  "email": "[email protected]",
  "roleId": 4
}

Listing Users

Get All Users

curl -X GET http://localhost:3000/users \
  -H "Authorization: Bearer <token>"

Implementation

apps/backend/src/users/users.service.ts
async findAll(creator: TokenDto) {
  // Lista apenas os usuários com nível de acesso menor ou igual ao do criador
  const users = await this.prisma.usuario.findMany({
    where: {
      roleId: {
        gte: creator.access, // Greater than or equal to creator's role ID
      },
    },
    select: {
      id_User: true,
      nome: true,
      email: true,
      senhaHash: false,
      roleId: true,
      role: {
        select: {
          id_Role: true,
          role: true,
          descricao: true,
        },
      },
    },
  });
  
  if (!users) {
    throw new BadGatewayException(`Erro ao buscar usuários.`);
  } else if (users.length === 0) {
    throw new NotFoundException(`Nenhum usuário encontrado.`);
  }

  return users;
}
Users can only see accounts with equal or lower privilege levels. For example, a Supervisor (roleId=3) can only see other Supervisors and Estagiários, not Admins or Secretários.

Response

[
  {
    "id_User": "550e8400-e29b-41d4-a716-446655440000",
    "nome": "Estagiário Padrão",
    "email": "[email protected]",
    "roleId": 4,
    "role": {
      "id_Role": 4,
      "role": "ESTAGIARIO",
      "descricao": "Estagiário em psicologia"
    }
  },
  {
    "id_User": "660e8400-e29b-41d4-a716-446655440001",
    "nome": "João Silva",
    "email": "[email protected]",
    "roleId": 4,
    "role": {
      "id_Role": 4,
      "role": "ESTAGIARIO",
      "descricao": "Estagiário em psicologia"
    }
  }
]

Get Single User

curl -X GET http://localhost:3000/users/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <token>"
apps/backend/src/users/users.service.ts
async findOne(id: UUID, creator: TokenDto) {
  // Lista apenas o usuário com nível de acesso menor ao do criador
  const user = await this.prisma.usuario.findFirst({
    where: {
      id_User: id,
      roleId: {
        gte: creator.access,
      },
    },
    select: {
      id_User: true,
      nome: true,
      email: true,
      senhaHash: false,
      roleId: true,
    },
  });
  
  if (!user) {
    throw new NotFoundException(
      `Usuário não encontrado ou acesso não permitido.`,
    );
  }

  return user;
}

Updating Users

API Endpoint

curl -X PUT http://localhost:3000/users/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "nome": "João Silva Santos",
    "email": "[email protected]"
  }'

Request Body (UpdateUserDto)

nome
string
Updated user name (optional, max 50 characters)
email
string
Updated email address (optional, max 100 characters, must be unique)
senha
string
New password (optional, min 8 characters, max 255 characters)
roleId cannot be changed through the update endpoint. Role assignments require special administrative procedures to prevent privilege escalation.

Permission Rules

apps/backend/src/users/users.service.ts
async update(id: UUID, updateUserDto: UpdateUserDto, creator: TokenDto) {
  const user = await this.prisma.usuario.findFirst({
    where: { id_User: id },
  });

  if (!user) {
    throw new NotFoundException(`Usuário não encontrado.`);
  }

  // REGRA 1: Você não pode editar um usuário com nível igual ou superior ao seu
  const canEditUser =
    creator.access < user.roleId ||
    (creator.access === 1 && user.roleId === 1);

  if (!canEditUser) {
    throw new ForbiddenException(
      'Você não tem permissão para editar um usuário com nível de acesso igual ou superior ao seu.',
    );
  }

  // Verifica se o email já existe
  if (updateUserDto.email && updateUserDto.email !== user.email) {
    const existingUser = await this.prisma.usuario.findFirst({
      where: {
        email: updateUserDto.email,
        id_User: { not: id },
      },
    });

    if (existingUser) {
      throw new BadRequestException(
        'O e-mail informado já está em uso por outro usuário.',
      );
    }
  }

  // Preparar dados para atualização - excluir roleId
  const dataToUpdate: Prisma.UsuarioUpdateInput = {
    nome: updateUserDto.nome,
    email: updateUserDto.email,
  };

  if (updateUserDto.senha) {
    dataToUpdate.senhaHash = await this.HashingService.hash(
      updateUserDto.senha,
    );
  }

  return this.prisma.usuario.update({
    where: { id_User: id },
    data: dataToUpdate,
    select: {
      id_User: true,
      nome: true,
      email: true,
      roleId: true,
    },
  });
}
Key Rules:
  • Users cannot edit accounts with higher or equal privilege (except Admins editing Admins)
  • Email must remain unique across all users
  • Passwords are automatically hashed if provided
  • roleId is immutable via update endpoint

Deleting Users

API Endpoint

curl -X DELETE http://localhost:3000/users/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <token>"

Permission Rules

apps/backend/src/users/users.service.ts
async remove(id: UUID, creator: TokenDto) {
  // 1. Busca o usuário que será deletado
  const user = await this.prisma.usuario.findUnique({
    where: { id_User: id },
  });

  if (!user) {
    throw new NotFoundException(`Usuário não encontrado.`);
  }

  // Você não pode deletar a si mesmo.
  if (creator.sub === user.id_User) {
    throw new ForbiddenException('Você não pode deletar sua própria conta.');
  }

  // Você não pode deletar um usuário com nível de acesso igual ou superior.
  const canDelete =
    creator.access < user.roleId ||
    (creator.access === 1 && user.roleId === 1);

  if (!canDelete) {
    throw new ForbiddenException(
      'Você não tem permissão para deletar um usuário com nível de acesso igual ou superior ao seu.',
    );
  }

  await this.prisma.usuario.delete({
    where: { id_User: id },
  });

  return { message: `Usuário com ID ${id} removido com sucesso.` };
}
Critical Rules:
  • Users cannot delete themselves
  • Users cannot delete accounts with higher or equal privilege (except Admins deleting Admins)
  • Related records are handled by database CASCADE rules

Response

{
  "message": "Usuário com ID 550e8400-e29b-41d4-a716-446655440000 removido com sucesso."
}

API Reference

Controller Routes

apps/backend/src/users/users.controller.ts
import { Controller, Get, Post, Body, Put, Param, Delete, UseGuards, Req } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('users')
@UseGuards(JwtAuthGuard) // All routes require authentication
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto, @Req() req: any) {
    return this.usersService.create(createUserDto, req.user);
  }

  @Get()
  findAll(@Req() req: any) {
    return this.usersService.findAll(req.user);
  }

  @Get(':id')
  findOne(@Param('id', ParseUUIDPipe) id: UUID, @Req() req: any) {
    return this.usersService.findOne(id, req.user);
  }

  @Put(':id')
  update(@Param('id', ParseUUIDPipe) id: UUID, @Body() updateUserDto: UpdateUserDto, @Req() req: any) {
    return this.usersService.update(id, updateUserDto, req.user);
  }

  @Delete(':id')
  remove(@Param('id', ParseUUIDPipe) id: UUID, @Req() req: any) {
    return this.usersService.remove(id, req.user);
  }
}

Common Errors

Cause: Attempting to manage a user account with higher or equal privilege level.Solution: Only Admins can manage other Admins. Secretários can manage Supervisors and Estagiários. Supervisors can only manage Estagiários.
Cause: Email address already exists in the system.Solution: Use a different email address. Email addresses must be unique.
Cause: User doesn’t exist or you don’t have permission to view them.Solution: Verify the user ID and ensure you have sufficient privileges to access this user.
Cause: Attempting to delete your own user account.Solution: Ask another administrator to delete your account if needed.
Cause: Invalid data in request body (e.g., email format, password length).Solution:
  • Nome: max 50 characters
  • Email: valid email format, max 100 characters
  • Senha: min 8 characters, max 255 characters
  • roleId: must be 1, 2, 3, or 4

Best Practices

Use Strong Passwords

Enforce minimum 8 characters with complexity requirements (uppercase, lowercase, numbers, special characters)

Verify Email Addresses

Consider implementing email verification to ensure users have access to their registered email

Audit User Changes

Log all user creation, updates, and deletions for security auditing

Regular Account Reviews

Periodically review user accounts to remove inactive users and adjust privileges

Roles & Permissions

Detailed information about the role hierarchy and permission system

Authentication

Learn about login, JWT tokens, and session management

Build docs developers (and LLMs) love