mirror of
https://github.com/immich-app/immich.git
synced 2025-03-11 02:23:09 -05:00
refactor: api key spec to use factories (#16776)
This commit is contained in:
parent
fe959b2f05
commit
e97df503f2
9 changed files with 150 additions and 82 deletions
|
@ -4,13 +4,13 @@ import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpda
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { APIKeyService } from 'src/services/api-key.service';
|
import { ApiKeyService } from 'src/services/api-key.service';
|
||||||
import { UUIDParamDto } from 'src/validation';
|
import { UUIDParamDto } from 'src/validation';
|
||||||
|
|
||||||
@ApiTags('API Keys')
|
@ApiTags('API Keys')
|
||||||
@Controller('api-keys')
|
@Controller('api-keys')
|
||||||
export class APIKeyController {
|
export class APIKeyController {
|
||||||
constructor(private service: APIKeyService) {}
|
constructor(private service: ApiKeyService) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@Authenticated({ permission: Permission.API_KEY_CREATE })
|
@Authenticated({ permission: Permission.API_KEY_CREATE })
|
||||||
|
|
|
@ -29,6 +29,15 @@ export type AuthApiKey = {
|
||||||
permissions: Permission[];
|
permissions: Permission[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiKey = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
userId: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
permissions: Permission[];
|
||||||
|
};
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class ApiKeyRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
create(dto: Insertable<ApiKeys>) {
|
create(dto: Insertable<ApiKeys>) {
|
||||||
return this.db.insertInto('api_keys').values(dto).returningAll().executeTakeFirstOrThrow();
|
return this.db.insertInto('api_keys').values(dto).returning(columns.apiKey).executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(userId: string, id: string, dto: Updateable<ApiKeys>) {
|
async update(userId: string, id: string, dto: Updateable<ApiKeys>) {
|
||||||
|
@ -21,7 +21,7 @@ export class ApiKeyRepository {
|
||||||
.set(dto)
|
.set(dto)
|
||||||
.where('api_keys.userId', '=', userId)
|
.where('api_keys.userId', '=', userId)
|
||||||
.where('id', '=', asUuid(id))
|
.where('id', '=', asUuid(id))
|
||||||
.returningAll()
|
.returning(columns.apiKey)
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,112 +1,145 @@
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { APIKeyService } from 'src/services/api-key.service';
|
import { ApiKeyService } from 'src/services/api-key.service';
|
||||||
import { keyStub } from 'test/fixtures/api-key.stub';
|
import { factory, newUuid } from 'test/small.factory';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
|
||||||
import { newTestService, ServiceMocks } from 'test/utils';
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
|
||||||
describe(APIKeyService.name, () => {
|
describe(ApiKeyService.name, () => {
|
||||||
let sut: APIKeyService;
|
let sut: ApiKeyService;
|
||||||
let mocks: ServiceMocks;
|
let mocks: ServiceMocks;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
({ sut, mocks } = newTestService(APIKeyService));
|
({ sut, mocks } = newTestService(ApiKeyService));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
it('should create a new key', async () => {
|
it('should create a new key', async () => {
|
||||||
mocks.apiKey.create.mockResolvedValue(keyStub.admin);
|
const auth = factory.auth();
|
||||||
await sut.create(authStub.admin, { name: 'Test Key', permissions: [Permission.ALL] });
|
const apiKey = factory.apiKey({ userId: auth.user.id, permissions: [Permission.ALL] });
|
||||||
|
const key = 'super-secret';
|
||||||
|
|
||||||
|
mocks.crypto.newPassword.mockReturnValue(key);
|
||||||
|
mocks.apiKey.create.mockResolvedValue(apiKey);
|
||||||
|
|
||||||
|
await sut.create(auth, { name: apiKey.name, permissions: apiKey.permissions });
|
||||||
|
|
||||||
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
||||||
key: 'cmFuZG9tLWJ5dGVz (hashed)',
|
key: 'super-secret (hashed)',
|
||||||
name: 'Test Key',
|
name: apiKey.name,
|
||||||
permissions: [Permission.ALL],
|
permissions: apiKey.permissions,
|
||||||
userId: authStub.admin.user.id,
|
userId: apiKey.userId,
|
||||||
});
|
});
|
||||||
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
||||||
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not require a name', async () => {
|
it('should not require a name', async () => {
|
||||||
mocks.apiKey.create.mockResolvedValue(keyStub.admin);
|
const auth = factory.auth();
|
||||||
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||||
|
const key = 'super-secret';
|
||||||
|
|
||||||
await sut.create(authStub.admin, { permissions: [Permission.ALL] });
|
mocks.crypto.newPassword.mockReturnValue(key);
|
||||||
|
mocks.apiKey.create.mockResolvedValue(apiKey);
|
||||||
|
|
||||||
|
await sut.create(auth, { permissions: [Permission.ALL] });
|
||||||
|
|
||||||
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
||||||
key: 'cmFuZG9tLWJ5dGVz (hashed)',
|
key: 'super-secret (hashed)',
|
||||||
name: 'API Key',
|
name: 'API Key',
|
||||||
permissions: [Permission.ALL],
|
permissions: [Permission.ALL],
|
||||||
userId: authStub.admin.user.id,
|
userId: auth.user.id,
|
||||||
});
|
});
|
||||||
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
||||||
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if the api key does not have sufficient permissions', async () => {
|
it('should throw an error if the api key does not have sufficient permissions', async () => {
|
||||||
await expect(
|
const auth = factory.auth({ apiKey: factory.authApiKey({ permissions: [Permission.ASSET_READ] }) });
|
||||||
sut.create({ ...authStub.admin, apiKey: keyStub.authKey }, { permissions: [Permission.ASSET_READ] }),
|
|
||||||
).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.create(auth, { permissions: [Permission.ASSET_UPDATE] })).rejects.toBeInstanceOf(
|
||||||
|
BadRequestException,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('update', () => {
|
describe('update', () => {
|
||||||
it('should throw an error if the key is not found', async () => {
|
it('should throw an error if the key is not found', async () => {
|
||||||
await expect(sut.update(authStub.admin, 'random-guid', { name: 'New Name' })).rejects.toBeInstanceOf(
|
const id = newUuid();
|
||||||
BadRequestException,
|
const auth = factory.auth();
|
||||||
);
|
|
||||||
|
|
||||||
expect(mocks.apiKey.update).not.toHaveBeenCalledWith('random-guid');
|
await expect(sut.update(auth, id, { name: 'New Name' })).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.update).not.toHaveBeenCalledWith(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update a key', async () => {
|
it('should update a key', async () => {
|
||||||
mocks.apiKey.getById.mockResolvedValue(keyStub.admin);
|
const auth = factory.auth();
|
||||||
mocks.apiKey.update.mockResolvedValue(keyStub.admin);
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||||
|
const newName = 'New name';
|
||||||
|
|
||||||
await sut.update(authStub.admin, 'random-guid', { name: 'New Name' });
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
||||||
|
mocks.apiKey.update.mockResolvedValue(apiKey);
|
||||||
|
|
||||||
expect(mocks.apiKey.update).toHaveBeenCalledWith(authStub.admin.user.id, 'random-guid', { name: 'New Name' });
|
await sut.update(auth, apiKey.id, { name: newName });
|
||||||
|
|
||||||
|
expect(mocks.apiKey.update).toHaveBeenCalledWith(auth.user.id, apiKey.id, { name: newName });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('delete', () => {
|
describe('delete', () => {
|
||||||
it('should throw an error if the key is not found', async () => {
|
it('should throw an error if the key is not found', async () => {
|
||||||
await expect(sut.delete(authStub.admin, 'random-guid')).rejects.toBeInstanceOf(BadRequestException);
|
const auth = factory.auth();
|
||||||
|
const id = newUuid();
|
||||||
|
|
||||||
expect(mocks.apiKey.delete).not.toHaveBeenCalledWith('random-guid');
|
await expect(sut.delete(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.delete).not.toHaveBeenCalledWith(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete a key', async () => {
|
it('should delete a key', async () => {
|
||||||
mocks.apiKey.getById.mockResolvedValue(keyStub.admin);
|
const auth = factory.auth();
|
||||||
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||||
|
|
||||||
await sut.delete(authStub.admin, 'random-guid');
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
||||||
|
|
||||||
expect(mocks.apiKey.delete).toHaveBeenCalledWith(authStub.admin.user.id, 'random-guid');
|
await sut.delete(auth, apiKey.id);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.delete).toHaveBeenCalledWith(auth.user.id, apiKey.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getById', () => {
|
describe('getById', () => {
|
||||||
it('should throw an error if the key is not found', async () => {
|
it('should throw an error if the key is not found', async () => {
|
||||||
await expect(sut.getById(authStub.admin, 'random-guid')).rejects.toBeInstanceOf(BadRequestException);
|
const auth = factory.auth();
|
||||||
|
const id = newUuid();
|
||||||
|
|
||||||
expect(mocks.apiKey.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'random-guid');
|
await expect(sut.getById(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a key by id', async () => {
|
it('should get a key by id', async () => {
|
||||||
mocks.apiKey.getById.mockResolvedValue(keyStub.admin);
|
const auth = factory.auth();
|
||||||
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||||
|
|
||||||
await sut.getById(authStub.admin, 'random-guid');
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
||||||
|
|
||||||
expect(mocks.apiKey.getById).toHaveBeenCalledWith(authStub.admin.user.id, 'random-guid');
|
await sut.getById(auth, apiKey.id);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAll', () => {
|
describe('getAll', () => {
|
||||||
it('should return all the keys for a user', async () => {
|
it('should return all the keys for a user', async () => {
|
||||||
mocks.apiKey.getByUserId.mockResolvedValue([keyStub.admin]);
|
const auth = factory.auth();
|
||||||
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||||
|
|
||||||
await expect(sut.getAll(authStub.admin)).resolves.toHaveLength(1);
|
mocks.apiKey.getByUserId.mockResolvedValue([apiKey]);
|
||||||
|
|
||||||
expect(mocks.apiKey.getByUserId).toHaveBeenCalledWith(authStub.admin.user.id);
|
await expect(sut.getAll(auth)).resolves.toHaveLength(1);
|
||||||
|
|
||||||
|
expect(mocks.apiKey.getByUserId).toHaveBeenCalledWith(auth.user.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ApiKeyItem } from 'src/types';
|
||||||
import { isGranted } from 'src/utils/access';
|
import { isGranted } from 'src/utils/access';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class APIKeyService extends BaseService {
|
export class ApiKeyService extends BaseService {
|
||||||
async create(auth: AuthDto, dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
|
async create(auth: AuthDto, dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
|
||||||
const secret = this.cryptoRepository.newPassword(32);
|
const secret = this.cryptoRepository.newPassword(32);
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,11 @@ import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { AuthType, Permission } from 'src/enum';
|
import { AuthType, Permission } from 'src/enum';
|
||||||
import { AuthService } from 'src/services/auth.service';
|
import { AuthService } from 'src/services/auth.service';
|
||||||
import { keyStub } from 'test/fixtures/api-key.stub';
|
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
|
||||||
import { sessionStub } from 'test/fixtures/session.stub';
|
import { sessionStub } from 'test/fixtures/session.stub';
|
||||||
import { sharedLinkStub } from 'test/fixtures/shared-link.stub';
|
import { sharedLinkStub } from 'test/fixtures/shared-link.stub';
|
||||||
import { systemConfigStub } from 'test/fixtures/system-config.stub';
|
import { systemConfigStub } from 'test/fixtures/system-config.stub';
|
||||||
import { userStub } from 'test/fixtures/user.stub';
|
import { userStub } from 'test/fixtures/user.stub';
|
||||||
|
import { factory } from 'test/small.factory';
|
||||||
import { newTestService, ServiceMocks } from 'test/utils';
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
|
||||||
const oauthResponse = {
|
const oauthResponse = {
|
||||||
|
@ -398,7 +397,10 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if api key has insufficient permissions', async () => {
|
it('should throw an error if api key has insufficient permissions', async () => {
|
||||||
mocks.apiKey.getKey.mockResolvedValue(keyStub.authKey);
|
const authUser = factory.authUser();
|
||||||
|
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||||
|
|
||||||
|
mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser });
|
||||||
await expect(
|
await expect(
|
||||||
sut.authenticate({
|
sut.authenticate({
|
||||||
headers: { 'x-api-key': 'auth_token' },
|
headers: { 'x-api-key': 'auth_token' },
|
||||||
|
@ -409,14 +411,18 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an auth dto', async () => {
|
it('should return an auth dto', async () => {
|
||||||
mocks.apiKey.getKey.mockResolvedValue(keyStub.authKey);
|
const authUser = factory.authUser();
|
||||||
|
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||||
|
|
||||||
|
mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser });
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
sut.authenticate({
|
sut.authenticate({
|
||||||
headers: { 'x-api-key': 'auth_token' },
|
headers: { 'x-api-key': 'auth_token' },
|
||||||
queryParams: {},
|
queryParams: {},
|
||||||
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
|
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual({ user: userStub.admin, apiKey: keyStub.authKey });
|
).resolves.toEqual({ user: authUser, apiKey: expect.objectContaining(authApiKey) });
|
||||||
expect(mocks.apiKey.getKey).toHaveBeenCalledWith('auth_token (hashed)');
|
expect(mocks.apiKey.getKey).toHaveBeenCalledWith('auth_token (hashed)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -622,19 +628,27 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('link', () => {
|
describe('link', () => {
|
||||||
it('should link an account', async () => {
|
it('should link an account', async () => {
|
||||||
|
const authUser = factory.authUser();
|
||||||
|
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||||
|
const auth = { user: authUser, apiKey: authApiKey };
|
||||||
|
|
||||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
||||||
mocks.user.update.mockResolvedValue(userStub.user1);
|
mocks.user.update.mockResolvedValue(userStub.user1);
|
||||||
|
|
||||||
await sut.link(authStub.user1, { url: 'http://immich/user-settings?code=abc123' });
|
await sut.link(auth, { url: 'http://immich/user-settings?code=abc123' });
|
||||||
|
|
||||||
expect(mocks.user.update).toHaveBeenCalledWith(authStub.user1.user.id, { oauthId: sub });
|
expect(mocks.user.update).toHaveBeenCalledWith(auth.user.id, { oauthId: sub });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not link an already linked oauth.sub', async () => {
|
it('should not link an already linked oauth.sub', async () => {
|
||||||
|
const authUser = factory.authUser();
|
||||||
|
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||||
|
const auth = { user: authUser, apiKey: authApiKey };
|
||||||
|
|
||||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
||||||
mocks.user.getByOAuthId.mockResolvedValue({ id: 'other-user' } as UserEntity);
|
mocks.user.getByOAuthId.mockResolvedValue({ id: 'other-user' } as UserEntity);
|
||||||
|
|
||||||
await expect(sut.link(authStub.user1, { url: 'http://immich/user-settings?code=abc123' })).rejects.toBeInstanceOf(
|
await expect(sut.link(auth, { url: 'http://immich/user-settings?code=abc123' })).rejects.toBeInstanceOf(
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -644,12 +658,16 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('unlink', () => {
|
describe('unlink', () => {
|
||||||
it('should unlink an account', async () => {
|
it('should unlink an account', async () => {
|
||||||
|
const authUser = factory.authUser();
|
||||||
|
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||||
|
const auth = { user: authUser, apiKey: authApiKey };
|
||||||
|
|
||||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
||||||
mocks.user.update.mockResolvedValue(userStub.user1);
|
mocks.user.update.mockResolvedValue(userStub.user1);
|
||||||
|
|
||||||
await sut.unlink(authStub.user1);
|
await sut.unlink(auth);
|
||||||
|
|
||||||
expect(mocks.user.update).toHaveBeenCalledWith(authStub.user1.user.id, { oauthId: '' });
|
expect(mocks.user.update).toHaveBeenCalledWith(auth.user.id, { oauthId: '' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ActivityService } from 'src/services/activity.service';
|
import { ActivityService } from 'src/services/activity.service';
|
||||||
import { AlbumService } from 'src/services/album.service';
|
import { AlbumService } from 'src/services/album.service';
|
||||||
import { APIKeyService } from 'src/services/api-key.service';
|
import { ApiKeyService } from 'src/services/api-key.service';
|
||||||
import { ApiService } from 'src/services/api.service';
|
import { ApiService } from 'src/services/api.service';
|
||||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||||
import { AssetService } from 'src/services/asset.service';
|
import { AssetService } from 'src/services/asset.service';
|
||||||
|
@ -40,7 +40,7 @@ import { VersionService } from 'src/services/version.service';
|
||||||
import { ViewService } from 'src/services/view.service';
|
import { ViewService } from 'src/services/view.service';
|
||||||
|
|
||||||
export const services = [
|
export const services = [
|
||||||
APIKeyService,
|
ApiKeyService,
|
||||||
ActivityService,
|
ActivityService,
|
||||||
AlbumService,
|
AlbumService,
|
||||||
ApiService,
|
ApiService,
|
||||||
|
|
20
server/test/fixtures/api-key.stub.ts
vendored
20
server/test/fixtures/api-key.stub.ts
vendored
|
@ -1,20 +0,0 @@
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
|
||||||
import { userStub } from 'test/fixtures/user.stub';
|
|
||||||
|
|
||||||
export const keyStub = {
|
|
||||||
authKey: Object.freeze({
|
|
||||||
id: 'my-random-guid',
|
|
||||||
key: 'my-api-key (hashed)',
|
|
||||||
user: userStub.admin,
|
|
||||||
permissions: [],
|
|
||||||
} as any),
|
|
||||||
|
|
||||||
admin: Object.freeze({
|
|
||||||
id: 'my-random-guid',
|
|
||||||
name: 'My Key',
|
|
||||||
key: 'my-api-key (hashed)',
|
|
||||||
userId: authStub.admin.user.id,
|
|
||||||
user: userStub.admin,
|
|
||||||
permissions: [],
|
|
||||||
} as any),
|
|
||||||
};
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { Asset, AuthUser, Library, User } from 'src/database';
|
import { ApiKey, Asset, AuthApiKey, AuthUser, Library, User } from 'src/database';
|
||||||
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { OnThisDayData } from 'src/entities/memory.entity';
|
import { OnThisDayData } from 'src/entities/memory.entity';
|
||||||
import { AssetStatus, AssetType, MemoryType } from 'src/enum';
|
import { AssetStatus, AssetType, MemoryType, Permission } from 'src/enum';
|
||||||
import { ActivityItem, MemoryItem } from 'src/types';
|
import { ActivityItem, MemoryItem } from 'src/types';
|
||||||
|
|
||||||
export const newUuid = () => randomUUID() as string;
|
export const newUuid = () => randomUUID() as string;
|
||||||
|
@ -13,11 +14,25 @@ export const newDate = () => new Date();
|
||||||
export const newUpdateId = () => 'uuid-v7';
|
export const newUpdateId = () => 'uuid-v7';
|
||||||
export const newSha1 = () => Buffer.from('this is a fake hash');
|
export const newSha1 = () => Buffer.from('this is a fake hash');
|
||||||
|
|
||||||
const authFactory = (user: Partial<AuthUser> = {}) => ({
|
const authFactory = ({ apiKey, ...user }: Partial<AuthUser> & { apiKey?: Partial<AuthApiKey> } = {}) => {
|
||||||
user: authUserFactory(user),
|
const auth: AuthDto = {
|
||||||
|
user: authUserFactory(user),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (apiKey) {
|
||||||
|
auth.apiKey = authApiKeyFactory(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth;
|
||||||
|
};
|
||||||
|
|
||||||
|
const authApiKeyFactory = (apiKey: Partial<AuthApiKey> = {}) => ({
|
||||||
|
id: newUuid(),
|
||||||
|
permissions: [Permission.ALL],
|
||||||
|
...apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const authUserFactory = (authUser: Partial<AuthUser>) => ({
|
const authUserFactory = (authUser: Partial<AuthUser> = {}) => ({
|
||||||
id: newUuid(),
|
id: newUuid(),
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
name: 'Test User',
|
name: 'Test User',
|
||||||
|
@ -86,6 +101,17 @@ const activityFactory = (activity: Partial<ActivityItem> = {}) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const apiKeyFactory = (apiKey: Partial<ApiKey> = {}) => ({
|
||||||
|
id: newUuid(),
|
||||||
|
userId: newUuid(),
|
||||||
|
createdAt: newDate(),
|
||||||
|
updatedAt: newDate(),
|
||||||
|
updateId: newUpdateId(),
|
||||||
|
name: 'Api Key',
|
||||||
|
permissions: [Permission.ALL],
|
||||||
|
...apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
const libraryFactory = (library: Partial<Library> = {}) => ({
|
const libraryFactory = (library: Partial<Library> = {}) => ({
|
||||||
id: newUuid(),
|
id: newUuid(),
|
||||||
createdAt: newDate(),
|
createdAt: newDate(),
|
||||||
|
@ -121,8 +147,10 @@ const memoryFactory = (memory: Partial<MemoryItem> = {}) => ({
|
||||||
|
|
||||||
export const factory = {
|
export const factory = {
|
||||||
activity: activityFactory,
|
activity: activityFactory,
|
||||||
|
apiKey: apiKeyFactory,
|
||||||
asset: assetFactory,
|
asset: assetFactory,
|
||||||
auth: authFactory,
|
auth: authFactory,
|
||||||
|
authApiKey: authApiKeyFactory,
|
||||||
authUser: authUserFactory,
|
authUser: authUserFactory,
|
||||||
library: libraryFactory,
|
library: libraryFactory,
|
||||||
memory: memoryFactory,
|
memory: memoryFactory,
|
||||||
|
|
Loading…
Add table
Reference in a new issue