Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
91b2944
feat: implement feature-role mapping and associated queries for organ…
priyanka-TL Oct 3, 2025
b2f986b
Remove commented-out association for UserRole in FeatureRoleMapping m…
priyanka-TL Oct 3, 2025
5e919bd
Implement transaction handling in organizationFeatureHelper for creat…
priyanka-TL Oct 4, 2025
81b1915
add(role): migration to create tenant_admin role and assign permissions
nevil-mathew Oct 22, 2025
25f2b4c
feat(auth): add tenant_admin role and update permission checks in aut…
nevil-mathew Oct 22, 2025
0862663
fix(auth): update error messages for tenant admin organization checks
nevil-mathew Oct 22, 2025
d23f99d
fix(auth): rename organization variable to overrideOrg for clarity in…
nevil-mathew Oct 22, 2025
4f8a947
fix(locales): correct formatting of organization code header message …
nevil-mathew Oct 22, 2025
a91bd0a
Merge branch 'develop' of https://github.com/ELEVATE-Project/user int…
priyanka-TL Oct 23, 2025
8cdc839
fix(auth): update role access validation to include tenant_admin in n…
nevil-mathew Oct 23, 2025
7ea3863
fix(account): include tenant_code in role query for accurate role ret…
nevil-mathew Oct 23, 2025
81668c2
Refactor migrations to check for existing features and roles before i…
priyanka-TL Oct 23, 2025
899e99b
feat(admin): add assignRole method for user role assignment with toke…
nevil-mathew Oct 24, 2025
c47f705
fix(admin): handle UniqueConstraintError in user role assignment
nevil-mathew Oct 24, 2025
2cde904
enhance: email template functions to include tenant and organization …
nevil-mathew Oct 24, 2025
6a13a69
fix(admin): streamline user session termination process in deleteUser…
nevil-mathew Oct 24, 2025
10cab5f
fix(admin): add error handling for post-assignment operations in user…
nevil-mathew Oct 24, 2025
33a2150
fix(admin): correct user ID assignment in user organization role crea…
nevil-mathew Oct 24, 2025
0ff1979
Merge pull request #852 from ELEVATE-Project/notification-temp-fix
nevil-mathew Oct 24, 2025
4ad7872
Add tenant_admin role and update feature-role mapping with created_by…
priyanka-TL Oct 24, 2025
20c1890
Refactor role-feature mapping logic to improve efficiency and reduce …
priyanka-TL Oct 24, 2025
5ceaf63
Update feature-role mapping to enforce non-null constraints and strea…
priyanka-TL Oct 24, 2025
d9246dd
organization miss fix
adithyadinesh0412 Oct 27, 2025
061648d
Add transaction rollback on role validation failure and restrict orga…
priyanka-TL Oct 27, 2025
4104cdc
feat: add database connection pool and timeout configurations
nevil-mathew Oct 27, 2025
65f98c4
fix: update default values for database connection pool settings
nevil-mathew Oct 27, 2025
0629c7e
fix: update default application name in environment variables
nevil-mathew Oct 27, 2025
04df57a
Merge pull request #854 from ELEVATE-Project/database-connection-pool
nevil-mathew Oct 27, 2025
75278b6
Merge pull request #853 from adithyadinesh0412/org-migration-bulk
nevil-mathew Oct 27, 2025
1047843
Merge pull request #851 from ELEVATE-Project/add-tenant-admin-role
nevil-mathew Oct 27, 2025
a6cd6da
Merge pull request #836 from ELEVATE-Project/featureRoleMap
nevil-mathew Oct 27, 2025
4d1b2c9
fix: improve error handling in database initialization and seeding sc…
nevil-mathew Oct 27, 2025
865d7ff
fix: correct syntax in database initialization and seeding scripts
nevil-mathew Oct 27, 2025
04aee75
Merge pull request #856 from ELEVATE-Project/fix-package-issue
nevil-mathew Oct 27, 2025
4bcd72a
Merge branch 'staging' of https://github.com/ELEVATE-Project/user int…
nevil-mathew Oct 27, 2025
681e22c
feat(admin): add assignRole method for validating user and role IDs
nevil-mathew Oct 27, 2025
03bc059
fix(admin): remove organization_id validation from assignRole method
nevil-mathew Oct 27, 2025
c05e2df
Merge pull request #857 from ELEVATE-Project/add-tenant-admin-role
nevil-mathew Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/configs/postgres.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ module.exports = {
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
},
pool: {
max: parseInt(process.env.DB_POOL_MAX),
min: parseInt(process.env.DB_POOL_MIN),
acquire: parseInt(process.env.DB_POOL_ACQUIRE_MS),
idle: parseInt(process.env.DB_POOL_IDLE_MS),
evict: parseInt(process.env.DB_POOL_EVICT_MS),
},
dialectOptions: {
application_name: process.env.APP_NAME,
keepAlive: true,
statement_timeout: parseInt(process.env.PG_STATEMENT_TIMEOUT_MS),
idle_in_transaction_session_timeout: parseInt(process.env.PG_IDLE_TX_TIMEOUT_MS),
},
//logging: false,
defaultOrgId: parseInt(process.env.DEFAULT_ORG_ID),
},
Expand All @@ -37,6 +50,19 @@ module.exports = {
production: {
url: process.env.DATABASE_URL,
dialect: 'postgres',
pool: {
max: parseInt(process.env.DB_POOL_MAX),
min: parseInt(process.env.DB_POOL_MIN),
acquire: parseInt(process.env.DB_POOL_ACQUIRE_MS),
idle: parseInt(process.env.DB_POOL_IDLE_MS),
evict: parseInt(process.env.DB_POOL_EVICT_MS),
},
dialectOptions: {
application_name: process.env.APP_NAME,
keepAlive: true,
statement_timeout: parseInt(process.env.PG_STATEMENT_TIMEOUT_MS),
idle_in_transaction_session_timeout: parseInt(process.env.PG_IDLE_TX_TIMEOUT_MS),
},
defaultOrgId: parseInt(process.env.DEFAULT_ORG_ID),
},
}
2 changes: 2 additions & 0 deletions src/constants/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ module.exports = {
refreshTokenLimit: 3,
otpExpirationTime: process.env.OTP_EXP_TIME, // In Seconds,
ADMIN_ROLE: 'admin',
TENANT_ADMIN_ROLE: 'tenant_admin',
ORG_ADMIN_ROLE: 'org_admin',
TENANT_ADMIN_ROLE: 'tenant_admin',
USER_ROLE: 'user',
SESSION_MANAGER_ROLE: 'session_manager',
PUBLIC_ROLE: 'public',
Expand Down
43 changes: 43 additions & 0 deletions src/controllers/v1/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,49 @@ module.exports = class Admin {
}
}

