Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions data-filter-resolver.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// MOST Web Framework 2.0 Codename Blueshift BSD-3-Clause license Copyright (c) 2017-2022, THEMOST LP All rights reserved
export declare class DataFilterResolver {
resolveMember(member: string, callback: (err?: Error, res?: any) => void);
resolveMethod(name: string, args: Array<any>, callback: (err?: Error, res?: any) => void);
me(callback: (err?: Error, res?: any) => void);
user(callback: (err?: Error, res?: any) => void);
now(callback: (err?: Error, res?: Date) => void);
today(callback: (err?: Error, res?: Date) => void);
lang(callback: (err?: Error, res?: string) => void);

resolveMember(member: string, callback: (err?: Error, res?: any) => void): void;
resolveMethod(name: string, args: Array<any>, callback: (err?: Error, res?: any) => void): void;
me(callback: (err?: Error, res?: any) => void): void;
user(callback: (err?: Error, res?: any) => void): void;
now(callback: (err?: Error, res?: Date) => void): void;
today(callback: (err?: Error, res?: Date) => void): void;
lang(callback: (err?: Error, res?: string) => void): void;
}
28 changes: 22 additions & 6 deletions data-model.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// MOST Web Framework 2.0 Codename Blueshift BSD-3-Clause license Copyright (c) 2017-2022, THEMOST LP All rights reserved
// noinspection ES6ConvertVarToLetConst
// noinspection ES6ConvertVarToLetConst,JSUnresolvedReference

var _ = require('lodash');
var {cloneDeep} = require('lodash');
Expand All @@ -8,7 +8,7 @@ var Symbol = require('symbol');
var path = require('path');
var pluralize = require('pluralize');
var async = require('async');
var {QueryUtils, Expression} = require('@themost/query');
var {QueryUtils, Expression, MemberExpression} = require('@themost/query');
var {OpenDataParser} = require('@themost/query');
var types = require('./types');
var {DataAssociationMapping} = require('./types');
Expand Down Expand Up @@ -800,10 +800,26 @@ function filterInternal(params, callback) {
DataFilterResolver.prototype.resolveMember.call(self, member, cb);
};
parser.resolveMethod = function(name, args, cb) {
if (typeof self.resolveMethod === 'function')
self.resolveMethod.call(self, name, args, cb);
else
DataFilterResolver.prototype.resolveMethod.call(self, name, args, cb);
var resolveMethod = (typeof self.resolveMethod === 'function') ? self.resolveMethod : DataFilterResolver.prototype.resolveMethod;
resolveMethod.call(self, name, args, function(err, result) {
if (err) { return cb(err); }
if (result instanceof MemberExpression) {
if (Object.prototype.hasOwnProperty.call(result, '$expand')) {
// handle $expand property
result.$expand.forEach(function(expand) {
// try to find if the expand expression already exists in $joinExpressions
var joinExpr = $joinExpressions.find(function(x) {
return expand.$entity.$as === x.$entity.$as;
});
if (joinExpr == null) {
$joinExpressions.push(expand);
}
});

}
}
return cb(null, result);
});
};
var filter;

Expand Down
3 changes: 2 additions & 1 deletion jest.setup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const { TraceUtils } = require('@themost/common');
const { JsonLogger } = require('@themost/json-logger');
TraceUtils.useLogger(new JsonLogger({
format: 'raw'
format: 'raw',
level: 'debug',
}));
/* env */
process.env.NODE_ENV = 'development';
Expand Down
113 changes: 110 additions & 3 deletions spec/DataPrivileges.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,73 @@
import { resolve } from 'path';
import { DataContext } from '../index';
import { TestApplication } from './TestApplication';
// noinspection JSUnusedLocalSymbols

import {resolve} from 'path';
import {DataAssociationMapping, DataContext, DataObjectTag} from '../index';
import {TestApplication} from './TestApplication';
import {DataFilterResolver} from '@themost/data';
import {MemberExpression, QueryEntity, QueryExpression, QueryField} from '@themost/query';

declare class DataFilterResolverWithExtensions extends DataFilterResolver {
regions(): Promise<string[]>;
}

