fix: correct Argon2 password hash and organization UUIDs in migrations
All checks were successful
CI/CD Pipeline / Backend - Build, Test & Push (push) Successful in 7m52s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Successful in 11m40s
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Deployment Summary (push) Successful in 1s
CI/CD Pipeline / Deploy to Portainer (push) Successful in 14s
CI/CD Pipeline / Discord Notification (Failure) (push) Has been skipped
CI/CD Pipeline / Discord Notification (Success) (push) Successful in 1s

- Fixed test user migration to use real Argon2id hash for Password123!
- Replaced random uuidv4() with fixed UUIDs in organization seeds
- Updated auth.service.ts to use DEFAULT_ORG_ID constant
- Added ON CONFLICT DO UPDATE to migration for existing users
- Ensures consistent UUIDs across environments (dev/preprod/prod)

Fixes:
- Registration 500 error (foreign key constraint violation)
- Login 401 error (invalid password hash)
- Organization ID mismatch between migrations and application code

Test users (Password: Password123!):
- admin@xpeditis.com (ADMIN)
- manager@xpeditis.com (MANAGER)
- user@xpeditis.com (USER)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
David 2025-11-20 20:05:22 +01:00
parent 6e3191b50e
commit dc62166272
3 changed files with 28 additions and 30 deletions

View File

@ -11,6 +11,7 @@ import * as argon2 from 'argon2';
import { UserRepository, USER_REPOSITORY } from '@domain/ports/out/user.repository'; import { UserRepository, USER_REPOSITORY } from '@domain/ports/out/user.repository';
import { User, UserRole } from '@domain/entities/user.entity'; import { User, UserRole } from '@domain/entities/user.entity';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { DEFAULT_ORG_ID } from '@infrastructure/persistence/typeorm/seeds/test-organizations.seed';
export interface JwtPayload { export interface JwtPayload {
sub: string; // user ID sub: string; // user ID
@ -221,8 +222,7 @@ export class AuthService {
// Use default organization "Test Freight Forwarder Inc." if not provided // Use default organization "Test Freight Forwarder Inc." if not provided
// This ID comes from the seed migration 1730000000006-SeedOrganizations // This ID comes from the seed migration 1730000000006-SeedOrganizations
const defaultOrgId = '1fa9a565-f3c8-4e11-9b30-120d1052cef0'; this.logger.log(`Using default organization ID for user registration: ${DEFAULT_ORG_ID}`);
this.logger.log(`Using default organization ID for user registration: ${defaultOrgId}`); return DEFAULT_ORG_ID;
return defaultOrgId;
} }
} }

View File

