Add custom saved params on featherjs - feathersjs

Im noobs use FeatherJS and I stuck on roles and permissions management in FeatherJS
I have tables contains users, users_role, and roles. Table roles have relationship with two permissions table. I dont save roles to users's table because one user can have more than one role.
I want to save roles.id into params.user, but I don't find the way how to save it. I want it because it's not efficient to query to database if I want to check a permission.
I try using
app.on('login', (authResult: AuthenticationResult, { connection }: Params) => {
app
.get('postgresqlClient')
.select('users_roles.role_id')
.from('users_roles')
.where('users_roles.user_id', '=', authResult.user.id)
.then(function (results) {
for (const index in results) {
authResult.user['roles'].push(results[index].role_id)
}
})
.catch((err) => console.log(err))
if (connection) {
app.channel('anonymous').leave(connection)
console.log('An user leave anonymous channel')
app.channel('authenticated').join(connection)
app.channel('authenticated')
console.log('An user authenticated and join authenticated channel')
}
})
But it seems not saved into context.
{
"accessToken": "jwtinhere",
"authentication": {
"strategy": "local",
"payload": {
"iat": 1675662745,
"exp": 1675749145,
"aud": "https://yourdomain.com",
"sub": "843438ee-69cb-493f-8812-71f0c7462f2f",
"jti": "dbd0641c-65b2-413c-ae80-52ba5f9c4e89"
}
},
"user": {
"id": "843438ee-69cb-493f-8812-71f0c7462f2f",
"full_name": "Super Admin",
"email": "admin#local.local",
"created_at": "2023-01-31T15:08:06.141Z",
"updated_at": "2023-01-31T15:08:06.141Z"
}
}
I want to my roles saved on context, so the result after successfully authentication is
{
"accessToken": "jwtinhere",
"authentication": {
"strategy": "local",
"payload": {
"iat": 1675662745,
"exp": 1675749145,
"aud": "https://yourdomain.com",
"sub": "843438ee-69cb-493f-8812-71f0c7462f2f",
"jti": "dbd0641c-65b2-413c-ae80-52ba5f9c4e89"
}
},
"user": {
"id": "843438ee-69cb-493f-8812-71f0c7462f2f",
"full_name": "Super Admin",
"email": "admin#local.local",
"created_at": "2023-01-31T15:08:06.141Z",
"updated_at": "2023-01-31T15:08:06.141Z",
"role": [0,1] // Added Role
}
}
And I only call context.params.user.role in hook if want to check the user roles. How can I do this? Thanks

Related

FeathersJS Not Authenticated 401 Error when trying to use Access Token

