Feathers - restrict service response to data owned by current user only - hook

In Feathers the goal is to restrict the data accessible on a certain service to the data owned by the currently logged in user only.
Assuming that I am using Feathers authentication, the data available on this service is stored in a database table, and the table column that contains the user ID is called user_id, will this hook achieve the goal?
If not then what needs to change?
In case it is important to be able to answer the question then I am using Sequelize and Postgres.
const { authenticate } = require('feathers-authentication').hooks;
const { queryWithCurrentUser } = require('feathers-authentication-hooks');
const { associateCurrentUser } = require('feathers-authentication-hooks');
const readRestrict = [
queryWithCurrentUser({
idField: 'id',
as: 'user_id'
})
];
const modRestrict = [
associateCurrentUser({
idField: 'id',
as: 'user_id'
})
];
module.exports = {
before: {
all: [ authenticate('jwt') ],
find: [ ...readRestrict ],
get: [ ...readRestrict ],
create: [ ...modRestrict ],
update: [ ...modRestrict ],
patch: [ ...modRestrict ],
remove: [ ...modRestrict ]
},
after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
It seems to work but since I'm a Feathers noob I thought I'd better check before this is put into the wild to make sure there are no cases that I am unaware of that will cause leaks.

As a totoal beginner to feathers and express, I am unsure. Now, all works as stated above.
Old Answer
For remove, I used restrictToOwner. (I also think for patch and update because they operate on the existing data. I did not test that though.)
Otherwise I was able to cross-delete data by specifying the id. Maybe you can check if this is the case for you, too.
This is the test case:
user 1 creates an model object with
user id to check authorization
object id to identify the object
user 2 deletes the object with object id
test ok: 404 expected
test fail: 204 or 200 worked
user 1 tries to get the object
test ok: object is there, 200
test fail: object is absent, 404
Test code:
test_cannot_cross_delete
Thank you very much, you post was really helpful to me!

Related

How to identify "insert" vs "update" diff in Azure Cosmos DB trigger?

I have created a function triggered by Azure Cosmos DB by following https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-cosmos-db-triggered-function which is working.
Upon seeing logs, I am unable to identify if this trigger is for INSERT or UPDATE. I know that in AWS, when you add lambda as the dynamo trigger, you could identify that easily. You can even see what was original record vs updated one (for update)
Question:
How to identify INSERT vs UPDATE vs DELETE action?
I am new to Azure, so it is possible that I am missing something in my function code.
Function Code:
module.exports = async function (context, documents) {
context.log('1');
if (!!documents && documents.length > 0) {
context.log('Document Id: ', documents[0].id); <-- SHOWS ID OF THE RECORD
context.log('Document[0]', documents[0]); <-- ENTIRE RECORD
context.log('Documents', documents); <-- ALL RECORDS
}
}
context object from above:
{
invocationId: '3ee0136e-d005-4517-a11e-c43ec9ca7c67',
traceContext: {
traceparent: '00-4938136b13476845524c7ae5059d84f3-badfbc56b5ef0067-00',
tracestate: '',
attributes: {
OperationName: 'CosmosTrigger1'
}
},
executionContext: {
invocationId: '3ee0136e-d005-4517-a11e-c43ec9ca7c67',
functionName: 'CosmosTrigger1',
functionDirectory: 'C:\\home\\site\\wwwroot\\CosmosTrigger1',
retryContext: null
},
bindings: {
documents: [
[
Object
]
]
},
log: [
Function(anonymous)
]{
error: [
Function: error
],
warn: [
Function: warn
],
info: [
Function: info
],
verbose: [
Function: verbose
]
},
bindingData: {
invocationId: '3ee0136e-d005-4517-a11e-c43ec9ca7c67'
},
bindingDefinitions: [
{
name: 'documents',
type: 'cosmosDBTrigger',
direction: 'in'
}
],
done: [
Function(anonymous)
]
}
Function Log:
Unfortunately, you can't distinguish between the two types (insert & update). The trigger consumes the Change Feed, and you can't filter it for a specific type of operation.
Today, you see all inserts and updates in the change feed. You can't
filter the change feed for a specific type of operation.

Converting ODATA url to sequelize selector

My node.js application (getting requests with express, stores data with sequelize and proxies to a sap odata api) gets odata requests. As sequelize can't handle the odata scheme, I have to create a parser by myself.
For example the odata URI could be:
$expand=Car($expand=Brand($expand=Type)),Person($expand=Gender)&$filter=id eq 1
As you can see, there are some nested expands.
The parsed data should look like that:
{
include: [
{
association: "Car",
include: [
{
association: "Brand"
include: [
{
association: "Type"
}
}
]
},
{
association: "Person",
include: [
{
association: "Gender"
}
]
}
],
where: {
id: 1
}
}
I already had a look at some npm packages, but none of them does support the expand of entities/entitysets.
Any idea how to recursively convert that odata scheme to the sequelize selectors?
I'm pretty sure that would be interesting for many api developers using node.

Gremlin - How to return a property of an edge alongside a node result?

I would like to know how to add to the result a property of the edge that links a node to another node
I'm using node.js to make queries to a Neptune database through Gremlin. Then I send the result to Javascript through an API.
let data = [];
const id_vertex = "1"
data = await g.V().has(id,id_vertex)
.out()
.valueMap()
.with_('~tinkerpop.valueMap.tokens'))
.toList();
This is how data looks like:
[
{
name: [ 'Colt Bogan II' ],
label: 'User',
notification: [ 'Both' ],
usercity: [ 'Wardburgh' ],
id: '1',
RegisteredDate: [ '2010-10-14' ]
},
{
name: [ 'Webster Sanford' ],
label: 'User',
notification: [ 'SMS' ],
usercity: [ 'Nanniefurt' ],
id: '2',
RegisteredDate: [ '2006-02-03' ]
}
]
Then I use JSON.stringify to make the body of what I send back to javascript
body: JSON.stringify(data)
"body":
"[{\"name\":[\"Colt Bogan II\"],\"label\":\"User\",\"notification\":[\"Both\"],\"usercity\":[\"Wardburgh\"],\"id\":\"1\",\"RegisteredDate\":[\"2010-10-14\"]},
{\"name\":[\"Webster Sanford\"],\"label\":\"User\",\"notification\":[\"SMS\"],\"usercity\":[\"Nanniefurt\"],\"id\":\"2\",\"RegisteredDate\":[\"2006-02-03\"]}]"
I would like to add the property of the edge that connects my node that has the id id_vertex to another node (when returning all the properties of that node) in the body. Either while I'm making the Gremlin query or after that. Let's say that the edge property I'm interested in is color, then in the end the body I would like to look like this:
"body":
"[{\"edgeColor\":[\"Green\"], \"name\":[\"Colt Bogan II\"],\"label\":\"User\",\"notification\":[\"Both\"],\"usercity\":[\"Wardburgh\"],\"id\":\"1\",\"RegisteredDate\":[\"2010-10-14\"]},
{\"edgeColor\":[\"Blue\"], \"name\":[\"Webster Sanford\"],\"label\":\"User\",\"notification\":[\"SMS\"],\"usercity\":[\"Nanniefurt\"],\"id\":\"2\",\"RegisteredDate\":[\"2006-02-03\"]}]"
Also, I have maximum one edge from one node to another, if this helps
If you modify your query slightly you can include edge properties in the result.
let data = [];
const id_vertex = "1"
data = await g.V().has(id,id_vertex)
.outE()
.project('edge','vertex')
.by(valueMap('edgeColor'))
.by(inV().valueMap().with_('~tinkerpop.valueMap.tokens'))
.toList();

Unable to get permissions to work properly

I'm messing around with permissions and am unable to figure out what's going on. I created a custom chat type, gallery, with permissions that look like this:
[
{
"action": "Deny",
"name": "No access",
"resources": [
"*"
],
"roles": [
"*"
],
"owner": false,
"priority": 999
}
]
So basically, nobody can do anything.
Now, to test this, I create a gallery channel with a user:
const client = new StreamChat(<STREAM_KEY>);
const token = <TOKEN>
chatClient.setUser(
{
id: 'user1',
name: 'User 1',
},
token,
);
const channel = client.channel('gallery', 'example', {
name: 'Example',
});
Using the React UI kit, user1 (or any user) should NOT be able to view the channel given the permissions, right? And yet the channel loads and I can type messages normally. What am I doing wrong? I don't think being an owner or not matters, as I've tested this with two different users.
Thanks #ferhatelmas for the heads up. Though my app was in production mode, I noticed a toggle that was ON that disabled permissions. I toggled it off and I think everything seems to be OK.

Find paths from a graph and then count how many times a path occurs in Azure Cosmos DB using Gremlin

I am storing clickstream events in graph database using the below structure
User perform multiple events and each event has a edge towards previous event:
Vertices are 'user' and 'event'
Edges are 'performed' and 'previous'
Each event has a property named referer.
For eg, if a user views a page www.foobar.com/aaa
then there will be a page view event and it will have referer:www.foobar.com/aaa
Now I want to find the possible paths from homepage with their count
Using the below Gremlin query I am able to find the possible paths, but I am not able to group them to find counts of each path:
g.V().hasLabel('event').has('referer','https://www.foobar.com/').in('previous').in('previous').path().by('referer')
Output:
[
{
"labels": [
[],
[],
[]
],
"objects": [
"https://www.foobar.com/",
"https://www.foobar.com/aaa",
"https://www.foobar.com/bbb"
]
},
{
"labels": [
[],
[],
[]
],
"objects": [
"https://www.foobar.com/",
"https://www.foobar.com/aaa",
"https://www.foobar.com/bbb"
]
},
{
"labels": [
[],
[],
[]
],
"objects": [
"https://www.foobar.com/",
"https://www.foobar.com/ccc",
"https://www.foobar.com/ddd"
]
}
]
I want an output like this:
[[
"https://www.foobar.com/",
"https://www.foobar.com/aaa",
"https://www.foobar.com/bbb"
]:2,
[
"https://www.foobar.com/",
"https://www.foobar.com/ccc",
"https://www.foobar.com/ddd"
]:1]
Since I am using azure cosmos graph db only these gremlin operators are available
https://learn.microsoft.com/en-us/azure/cosmos-db/gremlin-support
Thanks
You can apply groupCount to a path using a syntax such as this:
groupCount().by(path().by('referer'))
So you could rewrite your query as:
g.V().hasLabel('event').
has('referer','https://www.foobar.com/').
in('previous').
in('previous').
groupCount().by(path().by('referer'))
Hope this helps,
Cheers
Kelvin

Resources