describe('Permissions', () => {
let app: TestApplication;
let context: DataContext;

function regions(callback: (err?: Error, res?: any) => void) {
return this.context.model('User').asQueryable()
.select('id', 'name')
.where('name').equal(this.context.user.name)
.expand('userRegions').silent().getItem().then((user: any) => {
const values = (user && user.userRegions || []);
return callback(null, values);
}).catch((err: Error) => {
return callback(err);
});
}

function userRegions(callback: (err?: Error, res?: any) => void) {
const Users = this.context.model('User');
const { viewAdapter: UserView } = Users;
const property: DataObjectTag = Users.convert({}).property('userRegions');
const { viewAdapter: UserRegionView } = property.getBaseModel();
const mapping: DataAssociationMapping = property.mapping;
void property.migrate((err: Error) => {
if (err) {
return callback(err);
}
const query = new QueryExpression().select(
new QueryField(mapping.associationValueField).from('userRegions')
).from('Any')
.join(new QueryEntity(UserView).as('userRegions_Users'))
.with(
new QueryExpression().where(
new QueryField('name').from('userRegions_Users')
).equal(
this.context.user.name
)
)
.join(new QueryEntity(UserRegionView).as('userRegions')).with(
new QueryExpression().where(
new QueryField(mapping.associationObjectField).from('userRegions')
).equal(
new QueryField('id').from('userRegions_Users')
)
);
const { $expand } = query;
return callback(null, Object.assign(new MemberExpression(`userRegions.${mapping.associationValueField}`), {
$expand
}));
});
}

if (Object.prototype.hasOwnProperty.call(DataFilterResolver.prototype, 'regions') === false) {
Object.assign(DataFilterResolver.prototype, {
regions,
userRegions
})
}

beforeAll(async () => {
app = new TestApplication(resolve(__dirname, 'test2'));
context = app.createContext();
Expand Down Expand Up @@ -125,4 +188,48 @@ describe('Permissions', () => {

});

it('should get items based on user access', async () => {
context.user = {
name: 'jane.keene@example.com'
};
const q = await context.model('Person').filterAsync(
{ $filter: 'address/addressRegion eq regions()'}
);

const user = await context.model('User').asQueryable()
.select('id', 'name')
.where('name').equal(context.user.name)
.expand('userRegions').silent().getItem();

expect(q).toBeTruthy();
const items = await q.getItems();
expect(items).toBeTruthy();
expect(items.length).toBeTruthy();
for (const item of items) {
expect(user.userRegions.some((region: string) => region === item.address.addressRegion)).toBeTruthy();
}
});

it('should get items using queryable method', async () => {
context.user = {
name: 'jane.keene@example.com'
};
const q = await context.model('Person').filterAsync(
{ $filter: 'address/addressRegion eq userRegions()'}
);

const user = await context.model('User').asQueryable()
.select('id', 'name')
.where('name').equal(context.user.name)
.expand('userRegions').silent().getItem();

expect(q).toBeTruthy();
const items = await q.getItems();
expect(items).toBeTruthy();
expect(items.length).toBeTruthy();
for (const item of items) {
expect(user.userRegions.some((region: string) => region === item.address.addressRegion)).toBeTruthy();
}
});

});
28 changes: 28 additions & 0 deletions spec/test2/config/models/User.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,34 @@
"@id": "https://themost.io/schemas/userReviews",
"name": "userReviews",
"type": "UserReview"
},
{
"@id": "https://themost.io/schemas/region",
"name": "userRegions",
"type": "Text",
"many": true,
"mapping": {
"associationType": "junction",
"associationAdapter": "UserRegions",
"associationObjectField": "user",
"associationValueField": "region",
"privileges": [
{
"mask": 15,
"type": "global"
},
{
"mask": 15,
"type": "global",
"account": "Administrators"
},
{
"mask": 1,
"type": "self",
"filter": "user eq me()"
}
]
}
}
],
"privileges": [
Expand Down
Binary file modified spec/test2/db/local.db
Binary file not shown.