Skip to content

Commit

Permalink
fix: support user.id placeholder for conditions in rules (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
sleidig authored Feb 11, 2025
1 parent 1090b71 commit 9920623
Show file tree
Hide file tree
Showing 13 changed files with 49 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/auth/cookie/cookie.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('CookieService', () => {
});

it('should set a cookie containing the user info on the response', () => {
const user = new UserInfo('Username', ['user_app']);
const user = new UserInfo('user-id', 'Username', ['user_app']);
const request = { user: user };
const cookies = {};
const setCookieMock = jest.fn(
Expand Down
4 changes: 2 additions & 2 deletions src/auth/guards/combined-auth/combined-auth.guard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('CombinedAuthGuard', () => {

it('[MIDDLEWARE/GUARD] should assign user to sentry after a successful authentication', async () => {
jest.spyOn(basicAuthGuard, 'canActivate').mockResolvedValue(true);
const user = new UserInfo('testUser', []);
const user = new UserInfo('user-id', 'testUser', []);
const setUserSpy = jest.spyOn(Sentry, 'setUser');

await guard.use({ user } as any, undefined, () => undefined);
Expand All @@ -72,7 +72,7 @@ describe('CombinedAuthGuard', () => {
it('[MIDDLEWARE] should call next if authentication passes', async () => {
jest.spyOn(jwtCookieGuard, 'canActivate').mockResolvedValue(true);
let funCalled = false;
const user = new UserInfo('testUser', []);
const user = new UserInfo('user-id', 'testUser', []);

await guard.use({ user } as any, undefined, () => (funCalled = true));

Expand Down
1 change: 1 addition & 0 deletions src/auth/guards/jwt-bearer/jwt-bearer.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class JwtBearerStrategy extends PassportStrategy(
).catch(() => {});

return new UserInfo(
data.sub,
data.username,
data['_couchdb.roles'],
user && user['projects'] ? user['projects'] : [],
Expand Down
1 change: 1 addition & 0 deletions src/auth/guards/jwt-cookie/jwt-cookie-strategy.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class JwtCookieStrategy extends PassportStrategy(
).catch(() => {});

return new UserInfo(
data.sub,
data.name,
data.sub,
user && user['projects'] ? user['projects'] : [],
Expand Down
2 changes: 1 addition & 1 deletion src/permissions/permission/permission.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('PermissionService', () => {

service = module.get<PermissionService>(PermissionService);

normalUser = new UserInfo('normalUser', ['user_app']);
normalUser = new UserInfo('user-id', 'normalUser', ['user_app']);
});

it('should be defined', () => {
Expand Down
16 changes: 11 additions & 5 deletions src/permissions/rules/rules.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ describe('RulesService', () => {
let testPermission: Permission;
let changesResponse: ChangesResponse;

const normalUser = new UserInfo('normalUser', ['user_app']);
const adminUser = new UserInfo('superUser', ['user_app', 'admin_app']);
const normalUser = new UserInfo('user-normal', 'normalUser', ['user_app']);
const adminUser = new UserInfo('user-super', 'superUser', [
'user_app',
'admin_app',
]);
const DATABASE_NAME = 'app';

beforeEach(async () => {
Expand Down Expand Up @@ -131,7 +134,10 @@ describe('RulesService', () => {

it('should not fail if no rules exist for a given role', () => {
const result = service.getRulesForUser(
new UserInfo('specialUser', ['user_app', 'non_existing_role']),
new UserInfo('user-special', 'specialUser', [
'user_app',
'non_existing_role',
]),
);
expect(result).toEqual(userRules);
});
Expand All @@ -152,7 +158,7 @@ describe('RulesService', () => {
{
subject: 'User',
action: 'read',
conditions: { name: '${user.name}' },
conditions: { name: '${user.name}', id: '${user.id}' },
},
];

Expand All @@ -162,7 +168,7 @@ describe('RulesService', () => {
{
subject: 'User',
action: 'read',
conditions: { name: normalUser.name },
conditions: { name: normalUser.name, id: normalUser.id },
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ describe('AttachmentController', () => {
let mockCouchDB: CouchdbService;
let mockPermissions: PermissionService;

let user: UserInfo;

beforeEach(async () => {
user = new UserInfo('user-id', 'user', []);

mockCouchDB = {
get: () => of(undefined),
delete: () => of(undefined),
Expand Down Expand Up @@ -68,7 +72,7 @@ describe('AttachmentController', () => {
'docId',
'prop',
{ rev: '1' },
new UserInfo('user', []),
user,
undefined,
undefined,
),
Expand All @@ -89,7 +93,7 @@ describe('AttachmentController', () => {
'docId',
'prop',
{ rev: '1' },
new UserInfo('user', []),
user,
undefined,
undefined,
);
Expand All @@ -110,7 +114,7 @@ describe('AttachmentController', () => {
'db',
'docId',
'prop',
new UserInfo('user', []),
user,
undefined,
undefined,
),
Expand All @@ -130,7 +134,7 @@ describe('AttachmentController', () => {
'db',
'docId',
'prop',
new UserInfo('user', []),
user,
undefined,
undefined,
);
Expand All @@ -150,7 +154,7 @@ describe('AttachmentController', () => {
'docId',
'prop',
{ rev: '1-rev' },
new UserInfo('user', []),
user,
),
).rejects.toThrow(ForbiddenException);
});
Expand All @@ -168,7 +172,7 @@ describe('AttachmentController', () => {
'docId',
'prop',
{ rev: '1-rev' },
new UserInfo('user', []),
user,
);

expect(mockCouchDB.delete).toHaveBeenCalledWith('db', 'docId/prop', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('DocumentController', () => {
roles: [],
type: 'user',
};
const requestingUser: UserInfo = new UserInfo('testUser', []);
const requestingUser: UserInfo = new UserInfo('user-id', 'testUser', []);
const SUCCESS_RESPONSE: DocSuccess = {
ok: true,
id: userDoc._id,
Expand Down Expand Up @@ -329,6 +329,7 @@ describe('DocumentController', () => {

it('should throw exception if the update permission is not given', async () => {
const otherUser: UserInfo = {
id: 'user-other',
name: 'anotherUser',
roles: [],
projects: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('BulkDocEndpointsController', () => {
jest
.spyOn(documentFilter, 'filterBulkGetResponse')
.mockReturnValue(filteredResponse);
const user = new UserInfo('username', ['user']);
const user = new UserInfo('user-id', 'username', ['user']);

const result = await firstValueFrom(
controller.bulkGetPost(null, null, null, user),
Expand Down Expand Up @@ -107,7 +107,7 @@ describe('BulkDocEndpointsController', () => {
jest
.spyOn(documentFilter, 'filterAllDocsResponse')
.mockReturnValue(filteredResponse);
const user = new UserInfo('username', ['user']);
const user = new UserInfo('user-id', 'username', ['user']);

const result = await firstValueFrom(
controller.allDocs('db', null, user, null),
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('BulkDocEndpointsController', () => {
jest
.spyOn(documentFilter, 'filterBulkDocsRequest')
.mockReturnValue(Promise.resolve(filteredRequest));
const user = new UserInfo('username', ['admin']);
const user = new UserInfo('user-id', 'username', ['admin']);

await firstValueFrom(controller.bulkDocs('db', request, user));

Expand Down Expand Up @@ -197,7 +197,7 @@ describe('BulkDocEndpointsController', () => {
jest
.spyOn(documentFilter, 'filterFindResponse')
.mockReturnValue(filteredResponse);
const user = new UserInfo('username', ['admin']);
const user = new UserInfo('user-id', 'username', ['admin']);

await firstValueFrom(controller.find('db', request, user));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('BulkDocumentService', () => {
mockCouchDBService = {
post: () => of({}),
} as any;
normalUser = new UserInfo('normalUser', ['user']);
normalUser = new UserInfo('user-id', 'normalUser', ['user']);
schoolDoc = getSchoolDoc();
childDoc = getChildDoc();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ describe('ChangesController', () => {
childDoc,
deletedChildDoc,
]);
const user: UserInfo = { name: 'username', roles: [], projects: [] };
const user: UserInfo = {
id: 'user-id',
name: 'username',
roles: [],
projects: [],
};
const mockCouchdbService = { get: () => undefined } as CouchdbService;
const getSpy = jest.spyOn(mockCouchdbService, 'get');
const mockRulesService = {
Expand Down
4 changes: 2 additions & 2 deletions src/restricted-endpoints/session/session.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ describe('SessionController', () => {
});

it('should return the user object on the request', () => {
const user = new UserInfo('user', ['user_app']);
const user = new UserInfo('user-id', 'user', ['user_app']);

const response = controller.login(user);

expect(response).toBe(user);
});

it('should return user object from combinedAuth middleware if user is authenticated', async () => {
const user = new UserInfo('user', ['user_app']);
const user = new UserInfo('user-id', 'user', ['user_app']);

const res = await controller.session(user);

Expand Down
7 changes: 7 additions & 0 deletions src/restricted-endpoints/session/user-auth.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ export class UserCredentials {
* User object as used by CouchDB
*/
export class UserInfo {
/**
* @param id The account id (Keycloak user id)
* @param name The entityId of the user profile (e.g. User:123 -> CouchDB)
* @param roles The roles the user has (via the Keycloak user)
* @param projects The projects the user is linked to (via the user profile entity)
*/
constructor(
public id: string,
public name: string,
public roles: string[],
public projects: string[] = [],
Expand Down

0 comments on commit 9920623

Please sign in to comment.