Fix linting errors

This commit is contained in:
Alice Gaudon 2020-11-22 14:13:57 +01:00
parent 5e7d29ab98
commit 83ab56b4ac
13 changed files with 55 additions and 39 deletions

View File

@ -104,7 +104,7 @@ export default class App extends Application {
this.use(redisComponent); this.use(redisComponent);
this.use(new SessionComponent(redisComponent)); this.use(new SessionComponent(redisComponent));
this.use(new AuthComponent(new class extends AuthGuard<MagicLink | AuthToken> { this.use(new AuthComponent(new class extends AuthGuard<MagicLink | AuthToken> {
public async getProofForSession(session: Session): Promise<any | null> { public async getProofForSession(session: Session): Promise<MagicLink | AuthToken | null> {
return await MagicLink.bySessionId( return await MagicLink.bySessionId(
session.id, session.id,
[MagicLinkActionType.LOGIN, MagicLinkActionType.REGISTER], [MagicLinkActionType.LOGIN, MagicLinkActionType.REGISTER],
@ -122,7 +122,7 @@ export default class App extends Application {
return token; return token;
} }
return super.getProofForRequest(req); return await super.getProofForRequest(req);
} }
}(this))); }(this)));

View File

@ -14,4 +14,4 @@ export default async function generateSlug(tries: number): Promise<string> {
i++; i++;
} while (i < tries); } while (i < tries);
throw new ServerError('Failed to generate slug; newly generated slug size should be increased by 1.'); throw new ServerError('Failed to generate slug; newly generated slug size should be increased by 1.');
}; }

View File

