Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

N21-1269 roster adaptation #4412

Merged
merged 65 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b62e126
N21-1269 wip
arnegns Sep 13, 2023
5f3d4c9
add pseudonym controller
IgorCapCoder Sep 13, 2023
064a684
Merge branch 'N21-1269-roster-adaptation-temp' into N21-1269-roster-a…
IgorCapCoder Sep 13, 2023
5f0f69a
N21-1269 logs
arnegns Sep 14, 2023
1d5c2e3
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 14, 2023
e17ff65
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 14, 2023
3e33484
N21-1269 logs 2
arnegns Sep 14, 2023
4fddaf4
N21-1269 changes loading user
arnegns Sep 14, 2023
7902c8c
N21-1269 changes response
arnegns Sep 14, 2023
5e4ac9f
N21-1269 logging
arnegns Sep 14, 2023
6b05d35
N21-1269 changed repo
arnegns Sep 14, 2023
d8d7296
N21-1269 changes
arnegns Sep 14, 2023
a5e96f5
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 14, 2023
bc2c525
pseudonyms endpoint
IgorCapCoder Sep 14, 2023
ed20ee1
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
IgorCapCoder Sep 14, 2023
de27649
N21-1269 pseudonym endpoint with pagination implemented
arnegns Sep 14, 2023
be61610
N21-1269 changed endpoint to get with id
arnegns Sep 14, 2023
5b7e88e
N21-1269 moved iframe code from id-token service to pseudonym service
arnegns Sep 15, 2023
a71f75b
N21-1269 wip
arnegns Sep 15, 2023
ff095f5
check permission, uc.spec update
IgorCapCoder Sep 15, 2023
69c9214
api test WIP
IgorCapCoder Sep 15, 2023
06f820b
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 18, 2023
1b8197d
N21-1269 getUserGroups changes
arnegns Sep 18, 2023
17c2130
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
57430ed
N21-1269 adds feathers test
arnegns Sep 18, 2023
185c423
N21-1269 adds some nest tests
arnegns Sep 18, 2023
40080e8
api test
IgorCapCoder Sep 18, 2023
0c149e3
N21-1269 adds loggable exception for too many pseudonyms
arnegns Sep 18, 2023
d842aeb
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
32ac9e8
refactor controller and uc to use a pseudonym string directly
IgorCapCoder Sep 18, 2023
7409893
N21-1269 changes feathers test
arnegns Sep 18, 2023
6635d08
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
04bf666
N21-1269 fixes feathers test with resetting sinon stubs
arnegns Sep 18, 2023
35e5e07
code readabilty enhancement
IgorCapCoder Sep 18, 2023
59cc0e5
code readabilty enhancement
IgorCapCoder Sep 18, 2023
6fdcbc7
unit test fix
IgorCapCoder Sep 18, 2023
3be3eea
fix import
IgorCapCoder Sep 18, 2023
446486b
N21-1269 fix test
arnegns Sep 18, 2023
d037222
N21-1269 fix test 2
arnegns Sep 18, 2023
5df4bc6
N21-1269 fix tests
arnegns Sep 18, 2023
febe48a
N21-12569 fixed import
arnegns Sep 18, 2023
e50c3da
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 19, 2023
0d80aff
N21-1269 fix test using more specific import
arnegns Sep 19, 2023
72024ef
N21-1269 adds missing test
arnegns Sep 19, 2023
01433fc
N21-1269 fix roster logic
arnegns Sep 19, 2023
091eada
N21-1269 changes pseudonym logic
arnegns Sep 20, 2023
075e5d1
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 20, 2023
2d5e51e
N21-1269 changed pseudonym mapping
arnegns Sep 20, 2023
9a5a3af
N21-1269 changes to create pseudonym if it does not exist
arnegns Sep 20, 2023
5bf70ff
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 20, 2023
ca37126
N21-1269 fixes tests
arnegns Sep 20, 2023
f9074f3
fix check permisssion
IgorCapCoder Sep 20, 2023
4b6a687
N21-1269 fix import
arnegns Sep 20, 2023
b2236da
Merge branch 'main' into N21-1269-roster-adaptation
IgorCapCoder Sep 20, 2023
0022c0d
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 21, 2023
c80fd91
Merge branch 'main' into N21-1269-roster-adaptation
IgorCapCoder Sep 21, 2023
eccb278
N21-1269 review fixes
arnegns Sep 21, 2023
35cbe60
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
IgorCapCoder Sep 21, 2023
36fd83c
request changes
IgorCapCoder Sep 21, 2023
6457c0c
Merge branch 'main' into N21-1269-roster-adaptation
IgorCapCoder Sep 21, 2023
8a83475
fix unit test
IgorCapCoder Sep 21, 2023
f1ce51f
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
IgorCapCoder Sep 21, 2023
cd0d924
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 22, 2023
2dd3b44
request changes
IgorCapCoder Sep 22, 2023
a8dbccc
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 22, 2023
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
3 changes: 3 additions & 0 deletions apps/server/src/apps/server.app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { join } from 'path';

