feat: secure penalty catalog management

This commit is contained in:
Bastian Wagner
2026-08-01 13:17:44 +02:00
parent 45658e42fa
commit 17228a52db
12 changed files with 629 additions and 98 deletions

View File

@@ -65,6 +65,30 @@ describe('TeamAccessService', () => {
await expect(service.assertManager(2, 9)).resolves.toBeUndefined();
});
it('uses repositories from the supplied transaction manager', async () => {
const transactionalUserRepository = {
findOne: jest.fn().mockResolvedValue({
id: 2,
role: { id: RoleEnum.user },
}),
};
const transactionalPlayerRepository = {
find: jest
.fn()
.mockResolvedValue([{ active: true, teamRole: { id: 3 } }]),
};
const manager = {
getRepository: jest
.fn()
.mockReturnValueOnce(transactionalUserRepository)
.mockReturnValueOnce(transactionalPlayerRepository),
};
await expect(service.assertManager(2, 9, manager as any)).resolves.toBeUndefined();
expect(userRepository.findOne).not.toHaveBeenCalled();
expect(playerRepository.find).not.toHaveBeenCalled();
});
it.each([
['a player', [{ active: true, teamRole: { id: 1 } }]],
['a second treasurer', [{ active: true, teamRole: { id: 2 } }]],

View File

@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Player } from '../players/entities/player.entity';
import { RoleEnum } from '../roles/roles.enum';
import { User } from '../users/entities/user.entity';
import { Repository } from 'typeorm';
import { EntityManager, Repository } from 'typeorm';
@Injectable()
export class TeamAccessService {
@@ -14,23 +14,35 @@ export class TeamAccessService {
private readonly playerRepository: Repository<Player>,
) {}
async assertMember(userId: number, teamId: number): Promise<void> {
await this.assertMinimumRole(userId, teamId, 1);
async assertMember(
userId: number,
teamId: number,
manager?: EntityManager,
): Promise<void> {
await this.assertMinimumRole(userId, teamId, 1, manager);
}
async assertManager(userId: number, teamId: number): Promise<void> {
await this.assertMinimumRole(userId, teamId, 3);
async assertManager(
userId: number,
teamId: number,
manager?: EntityManager,
): Promise<void> {
await this.assertMinimumRole(userId, teamId, 3, manager);
}
private async assertMinimumRole(
userId: number,
teamId: number,
minimumRole: number,
manager?: EntityManager,
): Promise<void> {
const user = await this.userRepository.findOne({ where: { id: userId } });
const userRepository = manager?.getRepository(User) ?? this.userRepository;
const playerRepository =
manager?.getRepository(Player) ?? this.playerRepository;
const user = await userRepository.findOne({ where: { id: userId } });
if (user?.role?.id === RoleEnum.admin) return;
const players = await this.playerRepository.find({
const players = await playerRepository.find({
where: { user: { id: userId }, team: { id: teamId } },
});
const highestActiveRole = players

View File

@@ -31,5 +31,6 @@ import { PenaltyEntity } from '../penalty/entities/penalty.entity';
],
controllers: [TeamsController, PublicTeamsController],
providers: [TeamsService, TeamAccessService, PublicTeamAccessService],
exports: [TeamAccessService],
})
export class TeamsModule {}