[fix] Use db permissions instead of jwt
This commit is contained in:
parent
25b9388f29
commit
881a15b2a4
@ -5,10 +5,9 @@ import { Service } from "typedi";
|
||||
import AuthService, { IUserJwtPayload, PROVIDER_OPENID } from "@Services/common/AuthService/AuthService";
|
||||
|
||||
import IdNotService from "@Services/common/IdNotService/IdNotService";
|
||||
import User, { RulesGroup } from "le-coffre-resources/dist/Admin";
|
||||
import User from "le-coffre-resources/dist/Admin";
|
||||
import UsersService from "@Services/super-admin/UsersService/UsersService";
|
||||
import SubscriptionsService from "@Services/admin/SubscriptionsService/SubscriptionsService.ts";
|
||||
import RulesGroupsService from "@Services/admin/RulesGroupsService/RulesGroupsService";
|
||||
|
||||
@Controller()
|
||||
@Service()
|
||||
@ -18,7 +17,6 @@ export default class UserController extends ApiController {
|
||||
private idNotService: IdNotService,
|
||||
private userService: UsersService,
|
||||
private subscriptionsService: SubscriptionsService,
|
||||
private rulesGroupsService: RulesGroupsService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -75,47 +73,18 @@ export default class UserController extends ApiController {
|
||||
this.httpUnauthorized(response, "Email not found");
|
||||
return;
|
||||
}
|
||||
let isSubscribed = await this.subscriptionsService.isUserSubscribed(user.uid, userHydrated.office_membership?.uid!);
|
||||
|
||||
//Check if user is whitelisted
|
||||
// const isWhitelisted = await this.whitelistService.getByEmail(userHydrated.contact!.email);
|
||||
|
||||
//When we'll switch to idNotId whitelisting
|
||||
// const isWhitelisted = await this.userWhitelistService.getByIdNotId(user.idNot);
|
||||
|
||||
//If not whitelisted, return 409 Not whitelisted
|
||||
// if (!isWhitelisted || isWhitelisted.length === 0) {
|
||||
// this.httpNotWhitelisted(response);
|
||||
// return;
|
||||
// }
|
||||
|
||||
await this.idNotService.updateOffice(user.office_uid);
|
||||
|
||||
const payload = await this.authService.getUserJwtPayload(user.idNot);
|
||||
console.log("payload", payload);
|
||||
|
||||
|
||||
if (!payload) {
|
||||
console.error("No payload");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSubscribed && (userHydrated.role?.name === "admin" || userHydrated.role?.name === "super-admin")) {
|
||||
|
||||
const manageSubscriptionRulesEntity = await this.rulesGroupsService.get({
|
||||
where: { uid: "94343601-04c8-44ef-afb9-3047597528a9" },
|
||||
include: { rules: true },
|
||||
});
|
||||
|
||||
const manageSubscriptionRules = RulesGroup.hydrateArray<RulesGroup>(manageSubscriptionRulesEntity, {
|
||||
strategy: "excludeAll",
|
||||
});
|
||||
if (!manageSubscriptionRules[0]) return;
|
||||
|
||||
payload.rules = manageSubscriptionRules[0].rules!.map((rule) => rule.name) || [];
|
||||
|
||||
isSubscribed = true;
|
||||
}
|
||||
// Check subscription status - but don't modify JWT rules since RulesHandler will fetch from DB
|
||||
let isSubscribed = await this.subscriptionsService.isUserSubscribed(user.uid, userHydrated.office_membership?.uid!);
|
||||
|
||||
if (!isSubscribed) {
|
||||
console.error("User not subscribed");
|
||||
@ -162,20 +131,11 @@ export default class UserController extends ApiController {
|
||||
)) as IUserJwtPayload;
|
||||
let isSubscribed = await this.subscriptionsService.isUserSubscribed(newUserPayload.userId, newUserPayload.office_Id);
|
||||
|
||||
if (!isSubscribed && (newUserPayload.role === "admin" || newUserPayload.role === "super-admin")) {
|
||||
const manageSubscriptionRulesEntity = await this.rulesGroupsService.get({
|
||||
where: { uid: "94343601-04c8-44ef-afb9-3047597528a9" },
|
||||
include: { rules: true },
|
||||
});
|
||||
|
||||
const manageSubscriptionRules = RulesGroup.hydrateArray<RulesGroup>(manageSubscriptionRulesEntity, {
|
||||
strategy: "excludeAll",
|
||||
});
|
||||
if (!manageSubscriptionRules[0]) return;
|
||||
|
||||
newUserPayload.rules = manageSubscriptionRules[0].rules!.map((rule) => rule.name) || [];
|
||||
|
||||
isSubscribed = true;
|
||||
// Check subscription status - but don't modify JWT rules since RulesHandler will fetch from DB
|
||||
if (!isSubscribed) {
|
||||
console.error("User not subscribed during token refresh");
|
||||
this.httpUnauthorized(response, "User not subscribed");
|
||||
return;
|
||||
}
|
||||
delete newUserPayload.iat;
|
||||
delete newUserPayload.exp;
|
||||
|
@ -1,34 +1,92 @@
|
||||
import HttpCodes from "@Common/system/controller-pattern/HttpCodes";
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import Container from "typedi";
|
||||
import UsersService from "@Services/super-admin/UsersService/UsersService";
|
||||
import subscriptionRulesHandler from "./SubscriptionRulesHandler";
|
||||
|
||||
export default async function ruleHandler(req: Request, response: Response, next: NextFunction) {
|
||||
try {
|
||||
const rules = req.body.user.rules;
|
||||
const userId = req.body.user.userId;
|
||||
const service = req.path && req.path.split("/")[4];
|
||||
const requiredRule = req.method + " " + service;
|
||||
|
||||
if (!rules) {
|
||||
response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized without rules");
|
||||
if (!userId) {
|
||||
response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized - no user ID");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch user rules directly from database instead of trusting JWT
|
||||
const usersService = Container.get(UsersService);
|
||||
const user = await usersService.getByUid(userId, {
|
||||
role: { include: { rules: true } },
|
||||
office_role: { include: { rules: true } }
|
||||
}) as any; // Type assertion for database result with includes
|
||||
|
||||
if (!user) {
|
||||
console.error(`User not found in database: ${userId}`);
|
||||
response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized - user not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build rules array from database
|
||||
const rules: string[] = [];
|
||||
|
||||
// Add role rules
|
||||
if (user.role && user.role.rules) {
|
||||
user.role.rules.forEach((rule: any) => {
|
||||
rules.push(rule.name);
|
||||
});
|
||||
}
|
||||
|
||||
// Add office role rules (if any)
|
||||
if (user.office_role && user.office_role.rules) {
|
||||
user.office_role.rules.forEach((rule: any) => {
|
||||
if (!rules.includes(rule.name)) {
|
||||
rules.push(rule.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user has required rule
|
||||
if (!rules.includes(requiredRule)) {
|
||||
console.error(`Rule check failed for user ${req.body.user.userId}:`);
|
||||
console.error(`Rule check failed for user ${userId}:`);
|
||||
console.error(` Required rule: "${requiredRule}"`);
|
||||
console.error(` User rules: [${rules.join(", ")}]`);
|
||||
console.error(` User role: ${user.role?.name}`);
|
||||
console.error(` User office role: ${user.office_role?.name}`);
|
||||
console.error(` Database rules: [${rules.join(", ")}]`);
|
||||
console.error(` Path: ${req.path}`);
|
||||
console.error(` Method: ${req.method}`);
|
||||
console.error(` Service: ${service}`);
|
||||
|
||||
response.status(HttpCodes.UNAUTHORIZED).send(
|
||||
`Unauthorized with those rules. Required: "${requiredRule}", Provided: [${rules.join(", ")}]`
|
||||
`Unauthorized. Required: "${requiredRule}", User has: [${rules.join(", ")}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the request with the fresh rules from database
|
||||
req.body.user.rules = rules;
|
||||
|
||||
console.log(`[RulesHandler] User ${userId} authorized for ${requiredRule}`);
|
||||
|
||||
// Apply subscription rules if needed (for admin/super-admin users)
|
||||
await subscriptionRulesHandler(req, response, () => {
|
||||
// Check if user has required rule after subscription rules are applied
|
||||
if (!req.body.user.rules.includes(requiredRule)) {
|
||||
console.error(`Rule check failed for user ${userId} after subscription rules:`);
|
||||
console.error(` Required rule: "${requiredRule}"`);
|
||||
console.error(` Final rules: [${req.body.user.rules.join(", ")}]`);
|
||||
|
||||
response.status(HttpCodes.UNAUTHORIZED).send(
|
||||
`Unauthorized. Required: "${requiredRule}", User has: [${req.body.user.rules.join(", ")}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error(`[RulesHandler] Error checking rules for user:`, error);
|
||||
response.status(HttpCodes.INTERNAL_ERROR).send("Internal server error");
|
||||
return;
|
||||
}
|
||||
|
63
src/app/middlewares/SubscriptionRulesHandler.ts
Normal file
63
src/app/middlewares/SubscriptionRulesHandler.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import Container from "typedi";
|
||||
import SubscriptionsService from "@Services/admin/SubscriptionsService/SubscriptionsService";
|
||||
import RulesGroupsService from "@Services/admin/RulesGroupsService/RulesGroupsService";
|
||||
import { RulesGroup } from "le-coffre-resources/dist/Admin";
|
||||
|
||||
/**
|
||||
* Middleware to add subscription management rules for admin/super-admin users
|
||||
* who aren't subscribed but need to manage subscriptions
|
||||
*/
|
||||
export default async function subscriptionRulesHandler(req: Request, response: Response, next: NextFunction) {
|
||||
try {
|
||||
const userId = req.body.user.userId;
|
||||
const userRole = req.body.user.role;
|
||||
const officeId = req.body.user.office_Id;
|
||||
|
||||
// Only apply to admin/super-admin users
|
||||
if (userRole !== "admin" && userRole !== "super-admin") {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Check if user is subscribed
|
||||
const subscriptionsService = Container.get(SubscriptionsService);
|
||||
const isSubscribed = await subscriptionsService.isUserSubscribed(userId, officeId);
|
||||
|
||||
// If subscribed, no need to add subscription management rules
|
||||
if (isSubscribed) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// User is admin/super-admin but not subscribed - add subscription management rules
|
||||
console.log(`[SubscriptionRulesHandler] Admin user ${userId} not subscribed, adding subscription management rules`);
|
||||
|
||||
const rulesGroupsService = Container.get(RulesGroupsService);
|
||||
const manageSubscriptionRulesEntity = await rulesGroupsService.get({
|
||||
where: { uid: "94343601-04c8-44ef-afb9-3047597528a9" },
|
||||
include: { rules: true },
|
||||
});
|
||||
|
||||
const manageSubscriptionRules = RulesGroup.hydrateArray<RulesGroup>(manageSubscriptionRulesEntity, {
|
||||
strategy: "excludeAll",
|
||||
});
|
||||
|
||||
if (!manageSubscriptionRules[0]) {
|
||||
console.error("[SubscriptionRulesHandler] Subscription rules group not found");
|
||||
return next();
|
||||
}
|
||||
|
||||
// Add subscription rules to existing rules
|
||||
const subscriptionRules = manageSubscriptionRules[0].rules!.map((rule) => rule.name) || [];
|
||||
console.log(`[SubscriptionRulesHandler] Adding subscription rules:`, subscriptionRules);
|
||||
console.log(`[SubscriptionRulesHandler] Original rules:`, req.body.user.rules);
|
||||
|
||||
req.body.user.rules = [...new Set([...req.body.user.rules, ...subscriptionRules])];
|
||||
console.log(`[SubscriptionRulesHandler] Final rules after adding subscription rules:`, req.body.user.rules);
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error(`[SubscriptionRulesHandler] Error adding subscription rules:`, error);
|
||||
// Don't fail the request, just continue without subscription rules
|
||||
next();
|
||||
}
|
||||
}
|
@ -814,14 +814,14 @@ export default async function main() {
|
||||
label: "Notaire",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
rules: [],
|
||||
rules: globalRules, // Fixed: was empty array
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
label: "Utilisateur",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
rules: [],
|
||||
rules: globalRules, // Fixed: was empty array
|
||||
},
|
||||
];
|
||||
|
||||
@ -858,7 +858,7 @@ export default async function main() {
|
||||
contact: contacts[2],
|
||||
office_membership: offices[0],
|
||||
role: roles[2],
|
||||
office_role: officeRoles[2],
|
||||
office_role: officeRoles[0], // Fixed: was officeRoles[2]
|
||||
},
|
||||
{
|
||||
created_at: new Date(),
|
||||
@ -867,7 +867,7 @@ export default async function main() {
|
||||
contact: contacts[3],
|
||||
office_membership: offices[0],
|
||||
role: roles[1],
|
||||
office_role: officeRoles[3],
|
||||
office_role: officeRoles[1], // Fixed: was officeRoles[3]
|
||||
},
|
||||
{
|
||||
created_at: new Date(),
|
||||
|
@ -33,7 +33,7 @@ export interface IUserJwtPayload {
|
||||
};
|
||||
office_Id: string;
|
||||
role: string;
|
||||
rules: string[];
|
||||
rules: string[]; // Kept for backward compatibility, but will be empty in JWT
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
}
|
||||
@ -62,26 +62,14 @@ export default class AuthService extends BaseService {
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const rules: string[] = [];
|
||||
|
||||
user.role.rules.forEach((rule) => {
|
||||
rules.push(rule.name);
|
||||
});
|
||||
|
||||
if (user.office_role) {
|
||||
user.office_role.rules.forEach((rule) => {
|
||||
if (!rules.includes(rule.name)) {
|
||||
rules.push(rule.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Since RulesHandler now fetches rules from database, we can simplify JWT payload
|
||||
// Rules are no longer included in JWT for security reasons
|
||||
return {
|
||||
userId: user.uid,
|
||||
openId: { providerName: providerName, userId: user.idNot },
|
||||
office_Id: user.office_membership.uid,
|
||||
role: user.role.name,
|
||||
rules: rules,
|
||||
rules: [], // Rules will be fetched fresh from database by RulesHandler
|
||||
};
|
||||
}
|
||||
public generateAccessToken(user: any): string {
|
||||
|
Loading…
x
Reference in New Issue
Block a user