remove some anys

This commit is contained in:
Evert Prants 2022-09-18 10:22:57 +03:00
parent 9c7820dc94
commit 33080558bc
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
24 changed files with 110 additions and 90 deletions

View File

@ -35,7 +35,7 @@ export class OAuth2Guard implements CanActivate {
}) })
.catch(reject); .catch(reject);
}); });
} catch (e: any) { } catch (e: unknown) {
reject(e); reject(e);
} }
}); });

View File

@ -22,7 +22,7 @@ export class FlashMiddleware implements NestMiddleware {
return msgs; return msgs;
} else { } else {
result = msg.length > 1 ? format(...msg) : msg[0]; result = (msg.length > 1 ? format(...msg) : msg[0]) as string;
} }
return (msgs[type] = msgs[type] || []).push(result); return (msgs[type] = msgs[type] || []).push(result);

View File

@ -204,7 +204,7 @@ export class UserAdminController {
try { try {
await this._user.sendActivationEmail(user); await this._user.sendActivationEmail(user);
} catch (e: any) { } catch (e: unknown) {
error = e as Error; error = e as Error;
} }
@ -229,7 +229,7 @@ export class UserAdminController {
try { try {
await this._user.sendPasswordEmail(user); await this._user.sendPasswordEmail(user);
} catch (e: any) { } catch (e: unknown) {
error = e as Error; error = e as Error;
} }

View File

@ -55,7 +55,7 @@ export class ApiController {
// Management scope allows access to basically everything anyway // Management scope allows access to basically everything anyway
const scopelessAccess = scope.includes('management'); const scopelessAccess = scope.includes('management');
const userData: Record<string, any> = { const userData: Record<string, unknown> = {
id: user.id, id: user.id,
uuid: user.uuid, uuid: user.uuid,
username: user.username, username: user.username,

View File

@ -23,8 +23,13 @@ export interface EmailConfiguration {
export interface AppConfiguration { export interface AppConfiguration {
base_url: string; base_url: string;
host: string;
port: number;
session_name: string;
session_secret: string; session_secret: string;
challenge_secret: string; challenge_secret: string;
registrations: boolean;
redis_url?: string;
} }
export interface Configuration { export interface Configuration {

View File

@ -2,16 +2,16 @@ import * as toml from 'toml';
import { resolve } from 'path'; import { resolve } from 'path';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import { Configuration } from './config.interfaces'; import { Configuration } from './config.interfaces';
import { Provider } from '@nestjs/common'; import { FactoryProvider, ValueProvider } from '@nestjs/common';
const CONFIG_ENV = process.env.NODE_ENV === 'production' ? 'prod' : 'dev'; const CONFIG_ENV = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
const CONFIG_FILENAME = process.env.CONFIG || `config.${CONFIG_ENV}.toml`; const CONFIG_FILENAME = process.env.CONFIG || `config.${CONFIG_ENV}.toml`;
export const configProviders: Provider<any>[] = [ export const configProviders = [
{ {
provide: 'CONFIG_PATH', provide: 'CONFIG_PATH',
useValue: resolve(__dirname, '..', '..', '..', CONFIG_FILENAME), useValue: resolve(__dirname, '..', '..', '..', CONFIG_FILENAME),
}, } as ValueProvider<string>,
{ {
provide: 'DEFAULT_CONFIG', provide: 'DEFAULT_CONFIG',
useValue: { useValue: {
@ -57,7 +57,7 @@ export const configProviders: Provider<any>[] = [
}, },
}, },
} as Configuration, } as Configuration,
}, } as ValueProvider<Configuration>,
{ {
provide: 'CONFIGURATION', provide: 'CONFIGURATION',
useFactory: async ( useFactory: async (
@ -70,11 +70,11 @@ export const configProviders: Provider<any>[] = [
...defaultConfig, ...defaultConfig,
...JSON.parse(JSON.stringify(toml.parse(file))), ...JSON.parse(JSON.stringify(toml.parse(file))),
}; };
} catch (e: any) { } catch (e: unknown) {
console.error('Failed to load configuration:', e.message); console.error('Failed to load configuration:', (e as Error).message);
return defaultConfig; return defaultConfig;
} }
}, },
inject: ['CONFIG_PATH', 'DEFAULT_CONFIG'], inject: ['CONFIG_PATH', 'DEFAULT_CONFIG'],
}, } as FactoryProvider<Configuration>,
]; ];

View File

@ -1,20 +1,20 @@
import { join } from 'path'; import { join } from 'path';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import { Provider } from '@nestjs/common'; import { FactoryProvider, ValueProvider } from '@nestjs/common';
export const jwtProviders: Provider<any>[] = [ export const jwtProviders = [
{ {
provide: 'PRIVATE_PATH', provide: 'PRIVATE_PATH',
useValue: join(__dirname, '..', '..', '..', 'private'), useValue: join(__dirname, '..', '..', '..', 'private'),
}, } as ValueProvider<string>,
{ {
provide: 'JWT_PRIVATE_KEY', provide: 'JWT_PRIVATE_KEY',
useFactory: async (path: string) => readFile(join(path, 'jwt.private.pem')), useFactory: async (path: string) => readFile(join(path, 'jwt.private.pem')),
inject: ['PRIVATE_PATH'], inject: ['PRIVATE_PATH'],
}, } as FactoryProvider<Buffer>,
{ {
provide: 'JWT_PUBLIC_KEY', provide: 'JWT_PUBLIC_KEY',
useFactory: async (path: string) => readFile(join(path, 'jwt.public.pem')), useFactory: async (path: string) => readFile(join(path, 'jwt.public.pem')),
inject: ['PRIVATE_PATH'], inject: ['PRIVATE_PATH'],
}, } as FactoryProvider<Buffer>,
]; ];

View File

@ -10,13 +10,13 @@ import * as jwt from 'jsonwebtoken';
@Injectable() @Injectable()
export class JWTService { export class JWTService {
constructor( constructor(
@Inject('JWT_PRIVATE_KEY') private _privateKey: string, @Inject('JWT_PRIVATE_KEY') private _privateKey: Buffer,
@Inject('JWT_PUBLIC_KEY') private _publicKey: string, @Inject('JWT_PUBLIC_KEY') private _publicKey: Buffer,
private _config: ConfigurationService, private _config: ConfigurationService,
) {} ) {}
public issue( public issue(
claims: Record<string, any>, claims: Record<string, unknown>,
subject: string, subject: string,
audience?: string, audience?: string,
): string { ): string {

View File

@ -1,17 +1,17 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider, ValueProvider } from '@nestjs/common';
import { join } from 'path'; import { join } from 'path';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { Document } from './document.entity'; import { Document } from './document.entity';
export const documentProviders: Provider<any>[] = [ export const documentProviders = [
{ {
provide: 'DOCUMENT_CACHE', provide: 'DOCUMENT_CACHE',
useValue: join(process.cwd(), '.cache'), useValue: join(process.cwd(), '.cache'),
}, } as ValueProvider<string>,
{ {
provide: 'DOCUMENT_REPOSITORY', provide: 'DOCUMENT_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(Document), useFactory: (dataSource: DataSource) => dataSource.getRepository(Document),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<Document>>,
]; ];

View File

@ -17,7 +17,7 @@ export class EmailService {
text: string, text: string,
html?: string, html?: string,
from = this.config.get<string>('email.from'), from = this.config.get<string>('email.from'),
): Promise<any> { ): Promise<unknown> {
return this.transport.sendMail({ return this.transport.sendMail({
to, to,
subject, subject,
@ -31,7 +31,7 @@ export class EmailService {
to: string, to: string,
subject: string, subject: string,
message: EmailTemplate, message: EmailTemplate,
): Promise<any> { ): Promise<unknown> {
return this.sendEmail(to, subject, message.text, message.html); return this.sendEmail(to, subject, message.text, message.html);
} }
} }

View File

@ -1,10 +1,12 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { OAuth2ClientAuthorization } from './oauth2-client-authorization.entity'; import { OAuth2ClientAuthorization } from './oauth2-client-authorization.entity';
import { OAuth2ClientURL } from './oauth2-client-url.entity'; import { OAuth2ClientURL } from './oauth2-client-url.entity';
import { OAuth2Client } from './oauth2-client.entity'; import { OAuth2Client } from './oauth2-client.entity';
export const clientProviders: Provider<any>[] = [ export const clientProviders: FactoryProvider<
Repository<OAuth2Client | OAuth2ClientURL | OAuth2ClientAuthorization>
>[] = [
{ {
provide: 'CLIENT_REPOSITORY', provide: 'CLIENT_REPOSITORY',
useFactory: (dataSource: DataSource) => useFactory: (dataSource: DataSource) =>

View File

@ -1,12 +1,12 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { OAuth2Token } from './oauth2-token.entity'; import { OAuth2Token } from './oauth2-token.entity';
export const tokenProviders: Provider<any>[] = [ export const tokenProviders = [
{ {
provide: 'TOKEN_REPOSITORY', provide: 'TOKEN_REPOSITORY',
useFactory: (dataSource: DataSource) => useFactory: (dataSource: DataSource) =>
dataSource.getRepository(OAuth2Token), dataSource.getRepository(OAuth2Token),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<OAuth2Token>>,
]; ];

View File

@ -1,11 +1,11 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { Privilege } from './privilege.entity'; import { Privilege } from './privilege.entity';
export const privilegeProviders: Provider<any>[] = [ export const privilegeProviders = [
{ {
provide: 'PRIVILEGE_REPOSITORY', provide: 'PRIVILEGE_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(Privilege), useFactory: (dataSource: DataSource) => dataSource.getRepository(Privilege),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<Privilege>>,
]; ];

View File

@ -1,11 +1,11 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { Upload } from './upload.entity'; import { Upload } from './upload.entity';
export const uploadProviders: Provider<any>[] = [ export const uploadProviders = [
{ {
provide: 'UPLOAD_REPOSITORY', provide: 'UPLOAD_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(Upload), useFactory: (dataSource: DataSource) => dataSource.getRepository(Upload),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<Upload>>,
]; ];

View File

@ -56,8 +56,8 @@ export class UploadService {
try { try {
await unlink(path); await unlink(path);
} catch (e: any) { } catch (e: unknown) {
console.error('Failed to unlink avatar file:', e.stack); console.error('Failed to unlink avatar file:', (e as Error).stack);
} }
await this.uploadRepository.remove(upload); await this.uploadRepository.remove(upload);

View File

@ -1,11 +1,11 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { UserToken } from './user-token.entity'; import { UserToken } from './user-token.entity';
export const userTokenProviders: Provider<any>[] = [ export const userTokenProviders = [
{ {
provide: 'USER_TOKEN_REPOSITORY', provide: 'USER_TOKEN_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(UserToken), useFactory: (dataSource: DataSource) => dataSource.getRepository(UserToken),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<UserToken>>,
]; ];

View File

@ -1,11 +1,11 @@
import { Provider } from '@nestjs/common'; import { FactoryProvider } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { User } from './user.entity'; import { User } from './user.entity';
export const userProviders: Provider<any>[] = [ export const userProviders = [
{ {
provide: 'USER_REPOSITORY', provide: 'USER_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(User), useFactory: (dataSource: DataSource) => dataSource.getRepository(User),
inject: ['DATA_SOURCE'], inject: ['DATA_SOURCE'],
}, } as FactoryProvider<Repository<User>>,
]; ];

View File

@ -41,7 +41,7 @@ export class LoginController {
public loginView( public loginView(
@Req() req: Request, @Req() req: Request,
@Query('redirectTo') redirectTo?: string, @Query('redirectTo') redirectTo?: string,
): Record<string, any> { ): Record<string, unknown> {
return this.formUtil.populateTemplate(req, { return this.formUtil.populateTemplate(req, {
query: redirectTo query: redirectTo
? new URLSearchParams({ redirectTo }).toString() ? new URLSearchParams({ redirectTo }).toString()
@ -141,7 +141,11 @@ export class LoginController {
throw new Error('No challenge'); throw new Error('No challenge');
} }
const decrypted = await this.token.decryptChallenge(query.challenge); const decrypted = await this.token.decryptChallenge<{
type: 'verify';
user: string;
remember: boolean;
}>(query.challenge);
if (!decrypted || decrypted.type !== 'verify' || !decrypted.user) { if (!decrypted || decrypted.type !== 'verify' || !decrypted.user) {
throw new Error('Bad challenge'); throw new Error('Bad challenge');
} }
@ -152,7 +156,7 @@ export class LoginController {
} }
remember = decrypted.remember; remember = decrypted.remember;
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: 'An unexpected error occured, please log in again.', text: 'An unexpected error occured, please log in again.',
@ -173,10 +177,10 @@ export class LoginController {
if (!this.totpService.validateTOTP(totp.token, body.totp)) { if (!this.totpService.validateTOTP(totp.token, body.totp)) {
throw new Error('Invalid code!'); throw new Error('Invalid code!');
} }
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
res.redirect(req.originalUrl); res.redirect(req.originalUrl);
return; return;
@ -220,7 +224,7 @@ export class LoginController {
} }
user = token.user; user = token.user;
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: 'Invalid or expired activation link.', text: 'Invalid or expired activation link.',
@ -355,8 +359,8 @@ export class LoginController {
}); });
res.redirect('/login'); res.redirect('/login');
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { error: true, text: e.message }); req.flash('message', { error: true, text: (e as Error).message });
res.redirect(req.originalUrl); res.redirect(req.originalUrl);
} }
} }

View File

@ -8,7 +8,7 @@ import {
Res, Res,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { Scope } from 'src/decorators/scope.decorator'; import { Scope } from 'src/decorators/scope.decorator';
import { CurrentUser } from 'src/decorators/user.decorator'; import { CurrentUser } from 'src/decorators/user.decorator';
@ -36,6 +36,7 @@ export class OAuth2Controller {
return this._service.oauth.controller.authorization(req, res, next); return this._service.oauth.controller.authorization(req, res, next);
} }
@ApiExcludeEndpoint()
@Post('authorize') @Post('authorize')
public authorizePostWrapper( public authorizePostWrapper(
@Req() req: Request, @Req() req: Request,
@ -66,18 +67,18 @@ export class OAuth2Controller {
// User information endpoint // User information endpoint
@ApiBearerAuth() @ApiExcludeEndpoint()
@Get('user') @Get('user')
@UseGuards(OAuth2Guard) @UseGuards(OAuth2Guard)
public async userInfo( public async userInfo(
@CurrentUser() user: User, @CurrentUser() user: User,
@Scope() scope: string, @Scope() scope: string,
): Promise<Record<string, any>> { ): Promise<Record<string, unknown>> {
if (!user) { if (!user) {
throw new NotFoundException('No such user'); throw new NotFoundException('No such user');
} }
const userData: Record<string, any> = { const userData: Record<string, unknown> = {
id: user.id, id: user.id,
uuid: user.uuid, uuid: user.uuid,
username: user.username, username: user.username,

View File

@ -29,7 +29,7 @@ export class RegisterController {
@Get() @Get()
@Render('register') @Render('register')
public registerView(@Req() req: Request): Record<string, any> { public registerView(@Req() req: Request): Record<string, unknown> {
return this.formUtil.populateTemplate(req, { return this.formUtil.populateTemplate(req, {
registrationAuthorized: this.config.get<boolean>('app.registrations'), registrationAuthorized: this.config.get<boolean>('app.registrations'),
}); });
@ -99,8 +99,8 @@ export class RegisterController {
}); });
res.redirect('/login' + (redirectTo ? '?redirectTo=' + redirectTo : '')); res.redirect('/login' + (redirectTo ? '?redirectTo=' + redirectTo : ''));
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { error: true, text: e.message }); req.flash('message', { error: true, text: (e as Error).message });
req.flash('form', { ...body, password_repeat: undefined }); req.flash('form', { ...body, password_repeat: undefined });
res.redirect(req.originalUrl); res.redirect(req.originalUrl);
} }

View File

@ -80,10 +80,10 @@ export class SettingsController {
error: false, error: false,
text: 'Display name has been changed!', text: 'Display name has been changed!',
}); });
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
} }
res.redirect('/account/general'); res.redirect('/account/general');
@ -222,10 +222,10 @@ export class SettingsController {
if (new_password !== password_repeat) { if (new_password !== password_repeat) {
throw new Error('The passwords do not match.'); throw new Error('The passwords do not match.');
} }
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
res.redirect('/account/security'); res.redirect('/account/security');
return; return;
@ -288,10 +288,10 @@ export class SettingsController {
'There is already an existing user with this email address.', 'There is already an existing user with this email address.',
); );
} }
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
res.redirect('/account/security'); res.redirect('/account/security');
return; return;

View File

@ -8,6 +8,12 @@ import { FormUtilityService } from 'src/modules/utility/services/form-utility.se
import { QRCodeService } from 'src/modules/utility/services/qr-code.service'; import { QRCodeService } from 'src/modules/utility/services/qr-code.service';
import { TokenService } from 'src/modules/utility/services/token.service'; import { TokenService } from 'src/modules/utility/services/token.service';
interface ChallengeType {
secret: string;
type: 'totp';
user: string;
}
@Controller('/account/two-factor') @Controller('/account/two-factor')
export class TwoFactorController { export class TwoFactorController {
constructor( constructor(
@ -27,7 +33,9 @@ export class TwoFactorController {
if (!twoFA) { if (!twoFA) {
const challengeString = req.query.challenge as string; const challengeString = req.query.challenge as string;
if (challengeString) { if (challengeString) {
const challenge = await this.token.decryptChallenge(challengeString); const challenge = await this.token.decryptChallenge<ChallengeType>(
challengeString,
);
if (challenge.type === 'totp' && challenge.user === req.user.uuid) { if (challenge.type === 'totp' && challenge.user === req.user.uuid) {
secret = challenge.secret; secret = challenge.secret;
} }
@ -69,7 +77,9 @@ export class TwoFactorController {
throw new Error('Invalid request'); throw new Error('Invalid request');
} }
const challenge = await this.token.decryptChallenge(challengeString); const challenge = await this.token.decryptChallenge<ChallengeType>(
challengeString,
);
secret = challenge.secret; secret = challenge.secret;
if ( if (
@ -84,10 +94,10 @@ export class TwoFactorController {
if (!verify) { if (!verify) {
throw new Error('Invalid code! Try again.'); throw new Error('Invalid code! Try again.');
} }
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
res.redirect('/two-factor'); res.redirect('/two-factor');
return; return;
@ -137,10 +147,10 @@ export class TwoFactorController {
await this.totp.deactivateTOTP(twoFA); await this.totp.deactivateTOTP(twoFA);
await this.audit.auditRequest(req, AuditAction.TOTP_DEACTIVATE); await this.audit.auditRequest(req, AuditAction.TOTP_DEACTIVATE);
} catch (e: any) { } catch (e: unknown) {
req.flash('message', { req.flash('message', {
error: true, error: true,
text: e.message, text: (e as Error).message,
}); });
res.redirect('/account/two-factor/disable'); res.redirect('/account/two-factor/disable');
return; return;

View File

@ -13,8 +13,10 @@ export class FormUtilityService {
* @param flash Array of objects * @param flash Array of objects
* @returns Merged object * @returns Merged object
*/ */
public mergeObjectArray(flash: Record<string, any>[]): Record<string, any> { public mergeObjectArray(
return flash.reduce<Record<string, any>>( flash: Record<string, unknown>[],
): Record<string, unknown> {
return flash.reduce<Record<string, unknown>>(
(obj, item) => ({ ...obj, ...item }), (obj, item) => ({ ...obj, ...item }),
{}, {},
); );
@ -93,11 +95,11 @@ export class FormUtilityService {
*/ */
public populateTemplate( public populateTemplate(
req: Request, req: Request,
additional: Record<string, any> = {}, additional: Record<string, unknown> = {},
): Record<string, any> { ): Record<string, unknown> {
const message = req.flash('message')[0] || {}; const message = req.flash('message')[0] || {};
const form = this.mergeObjectArray( const form = this.mergeObjectArray(
(req.flash('form') as Record<string, any>[]) || [], (req.flash('form') as Record<string, unknown>[]) || [],
); );
return { return {

View File

@ -78,18 +78,14 @@ export class TokenService {
return decrypted.toString(); return decrypted.toString();
} }
public async encryptChallenge( public async encryptChallenge<T>(challenge: T): Promise<string> {
challenge: Record<string, any>,
): Promise<string> {
return this.encrypt( return this.encrypt(
JSON.stringify(challenge), JSON.stringify(challenge),
this.config.get<string>('app.challenge_secret'), this.config.get<string>('app.challenge_secret'),
); );
} }
public async decryptChallenge( public async decryptChallenge<T>(challenge: string): Promise<T> {
challenge: string,
): Promise<Record<string, any>> {
return JSON.parse( return JSON.parse(
this.decrypt(challenge, this.config.get<string>('app.challenge_secret')), this.decrypt(challenge, this.config.get<string>('app.challenge_secret')),
); );