UNPKG

@intuitionrobotics/permissions

Version:
288 lines • 16.3 kB
"use strict"; /* * ts-common is the basic building blocks of our typescript projects * * Copyright (C) 2020 Intuition Robotics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserPermissionsDB = exports.GroupPermissionsDB = exports.UsersDB_Class = exports.GroupsDB_Class = void 0; const _imports_1 = require("../_imports"); const backend_1 = require("@intuitionrobotics/db-api-generator/backend"); const backend_2 = require("@intuitionrobotics/user-account/backend"); const backend_3 = require("@intuitionrobotics/thunderstorm/backend"); const ts_common_1 = require("@intuitionrobotics/ts-common"); const managment_1 = require("./managment"); const permissions_share_1 = require("../permissions-share"); const validateUserUuid = (0, ts_common_1.validateRegexp)(/^.{0,50}$/); const validateGroupLabel = (0, ts_common_1.validateRegexp)(/^[A-Za-z-\._ ]+$/); const validateCustomFieldValues = (0, ts_common_1.validateRegexp)(/^.{0,500}$/); function checkDuplicateLevelsDomain(levels) { const domainIds = levels.map(level => level.domainId); const filteredDomainIds = (0, ts_common_1.filterDuplicates)(domainIds); if (filteredDomainIds.length !== domainIds.length) throw new backend_3.ApiException(422, 'You trying insert duplicate accessLevel with the same domain'); } class GroupsDB_Class extends backend_1.BaseDB_ApiGenerator { constructor() { super(_imports_1.CollectionName_Groups, GroupsDB_Class._validator, "group", "GroupsDB"); this.setLockKeys(['__accessLevels']); } externalFilter(item) { const { label } = item; return { label }; } internalFilter(item) { const { label } = item; return [{ label }]; } assertDeletion(transaction, dbInstance) { return __awaiter(this, void 0, void 0, function* () { const groups = yield exports.UserPermissionsDB.collection.query({ where: { __groupIds: { $ac: dbInstance._id } } }); if (groups.length) { throw new backend_3.ApiException(403, 'You trying delete group that associated with users, you need delete this group from users first'); } }); } setAccessLevels(dbInstance) { return __awaiter(this, void 0, void 0, function* () { dbInstance.__accessLevels = []; const accessLevelIds = dbInstance.accessLevelIds || []; if (accessLevelIds.length) { const groupLevels = yield (0, ts_common_1.batchAction)(accessLevelIds, 10, (chunked) => { return managment_1.AccessLevelPermissionsDB.query({ where: { _id: { $in: chunked } } }); }); checkDuplicateLevelsDomain(groupLevels); dbInstance.__accessLevels = groupLevels.map(level => { return { domainId: level.domainId, value: level.value }; }); } }); } getGroupsByTags(tags) { return __awaiter(this, void 0, void 0, function* () { const groupsByTags = yield this.collection.query({ where: { tags: { $aca: tags } } }); if (!groupsByTags) return []; return groupsByTags; }); } deleteTags(tag) { return __awaiter(this, void 0, void 0, function* () { const groupsWithTags = yield this.collection.query({ where: { tags: { $aca: [tag] } } }); if (!groupsWithTags) return; for (const _group of groupsWithTags) { if (!_group.tags) continue; (0, ts_common_1.removeItemFromArray)(_group.tags, tag); yield this.collection.upsert(_group); } }); } preUpsertProcessing(transaction, dbInstance, request) { return __awaiter(this, void 0, void 0, function* () { var _a; if (request) { const account = yield backend_2.AccountModule.validateSession(request, []); dbInstance._audit = (0, ts_common_1.auditBy)(account.email); } if (!dbInstance.accessLevelIds) return; yield this.setAccessLevels(dbInstance); const filterAccessLevelIds = (0, ts_common_1.filterDuplicates)(dbInstance.accessLevelIds); if (filterAccessLevelIds.length !== ((_a = dbInstance.accessLevelIds) === null || _a === void 0 ? void 0 : _a.length)) throw new backend_3.ApiException(422, 'You trying insert duplicate accessLevel id in group'); }); } getConfig() { return this.config; } getPredefinedGroupId(projectId, predefinedGroupId) { return `${projectId}--${predefinedGroupId}`; } upsertPredefinedGroups(projectId, projectName, predefinedGroups) { return this.runInTransaction((transaction) => __awaiter(this, void 0, void 0, function* () { const _groups = predefinedGroups.map(group => ({ _id: this.getPredefinedGroupId(projectId, group._id), label: `${projectName}--${group.key}-${group.label}` })); const dbGroups = (0, ts_common_1.filterInstances)(yield (0, ts_common_1.batchAction)(_groups.map(group => group._id), 10, (chunk) => { return transaction.query(this.collection, { where: { _id: { $in: chunk } } }); })); //TODO patch the predefined groups, in case app changed the label of the group.. const groupsToInsert = _groups.filter(group => !dbGroups.find(dbGroup => dbGroup._id === group._id)); return this.upsertAll(groupsToInsert, transaction); })); } } exports.GroupsDB_Class = GroupsDB_Class; GroupsDB_Class._validator = { _id: backend_1.validateStringAndNumbersWithDashes, label: validateGroupLabel, tags: undefined, accessLevelIds: (0, ts_common_1.validateArray)(backend_1.validateUniqueId, false), customFields: (0, ts_common_1.validateArray)((0, ts_common_1.validateObjectValues)(validateCustomFieldValues), false), __accessLevels: undefined, _audit: undefined }; class UsersDB_Class extends backend_1.BaseDB_ApiGenerator { constructor() { super(_imports_1.CollectionName_Users, UsersDB_Class._validator, "user", "UsersDB"); this.setLockKeys(["accountId"]); } preUpsertProcessing(transaction, dbInstance, request) { return __awaiter(this, void 0, void 0, function* () { var _a; if (request) { const account = yield backend_2.AccountModule.validateSession(request, []); dbInstance._audit = (0, ts_common_1.auditBy)(account.email); } this.setGroupIds(dbInstance); const userGroupIds = (0, ts_common_1.filterDuplicates)(((_a = dbInstance.groups) === null || _a === void 0 ? void 0 : _a.map(group => group.groupId)) || []); if (!userGroupIds.length) return; const userGroups = yield (0, ts_common_1.batchAction)(userGroupIds, 10, (chunked) => { return exports.GroupPermissionsDB.query({ where: { _id: { $in: chunked } } }); }); if (userGroupIds.length !== userGroups.length) { throw new backend_3.ApiException(422, 'You trying upsert user with group that not found in group permissions db'); } const userGroupsItems = dbInstance.groups || []; userGroupsItems.forEach((userGroupItem) => { userGroupsItems.forEach(innerUserGroupItem => { if (userGroupsItems.indexOf(userGroupItem) === userGroupsItems.indexOf(innerUserGroupItem)) return; if ((0, ts_common_1.compare)(userGroupItem.groupId, innerUserGroupItem.groupId) && (0, ts_common_1.compare)(userGroupItem.customField || {}, innerUserGroupItem.customField || {})) { throw new backend_3.ApiException(422, 'You trying upsert user with duplicate UserGroup (with the same groupId && customField)'); } }); }); }); } internalFilter(item) { const { accountId } = item; return [{ accountId }]; } setGroupIds(dbInstance) { dbInstance.__groupIds = []; const userGroups = dbInstance.groups || []; if (userGroups.length) { dbInstance.__groupIds = userGroups.map(userGroup => userGroup.groupId); } } __onUserLogin(account) { return __awaiter(this, void 0, void 0, function* () { yield this.insertIfNotExist(account.email); }); } __onNewUserRegistered(account) { return __awaiter(this, void 0, void 0, function* () { yield this.insertIfNotExist(account.email); }); } insertIfNotExist(email) { return __awaiter(this, void 0, void 0, function* () { return this.runInTransaction((transaction) => __awaiter(this, void 0, void 0, function* () { const account = yield backend_2.AccountModule.getUser(email); if (!account) throw new backend_3.ApiException(404, `user not found for email ${email}`); const users = yield transaction.query(this.collection, { where: { accountId: account._id } }); if (users.length) return; return this.upsert({ accountId: account._id, groups: [] }, transaction); })); }); } assignAppPermissions(assignAppPermissionsObj, request) { return __awaiter(this, void 0, void 0, function* () { const sharedUserIds = assignAppPermissionsObj.sharedUserIds || []; if (!sharedUserIds.length) throw new ts_common_1.BadImplementationException("SharedUserIds is missing"); const groupId = exports.GroupPermissionsDB.getPredefinedGroupId(assignAppPermissionsObj.projectId, assignAppPermissionsObj.group._id); yield permissions_share_1.PermissionsShare.verifyPermissionGrantingAllowed(assignAppPermissionsObj.granterUserId, { groupId, customField: assignAppPermissionsObj.customField }); if (!assignAppPermissionsObj.groupsToRemove.find(groupToRemove => groupToRemove._id === assignAppPermissionsObj.group._id)) throw new ts_common_1.BadImplementationException("Group to must be a part of the groups to removed array"); yield this.runInTransaction((transaction) => __awaiter(this, void 0, void 0, function* () { const users = yield (0, ts_common_1.batchAction)(sharedUserIds, 10, (chunked) => { return transaction.query(this.collection, { where: { accountId: { $in: chunked } } }); }); if (users.length !== sharedUserIds.length) throw new backend_3.ApiException(404, `No permissions USER for all user ids`); // TODO mention who miss? if (!assignAppPermissionsObj.customField) throw new backend_3.ApiException(400, `Cannot set app permissions '${assignAppPermissionsObj.projectId}--${assignAppPermissionsObj.group._id}', request must have custom fields restriction!!`); const _group = yield transaction.queryUnique(exports.GroupPermissionsDB.collection, { where: { _id: groupId } }); if (!_group) throw new backend_3.ApiException(404, `No permissions GROUP for id ${groupId}`); const updatedUsers = users.map(user => { var _a; const newGroups = (_a = (user.groups || [])) === null || _a === void 0 ? void 0 : _a.filter(group => !assignAppPermissionsObj.groupsToRemove.find(groupToRemove => { if (exports.GroupPermissionsDB.getPredefinedGroupId(assignAppPermissionsObj.projectId, groupToRemove._id) !== group.groupId) return false; return (0, ts_common_1.compare)(group.customField, assignAppPermissionsObj.customField, assignAppPermissionsObj.assertKeys); })); if (!newGroups.find(nGroup => nGroup.groupId === _group._id && (0, ts_common_1.compare)(nGroup.customField, assignAppPermissionsObj.customField))) { newGroups.push({ groupId: _group._id, customField: assignAppPermissionsObj.customField }); } user.groups = newGroups; return user; }); return this.upsertAll(updatedUsers, transaction, request); })); }); } patch(instance, propsToPatch, request) { return __awaiter(this, void 0, void 0, function* () { return this.collection.runInTransaction((transaction) => __awaiter(this, void 0, void 0, function* () { var _a; const dbInstance = yield this.assertExternalQueryUnique(instance, transaction); // If the caller has specified props to be changed, make sure the don't conflict with the lockKeys. const wrongKey = propsToPatch === null || propsToPatch === void 0 ? void 0 : propsToPatch.find(prop => this.config.lockKeys.includes(prop)); if (wrongKey) throw new ts_common_1.BadImplementationException(`Key ${wrongKey} is part of the 'lockKeys' and cannot be updated.`); // If the caller has not specified props, we remove the keys from the caller's instance // before merging with the original dbInstance. (0, ts_common_1._keys)(instance).forEach(key => { if (this.config.lockKeys.includes(key) || (propsToPatch && !propsToPatch.includes(key))) { delete instance[key]; } }); if (instance.groups && instance.groups.length < (((_a = dbInstance.groups) === null || _a === void 0 ? void 0 : _a.length) || 0)) { yield backend_2.AccountModule.logoutAccount(dbInstance.accountId); } const mergedObject = (0, ts_common_1.merge)(dbInstance, instance); yield (0, ts_common_1.validate)(mergedObject, this.validator); yield this.assertUniqueness(transaction, mergedObject, request); return this.upsertImpl(transaction, mergedObject, request); })); }); } } exports.UsersDB_Class = UsersDB_Class; UsersDB_Class._validator = { _id: undefined, accountId: validateUserUuid, groups: (0, ts_common_1.validateArray)({ groupId: backend_1.validateStringAndNumbersWithDashes, customField: (0, ts_common_1.validateObjectValues)(validateCustomFieldValues, false) }, false), __groupIds: (0, ts_common_1.validateArray)(backend_1.validateStringAndNumbersWithDashes, false), _audit: undefined }; exports.GroupPermissionsDB = new GroupsDB_Class(); exports.UserPermissionsDB = new UsersDB_Class(); //# sourceMappingURL=assign.js.map