@ -2,36 +2,26 @@
* Seed Test Users Migration * Seed Test Users Migration
* *
* Seeds test users for development and testing * Seeds test users for development and testing
* Password for all users: AdminPassword123! * Password for all users: Password123!
* Hash generated with bcrypt rounds=10 * Hash generated with Argon2id
*/ */
import { MigrationInterface, QueryRunner } from 'typeorm'; import { MigrationInterface, QueryRunner } from 'typeorm';
import { v4 as uuidv4 } from 'uuid'; import { DEFAULT_ORG_ID } from '../seeds/test-organizations.seed';
export class SeedTestUsers1730000000007 implements MigrationInterface { export class SeedTestUsers1730000000007 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> { public async up(queryRunner: QueryRunner): Promise<void> {
// Get the first organization ID from the database // Use fixed organization ID from seed
const result = await queryRunner.query(` const organizationId = DEFAULT_ORG_ID;
SELECT id FROM organizations WHERE type = 'FREIGHT_FORWARDER' LIMIT 1
`);
if (result.length === 0) { // Pre-hashed password: Password123! (Argon2id)
throw new Error( // Generated with: argon2.hash('Password123!', { type: argon2.argon2id, memoryCost: 65536, timeCost: 3, parallelism: 4 })
'No organization found to seed users. Please run organization seed migration first.' const passwordHash = '$argon2id$v=19$m=65536,t=3,p=4$Uj+yeQiaqgBFqyTJ5FX3Cw$wpRCYORyFwjQFSuO3gpmzh10gx9wjYFOCvVZ8TVaP8Q';
);
}
const organizationId = result[0].id; // Fixed UUIDs for test users (matching existing data in database)
// Pre-hashed password: AdminPassword123! (bcrypt rounds=10)
// This hash is deterministic for testing purposes
const passwordHash = '$2b$10$5qK9KqP7YqXZ5Z0kZ0kZ0OqK9KqP7YqXZ5Z0kZ0kZ0OqK9KqP7YqX';
// Insert test users
const users = [ const users = [
{ {
id: uuidv4(), id: 'c59ae389-da30-4533-be0c-fdfe6ac945de',
email: 'admin@xpeditis.com', email: 'admin@xpeditis.com',
passwordHash, passwordHash,
firstName: 'Admin', firstName: 'Admin',
@ -39,7 +29,7 @@ export class SeedTestUsers1730000000007 implements MigrationInterface {
role: 'ADMIN', role: 'ADMIN',
}, },
{ {
id: uuidv4(), id: '496ba881-c055-4b78-b0c0-6c048215253b',
email: 'manager@xpeditis.com', email: 'manager@xpeditis.com',
passwordHash, passwordHash,
firstName: 'Manager', firstName: 'Manager',
@ -47,7 +37,7 @@ export class SeedTestUsers1730000000007 implements MigrationInterface {
role: 'MANAGER', role: 'MANAGER',
}, },
{ {
id: uuidv4(), id: '361b409d-a32b-4ff9-a61b-e927450c1daf',
email: 'user@xpeditis.com', email: 'user@xpeditis.com',
passwordHash, passwordHash,
firstName: 'Regular', firstName: 'Regular',
@ -66,7 +56,10 @@ export class SeedTestUsers1730000000007 implements MigrationInterface {
'${user.firstName}', '${user.lastName}', '${user.role}', '${user.firstName}', '${user.lastName}', '${user.role}',
'${organizationId}', NULL, true, NOW(), NOW() '${organizationId}', NULL, true, NOW(), NOW()
) )
ON CONFLICT ("email") DO NOTHING; ON CONFLICT ("email") DO UPDATE SET
"password_hash" = EXCLUDED."password_hash",
"organization_id" = EXCLUDED."organization_id",
"updated_at" = NOW();
`); `);
} }
@ -74,7 +67,7 @@ export class SeedTestUsers1730000000007 implements MigrationInterface {
console.log(' - admin@xpeditis.com (ADMIN)'); console.log(' - admin@xpeditis.com (ADMIN)');
console.log(' - manager@xpeditis.com (MANAGER)'); console.log(' - manager@xpeditis.com (MANAGER)');
console.log(' - user@xpeditis.com (USER)'); console.log(' - user@xpeditis.com (USER)');
console.log(' - Password: AdminPassword123!'); console.log(' - Password: Password123!');
} }
public async down(queryRunner: QueryRunner): Promise<void> { public async down(queryRunner: QueryRunner): Promise<void> {

View File

@ -19,9 +19,14 @@ export interface OrganizationSeed {
isActive: boolean; isActive: boolean;
} }
// Fixed UUIDs for consistent seed data across environments
export const DEFAULT_ORG_ID = 'c6042c7b-cffe-4fef-94f6-f27c2d0eb809';
export const DEMO_CARRIER_ID = '462001d1-6829-4554-a4e1-477667edab6b';
export const SAMPLE_SHIPPER_ID = '39e49605-5292-4935-9bff-c4abb547d3b9';
export const organizationSeeds: OrganizationSeed[] = [ export const organizationSeeds: OrganizationSeed[] = [
{ {
id: uuidv4(), id: DEFAULT_ORG_ID,
name: 'Test Freight Forwarder Inc.', name: 'Test Freight Forwarder Inc.',
type: 'FREIGHT_FORWARDER', type: 'FREIGHT_FORWARDER',
scac: null, scac: null,
@ -33,7 +38,7 @@ export const organizationSeeds: OrganizationSeed[] = [
isActive: true, isActive: true,
}, },
{ {
id: uuidv4(), id: DEMO_CARRIER_ID,
name: 'Demo Shipping Company', name: 'Demo Shipping Company',
type: 'CARRIER', type: 'CARRIER',
scac: 'DEMO', scac: 'DEMO',
@ -45,7 +50,7 @@ export const organizationSeeds: OrganizationSeed[] = [
isActive: true, isActive: true,
}, },
{ {
id: uuidv4(), id: SAMPLE_SHIPPER_ID,
name: 'Sample Shipper Ltd.', name: 'Sample Shipper Ltd.',
type: 'SHIPPER', type: 'SHIPPER',
scac: null, scac: null,