UNPKG

@intuitionrobotics/permissions

Version:
210 lines • 9.44 kB
/* * 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. */ import { BaseDB_ApiGenerator, validateNameWithDashesAndDots, validateOptionalId, validateStringWithDashes, validateUniqueId } from "@intuitionrobotics/db-api-generator/backend"; import { CollectionName_Api, CollectionName_Domain, CollectionName_Level, CollectionName_Projects } from "../_imports.js"; import { auditBy, filterDuplicates, MUSTNeverHappenException, validateArray, validateRange, validateRegexp } from "@intuitionrobotics/ts-common"; import { FirestoreTransaction } from "@intuitionrobotics/firebase/backend"; import { GroupPermissionsDB } from "./assign.js"; import {} from "@intuitionrobotics/firebase"; import { ApiException } from "@intuitionrobotics/thunderstorm/app-backend/exceptions"; import {} from "@intuitionrobotics/thunderstorm/backend"; import { AccountModule } from "@intuitionrobotics/user-account/app-backend/modules/AccountModule"; const validateProjectId = validateRegexp(/^[a-z-]{3,20}$/); export const validateProjectName = validateRegexp(/^[A-Za-z- ]{3,20}$/); export const validateStringWithDashesAndSlash = validateRegexp(/^[0-9A-Za-z-:/]+$/); export class ProjectDB_Class extends BaseDB_ApiGenerator { static _validator = { _id: validateProjectId, name: validateProjectName, customKeys: undefined, _audit: undefined }; constructor() { super(CollectionName_Projects, ProjectDB_Class._validator, "project", "ProjectDB"); } async preUpsertProcessing(transaction, dbInstance, request) { if (request) { const account = await AccountModule.validateSession(request, []); dbInstance._audit = auditBy(account.email); } } apis(pathPart) { return [ // this.apiDelete(pathPart), this.apiQuery(pathPart), this.apiQueryUnique(pathPart), ]; } } export class DomainDB_Class extends BaseDB_ApiGenerator { static _validator = { _id: validateOptionalId, projectId: validateProjectId, namespace: validateNameWithDashesAndDots, _audit: undefined }; constructor() { super(CollectionName_Domain, DomainDB_Class._validator, "domain", "DomainDB"); this.setLockKeys(['projectId']); } async assertDeletion(transaction, dbInstance) { const accessLevels = await AccessLevelPermissionsDB.query({ where: { domainId: dbInstance._id } }); if (accessLevels.length) { throw new ApiException(403, 'You trying delete domain that associated with accessLevels, you need delete the accessLevels first'); } } async preUpsertProcessing(transaction, dbInstance, request) { await ProjectPermissionsDB.queryUnique({ _id: dbInstance.projectId }); if (request) { const account = await AccountModule.validateSession(request, []); dbInstance._audit = auditBy(account.email); } } } export class LevelDB_Class extends BaseDB_ApiGenerator { static _validator = { _id: validateOptionalId, domainId: validateUniqueId, name: validateStringWithDashes, value: validateRange([[0, 1000]]), _audit: undefined }; constructor() { super(CollectionName_Level, LevelDB_Class._validator, "level", "LevelDB"); this.setLockKeys(['domainId']); } internalFilter(item) { const { domainId, name, value } = item; return [{ domainId, name }, { domainId, value }]; } async preUpsertProcessing(transaction, dbInstance, request) { await DomainPermissionsDB.queryUnique({ _id: dbInstance.domainId }); if (request) { const account = await AccountModule.validateSession(request, []); dbInstance._audit = auditBy(account.email); } } async upsertImpl_Read(transaction, dbInstance, request) { const existDbLevel = await transaction.queryUnique(this.collection, { where: { _id: dbInstance._id } }); const groups = await GroupPermissionsDB.query({ where: { accessLevelIds: { $ac: dbInstance._id } } }); const returnWrite = await super.upsertImpl_Read(transaction, dbInstance, request); if (existDbLevel) { const callbackfn = (group) => { const index = group.accessLevelIds?.indexOf(dbInstance._id); if (index === undefined) throw new MUSTNeverHappenException("Query said it does exists!!"); const accessLevel = group.__accessLevels?.[index]; if (accessLevel === undefined) throw new MUSTNeverHappenException("Query said it does exists!!"); accessLevel.value = dbInstance.value; }; const asyncs = []; asyncs.push(...groups.map(async (group) => { await GroupPermissionsDB.validateImpl(group); await GroupPermissionsDB.assertUniqueness(transaction, group); callbackfn(group); })); const upsertGroups = await transaction.upsertAll_Read(GroupPermissionsDB.collection, groups); await Promise.all(asyncs); // --- writes part upsertGroups(); } return returnWrite; } async assertDeletion(transaction, dbInstance) { const groups = await GroupPermissionsDB.query({ where: { accessLevelIds: { $ac: dbInstance._id } } }); const apis = await ApiPermissionsDB.query({ where: { accessLevelIds: { $ac: dbInstance._id } } }); if (groups.length || apis.length) throw new ApiException(403, 'You trying delete access level that associated with users/groups/apis, you need delete the associations first'); } setUpdatedLevel(dbLevel, units) { units.forEach(unit => { let hasGroupDomainLevel = false; const updatedLevels = unit.__accessLevels?.map(level => { if (level.domainId === dbLevel.domainId) { level.value = dbLevel.value; hasGroupDomainLevel = true; } return level; }) || []; if (!hasGroupDomainLevel) { updatedLevels.push({ domainId: dbLevel.domainId, value: dbLevel.value }); } unit.__accessLevels = updatedLevels; }); } } export class ApiDB_Class extends BaseDB_ApiGenerator { static _validator = { _id: validateOptionalId, projectId: validateProjectId, path: validateStringWithDashesAndSlash, accessLevelIds: validateArray(validateUniqueId, false), _audit: undefined, deprecated: undefined, onlyForApplication: undefined }; constructor() { super(CollectionName_Api, ApiDB_Class._validator, "api", "ApiDB"); this.setLockKeys(['projectId', "path"]); } externalFilter(item) { const { projectId, path } = item; return { projectId, path }; } internalFilter(item) { const { projectId, path } = item; return [{ projectId, path }]; } async preUpsertProcessing(transaction, dbInstance, request) { if (request) { const account = await AccountModule.validateSession(request, []); dbInstance._audit = auditBy(account.email); } await ProjectPermissionsDB.queryUnique({ _id: dbInstance.projectId }); // need to assert that all the permissions levels exists in the db const _permissionsIds = dbInstance.accessLevelIds; if (!_permissionsIds || _permissionsIds.length <= 0) return; const permissionsIds = filterDuplicates(_permissionsIds); await Promise.all(permissionsIds.map(id => AccessLevelPermissionsDB.queryUnique({ _id: id }))); dbInstance.accessLevelIds = permissionsIds; } registerApis(projectId, routes) { return this.runInTransaction(async (transaction) => { const existingProjectApis = await ApiPermissionsDB.query({ where: { projectId: projectId } }); const apisToAdd = routes .filter(path => !existingProjectApis.find(api => api.path === path)) .map(path => ({ path, projectId: projectId })); return this.upsertAll(apisToAdd, transaction); }); } apis(pathPart) { return [ this.apiDelete(pathPart), this.apiQuery(pathPart), this.apiQueryUnique(pathPart), this.apiUpdate(pathPart), ]; } } export const ProjectPermissionsDB = new ProjectDB_Class(); export const DomainPermissionsDB = new DomainDB_Class(); export const AccessLevelPermissionsDB = new LevelDB_Class(); export const ApiPermissionsDB = new ApiDB_Class(); //# sourceMappingURL=managment.js.map