mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-13 23:35:39 +01:00
Fix bugs in data validation
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
import { SupportedMime } from 'imagur-shared/dist/dto/mimes.dto';
|
import { SupportedMime } from 'imagur-shared/dist/dto/mimes.dto';
|
||||||
import { GetCols } from '../collectionutils';
|
import { GetCols } from '../collectionutils';
|
||||||
import { EImage } from 'imagur-shared/dist/entities/image.entity';
|
import { EImage } from 'imagur-shared/dist/entities/image.entity';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImageDBService {
|
export class ImageDBService {
|
||||||
@@ -27,15 +28,19 @@ export class ImageDBService {
|
|||||||
const find = await this.findOne(hash);
|
const find = await this.findOne(hash);
|
||||||
if (HasSuccess(find)) return find;
|
if (HasSuccess(find)) return find;
|
||||||
|
|
||||||
const imageEntity = new EImage();
|
let imageEntity = new EImage();
|
||||||
imageEntity.data = image;
|
imageEntity.data = image;
|
||||||
imageEntity.mime = type;
|
imageEntity.mime = type;
|
||||||
imageEntity.hash = hash;
|
imageEntity.hash = hash;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.imageRepository.save(imageEntity);
|
imageEntity = await this.imageRepository.save(imageEntity);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return Fail(e?.message);
|
return Fail(e?.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strips unwanted data
|
||||||
|
return plainToClass(EImage, imageEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findOne<B extends true | undefined = undefined>(
|
public async findOne<B extends true | undefined = undefined>(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
import { validate } from 'class-validator';
|
import { validate } from 'class-validator';
|
||||||
import { EUser } from 'imagur-shared/dist/entities/user.entity';
|
import { EUser } from 'imagur-shared/dist/entities/user.entity';
|
||||||
import {
|
import {
|
||||||
@@ -26,17 +27,20 @@ export class UsersService {
|
|||||||
): AsyncFailable<EUser> {
|
): AsyncFailable<EUser> {
|
||||||
if (await this.exists(username)) return Fail('User already exists');
|
if (await this.exists(username)) return Fail('User already exists');
|
||||||
|
|
||||||
const user = new EUser();
|
let user = new EUser();
|
||||||
user.username = username;
|
user.username = username;
|
||||||
user.password = hashedPassword;
|
user.password = hashedPassword;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.usersRepository.save(user);
|
user = await this.usersRepository.save(user, { reload: true });
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return Fail(e?.message);
|
return Fail(e?.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return plainToClass(EUser, user); // Strips unwanted data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns user object without id
|
||||||
public async delete(user: string | EUser): AsyncFailable<EUser> {
|
public async delete(user: string | EUser): AsyncFailable<EUser> {
|
||||||
const userToModify = await this.resolve(user);
|
const userToModify = await this.resolve(user);
|
||||||
if (HasFailed(userToModify)) return userToModify;
|
if (HasFailed(userToModify)) return userToModify;
|
||||||
@@ -94,7 +98,8 @@ export class UsersService {
|
|||||||
if (typeof user === 'string') {
|
if (typeof user === 'string') {
|
||||||
return await this.findOne(user);
|
return await this.findOne(user);
|
||||||
} else {
|
} else {
|
||||||
const errors = await validate(user);
|
user = plainToClass(EUser, user);
|
||||||
|
const errors = await validate(user, { forbidUnknownValues: true });
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
this.logger.warn(errors);
|
this.logger.warn(errors);
|
||||||
return Fail('Invalid user');
|
return Fail('Invalid user');
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
import { validate } from 'class-validator';
|
||||||
import { JwtDataDto } from 'imagur-shared/dist/dto/auth.dto';
|
import { JwtDataDto } from 'imagur-shared/dist/dto/auth.dto';
|
||||||
import { EUser } from 'imagur-shared/dist/entities/user.entity';
|
import { EUser } from 'imagur-shared/dist/entities/user.entity';
|
||||||
import { AsyncFailable, HasFailed, Fail } from 'imagur-shared/dist/types';
|
import { AsyncFailable, HasFailed, Fail } from 'imagur-shared/dist/types';
|
||||||
@@ -8,6 +10,8 @@ import { UsersService } from '../../../collections/userdb/userdb.service';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
private readonly logger = new Logger('AuthService');
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private usersService: UsersService,
|
private usersService: UsersService,
|
||||||
private jwtService: JwtService,
|
private jwtService: JwtService,
|
||||||
@@ -37,9 +41,15 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createToken(user: EUser): Promise<string> {
|
async createToken(user: EUser): Promise<string> {
|
||||||
const jwtData: JwtDataDto = {
|
const jwtData: JwtDataDto = plainToClass(JwtDataDto, {
|
||||||
user,
|
user,
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const errors = await validate(jwtData, { forbidUnknownValues: true });
|
||||||
|
if (errors.length > 0) {
|
||||||
|
this.logger.warn(errors);
|
||||||
|
throw new Error('Invalid jwt token generated');
|
||||||
|
}
|
||||||
|
|
||||||
return this.jwtService.signAsync(jwtData);
|
return this.jwtService.signAsync(jwtData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|||||||
async validate(payload: any): Promise<EUser> {
|
async validate(payload: any): Promise<EUser> {
|
||||||
const jwt = plainToClass(JwtDataDto, payload);
|
const jwt = plainToClass(JwtDataDto, payload);
|
||||||
|
|
||||||
const errors = await validate(jwt, { forbidUnknownValues: true });
|
const errors = await validate(jwt, {
|
||||||
|
forbidUnknownValues: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
this.logger.warn(errors);
|
this.logger.warn(errors);
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import { IsDefined, IsEnum, IsHash, IsNumber, IsOptional, IsString } from 'class-validator';
|
import { Exclude } from 'class-transformer';
|
||||||
|
import {
|
||||||
|
IsDefined,
|
||||||
|
IsEnum,
|
||||||
|
IsHash,
|
||||||
|
IsOptional,
|
||||||
|
} from 'class-validator';
|
||||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { SupportedMime, SupportedMimes } from '../dto/mimes.dto';
|
import { SupportedMime, SupportedMimes } from '../dto/mimes.dto';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class EImage {
|
export class EImage {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@IsNumber()
|
@IsOptional()
|
||||||
@IsDefined()
|
id?: number;
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ unique: true })
|
@Column({ unique: true })
|
||||||
@IsString()
|
|
||||||
@IsHash('sha256')
|
@IsHash('sha256')
|
||||||
hash: string;
|
hash: string;
|
||||||
|
|
||||||
// Binary data
|
// Binary data
|
||||||
@Column({ type: 'bytea', nullable: false, select: false })
|
@Column({ type: 'bytea', nullable: false, select: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@Exclude()
|
||||||
data?: Buffer;
|
data?: Buffer;
|
||||||
|
|
||||||
@Column({ enum: SupportedMimes })
|
@Column({ enum: SupportedMimes })
|
||||||
|
|||||||
@@ -1,33 +1,31 @@
|
|||||||
|
import { Exclude, Expose } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsBoolean,
|
|
||||||
IsDefined,
|
IsDefined,
|
||||||
|
IsEmpty,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
// Different data for public and private
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class EUser {
|
export class EUser {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@IsNumber()
|
@IsOptional()
|
||||||
@IsDefined()
|
id?: number;
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ unique: true })
|
@Column({ unique: true })
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
@IsDefined()
|
@IsDefined()
|
||||||
@IsBoolean()
|
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
|
||||||
@Column({ select: false })
|
@Column({ select: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@Exclude()
|
||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user