// register source-map-support for debugging
import { install as sourceMapInstall } from 'source-map-support';
import { FeathersRosterService } from '@src/modules/pseudonym';
import legacyAppPromise = require('../../../../src/app');

import { AppStartLoggable } from './helpers/app-start-loggable';
Expand Down Expand Up @@ -80,6 +81,8 @@ async function bootstrap() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-team-service'] = nestApp.get(TeamService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
feathersExpress.services['nest-feathers-roster-service'] = nestApp.get(FeathersRosterService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
feathersExpress.services['nest-orm'] = orm;

// mount instances
Expand Down
27 changes: 25 additions & 2 deletions apps/server/src/modules/learnroom/service/course.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,18 @@ describe('CourseService', () => {
});

describe('findById', () => {
it('should call findById from course repository', async () => {
const setup = () => {
const courseId = 'courseId';
courseRepo.findById.mockResolvedValueOnce({} as Course);

return { courseId };
};

it('should call findById from course repository', async () => {
const { courseId } = setup();

await expect(courseService.findById(courseId)).resolves.not.toThrow();
expect(courseRepo.findById).toBeCalledTimes(1);

expect(courseRepo.findById).toBeCalledWith(courseId);
});
});
Expand Down Expand Up @@ -78,4 +84,21 @@ describe('CourseService', () => {
expect(result).toEqual(3);
});
});

describe('findAllByUserId', () => {
const setup = () => {
const userId = 'userId';
courseRepo.findAllByUserId.mockResolvedValueOnce([[], 0]);

return { userId };
};

it('should call findAllByUserId from course repository', async () => {
const { userId } = setup();

await expect(courseService.findAllByUserId(userId)).resolves.not.toThrow();

expect(courseRepo.findAllByUserId).toBeCalledWith(userId);
});
});
});
6 changes: 6 additions & 0 deletions apps/server/src/modules/learnroom/service/course.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ export class CourseService {

return count;
}