@ -1,4 +1,4 @@
export function encodeRFC5987ValueChars(str: string) { export function encodeRFC5987ValueChars(str: string): string {
return encodeURIComponent(str). return encodeURIComponent(str).
// Note that although RFC3986 reserves "!", RFC5987 does not, // Note that although RFC3986 reserves "!", RFC5987 does not,
// so we do not need to escape it // so we do not need to escape it

View File

@ -2,7 +2,7 @@ import Controller from "swaf/Controller";
import {Request, Response} from "express"; import {Request, Response} from "express";
export default class AboutController extends Controller { export default class AboutController extends Controller {
routes(): void { public routes(): void {
this.get('/', this.getAbout, 'home'); this.get('/', this.getAbout, 'home');
this.get('/', this.getAbout, 'about'); this.get('/', this.getAbout, 'about');
} }

View File

@ -5,7 +5,7 @@ import AuthToken from "../models/AuthToken";
import {BadRequestError, ForbiddenHttpError, NotFoundHttpError} from "swaf/HttpError"; import {BadRequestError, ForbiddenHttpError, NotFoundHttpError} from "swaf/HttpError";
export default class AuthTokenController extends Controller { export default class AuthTokenController extends Controller {
routes(): void { public routes(): void {
this.post('/gen-auth-token', this.postGenAuthToken, 'generate-token', RequireAuthMiddleware); this.post('/gen-auth-token', this.postGenAuthToken, 'generate-token', RequireAuthMiddleware);
this.post('/revoke-auth-token/:id', this.postRevokeAuthToken, 'revoke-token', RequireAuthMiddleware); this.post('/revoke-auth-token/:id', this.postRevokeAuthToken, 'revoke-token', RequireAuthMiddleware);
} }

View File

@ -13,7 +13,7 @@ import FileUploadMiddleware from "swaf/FileUploadMiddleware";
export default class FileController extends Controller { export default class FileController extends Controller {
routes(): void { public routes(): void {
this.get('/files/upload', this.getFileUploader, 'file-upload', RequireAuthMiddleware); this.get('/files/upload', this.getFileUploader, 'file-upload', RequireAuthMiddleware);
this.get('/files/upload/script', this.downloadLinuxScript, 'file-linux-script'); this.get('/files/upload/script', this.downloadLinuxScript, 'file-linux-script');
this.post('/files/post', this.postFileFrontend, 'post-file-frontend', RequireAuthMiddleware, FileUploadFormMiddleware); this.post('/files/post', this.postFileFrontend, 'post-file-frontend', RequireAuthMiddleware, FileUploadFormMiddleware);
@ -45,12 +45,18 @@ export default class FileController extends Controller {
protected async postFileFrontend(req: Request, res: Response): Promise<void> { protected async postFileFrontend(req: Request, res: Response): Promise<void> {
req.body.type = 'file'; req.body.type = 'file';
await FileController.handleFileUpload(req.body.autogen_url === undefined && req.body.slug ? req.body.slug : await generateSlug(10), req, res); await FileController.handleFileUpload(
req.body.autogen_url === undefined && req.body.slug ?
req.body.slug :
await generateSlug(10),
req,
res,
);
} }
public static async handleFileUpload(slug: string, req: Request, res: Response): Promise<void> { public static async handleFileUpload(slug: string, req: Request, res: Response): Promise<void> {
// Check for file upload // Check for file upload
if (!req.files || !req.files['upload']) { if (Object.keys(req.files).indexOf('upload') < 0) {
throw new BadRequestError('No file received.', 'You must upload exactly one (1) file.', req.url); throw new BadRequestError('No file received.', 'You must upload exactly one (1) file.', req.url);
} }
@ -75,7 +81,7 @@ export default class FileController extends Controller {
}); });
await file.save(); await file.save();
fs.renameSync(upload.path, file.storage_path!); fs.renameSync(upload.path, file.getOrFail('storage_path'));
const domain = req.body.url_domain || config.get<string[]>('allowed_url_domains')[config.get<number>('default_url_domain_for_files')]; const domain = req.body.url_domain || config.get<string[]>('allowed_url_domains')[config.get<number>('default_url_domain_for_files')];
res.format({ res.format({
@ -122,7 +128,7 @@ export default class FileController extends Controller {
} }
public static async deleteFile(file: FileModel): Promise<void> { public static async deleteFile(file: FileModel): Promise<void> {
fs.unlinkSync(file.storage_path!); fs.unlinkSync(file.getOrFail('storage_path'));
await file.delete(); await file.delete();
log.info('Deleted', file.storage_path, `(${file.real_name})`); log.info('Deleted', file.storage_path, `(${file.real_name})`);
} }

View File

@ -14,7 +14,7 @@ import {promisify} from "util";
import {log} from "swaf/Logger"; import {log} from "swaf/Logger";
export default class LinkController extends Controller { export default class LinkController extends Controller {
routes(): void { public routes(): void {
this.post('/', this.postFile, 'post-file', RequireRequestAuthMiddleware, FileUploadFormMiddleware); this.post('/', this.postFile, 'post-file', RequireRequestAuthMiddleware, FileUploadFormMiddleware);
this.delete('/:slug', FileController.deleteFileRoute, 'delete-file', RequireRequestAuthMiddleware); this.delete('/:slug', FileController.deleteFileRoute, 'delete-file', RequireRequestAuthMiddleware);
this.get('/:slug', this.getFile, 'get-file'); this.get('/:slug', this.getFile, 'get-file');
@ -29,7 +29,6 @@ export default class LinkController extends Controller {
} }
protected async getFile(req: Request, res: Response, next: NextFunction): Promise<void> { protected async getFile(req: Request, res: Response, next: NextFunction): Promise<void> {
console.log('get file', req.params.slug)
const file = await FileModel.getBySlug(req.params.slug); const file = await FileModel.getBySlug(req.params.slug);
if (!file) return next(); if (!file) return next();
if (file.shouldBeDeleted()) { if (file.shouldBeDeleted()) {
@ -37,16 +36,16 @@ export default class LinkController extends Controller {
return next(); return next();
} }
const fileName = file.real_name!; const fileName = file.getOrFail('real_name');
switch (file.storage_type) { switch (file.storage_type) {
case 'local': case 'local': {
const stats = await promisify(fs.stat)(file.storage_path!); const stats = await promisify(fs.stat)(file.getOrFail('storage_path'));
// If file is bigger than max hotlink size, fallback to express download // If file is bigger than max hotlink size, fallback to express download
if (stats.size > config.get<number>('max_hotlink_size') * 1024 * 1024) { if (stats.size > config.get<number>('max_hotlink_size') * 1024 * 1024) {
log.info(`Fallback to express download for file of size ${stats.size}`); log.info(`Fallback to express download for file of size ${stats.size}`);
return res.download(file.storage_path!, fileName); return res.download(file.getOrFail('storage_path'), fileName);
} }
// File type // File type
@ -56,11 +55,12 @@ export default class LinkController extends Controller {
// File name // File name
res.header('Content-Disposition', `inline; filename*=UTF-8''${encodeRFC5987ValueChars(fileName)}`); res.header('Content-Disposition', `inline; filename*=UTF-8''${encodeRFC5987ValueChars(fileName)}`);
fs.readFile(file.storage_path!, (err, data) => { fs.readFile(file.getOrFail('storage_path'), (err, data) => {
if (err) return next(err); if (err) return next(err);
res.send(data); res.send(data);
}); });
break; break;
}
default: default:
throw new ServerError(`This file cannot be served. Download protocol for ${file.storage_type} storage type not implemented.`); throw new ServerError(`This file cannot be served. Download protocol for ${file.storage_type} storage type not implemented.`);
} }
@ -84,7 +84,7 @@ export default class LinkController extends Controller {
const url = await URLRedirect.getBySlug(req.params.slug); const url = await URLRedirect.getBySlug(req.params.slug);
if (!url) return next(); if (!url) return next();
res.redirect(url.target_url!, 301); res.redirect(url.getOrFail('target_url'), 301);
} }
protected async deleteURL(req: Request, res: Response, next: NextFunction): Promise<void> { protected async deleteURL(req: Request, res: Response, next: NextFunction): Promise<void> {
@ -94,7 +94,7 @@ export default class LinkController extends Controller {
throw new BadRequestError( throw new BadRequestError(
'Deleting url redirects is disabled for security reasons.', 'Deleting url redirects is disabled for security reasons.',
'If you still want to disable the redirection, please contact us via email.', 'If you still want to disable the redirection, please contact us via email.',
req.url req.url,
); );
} }

View File

@ -9,16 +9,16 @@ import App from "../App";
import AuthComponent from "swaf/auth/AuthComponent"; import AuthComponent from "swaf/auth/AuthComponent";
export default class MagicLinkController extends _MagicLinkController<App> { export default class MagicLinkController extends _MagicLinkController<App> {
constructor(magicLinkWebSocketListener: MagicLinkWebSocketListener<App>) { public constructor(magicLinkWebSocketListener: MagicLinkWebSocketListener<App>) {
super(magicLinkWebSocketListener); super(magicLinkWebSocketListener);
} }
protected async performAction(magicLink: MagicLink, req: Request, res: Response): Promise<void> { protected async performAction(magicLink: MagicLink, req: Request, res: Response): Promise<void> {
switch (magicLink.action_type) { switch (magicLink.action_type) {
case MagicLinkActionType.LOGIN: case MagicLinkActionType.LOGIN:
case MagicLinkActionType.REGISTER: case MagicLinkActionType.REGISTER: {
await AuthController.checkAndAuth(req, res, magicLink); await AuthController.checkAndAuth(req, res, magicLink);
const proof = await this.getApp().as(AuthComponent).getAuthGuard().isAuthenticated(req.session!); const proof = await this.getApp().as(AuthComponent).getAuthGuard().isAuthenticated(req.getSession());
const user = await proof?.getResource(); const user = await proof?.getResource();
if (!res.headersSent && user) { if (!res.headersSent && user) {
@ -27,6 +27,7 @@ export default class MagicLinkController extends _MagicLinkController<App> {
res.redirect(req.query.redirect_uri?.toString() || Controller.route('home')); res.redirect(req.query.redirect_uri?.toString() || Controller.route('home'));
} }
break; break;
}
} }
} }
} }

View File

@ -7,7 +7,7 @@ import config from "config";
import AuthToken from "../models/AuthToken"; import AuthToken from "../models/AuthToken";
export default class URLRedirectController extends Controller { export default class URLRedirectController extends Controller {
routes(): void { public routes(): void {
this.get('/url/shrink', this.getURLShrinker, 'url-shrinker', RequireAuthMiddleware); this.get('/url/shrink', this.getURLShrinker, 'url-shrinker', RequireAuthMiddleware);
this.get('/url/shrink/script', this.downloadLinuxScript, 'url-linux-script'); this.get('/url/shrink/script', this.downloadLinuxScript, 'url-linux-script');
this.post('/url/shrink', this.addURLFrontend, 'shrink-url', RequireAuthMiddleware); this.post('/url/shrink', this.addURLFrontend, 'shrink-url', RequireAuthMiddleware);
@ -37,7 +37,14 @@ export default class URLRedirectController extends Controller {
protected async addURLFrontend(req: Request, res: Response, next: NextFunction): Promise<void> { protected async addURLFrontend(req: Request, res: Response, next: NextFunction): Promise<void> {
req.body.type = 'url'; req.body.type = 'url';
await URLRedirectController.addURL(req, res, next, req.body.autogen_url === undefined && req.body.slug ? req.body.slug : await generateSlug(10)); await URLRedirectController.addURL(
req,
res,
next,
req.body.autogen_url === undefined && req.body.slug ?
req.body.slug :
await generateSlug(10),
);
} }
public static async addURL(req: Request, res: Response, next: NextFunction, slug?: string): Promise<void> { public static async addURL(req: Request, res: Response, next: NextFunction, slug?: string): Promise<void> {

View File

@ -9,7 +9,4 @@ export default class IncreaseFilesSizeField extends Migration {
public async rollback(connection: Connection): Promise<void> { public async rollback(connection: Connection): Promise<void> {
await this.query(`ALTER TABLE files MODIFY size INT UNSIGNED`, connection); await this.query(`ALTER TABLE files MODIFY size INT UNSIGNED`, connection);
} }
public registerModels(): void {
}
} }

View File

@ -6,34 +6,35 @@ import {cryptoRandomDictionary} from "swaf/Utils";
export default class AuthToken extends Model implements AuthProof<User> { export default class AuthToken extends Model implements AuthProof<User> {
public id?: number = undefined; public id?: number = undefined;
protected readonly user_id?: number = undefined; protected readonly user_id?: number = undefined;
protected readonly secret?: string = undefined; private secret?: string = undefined;
protected created_at?: Date = undefined; protected created_at?: Date = undefined;
protected used_at?: Date = undefined; protected used_at?: Date = undefined;
protected readonly ttl?: number = undefined; protected readonly ttl?: number = undefined;
protected init() { protected init(): void {
this.setValidation('user_id').defined().exists(User, 'id'); this.setValidation('user_id').defined().exists(User, 'id');
this.setValidation('secret').defined().between(32, 64); this.setValidation('secret').defined().between(32, 64);
this.setValidation('ttl').defined().min(1).max(5 * 365 * 24 * 3600 /* 5 years */); this.setValidation('ttl').defined().min(1).max(5 * 365 * 24 * 3600 /* 5 years */);
} }
protected async autoFill(): Promise<void> { protected async autoFill(): Promise<void> {
// @ts-ignore if (!this.secret) {
if (!this.secret) this['secret'] = cryptoRandomDictionary(64, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_'); this.secret = cryptoRandomDictionary(64, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_');
}
} }
public use() { public use(): void {
this.used_at = new Date(); this.used_at = new Date();
} }
public canDelete(user_id: number) { public canDelete(user_id: number): boolean {
return this.user_id === user_id; return this.user_id === user_id;
} }
public getExpirationDate(): Date { public getExpirationDate(): Date {
if (!this.created_at) return new Date(); if (!this.created_at) return new Date();
return new Date(this.created_at.getTime() + this.ttl! * 1000); return new Date(this.created_at.getTime() + this.getOrFail('ttl') * 1000);
} }
public async getResource(): Promise<User | null> { public async getResource(): Promise<User | null> {
@ -51,4 +52,8 @@ export default class AuthToken extends Model implements AuthProof<User> {
public async revoke(): Promise<void> { public async revoke(): Promise<void> {
await this.delete(); await this.delete();
} }
protected getSecret(): string | undefined {
return this.secret;
}
} }

View File

@ -28,7 +28,7 @@ export default class FileModel extends Model {
public created_at?: Date = undefined; public created_at?: Date = undefined;
public readonly ttl?: number = undefined; public readonly ttl?: number = undefined;
protected init() { protected init(): void {
this.setValidation('user_id').defined().exists(User, 'id'); this.setValidation('user_id').defined().exists(User, 'id');
this.setValidation('slug').defined().minLength(1).maxLength(259).unique(FileModel, 'slug').unique(URLRedirect, 'slug'); this.setValidation('slug').defined().minLength(1).maxLength(259).unique(FileModel, 'slug').unique(URLRedirect, 'slug');
this.setValidation('real_name').defined().minLength(1).maxLength(259); this.setValidation('real_name').defined().minLength(1).maxLength(259);
@ -40,7 +40,7 @@ export default class FileModel extends Model {
public getURL(domain: string = config.get<string>('base_url')): string { public getURL(domain: string = config.get<string>('base_url')): string {
return (/^https?:\/\//.test(domain) ? '' : 'https://') + domain + Controller.route('get-file', { return (/^https?:\/\//.test(domain) ? '' : 'https://') + domain + Controller.route('get-file', {
slug: this.slug!, slug: this.getOrFail('slug'),
}); });
} }
@ -48,7 +48,7 @@ export default class FileModel extends Model {
if (!this.created_at) return new Date(); if (!this.created_at) return new Date();
if (this.ttl === 0) return null; if (this.ttl === 0) return null;
return new Date(this.created_at.getTime() + this.ttl! * 1000); return new Date(this.created_at.getTime() + this.getOrFail('ttl') * 1000);
} }
public shouldBeDeleted(): boolean { public shouldBeDeleted(): boolean {

View File

@ -32,7 +32,7 @@ export default class URLRedirect extends Model {
public getURL(domain: string = config.get<string>('base_url')): string { public getURL(domain: string = config.get<string>('base_url')): string {
return (/^https?:\/\//.test(domain) ? '' : 'https://') + domain + Controller.route('get-url', { return (/^https?:\/\//.test(domain) ? '' : 'https://') + domain + Controller.route('get-url', {
slug: this.slug!, slug: this.getOrFail('slug'),
}); });
} }
} }