@intuitionrobotics/permissions
Version:
288 lines • 16.3 kB
JavaScript
;
/*
* 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