some validation on entities' properties

This commit is contained in:
2020-10-14 09:05:31 +03:00
committed by Stepan Usatiuk
parent e792fc6adc
commit 0dbf4b020e
5 changed files with 88 additions and 2 deletions

26
package-lock.json generated
View File

@@ -500,6 +500,11 @@
"@types/superagent": "*" "@types/superagent": "*"
} }
}, },
"@types/validator": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.0.0.tgz",
"integrity": "sha512-WAy5txG7aFX8Vw3sloEKp5p/t/Xt8jD3GRD9DacnFv6Vo8ubudAsRTXgxpQwU0mpzY/H8U4db3roDuCMjShBmw=="
},
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.0.tgz",
@@ -1149,6 +1154,17 @@
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
}, },
"class-validator": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.12.2.tgz",
"integrity": "sha512-TDzPzp8BmpsbPhQpccB3jMUE/3pK0TyqamrK0kcx+ZeFytMA+O6q87JZZGObHHnoo9GM8vl/JppIyKWeEA/EVw==",
"requires": {
"@types/validator": "13.0.0",
"google-libphonenumber": "^3.2.8",
"tslib": ">=1.9.0",
"validator": "13.0.0"
}
},
"clean-stack": { "clean-stack": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -2466,6 +2482,11 @@
} }
} }
}, },
"google-libphonenumber": {
"version": "3.2.13",
"resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.13.tgz",
"integrity": "sha512-USnpjJkD8St+wyshy154lF74JeauNCd8vrcusSlWjSFWitXzl7ZSuCunA/XxeVLqBv6DShrSfFMYdwGZ7x4hOw=="
},
"graceful-fs": { "graceful-fs": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
@@ -5467,6 +5488,11 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"validator": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz",
"integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@@ -28,6 +28,7 @@
"@typescript-eslint/parser": "^4.4.0", "@typescript-eslint/parser": "^4.4.0",
"bcrypt": "^5.0.0", "bcrypt": "^5.0.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"class-validator": "^0.12.2",
"concurrently": "^5.3.0", "concurrently": "^5.3.0",
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"deasync": "^0.1.20", "deasync": "^0.1.20",

View File

@@ -6,7 +6,9 @@ import { constants as fsConstants } from "fs";
import { import {
AfterRemove, AfterRemove,
BaseEntity, BaseEntity,
BeforeInsert,
BeforeRemove, BeforeRemove,
BeforeUpdate,
Column, Column,
Entity, Entity,
Index, Index,
@@ -14,6 +16,15 @@ import {
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
} from "typeorm"; } from "typeorm";
import { User } from "./User"; import { User } from "./User";
import {
isAlphanumeric,
IsAlphanumeric,
IsHash,
IsMimeType,
Length,
Matches,
validateOrReject,
} from "class-validator";
export interface IPhotoJSON { export interface IPhotoJSON {
id: number; id: number;
@@ -32,14 +43,16 @@ export class Photo extends BaseEntity {
@Column({ length: 190 }) @Column({ length: 190 })
@Index() @Index()
@IsHash("md5")
public hash: string; public hash: string;
@Column({ length: 190 }) @Column({ length: 190 })
@Index() @IsAlphanumeric()
@Matches(/\d*x\d*/)
public size: string; public size: string;
@Column({ length: 190 }) @Column({ length: 190 })
@Index() @IsMimeType()
public format: string; public format: string;
@Column({ type: "timestamp", default: null }) @Column({ type: "timestamp", default: null })
@@ -61,6 +74,16 @@ export class Photo extends BaseEntity {
return path.join(this.user.getDataPath(), this.getFileName()); return path.join(this.user.getDataPath(), this.getFileName());
} }
@BeforeInsert()
async beforeInsertValidate(): Promise<void> {
return validateOrReject(this);
}
@BeforeUpdate()
async beforeUpdateValidate(): Promise<void> {
return validateOrReject(this);
}
@BeforeRemove() @BeforeRemove()
async cleanupFiles(): Promise<void> { async cleanupFiles(): Promise<void> {
try { try {

View File

@@ -8,6 +8,7 @@ import {
BaseEntity, BaseEntity,
BeforeInsert, BeforeInsert,
BeforeRemove, BeforeRemove,
BeforeUpdate,
Column, Column,
Entity, Entity,
Index, Index,
@@ -16,6 +17,14 @@ import {
} from "typeorm"; } from "typeorm";
import { config } from "../config"; import { config } from "../config";
import { Photo } from "./Photo"; import { Photo } from "./Photo";
import {
IsAlphanumeric,
IsBase32,
IsBase64,
IsEmail,
IsHash,
validateOrReject,
} from "class-validator";
export type IUserJSON = Pick<User, "id" | "username">; export type IUserJSON = Pick<User, "id" | "username">;
@@ -35,10 +44,12 @@ export class User extends BaseEntity {
@Column({ length: 190 }) @Column({ length: 190 })
@Index({ unique: true }) @Index({ unique: true })
@IsAlphanumeric()
public username: string; public username: string;
@Column({ length: 190 }) @Column({ length: 190 })
@Index({ unique: true }) @Index({ unique: true })
@IsEmail()
public email: string; public email: string;
@Column({ length: 190 }) @Column({ length: 190 })
@@ -75,6 +86,16 @@ export class User extends BaseEntity {
await fs.rmdir(this.getDataPath(), { recursive: true }); await fs.rmdir(this.getDataPath(), { recursive: true });
} }
@BeforeInsert()
async beforeInsertValidate(): Promise<void> {
return validateOrReject(this);
}
@BeforeUpdate()
async beforeUpdateValidate(): Promise<void> {
return validateOrReject(this);
}
public toJSON(): IUserJSON { public toJSON(): IUserJSON {
const { id, username } = this; const { id, username } = this;
return { id, username }; return { id, username };

View File

@@ -222,6 +222,21 @@ describe("photos", function () {
.expect(404); .expect(404);
}); });
it("should not create a photo with weird properties", async function () {
const response = await request(callback)
.post("/photos/new")
.set({
Authorization: `Bearer ${seed.user1.toJWT()}`,
"Content-Type": "application/json",
})
.send({
hash: "../test",
size: "33333",
format: dogFormat,
} as IPhotosNewPostBody)
.expect(400);
});
/* /*
it("should update a photo", async function () { it("should update a photo", async function () {
const response = await request(callback) const response = await request(callback)