Skip to content

Commit

Permalink
N21-1269 roster adaptation (#4412)
Browse files Browse the repository at this point in the history
* implement feathers-roster-service
* add pseudonyms endpoint
* moved iframe code from id-token service to pseudonym service

---------

Co-authored-by: Arne Gnisa <[email protected]>
  • Loading branch information
IgorCapCoder and arnegns authored Sep 22, 2023
1 parent 67704f5 commit 251981f
Show file tree
Hide file tree
Showing 35 changed files with 2,074 additions and 127 deletions.
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

0 comments on commit 251981f

Please sign in to comment.