Updating dependencies and switching to ESModules where possible
Some checks failed
CI / build (push) Has been cancelled

This commit is contained in:
Fabian Stamm
2025-09-15 22:04:57 +02:00
parent 8135190cd8
commit c6158fe2e2
66 changed files with 4540 additions and 3752 deletions

View File

@ -1,123 +1,122 @@
import { NextFunction, Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import Client from "../../models/client";
import { validateJWT } from "../../keys";
import User from "../../models/user";
import Mail from "../../models/mail";
import { OAuthJWT } from "../../helper/jwt";
import Logging from "@hibas123/nodelogging";
export function GetClientAuthMiddleware(
checksecret = true,
internal = false,
checksecret_if_available = false
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
let client_id = req.query.client_id || req.body.client_id;
let client_secret = req.query.client_secret || req.body.client_secret;
if (!client_id && !client_secret && req.headers.authorization) {
let header = req.headers.authorization;
let [type, val] = header.split(" ");
if (val) {
let str = Buffer.from(val, "base64").toString("utf-8");
let [id, secret] = str.split(":");
client_id = id;
client_secret = secret;
}
}
if (!client_id || (!client_secret && checksecret)) {
throw new RequestError(
"No client credentials",
HttpStatusCode.BAD_REQUEST
);
}
let w = { client_id: client_id, client_secret: client_secret };
if (!checksecret && !(checksecret_if_available && client_secret))
delete w.client_secret;
let client = await Client.findOne(w);
if (!client) {
throw new RequestError(
"Invalid client_id" + (checksecret ? "or client_secret" : ""),
HttpStatusCode.BAD_REQUEST
);
}
if (internal && !client.internal) {
throw new RequestError(
req.__("Client has no permission for access"),
HttpStatusCode.FORBIDDEN
);
}
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
export const ClientAuthMiddleware = GetClientAuthMiddleware();
export function GetClientApiAuthMiddleware(permissions?: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const invalid_err = new RequestError(
req.__("Unauthorized"),
HttpStatusCode.UNAUTHORIZED
);
let token =
(req.query.access_token as string) ||
(req.headers.authorization as string);
if (!token) {
Logging.debug("No token found. Searched in query (access_token) and header (authorization)");
throw invalid_err;
}
if (token.toLowerCase().startsWith("bearer "))
token = token.substring(7);
let data: OAuthJWT;
try {
data = await validateJWT(token);
} catch (err) {
Logging.debug("Invalid JWT", err.message);
throw invalid_err;
}
let user = await User.findOne({ uid: data.user });
if (!user) {
Logging.debug("User not found");
throw invalid_err;
}
let client = await Client.findOne({ client_id: data.application });
if (!client) {
Logging.debug("Client not found");
throw invalid_err;
}
if (
permissions &&
(!data.permissions ||
!permissions.every((e) => data.permissions.indexOf(e) >= 0))
) {
Logging.debug("Invalid permissions");
throw invalid_err;
}
req.user = user;
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
import { NextFunction, Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import Client from "../../models/client.js";
import { validateJWT } from "../../keys.js";
import User from "../../models/user.js";
import { OAuthJWT } from "../../helper/jwt.js";
import Logging from "@hibas123/nodelogging";
export function GetClientAuthMiddleware(
checksecret = true,
internal = false,
checksecret_if_available = false
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
let client_id = req.query.client_id || req.body.client_id;
let client_secret = req.query.client_secret || req.body.client_secret;
if (!client_id && !client_secret && req.headers.authorization) {
let header = req.headers.authorization;
let [type, val] = header.split(" ");
if (val) {
let str = Buffer.from(val, "base64").toString("utf-8");
let [id, secret] = str.split(":");
client_id = id;
client_secret = secret;
}
}
if (!client_id || (!client_secret && checksecret)) {
throw new RequestError(
"No client credentials",
HttpStatusCode.BAD_REQUEST
);
}
let w = { client_id: client_id, client_secret: client_secret };
if (!checksecret && !(checksecret_if_available && client_secret))
delete w.client_secret;
let client = await Client.findOne(w);
if (!client) {
throw new RequestError(
"Invalid client_id" + (checksecret ? "or client_secret" : ""),
HttpStatusCode.BAD_REQUEST
);
}
if (internal && !client.internal) {
throw new RequestError(
req.__("Client has no permission for access"),
HttpStatusCode.FORBIDDEN
);
}
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
export const ClientAuthMiddleware = GetClientAuthMiddleware();
export function GetClientApiAuthMiddleware(permissions?: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const invalid_err = new RequestError(
req.__("Unauthorized"),
HttpStatusCode.UNAUTHORIZED
);
let token =
(req.query.access_token as string) ||
(req.headers.authorization as string);
if (!token) {
Logging.debug("No token found. Searched in query (access_token) and header (authorization)");
throw invalid_err;
}
if (token.toLowerCase().startsWith("bearer "))
token = token.substring(7);
let data: OAuthJWT;
try {
data = await validateJWT(token);
} catch (err) {
Logging.debug("Invalid JWT", err.message);
throw invalid_err;
}
let user = await User.findOne({ uid: data.user });
if (!user) {
Logging.debug("User not found");
throw invalid_err;
}
let client = await Client.findOne({ client_id: data.application });
if (!client) {
Logging.debug("Client not found");
throw invalid_err;
}
if (
permissions &&
(!data.permissions ||
!permissions.every((e) => data.permissions.indexOf(e) >= 0))
) {
Logging.debug("Invalid permissions");
throw invalid_err;
}
req.user = user;
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}

View File

@ -1,28 +1,28 @@
import { Request, Response, NextFunction, RequestHandler } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware";
type RH = (req: Request, res: Response, next?: NextFunction) => any;
function call(handler: RH, req: Request, res: Response) {
return new Promise<void>((yes, no) => {
let p = handler(req, res, (err) => {
if (err) no(err);
else yes();
});
if (p && p.catch) p.catch((err) => no(err));
});
}
const Stacker = (...handler: RH[]) => {
return promiseMiddleware(
async (req: Request, res: Response, next: NextFunction) => {
let hc = handler.concat();
while (hc.length > 0) {
let h = hc.shift();
await call(h, req, res);
}
next();
}
);
};
export default Stacker;
import { Request, Response, NextFunction, RequestHandler } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
type RH = (req: Request, res: Response, next?: NextFunction) => any;
function call(handler: RH, req: Request, res: Response) {
return new Promise<void>((yes, no) => {
let p = handler(req, res, (err) => {
if (err) no(err);
else yes();
});
if (p && p.catch) p.catch((err) => no(err));
});
}
const Stacker = (...handler: RH[]) => {
return promiseMiddleware(
async (req: Request, res: Response, next: NextFunction) => {
let hc = handler.concat();
while (hc.length > 0) {
let h = hc.shift();
await call(h, req, res);
}
next();
}
);
};
export default Stacker;

View File

@ -1,8 +1,8 @@
import { NextFunction, Request, Response } from "express";
import Logging from "@hibas123/nodelogging";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import promiseMiddleware from "../../helper/promiseMiddleware";
import { requireLoginState } from "../../helper/login";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import { requireLoginState } from "../../helper/login.js";
class Invalid extends Error { }

View File

@ -1,142 +1,141 @@
import { Request, Response, NextFunction } from "express";
import Logging from "@hibas123/nodelogging";
import {
isString,
isDate,
} from "util";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
export enum Types {
STRING,
NUMBER,
BOOLEAN,
EMAIL,
OBJECT,
DATE,
ARRAY,
ENUM,
}
function isEmail(value: any): boolean {
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
}
export interface CheckObject {
type: Types;
query?: boolean;
optional?: boolean;
/**
* Only when Type.ENUM
*
* values to check before
*/
values?: string[];
/**
* Only when Type.STRING
*/
notempty?: boolean; // Only STRING
}
export interface Checks {
[index: string]: CheckObject; // | Types
}
// req: Request, res: Response, next: NextFunction
export default function (fields: Checks, noadditional = false) {
return (req: Request, res: Response, next: NextFunction) => {
let errors: { message: string; field: string }[] = [];
function check(data: any, field_name: string, field: CheckObject) {
if (data !== undefined && data !== null) {
switch (field.type) {
case Types.STRING:
if (isString(data)) {
if (!field.notempty) return;
if (data !== "") return;
}
break;
case Types.NUMBER:
if (typeof data == "number") return;
break;
case Types.EMAIL:
if (isEmail(data)) return;
break;
case Types.BOOLEAN:
if (typeof data == "boolean") return;
break;
case Types.OBJECT:
if (typeof data == "object") return;
break;
case Types.ARRAY:
if (Array.isArray(data)) return;
break;
case Types.DATE:
if (isDate(data)) return;
break;
case Types.ENUM:
if (typeof data == "string") {
if (field.values.indexOf(data) >= 0) return;
}
break;
default:
Logging.error(
`Invalid type to check: ${field.type} ${Types[field.type]}`
);
}
errors.push({
message: res.__(
"Field {{field}} has wrong type. It should be from type {{type}}",
{ field: field_name, type: Types[field.type].toLowerCase() }
),
field: field_name,
});
} else {
if (!field.optional)
errors.push({
message: res.__("Field {{field}} is not defined", {
field: field_name,
}),
field: field_name,
});
}
}
for (let field_name in fields) {
let field = fields[field_name];
let data = fields[field_name].query
? req.query[field_name]
: req.body[field_name];
check(data, field_name, field);
}
if (noadditional) {
//Checks if the data given has additional parameters
let should = Object.keys(fields);
should = should.filter((e) => !fields[e].query); //Query parameters should not exist on body
let has = Object.keys(req.body);
has.every((e) => {
if (should.indexOf(e) >= 0) {
return true;
} else {
errors.push({
message: res.__("Field {{field}} should not be there", {
field: e,
}),
field: e,
});
return false;
}
});
}
if (errors.length > 0) {
let err = new RequestError(errors, HttpStatusCode.BAD_REQUEST, true);
next(err);
} else next();
};
}
import { Request, Response, NextFunction } from "express";
import Logging from "@hibas123/nodelogging";
import {
types
} from "util";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
export enum Types {
STRING,
NUMBER,
BOOLEAN,
EMAIL,
OBJECT,
DATE,
ARRAY,
ENUM,
}
function isEmail(value: any): boolean {
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
}
export interface CheckObject {
type: Types;
query?: boolean;
optional?: boolean;
/**
* Only when Type.ENUM
*
* values to check before
*/
values?: string[];
/**
* Only when Type.STRING
*/
notempty?: boolean; // Only STRING
}
export interface Checks {
[index: string]: CheckObject; // | Types
}
// req: Request, res: Response, next: NextFunction
export default function (fields: Checks, noadditional = false) {
return (req: Request, res: Response, next: NextFunction) => {
let errors: { message: string; field: string }[] = [];
function check(data: any, field_name: string, field: CheckObject) {
if (data !== undefined && data !== null) {
switch (field.type) {
case Types.STRING:
if (typeof data === "string") {
if (!field.notempty) return;
if (data !== "") return;
}
break;
case Types.NUMBER:
if (typeof data == "number") return;
break;
case Types.EMAIL:
if (isEmail(data)) return;
break;
case Types.BOOLEAN:
if (typeof data == "boolean") return;
break;
case Types.OBJECT:
if (typeof data == "object") return;
break;
case Types.ARRAY:
if (Array.isArray(data)) return;
break;
case Types.DATE:
if (types.isDate(data)) return;
break;
case Types.ENUM:
if (typeof data == "string") {
if (field.values.indexOf(data) >= 0) return;
}
break;
default:
Logging.error(
`Invalid type to check: ${field.type} ${Types[field.type]}`
);
}
errors.push({
message: res.__(
"Field {{field}} has wrong type. It should be from type {{type}}",
{ field: field_name, type: Types[field.type].toLowerCase() }
),
field: field_name,
});
} else {
if (!field.optional)
errors.push({
message: res.__("Field {{field}} is not defined", {
field: field_name,
}),
field: field_name,
});
}
}
for (let field_name in fields) {
let field = fields[field_name];
let data = fields[field_name].query
? req.query[field_name]
: req.body[field_name];
check(data, field_name, field);
}
if (noadditional) {
//Checks if the data given has additional parameters
let should = Object.keys(fields);
should = should.filter((e) => !fields[e].query); //Query parameters should not exist on body
let has = Object.keys(req.body);
has.every((e) => {
if (should.indexOf(e) >= 0) {
return true;
} else {
errors.push({
message: res.__("Field {{field}} should not be there", {
field: e,
}),
field: e,
});
return false;
}
});
}
if (errors.length > 0) {
let err = new RequestError(errors, HttpStatusCode.BAD_REQUEST, true);
next(err);
} else next();
};
}