Loopback - require 2 custom roles to access method - acl

I have kind of multi-tenant application.
I have many groups, users belongsTo one group. Group has many different models e.g. news, files etc.
Users have access only to elements of their group.
It is similar to the 'teamMember' example on the Loopback docs, in my case I have `groupMember', so when user is trying to access, I check if User.groupId === Model.groupId
The issue which I have, is that I need to have roles for those groups.
So, e.g. basic, privileged, admin. Basic can access some of methods, privileged more than basic but not all of them and admin can access all methods, but ONLY FOR ELEMENTS of his group.
So, in my understanding I have to combine - groupMember and e.g. admin, but I have no idea how to do that.
Anyone can help? I think it's quite common thing.

module.exports = function (app) {
//get User model from the express app
var loopback = require('loopback');
var User = app.models.User;
let { Role, RoleMapping } = require('../../server/server').models;
//Role Mapping
User.observe('after save', function setRoleMapping(ctx, next) {
console.log("Current User : "+currentUser);
if (ctx.instance) {
if(ctx.isNewInstance) {
let rol_name=[ctx.instance.type];
// look up role based on type
Role.find({where: {name:{inq:rol_name} }}, function(err, role) {
console.log('Role: '+role)
if (err) {return console.log(err);}
RoleMapping.create({
principalType: rol_name,
principalId: ctx.instance.id,
roleId: role[0].id
}, function(err, roleMapping) {
if (err) {return console.log(err);}
console.log("Created Role :"+JSON. parse(JSON. stringify(role)));
console.log('User assigned RoleID ' + role[0].id + ' (' + ctx.instance.type + ')');
});
});
}
}
next();
});
}
Post json Data Format
{
"email":"test54#gmail.coms",
"password":"123456",
"type":"storeAdmin"
}
My Role Table:

Related

Having multiple get routes on a NodeJS / Express REST API?

I'm creating a JS web app using NodeJS and Express (with SQL/MySQL for the database), pretty much directly implementing this API tutorial: https://www.bezkoder.com/node-js-rest-api-express-mysql/ (just replacing 'tutorials' with 'Employees').
I'm trying to write API functions to get all Employees with certain attributes (in the SQL table), for example all employees with lastName = "Garcia" or all employees with teamID = 43682, etc.
In my routes.js file I have this:
module.exports = app => {
const employees = require("../controllers/employee.controller.js");
const router = require("express").Router();
// Create a new Employee
router.post("/", employees.create);
// Retrieve all Employees
router.get("/", employees.findAll);
// Retrieve all Employees with lastName
router.get('/', employees.findLastName);
... a bunch more CRUD functions ...
app.use('/api/employees', router);
};
And this is the corresponding Controller function:
exports.findLastName = (req, res) => {
const lastName = req.query.lastName; // tried changing req.query.lastName to req.params.lastName
Employee.getLastName(lastName, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Error occurred while retrieving by last name."
});
else {
console.log(`Employees with lastName ${lastName} were found!` );
res.send(data);
}
});
};
exports.findAll = (req, res) => {
const title = req.query.title;
Employee.getAll(title, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while retrieving employees."
});
else {
console.log(`Employee with ${title} title was found!` );
res.send(data);
}
});
};
The findAll route/function (just copied from that tutorial) works by finding all Employees with a certain ID number (the primary key in the DB) and I know that works from testing it through Postman. I wrote the findLastName route/function by copying the findAll function and changing it to search by lastName, and making the corresponding functions in the model and controller classes.
The new function, findLastName, doesn't work... unless I put the route before the findAll route (or comment it out). Then it correctly calls all my functions and returns all employees with the lastName param.
What's actually happening here? Are you not allowed to have multiple .get() routes or something? If so, how would I implement a search API like this? This is my first time building a web app like this so admittedly I'm still a little hazy on how routing and all that works. Thank you for any help though!
In Express whenever the first route matches second will be ignored, so in your scenario you have two route.get with same path /
router.get('/', employees.findAll);
//Since route with path `/` is matched above already this will be ignored
router.get('/', properties.findLastName);
In order to find Employees with last name you will need to create a new route with param (param will contain the last name)
router.get('/:lastName', properties.findLastName);
You can access the param value like this req.params.lastName in controller

google-apps-script ContactsApp.getContact(email) migration to People API

How to write this simple line :
ContactsApp.getContact(email);
with the new People API ?
Thanks a lot !
Workflow:
Use people.connections.list to get the list of contacts of the requested user, specified via resourceName (people/me refers to the authenticated user).
Find the contact who has an email address like the one you are looking for.
Important notes:
Naturally, you won't get exactly the same information as in ContactsApp.getContact, since they are different API's. In this case, you'll get an instance of the Person resource.
You can choose which information should be populated on the retrieved person, using personFields (see the list of available fields here). Request multiple fields by providing a comma-separated string of fields (in the example below, emailAddresses and biographies are requested).
Code sample:
function getPerson(email) {
const resourceName = "people/me";
const optionalArgs = {
personFields: 'emailAddresses,biographies' // Add the person fields you want
}
const response = People.People.Connections.list(resourceName, optionalArgs);
const { connections } = response;
const person = connections.find(connection => {
return connection["emailAddresses"] && connection["emailAddresses"].some(emailAddress => emailAddress["value"] === email);
});
return person;
}

How to separate login credentials from user data in mysql and nodejs?

I was curious when I been using Django and found that the user data and authentication credentials were in different tables. Unfortunately I never understood how this worked, but I imagine through relationships.
My database is in MySQL and I have created the users table with the user data, then I have created another table called auth with its respective field, id, salt and hash which is what I am interested in saving. I have also created a field called user_id to relate to the user in the users table with the id (I don't know if it's fine like that).
Now, I have a function in my NodeJS code that is responsible for saving the data received by the network layer:
function insert (table, data) {
return new Promise((resolve, reject) => {
connection.query(`INSERT INTO ${table} SET ?`, data, (err, data) => {
if (err) return reject(err)
resolve(data)
})
})
}
has that structure so that it is reusable at all times.
On the other hand I have the controller that handles the business logic of the data received by the network layer:
function insert (user) {
if (!user) {
throw dataMissed
}
bcrypt.genSalt(1000, function(err, salt) {
if (err) throw err
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) throw err
user = {
name: user.name,
email: user.email,
position: user.position,
hash,
salt
}
return store.insert(collection, user)
})
})
}
Unlike the function that is dedicated to saving the data in the database, this one is unique since the logic will depend on the data, obviously.
And the problem I have is that I don't know how to save the generated salt and the hash in the corresponding table. As you can see, I use the same function to save both but it gives me an error, since it is a logic that does not work.
How do I save a value that will later be referenced by another table?
I think the problem is that your 2nd insert function expect user and it also expects user to have password when in reality user should not have password. You need to have 2 DALs, one to control user table and the other to control the credentials Then in your service layer you do your logic using waterfall since credentials DAL depends on user's id
Here is pseudo code of what I mean
Userdal.js
/*
* User should look something like
* {
* name: "xyz",
* age: 40,
* email: "test#test.com"
* etc...
* }
* Basically it has non sensitive information nothing has to do with credentials.
*/
function insert(user) {
/// does insert logic
return store.insert(collection, user) //or resolve, whatever works for you
}
CredentialsDal.js
/*
* creds should look something like
* {
* username: "aaaa",
* password: "asfa",
* userId: "1234567"
* }
*/
function insert(creds) {
// Do your logic here for creds, for example
bcrypt.genSalt(1000, function(err, salt) {
if (err) throw err
bcrypt.hash(creds.password, salt, function (err, hash) {
if (err) throw err
const sanitizedCreds = {
username: creds.username,
salt: salt,
hash: hash
// Literally anything you can credential goes here.
}
return store.insert(collection, sanitizedCreds)
})
})
}
UserService.js
// This is a nice library this gives things such as waterfall, series, etc... (you dont need to use it though)
const asyncx = require('async')
const credsDals = require('./dals/CredentialsDal')
const userDal = require('./dals/UserDal')
function register(userData, credsData) {
asyncx.waterfall([
// Create user
(callback) => {
let user = userDal.insert(userData)
callback(null, user.id)
},
// Create credentials
(userId, callback) => {
let creds = credsDal.insert(credsData)
callback(null, true)
}
], (err, res) => {
// Handle your result here
})
}
Controller.js
const UserService = require('./Services/UserService')
function register(req, res) {
const userData = req.body.user
const credsData = req.body.credentials
UserService.register(userData, credsData)
res.status(200).end()
}
finally your router
const controllers = require('./controllers')
router.post('/user', controllers.register)
The above code is missing some pieces I left for you to fill, such as actual logic implementation, callbacks and returns, promises, etc...
Note: such separation is really important and nice to have since it create a secure code. whenever you request a specific user you don't want to send all of their sensitive information with the response. For example, imagine you tap on your friend's user profile on facebook and all of the sudden that facebook frontend request will bring back even the user's credentials even if they are not rendered they are still being requested and can be sniffed! so these sensitive information are stored on a separate table if not separate server all together. In cases such as credit cards, etc... they have different servers that can only accept requests from the same network and bans all other requests, this way it makes it harder for anyone to make any request to those servers, basically you have to be inside the server's network to make that request.
Link to repl.it: https://repl.it/repls/BlindCuddlyUserinterface#index.js

MEANJS Server controller list function

i have some problems changing my list function in the server controller of my MEANJS app. This is the export.list function in the server controller of my module:
exports.list = function(req, res) {
Wordset.find({ 'user':req.user._id }).sort('-created').populate('user', 'displayName').exec(function(err, wordsets) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(wordsets);
}
});
};
What it does is that it lists all the items from this module in the listview. In this case it shows all Wordset fromt this user. What i would like to change is that it should show Wordsets not depending on the user, but depending on another module (a user can create persons and each person should have his own wordsets). Lets say i have stored the person._id already, what changes do i need to do here to show only the Worsets for this specific person?
May be something like this (ofc. if Wordset has person field):
Wordset.find({ 'person': person._id })

sails.js: how to update model

Forgive my noob question. I'm using angularjs to send a user model (json) with varying fields. It works well with sails.js default PUT. I overrode the PUT, the problem is that I wish to update the model with the received JSON and do some processing on the modified model. Now I can't update the model with
User.update({
id: req.body.id
},{
req.body
}, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Please help
EDIT:
After knocking my head on the wall for days, problem solved! I know, my code formatting here is not the best..
changed this:
{
req.body
}
to just:
req.body
(without the braces)
full snippet becomes:
User.update({
id: req.body.id
},
req.body
, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Thanks.
So you figured out your problem, sort of. req.body is already an object. But you really should sanitize it before you put it into your update and then save the object. There's a lot of reasons for this but with Mongo when you get only a partial object you'll replace the object in the collection which, in your example with a user, could be bad. When I send users to the frontend I cull off things I don't want transmitted all over like passwords. The other reason is the golden rule of web application development - never trust the client! I'd start with something like:
var user = User.findOne(req.body.id).done(function(error, user) {
if(error) {
// do something with the error.
}
if(req.body.email) {
// validate whether the email address is valid?
// Then save it to the object.
user.email = req.body.email;
}
// Repeat for each eligible attribute, etc.
user.save(function(error) {
if(error) {
// do something with the error.
} else {
// value saved!
req.send(user);
}
});
});