-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into N21-1293-remove-groups-from-course
- Loading branch information
Showing
28 changed files
with
853 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
210 changes: 210 additions & 0 deletions
210
apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import { createMock, DeepMocked } from '@golevelup/ts-jest'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { Permission, Role, SchoolEntity, User } from '@shared/domain'; | ||
import { groupFactory, roleFactory, schoolFactory, setupEntities, userFactory } from '@shared/testing'; | ||
import { Action, AuthorizationContext, AuthorizationHelper } from '@src/modules/authorization'; | ||
import { Group } from '@src/modules/group'; | ||
import { ObjectId } from 'bson'; | ||
import { GroupRule } from './group.rule'; | ||
|
||
describe('GroupRule', () => { | ||
let module: TestingModule; | ||
let rule: GroupRule; | ||
|
||
let authorizationHelper: DeepMocked<AuthorizationHelper>; | ||
|
||
beforeAll(async () => { | ||
await setupEntities(); | ||
|
||
module = await Test.createTestingModule({ | ||
providers: [ | ||
GroupRule, | ||
{ | ||
provide: AuthorizationHelper, | ||
useValue: createMock<AuthorizationHelper>(), | ||
}, | ||
], | ||
}).compile(); | ||
|
||
rule = module.get(GroupRule); | ||
authorizationHelper = module.get(AuthorizationHelper); | ||
}); | ||
|
||
afterAll(async () => { | ||
await module.close(); | ||
}); | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('isApplicable', () => { | ||
describe('when the entity is applicable', () => { | ||
const setup = () => { | ||
const role: Role = roleFactory.buildWithId(); | ||
const user: User = userFactory.buildWithId({ roles: [role] }); | ||
const group: Group = groupFactory.build({ | ||
users: [ | ||
{ | ||
userId: user.id, | ||
roleId: user.roles[0].id, | ||
}, | ||
], | ||
}); | ||
|
||
return { | ||
user, | ||
group, | ||
}; | ||
}; | ||
|
||
it('should return true', () => { | ||
const { user, group } = setup(); | ||
|
||
const result = rule.isApplicable(user, group); | ||
|
||
expect(result).toEqual(true); | ||
}); | ||
}); | ||
|
||
describe('when the entity is not applicable', () => { | ||
const setup = () => { | ||
const role: Role = roleFactory.buildWithId(); | ||
const userNotInGroup: User = userFactory.buildWithId({ roles: [role] }); | ||
|
||
return { | ||
userNotInGroup, | ||
}; | ||
}; | ||
|
||
it('should return false', () => { | ||
const { userNotInGroup } = setup(); | ||
|
||
const result = rule.isApplicable(userNotInGroup, {} as unknown as Group); | ||
|
||
expect(result).toEqual(false); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('hasPermission', () => { | ||
describe('when the user has all required permissions and is at the same school then the group', () => { | ||
const setup = () => { | ||
const role: Role = roleFactory.buildWithId(); | ||
const school: SchoolEntity = schoolFactory.buildWithId(); | ||
const user: User = userFactory.buildWithId({ school, roles: [role] }); | ||
const group: Group = groupFactory.build({ | ||
users: [ | ||
{ | ||
userId: user.id, | ||
roleId: user.roles[0].id, | ||
}, | ||
], | ||
organizationId: user.school.id, | ||
}); | ||
const context: AuthorizationContext = { | ||
action: Action.write, | ||
requiredPermissions: [Permission.GROUP_VIEW], | ||
}; | ||
|
||
authorizationHelper.hasAllPermissions.mockReturnValue(true); | ||
|
||
return { | ||
user, | ||
group, | ||
context, | ||
}; | ||
}; | ||
|
||
it('should check all permissions', () => { | ||
const { user, group, context } = setup(); | ||
|
||
rule.hasPermission(user, group, context); | ||
|
||
expect(authorizationHelper.hasAllPermissions).toHaveBeenCalledWith(user, context.requiredPermissions); | ||
}); | ||
|
||
it('should return true', () => { | ||
const { user, group, context } = setup(); | ||
|
||
const result = rule.hasPermission(user, group, context); | ||
|
||
expect(result).toEqual(true); | ||
}); | ||
}); | ||
|
||
describe('when the user has not the required permission', () => { | ||
const setup = () => { | ||
const role: Role = roleFactory.buildWithId({ permissions: [] }); | ||
const school: SchoolEntity = schoolFactory.buildWithId(); | ||
const user: User = userFactory.buildWithId({ school, roles: [role] }); | ||
const group: Group = groupFactory.build({ | ||
users: [ | ||
{ | ||
userId: user.id, | ||
roleId: user.roles[0].id, | ||
}, | ||
], | ||
organizationId: user.school.id, | ||
}); | ||
const context: AuthorizationContext = { | ||
action: Action.write, | ||
requiredPermissions: [Permission.GROUP_VIEW], | ||
}; | ||
|
||
authorizationHelper.hasAllPermissions.mockReturnValue(false); | ||
|
||
return { | ||
user, | ||
group, | ||
context, | ||
}; | ||
}; | ||
|
||
it('should return false', () => { | ||
const { user, group, context } = setup(); | ||
|
||
const result = rule.hasPermission(user, group, context); | ||
|
||
expect(result).toEqual(false); | ||
}); | ||
}); | ||
|
||
describe('when the user is at another school then the group', () => { | ||
const setup = () => { | ||
const role: Role = roleFactory.buildWithId({ permissions: [] }); | ||
const school: SchoolEntity = schoolFactory.buildWithId(); | ||
const user: User = userFactory.buildWithId({ school, roles: [role] }); | ||
const group: Group = groupFactory.build({ | ||
users: [ | ||
{ | ||
userId: user.id, | ||
roleId: user.roles[0].id, | ||
}, | ||
], | ||
organizationId: new ObjectId().toHexString(), | ||
}); | ||
const context: AuthorizationContext = { | ||
action: Action.write, | ||
requiredPermissions: [Permission.GROUP_VIEW], | ||
}; | ||
|
||
authorizationHelper.hasAllPermissions.mockReturnValue(true); | ||
|
||
return { | ||
user, | ||
group, | ||
context, | ||
}; | ||
}; | ||
|
||
it('should return false', () => { | ||
const { user, group, context } = setup(); | ||
|
||
const result = rule.hasPermission(user, group, context); | ||
|
||
expect(result).toEqual(false); | ||
}); | ||
}); | ||
}); | ||
}); |
24 changes: 24 additions & 0 deletions
24
apps/server/src/modules/authorization/domain/rules/group.rule.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { User } from '@shared/domain'; | ||
import { Group } from '@src/modules/group'; | ||
import { AuthorizationContext, Rule } from '../type'; | ||
import { AuthorizationHelper } from '../service/authorization.helper'; | ||
|
||
@Injectable() | ||
export class GroupRule implements Rule<Group> { | ||
constructor(private readonly authorizationHelper: AuthorizationHelper) {} | ||
|
||
public isApplicable(user: User, domainObject: Group): boolean { | ||
const isMatched: boolean = domainObject instanceof Group; | ||
|
||
return isMatched; | ||
} | ||
|
||
public hasPermission(user: User, domainObject: Group, context: AuthorizationContext): boolean { | ||
const hasPermission: boolean = | ||
this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions) && | ||
(domainObject.organizationId ? user.school.id === domainObject.organizationId : true); | ||
|
||
return hasPermission; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.