module creation
This commit is contained in:
129
adapters/identity/persistence/inmemory/InMemoryAuthRepository.ts
Normal file
129
adapters/identity/persistence/inmemory/InMemoryAuthRepository.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { IAuthRepository } from '@gridpilot/core/identity/domain/repositories/IAuthRepository';
|
||||
import { IUserRepository, StoredUser } from '@gridpilot/core/identity/domain/repositories/IUserRepository';
|
||||
import { IPasswordHashingService } from '@gridpilot/core/identity/domain/services/PasswordHashingService';
|
||||
import { User } from '@gridpilot/core/identity/domain/entities/User';
|
||||
import { ILogger } from '@gridpilot/shared/logging/ILogger';
|
||||
import { EmailAddress } from '@gridpilot/core/identity/domain/value-objects/EmailAddress';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
export class InMemoryAuthRepository implements IAuthRepository {
|
||||
constructor(
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly passwordHashingService: IPasswordHashingService,
|
||||
private readonly logger: ILogger,
|
||||
) {}
|
||||
|
||||
async findByEmail(email: EmailAddress): Promise<User | null> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Finding user by email: ${email.value}`);
|
||||
const storedUser = await this.userRepository.findByEmail(email.value);
|
||||
if (!storedUser) {
|
||||
return null;
|
||||
}
|
||||
return User.fromStored(storedUser);
|
||||
}
|
||||
|
||||
async save(user: User): Promise<void> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Saving user with email: ${user.getEmail()}`);
|
||||
|
||||
const userId = user.getId().value;
|
||||
const userEmail = user.getEmail() ?? '';
|
||||
const userDisplayName = user.getDisplayName();
|
||||
const userPasswordHash = user.getPasswordHash()?.value ?? '';
|
||||
|
||||
const storedUser: StoredUser = {
|
||||
id: userId,
|
||||
email: userEmail,
|
||||
passwordHash: userPasswordHash,
|
||||
displayName: userDisplayName,
|
||||
salt: '', // In-memory doesn't use actual salt, but Stub has to provide.
|
||||
primaryDriverId: user.getPrimaryDriverId() ?? undefined,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
// Check if user exists to decide between create or update
|
||||
const existingStoredUser = await this.userRepository.findById(userId);
|
||||
if (existingStoredUser) {
|
||||
// For update, ensure createdAt is preserved from existing user
|
||||
const updatedStoredUser: StoredUser = {
|
||||
...storedUser,
|
||||
createdAt: existingStoredUser.createdAt,
|
||||
};
|
||||
await this.userRepository.update(updatedStoredUser);
|
||||
} else {
|
||||
await this.userRepository.create(storedUser);
|
||||
}
|
||||
}
|
||||
|
||||
async create(user: User, passwordPlain: string): Promise<User> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Creating user with email: ${user.getEmail()}`);
|
||||
const passwordHashValue = await this.passwordHashingService.hash(passwordPlain);
|
||||
|
||||
// Ensure email is not undefined before using
|
||||
const userEmail = user.getEmail() ?? '';
|
||||
const userDisplayName = user.getDisplayName();
|
||||
const userId = user.getId().value;
|
||||
|
||||
const storedUser: StoredUser = {
|
||||
id: userId,
|
||||
email: userEmail,
|
||||
passwordHash: passwordHashValue,
|
||||
displayName: userDisplayName,
|
||||
salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it.
|
||||
primaryDriverId: user.getPrimaryDriverId() ?? undefined,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
const createdStoredUser = await this.userRepository.create(storedUser);
|
||||
return User.fromStored(createdStoredUser);
|
||||
}
|
||||
|
||||
async update(user: User): Promise<User> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Updating user with email: ${user.getEmail()}`);
|
||||
// Ensure values are not undefined before using
|
||||
const userId = user.getId().value;
|
||||
const userEmail = user.getEmail() ?? '';
|
||||
const userPasswordHash = user.getPasswordHash()?.value ?? '';
|
||||
const userDisplayName = user.getDisplayName();
|
||||
|
||||
const storedUser: StoredUser = {
|
||||
id: userId,
|
||||
email: userEmail,
|
||||
passwordHash: userPasswordHash,
|
||||
displayName: userDisplayName,
|
||||
salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it.
|
||||
primaryDriverId: user.getPrimaryDriverId() ?? undefined,
|
||||
createdAt: new Date(), // Assuming creation date is maintained or updated within UserRepository
|
||||
};
|
||||
const updatedStoredUser = await this.userRepository.update(storedUser);
|
||||
return User.fromStored(updatedStoredUser);
|
||||
}
|
||||
|
||||
async delete(userId: string): Promise<boolean> {
|
||||
// Deletion needs to be implemented in InMemoryUserRepository and exposed via IUserRepository
|
||||
// For now, it's not supported.
|
||||
this.logger.warn(`[InMemoryAuthRepository] Delete operation not implemented for user: ${userId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
async userExists(email: string): Promise<boolean> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Checking if user exists for email: ${email}`);
|
||||
return this.userRepository.emailExists(email);
|
||||
}
|
||||
|
||||
async verifyPassword(email: string, passwordPlain: string): Promise<User | null> {
|
||||
this.logger.debug(`[InMemoryAuthRepository] Verifying password for user with email: ${email}`);
|
||||
const user = await this.findByEmail(EmailAddress.create(email)); // Use EmailAddress value object
|
||||
if (!user) {
|
||||
this.logger.warn(`[InMemoryAuthRepository] User not found for email: ${email}`);
|
||||
return null;
|
||||
}
|
||||
const isPasswordValid = await this.passwordHashingService.verify(
|
||||
passwordPlain,
|
||||
user.getPasswordHash()?.value ?? '', // Access value and provide fallback
|
||||
);
|
||||
if (!isPasswordValid) {
|
||||
this.logger.warn(`[InMemoryAuthRepository] Invalid password for user with email: ${email}`);
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user