移除账号启用状态统一使用归档
This commit is contained in:
@@ -47,4 +47,28 @@ describe('AuthService — authentication boundaries', () => {
|
||||
).rejects.toThrow('账号已失效');
|
||||
expect(userRepo.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('allows a legacy disabled user because archive is the only account status', async () => {
|
||||
const userRepo = {
|
||||
findOne: jest.fn().mockResolvedValue({
|
||||
id: 3,
|
||||
username: 'legacy-disabled',
|
||||
name: '旧账号',
|
||||
passwordHash: await bcrypt.hash('secret', 4),
|
||||
isActive: false,
|
||||
isArchived: false,
|
||||
roles: [],
|
||||
}),
|
||||
save: jest.fn(),
|
||||
};
|
||||
const service = new AuthService(
|
||||
userRepo as never,
|
||||
{ sign: jest.fn().mockReturnValue('token') } as never,
|
||||
{ getUserPermissions: jest.fn().mockResolvedValue([]) } as never,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.login({ username: 'legacy-disabled', password: 'secret' }, '192.0.2.11'),
|
||||
).resolves.toEqual(expect.objectContaining({ access_token: 'token' }));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ export class AuthService {
|
||||
this.recordFailedAttempt(attemptKey);
|
||||
throw new UnauthorizedException('用户名或密码错误');
|
||||
}
|
||||
if (!user.isActive || user.isArchived) {
|
||||
if (user.isArchived) {
|
||||
throw new UnauthorizedException('账号已失效,请联系管理员');
|
||||
}
|
||||
const valid = await bcrypt.compare(dto.password, user.passwordHash);
|
||||
|
||||
@@ -50,16 +50,32 @@ describe('JwtStrategy', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ id: 7, isActive: false, isArchived: false, roles: [] }],
|
||||
[{ id: 7, isActive: true, isArchived: true, roles: [] }],
|
||||
[null],
|
||||
])('rejects disabled, archived, or deleted users', async (user) => {
|
||||
const userRepo = { findOne: jest.fn().mockResolvedValue(user) };
|
||||
it.each([[{ id: 7, isArchived: true, roles: [] }], [null]])(
|
||||
'rejects archived or deleted users',
|
||||
async (user) => {
|
||||
const userRepo = { findOne: jest.fn().mockResolvedValue(user) };
|
||||
const strategy = new JwtStrategy(config as never, userRepo as never);
|
||||
|
||||
await expect(strategy.validate({ sub: 7, username: 'teacher' })).rejects.toBeInstanceOf(
|
||||
UnauthorizedException,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it('accepts a legacy disabled user when the account is not archived', async () => {
|
||||
const userRepo = {
|
||||
findOne: jest.fn().mockResolvedValue({
|
||||
id: 7,
|
||||
username: 'teacher',
|
||||
isActive: false,
|
||||
isArchived: false,
|
||||
roles: [],
|
||||
}),
|
||||
};
|
||||
const strategy = new JwtStrategy(config as never, userRepo as never);
|
||||
|
||||
await expect(strategy.validate({ sub: 7, username: 'teacher' })).rejects.toBeInstanceOf(
|
||||
UnauthorizedException,
|
||||
await expect(strategy.validate({ sub: 7, username: 'teacher' })).resolves.toEqual(
|
||||
expect.objectContaining({ id: 7, username: 'teacher' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
where: { id: payload.sub },
|
||||
relations: ['roles', 'roles.permissions'],
|
||||
});
|
||||
if (!user || !user.isActive || user.isArchived) {
|
||||
if (!user || user.isArchived) {
|
||||
throw new UnauthorizedException('账号已失效,请重新登录');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user