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 e8fb31b + f458746 commit b86f891
Show file tree
Hide file tree
Showing 75 changed files with 1,786 additions and 466 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

permissions:
contents: read
Expand Down
30 changes: 30 additions & 0 deletions ansible/roles/schulcloud-server-core/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,50 @@
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: deployment.yml.j2

- name: Ingress
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: ingress.yml.j2
apply: yes

- name: FileStorageDeployment
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-files-deployment.yml.j2

- name: FileStorageDeployment
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-files-deployment.yml.j2

- name: File Storage Ingress
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-files-ingress.yml.j2
apply: yes

- name: FwuLearningContentsDeployment
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-fwu-deployment.yml.j2
when: FEATURE_FWU_CONTENT_ENABLED is defined and FEATURE_FWU_CONTENT_ENABLED|bool

- name: Fwu Learning Contents Ingress Remove
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
state: absent
api_version: networking.k8s.io/v1
kind: Ingress
name: "{{ NAMESPACE }}-api-fwu-ingress"
when: FEATURE_FWU_CONTENT_ENABLED is defined and FEATURE_FWU_CONTENT_ENABLED|bool

- name: Delete Files CronJob
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ NAMESPACE }}-api-files-ingress
namespace: {{ NAMESPACE }}
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "{{ TLS_ENABELD|default("false") }}"
nginx.ingress.kubernetes.io/proxy-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
nginx.org/client-max-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
# The following properties added with BC-3606.
# The header size of the request is too big. For e.g. state and the permanent growing jwt.
# Nginx throws away the Location header, resulting in the 502 Bad Gateway.
nginx.ingress.kubernetes.io/client-header-buffer-size: 100k
nginx.ingress.kubernetes.io/http2-max-header-size: 96k
nginx.ingress.kubernetes.io/large-client-header-buffers: 4 100k
nginx.ingress.kubernetes.io/proxy-buffer-size: 96k
{% if CLUSTER_ISSUER is defined %}
cert-manager.io/cluster-issuer: {{ CLUSTER_ISSUER }}
{% endif %}

spec:
ingressClassName: nginx
{% if CLUSTER_ISSUER is defined or (TLS_ENABELD is defined and TLS_ENABELD|bool) %}
tls:
- hosts:
- {{ DOMAIN }}
{% if CLUSTER_ISSUER is defined %}
secretName: {{ DOMAIN }}-tls
{% endif %}
{% endif %}
rules:
- host: {{ DOMAIN }}
http:
paths:
- path: /api/v3/file/
backend:
service:
name: api-files-svc
port:
number: {{ PORT_FILE_SERVICE }}
pathType: Prefix
41 changes: 41 additions & 0 deletions ansible/roles/schulcloud-server-core/templates/ingress.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ NAMESPACE }}-api-ingress
namespace: {{ NAMESPACE }}
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "{{ TLS_ENABELD|default("false") }}"
nginx.ingress.kubernetes.io/proxy-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
nginx.org/client-max-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
# The following properties added with BC-3606.
# The header size of the request is too big. For e.g. state and the permanent growing jwt.
# Nginx throws away the Location header, resulting in the 502 Bad Gateway.
nginx.ingress.kubernetes.io/client-header-buffer-size: 100k
nginx.ingress.kubernetes.io/http2-max-header-size: 96k
nginx.ingress.kubernetes.io/large-client-header-buffers: 4 100k
nginx.ingress.kubernetes.io/proxy-buffer-size: 96k
{% if CLUSTER_ISSUER is defined %}
cert-manager.io/cluster-issuer: {{ CLUSTER_ISSUER }}
{% endif %}

spec:
ingressClassName: nginx
{% if CLUSTER_ISSUER is defined or (TLS_ENABELD is defined and TLS_ENABELD|bool) %}
tls:
- hosts:
- {{ DOMAIN }}
{% if CLUSTER_ISSUER is defined %}
secretName: {{ DOMAIN }}-tls
{% endif %}
{% endif %}
rules:
- host: {{ DOMAIN }}
http:
paths:
- path: /api/v3/
backend:
service:
name: api-svc
port:
number: {{ PORT_SERVER }}
pathType: Prefix
8 changes: 8 additions & 0 deletions ansible/roles/schulcloud-server-h5p/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,12 @@
namespace: "{{ NAMESPACE }}"
template: api-h5p-deployment.yml.j2
when: WITH_H5P_EDITOR is defined and WITH_H5P_EDITOR|bool

- name: H5p Editor Ingress
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: api-h5p-ingress.yml.j2
apply: yes
when: WITH_H5P_EDITOR is defined and WITH_H5P_EDITOR|bool

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ NAMESPACE }}-api-h5p-ingress
namespace: {{ NAMESPACE }}
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "{{ TLS_ENABELD|default("false") }}"
nginx.ingress.kubernetes.io/proxy-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
nginx.org/client-max-body-size: "{{ INGRESS_MAX_BODY_SIZE|default("2560") }}m"
# The following properties added with BC-3606.
# The header size of the request is too big. For e.g. state and the permanent growing jwt.
# Nginx throws away the Location header, resulting in the 502 Bad Gateway.
nginx.ingress.kubernetes.io/client-header-buffer-size: 100k
nginx.ingress.kubernetes.io/http2-max-header-size: 96k
nginx.ingress.kubernetes.io/large-client-header-buffers: 4 100k
nginx.ingress.kubernetes.io/proxy-buffer-size: 96k
{% if CLUSTER_ISSUER is defined %}
cert-manager.io/cluster-issuer: {{ CLUSTER_ISSUER }}
{% endif %}

