@qelos/auth
Version:
Express Passport authentication service
490 lines (489 loc) • 21.4 kB
JavaScript
"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;