diff --git a/modules/authorization/src/Authorization.ts b/modules/authorization/src/Authorization.ts index a3d18699e..3996cd421 100644 --- a/modules/authorization/src/Authorization.ts +++ b/modules/authorization/src/Authorization.ts @@ -72,7 +72,6 @@ export default class Authorization extends ManagedModule { async onServerStart() { await this.grpcSdk.waitForExistence('database'); this.database = this.grpcSdk.database!; - await runMigrations(this.grpcSdk); } async onConfig() { @@ -81,6 +80,7 @@ export default class Authorization extends ManagedModule { this.updateHealth(HealthCheckStatus.NOT_SERVING); } else { await this.registerSchemas(); + await runMigrations(this.grpcSdk); this.indexController = IndexController.getInstance(this.grpcSdk); this.relationsController = RelationsController.getInstance( this.grpcSdk, diff --git a/modules/authorization/src/controllers/index.controller.ts b/modules/authorization/src/controllers/index.controller.ts index 4ce8e3964..24028c0fc 100644 --- a/modules/authorization/src/controllers/index.controller.ts +++ b/modules/authorization/src/controllers/index.controller.ts @@ -24,7 +24,14 @@ export class IndexController { async createOrUpdateObject(subject: string, entity: string) { const index = await ObjectIndex.getInstance().findOne({ subject, entity }); if (!index) { - await ObjectIndex.getInstance().create({ subject, entity }); + await ObjectIndex.getInstance().create({ + subject, + subjectType: subject.split(':')[0], + subjectPermission: subject.split('#')[1], + entity, + entityType: entity.split(':')[0], + relation: entity.split('#')[1], + }); } } @@ -42,7 +49,10 @@ export class IndexController { if (!found) { await ActorIndex.getInstance().create({ subject: subject, + subjectType: subject.split(':')[0], entity: `${object}#${relation}`, + entityType: object.split(':')[0], + relation: relation, }); } const permissions = Object.keys(objectDefinition.permissions); diff --git a/modules/authorization/src/controllers/permissions.controller.ts b/modules/authorization/src/controllers/permissions.controller.ts index dd5368d55..1a93f6974 100644 --- a/modules/authorization/src/controllers/permissions.controller.ts +++ b/modules/authorization/src/controllers/permissions.controller.ts @@ -33,8 +33,10 @@ export class PermissionsController { const computedTuple = computePermissionTuple(subject, action, resource); await Permission.getInstance().create({ subject, + subjectType: subject.split(':')[0], permission: action, resource, + resourceType: resource.split(':')[0], computedTuple, }); } diff --git a/modules/authorization/src/controllers/relations.controller.ts b/modules/authorization/src/controllers/relations.controller.ts index 3605f228c..6d2c4822d 100644 --- a/modules/authorization/src/controllers/relations.controller.ts +++ b/modules/authorization/src/controllers/relations.controller.ts @@ -50,8 +50,10 @@ export class RelationsController { relationResource = await Relationship.getInstance().create({ subject: subject, + subjectType: subject.split(':')[0], relation: relation, resource: object, + resourceType: object.split(':')[0], computedTuple: computeRelationTuple(subject, relation, object), }); await this.indexController.constructRelationIndex(subject, relation, object); diff --git a/modules/authorization/src/migrations/actorIndex.migration.ts b/modules/authorization/src/migrations/actorIndex.migration.ts new file mode 100644 index 000000000..c1057cebb --- /dev/null +++ b/modules/authorization/src/migrations/actorIndex.migration.ts @@ -0,0 +1,37 @@ +import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import { ActorIndex } from '../models'; + +export const migrateActorIndex = async (grpcSdk: ConduitGrpcSdk) => { + const count = await ActorIndex.getInstance().countDocuments({ + entityType: '', + }); + if (count === 0) { + return; + } + let actorIndexes = await ActorIndex.getInstance().findMany( + { + entityType: '', + }, + undefined, + 0, + 100, + ); + let iterator = 0; + while (actorIndexes.length > 0) { + for (const actorIndex of actorIndexes) { + await ActorIndex.getInstance().findByIdAndUpdate(actorIndex._id, { + entityType: actorIndex.entity.split(':')[0], + subjectType: actorIndex.subject.split(':')[0], + relation: actorIndex.subject.split('#')[1], + }); + } + actorIndexes = await ActorIndex.getInstance().findMany( + { + entityType: '', + }, + undefined, + ++iterator * 100, + 100, + ); + } +}; diff --git a/modules/authorization/src/migrations/index.ts b/modules/authorization/src/migrations/index.ts index 801771f5e..8d8a0026a 100644 --- a/modules/authorization/src/migrations/index.ts +++ b/modules/authorization/src/migrations/index.ts @@ -1,5 +1,14 @@ import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import { migrateObjectIndex } from './objectIndex.migration'; +import { migrateActorIndex } from './actorIndex.migration'; +import { migrateRelationships } from './relationship.migration'; +import { migratePermission } from './permission.migration'; export async function runMigrations(grpcSdk: ConduitGrpcSdk) { - // ... + await Promise.all([ + migrateObjectIndex(grpcSdk), + migrateActorIndex(grpcSdk), + migrateRelationships(grpcSdk), + migratePermission(grpcSdk), + ]); } diff --git a/modules/authorization/src/migrations/objectIndex.migration.ts b/modules/authorization/src/migrations/objectIndex.migration.ts new file mode 100644 index 000000000..77111d580 --- /dev/null +++ b/modules/authorization/src/migrations/objectIndex.migration.ts @@ -0,0 +1,38 @@ +import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import { ObjectIndex } from '../models'; + +export const migrateObjectIndex = async (grpcSdk: ConduitGrpcSdk) => { + const count = await ObjectIndex.getInstance().countDocuments({ + entityType: '', + }); + if (count === 0) { + return; + } + let objectIndexes = await ObjectIndex.getInstance().findMany( + { + entityType: '', + }, + undefined, + 0, + 100, + ); + let iterator = 0; + while (objectIndexes.length > 0) { + for (const objectIndex of objectIndexes) { + await ObjectIndex.getInstance().findByIdAndUpdate(objectIndex._id, { + subjectType: objectIndex.subject.split(':')[0], + subjectPermission: objectIndex.subject.split('#')[1], + entityType: objectIndex.entity.split(':')[0], + relation: objectIndex.subject.split('#')[1], + }); + } + objectIndexes = await ObjectIndex.getInstance().findMany( + { + entityType: '', + }, + undefined, + ++iterator * 100, + 100, + ); + } +}; diff --git a/modules/authorization/src/migrations/permission.migration.ts b/modules/authorization/src/migrations/permission.migration.ts new file mode 100644 index 000000000..63ed2b315 --- /dev/null +++ b/modules/authorization/src/migrations/permission.migration.ts @@ -0,0 +1,36 @@ +import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import { Permission } from '../models'; + +export const migratePermission = async (grpcSdk: ConduitGrpcSdk) => { + const count = await Permission.getInstance().countDocuments({ + resourceType: '', + }); + if (count === 0) { + return; + } + let permissions = await Permission.getInstance().findMany( + { + resourceType: '', + }, + undefined, + 0, + 100, + ); + let iterator = 0; + while (permissions.length > 0) { + for (const permission of permissions) { + await Permission.getInstance().findByIdAndUpdate(permission._id, { + subjectType: permission.subject.split(':')[0], + resourceType: permission.resource.split(':')[0], + }); + } + permissions = await Permission.getInstance().findMany( + { + resourceType: '', + }, + undefined, + ++iterator * 100, + 100, + ); + } +}; diff --git a/modules/authorization/src/migrations/relationship.migration.ts b/modules/authorization/src/migrations/relationship.migration.ts new file mode 100644 index 000000000..373b4663b --- /dev/null +++ b/modules/authorization/src/migrations/relationship.migration.ts @@ -0,0 +1,36 @@ +import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import { Relationship } from '../models'; + +export const migrateRelationships = async (grpcSdk: ConduitGrpcSdk) => { + const count = await Relationship.getInstance().countDocuments({ + resourceType: '', + }); + if (count === 0) { + return; + } + let relationships = await Relationship.getInstance().findMany( + { + resourceType: '', + }, + undefined, + 0, + 100, + ); + let iterator = 0; + while (relationships.length > 0) { + for (const objectIndex of relationships) { + await Relationship.getInstance().findByIdAndUpdate(objectIndex._id, { + subjectType: objectIndex.subject.split(':')[0], + resourceType: objectIndex.resource.split(':')[0], + }); + } + relationships = await Relationship.getInstance().findMany( + { + resourceType: '', + }, + undefined, + ++iterator * 100, + 100, + ); + } +}; diff --git a/modules/authorization/src/models/ActorIndex.schema.ts b/modules/authorization/src/models/ActorIndex.schema.ts index 169dc8c87..34120859f 100644 --- a/modules/authorization/src/models/ActorIndex.schema.ts +++ b/modules/authorization/src/models/ActorIndex.schema.ts @@ -9,6 +9,7 @@ const schema: ConduitModel = { _id: TYPE.ObjectId, /** * { + * subject entity * "user:12312312": "organization:123123#member" * } */ @@ -16,10 +17,28 @@ const schema: ConduitModel = { type: TYPE.String, required: true, }, + // user + subjectType: { + type: TYPE.String, + required: true, + default: '', + }, entity: { type: TYPE.String, required: true, }, + // organization + entityType: { + type: TYPE.String, + required: true, + default: '', + }, + // member + relation: { + type: TYPE.String, + required: true, + default: '', + }, createdAt: TYPE.Date, updatedAt: TYPE.Date, }; @@ -40,7 +59,10 @@ export class ActorIndex extends ConduitActiveSchema { private static _instance: ActorIndex; _id: string; subject: string; + subjectType: string; entity: string; + entityType: string; + relation: string; createdAt: Date; updatedAt: Date; diff --git a/modules/authorization/src/models/ObjectIndex.schema.ts b/modules/authorization/src/models/ObjectIndex.schema.ts index 204e9d3c8..3f25c78d2 100644 --- a/modules/authorization/src/models/ObjectIndex.schema.ts +++ b/modules/authorization/src/models/ObjectIndex.schema.ts @@ -17,10 +17,34 @@ const schema: ConduitModel = { type: TYPE.String, required: true, }, + // organization + subjectType: { + type: TYPE.String, + required: true, + default: '', + }, + // view + subjectPermission: { + type: TYPE.String, + required: true, + default: '', + }, entity: { type: TYPE.String, required: true, }, + // organization + entityType: { + type: TYPE.String, + required: true, + default: '', + }, + // member + relation: { + type: TYPE.String, + required: true, + default: '', + }, createdAt: TYPE.Date, updatedAt: TYPE.Date, }; @@ -41,7 +65,11 @@ export class ObjectIndex extends ConduitActiveSchema { private static _instance: ObjectIndex; _id: string; subject: string; + subjectType: string; + subjectPermission: string; entity: string; + entityType: string; + relation: string; createdAt: Date; updatedAt: Date; diff --git a/modules/authorization/src/models/Permission.schema.ts b/modules/authorization/src/models/Permission.schema.ts index 2cea9f3f5..8574ab7e7 100644 --- a/modules/authorization/src/models/Permission.schema.ts +++ b/modules/authorization/src/models/Permission.schema.ts @@ -13,12 +13,24 @@ const schema: ConduitModel = { type: TYPE.String, required: true, }, + // organization + resourceType: { + type: TYPE.String, + required: true, + default: '', + }, // user:1adasdas subject: { type: TYPE.String, required: true, }, - // member relation: "owner" + // user + subjectType: { + type: TYPE.String, + required: true, + default: '', + }, + // read permission: { type: TYPE.String, required: true, @@ -48,7 +60,9 @@ export class Permission extends ConduitActiveSchema { private static _instance: Permission; _id: string; resource: string; + resourceType: string; subject: string; + subjectType: string; permission: string; computedTuple: string; createdAt: Date; diff --git a/modules/authorization/src/models/Relationship.schema.ts b/modules/authorization/src/models/Relationship.schema.ts index 06ffd7a1d..d90bc4e74 100644 --- a/modules/authorization/src/models/Relationship.schema.ts +++ b/modules/authorization/src/models/Relationship.schema.ts @@ -12,11 +12,23 @@ const schema: ConduitModel = { type: TYPE.String, required: true, }, + // organization + resourceType: { + type: TYPE.String, + required: true, + default: '', + }, // user:1adasdas subject: { type: TYPE.String, required: true, }, + // user + subjectType: { + type: TYPE.String, + required: true, + default: '', + }, // member relation: "owner" relation: { type: TYPE.String, @@ -47,8 +59,9 @@ export class Relationship extends ConduitActiveSchema { private static _instance: Relationship; _id: string; resource: string; - resourceId: string; + resourceType: string; subject: string; + subjectType: string; relation: string; computedTuple: string; createdAt: Date; diff --git a/modules/authorization/src/utils/index.ts b/modules/authorization/src/utils/index.ts index 967beac0f..1cd460265 100644 --- a/modules/authorization/src/utils/index.ts +++ b/modules/authorization/src/utils/index.ts @@ -41,7 +41,7 @@ export function getPostgresAccessListQuery( return `SELECT "${objectTypeCollection}".* FROM "${objectTypeCollection}" INNER JOIN ( SELECT * FROM "cnd_Permission" - WHERE "computedTuple" LIKE '${computedTuple}%' + WHERE "computedTuple" LIKE '${computedTuple}%' ) permissions ON permissions."computedTuple" = '${computedTuple}:' || "${objectTypeCollection}"._id INNER JOIN ( SELECT * FROM "cnd_ActorIndex"