In postman I am able to log in the user and get an accessToken:
Doing Post at http://localhost:3030/authentication I type in:
{
"strategy": "local",
"email": "bob#bob.com",
"password": "bob"
}
and then I get:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJpYXQiOjE2NjEzMjgzMDEsImV4cCI6MTY2MTQxNDcwMSwiYXVkIjoiaHR0cHM6Ly95b3VyZG9tYWluLmNvbSIsImlzcyI6ImZlYXRoZXJzIiwic3ViIjoiMSIsImp0aSI6IjMyOWMwZTU3LTM5NTctNDUxOS05N2ZmLTRiNDIxOWI2MDQ2YSJ9.tIiRCMqzNg8F4lb1tzfYrOVvc148qRmZrZ7FPouHhKg",
"authentication": {
"strategy": "local",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJpYXQiOjE2NjEzMjgzMDEsImV4cCI6MTY2MTQxNDcwMSwiYXVkIjoiaHR0cHM6Ly95b3VyZG9tYWluLmNvbSIsImlzcyI6ImZlYXRoZXJzIiwic3ViIjoiMSIsImp0aSI6IjMyOWMwZTU3LTM5NTctNDUxOS05N2ZmLTRiNDIxOWI2MDQ2YSJ9.tIiRCMqzNg8F4lb1tzfYrOVvc148qRmZrZ7FPouHhKg",
"payload": {
"iat": 1661328301,
"exp": 1661414701,
"aud": "https://yourdomain.com",
"iss": "feathers",
"sub": "1",
"jti": "329c0e57-3957-4519-97ff-4b4219b6046a"
}
},
"user": {
"id": 1,
"email": "bob#bob.com",
"createdAt": "2022-08-24T08:04:57.464Z",
"updatedAt": "2022-08-24T08:04:57.464Z"
}
}
But then when I try to use accessToken for another POST in Postman, I get:
{
"name": "NotAuthenticated",
"message": "Not authenticated",
"code": 401,
"className": "not-authenticated",
"errors": {}
}
Why is this?
One idea I have is maybe I did this part wrong. I thought I was supposed to add
auth.associateCurrentUser({
idField: "id",
as: "group_admin",
}),
But I was getting an error & found something online that said to change it to this (context below):
setField({
from: "params.user.id",
as: "data.group_admin",
}),
import * as authentication from "#feathersjs/authentication";
// Don't remove this comment. It's needed to format import lines nicely.
const { authenticate } = authentication.hooks;
import { setField } from "feathers-authentication-hooks";
export default {
before: {
all: [authenticate("jwt")],
find: [],
get: [],
create: [
setField({
from: "params.user.id",
as: "data.group_admin",
}),
// auth.associateCurrentUser({
// idField: "id",
// as: "group_admin",
// }),
],
update: [],
patch: [],
remove: [],
},
Do you have any ideas to get the accessToken to work with authentication?
I can post more code if needed.

feathers.js - how to remove field payload from authentication response

After POST /authentication api, the response will be like this.
{
"accessToken": "XXXXX",
"authentication": {
"strategy": "local",
"accessToken": "XXXXX",
"payload": {
"iat": 1616402936,
"exp": 1616489336,
"aud": "https://yourdomain.com",
"iss": "feathers",
"sub": "c15ef318-68fc-471c-9710-52f14d87abda",
"jti": "57d103e1-c81b-4fc6-8bbe-952b74aaf8e3"
}
},
"user": {
"id": "c15ef320-68fc-471c-9710-52f14d87ccda",
"email": "abc.abc#abc.com",
}
}
I want to remove the payload object from above response.
How can I do that?
You should look into FeatherJS Hooks - https://docs.feathersjs.com/api/hooks.html
These can be used to inject logic into the API requests. Something like this should work for your example -
app.service('messages').hooks({
after: {
get: [
async context => {
delete context.authentication.payload
return context;
}
]
}
});

Sequelize Insert Cascade

im trying insert in multiple tables.
let me explain User create a new client, client insert id into bill - (idClient) table
this is the payload
{
"name": "Evelyn",
"lastName": "Doe",
"phone": "4534534",
"email": "eve#hotmail.com",
"identification": "xxxxx",
"services": [1, 2, 3],
"bill":{
"description": "New project"
}
}
the insert
_service.create = async (client) => {
const { description } = client.bill;
try {
const data = await Client.create(
{ client, bill: { description: description } },
{ include: { model: Bill } }
);
return data.id;
} catch (err) {
handleError = err.hasOwnProperty("errors")
? err.errors.map((error) => error.message)
: err;
throw new Error(handleError);
}
};
but im getting this error
{
"name": "Error",
"message": "ReferenceError: description is not defined"
}
and yes, bill table has that column.
the relation
Client.hasOne(models.Bill, { foreignKey: "idClient" });
so, im stuck, i read the documentation and i trying to do the same way as they do but i dont know what i doing wrong
https://sequelize.org/master/manual/creating-with-associations.html
I already did, i dont know if the best way
i modified the payload
{
"name": "Evelyn",
"lastName": "Doe",
"phone": "4534534",
"email": "eve#hotmail.com",
"identification": "xxxxx",
"description": "new Proyect",
}
the insert query, change bill to Bill as my table name and then add description value
const { description } = client;
const data = await Client.create(
{ ...client, Bill: { description } },
{ include: { model: Bill } }
);
and the result
{
"id": 6,
"name": "Evelyn",
"lastName": "Doe",
"phone": "4534534",
"email": "eve#hotmail.com",
"identification": "xxxxx",
"idStatus": 2,
"createdAt": "2020-11-13T08:52:37.000Z",
"updatedAt": "2020-11-13T08:52:37.000Z",
"Bill": {
"id": 4,
"idClient": 6,
"totalAmount": null,
"description": "new Proyect",
"idStatus": 2,
"createdAt": "2020-11-13T08:52:37.000Z",
"updatedAt": "2020-11-13T08:52:37.000Z"
}
}

AWS Lambda Handler extend S3event

I have the following pipeline:
A file is uploaded to S3, it triggers a Lambda (Let's call it L1) which runs and does some processing.
So at the moment, my entry point looks like this:
public Response handleRequest(S3Event event, Context context) {
....
}
Now, a S3Event JSON looks like this:
{
"Records": [
{
"awsRegion": "xxxxx",
"eventName": "ObjectCreated:Put",
"eventSource": "aws:s3",
"eventTime": "2017-09-12T09:27:59.471Z",
"eventVersion": "2.0",
"requestParameters": {
"sourceIPAddress": "xxxxxx"
},
"responseElements": {
"x-amz-id-2": "xxxxxx",
"x-amz-request-id": "xxxx"
},
"s3": {
"configurationId": "xxxxxx1",
"bucket": {
"name": "xxxxx",
"ownerIdentity": {
"principalId": "xxxxx"
},
"arn": "xxx"
},
"object": {
"key": "xxx",
"size": xxx,
"eTag": "xxxx",
"versionId": null,
"sequencer": "xxx",
"urlDecodedKey": "xxx"
},
"s3SchemaVersion": "1.0"
},
"userIdentity": {
"principalId": "xxxx"
}
}
],
}
If you pass this JSON in the "Test" section, it will succeed.
Now, to the point: I wish to add information to this JSON, something that would look like this:
{
"Records": [
{
"awsRegion": "xxxxx",
"eventName": "ObjectCreated:Put",
"eventSource": "aws:s3",
"eventTime": "2017-09-12T09:27:59.471Z",
"eventVersion": "2.0",
"requestParameters": {
"sourceIPAddress": "xxxxxx"
},
"responseElements": {
"x-amz-id-2": "xxxxxx",
"x-amz-request-id": "xxxx"
},
"s3": {
"configurationId": "xxxxxx1",
"bucket": {
"name": "xxxxx",
"ownerIdentity": {
"principalId": "xxxxx"
},
"arn": "xxx"
},
"object": {
"key": "xxx",
"size": xxx,
"eTag": "xxxx",
"versionId": null,
"sequencer": "xxx",
"urlDecodedKey": "xxx"
},
"s3SchemaVersion": "1.0"
},
"userIdentity": {
"principalId": "xxxx"
}
}
],
"MyErrorMessage":
{
"EnvelopeErrors": [
{
"EnvelopeErrorTrace": "stackTrace",
"EnvelopeErrorPositions": 1,
"EnvelopeErrorLength": 2
},
{
"EnvelopeErrorTrace": "SecondTrace",
"EnvelopeErrorPositions": 3,
"EnvelopeErrorLength": 4
}
],
}
}
Notice is the S3Event JSon but with a bit more data.
My question problem is the following: I want to have a custom input that also works when a pure S3Event is called.
public Response handleRequest(MyS3Event event, Context context) {
....
}
However, I have not been able to achieve this.
I have tried a custom POJO but it does not work when I upload to S3 a file.
I tried to extend the S3EventNotification class (from which S3Event extends), but again with no success.
Is it possible what I am trying to do?
What you can do is to have your Lambda (L1) call itself (asynchronously) by sending it the new, modified event similar to how recursive functions work.
Just be very careful though. You have to put a limit as to how deep you want to keep recursing. You don't want to end up with infinite calls. I am not sure if AWS guards against this.
In the AWS SDK Lambda has an invoke method:
Invokes a specific Lambda function. For an example, see Create the Lambda Function and Test It Manually.
If you are using the versioning feature, you can invoke the specific
function version by providing function version or alias name that is
pointing to the function version using the Qualifier parameter in the
request. If you don't provide the Qualifier parameter, the $LATEST
version of the Lambda function is invoked. Invocations occur at least
once in response to an event and functions must be idempotent to
handle this. For information about the versioning feature, see AWS Lambda Function Versioning and Aliases.
This operation requires permission for the lambda:InvokeFunction
action.
var params = {
FunctionName: 'STRING_VALUE', /* required */
ClientContext: 'STRING_VALUE',
InvocationType: Event | RequestResponse | DryRun,
LogType: None | Tail,
Payload: new Buffer('...') || 'STRING_VALUE',
Qualifier: 'STRING_VALUE'
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
One of the params you send a Payload which is the event the invoked function receives so you can send your MyErrorMessage in/as this payload to get the desired result.

MongoDB, NodeJS: updating an embedded document with new members

Using: MongoDB and native nodeJS mongoDB driver.
I'm trying to parse all the data from fb graph api, send it to my API and then save it to my DB.
PUT handling in my server:
//Update user's data
app.put('/api/users/:fbuser_id/:category', function(req, res) {
var body = JSON.stringify(req.body);
var rep = /"data":/;
body = body.replace(rep, '"' + req.params.category + '"' + ':');
req.body = JSON.parse(body);
db.fbusers.update({
id: req.params.fbuser_id
}, {
$set: req.body
}, {
safe: true,
multi: false
},
function(e, result) {
if (e) return next(e)
res.send((result === 1) ? {
msg: 'success'
} : {
msg: 'error'
})
});
});
I'm sending 25 elements at a time, and this code just overrides instead of updating the document.
Data I'm sending to the API:
{
"data": [
{
"category": "App page",
"name": "SoundCloud",
"id": "7919071058",
"created_time": "2013-09-16T18:16:59+0000"
},
{
...and so on
}
]
}
Basically my API changes "data" key from sent json to the category name, f.e.:
PUT to /api/users/000/likes will change the "data" key to "likes":
{
"likes": [
{
"category": "App page",
"name": "SoundCloud",
"id": "7919071058",
"created_time": "2013-09-16T18:16:59+0000"
},
{
...and so on
}
]
}
Then this JSON is put to the db.
Hierarchy in mongodb:
{
"_id": ObjectID("556584c8e908f0042836edce"),
"id": "0000000000000",
"email": "XXXX#gmail.com",
"first_name": "XXXXXXXX",
"gender": "male",
"last_name": "XXXXXXXXXX",
"link": "https://www.facebook.com/app_scoped_user_id/0000000000000/",
"locale": "en_US",
"name": "XXXXXXXXXX XXXXXXXXXX",
"timezone": 3,
"updated_time": "2015-05-26T18:11:59+0000",
"verified": true,
"likes": [
{
"category": "App page",
"name": "SoundCloud",
"id": "7919071058",
"created_time": "2013-09-16T18:16:59+0000"
},
{
"category": "App page",
"name": "SoundCloud",
"id": "7919071058",
"created_time": "2013-09-16T18:16:59+0000"
},
{
....and so on
}
]
}
So the problem is that my api overrides the field (in this case "likes") with newly sent data, instead of appending it to already existing data document.
I am pretty sure that I should be using other parameter than "$put" in the update, however, I have no idea which one and how to pass parameters to it programatically.
Use $push with the $each modifier to append multiple values to the array field.
var newLikes = [
{/* new item here */},
{/* new item here */},
{/* new item here */},
];
db.fbusers.update(
{ _id: req.params.fbuser_id },
{ $push: { likes: { $each: newLikes } } }
);
See also the $addToSet operator, it adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array.