jwt gen & rename fixes
This commit is contained in:
parent
313785b4ac
commit
a165231c47
13 changed files with 101 additions and 41 deletions
|
@ -33,7 +33,10 @@ export default abstract class MemberCommandHandler {
|
||||||
return result.identifiers[0].id;
|
return result.identifiers[0].id;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed creating member", err);
|
throw new InternalException(
|
||||||
|
`Failed creating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +61,10 @@ export default abstract class MemberCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed updating member", err);
|
throw new InternalException(
|
||||||
|
`Failed updating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +89,7 @@ export default abstract class MemberCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed updating member", err);
|
throw new InternalException(`Failed updating member`, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,10 @@ export interface UpdateWebapiCommand {
|
||||||
expiry?: Date;
|
expiry?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateLastUsageWebapiCommand {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DeleteWebapiCommand {
|
export interface DeleteWebapiCommand {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { dataSource } from "../../../data-source";
|
import { dataSource } from "../../../data-source";
|
||||||
import { webapi } from "../../../entity/user/webapi";
|
import { webapi } from "../../../entity/user/webapi";
|
||||||
import InternalException from "../../../exceptions/internalException";
|
import InternalException from "../../../exceptions/internalException";
|
||||||
import { CreateWebapiCommand, DeleteWebapiCommand, UpdateWebapiCommand } from "./webapiCommand";
|
import {
|
||||||
|
CreateWebapiCommand,
|
||||||
|
DeleteWebapiCommand,
|
||||||
|
UpdateLastUsageWebapiCommand,
|
||||||
|
UpdateWebapiCommand,
|
||||||
|
} from "./webapiCommand";
|
||||||
|
|
||||||
export default abstract class WebapiCommandHandler {
|
export default abstract class WebapiCommandHandler {
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +29,10 @@ export default abstract class WebapiCommandHandler {
|
||||||
return result.identifiers[0].token;
|
return result.identifiers[0].token;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed creating api", err);
|
throw new InternalException(
|
||||||
|
`Failed creating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +53,30 @@ export default abstract class WebapiCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed updating api", err);
|
throw new InternalException(
|
||||||
|
`Failed updating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description update api usage
|
||||||
|
* @param {UpdateLastUsageWebapiCommand} updateWebapi
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async updateUsage(updateWebapi: UpdateLastUsageWebapiCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(webapi)
|
||||||
|
.set({
|
||||||
|
lastUsage: new Date(),
|
||||||
|
})
|
||||||
|
.where("id = :id", { id: updateWebapi.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException(`Failed updating api last usage`, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@ import { PermissionString } from "../../../type/permissionTypes";
|
||||||
|
|
||||||
export interface CreateWebapiPermissionCommand {
|
export interface CreateWebapiPermissionCommand {
|
||||||
permission: PermissionString;
|
permission: PermissionString;
|
||||||
apiId: number;
|
webapiId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteWebapiPermissionCommand {
|
export interface DeleteWebapiPermissionCommand {
|
||||||
permission: PermissionString;
|
permission: PermissionString;
|
||||||
apiId: number;
|
webapiId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateWebapiPermissionsCommand {
|
export interface UpdateWebapiPermissionsCommand {
|
||||||
apiId: number;
|
webapiId: number;
|
||||||
permissions: Array<PermissionString>;
|
permissions: Array<PermissionString>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default abstract class WebapiPermissionCommandHandler {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async updatePermissions(updateWebapiPermissions: UpdateWebapiPermissionsCommand): Promise<void> {
|
static async updatePermissions(updateWebapiPermissions: UpdateWebapiPermissionsCommand): Promise<void> {
|
||||||
let currentPermissions = (await WebapiPermissionService.getByApi(updateWebapiPermissions.apiId)).map(
|
let currentPermissions = (await WebapiPermissionService.getByApi(updateWebapiPermissions.webapiId)).map(
|
||||||
(r) => r.permission
|
(r) => r.permission
|
||||||
);
|
);
|
||||||
return await dataSource.manager
|
return await dataSource.manager
|
||||||
|
@ -30,10 +30,10 @@ export default abstract class WebapiPermissionCommandHandler {
|
||||||
updateWebapiPermissions.permissions
|
updateWebapiPermissions.permissions
|
||||||
);
|
);
|
||||||
if (newPermissions.length != 0) {
|
if (newPermissions.length != 0) {
|
||||||
await this.updatePermissionsAdd(manager, updateWebapiPermissions.apiId, newPermissions);
|
await this.updatePermissionsAdd(manager, updateWebapiPermissions.webapiId, newPermissions);
|
||||||
}
|
}
|
||||||
if (removePermissions.length != 0) {
|
if (removePermissions.length != 0) {
|
||||||
await this.updatePermissionsRemove(manager, updateWebapiPermissions.apiId, removePermissions);
|
await this.updatePermissionsRemove(manager, updateWebapiPermissions.webapiId, removePermissions);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
|
@ -54,7 +54,7 @@ export default abstract class WebapiPermissionCommandHandler {
|
||||||
.values(
|
.values(
|
||||||
permissions.map((p) => ({
|
permissions.map((p) => ({
|
||||||
permission: p,
|
permission: p,
|
||||||
apiId: webapiId,
|
webapiId: webapiId,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.orIgnore()
|
.orIgnore()
|
||||||
|
@ -87,7 +87,7 @@ export default abstract class WebapiPermissionCommandHandler {
|
||||||
.into(webapiPermission)
|
.into(webapiPermission)
|
||||||
.values({
|
.values({
|
||||||
permission: createPermission.permission,
|
permission: createPermission.permission,
|
||||||
webapiId: createPermission.apiId,
|
webapiId: createPermission.webapiId,
|
||||||
})
|
})
|
||||||
.execute()
|
.execute()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
@ -108,7 +108,7 @@ export default abstract class WebapiPermissionCommandHandler {
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.delete()
|
.delete()
|
||||||
.from(webapiPermission)
|
.from(webapiPermission)
|
||||||
.where("webapiId = :id", { id: deletePermission.apiId })
|
.where("webapiId = :id", { id: deletePermission.webapiId })
|
||||||
.andWhere("permission = :permission", { permission: deletePermission.permission })
|
.andWhere("permission = :permission", { permission: deletePermission.permission })
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
|
|
|
@ -74,14 +74,16 @@ export async function getWebapiPermissions(req: Request, res: Response): Promise
|
||||||
*/
|
*/
|
||||||
export async function createWebapi(req: Request, res: Response): Promise<any> {
|
export async function createWebapi(req: Request, res: Response): Promise<any> {
|
||||||
let title = req.body.title;
|
let title = req.body.title;
|
||||||
let expiry = req.body.expiry;
|
let expiry = req.body.expiry || null;
|
||||||
|
|
||||||
let token = await JWTHelper.create({
|
let token = await JWTHelper.create(
|
||||||
iss: CLUB_NAME,
|
{
|
||||||
sub: "api_token_retrieve",
|
iss: CLUB_NAME,
|
||||||
iat: new Date().toISOString(),
|
sub: "api_token_retrieve",
|
||||||
aud: StringHelper.random(32),
|
aud: StringHelper.random(32),
|
||||||
});
|
},
|
||||||
|
{ useExpiration: false }
|
||||||
|
);
|
||||||
|
|
||||||
let createApi: CreateWebapiCommand = {
|
let createApi: CreateWebapiCommand = {
|
||||||
token: token,
|
token: token,
|
||||||
|
@ -102,7 +104,7 @@ export async function createWebapi(req: Request, res: Response): Promise<any> {
|
||||||
export async function updateWebapi(req: Request, res: Response): Promise<any> {
|
export async function updateWebapi(req: Request, res: Response): Promise<any> {
|
||||||
const id = parseInt(req.params.id);
|
const id = parseInt(req.params.id);
|
||||||
let title = req.body.title;
|
let title = req.body.title;
|
||||||
let expiry = req.body.expiry;
|
let expiry = req.body.expiry || null;
|
||||||
|
|
||||||
let updateApi: UpdateWebapiCommand = {
|
let updateApi: UpdateWebapiCommand = {
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -127,7 +129,7 @@ export async function updateWebapiPermissions(req: Request, res: Response): Prom
|
||||||
let permissionStrings = PermissionHelper.convertToStringArray(permissions);
|
let permissionStrings = PermissionHelper.convertToStringArray(permissions);
|
||||||
|
|
||||||
let updateApiPermissions: UpdateWebapiPermissionsCommand = {
|
let updateApiPermissions: UpdateWebapiPermissionsCommand = {
|
||||||
apiId: id,
|
webapiId: id,
|
||||||
permissions: permissionStrings,
|
permissions: permissionStrings,
|
||||||
};
|
};
|
||||||
await WebapiPermissionCommandHandler.updatePermissions(updateApiPermissions);
|
await WebapiPermissionCommandHandler.updatePermissions(updateApiPermissions);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UnauthorizedRequestException from "../exceptions/unauthorizedRequestExcep
|
||||||
import RefreshService from "../service/refreshService";
|
import RefreshService from "../service/refreshService";
|
||||||
import WebapiService from "../service/user/webapiService";
|
import WebapiService from "../service/user/webapiService";
|
||||||
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||||
|
import WebapiCommandHandler from "../command/user/webapi/webapiCommandHandler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Check authentication status by token
|
* @description Check authentication status by token
|
||||||
|
@ -20,13 +21,15 @@ import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||||
export async function getWebApiAccess(req: Request, res: Response): Promise<any> {
|
export async function getWebApiAccess(req: Request, res: Response): Promise<any> {
|
||||||
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||||
|
|
||||||
let { expiry } = await WebapiService.getByToken(bearer);
|
let { id, expiry } = await WebapiService.getByToken(bearer);
|
||||||
|
|
||||||
if (new Date() > new Date(expiry)) {
|
if (expiry != null && new Date() > new Date(expiry)) {
|
||||||
throw new ForbiddenRequestException("api token expired");
|
throw new ForbiddenRequestException("api token expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
let accessToken = await JWTHelper.buildWebapiToken(bearer);
|
await WebapiCommandHandler.updateUsage({ id });
|
||||||
|
|
||||||
|
let accessToken = await JWTHelper.buildWebapiToken(bearer, expiry);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
accessToken,
|
accessToken,
|
||||||
|
|
|
@ -6,10 +6,10 @@ export class webapi {
|
||||||
@PrimaryColumn({ generated: "increment", type: "int" })
|
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, unique: true, select: false })
|
@Column({ type: "text", unique: true, select: false })
|
||||||
token: string;
|
token: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255 })
|
@Column({ type: "varchar", length: 255, unique: true })
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
|
@ -18,7 +18,7 @@ export class webapi {
|
||||||
@Column({ type: "datetime", nullable: true })
|
@Column({ type: "datetime", nullable: true })
|
||||||
lastUsage?: Date;
|
lastUsage?: Date;
|
||||||
|
|
||||||
@Column({ type: "datetime", nullable: true })
|
@Column({ type: "date", nullable: true })
|
||||||
expiry?: Date;
|
expiry?: Date;
|
||||||
|
|
||||||
@OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi)
|
@OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import UserService from "../service/user/userService";
|
||||||
import PermissionHelper from "./permissionHelper";
|
import PermissionHelper from "./permissionHelper";
|
||||||
import WebapiService from "../service/user/webapiService";
|
import WebapiService from "../service/user/webapiService";
|
||||||
import WebapiPermissionService from "../service/user/webapiPermissionService";
|
import WebapiPermissionService from "../service/user/webapiPermissionService";
|
||||||
|
import ms from "ms";
|
||||||
|
|
||||||
export abstract class JWTHelper {
|
export abstract class JWTHelper {
|
||||||
static validate(token: string): Promise<string | jwt.JwtPayload> {
|
static validate(token: string): Promise<string | jwt.JwtPayload> {
|
||||||
|
@ -19,13 +20,16 @@ export abstract class JWTHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(data: JWTData): Promise<string> {
|
static create(
|
||||||
|
data: JWTData,
|
||||||
|
{ expOverwrite, useExpiration }: { expOverwrite?: number; useExpiration?: boolean } = { useExpiration: true }
|
||||||
|
): Promise<string> {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
jwt.sign(
|
jwt.sign(
|
||||||
data,
|
data,
|
||||||
JWT_SECRET,
|
JWT_SECRET,
|
||||||
{
|
{
|
||||||
expiresIn: JWT_EXPIRATION,
|
...(useExpiration ?? true ? { expiresIn: expOverwrite ?? JWT_EXPIRATION } : {}),
|
||||||
},
|
},
|
||||||
(err, token) => {
|
(err, token) => {
|
||||||
if (err) reject(err.message);
|
if (err) reject(err.message);
|
||||||
|
@ -75,7 +79,7 @@ export abstract class JWTHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildWebapiToken(token: string): Promise<string> {
|
static async buildWebapiToken(token: string, expiration?: Date): Promise<string> {
|
||||||
let { id, title } = await WebapiService.getByToken(token);
|
let { id, title } = await WebapiService.getByToken(token);
|
||||||
let webapiPermissions = await WebapiPermissionService.getByApi(id);
|
let webapiPermissions = await WebapiPermissionService.getByApi(id);
|
||||||
let webapiPermissionStrings = webapiPermissions.map((e) => e.permission);
|
let webapiPermissionStrings = webapiPermissions.map((e) => e.permission);
|
||||||
|
@ -92,7 +96,12 @@ export abstract class JWTHelper {
|
||||||
sub: "webapi_access_token",
|
sub: "webapi_access_token",
|
||||||
};
|
};
|
||||||
|
|
||||||
return await JWTHelper.create(jwtData)
|
let overwriteExpiration =
|
||||||
|
ms(JWT_EXPIRATION) < new Date().getTime() - new Date(expiration).getTime()
|
||||||
|
? null
|
||||||
|
: Date.now() - new Date(expiration).getTime();
|
||||||
|
|
||||||
|
return await JWTHelper.create(jwtData, { expOverwrite: overwriteExpiration, useExpiration: true })
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,11 +12,11 @@ export class AddWebapiTokens1737453096674 implements MigrationInterface {
|
||||||
name: "webapi",
|
name: "webapi",
|
||||||
columns: [
|
columns: [
|
||||||
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
|
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
|
||||||
{ name: "token", type: "varchar", length: "255", isUnique: true, isNullable: false },
|
{ name: "token", type: "text", isUnique: true, isNullable: false },
|
||||||
{ name: "title", type: "varchar", length: "255", isNullable: false },
|
{ name: "title", type: "varchar", isUnique: true, length: "255", isNullable: false },
|
||||||
{ name: "createdAt", type: "datetime", default: "CURRENT_TIMESTAMP(6)", isNullable: false },
|
{ name: "createdAt", type: "datetime", default: "CURRENT_TIMESTAMP(6)", isNullable: false },
|
||||||
{ name: "lastUsage", type: "datetime", isNullable: true, default: null },
|
{ name: "lastUsage", type: "datetime", isNullable: true, default: null },
|
||||||
{ name: "expiry", type: "datetime", isNullable: true, default: null },
|
{ name: "expiry", type: "date", isNullable: true, default: null },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
getAllWebapis,
|
getAllWebapis,
|
||||||
getWebapiById,
|
getWebapiById,
|
||||||
getWebapiPermissions,
|
getWebapiPermissions,
|
||||||
|
getWebapiTokenById,
|
||||||
updateWebapi,
|
updateWebapi,
|
||||||
updateWebapiPermissions,
|
updateWebapiPermissions,
|
||||||
} from "../../../controller/admin/user/webapiController";
|
} from "../../../controller/admin/user/webapiController";
|
||||||
|
@ -20,6 +21,10 @@ router.get("/:id", async (req: Request, res: Response) => {
|
||||||
await getWebapiById(req, res);
|
await getWebapiById(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/:id/token", async (req: Request, res: Response) => {
|
||||||
|
await getWebapiTokenById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/:id/permissions", async (req: Request, res: Response) => {
|
router.get("/:id/permissions", async (req: Request, res: Response) => {
|
||||||
await getWebapiPermissions(req, res);
|
await getWebapiPermissions(req, res);
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,14 +11,14 @@ export default abstract class WebapiPermissionService {
|
||||||
static async getByApi(webapiId: number): Promise<Array<webapiPermission>> {
|
static async getByApi(webapiId: number): Promise<Array<webapiPermission>> {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(webapiPermission)
|
.getRepository(webapiPermission)
|
||||||
.createQueryBuilder("api_permission")
|
.createQueryBuilder("webapi_permission")
|
||||||
.where("api_permission.apiId = :apiId", { apiId: webapiId })
|
.where("webapi_permission.webapiId = :webapiId", { webapiId: webapiId })
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("api permissions not found by api", err);
|
throw new InternalException("webapi permissions not found by api", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default abstract class WebapiService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(webapi)
|
.getRepository(webapi)
|
||||||
.createQueryBuilder("webapi")
|
.createQueryBuilder("webapi")
|
||||||
.select("token")
|
.select("webapi.token")
|
||||||
.where("webapi.id = :id", { id: id })
|
.where("webapi.id = :id", { id: id })
|
||||||
.getOneOrFail()
|
.getOneOrFail()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
Loading…
Reference in a new issue