Security level access nodejs and passport - node.js

I am currently making a Timecard system for employees to use at the job I work at (just to make things a little less chaotic) and I would like to have regular users and managers. My basic structure that I want to achieve is this for the Schemas:
User
---name
---password
---isClockedIn
---phoneNumber
---isManager
--if isManager == true
--deleteUser
--addUser
--etc
Now, the current User schema I have is this:
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = mongoose.Schema({
username: String,
password: String,
first_name: String,
middle_initial: String,
last_name: String,
phone: Number,
isManager: Boolean,
isLoggedIn: Boolean,
points_accrued: Number
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
I am not sure if I should add an extra schema for the Admin permissions or really how to go about giving a regular user that type of access.
Now for my routes, I want the user (Employee) and the Admin to see pretty much the same view, except the manager will have some additional links that will allow his to see all users, delete them, change timecards ect.
for now this is what my routes look like:
//INDEX SHOW USER INFO
router.get("/", middleware.isLoggedIn, middleware.isManager, function(req, res){
User.find({}, function(err, currentUser){
if(err){
console.log("error occured");
console.log(err);
} else {
res.render("users/index", {user: currentUser});
}
});
});
And then my middleware is going to look like this:
middlewareObj.isManager = function(req, res, next){
if(req.isAuthenticated() && req.isManager === true){
//Load Page for admins view example: see all employees clocked in and admins timesheet
return next();
} else {
//Load Page to only show current Users timesheet
return next();
}
now this begs the question, would it be better and more fluid for the first page to be the same for both user and admin (like show generic time info for that particular employee) and the if the user is an admin have a link show up that only they can access to make any low level changes, OR have the admin see all of that data on his homepage without clicking on a different link?
And further more, how can I make my middleware tell the route, and then the route tell the html (ejs in my case) what to show and what not to display.
I appreciate it and thank you!

For the first ask i think is only a business logic, so depend of you what you want. For experience i prefer the first way in case of small projects becouse is more fast to implement (show/hide element of same page), for complex projects can be usefull do also a different page only for the admin.
For the second ask, you can simply put a condition, assume, like your example, you pass the user to ejs.
<% if (user && user.isManage) { %>
<button>Admin button</button>
<% } %>

Related

Exclude user's password from query with Prisma 2

Recently I started working on a new project to learn some new technologies (Prisma 2, REST api with Express, etc.). Tho, I faced a problem.
My app has a user authentication system and the user model has a password column. So, when the client requests a user, the backend selects all the columns from the database including the password (that's hashed by the way).
I tried to not select the password column on the prisma findMany, like this:
await prisma.user.findUnique({
where: {
...
},
select: {
password: false
}
});
But I got an error by prisma saying that the select should contain at least one truly value. Thus, I added id: true to the select. I made an api request and I saw that only the id was returning for the user.
By my understanding, prisma expects me to add all the columns I care to the select object. But, I need a lot of columns from the user and I am making a lot of queries to fetch users and I cannot just write all the field I need everytime.
So, I wanted to ask you if there is a legit way to do that.
PS: I don't take "use rawQuery instead" as a solution.
The only legit way is adding column: true to the columns you want to include. There are requests for excluding columns here so it would be great if you could add a đź‘Ť to the request relevant to you so that we can look at the priority.
https://github.com/prisma/prisma/issues/5042
https://github.com/prisma/prisma/issues/7380
https://github.com/prisma/prisma/issues/3796
I've been wondering about how to implement this as well, and bafflingly the issues linked in #Ryan's post are over two years old, and still unresolved. I came up with a temporary workaround, which is to implement a middleware function for the Prisma client which removes the password field manually after each call.
import { PrismaClient } from '#prisma/client'
async function excludePasswordMiddleware(params, next) {
const result = await next(params)
if (params?.model === 'User' && params?.args?.select?.password !== true) {
delete result.password
}
return result
}
const prisma = new PrismaClient()
prisma.$use(excludePasswordMiddlware)
This will check if the model being queried is a User, and it will not delete the field if you explicitly include the password using a select query. This should allow you to still get the password when needed, like when you need to authenticate a user who is signing in:
async validateUser(email: string, password: string) {
const user = await this.prisma.user.findUnique({
where: { email },
select: {
emailVerified: true,
password: true,
},
})
// Continue to validate user, compare passwords, etc.
return isValid
}
Check out the following code
Exclude keys from user
function exclude(user, ...keys) {
for (let key of keys) {
delete user[key]
}
return user
}
function main() {
const user = await prisma.user.findUnique({ where: 1 })
const userWithoutPassword = exclude(user, 'password')
}
reference
prima official Website

meteor-roles not working as expected it returning false

we are using meteor METEOR#1.4.0.1 and added alanning:roles for managing role.
below code that we using for apply role.
Accounts.onCreateUser(function(options, user) {
console.log("on account create");
Roles.addUsersToRoles(user._id,'admin')
console.log(Roles.userIsInRole(Meteor.userId(),'admin'));
return user;
});
Roles.userIsInRole(Meteor.userId(),'admin') return false
help us what wrong.
with alanning-roles, you don't need to include that to Accounts.onCreateUser.
You just need to use Roles.addUserToRoles() after you've inserted the new document. Like so:
const id = Accounts.createUser({
email: user.email,
password: "apple1",
profile: { name: user.name }
});
Roles.addUsersToRoles(id,'whateverRole')
Based on their README.md:
Note that the Roles.addUsersToRoles call needs to come after Accounts.createUser or Accounts.onCreate or else the roles package won't be able to find the user record (since it hasn't been created yet).

Expressjs (node) - How to get a set of data which is filtered according to the user

I am new to express js. I am currently building a todo list web app. I have been following the tutorials and is able to perform basic CRUD operation. I have two models.
Users - (name, email, password hash, password salt, todo_items - which reference to the second model).
Todo_items - (title, description, due_date, user - which reference to the first model).
When a user log in, I am able to read his user_id. However, how can i filter the todo_items that only belongs to the user? My current code looks like this and is returning all the todo_items from all the users.
router.get('/api/todo_items', function(req, res, next){
Todo_items.find(function(err,todo_items){
if(err){return next(err); }
res.json(todo_items);
})
});
Currently, I am using a front end framework, Angularjs to filter the results that get displayed. But I thought this is inefficient as the user base gets big. How do I create a filtering system from the backend?
You can query the collection on the user field, assuming it references the User model on the _id field so you could have something like the following:
router.get('/api/todo_items', function(req, res, next){
Guest.find({ "user": user_id }).exec(function(err, todo_items){
if(err) { return next(err); }
res.json(todo_items);
});
});

node not recognizing duplicated entries

I'm trying to create a basic MEAN stack CRUD api to add shops into my database. I want every shop to have a unique name (to avoid adding duplicates). So far, everything gets saved into the database even if I post the same request 10 times. Went trough the code a couple of times and can't figure out what's wrong, if anyone could point me in the right direction I'd be very grateful.
shop model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
//shop schema
var ShopSchema = new Schema({
name: { type: String, required: true, index: { unique: true }},
address: { type: String, required: true, index: { unique: true }}
});
module.exports = mongoose.model('Shop', ShopSchema);
post function:
apiRouter.route('/shops')
//create a shop
.post(function(req, res) {
//new instance of shop model
var shop = new Shop();
//set the shop information
shop.name = req.body.name;
shop.address = req.body.address;
//save shop and check for errors
shop.save(function(err) {
if(err) {
//duplicate entry
if(err.code == 11000) {
return res.json({ success: false, message: 'A shop with that name already exists.'});
}
else {
return res.send(err);
}
}
else {
res.json({ message:'Shop created! '});
}
});
})
I do not receive errors of any kind, like I said everything just gets written into the database.
Thanks for the help.
Basically your writes haven't finished before the new entries are saved. You can read more about creating unique keys Here, but the gist is below. The solution is to create an index over the unique fields ahead of time.
When we declare a property to be unique, we’re actually declaring that we want a database-level index on that property. Some database abstraction layers will issue a query to see if a there’s another record with the same value for the unique property, and if that query comes back empty, it allows the save or update to proceed. If you trust this method, you either have incredibly low traffic or you’re about to learn about race conditions, because 2 or more requests could have their checks to the database occur before any writes go out, and you end up with non-unique data in your DB.
In between the time that check query is issued, another insert could come along doing the exact same thing, and you still end up with duplication. Uniqueness can’t be correctly validated at the application level. So it’s good that Mongoose tries to create an index for us.

Cannot delete json element

I have a node js function:
function func() {
USER.find({},function(err, users){
user = users[0];
console.log(user); // {"name":"mike", "age":15, "job":"engineer"}
user.name = "bob"; //{"name":"bob", "age":15, "job":"engineer"}
delete user.name;
console.log(user); // {"name":"mike", "age":15, "job":"engineer"} name still there??
});
}
Here USER is a mongoose data model and find is to query the mongodb. The callback provide an array of user if not err. The user data model looks like
{"name":"mike", "age":15, "job":"engineer"}.
So the callback is invoked and passed in users, I get the first user and trying to delete the "name" from user. The wired part is I can access the value correctly and modify the value. But if I 'delete user.name', this element is not deleted from json object user. Why is that?
As others have said, this is due to mongoose not giving you a plain object, but something enriched with things like save and modifiedPaths.
If you don't plan to save the user object later, you can also ask for lean document (plain js object, no mongoose stuff):
User.findOne({})
.lean()
.exec(function(err, user) {
delete user.name; // works
});
Alternatively, if you just want to fetch the user and pay it forward without some properties, you can also useselect, and maybe even combine it with lean:
User.findOne({})
.lean()
.select('email firstname')
.exec(function(err, user) {
console.log(user.name); // undefined
});
Not the best workaround, but... have you tried setting to undefined?
user.name = undefined;

Resources