WIP: add user role check optimization to user principal check, update type comparisons

This commit is contained in:
Danny Avila 2025-08-03 21:53:06 -04:00
parent 89f0a4e02f
commit ecd7bf0d51
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
19 changed files with 481 additions and 71 deletions

View file

@ -53,7 +53,9 @@ describe('Role-based Permissions Integration', () => {
role: 'admin',
});
const principals = await methods.getUserPrincipals(adminUser._id as mongoose.Types.ObjectId);
const principals = await methods.getUserPrincipals({
userId: adminUser._id as mongoose.Types.ObjectId,
});
// Should have user, role, and public principals
expect(principals).toHaveLength(3);
@ -84,9 +86,9 @@ describe('Role-based Permissions Integration', () => {
role: null, // Explicitly set to null to override default
});
const principals = await methods.getUserPrincipals(
regularUser._id as mongoose.Types.ObjectId,
);
const principals = await methods.getUserPrincipals({
userId: regularUser._id as mongoose.Types.ObjectId,
});
// Should only have user and public principals
expect(principals).toHaveLength(2);
@ -116,7 +118,9 @@ describe('Role-based Permissions Integration', () => {
memberIds: [(user._id as mongoose.Types.ObjectId).toString()],
});
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
const principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
// Should have user, role, 2 groups, and public
expect(principals).toHaveLength(5);
@ -156,7 +160,9 @@ describe('Role-based Permissions Integration', () => {
role: testCase.role,
});
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
const principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
expect(rolePrincipal).toBeDefined();
@ -292,7 +298,9 @@ describe('Role-based Permissions Integration', () => {
});
// Initial principals
let principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
let principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
let rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
expect(rolePrincipal?.principalId).toBe('viewer');
@ -301,7 +309,9 @@ describe('Role-based Permissions Integration', () => {
await user.save();
// Get principals again
principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
expect(rolePrincipal?.principalId).toBe('editor');
});
@ -315,7 +325,9 @@ describe('Role-based Permissions Integration', () => {
});
// Initial check
let principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
let principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
expect(principals).toHaveLength(3); // user, role, public
// Remove role
@ -323,7 +335,9 @@ describe('Role-based Permissions Integration', () => {
await user.save();
// Check again
principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
expect(principals).toHaveLength(2); // user, public
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
expect(rolePrincipal).toBeUndefined();
@ -352,7 +366,9 @@ describe('Role-based Permissions Integration', () => {
const user = await User.create(userData);
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
const principals = await methods.getUserPrincipals({
userId: user._id as mongoose.Types.ObjectId,
});
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
if (testCase.expected) {

View file

@ -325,7 +325,9 @@ describe('User Group Methods Tests', () => {
);
/** Get user principals */
const principals = await methods.getUserPrincipals(testUser1._id as mongoose.Types.ObjectId);
const principals = await methods.getUserPrincipals({
userId: testUser1._id as mongoose.Types.ObjectId,
});
/** Should include user, role (default USER), group, and public principals */
expect(principals).toHaveLength(4);
@ -349,7 +351,9 @@ describe('User Group Methods Tests', () => {
test('should return user and public principals for non-existent user in getUserPrincipals', async () => {
const nonExistentId = new mongoose.Types.ObjectId();
const principals = await methods.getUserPrincipals(nonExistentId);
const principals = await methods.getUserPrincipals({
userId: nonExistentId,
});
/** Should still return user and public principals even for non-existent user */
expect(principals).toHaveLength(2);

View file

@ -237,35 +237,45 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
/**
* Get a list of all principal identifiers for a user (user ID + group IDs + public)
* For use in permission checks
* @param userId - The user ID
* @param params - Parameters object
* @param params.userId - The user ID
* @param params.role - Optional user role (if not provided, will query from DB)
* @param session - Optional MongoDB session for transactions
* @returns Array of principal objects with type and id
*/
async function getUserPrincipals(
userId: string | Types.ObjectId,
params: {
userId: string | Types.ObjectId;
role?: string | null;
},
session?: ClientSession,
): Promise<Array<{ principalType: string; principalId?: string | Types.ObjectId }>> {
const { userId, role } = params;
const principals: Array<{ principalType: string; principalId?: string | Types.ObjectId }> = [
{ principalType: PrincipalType.USER, principalId: userId },
];
// Get user to check their role
const User = mongoose.models.User as Model<IUser>;
const query = User.findById(userId).select('role');
if (session) {
query.session(session);
// If role is not provided, query user to get it
let userRole = role;
if (userRole === undefined) {
const User = mongoose.models.User as Model<IUser>;
const query = User.findById(userId).select('role');
if (session) {
query.session(session);
}
const user = await query.lean();
userRole = user?.role;
}
const user = await query.lean();
// Add role as a principal if user has one
if (user?.role && user.role.trim()) {
principals.push({ principalType: PrincipalType.ROLE, principalId: user.role });
if (userRole && userRole.trim()) {
principals.push({ principalType: PrincipalType.ROLE, principalId: userRole });
}
const userGroups = await getUserGroups(userId, session);
if (userGroups && userGroups.length > 0) {
userGroups.forEach((group) => {
principals.push({ principalType: PrincipalType.GROUP, principalId: group._id.toString() });
principals.push({ principalType: PrincipalType.GROUP, principalId: group._id });
});
}