diff --git a/src/api/controllers/ApiKeyController.ts b/src/api/controllers/ApiKeyController.ts index de3dbfde7..2fd642d45 100644 --- a/src/api/controllers/ApiKeyController.ts +++ b/src/api/controllers/ApiKeyController.ts @@ -44,8 +44,9 @@ export class ApiKeyController { await getRepository(ApiKey).save({ id, secretHash, + creator, description: data.description, - creator + expires: data.expires }); return { token }; diff --git a/src/api/data/ApiKeyData/index.ts b/src/api/data/ApiKeyData/index.ts index 84e822d6e..4731a29f7 100644 --- a/src/api/data/ApiKeyData/index.ts +++ b/src/api/data/ApiKeyData/index.ts @@ -8,9 +8,9 @@ export function convertApiKeyToData(apiKey: ApiKey): GetApiKeyData { return { id: apiKey.id, description: apiKey.description, + expires: apiKey.expires, creator: convertContactToData(apiKey.creator), - createdAt: apiKey.createdAt, - secretHash: apiKey.secretHash + createdAt: apiKey.createdAt }; } diff --git a/src/api/data/ApiKeyData/interface.ts b/src/api/data/ApiKeyData/interface.ts index 6a81e47e3..38f8d8bac 100644 --- a/src/api/data/ApiKeyData/interface.ts +++ b/src/api/data/ApiKeyData/interface.ts @@ -1,20 +1,25 @@ -import { IsIn, IsString } from "class-validator"; +import { IsDate, IsIn, IsOptional, IsString } from "class-validator"; import { GetContactData } from "../ContactData"; import { GetPaginatedQuery } from "../PaginatedData"; +import { Type } from "class-transformer"; export class CreateApiKeyData { @IsString() description!: string; + + @IsOptional() + @Type(() => Date) + @IsDate() + expires!: Date | null; } export interface GetApiKeyData extends CreateApiKeyData { id: string; creator: GetContactData; createdAt: Date; - secretHash: string; } export class GetApiKeysQuery extends GetPaginatedQuery { - @IsIn(["createdAt"]) + @IsIn(["createdAt", "expires"]) sort?: string; } diff --git a/src/core/services/AuthService.ts b/src/core/services/AuthService.ts index e7ce92912..b70830fd7 100644 --- a/src/core/services/AuthService.ts +++ b/src/core/services/AuthService.ts @@ -11,7 +11,7 @@ async function isValidApiKey(key: string): Promise { const [_, secret] = key.split("_"); const secretHash = crypto.createHash("sha256").update(secret).digest("hex"); const apiKey = await getRepository(ApiKey).findOne({ secretHash }); - return !!apiKey; + return !!apiKey && (!apiKey.expires || apiKey.expires > new Date()); } class AuthService { diff --git a/src/migrations/1698938717666-AddApiKeyExpiryDate.ts b/src/migrations/1698938717666-AddApiKeyExpiryDate.ts new file mode 100644 index 000000000..6a86e3c08 --- /dev/null +++ b/src/migrations/1698938717666-AddApiKeyExpiryDate.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddApiKeyExpiryDate1698938717666 implements MigrationInterface { + name = "AddApiKeyExpiryDate1698938717666"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "api_key" ADD "expires" TIMESTAMP`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "expires"`); + } +} diff --git a/src/models/ApiKey.ts b/src/models/ApiKey.ts index b9d9999a4..7a3eb1ef3 100644 --- a/src/models/ApiKey.ts +++ b/src/models/ApiKey.ts @@ -18,6 +18,9 @@ export default class ApiKey { @CreateDateColumn() createdAt!: Date; + @Column({ type: Date, nullable: true }) + expires!: Date | null; + @Column() secretHash!: string;