@intuitionrobotics/permissions
Version:
210 lines • 9.44 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.
*/
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