Skip to content

Commit

Permalink
Merge branch 'main' of github.com:hpi-schul-cloud/schulcloud-server i…
Browse files Browse the repository at this point in the history
…nto BC-5434-meta-data-endpoint
  • Loading branch information
hoeppner-dataport committed Nov 3, 2023
2 parents 05741fd + 0d2718d commit ad43c02
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { EntityManager } from '@mikro-orm/core';
import { SSOErrorCode } from '@modules/oauth/loggable';
import { OauthTokenResponse } from '@modules/oauth/service/dto';
import { ServerTestModule } from '@modules/server/server.module';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { Account, RoleName, SchoolEntity, SystemEntity, User } from '@shared/domain';
import { accountFactory, roleFactory, schoolFactory, systemFactory, userFactory } from '@shared/testing';
import { SSOErrorCode } from '@modules/oauth/loggable';
import { OauthTokenResponse } from '@modules/oauth/service/dto';
import { ServerTestModule } from '@modules/server/server.module';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import crypto, { KeyPairKeyObjectResult } from 'crypto';
import jwt from 'jsonwebtoken';
import request, { Response } from 'supertest';
import { ICurrentUser } from '../../interface';
import { LdapAuthorizationBodyParams, LocalAuthorizationBodyParams, OauthLoginResponse } from '../dto';

const ldapAccountUserName = 'ldapAccountUserName';
Expand Down Expand Up @@ -145,41 +146,38 @@ describe('Login Controller (api)', () => {
});

describe('loginLdap', () => {
let account: Account;
let user: User;
let school: SchoolEntity;
let system: SystemEntity;

beforeAll(async () => {
const schoolExternalId = 'mockSchoolExternalId';
system = systemFactory.withLdapConfig().buildWithId({});
school = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });
describe('when user login succeeds', () => {
const setup = async () => {
const schoolExternalId = 'mockSchoolExternalId';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });

user = userFactory.buildWithId({ school, roles: [studentRoles], ldapDn: mockUserLdapDN });
const user: User = userFactory.buildWithId({ school, roles: [studentRoles], ldapDn: mockUserLdapDN });

account = accountFactory.buildWithId({
userId: user.id,
username: `${schoolExternalId}/${ldapAccountUserName}`.toLowerCase(),
systemId: system.id,
});
const account: Account = accountFactory.buildWithId({
userId: user.id,
username: `${schoolExternalId}/${ldapAccountUserName}`.toLowerCase(),
systemId: system.id,
});

em.persist(system);
em.persist(school);
em.persist(studentRoles);
em.persist(user);
em.persist(account);
await em.flush();
});
await em.persistAndFlush([system, school, studentRoles, user, account]);

describe('when user login succeeds', () => {
it('should return jwt', async () => {
const params: LdapAuthorizationBodyParams = {
username: ldapAccountUserName,
password: defaultPassword,
schoolId: school.id,
systemId: system.id,
};

return {
params,
};
};

it('should return jwt', async () => {
const { params } = await setup();

const response = await request(app.getHttpServer()).post(`${basePath}/ldap`).send(params);

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
Expand All @@ -199,14 +197,99 @@ describe('Login Controller (api)', () => {
});

describe('when user login fails', () => {
it('should return error response', async () => {
const setup = async () => {
const schoolExternalId = 'mockSchoolExternalId';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });

const user: User = userFactory.buildWithId({ school, roles: [studentRoles], ldapDn: mockUserLdapDN });

const account: Account = accountFactory.buildWithId({
userId: user.id,
username: `${schoolExternalId}/${ldapAccountUserName}`.toLowerCase(),
systemId: system.id,
});

await em.persistAndFlush([system, school, studentRoles, user, account]);

const params: LdapAuthorizationBodyParams = {
username: 'nonExistentUser',
password: 'wrongPassword',
schoolId: school.id,
systemId: system.id,
};
await request(app.getHttpServer()).post(`${basePath}/ldap`).send(params).expect(401);

return {
params,
};
};

it('should return error response', async () => {
const { params } = await setup();

const response = await request(app.getHttpServer()).post(`${basePath}/ldap`).send(params);

expect(response.status).toEqual(HttpStatus.UNAUTHORIZED);
});
});

describe('when logging in as a user of the Central LDAP of Brandenburg', () => {
const setup = async () => {
const officialSchoolNumber = '01234';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({
systems: [system],
externalId: officialSchoolNumber,
officialSchoolNumber,
});
const studentRole = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });

const user: User = userFactory.buildWithId({ school, roles: [studentRole], ldapDn: mockUserLdapDN });

const account: Account = accountFactory.buildWithId({
userId: user.id,
username: `${officialSchoolNumber}/${ldapAccountUserName}`.toLowerCase(),
systemId: system.id,
});

await em.persistAndFlush([system, school, studentRole, user, account]);

const params: LdapAuthorizationBodyParams = {
username: ldapAccountUserName,
password: defaultPassword,
schoolId: school.id,
systemId: system.id,
};

return {
params,
user,
account,
school,
system,
studentRole,
};
};

it('should return a jwt', async () => {
const { params, user, account, school, system, studentRole } = await setup();

const response = await request(app.getHttpServer()).post(`${basePath}/ldap`).send(params);

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
expect(response.body.accessToken).toBeDefined();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
const decodedToken = jwt.decode(response.body.accessToken);
expect(decodedToken).toMatchObject<ICurrentUser>({
userId: user.id,
systemId: system.id,
roles: [studentRole.id],
schoolId: school.id,
accountId: account.id,
isExternalUser: false,
});
expect(decodedToken).not.toHaveProperty('externalIdToken');
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/modules/provisioning/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './provisioning.module';
export * from './dto/provisioning.dto';
export * from './service/provisioning.service';
export * from './strategy/index';
export * from './strategy';
2 changes: 1 addition & 1 deletion apps/server/src/modules/provisioning/strategy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export * from './base.strategy';
export * from './iserv/iserv.strategy';
export * from './oidc/oidc.strategy';
export * from './oidc-mock/oidc-mock.strategy';
export * from './sanis/sanis.strategy';
export * from './sanis';
3 changes: 3 additions & 0 deletions apps/server/src/modules/provisioning/strategy/sanis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './response';
export * from './sanis.strategy';
export * from './sanis-response.mapper';
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,21 @@ describe('SanisStrategy', () => {
provisioningUrl,
expect.objectContaining({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
headers: expect.objectContaining({ Authorization: 'Bearer sanisAccessToken' }),
headers: expect.objectContaining({ Authorization: 'Bearer sanisAccessToken', 'Accept-Encoding': 'gzip' }),
})
);
});

it('should accept gzip compressed data', async () => {
const { input, provisioningUrl } = setup();

await strategy.getData(input);

expect(httpService.get).toHaveBeenCalledWith(
provisioningUrl,
expect.objectContaining({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
headers: expect.objectContaining({ 'Accept-Encoding': 'gzip' }),
})
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ export class SanisProvisioningStrategy extends OidcProvisioningStrategy {
}

const axiosConfig: AxiosRequestConfig = {
headers: { Authorization: `Bearer ${input.accessToken}` },
headers: {
Authorization: `Bearer ${input.accessToken}`,
'Accept-Encoding': 'gzip',
},
};

const axiosResponse: AxiosResponse<SanisResponse> = await firstValueFrom(
Expand Down
Loading

0 comments on commit ad43c02

Please sign in to comment.