From 8485b954288b7a65c0cb1640294df05ab2d29ba4 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Thu, 3 Aug 2023 23:31:25 +0200 Subject: [PATCH] Created the license.ts controller --- src/controller/license.ts | 145 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/controller/license.ts diff --git a/src/controller/license.ts b/src/controller/license.ts new file mode 100644 index 0000000..1b410ec --- /dev/null +++ b/src/controller/license.ts @@ -0,0 +1,145 @@ +import { checkProjectAccess } from "@controller/projects"; +import { IKeyRole } from "@models/AccessKey"; +import { decryptField, encryptClearField } from "@utils/decryption"; +import { ILicense, License } from "@models/License"; +import { planLimits } from "../limits/plans"; +import { Permission } from "@models/Permission"; +import { IProject } from "@models/Project"; +import { Group } from "@models/Group"; +import { MetaData } from "@models/MetaData"; +import { convertIdsToPermissions } from "@controller/permission"; +import { convertIdsToGroups } from "@controller/group"; +import { convertIdsToMetaData, isValidMetaType } from "@controller/meta"; + +export const generateCharacter = () => { + return String.fromCharCode(Math.random() < 0.5 ? Math.floor(Math.random() * 26) + 65 : Math.floor(Math.random() * 26) + 97); +} + +export const generateSpecialCharacter = () => { + const specialChars = "!@#$%^&*()_+-=[]{};':\",./<>?"; + return specialChars[Math.floor(Math.random() * specialChars.length)]; +} + +export const generateAlphaNumeric = () => { + const alphanumericChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + return alphanumericChars[Math.floor(Math.random() * alphanumericChars.length)]; +} + +export const generateRandom = () => { + const allChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{};':\",./<>?"; + return allChars[Math.floor(Math.random() * allChars.length)]; +} + +export const replaceLicenseDefaults = (defaultKey: string) => { + return defaultKey.replace(/N/g, () => String(Math.floor(Math.random() * 10))) + .replace(/C/g, () => generateCharacter()) + .replace(/L/g, () => String.fromCharCode(Math.floor(Math.random() * 26) + 97)) + .replace(/U/g, () => String.fromCharCode(Math.floor(Math.random() * 26) + 65)) + .replace(/S/g, () => generateSpecialCharacter()) + .replace(/A/g, () => generateAlphaNumeric()) + .replace(/R/g, () => generateRandom()); +} + +export const checkLicenseConfiguration = async (access: IProject, config: ILicense) => { + if (config.groups) { + let groupsEncrypted = config.groups.map(group => encryptClearField(group)); + config.groups = (await Group.find({ projectId: encryptClearField(String(access._id)), + name: { $in: groupsEncrypted }})).map(group => group.id); + } + if (config.permissions) { + let permissionsEncrypted = config.permissions.map(permission => encryptClearField(permission)); + config.permissions = (await Permission.find({ projectId: encryptClearField(String(access._id)), + permission: { $in: permissionsEncrypted }})).map(permission => permission.id); + } + if (config.meta) { + let metaEncrypted = Object.keys(config.meta).map(meta => encryptClearField(meta)); + let meta = (await MetaData.find({ projectId: encryptClearField(String(access._id)), + name: { $in: metaEncrypted }})); + + let metaObj: { [key: string]: string } = {}; + meta.forEach(meta => { + if (isValidMetaType(meta.type, config.meta[meta.name])) + metaObj[meta.id] = config.meta[meta.name]; + }); + config.meta = metaObj; + } +} + +export const mapLicense = async (projectId: string, license: any, publicAccess: boolean) => { + return { + key: license.key, groups: await convertIdsToGroups(projectId, license.groups), permissions: await convertIdsToPermissions(projectId, license.permissions), + meta: await convertIdsToMetaData(projectId, publicAccess, license.meta), maxUses: license.maxUses, currentUses: license.currentUses, + expirationDate: license.expirationDate + } +} + +export const listLicensesPaginated = async (userId: string, projectId: string, page: number, limit: number) => { + const access = await checkProjectAccess(IKeyRole.VIEW)(userId, projectId); + if ("code" in access) return access; + + const licenses = await License.find({ projectId: encryptClearField(String(access._id)) }).skip(page * limit).limit(limit); + + return { + total: await License.countDocuments({ projectId: encryptClearField(String(access._id)) }), + licenses: await Promise.all(licenses.map(license => mapLicense(projectId, license, false))) + } +} + +export const getLicense = async (userId: string, projectId: string, licenseKey: string) => { + const access = await checkProjectAccess(IKeyRole.VIEW)(userId, projectId); + if ("code" in access) return access; + + const license = await License.findOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(licenseKey) }); + if (license === null) return { code: 4009, message: "The provided license key does not exist" }; + + return mapLicense(projectId, license, false); +} + +export const createLicense = async (userId: string, projectId: string, config: ILicense) => { + const access = await checkProjectAccess(IKeyRole.MANAGE)(userId, projectId); + if ("code" in access) return access; + + const count = await License.countDocuments({ projectId: encryptClearField(String(access._id)) }); + if (count >= planLimits[access.plan].LICENSES) return { code: 95, message: "You have exceeded the license limit" }; + + if (!config.key) config.key = replaceLicenseDefaults(access.defaults.licenseKey); + if (!config.maxUses) config.maxUses = access.defaults.maxUses; + if (!config.groups) config.groups = access.defaults.groups; + if (!config.permissions) config.permissions = access.defaults.permissions; + if (!config.expirationDate) config.expirationDate = access.defaults.expirationDate; + + const license = await License.findOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(config.key) }); + if (license !== null) return { code: 4008, message: "The provided license key is already in use" }; + + await checkLicenseConfiguration(access, config); + + const created = (await License.create({ ...config, projectId })); + + return { key: decryptField(created.key)}; +} + +export const updateLicense = async (userId: string, projectId: string, licenseKey: string, config: ILicense) => { + const access = await checkProjectAccess(IKeyRole.MANAGE)(userId, projectId); + if ("code" in access) return access; + + const license = await License.findOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(licenseKey) }); + if (license === null) return { code: 4009, message: "The provided license key does not exist" }; + + await checkLicenseConfiguration(access, config); + + await License.updateOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(licenseKey) }, config); + + return { }; +} + +export const deleteLicense = async (userId: string, projectId: string, licenseKey: string) => { + const access = await checkProjectAccess(IKeyRole.MANAGE)(userId, projectId); + if ("code" in access) return access; + + const license = await License.findOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(licenseKey) }); + if (license === null) return { code: 4009, message: "The provided license key does not exist" }; + + await License.deleteOne({ projectId: encryptClearField(String(access._id)), key: encryptClearField(licenseKey) }); + + return { }; +} \ No newline at end of file