async findAllByUserId(userId: EntityId): Promise<Course[]> {
const [courses] = await this.repo.findAllByUserId(userId);

return courses;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Configuration } from '@hpi-schul-cloud/commons/lib';
import { Test, TestingModule } from '@nestjs/testing';
import { Pseudonym, TeamEntity, UserDO } from '@shared/domain';
import { TeamsRepo } from '@shared/repo';
Expand All @@ -24,18 +23,7 @@ describe('IdTokenService', () => {
let teamsRepo: DeepMocked<TeamsRepo>;
let userService: DeepMocked<UserService>;

const hostUrl = 'https://host.de';

beforeAll(async () => {
jest.spyOn(Configuration, 'get').mockImplementation((key: string) => {
switch (key) {
case 'HOST':
return hostUrl;
default:
return null;
}
});

module = await Test.createTestingModule({
providers: [
IdTokenService,
Expand Down Expand Up @@ -90,24 +78,26 @@ describe('IdTokenService', () => {
userService.findById.mockResolvedValue(user);
userService.getDisplayName.mockResolvedValue(displayName);
oauthProviderLoginFlowService.findToolByClientId.mockResolvedValue(tool);
pseudonymService.findByUserAndTool.mockResolvedValue(pseudonym);
pseudonymService.findByUserAndToolOrThrow.mockResolvedValue(pseudonym);
const iframeSubject = 'iframeSubject';
pseudonymService.getIframeSubject.mockReturnValueOnce(iframeSubject);

return {
user,
displayName,
tool,
pseudonym,
iframeSubject,
};
};

it('should return the correct id token', async () => {
const { user } = setup();
const { user, iframeSubject } = setup();

const result: IdToken = await service.createIdToken('userId', [], 'clientId');

expect(result).toEqual<IdToken>({
iframe:
'<iframe src="https://host.de/oauth2/username/pseudonym" title="username" style="height: 26px; width: 180px; border: none;"></iframe>',
iframe: iframeSubject,
schoolId: user.schoolId,
});
});
Expand All @@ -129,25 +119,27 @@ describe('IdTokenService', () => {
userService.findById.mockResolvedValue(user);
userService.getDisplayName.mockResolvedValue(displayName);
oauthProviderLoginFlowService.findToolByClientId.mockResolvedValue(tool);
pseudonymService.findByUserAndTool.mockResolvedValue(pseudonym);
pseudonymService.findByUserAndToolOrThrow.mockResolvedValue(pseudonym);
const iframeSubject = 'iframeSubject';
pseudonymService.getIframeSubject.mockReturnValueOnce(iframeSubject);

return {
team,
user,
displayName,
tool,
pseudonym,
iframeSubject,
};
};

it('should return the correct id token', async () => {
const { user, team } = setup();
const { user, team, iframeSubject } = setup();

const result: IdToken = await service.createIdToken('userId', [OauthScope.GROUPS], 'clientId');

expect(result).toEqual<IdToken>({
iframe:
'<iframe src="https://host.de/oauth2/username/pseudonym" title="username" style="height: 26px; width: 180px; border: none;"></iframe>',
iframe: iframeSubject,
schoolId: user.schoolId,
groups: [
{
Expand All @@ -172,24 +164,26 @@ describe('IdTokenService', () => {
userService.findById.mockResolvedValue(user);
userService.getDisplayName.mockResolvedValue(displayName);
oauthProviderLoginFlowService.findToolByClientId.mockResolvedValue(tool);
pseudonymService.findByUserAndTool.mockResolvedValue(pseudonym);
pseudonymService.findByUserAndToolOrThrow.mockResolvedValue(pseudonym);
const iframeSubject = 'iframeSubject';
pseudonymService.getIframeSubject.mockReturnValueOnce(iframeSubject);

return {
user,
displayName,
tool,
pseudonym,
iframeSubject,
};
};

it('should return the correct id token', async () => {
const { user } = setup();
const { user, iframeSubject } = setup();

const result: IdToken = await service.createIdToken('userId', [OauthScope.EMAIL], 'clientId');

expect(result).toEqual<IdToken>({
iframe:
'<iframe src="https://host.de/oauth2/username/pseudonym" title="username" style="height: 26px; width: 180px; border: none;"></iframe>',
iframe: iframeSubject,
schoolId: user.schoolId,
email: user.email,
});
Expand All @@ -209,24 +203,26 @@ describe('IdTokenService', () => {
userService.findById.mockResolvedValue(user);
userService.getDisplayName.mockResolvedValue(displayName);
oauthProviderLoginFlowService.findToolByClientId.mockResolvedValue(tool);
pseudonymService.findByUserAndTool.mockResolvedValue(pseudonym);
pseudonymService.findByUserAndToolOrThrow.mockResolvedValue(pseudonym);
const iframeSubject = 'iframeSubject';
pseudonymService.getIframeSubject.mockReturnValueOnce(iframeSubject);

return {
user,
displayName,
tool,
pseudonym,
iframeSubject,
};
};

it('should return the correct id token', async () => {
const { user, displayName } = setup();
const { user, displayName, iframeSubject } = setup();

const result: IdToken = await service.createIdToken('userId', [OauthScope.PROFILE], 'clientId');

expect(result).toEqual<IdToken>({
iframe:
'<iframe src="https://host.de/oauth2/username/pseudonym" title="username" style="height: 26px; width: 180px; border: none;"></iframe>',
iframe: iframeSubject,
schoolId: user.schoolId,
name: displayName,
userId: user.id,
Expand Down
20 changes: 7 additions & 13 deletions apps/server/src/modules/oauth-provider/service/id-token.service.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import { Configuration } from '@hpi-schul-cloud/commons/lib';
import { Injectable } from '@nestjs/common';
import { LtiToolDO, Pseudonym, TeamEntity, UserDO } from '@shared/domain';
import { TeamsRepo } from '@shared/repo';
import { PseudonymService } from '@src/modules/pseudonym';
import { UserService } from '@src/modules/user';
import { ExternalTool } from '@src/modules/tool/external-tool/domain';
import { UserService } from '@src/modules/user';
import { IdTokenCreationLoggableException } from '../error/id-token-creation-exception.loggable';
import { GroupNameIdTuple, IdToken, OauthScope } from '../interface';
import { OauthProviderLoginFlowService } from './oauth-provider.login-flow.service';
import { IdTokenCreationLoggableException } from '../error/id-token-creation-exception.loggable';

@Injectable()
export class IdTokenService {
private readonly host: string;

protected iFrameProperties: string;

constructor(
private readonly oauthProviderLoginFlowService: OauthProviderLoginFlowService,
private readonly pseudonymService: PseudonymService,
private readonly teamsRepo: TeamsRepo,
private readonly userService: UserService
) {
this.host = Configuration.get('HOST') as string;
this.iFrameProperties = 'title="username" style="height: 26px; width: 180px; border: none;"';
}
) {}

async createIdToken(userId: string, scopes: string[], clientId: string): Promise<IdToken> {
let teams: TeamEntity[] = [];
Expand Down Expand Up @@ -63,8 +55,10 @@ export class IdTokenService {
throw new IdTokenCreationLoggableException(clientId, user.id);
}

const pseudonym: Pseudonym = await this.pseudonymService.findByUserAndTool(user, tool);
const pseudonym: Pseudonym = await this.pseudonymService.findByUserAndToolOrThrow(user, tool);

const iframeSubject: string = this.pseudonymService.getIframeSubject(pseudonym.pseudonym);

return `<iframe src="${this.host}/oauth2/username/${pseudonym.pseudonym}" ${this.iFrameProperties}></iframe>`;
return iframeSubject;
}
}
Loading
Loading