Undefined Security Context in extended cube using AbstractCube.sql(). CubeJS - sql

I have an Extended Cube that the sql attribute is based on the BaseCube/AbstractCube. The Base Cube uses the SECURITY_CONTEXT in its sql attribute.
When I query the extended cube I get TypeError: Cannot read property 'tenantId' of undefined
The SECURITY_CONTEXT is the following:
{
"tenantId": 1,
"iat": 1630526261,
"exp": 1808403576,
...
}
The cube definitions are something like this:
const BaseOrders = cube({
sql: `SELECT * FROM orders WHERE ${SECURITY_CONTEXT.tenantId.requiredFilter('tenantId')}`
measures: {
count: {
type: `count`,
sql: `id`
}
}
});
cube(`RestrictedOrderFacts`, {
extends: BaseOrders,
sql: `
SELECT * FROM (${BaseOrders.sql()}) AS bo WHERE status = 'RESTRICTED'
`,
measures: {
doubleCount: {
type: `number`,
sql: `${count} * 2`
}
}
});
When querying the RestrictedOrderFacts, it seems that the SQL Compiler has not available the security context. Am I doing something that is not supposed to be?
How can I add additional filters to an Abstract Cube depending the use case?
Note: The idea of the Abstract Cube is to provide the row level security for all of the Extended Cubes. So, we can centralize the tenant row level permissions on the Abstract Cube.

We don't recommend using SECURITY_CONTEXT and abstract cubes to manage data access. For this, we recommend using queryRewrite.
These documentation pages might be helpful:
https://cube.dev/docs/security/context#using-query-rewrite
https://cube.dev/docs/recipes/column-based-access

Related

Loopback join relation without selecting relation

I'am trying to build a query with loopback 4 with relation between 2 entities
customer.model.ts:
#model()
export class Customer extends Entity {
// id, name properties
#hasMany(() => Order)
orders?: Order[];
}
order.model.ts:
#model()
export class Order extends Entity {
// id, desc properties
#belongsTo(() => Customer)
customerId: Customer;
}
My goal is to get all cutomers that have at least 1 order but without selecting their orders, this my query
await customerRepository.find({
include: [{ relation: "orders" }],
});
I tried too :
await customerRepository.find({
include: [{ relation: "orders" }],
fields: {propertyName: }
});
Thank you for your help!
The behavior of excluding entries that doesn't have any related data (in your case excluding customers without any orders) is possible using INNER JOIN, which currently isn't supported in loopback by default.
You can use this newly published sequelize extension I recently worked on called
loopback4-sequelize which supports inner joins as well.
To achieve the expected results you'll need to set required to true as described here, so your filter object will look like this:
{
"include": [{
"relation": "orders",
"required": true, // returns only those customers who have orders
"scope": {
"fields": [] // exclude all fields in orders
}
}]
}
A point to keep in mind is that loopback by default never run SQL JOIN Queries internally. It uses inclusion resolvers which is responsible to include related models and is called after your source table's data is returned. Resulting in two different calls when you use include in your filter object.

Can I use TypeOrm's FindOperators inside a base class without getting bad SQL Unknown column '_type' in 'where clause'

I am trying to write a base class for CRUD operations for "typeorm": "~0.2.45" under "#nestjs/typeorm": "~8.0.3" using node v14.19.3.
The base class looks like this:
export class CrudService<T> {
protected repository: Repository<T>;
constructor(repository: Repository<T>) {
this.repository = repository;
}
...
// Minimized code example
async find() {
return this.repository.find({
where: {
created: Between("2022-06-21T14:18:00.000Z", "2022-06-21T14:19:00.000Z")
}
});
}
}
This generates the following SQL query (which is wrong), it seems to use the Between() object as a literal equality comparison.
query failed: SELECT ... FROM `carts` `CartEntity` WHERE `CartEntity`.`created` = ?
-- PARAMETERS: {
"_type":"between",
"_value":[
"2022-06-21T14:18:00.000Z",
"2022-06-21T14:19:00.000Z"
],
"_useParameter":true,
"_multipleParameters":true
}
If I implement the same code inside the CartsEntity service like this:
#Injectable()
export class CartsService extends CrudService<CartEntity> {
constructor(
#InjectRepository(CartsRepository) repository: CartsRepository
) {
super(repository);
}
...
async find() {
return this.repository.find({
where: {
created: Between("2022-06-21T14:18:00.000Z", "2022-06-21T14:19:00.000Z")
}
});
}
It works fine and retrieves the data with valid SQL using BETWEEN on MySql.
I am wondering what is wrong here since I am using the same instance of repository in both code examples. I tried looking at the TypeOrm FindOperator logic but could not pinpoint it.
It turns out that the problem is a difference in the typeorm npm versions I was using.
The CrudEntity described above is inside a private npm using typeorm 0.2.45 as are my microservices extending CrudEntity like the CartEntity above.
Since #nestjs/typeorm is tagged to typeorm 0.2.34 the type was not the same for FindOperator and thus returned as an object condition instead of a FindOperator generating the proper BETWEEN SQL statement.
Basically, make sure all typeorm versions are the same across all your projects sharing code.

Why am I seeing the _entities request in one service when the entity is native to another?

I'm working on implementing services compatible with Apollo GraphQL federation; my providing services are written in Lacinia (GraphQL library for Clojure).
I have one service that defines Users:
type User #key(fields: "id") {
id: String!
name: String!
}
type Query {
user_by_id(id:String!) : User
}
schema { query: Query }
and and a second that defines Products and extends Users:
type User #extends #key(fields: "id") {
id: String! #external
favorite_products: [Product]
}
type Product #key(fields: "upc") {
upc: String!
name: String!
price: Int!
}
type Query {
product_by_upc(upc: String!) : Product
}
schema { query: Query }
When I execute a query that spans services:
{
user_by_id(id: "me") {
id
name
favorite_products {
upc
name
price
}
}
}
I get a failure; the following request is sent to the products service:
INFO products.server - {:query "query($representations:[_Any!]!){_entities(representations:$representations){...on User{favorite_products{upc name price}}}}", :vars {:representations [{:__typename "User", :id "me"}]}, :line 52}
and that fails, because the products service shouldn't, as far as I know, have to provide the equivalent of __resolveReference for type User (which it extends); just type Product.
This is very unclear in the documentation and I'll experiment with providing a kind of stub reference resolver in Product for stubs of User.
Yes, indeed, you must provide the __resolveReference (or equivalent) for each type the service schema extends. In retrospect, it makes sense, as it provides the "kernel" of a raw value to be passed down the resolver tree.

