|
|
|
|
@ -1,10 +1,10 @@
|
|
|
|
|
import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
|
|
|
|
|
import { DateTime } from 'luxon';
|
|
|
|
|
import { AuthDto, SignUpDto } from 'src/dtos/auth.dto';
|
|
|
|
|
import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
|
|
|
|
|
import { UserEntity } from 'src/entities/user.entity';
|
|
|
|
|
import { AuthType, Permission } from 'src/enum';
|
|
|
|
|
import { AuthService } from 'src/services/auth.service';
|
|
|
|
|
import { sessionStub } from 'test/fixtures/session.stub';
|
|
|
|
|
import { sharedLinkStub } from 'test/fixtures/shared-link.stub';
|
|
|
|
|
import { systemConfigStub } from 'test/fixtures/system-config.stub';
|
|
|
|
|
import { userStub } from 'test/fixtures/user.stub';
|
|
|
|
|
@ -97,17 +97,19 @@ describe('AuthService', () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should successfully log the user in', async () => {
|
|
|
|
|
mocks.user.getByEmail.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
const user = { ...factory.user(), password: 'immich_password' } as UserEntity;
|
|
|
|
|
const session = factory.session();
|
|
|
|
|
mocks.user.getByEmail.mockResolvedValue(user);
|
|
|
|
|
mocks.session.create.mockResolvedValue(session);
|
|
|
|
|
|
|
|
|
|
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual({
|
|
|
|
|
accessToken: 'cmFuZG9tLWJ5dGVz',
|
|
|
|
|
userId: 'user-id',
|
|
|
|
|
userEmail: 'immich@test.com',
|
|
|
|
|
name: 'immich_name',
|
|
|
|
|
profileImagePath: '',
|
|
|
|
|
isAdmin: false,
|
|
|
|
|
shouldChangePassword: false,
|
|
|
|
|
userId: user.id,
|
|
|
|
|
userEmail: user.email,
|
|
|
|
|
name: user.name,
|
|
|
|
|
profileImagePath: user.profileImagePath,
|
|
|
|
|
isAdmin: user.isAdmin,
|
|
|
|
|
shouldChangePassword: user.shouldChangePassword,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
|
|
|
|
|
@ -256,8 +258,14 @@ describe('AuthService', () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should validate using authorization header', async () => {
|
|
|
|
|
mocks.user.get.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
|
|
|
|
const session = factory.session();
|
|
|
|
|
const sessionWithToken = {
|
|
|
|
|
id: session.id,
|
|
|
|
|
updatedAt: session.updatedAt,
|
|
|
|
|
user: factory.authUser(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
sut.authenticate({
|
|
|
|
|
@ -266,8 +274,8 @@ describe('AuthService', () => {
|
|
|
|
|
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
|
|
|
|
|
}),
|
|
|
|
|
).resolves.toEqual({
|
|
|
|
|
user: userStub.user1,
|
|
|
|
|
session: sessionStub.valid,
|
|
|
|
|
user: sessionWithToken.user,
|
|
|
|
|
session: { id: session.id },
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@ -371,7 +379,14 @@ describe('AuthService', () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should return an auth dto', async () => {
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
|
|
|
|
const session = factory.session();
|
|
|
|
|
const sessionWithToken = {
|
|
|
|
|
id: session.id,
|
|
|
|
|
updatedAt: session.updatedAt,
|
|
|
|
|
user: factory.authUser(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
sut.authenticate({
|
|
|
|
|
@ -380,13 +395,20 @@ describe('AuthService', () => {
|
|
|
|
|
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
|
|
|
|
|
}),
|
|
|
|
|
).resolves.toEqual({
|
|
|
|
|
user: userStub.user1,
|
|
|
|
|
session: sessionStub.valid,
|
|
|
|
|
user: sessionWithToken.user,
|
|
|
|
|
session: { id: session.id },
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should throw if admin route and not an admin', async () => {
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
|
|
|
|
const session = factory.session();
|
|
|
|
|
const sessionWithToken = {
|
|
|
|
|
id: session.id,
|
|
|
|
|
updatedAt: session.updatedAt,
|
|
|
|
|
user: factory.authUser(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
sut.authenticate({
|
|
|
|
|
@ -398,8 +420,15 @@ describe('AuthService', () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update when access time exceeds an hour', async () => {
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionStub.inactive as any);
|
|
|
|
|
mocks.session.update.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
const session = factory.session({ updatedAt: DateTime.now().minus({ hours: 2 }).toJSDate() });
|
|
|
|
|
const sessionWithToken = {
|
|
|
|
|
id: session.id,
|
|
|
|
|
updatedAt: session.updatedAt,
|
|
|
|
|
user: factory.authUser(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
|
|
|
|
|
mocks.session.update.mockResolvedValue(session);
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
sut.authenticate({
|
|
|
|
|
@ -408,7 +437,8 @@ describe('AuthService', () => {
|
|
|
|
|
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
|
|
|
|
|
}),
|
|
|
|
|
).resolves.toBeDefined();
|
|
|
|
|
expect(mocks.session.update.mock.calls[0][1]).toMatchObject({ id: 'not_active', updatedAt: expect.any(Date) });
|
|
|
|
|
|
|
|
|
|
expect(mocks.session.update).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -506,7 +536,7 @@ describe('AuthService', () => {
|
|
|
|
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled);
|
|
|
|
|
mocks.user.getByEmail.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.user.update.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
mocks.session.create.mockResolvedValue(factory.session());
|
|
|
|
|
|
|
|
|
|
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
|
|
|
|
oauthResponse,
|
|
|
|
|
@ -535,7 +565,7 @@ describe('AuthService', () => {
|
|
|
|
|
mocks.user.getByEmail.mockResolvedValue(void 0);
|
|
|
|
|
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.user.create.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
mocks.session.create.mockResolvedValue(factory.session());
|
|
|
|
|
|
|
|
|
|
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
|
|
|
|
oauthResponse,
|
|
|
|
|
@ -550,7 +580,7 @@ describe('AuthService', () => {
|
|
|
|
|
mocks.user.getByEmail.mockResolvedValue(void 0);
|
|
|
|
|
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.user.create.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
mocks.session.create.mockResolvedValue(factory.session());
|
|
|
|
|
mocks.oauth.getProfile.mockResolvedValue({ sub, email: undefined });
|
|
|
|
|
|
|
|
|
|
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf(
|
|
|
|
|
@ -572,7 +602,7 @@ describe('AuthService', () => {
|
|
|
|
|
it(`should use the mobile redirect override for a url of ${url}`, async () => {
|
|
|
|
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithMobileOverride);
|
|
|
|
|
mocks.user.getByOAuthId.mockResolvedValue(userStub.user1);
|
|
|
|
|
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
|
|
|
|
mocks.session.create.mockResolvedValue(factory.session());
|
|
|
|
|
|
|
|
|
|
await sut.callback({ url }, loginDetails);
|
|
|
|
|
|
|
|
|
|
|