refactor(backend): rename auth to public-auth-token

Signed-off-by: Yannick Bungers <git@innay.de>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2024-09-01 14:57:25 +02:00 committed by Philip Molares
parent 52fe7f55de
commit 73d9c3231b
30 changed files with 338 additions and 327 deletions

View file

@ -198,3 +198,9 @@ path = "backend/**/__snapshots__/**.snap"
precedence = "aggregate"
SPDX-FileCopyrightText = "2024 The HedgeDoc developers (see AUTHORS file)"
SPDX-License-Identifier = "AGPL-3.0-only"
[[annotations]]
path = "backend/src/migrations/*"
precedence = "aggregate"
SPDX-FileCopyrightText = "2024 The HedgeDoc developers (see AUTHORS file)"
SPDX-License-Identifier = "AGPL-3.0-only"

View file

@ -5,7 +5,6 @@
*/
import { Module } from '@nestjs/common';
import { AuthModule } from '../../auth/auth.module';
import { FrontendConfigModule } from '../../frontend-config/frontend-config.module';
import { GroupsModule } from '../../groups/groups.module';
import { HistoryModule } from '../../history/history.module';
@ -14,6 +13,7 @@ import { LoggerModule } from '../../logger/logger.module';
import { MediaModule } from '../../media/media.module';
import { NotesModule } from '../../notes/notes.module';
import { PermissionsModule } from '../../permissions/permissions.module';
import { PublicAuthTokenModule } from '../../public-auth-token/public-auth-token.module';
import { RevisionsModule } from '../../revisions/revisions.module';
import { UsersModule } from '../../users/users.module';
import { AliasController } from './alias/alias.controller';
@ -24,14 +24,14 @@ import { HistoryController } from './me/history/history.controller';
import { MeController } from './me/me.controller';
import { MediaController } from './media/media.controller';
import { NotesController } from './notes/notes.controller';
import { TokensController } from './tokens/tokens.controller';
import { PublicAuthTokensController } from './tokens/publicAuthTokensController';
import { UsersController } from './users/users.controller';
@Module({
imports: [
LoggerModule,
UsersModule,
AuthModule,
PublicAuthTokenModule,
FrontendConfigModule,
HistoryModule,
PermissionsModule,
@ -42,7 +42,7 @@ import { UsersController } from './users/users.controller';
GroupsModule,
],
controllers: [
TokensController,
PublicAuthTokensController,
ConfigController,
MediaController,
HistoryController,

View file

@ -15,14 +15,14 @@ import {
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
AuthTokenCreateDto,
AuthTokenDto,
AuthTokenWithSecretDto,
} from '../../../auth/auth-token.dto';
import { AuthService } from '../../../auth/auth.service';
import { SessionGuard } from '../../../identity/session.guard';
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import {
PublicAuthTokenCreateDto,
PublicAuthTokenDto,
PublicAuthTokenWithSecretDto,
} from '../../../public-auth-token/public-auth-token.dto';
import { PublicAuthTokenService } from '../../../public-auth-token/public-auth-token.service';
import { User } from '../../../users/user.entity';
import { OpenApi } from '../../utils/openapi.decorator';
import { RequestUser } from '../../utils/request-user.decorator';
@ -31,29 +31,31 @@ import { RequestUser } from '../../utils/request-user.decorator';
@OpenApi(401)
@ApiTags('tokens')
@Controller('tokens')
export class TokensController {
export class PublicAuthTokensController {
constructor(
private readonly logger: ConsoleLoggerService,
private authService: AuthService,
private publicAuthTokenService: PublicAuthTokenService,
) {
this.logger.setContext(TokensController.name);
this.logger.setContext(PublicAuthTokensController.name);
}
@Get()
@OpenApi(200)
async getUserTokens(@RequestUser() user: User): Promise<AuthTokenDto[]> {
return (await this.authService.getTokensByUser(user)).map((token) =>
this.authService.toAuthTokenDto(token),
async getUserTokens(
@RequestUser() user: User,
): Promise<PublicAuthTokenDto[]> {
return (await this.publicAuthTokenService.getTokensByUser(user)).map(
(token) => this.publicAuthTokenService.toAuthTokenDto(token),
);
}
@Post()
@OpenApi(201)
async postTokenRequest(
@Body() createDto: AuthTokenCreateDto,
@Body() createDto: PublicAuthTokenCreateDto,
@RequestUser() user: User,
): Promise<AuthTokenWithSecretDto> {
return await this.authService.addToken(
): Promise<PublicAuthTokenWithSecretDto> {
return await this.publicAuthTokenService.addToken(
user,
createDto.label,
createDto.validUntil,
@ -66,10 +68,10 @@ export class TokensController {
@RequestUser() user: User,
@Param('keyId') keyId: string,
): Promise<void> {
const tokens = await this.authService.getTokensByUser(user);
const tokens = await this.publicAuthTokenService.getTokensByUser(user);
for (const token of tokens) {
if (token.keyId == keyId) {
return await this.authService.removeToken(keyId);
return await this.publicAuthTokenService.removeToken(keyId);
}
}
throw new UnauthorizedException(

View file

@ -16,7 +16,6 @@ import {
} from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy';
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { AliasCreateDto } from '../../../notes/alias-create.dto';
import { AliasUpdateDto } from '../../../notes/alias-update.dto';
@ -24,11 +23,12 @@ import { AliasDto } from '../../../notes/alias.dto';
import { AliasService } from '../../../notes/alias.service';
import { NotesService } from '../../../notes/notes.service';
import { PermissionsService } from '../../../permissions/permissions.service';
import { PublicAuthTokenGuard } from '../../../public-auth-token/public-auth-token.strategy';
import { User } from '../../../users/user.entity';
import { OpenApi } from '../../utils/openapi.decorator';
import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@UseGuards(PublicAuthTokenGuard)
@OpenApi(401)
@ApiTags('alias')
@ApiSecurity('token')

View file

@ -14,7 +14,6 @@ import {
} from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy';
import { HistoryEntryUpdateDto } from '../../../history/history-entry-update.dto';
import { HistoryEntryDto } from '../../../history/history-entry.dto';
import { HistoryService } from '../../../history/history.service';
@ -24,6 +23,7 @@ import { MediaService } from '../../../media/media.service';
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
import { Note } from '../../../notes/note.entity';
import { NotesService } from '../../../notes/notes.service';
import { PublicAuthTokenGuard } from '../../../public-auth-token/public-auth-token.strategy';
import { FullUserInfoDto } from '../../../users/user-info.dto';
import { User } from '../../../users/user.entity';
import { UsersService } from '../../../users/users.service';
@ -32,7 +32,7 @@ import { OpenApi } from '../../utils/openapi.decorator';
import { RequestNote } from '../../utils/request-note.decorator';
import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@UseGuards(PublicAuthTokenGuard)
@OpenApi(401)
@ApiTags('me')
@ApiSecurity('token')

View file

@ -25,7 +25,6 @@ import {
} from '@nestjs/swagger';
import { Response } from 'express';
import { TokenAuthGuard } from '../../../auth/token.strategy';
import { PermissionError } from '../../../errors/errors';
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { MediaUploadDto } from '../../../media/media-upload.dto';
@ -36,13 +35,14 @@ import { PermissionsGuard } from '../../../permissions/permissions.guard';
import { PermissionsService } from '../../../permissions/permissions.service';
import { RequirePermission } from '../../../permissions/require-permission.decorator';
import { RequiredPermission } from '../../../permissions/required-permission.enum';
import { PublicAuthTokenGuard } from '../../../public-auth-token/public-auth-token.strategy';
import { User } from '../../../users/user.entity';
import { NoteHeaderInterceptor } from '../../utils/note-header.interceptor';
import { OpenApi } from '../../utils/openapi.decorator';
import { RequestNote } from '../../utils/request-note.decorator';
import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@UseGuards(PublicAuthTokenGuard)
@OpenApi(401)
@ApiTags('media')
@ApiSecurity('token')

View file

@ -6,12 +6,12 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy';
import { MonitoringService } from '../../../monitoring/monitoring.service';
import { ServerStatusDto } from '../../../monitoring/server-status.dto';
import { PublicAuthTokenGuard } from '../../../public-auth-token/public-auth-token.strategy';
import { OpenApi } from '../../utils/openapi.decorator';
@UseGuards(TokenAuthGuard)
@UseGuards(PublicAuthTokenGuard)
@OpenApi(401)
@ApiTags('monitoring')
@ApiSecurity('token')

View file

@ -17,7 +17,6 @@ import {
} from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy';
import { NotInDBError } from '../../../errors/errors';
import { GroupsService } from '../../../groups/groups.service';
import { HistoryService } from '../../../history/history.service';
@ -37,6 +36,7 @@ import { PermissionsGuard } from '../../../permissions/permissions.guard';
import { PermissionsService } from '../../../permissions/permissions.service';
import { RequirePermission } from '../../../permissions/require-permission.decorator';
import { RequiredPermission } from '../../../permissions/required-permission.enum';
import { PublicAuthTokenGuard } from '../../../public-auth-token/public-auth-token.strategy';
import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto';
import { RevisionDto } from '../../../revisions/revision.dto';
import { RevisionsService } from '../../../revisions/revisions.service';
@ -49,7 +49,7 @@ import { OpenApi } from '../../utils/openapi.decorator';
import { RequestNote } from '../../utils/request-note.decorator';
import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard, PermissionsGuard)
@UseGuards(PublicAuthTokenGuard, PermissionsGuard)
@OpenApi(401)
@ApiTags('notes')
@ApiSecurity('token')

View file

@ -12,7 +12,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { PrivateApiModule } from './api/private/private-api.module';
import { PublicApiModule } from './api/public/public-api.module';
import { AuthModule } from './auth/auth.module';
import { AuthorsModule } from './authors/authors.module';
import appConfig from './config/app.config';
import authConfig from './config/auth.config';
@ -34,6 +33,7 @@ import { MediaModule } from './media/media.module';
import { MonitoringModule } from './monitoring/monitoring.module';
import { NotesModule } from './notes/notes.module';
import { PermissionsModule } from './permissions/permissions.module';
import { PublicAuthTokenModule } from './public-auth-token/public-auth-token.module';
import { WebsocketModule } from './realtime/websocket/websocket.module';
import { RevisionsModule } from './revisions/revisions.module';
import { SessionModule } from './sessions/session.module';
@ -107,7 +107,7 @@ const routes: Routes = [
GroupsModule,
LoggerModule,
MediaModule,
AuthModule,
PublicAuthTokenModule,
FrontendConfigModule,
WebsocketModule,
IdentityModule,

View file

@ -1,27 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { LoggerModule } from '../logger/logger.module';
import { UsersModule } from '../users/users.module';
import { AuthToken } from './auth-token.entity';
import { AuthService } from './auth.service';
import { MockAuthGuard } from './mock-auth.guard';
import { TokenAuthGuard, TokenStrategy } from './token.strategy';
@Module({
imports: [
UsersModule,
PassportModule,
LoggerModule,
TypeOrmModule.forFeature([AuthToken]),
],
providers: [AuthService, TokenStrategy, MockAuthGuard, TokenAuthGuard],
exports: [AuthService],
})
export class AuthModule {}

View file

@ -11,7 +11,6 @@ import assert from 'assert';
import { Mock } from 'ts-mockery';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import appConfigMock from '../config/mock/app.config.mock';
import authConfigMock from '../config/mock/auth.config.mock';
@ -28,6 +27,7 @@ import { NotesModule } from '../notes/notes.module';
import { Tag } from '../notes/tag.entity';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
import { RevisionsModule } from '../revisions/revisions.module';
@ -112,7 +112,7 @@ describe('HistoryService', () => {
})
.overrideProvider(getRepositoryToken(User))
.useValue({})
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -11,7 +11,6 @@ import { promises as fs } from 'fs';
import { Repository } from 'typeorm';
import appConfigMock from '../../src/config/mock/app.config.mock';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import authConfigMock from '../config/mock/auth.config.mock';
import databaseConfigMock from '../config/mock/database.config.mock';
@ -28,6 +27,7 @@ import { NotesModule } from '../notes/notes.module';
import { Tag } from '../notes/tag.entity';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
import { Session } from '../sessions/session.entity';
@ -84,7 +84,7 @@ describe('MediaService', () => {
})
.overrideProvider(getRepositoryToken(Edit))
.useValue({})
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -1,26 +1,15 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Init1696756465867 implements MigrationInterface {
name = 'Init1696756465867';
export class MariadbInit1725204784823 implements MigrationInterface {
name = 'MariadbInit1725204784823';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE \`auth_token\` (\`id\` int NOT NULL AUTO_INCREMENT, \`keyId\` varchar(255) NOT NULL, \`label\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`accessTokenHash\` varchar(255) NOT NULL, \`validUntil\` datetime NOT NULL, \`lastUsedAt\` date NULL, \`userId\` int NULL, UNIQUE INDEX \`IDX_9d2cf0a2cc3df58b87cbbc43e4\` (\`keyId\`), UNIQUE INDEX \`IDX_15b2228d06b08bb4cc98876c65\` (\`accessTokenHash\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`history_entry\` (\`id\` int NOT NULL AUTO_INCREMENT, \`pinStatus\` tinyint NOT NULL, \`updatedAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`userId\` int NULL, \`noteId\` int NULL, UNIQUE INDEX \`IDX_928dd947355b0837366470a916\` (\`noteId\`, \`userId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`media_upload\` (\`id\` varchar(255) NOT NULL, \`backendType\` varchar(255) NOT NULL, \`fileUrl\` varchar(255) NOT NULL, \`backendData\` text NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`noteId\` int NULL, \`userId\` int NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`group\` (\`id\` int NOT NULL AUTO_INCREMENT, \`name\` varchar(255) NOT NULL, \`displayName\` varchar(255) NOT NULL, \`special\` tinyint NOT NULL, UNIQUE INDEX \`IDX_8a45300fd825918f3b40195fbd\` (\`name\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`note_group_permission\` (\`id\` int NOT NULL AUTO_INCREMENT, \`canEdit\` tinyint NOT NULL, \`groupId\` int NULL, \`noteId\` int NULL, UNIQUE INDEX \`IDX_ee1744842a9ef3ffbc05a7016a\` (\`groupId\`, \`noteId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
@ -48,14 +37,17 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE TABLE \`author\` (\`id\` int NOT NULL AUTO_INCREMENT, \`color\` int NOT NULL, \`userId\` int NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`user\` (\`id\` int NOT NULL AUTO_INCREMENT, \`username\` varchar(255) NOT NULL, \`displayName\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updatedAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`photo\` text NULL, \`email\` text NULL, UNIQUE INDEX \`IDX_78a916df40e02a9deb1c4b75ed\` (\`username\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`identity\` (\`id\` int NOT NULL AUTO_INCREMENT, \`providerType\` varchar(255) NOT NULL, \`providerName\` text NULL, \`syncSource\` tinyint NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updatedAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`providerUserId\` text NULL, \`oAuthAccessToken\` text NULL, \`passwordHash\` text NULL, \`userId\` int NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`group_members_user\` (\`groupId\` int NOT NULL, \`userId\` int NOT NULL, INDEX \`IDX_bfa303089d367a2e3c02b002b8\` (\`groupId\`), INDEX \`IDX_427107c650638bcb2f1e167d2e\` (\`userId\`), PRIMARY KEY (\`groupId\`, \`userId\`)) ENGINE=InnoDB`,
`CREATE TABLE \`public_auth_token\` (\`id\` int NOT NULL AUTO_INCREMENT, \`keyId\` varchar(255) NOT NULL, \`label\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`hash\` varchar(255) NOT NULL, \`validUntil\` datetime NOT NULL, \`lastUsedAt\` date NULL, \`userId\` int NULL, UNIQUE INDEX \`IDX_b4c4b9179f72ef63c32248e83a\` (\`keyId\`), UNIQUE INDEX \`IDX_6450514886fa4182c889c076df\` (\`hash\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`user\` (\`id\` int NOT NULL AUTO_INCREMENT, \`username\` varchar(255) NOT NULL, \`displayName\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updatedAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`photo\` text NULL, \`email\` text NULL, UNIQUE INDEX \`IDX_78a916df40e02a9deb1c4b75ed\` (\`username\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`group\` (\`id\` int NOT NULL AUTO_INCREMENT, \`name\` varchar(255) NOT NULL, \`displayName\` varchar(255) NOT NULL, \`special\` tinyint NOT NULL, UNIQUE INDEX \`IDX_8a45300fd825918f3b40195fbd\` (\`name\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`CREATE TABLE \`revision_tags_tag\` (\`revisionId\` int NOT NULL, \`tagId\` int NOT NULL, INDEX \`IDX_3382f45eefeb40f91e45cfd418\` (\`revisionId\`), INDEX \`IDX_19dbafe2a8b456c0ef40858d49\` (\`tagId\`), PRIMARY KEY (\`revisionId\`, \`tagId\`)) ENGINE=InnoDB`,
@ -64,7 +56,7 @@ export class Init1696756465867 implements MigrationInterface {
`CREATE TABLE \`revision_edits_edit\` (\`revisionId\` int NOT NULL, \`editId\` int NOT NULL, INDEX \`IDX_52c6a61e1a646768391c7854fe\` (\`revisionId\`), INDEX \`IDX_470886feb50e30114e39c42698\` (\`editId\`), PRIMARY KEY (\`revisionId\`, \`editId\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`ALTER TABLE \`auth_token\` ADD CONSTRAINT \`FK_5a326267f11b44c0d62526bc718\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
`CREATE TABLE \`group_members_user\` (\`groupId\` int NOT NULL, \`userId\` int NOT NULL, INDEX \`IDX_bfa303089d367a2e3c02b002b8\` (\`groupId\`), INDEX \`IDX_427107c650638bcb2f1e167d2e\` (\`userId\`), PRIMARY KEY (\`groupId\`, \`userId\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`ALTER TABLE \`history_entry\` ADD CONSTRAINT \`FK_42b8ae461cb58747a24340e6c64\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
@ -112,10 +104,7 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE \`identity\` ADD CONSTRAINT \`FK_12915039d2868ab654567bf5181\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` ADD CONSTRAINT \`FK_bfa303089d367a2e3c02b002b8f\` FOREIGN KEY (\`groupId\`) REFERENCES \`group\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` ADD CONSTRAINT \`FK_427107c650638bcb2f1e167d2e5\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`,
`ALTER TABLE \`public_auth_token\` ADD CONSTRAINT \`FK_b7b4f28eb8b4a0fc443448b9054\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE \`revision_tags_tag\` ADD CONSTRAINT \`FK_3382f45eefeb40f91e45cfd4180\` FOREIGN KEY (\`revisionId\`) REFERENCES \`revision\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`,
@ -129,9 +118,21 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`ALTER TABLE \`revision_edits_edit\` ADD CONSTRAINT \`FK_470886feb50e30114e39c426987\` FOREIGN KEY (\`editId\`) REFERENCES \`edit\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` ADD CONSTRAINT \`FK_bfa303089d367a2e3c02b002b8f\` FOREIGN KEY (\`groupId\`) REFERENCES \`group\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` ADD CONSTRAINT \`FK_427107c650638bcb2f1e167d2e5\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`group_members_user\` DROP FOREIGN KEY \`FK_427107c650638bcb2f1e167d2e5\``,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` DROP FOREIGN KEY \`FK_bfa303089d367a2e3c02b002b8f\``,
);
await queryRunner.query(
`ALTER TABLE \`revision_edits_edit\` DROP FOREIGN KEY \`FK_470886feb50e30114e39c426987\``,
);
@ -145,10 +146,7 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE \`revision_tags_tag\` DROP FOREIGN KEY \`FK_3382f45eefeb40f91e45cfd4180\``,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` DROP FOREIGN KEY \`FK_427107c650638bcb2f1e167d2e5\``,
);
await queryRunner.query(
`ALTER TABLE \`group_members_user\` DROP FOREIGN KEY \`FK_bfa303089d367a2e3c02b002b8f\``,
`ALTER TABLE \`public_auth_token\` DROP FOREIGN KEY \`FK_b7b4f28eb8b4a0fc443448b9054\``,
);
await queryRunner.query(
`ALTER TABLE \`identity\` DROP FOREIGN KEY \`FK_12915039d2868ab654567bf5181\``,
@ -196,8 +194,12 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE \`history_entry\` DROP FOREIGN KEY \`FK_42b8ae461cb58747a24340e6c64\``,
);
await queryRunner.query(
`ALTER TABLE \`auth_token\` DROP FOREIGN KEY \`FK_5a326267f11b44c0d62526bc718\``,
`DROP INDEX \`IDX_427107c650638bcb2f1e167d2e\` ON \`group_members_user\``,
);
await queryRunner.query(
`DROP INDEX \`IDX_bfa303089d367a2e3c02b002b8\` ON \`group_members_user\``,
);
await queryRunner.query(`DROP TABLE \`group_members_user\``);
await queryRunner.query(
`DROP INDEX \`IDX_470886feb50e30114e39c42698\` ON \`revision_edits_edit\``,
);
@ -213,17 +215,21 @@ export class Init1696756465867 implements MigrationInterface {
);
await queryRunner.query(`DROP TABLE \`revision_tags_tag\``);
await queryRunner.query(
`DROP INDEX \`IDX_427107c650638bcb2f1e167d2e\` ON \`group_members_user\``,
`DROP INDEX \`IDX_8a45300fd825918f3b40195fbd\` ON \`group\``,
);
await queryRunner.query(
`DROP INDEX \`IDX_bfa303089d367a2e3c02b002b8\` ON \`group_members_user\``,
);
await queryRunner.query(`DROP TABLE \`group_members_user\``);
await queryRunner.query(`DROP TABLE \`identity\``);
await queryRunner.query(`DROP TABLE \`group\``);
await queryRunner.query(
`DROP INDEX \`IDX_78a916df40e02a9deb1c4b75ed\` ON \`user\``,
);
await queryRunner.query(`DROP TABLE \`user\``);
await queryRunner.query(
`DROP INDEX \`IDX_6450514886fa4182c889c076df\` ON \`public_auth_token\``,
);
await queryRunner.query(
`DROP INDEX \`IDX_b4c4b9179f72ef63c32248e83a\` ON \`public_auth_token\``,
);
await queryRunner.query(`DROP TABLE \`public_auth_token\``);
await queryRunner.query(`DROP TABLE \`identity\``);
await queryRunner.query(`DROP TABLE \`author\``);
await queryRunner.query(
`DROP INDEX \`IDX_28c5d1d16da7908c97c9bc2f74\` ON \`session\``,
@ -248,21 +254,10 @@ export class Init1696756465867 implements MigrationInterface {
`DROP INDEX \`IDX_ee1744842a9ef3ffbc05a7016a\` ON \`note_group_permission\``,
);
await queryRunner.query(`DROP TABLE \`note_group_permission\``);
await queryRunner.query(
`DROP INDEX \`IDX_8a45300fd825918f3b40195fbd\` ON \`group\``,
);
await queryRunner.query(`DROP TABLE \`group\``);
await queryRunner.query(`DROP TABLE \`media_upload\``);
await queryRunner.query(
`DROP INDEX \`IDX_928dd947355b0837366470a916\` ON \`history_entry\``,
);
await queryRunner.query(`DROP TABLE \`history_entry\``);
await queryRunner.query(
`DROP INDEX \`IDX_15b2228d06b08bb4cc98876c65\` ON \`auth_token\``,
);
await queryRunner.query(
`DROP INDEX \`IDX_9d2cf0a2cc3df58b87cbbc43e4\` ON \`auth_token\``,
);
await queryRunner.query(`DROP TABLE \`auth_token\``);
}
}

View file

@ -1,17 +1,9 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Init1696756465867 implements MigrationInterface {
name = 'Init1696756465867';
export class Init1725203299761 implements MigrationInterface {
name = 'Init1725203299761';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "auth_token" ("id" SERIAL NOT NULL, "keyId" character varying NOT NULL, "label" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "accessTokenHash" character varying NOT NULL, "validUntil" TIMESTAMP NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_9d2cf0a2cc3df58b87cbbc43e48" UNIQUE ("keyId"), CONSTRAINT "UQ_15b2228d06b08bb4cc98876c651" UNIQUE ("accessTokenHash"), CONSTRAINT "PK_4572ff5d1264c4a523f01aa86a0" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "history_entry" ("id" SERIAL NOT NULL, "pinStatus" boolean NOT NULL, "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "userId" integer, "noteId" integer, CONSTRAINT "PK_b65bd95b0d2929668589d57b97a" PRIMARY KEY ("id"))`,
);
@ -21,9 +13,6 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE TABLE "media_upload" ("id" character varying NOT NULL, "backendType" character varying NOT NULL, "fileUrl" character varying NOT NULL, "backendData" text, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "noteId" integer, "userId" integer, CONSTRAINT "PK_b406d9cee56e253dfd3b3d52706" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "group" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "displayName" character varying NOT NULL, "special" boolean NOT NULL, CONSTRAINT "UQ_8a45300fd825918f3b40195fbdc" UNIQUE ("name"), CONSTRAINT "PK_256aa0fda9b1de1a73ee0b7106b" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "note_group_permission" ("id" SERIAL NOT NULL, "canEdit" boolean NOT NULL, "groupId" integer, "noteId" integer, CONSTRAINT "PK_6327989190949e6a55d02a080c3" PRIMARY KEY ("id"))`,
);
@ -60,20 +49,17 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE TABLE "author" ("id" SERIAL NOT NULL, "color" integer NOT NULL, "userId" integer, CONSTRAINT "PK_5a0e79799d372fe56f2f3fa6871" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "user" ("id" SERIAL NOT NULL, "username" character varying NOT NULL, "displayName" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "photo" text, "email" text, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "identity" ("id" SERIAL NOT NULL, "providerType" character varying NOT NULL, "providerName" text, "syncSource" boolean NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "providerUserId" text, "oAuthAccessToken" text, "passwordHash" text, "userId" integer, CONSTRAINT "PK_ff16a44186b286d5e626178f726" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_7170c9a27e7b823d391d9e11f2e" PRIMARY KEY ("groupId", "userId"))`,
`CREATE TABLE "public_auth_token" ("id" SERIAL NOT NULL, "keyId" character varying NOT NULL, "label" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "hash" character varying NOT NULL, "validUntil" TIMESTAMP NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_b4c4b9179f72ef63c32248e83ab" UNIQUE ("keyId"), CONSTRAINT "UQ_6450514886fa4182c889c076df6" UNIQUE ("hash"), CONSTRAINT "PK_1bdb7c2d237fb02d84fa75f48a5" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
`CREATE TABLE "user" ("id" SERIAL NOT NULL, "username" character varying NOT NULL, "displayName" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "photo" text, "email" text, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
`CREATE TABLE "group" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "displayName" character varying NOT NULL, "special" boolean NOT NULL, CONSTRAINT "UQ_8a45300fd825918f3b40195fbdc" UNIQUE ("name"), CONSTRAINT "PK_256aa0fda9b1de1a73ee0b7106b" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "revision_tags_tag" ("revisionId" integer NOT NULL, "tagId" integer NOT NULL, CONSTRAINT "PK_006354d3ecad6cb1e606320647b" PRIMARY KEY ("revisionId", "tagId"))`,
@ -94,7 +80,13 @@ export class Init1696756465867 implements MigrationInterface {
`CREATE INDEX "IDX_470886feb50e30114e39c42698" ON "revision_edits_edit" ("editId") `,
);
await queryRunner.query(
`ALTER TABLE "auth_token" ADD CONSTRAINT "FK_5a326267f11b44c0d62526bc718" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_7170c9a27e7b823d391d9e11f2e" PRIMARY KEY ("groupId", "userId"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
);
await queryRunner.query(
`ALTER TABLE "history_entry" ADD CONSTRAINT "FK_42b8ae461cb58747a24340e6c64" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
@ -142,10 +134,7 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE "identity" ADD CONSTRAINT "FK_12915039d2868ab654567bf5181" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" ADD CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f" FOREIGN KEY ("groupId") REFERENCES "group"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" ADD CONSTRAINT "FK_427107c650638bcb2f1e167d2e5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
`ALTER TABLE "public_auth_token" ADD CONSTRAINT "FK_b7b4f28eb8b4a0fc443448b9054" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "revision_tags_tag" ADD CONSTRAINT "FK_3382f45eefeb40f91e45cfd4180" FOREIGN KEY ("revisionId") REFERENCES "revision"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
@ -159,9 +148,21 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`ALTER TABLE "revision_edits_edit" ADD CONSTRAINT "FK_470886feb50e30114e39c426987" FOREIGN KEY ("editId") REFERENCES "edit"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" ADD CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f" FOREIGN KEY ("groupId") REFERENCES "group"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" ADD CONSTRAINT "FK_427107c650638bcb2f1e167d2e5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "group_members_user" DROP CONSTRAINT "FK_427107c650638bcb2f1e167d2e5"`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" DROP CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f"`,
);
await queryRunner.query(
`ALTER TABLE "revision_edits_edit" DROP CONSTRAINT "FK_470886feb50e30114e39c426987"`,
);
@ -175,10 +176,7 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE "revision_tags_tag" DROP CONSTRAINT "FK_3382f45eefeb40f91e45cfd4180"`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" DROP CONSTRAINT "FK_427107c650638bcb2f1e167d2e5"`,
);
await queryRunner.query(
`ALTER TABLE "group_members_user" DROP CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f"`,
`ALTER TABLE "public_auth_token" DROP CONSTRAINT "FK_b7b4f28eb8b4a0fc443448b9054"`,
);
await queryRunner.query(
`ALTER TABLE "identity" DROP CONSTRAINT "FK_12915039d2868ab654567bf5181"`,
@ -226,8 +224,12 @@ export class Init1696756465867 implements MigrationInterface {
`ALTER TABLE "history_entry" DROP CONSTRAINT "FK_42b8ae461cb58747a24340e6c64"`,
);
await queryRunner.query(
`ALTER TABLE "auth_token" DROP CONSTRAINT "FK_5a326267f11b44c0d62526bc718"`,
`DROP INDEX "public"."IDX_427107c650638bcb2f1e167d2e"`,
);
await queryRunner.query(
`DROP INDEX "public"."IDX_bfa303089d367a2e3c02b002b8"`,
);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(
`DROP INDEX "public"."IDX_470886feb50e30114e39c42698"`,
);
@ -242,15 +244,10 @@ export class Init1696756465867 implements MigrationInterface {
`DROP INDEX "public"."IDX_3382f45eefeb40f91e45cfd418"`,
);
await queryRunner.query(`DROP TABLE "revision_tags_tag"`);
await queryRunner.query(
`DROP INDEX "public"."IDX_427107c650638bcb2f1e167d2e"`,
);
await queryRunner.query(
`DROP INDEX "public"."IDX_bfa303089d367a2e3c02b002b8"`,
);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(`DROP TABLE "identity"`);
await queryRunner.query(`DROP TABLE "group"`);
await queryRunner.query(`DROP TABLE "user"`);
await queryRunner.query(`DROP TABLE "public_auth_token"`);
await queryRunner.query(`DROP TABLE "identity"`);
await queryRunner.query(`DROP TABLE "author"`);
await queryRunner.query(
`DROP INDEX "public"."IDX_28c5d1d16da7908c97c9bc2f74"`,
@ -269,12 +266,10 @@ export class Init1696756465867 implements MigrationInterface {
`DROP INDEX "public"."IDX_ee1744842a9ef3ffbc05a7016a"`,
);
await queryRunner.query(`DROP TABLE "note_group_permission"`);
await queryRunner.query(`DROP TABLE "group"`);
await queryRunner.query(`DROP TABLE "media_upload"`);
await queryRunner.query(
`DROP INDEX "public"."IDX_928dd947355b0837366470a916"`,
);
await queryRunner.query(`DROP TABLE "history_entry"`);
await queryRunner.query(`DROP TABLE "auth_token"`);
}
}

View file

@ -1,17 +1,9 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Init1696756465867 implements MigrationInterface {
name = 'Init1696756465867';
export class SqliteInit1725204990810 implements MigrationInterface {
name = 'SqliteInit1725204990810';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "accessTokenHash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_9d2cf0a2cc3df58b87cbbc43e48" UNIQUE ("keyId"), CONSTRAINT "UQ_15b2228d06b08bb4cc98876c651" UNIQUE ("accessTokenHash"))`,
);
await queryRunner.query(
`CREATE TABLE "history_entry" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "pinStatus" boolean NOT NULL, "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" integer, "noteId" integer)`,
);
@ -21,9 +13,6 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE TABLE "media_upload" ("id" varchar PRIMARY KEY NOT NULL, "backendType" varchar NOT NULL, "fileUrl" varchar NOT NULL, "backendData" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "noteId" integer, "userId" integer)`,
);
await queryRunner.query(
`CREATE TABLE "group" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "displayName" varchar NOT NULL, "special" boolean NOT NULL, CONSTRAINT "UQ_8a45300fd825918f3b40195fbdc" UNIQUE ("name"))`,
);
await queryRunner.query(
`CREATE TABLE "note_group_permission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "canEdit" boolean NOT NULL, "groupId" integer, "noteId" integer)`,
);
@ -60,20 +49,17 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE TABLE "author" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "color" integer NOT NULL, "userId" integer)`,
);
await queryRunner.query(
`CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar NOT NULL, "displayName" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "photo" text, "email" text, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"))`,
);
await queryRunner.query(
`CREATE TABLE "identity" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "providerType" varchar NOT NULL, "providerName" text, "syncSource" boolean NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "providerUserId" text, "oAuthAccessToken" text, "passwordHash" text, "userId" integer)`,
);
await queryRunner.query(
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, PRIMARY KEY ("groupId", "userId"))`,
`CREATE TABLE "public_auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "hash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_b4c4b9179f72ef63c32248e83ab" UNIQUE ("keyId"), CONSTRAINT "UQ_6450514886fa4182c889c076df6" UNIQUE ("hash"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
`CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar NOT NULL, "displayName" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "photo" text, "email" text, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
`CREATE TABLE "group" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "displayName" varchar NOT NULL, "special" boolean NOT NULL, CONSTRAINT "UQ_8a45300fd825918f3b40195fbdc" UNIQUE ("name"))`,
);
await queryRunner.query(
`CREATE TABLE "revision_tags_tag" ("revisionId" integer NOT NULL, "tagId" integer NOT NULL, PRIMARY KEY ("revisionId", "tagId"))`,
@ -94,14 +80,13 @@ export class Init1696756465867 implements MigrationInterface {
`CREATE INDEX "IDX_470886feb50e30114e39c42698" ON "revision_edits_edit" ("editId") `,
);
await queryRunner.query(
`CREATE TABLE "temporary_auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "accessTokenHash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_9d2cf0a2cc3df58b87cbbc43e48" UNIQUE ("keyId"), CONSTRAINT "UQ_15b2228d06b08bb4cc98876c651" UNIQUE ("accessTokenHash"), CONSTRAINT "FK_5a326267f11b44c0d62526bc718" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`,
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, PRIMARY KEY ("groupId", "userId"))`,
);
await queryRunner.query(
`INSERT INTO "temporary_auth_token"("id", "keyId", "label", "createdAt", "accessTokenHash", "validUntil", "lastUsedAt", "userId") SELECT "id", "keyId", "label", "createdAt", "accessTokenHash", "validUntil", "lastUsedAt", "userId" FROM "auth_token"`,
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
);
await queryRunner.query(`DROP TABLE "auth_token"`);
await queryRunner.query(
`ALTER TABLE "temporary_auth_token" RENAME TO "auth_token"`,
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
);
await queryRunner.query(`DROP INDEX "IDX_928dd947355b0837366470a916"`);
await queryRunner.query(
@ -223,23 +208,15 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`ALTER TABLE "temporary_identity" RENAME TO "identity"`,
);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(
`CREATE TABLE "temporary_group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f" FOREIGN KEY ("groupId") REFERENCES "group" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_427107c650638bcb2f1e167d2e5" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, PRIMARY KEY ("groupId", "userId"))`,
`CREATE TABLE "temporary_public_auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "hash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_b4c4b9179f72ef63c32248e83ab" UNIQUE ("keyId"), CONSTRAINT "UQ_6450514886fa4182c889c076df6" UNIQUE ("hash"), CONSTRAINT "FK_b7b4f28eb8b4a0fc443448b9054" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`,
);
await queryRunner.query(
`INSERT INTO "temporary_group_members_user"("groupId", "userId") SELECT "groupId", "userId" FROM "group_members_user"`,
`INSERT INTO "temporary_public_auth_token"("id", "keyId", "label", "createdAt", "hash", "validUntil", "lastUsedAt", "userId") SELECT "id", "keyId", "label", "createdAt", "hash", "validUntil", "lastUsedAt", "userId" FROM "public_auth_token"`,
);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(`DROP TABLE "public_auth_token"`);
await queryRunner.query(
`ALTER TABLE "temporary_group_members_user" RENAME TO "group_members_user"`,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
`ALTER TABLE "temporary_public_auth_token" RENAME TO "public_auth_token"`,
);
await queryRunner.query(`DROP INDEX "IDX_3382f45eefeb40f91e45cfd418"`);
await queryRunner.query(`DROP INDEX "IDX_19dbafe2a8b456c0ef40858d49"`);
@ -277,9 +254,45 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE INDEX "IDX_470886feb50e30114e39c42698" ON "revision_edits_edit" ("editId") `,
);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(
`CREATE TABLE "temporary_group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "FK_bfa303089d367a2e3c02b002b8f" FOREIGN KEY ("groupId") REFERENCES "group" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_427107c650638bcb2f1e167d2e5" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, PRIMARY KEY ("groupId", "userId"))`,
);
await queryRunner.query(
`INSERT INTO "temporary_group_members_user"("groupId", "userId") SELECT "groupId", "userId" FROM "group_members_user"`,
);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(
`ALTER TABLE "temporary_group_members_user" RENAME TO "group_members_user"`,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(
`ALTER TABLE "group_members_user" RENAME TO "temporary_group_members_user"`,
);
await queryRunner.query(
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, PRIMARY KEY ("groupId", "userId"))`,
);
await queryRunner.query(
`INSERT INTO "group_members_user"("groupId", "userId") SELECT "groupId", "userId" FROM "temporary_group_members_user"`,
);
await queryRunner.query(`DROP TABLE "temporary_group_members_user"`);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
);
await queryRunner.query(`DROP INDEX "IDX_470886feb50e30114e39c42698"`);
await queryRunner.query(`DROP INDEX "IDX_52c6a61e1a646768391c7854fe"`);
await queryRunner.query(
@ -316,24 +329,16 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE INDEX "IDX_3382f45eefeb40f91e45cfd418" ON "revision_tags_tag" ("revisionId") `,
);
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(
`ALTER TABLE "group_members_user" RENAME TO "temporary_group_members_user"`,
`ALTER TABLE "public_auth_token" RENAME TO "temporary_public_auth_token"`,
);
await queryRunner.query(
`CREATE TABLE "group_members_user" ("groupId" integer NOT NULL, "userId" integer NOT NULL, PRIMARY KEY ("groupId", "userId"))`,
`CREATE TABLE "public_auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "hash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_b4c4b9179f72ef63c32248e83ab" UNIQUE ("keyId"), CONSTRAINT "UQ_6450514886fa4182c889c076df6" UNIQUE ("hash"))`,
);
await queryRunner.query(
`INSERT INTO "group_members_user"("groupId", "userId") SELECT "groupId", "userId" FROM "temporary_group_members_user"`,
);
await queryRunner.query(`DROP TABLE "temporary_group_members_user"`);
await queryRunner.query(
`CREATE INDEX "IDX_427107c650638bcb2f1e167d2e" ON "group_members_user" ("userId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_bfa303089d367a2e3c02b002b8" ON "group_members_user" ("groupId") `,
`INSERT INTO "public_auth_token"("id", "keyId", "label", "createdAt", "hash", "validUntil", "lastUsedAt", "userId") SELECT "id", "keyId", "label", "createdAt", "hash", "validUntil", "lastUsedAt", "userId" FROM "temporary_public_auth_token"`,
);
await queryRunner.query(`DROP TABLE "temporary_public_auth_token"`);
await queryRunner.query(
`ALTER TABLE "identity" RENAME TO "temporary_identity"`,
);
@ -454,27 +459,19 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(
`CREATE UNIQUE INDEX "IDX_928dd947355b0837366470a916" ON "history_entry" ("noteId", "userId") `,
);
await queryRunner.query(
`ALTER TABLE "auth_token" RENAME TO "temporary_auth_token"`,
);
await queryRunner.query(
`CREATE TABLE "auth_token" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "keyId" varchar NOT NULL, "label" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "accessTokenHash" varchar NOT NULL, "validUntil" datetime NOT NULL, "lastUsedAt" date, "userId" integer, CONSTRAINT "UQ_9d2cf0a2cc3df58b87cbbc43e48" UNIQUE ("keyId"), CONSTRAINT "UQ_15b2228d06b08bb4cc98876c651" UNIQUE ("accessTokenHash"))`,
);
await queryRunner.query(
`INSERT INTO "auth_token"("id", "keyId", "label", "createdAt", "accessTokenHash", "validUntil", "lastUsedAt", "userId") SELECT "id", "keyId", "label", "createdAt", "accessTokenHash", "validUntil", "lastUsedAt", "userId" FROM "temporary_auth_token"`,
);
await queryRunner.query(`DROP TABLE "temporary_auth_token"`);
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(`DROP INDEX "IDX_470886feb50e30114e39c42698"`);
await queryRunner.query(`DROP INDEX "IDX_52c6a61e1a646768391c7854fe"`);
await queryRunner.query(`DROP TABLE "revision_edits_edit"`);
await queryRunner.query(`DROP INDEX "IDX_19dbafe2a8b456c0ef40858d49"`);
await queryRunner.query(`DROP INDEX "IDX_3382f45eefeb40f91e45cfd418"`);
await queryRunner.query(`DROP TABLE "revision_tags_tag"`);
await queryRunner.query(`DROP INDEX "IDX_427107c650638bcb2f1e167d2e"`);
await queryRunner.query(`DROP INDEX "IDX_bfa303089d367a2e3c02b002b8"`);
await queryRunner.query(`DROP TABLE "group_members_user"`);
await queryRunner.query(`DROP TABLE "identity"`);
await queryRunner.query(`DROP TABLE "group"`);
await queryRunner.query(`DROP TABLE "user"`);
await queryRunner.query(`DROP TABLE "public_auth_token"`);
await queryRunner.query(`DROP TABLE "identity"`);
await queryRunner.query(`DROP TABLE "author"`);
await queryRunner.query(`DROP INDEX "IDX_28c5d1d16da7908c97c9bc2f74"`);
await queryRunner.query(`DROP TABLE "session"`);
@ -487,10 +484,8 @@ export class Init1696756465867 implements MigrationInterface {
await queryRunner.query(`DROP TABLE "note_user_permission"`);
await queryRunner.query(`DROP INDEX "IDX_ee1744842a9ef3ffbc05a7016a"`);
await queryRunner.query(`DROP TABLE "note_group_permission"`);
await queryRunner.query(`DROP TABLE "group"`);
await queryRunner.query(`DROP TABLE "media_upload"`);
await queryRunner.query(`DROP INDEX "IDX_928dd947355b0837366470a916"`);
await queryRunner.query(`DROP TABLE "history_entry"`);
await queryRunner.query(`DROP TABLE "auth_token"`);
}
}

View file

@ -10,7 +10,6 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { Mock } from 'ts-mockery';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import appConfigMock from '../config/mock/app.config.mock';
import authConfigMock from '../config/mock/auth.config.mock';
@ -29,6 +28,7 @@ import { Identity } from '../identity/identity.entity';
import { LoggerModule } from '../logger/logger.module';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
@ -118,7 +118,7 @@ describe('AliasService', () => {
.useValue(aliasRepo)
.overrideProvider(getRepositoryToken(User))
.useClass(Repository)
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -15,7 +15,6 @@ import {
Repository,
} from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import { DefaultAccessLevel } from '../config/default-access-level.enum';
import appConfigMock from '../config/mock/app.config.mock';
@ -40,6 +39,7 @@ import { Identity } from '../identity/identity.entity';
import { LoggerModule } from '../logger/logger.module';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
@ -194,7 +194,7 @@ describe('NotesService', () => {
.useValue(aliasRepo)
.overrideProvider(getRepositoryToken(User))
.useValue(userRepo)
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -10,7 +10,6 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { Mock } from 'ts-mockery';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import { DefaultAccessLevel } from '../config/default-access-level.enum';
import { GuestAccess } from '../config/guest_access.enum';
@ -38,6 +37,7 @@ import {
import { Note } from '../notes/note.entity';
import { NotesModule } from '../notes/notes.module';
import { Tag } from '../notes/tag.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
import { Session } from '../sessions/session.entity';
@ -162,7 +162,7 @@ describe('PermissionsService', () => {
})
.overrideProvider(getRepositoryToken(User))
.useValue(userRepo)
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -10,7 +10,7 @@ import { User } from '../users/user.entity';
import { UsersService } from '../users/users.service';
@Injectable()
export class MockAuthGuard {
export class MockPublicAuthTokenGuard {
private user: User;
constructor(private usersService: UsersService) {}

View file

@ -9,7 +9,7 @@ import { IsDate, IsNumber, IsOptional, IsString } from 'class-validator';
import { BaseDto } from '../utils/base.dto.';
import { TimestampMillis } from '../utils/timestamp';
export class AuthTokenDto extends BaseDto {
export class PublicAuthTokenDto extends BaseDto {
@IsString()
label: string;
@ -30,12 +30,12 @@ export class AuthTokenDto extends BaseDto {
lastUsedAt: Date | null;
}
export class AuthTokenWithSecretDto extends AuthTokenDto {
export class PublicAuthTokenWithSecretDto extends PublicAuthTokenDto {
@IsString()
secret: string;
}
export class AuthTokenCreateDto extends BaseDto {
export class PublicAuthTokenCreateDto extends BaseDto {
@IsString()
label: string;

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,15 +14,15 @@ import {
import { User } from '../users/user.entity';
@Entity()
export class AuthToken {
export class PublicAuthToken {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
keyId: string;
@ManyToOne((_) => User, (user) => user.authTokens, {
onDelete: 'CASCADE', // This deletes the AuthToken, when the associated User is deleted
@ManyToOne((_) => User, (user) => user.publicAuthTokens, {
onDelete: 'CASCADE', // This deletes the PublicAuthToken, when the associated User is deleted
})
user: Promise<User>;
@ -33,7 +33,7 @@ export class AuthToken {
createdAt: Date;
@Column({ unique: true })
accessTokenHash: string;
hash: string;
@Column()
validUntil: Date;
@ -50,12 +50,12 @@ export class AuthToken {
label: string,
tokenString: string,
validUntil: Date,
): Omit<AuthToken, 'id' | 'createdAt'> {
const token = new AuthToken();
): Omit<PublicAuthToken, 'id' | 'createdAt'> {
const token = new PublicAuthToken();
token.keyId = keyId;
token.user = Promise.resolve(user);
token.label = label;
token.accessTokenHash = tokenString;
token.hash = tokenString;
token.validUntil = validUntil;
token.lastUsedAt = null;
return token;

View file

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { LoggerModule } from '../logger/logger.module';
import { UsersModule } from '../users/users.module';
import { MockPublicAuthTokenGuard } from './mock-public-auth-token-guard.service';
import { PublicAuthToken } from './public-auth-token.entity';
import { PublicAuthTokenService } from './public-auth-token.service';
import {
PublicAuthTokenGuard,
PublicAuthTokenStrategy,
} from './public-auth-token.strategy';
@Module({
imports: [
UsersModule,
PassportModule,
LoggerModule,
TypeOrmModule.forFeature([PublicAuthToken]),
],
providers: [
PublicAuthTokenService,
PublicAuthTokenStrategy,
MockPublicAuthTokenGuard,
PublicAuthTokenGuard,
],
exports: [PublicAuthTokenService],
})
export class PublicAuthTokenModule {}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -21,23 +21,23 @@ import { LoggerModule } from '../logger/logger.module';
import { Session } from '../sessions/session.entity';
import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module';
import { AuthToken } from './auth-token.entity';
import { AuthService } from './auth.service';
import { PublicAuthToken } from './public-auth-token.entity';
import { PublicAuthTokenService } from './public-auth-token.service';
describe('AuthService', () => {
let service: AuthService;
let service: PublicAuthTokenService;
let user: User;
let authToken: AuthToken;
let authToken: PublicAuthToken;
let userRepo: Repository<User>;
let authTokenRepo: Repository<AuthToken>;
let authTokenRepo: Repository<PublicAuthToken>;
class CreateQueryBuilderClass {
leftJoinAndSelect: () => CreateQueryBuilderClass;
where: () => CreateQueryBuilderClass;
orWhere: () => CreateQueryBuilderClass;
setParameter: () => CreateQueryBuilderClass;
getOne: () => AuthToken;
getMany: () => AuthToken[];
getOne: () => PublicAuthToken;
getMany: () => PublicAuthToken[];
}
let createQueryBuilderFunc: CreateQueryBuilderClass;
@ -45,9 +45,9 @@ describe('AuthService', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AuthService,
PublicAuthTokenService,
{
provide: getRepositoryToken(AuthToken),
provide: getRepositoryToken(PublicAuthToken),
useClass: Repository,
},
],
@ -69,20 +69,20 @@ describe('AuthService', () => {
.useValue({})
.compile();
service = module.get<AuthService>(AuthService);
service = module.get<PublicAuthTokenService>(PublicAuthTokenService);
userRepo = module.get<Repository<User>>(getRepositoryToken(User));
authTokenRepo = module.get<Repository<AuthToken>>(
getRepositoryToken(AuthToken),
authTokenRepo = module.get<Repository<PublicAuthToken>>(
getRepositoryToken(PublicAuthToken),
);
user = User.create('hardcoded', 'Testy') as User;
authToken = AuthToken.create(
authToken = PublicAuthToken.create(
'testKeyId',
user,
'testToken',
'abc',
new Date(new Date().getTime() + 60000), // make this AuthToken valid for 1min
) as AuthToken;
) as PublicAuthToken;
const createQueryBuilder = {
leftJoinAndSelect: () => createQueryBuilder,
@ -133,13 +133,13 @@ describe('AuthService', () => {
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce({
...authToken,
user: Promise.resolve(user),
accessTokenHash: accessTokenHash,
hash: accessTokenHash,
});
const authTokenFromCall = await service.getAuthToken(authToken.keyId);
expect(authTokenFromCall).toEqual({
...authToken,
user: Promise.resolve(user),
accessTokenHash: accessTokenHash,
hash: accessTokenHash,
});
});
describe('fails:', () => {
@ -160,13 +160,13 @@ describe('AuthService', () => {
);
expect(() =>
service.checkToken(secret, accessToken as AuthToken),
service.checkToken(secret, accessToken as PublicAuthToken),
).not.toThrow();
});
it('AuthToken has wrong hash', () => {
const [accessToken] = service.createToken(user, 'TestToken', undefined);
expect(() =>
service.checkToken('secret', accessToken as AuthToken),
service.checkToken('secret', accessToken as PublicAuthToken),
).toThrow(TokenNotValidError);
});
it('AuthToken has wrong validUntil Date', () => {
@ -176,7 +176,7 @@ describe('AuthService', () => {
1549312452000,
);
expect(() =>
service.checkToken(secret, accessToken as AuthToken),
service.checkToken(secret, accessToken as PublicAuthToken),
).toThrow(TokenNotValidError);
});
});
@ -191,7 +191,7 @@ describe('AuthService', () => {
jest
.spyOn(authTokenRepo, 'save')
.mockImplementationOnce(
async (authTokenSaved, _): Promise<AuthToken> => {
async (authTokenSaved, _): Promise<PublicAuthToken> => {
expect(authTokenSaved.keyId).toEqual(authToken.keyId);
expect(authTokenSaved.lastUsedAt).not.toEqual(1549312452000);
return authToken;
@ -217,16 +217,16 @@ describe('AuthService', () => {
.digest('hex');
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce({
...user,
authTokens: Promise.resolve([authToken]),
publicAuthTokens: Promise.resolve([authToken]),
});
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValue({
...authToken,
user: Promise.resolve(user),
accessTokenHash: accessTokenHash,
hash: accessTokenHash,
});
jest
.spyOn(authTokenRepo, 'save')
.mockImplementationOnce(async (_, __): Promise<AuthToken> => {
.mockImplementationOnce(async (_, __): Promise<PublicAuthToken> => {
return authToken;
});
const userByToken = await service.validateToken(
@ -234,7 +234,7 @@ describe('AuthService', () => {
);
expect(userByToken).toEqual({
...user,
authTokens: Promise.resolve([authToken]),
publicAuthTokens: Promise.resolve([authToken]),
});
});
describe('fails:', () => {
@ -276,7 +276,7 @@ describe('AuthService', () => {
});
jest
.spyOn(authTokenRepo, 'remove')
.mockImplementationOnce(async (token, __): Promise<AuthToken> => {
.mockImplementationOnce(async (token, __): Promise<PublicAuthToken> => {
expect(token).toEqual({
...authToken,
user: Promise.resolve(user),
@ -301,7 +301,10 @@ describe('AuthService', () => {
jest
.spyOn(authTokenRepo, 'save')
.mockImplementationOnce(
async (authTokenSaved: AuthToken, _): Promise<AuthToken> => {
async (
authTokenSaved: PublicAuthToken,
_,
): Promise<PublicAuthToken> => {
expect(authTokenSaved.lastUsedAt).toBeNull();
return authTokenSaved;
},
@ -320,7 +323,10 @@ describe('AuthService', () => {
jest
.spyOn(authTokenRepo, 'save')
.mockImplementationOnce(
async (authTokenSaved: AuthToken, _): Promise<AuthToken> => {
async (
authTokenSaved: PublicAuthToken,
_,
): Promise<PublicAuthToken> => {
expect(authTokenSaved.lastUsedAt).toBeNull();
return authTokenSaved;
},
@ -335,7 +341,7 @@ describe('AuthService', () => {
it('should throw TooManyTokensError when number of tokens >= 200', async () => {
jest
.spyOn(authTokenRepo, 'find')
.mockImplementationOnce(async (): Promise<AuthToken[]> => {
.mockImplementationOnce(async (): Promise<PublicAuthToken[]> => {
const inValidToken = [authToken];
inValidToken.length = 201;
return inValidToken;
@ -361,7 +367,7 @@ describe('AuthService', () => {
.mockResolvedValueOnce([expiredToken, authToken]);
jest
.spyOn(authTokenRepo, 'remove')
.mockImplementationOnce(async (token): Promise<AuthToken> => {
.mockImplementationOnce(async (token): Promise<PublicAuthToken> => {
expect(token).toEqual(expiredToken);
expect(token).not.toBe(authToken);
return authToken;
@ -388,7 +394,7 @@ describe('AuthService', () => {
});
describe('toAuthTokenDto', () => {
const authToken = new AuthToken();
const authToken = new PublicAuthToken();
authToken.keyId = 'testKeyId';
authToken.label = 'testLabel';
const date = new Date();

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -18,19 +18,22 @@ import { ConsoleLoggerService } from '../logger/console-logger.service';
import { User } from '../users/user.entity';
import { bufferToBase64Url } from '../utils/password';
import { TimestampMillis } from '../utils/timestamp';
import { AuthTokenDto, AuthTokenWithSecretDto } from './auth-token.dto';
import { AuthToken } from './auth-token.entity';
import {
PublicAuthTokenDto,
PublicAuthTokenWithSecretDto,
} from './public-auth-token.dto';
import { PublicAuthToken } from './public-auth-token.entity';
export const AUTH_TOKEN_PREFIX = 'hd2';
@Injectable()
export class AuthService {
export class PublicAuthTokenService {
constructor(
private readonly logger: ConsoleLoggerService,
@InjectRepository(AuthToken)
private authTokenRepository: Repository<AuthToken>,
@InjectRepository(PublicAuthToken)
private authTokenRepository: Repository<PublicAuthToken>,
) {
this.logger.setContext(AuthService.name);
this.logger.setContext(PublicAuthTokenService.name);
}
async validateToken(tokenString: string): Promise<User> {
@ -54,8 +57,8 @@ export class AuthService {
createToken(
user: User,
identifier: string,
validUntil: TimestampMillis | undefined,
): [Omit<AuthToken, 'id' | 'createdAt'>, string] {
userDefinedValidUntil: TimestampMillis | undefined,
): [Omit<PublicAuthToken, 'id' | 'createdAt'>, string] {
const secret = bufferToBase64Url(randomBytes(64));
const keyId = bufferToBase64Url(randomBytes(8));
// More about the choice of SHA-512 in the dev docs
@ -63,27 +66,23 @@ export class AuthService {
.createHash('sha512')
.update(secret)
.digest('hex');
let token;
// Tokens can only be valid for a maximum of 2 years
const maximumTokenValidity =
new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000;
if (!validUntil || validUntil === 0 || validUntil > maximumTokenValidity) {
token = AuthToken.create(
keyId,
user,
identifier,
accessTokenHash,
new Date(maximumTokenValidity),
);
} else {
token = AuthToken.create(
keyId,
user,
identifier,
accessTokenHash,
new Date(validUntil),
);
}
const isTokenLimitedToMaximumValidity =
!userDefinedValidUntil ||
userDefinedValidUntil === 0 ||
userDefinedValidUntil > maximumTokenValidity;
const validUntil = isTokenLimitedToMaximumValidity
? maximumTokenValidity
: userDefinedValidUntil;
const token = PublicAuthToken.create(
keyId,
user,
identifier,
accessTokenHash,
new Date(validUntil),
);
return [token, secret];
}
@ -91,10 +90,10 @@ export class AuthService {
user: User,
identifier: string,
validUntil: TimestampMillis | undefined,
): Promise<AuthTokenWithSecretDto> {
user.authTokens = this.getTokensByUser(user);
): Promise<PublicAuthTokenWithSecretDto> {
user.publicAuthTokens = this.getTokensByUser(user);
if ((await user.authTokens).length >= 200) {
if ((await user.publicAuthTokens).length >= 200) {
// This is a very high ceiling unlikely to hinder legitimate usage,
// but should prevent possible attack vectors
throw new TooManyTokensError(
@ -104,7 +103,7 @@ export class AuthService {
const [token, secret] = this.createToken(user, identifier, validUntil);
const createdToken = (await this.authTokenRepository.save(
token,
)) as AuthToken;
)) as PublicAuthToken;
return this.toAuthTokenWithSecretDto(
createdToken,
`${AUTH_TOKEN_PREFIX}.${createdToken.keyId}.${secret}`,
@ -122,7 +121,7 @@ export class AuthService {
await this.authTokenRepository.save(token);
}
async getAuthToken(keyId: string): Promise<AuthToken> {
async getAuthToken(keyId: string): Promise<PublicAuthToken> {
const token = await this.authTokenRepository.findOne({
where: { keyId: keyId },
relations: ['user'],
@ -133,11 +132,11 @@ export class AuthService {
return token;
}
checkToken(secret: string, token: AuthToken): void {
checkToken(secret: string, token: PublicAuthToken): void {
const userHash = Buffer.from(
crypto.createHash('sha512').update(secret).digest('hex'),
);
const dbHash = Buffer.from(token.accessTokenHash);
const dbHash = Buffer.from(token.hash);
if (
// Normally, both hashes have the same length, as they are both SHA512
// This is only defense-in-depth, as timingSafeEqual throws if the buffers are not of the same length
@ -159,7 +158,7 @@ export class AuthService {
}
}
async getTokensByUser(user: User): Promise<AuthToken[]> {
async getTokensByUser(user: User): Promise<PublicAuthToken[]> {
const tokens = await this.authTokenRepository.find({
where: { user: { id: user.id } },
});
@ -179,8 +178,8 @@ export class AuthService {
await this.authTokenRepository.remove(token);
}
toAuthTokenDto(authToken: AuthToken): AuthTokenDto {
const tokenDto: AuthTokenDto = {
toAuthTokenDto(authToken: PublicAuthToken): PublicAuthTokenDto {
const tokenDto: PublicAuthTokenDto = {
label: authToken.label,
keyId: authToken.keyId,
createdAt: authToken.createdAt,
@ -196,9 +195,9 @@ export class AuthService {
}
toAuthTokenWithSecretDto(
authToken: AuthToken,
authToken: PublicAuthToken,
secret: string,
): AuthTokenWithSecretDto {
): PublicAuthTokenWithSecretDto {
const tokenDto = this.toAuthTokenDto(authToken);
return {
...tokenDto,
@ -220,7 +219,7 @@ export class AuthService {
async removeInvalidTokens(): Promise<void> {
const currentTime = new Date().getTime();
const tokens: AuthToken[] = await this.authTokenRepository.find();
const tokens: PublicAuthToken[] = await this.authTokenRepository.find();
let removedTokens = 0;
for (const token of tokens) {
if (token.validUntil && token.validUntil.getTime() <= currentTime) {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -9,14 +9,17 @@ import { Strategy } from 'passport-http-bearer';
import { NotInDBError, TokenNotValidError } from '../errors/errors';
import { User } from '../users/user.entity';
import { AuthService } from './auth.service';
import { PublicAuthTokenService } from './public-auth-token.service';
@Injectable()
export class TokenAuthGuard extends AuthGuard('token') {}
export class PublicAuthTokenGuard extends AuthGuard('token') {}
@Injectable()
export class TokenStrategy extends PassportStrategy(Strategy, 'token') {
constructor(private authService: AuthService) {
export class PublicAuthTokenStrategy extends PassportStrategy(
Strategy,
'token',
) {
constructor(private authService: PublicAuthTokenService) {
super();
}

View file

@ -13,7 +13,6 @@ import { Mock } from 'ts-mockery';
import { Repository } from 'typeorm';
import WebSocket from 'ws';
import { AuthToken } from '../../auth/auth-token.entity';
import { Author } from '../../authors/author.entity';
import appConfigMock from '../../config/mock/app.config.mock';
import authConfigMock from '../../config/mock/auth.config.mock';
@ -33,6 +32,7 @@ import { NotePermission } from '../../permissions/note-permission.enum';
import { NoteUserPermission } from '../../permissions/note-user-permission.entity';
import { PermissionsModule } from '../../permissions/permissions.module';
import { PermissionsService } from '../../permissions/permissions.service';
import { PublicAuthToken } from '../../public-auth-token/public-auth-token.entity';
import { Edit } from '../../revisions/edit.entity';
import { Revision } from '../../revisions/revision.entity';
import { Session } from '../../sessions/session.entity';
@ -122,7 +122,7 @@ describe('Websocket gateway', () => {
})
.overrideProvider(getRepositoryToken(User))
.useClass(Repository)
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -10,7 +10,6 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { Mock } from 'ts-mockery';
import { Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import appConfigMock from '../config/mock/app.config.mock';
import authConfigMock from '../config/mock/auth.config.mock';
@ -27,6 +26,7 @@ import { NotesModule } from '../notes/notes.module';
import { Tag } from '../notes/tag.entity';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { Session } from '../sessions/session.entity';
import { User } from '../users/user.entity';
import { Edit } from './edit.entity';
@ -67,7 +67,7 @@ describe('RevisionsService', () => {
.useValue({})
.overrideProvider(getRepositoryToken(User))
.useValue({})
.overrideProvider(getRepositoryToken(AuthToken))
.overrideProvider(getRepositoryToken(PublicAuthToken))
.useValue({})
.overrideProvider(getRepositoryToken(Identity))
.useValue({})

View file

@ -5,7 +5,6 @@
*/
import { DataSource } from 'typeorm';
import { AuthToken } from './auth/auth-token.entity';
import { Author } from './authors/author.entity';
import { Group } from './groups/group.entity';
import { HistoryEntry } from './history/history-entry.entity';
@ -17,6 +16,7 @@ import { Note } from './notes/note.entity';
import { Tag } from './notes/tag.entity';
import { NoteGroupPermission } from './permissions/note-group-permission.entity';
import { NoteUserPermission } from './permissions/note-user-permission.entity';
import { PublicAuthToken } from './public-auth-token/public-auth-token.entity';
import { Edit } from './revisions/edit.entity';
import { Revision } from './revisions/revision.entity';
import { Session } from './sessions/session.entity';
@ -40,7 +40,7 @@ const dataSource = new DataSource({
HistoryEntry,
MediaUpload,
Tag,
AuthToken,
PublicAuthToken,
Identity,
Author,
Session,

View file

@ -13,13 +13,13 @@ import {
UpdateDateColumn,
} from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import { Group } from '../groups/group.entity';
import { HistoryEntry } from '../history/history-entry.entity';
import { Identity } from '../identity/identity.entity';
import { MediaUpload } from '../media/media-upload.entity';
import { Note } from '../notes/note.entity';
import { PublicAuthToken } from '../public-auth-token/public-auth-token.entity';
import { Username } from '../utils/username';
@Entity()
@ -56,8 +56,8 @@ export class User {
@OneToMany((_) => Note, (note) => note.owner)
ownedNotes: Promise<Note[]>;
@OneToMany((_) => AuthToken, (authToken) => authToken.user)
authTokens: Promise<AuthToken[]>;
@OneToMany((_) => PublicAuthToken, (authToken) => authToken.user)
publicAuthTokens: Promise<PublicAuthToken[]>;
@OneToMany((_) => Identity, (identity) => identity.user)
identities: Promise<Identity[]>;
@ -87,7 +87,7 @@ export class User {
newUser.photo = null;
newUser.email = null;
newUser.ownedNotes = Promise.resolve([]);
newUser.authTokens = Promise.resolve([]);
newUser.publicAuthTokens = Promise.resolve([]);
newUser.identities = Promise.resolve([]);
newUser.groups = Promise.resolve([]);
newUser.historyEntries = Promise.resolve([]);

View file

@ -14,11 +14,6 @@ import { Connection, createConnection } from 'typeorm';
import { PrivateApiModule } from '../src/api/private/private-api.module';
import { PublicApiModule } from '../src/api/public/public-api.module';
import { setupApp } from '../src/app-init';
import { AuthTokenWithSecretDto } from '../src/auth/auth-token.dto';
import { AuthModule } from '../src/auth/auth.module';
import { AuthService } from '../src/auth/auth.service';
import { MockAuthGuard } from '../src/auth/mock-auth.guard';
import { TokenAuthGuard } from '../src/auth/token.strategy';
import { AuthorsModule } from '../src/authors/authors.module';
import { AppConfig } from '../src/config/app.config';
import { AuthConfig } from '../src/config/auth.config';
@ -75,6 +70,11 @@ import { NotesModule } from '../src/notes/notes.module';
import { NotesService } from '../src/notes/notes.service';
import { PermissionsModule } from '../src/permissions/permissions.module';
import { PermissionsService } from '../src/permissions/permissions.service';
import { MockPublicAuthTokenGuard } from '../src/public-auth-token/mock-public-auth-token-guard.service';
import { PublicAuthTokenWithSecretDto } from '../src/public-auth-token/public-auth-token.dto';
import { PublicAuthTokenModule } from '../src/public-auth-token/public-auth-token.module';
import { PublicAuthTokenService } from '../src/public-auth-token/public-auth-token.service';
import { PublicAuthTokenGuard } from '../src/public-auth-token/public-auth-token.strategy';
import { RevisionsModule } from '../src/revisions/revisions.module';
import { RevisionsService } from '../src/revisions/revisions.service';
import { SessionModule } from '../src/sessions/session.module';
@ -105,12 +105,12 @@ export class TestSetup {
mediaService: MediaService;
historyService: HistoryService;
aliasService: AliasService;
authService: AuthService;
publicAuthTokenService: PublicAuthTokenService;
sessionService: SessionService;
revisionsService: RevisionsService;
users: User[] = [];
authTokens: AuthTokenWithSecretDto[] = [];
authTokens: PublicAuthTokenWithSecretDto[] = [];
anonymousNotes: Note[] = [];
ownedNotes: Note[] = [];
permissionsService: PermissionsService;
@ -288,7 +288,7 @@ export class TestSetupBuilder {
GroupsModule,
LoggerModule,
MediaModule,
AuthModule,
PublicAuthTokenModule,
FrontendConfigModule,
IdentityModule,
SessionModule,
@ -332,8 +332,10 @@ export class TestSetupBuilder {
this.testSetup.moduleRef.get<HistoryService>(HistoryService);
this.testSetup.aliasService =
this.testSetup.moduleRef.get<AliasService>(AliasService);
this.testSetup.authService =
this.testSetup.moduleRef.get<AuthService>(AuthService);
this.testSetup.publicAuthTokenService =
this.testSetup.moduleRef.get<PublicAuthTokenService>(
PublicAuthTokenService,
);
this.testSetup.permissionsService =
this.testSetup.moduleRef.get<PermissionsService>(PermissionsService);
this.testSetup.sessionService =
@ -363,8 +365,8 @@ export class TestSetupBuilder {
public withMockAuth() {
this.setupPreCompile.push(async () => {
this.testingModuleBuilder
.overrideGuard(TokenAuthGuard)
.useClass(MockAuthGuard);
.overrideGuard(PublicAuthTokenGuard)
.useClass(MockPublicAuthTokenGuard);
return await Promise.resolve();
});
return this;
@ -403,7 +405,7 @@ export class TestSetupBuilder {
// create auth tokens
this.testSetup.authTokens = await Promise.all(
this.testSetup.users.map(async (user) => {
return await this.testSetup.authService.addToken(
return await this.testSetup.publicAuthTokenService.addToken(
user,
'test',
new Date().getTime() + 60 * 60 * 1000,