@@ -56,6 +56,8 @@ export class PermissionsPlugin implements Plugin {
5656 defaultDeny : true ,
5757 permissionsDir : './permissions' ,
5858 cachePermissions : true ,
59+ tenantIsolation : false ,
60+ tenantField : '_organizationId' ,
5961 ...config ,
6062 } ;
6163
@@ -236,21 +238,69 @@ export class PermissionsPlugin implements Plugin {
236238 // Hook into data operations for permission checking (PRE-Operation)
237239 context . hook ( 'data.beforeCreate' , async ( data : any ) => {
238240 await this . checkDataPermission ( data , 'create' ) ;
241+ this . applyTenantToWrite ( data ) ;
239242 } ) ;
240243
241244 context . hook ( 'data.beforeUpdate' , async ( data : any ) => {
242245 await this . checkDataPermission ( data , 'update' ) ;
246+ this . applyTenantToWrite ( data ) ;
243247 } ) ;
244248
245249 context . hook ( 'data.beforeDelete' , async ( data : any ) => {
246250 await this . checkDataPermission ( data , 'delete' ) ;
251+ this . applyTenantFilter ( data ) ;
247252 } ) ;
248253
249254 context . hook ( 'data.beforeFind' , async ( data : any ) => {
250255 await this . applyRecordLevelSecurity ( data ) ;
256+ this . applyTenantFilter ( data ) ;
251257 } ) ;
252258
253259 this . context ?. logger . info ( '[Permissions Plugin] Event listeners registered' ) ;
260+
261+ if ( this . config . tenantIsolation ) {
262+ this . context ?. logger . info ( `[Permissions Plugin] Tenant isolation enabled (field: ${ this . config . tenantField } )` ) ;
263+ }
264+ }
265+
266+ /**
267+ * Apply tenant ID to write operations (create/update).
268+ * Stamps the tenant field on the record data so it belongs to the user's organization.
269+ */
270+ private applyTenantToWrite ( data : any ) : void {
271+ if ( ! this . config . tenantIsolation ) return ;
272+
273+ const organizationId = data . organizationId || data . metadata ?. organizationId ;
274+ if ( ! organizationId ) return ;
275+
276+ const tenantField = this . config . tenantField || '_organizationId' ;
277+
278+ // Stamp the tenant field on the record being written
279+ if ( data . doc ) {
280+ data . doc [ tenantField ] = organizationId ;
281+ }
282+ if ( data . record ) {
283+ data . record [ tenantField ] = organizationId ;
284+ }
285+ }
286+
287+ /**
288+ * Apply tenant filter to read/delete operations.
289+ * Ensures queries are automatically scoped to the user's organization.
290+ */
291+ private applyTenantFilter ( data : any ) : void {
292+ if ( ! this . config . tenantIsolation ) return ;
293+
294+ const organizationId = data . organizationId || data . metadata ?. organizationId ;
295+ if ( ! organizationId ) return ;
296+
297+ const tenantField = this . config . tenantField || '_organizationId' ;
298+
299+ // Merge tenant filter into existing query filters
300+ data . filters = {
301+ ...data . filters ,
302+ [ tenantField ] : organizationId ,
303+ } ;
254304 }
255305
256306 /**
@@ -266,6 +316,7 @@ export class PermissionsPlugin implements Plugin {
266316
267317 const permissionContext : PermissionContext = {
268318 userId,
319+ organizationId : data . organizationId || data . metadata ?. organizationId ,
269320 profiles : userProfiles || [ ] ,
270321 metadata : data . metadata ,
271322 } ;
@@ -302,6 +353,7 @@ export class PermissionsPlugin implements Plugin {
302353
303354 const permissionContext : PermissionContext = {
304355 userId,
356+ organizationId : data . organizationId || data . metadata ?. organizationId ,
305357 profiles : userProfiles || [ ] ,
306358 metadata : data . metadata ,
307359 } ;
0 commit comments