UNPKG

@qelos/auth

Version:

Express Passport authentication service

490 lines (489 loc) 21.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.onlyWorkspacePrivileged = exports.getWorkspaceByParams = exports.updateWorkspaceMember = exports.deleteWorkspaceMember = exports.addWorkspaceMember = exports.getWorkspaceMembers = exports.activateWorkspace = exports.deleteWorkspace = exports.updateWorkspace = exports.createWorkspace = exports.setWorkspaceEncryptedData = exports.getWorkspaceEncryptedData = exports.getEveryWorkspaces = exports.getWorkspaces = exports.getWorkspace = void 0; const mongoose_1 = require("mongoose"); const workspace_1 = __importDefault(require("../models/workspace")); const api_kit_1 = require("@qelos/api-kit"); const logger_1 = __importDefault(require("../services/logger")); const tokens_1 = require("../services/tokens"); const users_1 = require("../services/users"); const user_1 = __importDefault(require("../models/user")); const config_1 = require("../../config"); const req_host_1 = require("../services/req-host"); const workspace_configuration_1 = require("../services/workspace-configuration"); const encrypted_data_1 = require("../services/encrypted-data"); const ObjectId = mongoose_1.Types.ObjectId; function getWorkspaceIdIfExists(_id, tenant) { return workspace_1.default.findOne({ _id, tenant }).select('_id').lean().exec(); } function getWorkspace(req, res) { return __awaiter(this, void 0, void 0, function* () { if (!req.isWorkspacePrivileged) { res.status(200).json(req.workspace).end(); return; } try { const { members, invites } = yield workspace_1.default.findOne({ _id: req.workspace._id }, 'members invites').lean().exec(); res.status(200).json(Object.assign(Object.assign({}, req.workspace.toJSON()), { members, invites })).end(); } catch (err) { res.status(500).json({ message: 'Failed to load workspace' }).end(); } }); } exports.getWorkspace = getWorkspace; function getWorkspaces(req, res) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const userId = req.userPayload.sub; try { const workspaces = yield workspace_1.default.find({ tenant, 'members.user': new ObjectId(userId), }) .select('name logo tenant members.$').lean().exec(); res.status(200).json(workspaces.map(ws => { return Object.assign(Object.assign({}, ws), { isPrivilegedUser: ws.members[0].roles.includes('admin') }); })).end(); } catch (err) { res.status(500).json({ message: 'Failed to load workspaces' }).end(); } }); } exports.getWorkspaces = getWorkspaces; function getEveryWorkspaces(req, res) { var _a; return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const dbQuery = { tenant, }; if (req.query._id) { if (req.query._id instanceof Array) { dbQuery._id = { $in: req.query._id }; } else { dbQuery._id = { $in: req.query._id.toString().split(',') }; } } if (req.query.q) { const reg = new RegExp(req.query.q.toString(), 'i'); dbQuery.$or = [ { name: reg }, { 'invites.email': reg }, { 'invites.name': reg }, ]; } try { const workspaces = yield workspace_1.default.find(dbQuery) .select(((_a = req.query.select) === null || _a === void 0 ? void 0 : _a.toString().trim().replace(/,/, ' ')) || 'name logo tenant labels').lean().exec(); res.status(200).json(workspaces).end(); } catch (err) { res.status(500).json({ message: 'Failed to load workspaces' }).end(); } }); } exports.getEveryWorkspaces = getEveryWorkspaces; function getWorkspaceEncryptedData(req, res) { return __awaiter(this, void 0, void 0, function* () { const tenant = req.headers.tenant; if (!tenant) { return res.status(401).end(); } try { const workspace = yield getWorkspaceIdIfExists(req.params.workspaceId, tenant); if (!workspace) { throw new Error('workspace not found'); } const encryptedId = req.headers['x-encrypted-id']; const id = workspace._id + (encryptedId ? ('-' + encryptedId) : ''); const { value } = yield (0, encrypted_data_1.getEncryptedData)(tenant, id, 'workspace'); res.status(200).set('Content-Type', 'application/json').end(value); } catch (e) { res.status(200).json(null).end(); } }); } exports.getWorkspaceEncryptedData = getWorkspaceEncryptedData; function setWorkspaceEncryptedData(req, res) { return __awaiter(this, void 0, void 0, function* () { const tenant = req.headers.tenant; if (!tenant) { return res.status(401).end(); } try { const workspace = yield getWorkspaceIdIfExists(req.params.workspaceId, tenant); if (!workspace) { throw new Error('workspace not found'); } const encryptedId = req.headers['x-encrypted-id']; const id = workspace._id + (encryptedId ? ('-' + encryptedId) : ''); yield (0, encrypted_data_1.setEncryptedData)(tenant, id, JSON.stringify(req.body), 'workspace'); res.status(200).set('Content-Type', 'application/json').end('{}'); } catch (e) { res.status(400).json({ message: 'failed to set encrypted data for user' }).end(); } }); } exports.setWorkspaceEncryptedData = setWorkspaceEncryptedData; function createWorkspace(req, res) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const userId = req.userPayload.sub; const { name, logo, invites = [], labels = [] } = req.body; const wsConfig = yield (0, workspace_configuration_1.getWorkspaceConfiguration)(tenant); if (((_a = wsConfig.creationPrivilegedRoles) === null || _a === void 0 ? void 0 : _a.length) && !wsConfig.creationPrivilegedRoles.some(role => role === '*' || req.userPayload.roles.includes(role))) { res.status(403).json({ message: 'you are not permitted to create a workspace' }).end(); return; } if (!(labels instanceof Array)) { res.status(400).json({ message: 'please provide all mandatory properties (missing "labels").' }).end(); return; } if (!wsConfig.allowNonLabeledWorkspaces) { if (labels.length === 0) { res.status(400).json({ message: 'please provide labels with valid values.' }).end(); return; } const selectedLabelsDefinition = wsConfig.labels.find(definition => { var _a; return ((_a = definition.value) === null || _a === void 0 ? void 0 : _a.length) === labels.length && definition.value.map(label => labels.includes(label)).length === labels.length; }); if (!selectedLabelsDefinition) { res.status(400).json({ message: 'provided labels does not match available options.' }).end(); return; } } try { const workspace = new workspace_1.default({ tenant, name, logo, invites, labels }); workspace.members = [{ user: userId, roles: ['admin', 'user'] }]; if (req.userPayload.isPrivileged) { if ((_b = req.body.members) === null || _b === void 0 ? void 0 : _b.length) { workspace.members = req.body.members; } if (req.body.labels instanceof Array) { workspace.labels = req.body.labels; } } yield workspace.save(); res.status(200).json(workspace).end(); (0, api_kit_1.emitPlatformEvent)({ tenant: tenant, user: userId, source: 'auth', kind: 'workspaces', eventName: 'workspace-created', description: 'workspace created by user endpoint', metadata: workspace, }); (0, api_kit_1.emitPlatformEvent)({ tenant: tenant, user: userId, source: 'auth', kind: 'invites', eventName: 'invite-created', description: 'invites created', metadata: { workspaceId: workspace._id, invites, }, }); } catch (err) { res.status(500).json({ message: 'failed to create workspace' }).end(); } }); } exports.createWorkspace = createWorkspace; function updateWorkspace(req, res) { return __awaiter(this, void 0, void 0, function* () { const { name, logo, invites, members, labels } = req.body; const workspace = req.workspace; try { if (name) { workspace.name = name; } if (logo) { workspace.logo = logo; } if (invites) { workspace.invites = invites; } if (req.userPayload.isPrivileged) { if (members) { if (!Array.isArray(members)) { return res .status(400) .json({ message: "Invalid data. Must be an array." }) .end(); } if (members.length === 0) { return res .status(400) .json({ message: "Members cannot be empty." }) .end(); } workspace.members = members; } if (labels) { if (!(labels instanceof Array)) { return res .status(400) .json({ message: "Invalid data. Must be an array." }) .end(); } workspace.labels = labels; } } yield workspace.save(); res.status(200).json(workspace).end(); } catch (err) { logger_1.default.log('workspace update error', err); res.status(500).json({ message: 'failed to update workspace' }).end(); } }); } exports.updateWorkspace = updateWorkspace; function deleteWorkspace(req, res) { var _a; return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const userId = req.userPayload.sub; if (((_a = req.activeWorkspace) === null || _a === void 0 ? void 0 : _a._id) && req.workspace._id.toString() === req.activeWorkspace._id) { res.status(400).json({ message: 'You cannot remove your active workspace.' }).end(); return; } try { yield req.workspace.deleteOne(); res.status(200).json(req.workspace).end(); (0, api_kit_1.emitPlatformEvent)({ tenant: tenant, user: userId, source: 'auth', kind: 'workspaces', eventName: 'workspace-deleted', description: 'workspace deleted by user endpoint', metadata: req.workspace }); } catch (err) { res.status(500).json(err.message).end(); } }); } exports.deleteWorkspace = deleteWorkspace; function activateWorkspace(req, res) { var _a; return __awaiter(this, void 0, void 0, function* () { const token = (0, users_1.getCookieTokenValue)(req); const tenant = req.headers.tenant; try { const payload = yield (0, tokens_1.verifyToken)(token, tenant); const user = yield user_1.default .findOne({ _id: req.userPayload.sub, tenant }) .select('tenant email fullName firstName lastName roles tokens profileImage') .exec(); payload.workspace = { _id: req.workspace._id, name: req.workspace.name, roles: ((_a = req.workspace.members) === null || _a === void 0 ? void 0 : _a[0].roles) || ['admin'], labels: req.workspace.labels || [] }; const newCookieIdentifier = (0, tokens_1.getUniqueId)(); yield (0, users_1.updateToken)(user, 'cookie', payload, newCookieIdentifier); const { token: newToken } = (0, tokens_1.getSignedToken)(user, payload.workspace, newCookieIdentifier, String(config_1.cookieTokenExpiration / 1000)); (0, tokens_1.setCookie)(res, (0, users_1.getCookieTokenName)(tenant), newToken, null, (0, req_host_1.getRequestHost)(req)); res.json(req.workspace).end(); } catch (err) { res.status(500).json({ message: 'failed to activate workspace' }).end(); } }); } exports.activateWorkspace = activateWorkspace; function getWorkspaceMembers(req, res) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const userId = req.userPayload.sub; const _id = req.params.workspaceId; try { const query = { tenant, _id, 'members.user': userId }; if (req.userPayload.isPrivileged) { delete query['members.user']; } const workspace = yield workspace_1.default.findOne(query).select('members').lean().exec(); if (!workspace) { res.status(404).json({ message: 'workspace not found', from: 'get-members' }).end(); return; } let users = yield user_1.default.find({ tenant, _id: workspace.members.map(member => member.user) }).select('_id email fullName firstName lastName').lean().exec(); users = users.map(user => { return Object.assign(Object.assign({}, user), workspace.members.find(member => user._id.equals(member.user))); }); res.status(200).json(users).end(); } catch (err) { res.status(500).json({ message: 'Failed to load workspace members' }).end(); } }); } exports.getWorkspaceMembers = getWorkspaceMembers; function addWorkspaceMember(req, res) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const { workspaceId } = req.params; const { userId, roles } = req.body; if (!userId || !roles || !Array.isArray(roles)) { return res.status(400).json({ message: 'Invalid input. Provide "userId", "roles".' }).end(); } try { const user = yield user_1.default.findOne({ tenant, _id: userId }).exec(); if (!user) { return res.status(404).json({ message: 'User not found.' }).end(); } const workspace = yield workspace_1.default.findOne({ tenant, _id: workspaceId }).exec(); if (!workspace) { return res.status(404).json({ message: 'Workspace not found.' }).end(); } if (workspace.members.some(member => member.user.toString() === user._id.toString())) { return res.status(400).json({ message: 'User is already a member of the workspace.' }).end(); } workspace.members.push({ user: user._id, roles }); yield workspace.save(); res.status(200).json({ message: 'Member added successfully.', workspace }).end(); } catch (err) { res.status(500).json({ message: 'Failed to add member.', error: err.message }).end(); } }); } exports.addWorkspaceMember = addWorkspaceMember; function deleteWorkspaceMember(req, res) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const { userId } = req.params; try { const workspace = yield workspace_1.default.findOne({ tenant, 'members.user': userId }).exec(); if (!workspace) { return res.status(404).json({ message: 'Workspace not found.' }); } const memberIndex = workspace.members.findIndex((member) => { return member.user.toString() === userId; }); if (memberIndex === -1) { return res.status(404).json({ message: 'Member not found in the workspace.' }); } workspace.members.splice(memberIndex, 1); yield workspace.save(); return res.status(200).json({ message: 'Member removed from workspace.', removedMemberId: userId, userId, }); } catch (err) { return res.status(500).json({ message: 'Failed to remove member.', error: err.message }); } }); } exports.deleteWorkspaceMember = deleteWorkspaceMember; function updateWorkspaceMember(req, res) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const { workspaceId, userId } = req.params; const { roles } = req.body; if (!roles || !Array.isArray(roles)) { return res.status(400).json({ message: 'Invalid input. "roles" must be an array.' }); } try { const workspace = yield workspace_1.default.findOne({ tenant, _id: workspaceId }).exec(); if (!workspace) { return res.status(404).json({ message: 'Workspace not found.' }); } const member = workspace.members.find((member) => member.user.toString() === userId); if (!member) { return res.status(404).json({ message: 'Member not found in the workspace.' }); } member.roles = [...roles]; yield workspace.save(); return res.status(200).json({ message: 'Member roles updated successfully.', updatedMember: member, workspaceId, }); } catch (err) { return res.status(500).json({ message: 'Failed to update member roles.', error: err.message }); } }); } exports.updateWorkspaceMember = updateWorkspaceMember; function getWorkspaceByParams(req, res, next) { return __awaiter(this, void 0, void 0, function* () { const { tenant } = req.headers || {}; const _id = req.params.workspaceId; try { const userId = new ObjectId(req.userPayload.sub); const query = { tenant, _id, }; const isPrivilegedUser = req.userPayload.isPrivileged; if (!isPrivilegedUser) { query['members.user'] = userId; } const select = isPrivilegedUser ? 'name logo labels' : 'name logo labels members.$'; const workspace = yield workspace_1.default.findOne(query).select(select).exec(); if (!workspace) { res.status(404).json({ message: 'workspace not found', from: 'get-workspace' }).end(); return; } req.workspace = workspace; req.isWorkspacePrivileged = isPrivilegedUser || !!req.workspace.members.some(member => member.user.equals(userId) && member.roles.includes('admin')); next(); } catch (_a) { res.status(500).send({ message: 'failed to load workspace data' }); } }); } exports.getWorkspaceByParams = getWorkspaceByParams; function onlyWorkspacePrivileged(req, res, next) { if (!req.isWorkspacePrivileged) { res.status(403).send({ message: 'not authorized' }); return; } next(); } exports.onlyWorkspacePrivileged = onlyWorkspacePrivileged;