spec:
ingressClassName: nginx
{% if CLUSTER_ISSUER is defined or (TLS_ENABELD is defined and TLS_ENABELD|bool) %}
tls:
- hosts:
- {{ DOMAIN }}
{% if CLUSTER_ISSUER is defined %}
secretName: {{ DOMAIN }}-tls
{% endif %}
{% endif %}
rules:
- host: {{ DOMAIN }}
http:
paths:
- path: /api/v3/h5p-editor/
backend:
service:
name: api-h5p-svc
port:
number: 4448
pathType: Prefix
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface CreateJwtPayload {
systemId?: string; // without this the user needs to change his PW during first login
support?: boolean;
// support UserId is missed see featherJS
isExternalUser: boolean;
}

export interface JwtPayload extends CreateJwtPayload {
Expand Down
3 changes: 3 additions & 0 deletions apps/server/src/modules/authentication/interface/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export interface ICurrentUser {

/** True if a support member impersonates the user */
impersonated?: boolean;

/** True if the user is an external user e.g. an oauth user */
isExternalUser: boolean;
}

export interface OauthCurrentUser extends ICurrentUser {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ describe('CurrentUserMapper', () => {
describe('when userDO has no ID', () => {
it('should throw error', () => {
const user: UserDO = userDoFactory.build({ createdAt: new Date(), updatedAt: new Date() });

expect(() => CurrentUserMapper.mapToOauthCurrentUser(accountId, user, undefined, 'idToken')).toThrow(
ValidationError
);
Expand Down Expand Up @@ -100,6 +101,7 @@ describe('CurrentUserMapper', () => {
schoolId: user.schoolId,
userId,
externalIdToken: idToken,
isExternalUser: true,
});
});
});
Expand Down Expand Up @@ -139,6 +141,7 @@ describe('CurrentUserMapper', () => {
schoolId: user.schoolId,
userId,
externalIdToken: idToken,
isExternalUser: true,
});
});
});
Expand Down Expand Up @@ -181,22 +184,33 @@ describe('CurrentUserMapper', () => {

describe('jwtToICurrentUser', () => {
describe('when JWT is provided with all claims', () => {
it('should return current user', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
systemId: 'dummySystemId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
support: true,
isExternalUser: true,
sub: 'dummyAccountId',
jti: 'random string',
aud: 'some audience',
iss: 'feathers',
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};

return {
jwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
systemId: jwtPayload.systemId,
Expand All @@ -206,28 +220,60 @@ describe('CurrentUserMapper', () => {
impersonated: jwtPayload.support,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: jwtPayload.isExternalUser,
});
});
});

describe('when JWT is provided without optional claims', () => {
it('should return current user', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
isExternalUser: false,
sub: 'dummyAccountId',
jti: 'random string',
aud: 'some audience',
iss: 'feathers',
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};

return {
jwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
roles: [jwtPayload.roles[0]],
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
isExternalUser: false,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: false,
});
});
});
Expand All @@ -242,6 +288,7 @@ describe('CurrentUserMapper', () => {
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
impersonated: true,
isExternalUser: false,
};

const createJwtPayload: CreateJwtPayload = CurrentUserMapper.mapCurrentUserToCreateJwtPayload(currentUser);
Expand All @@ -253,6 +300,7 @@ describe('CurrentUserMapper', () => {
schoolId: currentUser.schoolId,
userId: currentUser.userId,
support: currentUser.impersonated,
isExternalUser: false,
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class CurrentUserMapper {
roles: user.roles.getItems().map((role: Role) => role.id),
schoolId: user.school.id,
userId: user.id,
isExternalUser: false,
};
}

Expand All @@ -33,6 +34,7 @@ export class CurrentUserMapper {
schoolId: user.schoolId,
userId: user.id,
externalIdToken,
isExternalUser: true,
};
}

Expand All @@ -44,6 +46,7 @@ export class CurrentUserMapper {
roles: currentUser.roles,
systemId: currentUser.systemId,
support: currentUser.impersonated,
isExternalUser: currentUser.isExternalUser,
};
}

Expand All @@ -55,6 +58,7 @@ export class CurrentUserMapper {
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
impersonated: jwtPayload.support,
isExternalUser: jwtPayload.isExternalUser,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ describe('AuthenticationService', () => {
roles: ['student'],
schoolId: 'mockSchoolId',
userId: 'mockUserId',
isExternalUser: false,
};
await authenticationService.generateJwt(mockCurrentUser);
expect(jwtService.sign).toBeCalledWith(
Expand Down
Loading

0 comments on commit b86f891

Please sign in to comment.