2021-08-08 19:58:54 +00:00
|
|
|
/*
|
2022-09-25 00:04:20 +00:00
|
|
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
2021-08-08 19:58:54 +00:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
import { ConfigModule } from '@nestjs/config';
|
|
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
|
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
|
|
import { Repository } from 'typeorm';
|
|
|
|
|
|
|
|
import appConfigMock from '../config/mock/app.config.mock';
|
|
|
|
import authConfigMock from '../config/mock/auth.config.mock';
|
2022-01-06 21:01:39 +00:00
|
|
|
import {
|
|
|
|
InvalidCredentialsError,
|
|
|
|
NoLocalIdentityError,
|
2022-09-25 00:04:20 +00:00
|
|
|
PasswordTooWeakError,
|
2022-01-06 21:01:39 +00:00
|
|
|
} from '../errors/errors';
|
2021-08-08 19:58:54 +00:00
|
|
|
import { LoggerModule } from '../logger/logger.module';
|
|
|
|
import { User } from '../users/user.entity';
|
|
|
|
import { checkPassword, hashPassword } from '../utils/password';
|
|
|
|
import { Identity } from './identity.entity';
|
|
|
|
import { IdentityService } from './identity.service';
|
|
|
|
import { ProviderType } from './provider-type.enum';
|
|
|
|
|
|
|
|
describe('IdentityService', () => {
|
|
|
|
let service: IdentityService;
|
|
|
|
let user: User;
|
|
|
|
let identityRepo: Repository<Identity>;
|
2022-09-25 00:04:20 +00:00
|
|
|
const password = 'AStrongPasswordToStartWith123';
|
2021-08-08 19:58:54 +00:00
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
|
|
providers: [
|
|
|
|
IdentityService,
|
|
|
|
{
|
|
|
|
provide: getRepositoryToken(Identity),
|
|
|
|
useClass: Repository,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
imports: [
|
|
|
|
ConfigModule.forRoot({
|
|
|
|
isGlobal: true,
|
|
|
|
load: [appConfigMock, authConfigMock],
|
|
|
|
}),
|
|
|
|
LoggerModule,
|
|
|
|
],
|
|
|
|
}).compile();
|
|
|
|
|
|
|
|
service = module.get<IdentityService>(IdentityService);
|
|
|
|
user = User.create('test', 'Testy') as User;
|
|
|
|
identityRepo = module.get<Repository<Identity>>(
|
|
|
|
getRepositoryToken(Identity),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('createLocalIdentity', () => {
|
|
|
|
it('works', async () => {
|
|
|
|
jest
|
|
|
|
.spyOn(identityRepo, 'save')
|
|
|
|
.mockImplementationOnce(
|
|
|
|
async (identity: Identity): Promise<Identity> => identity,
|
|
|
|
);
|
|
|
|
const identity = await service.createLocalIdentity(user, password);
|
|
|
|
await checkPassword(password, identity.passwordHash ?? '').then(
|
|
|
|
(result) => expect(result).toBeTruthy(),
|
|
|
|
);
|
2021-12-05 21:10:59 +00:00
|
|
|
expect(await identity.user).toEqual(user);
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateLocalPassword', () => {
|
|
|
|
beforeEach(async () => {
|
|
|
|
jest
|
|
|
|
.spyOn(identityRepo, 'save')
|
|
|
|
.mockImplementationOnce(
|
|
|
|
async (identity: Identity): Promise<Identity> => identity,
|
|
|
|
)
|
|
|
|
.mockImplementationOnce(
|
|
|
|
async (identity: Identity): Promise<Identity> => identity,
|
|
|
|
);
|
|
|
|
const identity = await service.createLocalIdentity(user, password);
|
|
|
|
user.identities = Promise.resolve([identity]);
|
|
|
|
});
|
|
|
|
it('works', async () => {
|
2022-09-25 00:04:20 +00:00
|
|
|
const newPassword = 'ThisIsAStrongNewP@ssw0rd';
|
2021-08-08 19:58:54 +00:00
|
|
|
const identity = await service.updateLocalPassword(user, newPassword);
|
|
|
|
await checkPassword(newPassword, identity.passwordHash ?? '').then(
|
|
|
|
(result) => expect(result).toBeTruthy(),
|
|
|
|
);
|
2021-12-05 21:10:59 +00:00
|
|
|
expect(await identity.user).toEqual(user);
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
|
|
|
it('fails, when user has no local identity', async () => {
|
|
|
|
user.identities = Promise.resolve([]);
|
|
|
|
await expect(service.updateLocalPassword(user, password)).rejects.toThrow(
|
2022-01-06 21:01:39 +00:00
|
|
|
NoLocalIdentityError,
|
2021-08-08 19:58:54 +00:00
|
|
|
);
|
|
|
|
});
|
2022-09-25 00:04:20 +00:00
|
|
|
it('fails, when new password is too weak', async () => {
|
|
|
|
await expect(
|
|
|
|
service.updateLocalPassword(user, 'password1'),
|
|
|
|
).rejects.toThrow(PasswordTooWeakError);
|
|
|
|
});
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('loginWithLocalIdentity', () => {
|
|
|
|
it('works', async () => {
|
2021-11-14 19:56:17 +00:00
|
|
|
const identity = Identity.create(
|
|
|
|
user,
|
|
|
|
ProviderType.LOCAL,
|
|
|
|
false,
|
|
|
|
) as Identity;
|
2021-08-08 19:58:54 +00:00
|
|
|
identity.passwordHash = await hashPassword(password);
|
|
|
|
user.identities = Promise.resolve([identity]);
|
2022-01-03 22:41:34 +00:00
|
|
|
await expect(service.checkLocalPassword(user, password)).resolves.toEqual(
|
|
|
|
undefined,
|
|
|
|
);
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
|
|
|
describe('fails', () => {
|
2022-01-06 21:01:39 +00:00
|
|
|
it('when the password is wrong', async () => {
|
|
|
|
const identity = Identity.create(
|
|
|
|
user,
|
|
|
|
ProviderType.LOCAL,
|
|
|
|
false,
|
|
|
|
) as Identity;
|
|
|
|
identity.passwordHash = await hashPassword(password);
|
|
|
|
user.identities = Promise.resolve([identity]);
|
2021-08-08 19:58:54 +00:00
|
|
|
await expect(
|
2022-01-06 21:01:39 +00:00
|
|
|
service.checkLocalPassword(user, 'wrong_password'),
|
|
|
|
).rejects.toThrow(InvalidCredentialsError);
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
2022-01-06 21:01:39 +00:00
|
|
|
it('when user has no local identity', async () => {
|
2021-08-08 19:58:54 +00:00
|
|
|
user.identities = Promise.resolve([]);
|
|
|
|
await expect(
|
2022-01-06 21:01:39 +00:00
|
|
|
service.checkLocalPassword(user, password),
|
|
|
|
).rejects.toThrow(NoLocalIdentityError);
|
2021-08-08 19:58:54 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|