mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
feat(web,server): manage authorized devices (#2329)
* feat: manage authorized devices * chore: open api * get header from mobile app * write header from mobile app * styling * fix unit test * feat: use relative time * feat: update access time * fix: tests * chore: confirm wording * chore: bump test coverage thresholds * feat: add some icons * chore: icon tweaks --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
@@ -17,9 +17,16 @@ import { ISystemConfigRepository } from '../system-config';
|
||||
import { IUserRepository } from '../user';
|
||||
import { IUserTokenRepository } from '../user-token';
|
||||
import { newUserTokenRepositoryMock } from '../../test/user-token.repository.mock';
|
||||
import { LoginDetails } from '../auth';
|
||||
|
||||
const email = 'user@immich.com';
|
||||
const sub = 'my-auth-user-sub';
|
||||
const loginDetails: LoginDetails = {
|
||||
isSecure: true,
|
||||
clientIp: '127.0.0.1',
|
||||
deviceOS: '',
|
||||
deviceType: '',
|
||||
};
|
||||
|
||||
describe('OAuthService', () => {
|
||||
let sut: OAuthService;
|
||||
@@ -95,13 +102,13 @@ describe('OAuthService', () => {
|
||||
|
||||
describe('login', () => {
|
||||
it('should throw an error if OAuth is not enabled', async () => {
|
||||
await expect(sut.login({ url: '' }, true)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.login({ url: '' }, loginDetails)).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('should not allow auto registering', async () => {
|
||||
sut = create(systemConfigStub.noAutoRegister);
|
||||
userMock.getByEmail.mockResolvedValue(null);
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, true)).rejects.toBeInstanceOf(
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
||||
@@ -113,7 +120,7 @@ describe('OAuthService', () => {
|
||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, true)).resolves.toEqual(
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
loginResponseStub.user1oauth,
|
||||
);
|
||||
|
||||
@@ -129,7 +136,7 @@ describe('OAuthService', () => {
|
||||
userMock.create.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, true)).resolves.toEqual(
|
||||
await expect(sut.login({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
loginResponseStub.user1oauth,
|
||||
);
|
||||
|
||||
@@ -143,7 +150,7 @@ describe('OAuthService', () => {
|
||||
userMock.getByOAuthId.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
|
||||
await sut.login({ url: `app.immich:/?code=abc123` }, true);
|
||||
await sut.login({ url: `app.immich:/?code=abc123` }, loginDetails);
|
||||
|
||||
expect(callbackMock).toHaveBeenCalledWith('http://mobile-redirect', { state: 'state' }, { state: 'state' });
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SystemConfig } from '@app/infra/entities';
|
||||
import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { AuthType, AuthUserDto, LoginResponseDto } from '../auth';
|
||||
import { AuthCore } from '../auth/auth.core';
|
||||
import { AuthCore, LoginDetails } from '../auth/auth.core';
|
||||
import { ICryptoRepository } from '../crypto';
|
||||
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
|
||||
import { IUserRepository, UserCore, UserResponseDto } from '../user';
|
||||
@@ -39,7 +39,10 @@ export class OAuthService {
|
||||
return this.oauthCore.generateConfig(dto);
|
||||
}
|
||||
|
||||
async login(dto: OAuthCallbackDto, isSecure: boolean): Promise<{ response: LoginResponseDto; cookie: string[] }> {
|
||||
async login(
|
||||
dto: OAuthCallbackDto,
|
||||
loginDetails: LoginDetails,
|
||||
): Promise<{ response: LoginResponseDto; cookie: string[] }> {
|
||||
const profile = await this.oauthCore.callback(dto.url);
|
||||
|
||||
this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`);
|
||||
@@ -66,7 +69,7 @@ export class OAuthService {
|
||||
user = await this.userCore.createUser(this.oauthCore.asUser(profile));
|
||||
}
|
||||
|
||||
return this.authCore.createLoginResponse(user, AuthType.OAUTH, isSecure);
|
||||
return this.authCore.createLoginResponse(user, AuthType.OAUTH, loginDetails);
|
||||
}
|
||||
|
||||
public async link(user: AuthUserDto, dto: OAuthCallbackDto): Promise<UserResponseDto> {
|
||||
|
||||
Reference in New Issue
Block a user