Roles Based Access Control ( RBAC ) using PostgreSQL, Node Js and Express

Ali Ahmad Jan
5 min readOct 25, 2023

--

Access control is a critical aspect of application development. In many cases, you need to define and enforce user permissions and roles to ensure that your application’s data and resources are secure and accessible only to authorized users. Roles-Based Access Control (RBAC) is a popular approach to managing user access to different parts of an application. In this blog, we’ll explore how to implement RBAC using PostgreSQL, Node.js, and Express.

Understanding Roles-Based Access Control (RBAC)

RBAC is a model for managing user permissions within an application. It involves defining roles, assigning permissions to those roles, and then assigning users to specific roles. With RBAC, you can easily control who has access to what parts of your application, making it a robust and scalable solution for access control.

Setting up the Environment

Before implementing RBAC, you’ll need to set up your development environment. Here’s what you’ll need:

  1. Node.js and Express: Node.js is a JavaScript runtime, and Express is a popular web application framework for Node.js.
  2. PostgreSQL: A powerful open-source relational database system that will store information about roles, permissions, and users.
  3. Object-Relational Mapping (ORM): I have used ORM:sequelizefor database interactions

Connecting Your Application With PostgreSQL

Create a file db.config.js and set your details like this

module.exports = {
HOST: "localhost",
USER: "postgres",
PASSWORD: "12345",
DB: "Testing01",
dialect: "postgres",
port: 5000
};

Make sure to adjust the details according to your postgres using PgAdmin

Now, you have to configure these details to connect with PostgreSQL using sequelize

const dbConfig = require("../DB/db.config");

const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
port: dbConfig.port,
logging: false
});
sequelize
.authenticate()
.then(() => {
console.log("Connection to the database has been established successfully.");
})
.catch((err) => {
console.error("Unable to connect to the database:", err);
});
const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

db.roles = require("./roles.model.js")(sequelize, Sequelize);
db.users = require("./users.model.js")(sequelize, Sequelize);
db.permissions = require("./permissions.model")(sequelize,Sequelize);
db.user_roles = require("./users_roles.model")(sequelize, Sequelize);
db.roles_permissions = require("./roles_permissions")(sequelize, Sequelize);
sequelize.sync();

module.exports = db;

Creating Database

After establishing the connection with PostgreSQL, start by creating a PostgreSQL database that will store information about roles, permissions, and users. I have used sequelize as a ORM for PostgreSQL, so i have created my schemas using sequelize

Create roles.model.js for storing roles

module.exports = (sequelize, Sequelize) => {
const Roles = sequelize.define("roles", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull:false

},
description: {
type: Sequelize.STRING,
allowNull:true
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
});

return Roles;
};

Create permissions.model.js for storing permissons

module.exports = (sequelize, Sequelize) => {
const Permissions = sequelize.define("permissions", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},

name: {
type: Sequelize.STRING, // Define an array of strings
allowNull: false
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
});

return Permissions;
};

Create roles_permissions.model.js for storing the ids of roles and permissions

module.exports = (sequelize, Sequelize) => {
const RolesPermissions = sequelize.define("roles_permissions", {

role_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'roles', // This should match the name of the "users" table
key: 'id'
}
},
permission_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'permissions', // This should match the name of the "roles" table
key: 'id'
}
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
});

return RolesPermissions;
};

Create users.model.js for storing the users

module.exports = (sequelize, Sequelize) => {
const Users = sequelize.define("users", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull:false

},
email: {
type: Sequelize.STRING,
allowNull:false
},
password: {
type: Sequelize.STRING,
allowNull:false
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
});

return Users;
};

Create users_roles.modej.js for storing the ids of users and roles

module.exports = (sequelize, Sequelize) => {
const RolesUsers = sequelize.define("users_roles", {
user_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'users', // This should match the name of the "users" table
key: 'id'
}
},
role_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'roles', // This should match the name of the "roles" table
key: 'id'
}
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
});

return RolesUsers;
};

After establishing the connection and creating the schemas, you now just have to write code for managing roles with permissions and users with roles, the code will vary according to your requirements, however I will give an example of managing roles with permissions

const db = require("../Models");
const Roles = db.roles;
const Permissions = db.permissions;
const RolesPermissions = db.roles_permissions;

exports.editRole = (req, res) => {
// Validate request
if (!req.body.role_id || !req.body.permission_id) {
res.status(400).send({
message: "Please provide role_id, and permission_id"
});
return;
}

// Before assigning, let's ensure that the provided IDs actually exist in their respective tables.
Promise.all([
Roles.findByPk(req.body.role_id),
Permissions.findByPk(req.body.permission_id)
])
.then(([role, permission]) => {

if (!role) {
res.status(400).send({
message: "Role not found!"
});
return;
}

if (!permission) {
res.status(400).send({
message: "Permission not found!"
});
return;
}

// All entities exist, let's link them
let linkData = {

role_id: role.id,
permission_id: permission.id,
};

RolesPermissions.create(linkData)
.then(() => {
res.send({ message: "Permissions assigned successfully!" });
})
.catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while linking the entities."
});
});
})
.catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while checking the entities."
});
});
};

This is a PUTrequest in my case, so I tested with Postman

http://localhost:8080/api/roles/edit

and giving ids in the body of the request

{
"role_id": 2,
"permission_id" :1
}

{
"message": "Permissions assigned successfully!"
}

Similarly I managed/accessed users with roles.

Conclusion

Roles-Based Access Control (RBAC) is a powerful approach for managing user access in your application. By combining PostgreSQL, Node.js, and Express, you can create a robust RBAC system that ensures data security and controlled access to your resources. Properly implemented RBAC not only enhances security but also simplifies the management of user permissions as your application grows.

Remember that RBAC is just one piece of the puzzle in building a secure application. It’s crucial to keep your application and libraries up-to-date, implement other security measures, and regularly audit your RBAC system to maintain a robust and secure environment for your users.

--

--

Responses (1)