/**
* Assigns a role to a user.
*
* Extracts `organization_code` and `tenant_code` from `req.decodedToken`
* instead of the request body and delegates the actual role assignment
* to the service layer. Performs only an admin-access check.
*
* @async
* @function assignRole
* @param {import('express').Request} req - Express request object
* @param {Object} req.decodedToken - Decoded JWT payload
* @param {string} req.decodedToken.organization_code - Organization code from token
* @param {string} req.decodedToken.tenant_code - Tenant code from token
* @param {Object} req.body - Request body containing assignment data
* @param {number|string} req.body.user_id - Target user ID
* @param {number|string} req.body.role_id - Role ID to assign
* @param {number|string} [req.body.organization_id] - Optional organization ID
* @returns {Promise<Object>} Service response object
*/

async assignRole(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ADMIN_ROLE)) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
responseCode: 'CLIENT_ERROR',
})
}

const params = {
organization_code: req.decodedToken.organization_code,
tenant_code: req.decodedToken.tenant_code,
}

// Pass token-derived params separately per new service pattern
const user = await adminService.assignRole(params, req.body)
return user
} catch (error) {
return error
}
}

/**
* create admin users
* @method
Expand Down
8 changes: 7 additions & 1 deletion src/controllers/v1/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ module.exports = class NotificationTemplate {

async template(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, [common.ADMIN_ROLE, common.ORG_ADMIN_ROLE])) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ADMIN_ROLE,
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand Down
49 changes: 42 additions & 7 deletions src/controllers/v1/org-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ module.exports = class OrgAdmin {
*/
async bulkUserCreate(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand All @@ -48,7 +53,12 @@ module.exports = class OrgAdmin {
*/
async getBulkInvitesFilesList(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand All @@ -72,7 +82,12 @@ module.exports = class OrgAdmin {
*/
async getRequestDetails(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand Down Expand Up @@ -102,7 +117,12 @@ module.exports = class OrgAdmin {
*/
async getRequests(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand All @@ -129,7 +149,12 @@ module.exports = class OrgAdmin {
*/
async updateRequestStatus(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand All @@ -153,7 +178,12 @@ module.exports = class OrgAdmin {
*/
async deactivateUser(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand All @@ -178,7 +208,12 @@ module.exports = class OrgAdmin {

async inheritEntityType(req) {
try {
if (!utilsHelper.validateRoleAccess(req.decodedToken.roles, common.ORG_ADMIN_ROLE)) {
if (
!utilsHelper.validateRoleAccess(req.decodedToken.roles, [
common.ORG_ADMIN_ROLE,
common.TENANT_ADMIN_ROLE,
])
) {
throw responses.failureResponse({
message: 'USER_IS_NOT_A_ADMIN',
statusCode: httpStatusCode.bad_request,
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/v1/organization-feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ module.exports = class OrganizationFeature {
)
: await organizationFeatureService.list(
req.decodedToken.tenant_code,
req.decodedToken.organization_code
req.decodedToken.organization_code,
req.decodedToken.roles
)
} catch (error) {
return error
Expand Down
9 changes: 5 additions & 4 deletions src/controllers/v1/organization.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.exports = class Organization {
let isAdmin = false
const roles = req.decodedToken.roles
if (roles && roles.length > 0) {
isAdmin = utilsHelper.validateRoleAccess(roles, common.ADMIN_ROLE)
isAdmin = utilsHelper.validateRoleAccess(roles, [common.ADMIN_ROLE, common.TENANT_ADMIN_ROLE])
}

if (!isAdmin) {
Expand Down Expand Up @@ -68,7 +68,7 @@ module.exports = class Organization {

if (roles && roles.length > 0) {
isAdmin = utilsHelper.validateRoleAccess(roles, common.ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, common.ORG_ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, [common.ORG_ADMIN_ROLE, common.TENANT_ADMIN_ROLE])
}

if (req.params.id != req.decodedToken.organization_id && isOrgAdmin) {
Expand Down Expand Up @@ -170,6 +170,7 @@ module.exports = class Organization {
isAdmin =
utilsHelper.validateRoleAccess(roles, common.ADMIN_ROLE) ||
utilsHelper.validateRoleAccess(roles, common.ORG_ADMIN_ROLE) ||
utilsHelper.validateRoleAccess(roles, common.TENANT_ADMIN_ROLE) ||
false
}
const result = await orgService.details(
Expand Down Expand Up @@ -215,7 +216,7 @@ module.exports = class Organization {

if (roles && roles.length > 0) {
isAdmin = utilsHelper.validateRoleAccess(roles, common.ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, common.ORG_ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, [common.ORG_ADMIN_ROLE, common.TENANT_ADMIN_ROLE])
}
if (!isAdmin && !isOrgAdmin) {
throw responses.failureResponse({
Expand Down Expand Up @@ -260,7 +261,7 @@ module.exports = class Organization {

if (roles && roles.length > 0) {
isAdmin = utilsHelper.validateRoleAccess(roles, common.ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, common.ORG_ADMIN_ROLE)
isOrgAdmin = utilsHelper.validateRoleAccess(roles, [common.ORG_ADMIN_ROLE, common.TENANT_ADMIN_ROLE])
}
if (!isAdmin && !isOrgAdmin) {
throw responses.failureResponse({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict'

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('feature_role_mapping', {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
},
feature_code: {
type: Sequelize.STRING,
allowNull: false,
},
role_title: {
type: Sequelize.STRING,
allowNull: false,
},
organization_code: {
type: Sequelize.STRING,
allowNull: false,
},
tenant_code: {
type: Sequelize.STRING,
allowNull: false,
},
created_by: {
type: Sequelize.INTEGER,
allowNull: false,
},
updated_by: {
type: Sequelize.INTEGER,
allowNull: true,
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
},
deleted_at: {
allowNull: true,
type: Sequelize.DATE,
},
})

// Add composite primary key
await queryInterface.addConstraint('feature_role_mapping', {
fields: ['id', 'tenant_code'],
type: 'primary key',
name: 'pk_feature_role_mapping',
})
},

async down(queryInterface) {
await queryInterface.dropTable('feature_role_mapping')
},
}
Loading