AngularJS/Ionic How to correctly get data from database to display in view?

What is the correct/best approach to get data from a database for something like calendar and its events.
I think there are two possible ways:
1) Should I get all data from the database and save it in a $scope object, so that I access the SQLite db only once and work with the object later on
2) Should I only get the data for the currently selected day of the calendar, and if I want to display another day, I do another sql query.
EDIT: the database is local, so network perfomance is no matter
I think it depends on what is more important for you and how big the data is.
If you are working with a local database you have keep in mind that you always have to do async operations to get data from your database. So you always have a little time (depending on the device performance) where the promises have to get resolved even if its on local db.
My ionic application follows the concept 2 of your options without using any cache. The trick to render correct data is to resolve relevant data before entering the view.
My stateprovider looks like this:
.state('app.messages', {
url: "/messages",
views: {
'menuContent': {
templateUrl: "pathToTemplate.html",
controller: 'ExampleMessageListCtrl'
}
},
resolve: {
messages: function($q,DatabaseService) {
var deferred = $q.defer();
//All db functions are async operations
DatabaseService.getMessageList().then(function (messageList) {
deferred.resolve(messageList);
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
},
//Disable cache to always get the values from db
cache:false
In the controller you can access the messages variable via injection:
.controller('ExampleMessageListCtrl', function ($scope,messages) {
var loadMessages = function() {
$scope.messages = messages;
};
...
});
The benefits of this concept with resolving the data before entering the state is that you always get the data which is inside the db without rendering frames with empty data inside, which comes from default data inside the scope variables.
I think options 1 is the right option if you want to display data very quickly. The challenge in this case is to hold your cached data synchronized with the data in the database without loosing much performance, which I think is not very easy.

Entity Framework will not recognize webpage_UsersInRoles

I am using EF5 VS2012 and using SimpleMembership. I have let MS auto-create the SQL tables
WebSecurity.InitializeDatabaseConnection("SqlRoleManagerConnection", "webpages_Users", "UserID", "Username", true);
And whenever I attempt to create a model from DB to create EDMX, it omits the webpages_UsersInRoles table. There are references to this table in the XML but it does not appear on the diagram and no classes are generated for it. I am running VS2012 Update 1 so this is not related to the commonly reported bug. I have also manually selected Run Custom Tool which does not fix.
As you probably know, this missing table only contains two FK fields to link the Users and Roles tables.
I have attempted creating a new project and new EDMX files and they all produce the same result - missing webpages_UsersInRoles diagram & classes.
EDIT: I can repeating go into Update from DB and select the table and it will not add to the diagram or class. What is the reason for this behavior and how can I force EF to connect everything so I can use this table and class?
Generally you don't reference that table directly in your code. You work with the Membership API.
Here's a good post to read about it: Seeding Membership & Roles in ASP.NET MVC 4
Sample code:
private void SeedMembership()
{
if (!Roles.RoleExists("Administrator"))
{
Roles.CreateRole("Administrator");
}
if (!Roles.RoleExists("Teacher"))
{
Roles.CreateRole("Teacher");
}
if (!Roles.RoleExists("Student"))
{
Roles.CreateRole("Student");
}
if (!WebSecurity.UserExists("leniel"))
{
WebSecurity.CreateUserAndAccount("leniel", "mypass");
}
if (!Roles.GetRolesForUser("leniel").Contains("Administrator"))
{
Roles.AddUsersToRoles(new[] { "leniel" }, new[] { "Administrator" });
}
if (!WebSecurity.UserExists("tester"))
{
WebSecurity.CreateUserAndAccount("tester", "test123");
}
if (!Roles.GetRolesForUser("tester").Contains("Administrator"))
{
Roles.AddUsersToRoles(new[] { "tester" }, new[] { "Administrator" });
}
}
I think it's the way EF works with Many-To-Many relationships,
below are 2 explanations and how to work with them:
http://nileshhirapra.blogspot.in/2012/03/entity-framework-insert-operation-with.html
http://weblogs.asp.net/zeeshanhirani/archive/2008/08/21/many-to-many-mappings-in-entity-framework.aspx