diff --git a/charts/dbildungs-iam-server/templates/configmap.yaml b/charts/dbildungs-iam-server/templates/configmap.yaml index cca9f526a..a6ccdca5a 100644 --- a/charts/dbildungs-iam-server/templates/configmap.yaml +++ b/charts/dbildungs-iam-server/templates/configmap.yaml @@ -20,3 +20,6 @@ data: LDAP_BIND_DN: "{{ .Values.ldap.bindDN }}" LDAP_OEFFENTLICHE_SCHULEN_DOMAIN: "{{ .Values.ldap.oeffentlicheSchulenDomain }}" LDAP_ERSATZSCHULEN_DOMAIN: "{{ .Values.ldap.ersatzschulenDomain }}" + ITSLEARNING_ROOT: "{{ .Values.itslearning.root }}" + ITSLEARNING_ROOT_OEFFENTLICH: "{{ .Values.itslearning.rootOeffentlich }}" + ITSLEARNING_ROOT_ERSATZ: "{{ .Values.itslearning.rootErsatz }}" diff --git a/charts/dbildungs-iam-server/values.yaml b/charts/dbildungs-iam-server/values.yaml index 3383ddddc..dcde3f1aa 100644 --- a/charts/dbildungs-iam-server/values.yaml +++ b/charts/dbildungs-iam-server/values.yaml @@ -32,6 +32,11 @@ ldap: oeffentlicheSchulenDomain: schule-sh.de ersatzschulenDomain: ersatzschule-sh.de +itslearning: + root: sh + rootOeffentlich: oeffentlich + rootErsatz: ersatz + auth: # existingSecret: Refers to a secret already present in the cluster, which is required. existingSecret: '' diff --git a/config/config.json b/config/config.json index 374875afb..ce2d97523 100644 --- a/config/config.json +++ b/config/config.json @@ -62,7 +62,7 @@ "BACKEND_FOR_FRONTEND_MODULE_LOG_LEVEL": "debug" }, "ITSLEARNING": { - "ENABLED": "false", + "ENABLED": false, "ENDPOINT": "https://itslearning-test.example.com", "USERNAME": "username", "PASSWORD": "password", @@ -71,7 +71,7 @@ "ROOT_ERSATZ": "ersatz" }, "OX": { - "ENABLED": "false", + "ENABLED": false, "ENDPOINT": "http://ox.dev.spsh.dbildungsplattform.de/webservices/", "CONTEXT_ID": "1337", "CONTEXT_NAME": "contextname", diff --git a/src/modules/itslearning/event-handlers/itslearning-organisations.event-handler.ts b/src/modules/itslearning/event-handlers/itslearning-organisations.event-handler.ts index 2ed957694..37516ded8 100644 --- a/src/modules/itslearning/event-handlers/itslearning-organisations.event-handler.ts +++ b/src/modules/itslearning/event-handlers/itslearning-organisations.event-handler.ts @@ -30,7 +30,7 @@ export class ItsLearningOrganisationsEventHandler { ) { const itsLearningConfig: ItsLearningConfig = configService.getOrThrow('ITSLEARNING'); - this.ENABLED = itsLearningConfig.ENABLED === 'true'; + this.ENABLED = itsLearningConfig.ENABLED; this.ROOT_OEFFENTLICH = itsLearningConfig.ROOT_OEFFENTLICH; } diff --git a/src/modules/itslearning/event-handlers/itslearning-persons.event-handler.ts b/src/modules/itslearning/event-handlers/itslearning-persons.event-handler.ts index 334984054..0e677851c 100644 --- a/src/modules/itslearning/event-handlers/itslearning-persons.event-handler.ts +++ b/src/modules/itslearning/event-handlers/itslearning-persons.event-handler.ts @@ -36,7 +36,7 @@ export class ItsLearningPersonsEventHandler { ) { const itsLearningConfig: ItsLearningConfig = configService.getOrThrow('ITSLEARNING'); - this.ENABLED = itsLearningConfig.ENABLED === 'true'; + this.ENABLED = itsLearningConfig.ENABLED; } @EventHandler(PersonRenamedEvent) diff --git a/src/modules/itslearning/event-handlers/itslearning-sync.event-handler.ts b/src/modules/itslearning/event-handlers/itslearning-sync.event-handler.ts index 5989a710b..4915a45ef 100644 --- a/src/modules/itslearning/event-handlers/itslearning-sync.event-handler.ts +++ b/src/modules/itslearning/event-handlers/itslearning-sync.event-handler.ts @@ -42,7 +42,7 @@ export class ItsLearningSyncEventHandler { ) { const itsLearningConfig: ItsLearningConfig = configService.getOrThrow('ITSLEARNING'); - this.ENABLED = itsLearningConfig.ENABLED === 'true'; + this.ENABLED = itsLearningConfig.ENABLED; } @EventHandler(PersonExternalSystemsSyncEvent) diff --git a/src/modules/ox/domain/ox-event-handler.ts b/src/modules/ox/domain/ox-event-handler.ts index cc205675e..d69a7666b 100644 --- a/src/modules/ox/domain/ox-event-handler.ts +++ b/src/modules/ox/domain/ox-event-handler.ts @@ -45,7 +45,7 @@ export class OxEventHandler { ) { const oxConfig: OxConfig = configService.getOrThrow('OX'); - this.ENABLED = oxConfig.ENABLED === 'true'; + this.ENABLED = oxConfig.ENABLED; this.authUser = oxConfig.USERNAME; this.authPassword = oxConfig.PASSWORD; this.contextID = oxConfig.CONTEXT_ID; diff --git a/src/shared/config/config.env.ts b/src/shared/config/config.env.ts index d64b61195..e6b2de850 100644 --- a/src/shared/config/config.env.ts +++ b/src/shared/config/config.env.ts @@ -7,6 +7,7 @@ import { LdapConfig } from './ldap.config.js'; import { PrivacyIdeaConfig } from './privacyidea.config.js'; import { OxConfig } from './ox.config.js'; import { RedisConfig } from './redis.config.js'; +import { envToOptionalBoolean } from './utils.js'; export default (): { DB: Partial; @@ -51,10 +52,13 @@ export default (): { PASSWORD: process.env['REDIS_PASSWORD'], }, ITSLEARNING: { - ENABLED: process.env['ITSLEARNING_ENABLED']?.toLowerCase() as 'true' | 'false', + ENABLED: envToOptionalBoolean('ITSLEARNING_ENABLED'), ENDPOINT: process.env['ITSLEARNING_ENDPOINT'], USERNAME: process.env['ITSLEARNING_USERNAME'], PASSWORD: process.env['ITSLEARNING_PASSWORD'], + ROOT: process.env['ITSLEARNING_ROOT'], + ROOT_OEFFENTLICH: process.env['ITSLEARNING_ROOT_OEFFENTLICH'], + ROOT_ERSATZ: process.env['ITSLEARNING_ROOT_ERSATZ'], }, PRIVACYIDEA: { ENDPOINT: process.env['PI_BASE_URL'], @@ -65,7 +69,7 @@ export default (): { RENAME_WAITING_TIME_IN_SECONDS: parseInt(process.env['PI_RENAME_WAITING_TIME'] || '0'), }, OX: { - ENABLED: process.env['OX_ENABLED']?.toLowerCase() as 'true' | 'false', + ENABLED: envToOptionalBoolean('OX_ENABLED'), ENDPOINT: process.env['OX_ENDPOINT'], USERNAME: process.env['OX_USERNAME'], PASSWORD: process.env['OX_PASSWORD'], diff --git a/src/shared/config/config.loader.spec.ts b/src/shared/config/config.loader.spec.ts index e3d55bf05..c5bd6328b 100644 --- a/src/shared/config/config.loader.spec.ts +++ b/src/shared/config/config.loader.spec.ts @@ -54,7 +54,7 @@ describe('configloader', () => { BIND_DN: 'cn=admin,dc=schule-sh,dc=de', }, ITSLEARNING: { - ENABLED: 'true', + ENABLED: true, ENDPOINT: 'http://itslearning', USERNAME: 'username', ROOT: 'sh', @@ -69,7 +69,7 @@ describe('configloader', () => { REALM: 'defrealm', }, OX: { - ENABLED: 'true', + ENABLED: true, ENDPOINT: 'https://ox_ip:ox_port/webservices/OXUserService', CONTEXT_ID: '1337', CONTEXT_NAME: 'context1', @@ -163,7 +163,7 @@ describe('configloader', () => { ADMIN_PASSWORD: 'password', }, ITSLEARNING: { - ENABLED: 'true', + ENABLED: true, ENDPOINT: 'http://itslearning', USERNAME: 'username', PASSWORD: 'password', @@ -179,7 +179,7 @@ describe('configloader', () => { REALM: 'defrealm', }, OX: { - ENABLED: 'true', + ENABLED: true, ENDPOINT: 'https://ox_ip:ox_port/webservices/OXUserService', CONTEXT_ID: '1337', CONTEXT_NAME: 'context1', diff --git a/src/shared/config/itslearning.config.ts b/src/shared/config/itslearning.config.ts index 18c9d4b5c..54b2a976e 100644 --- a/src/shared/config/itslearning.config.ts +++ b/src/shared/config/itslearning.config.ts @@ -1,8 +1,8 @@ -import { IsBooleanString, IsString } from 'class-validator'; +import { IsBoolean, IsString } from 'class-validator'; export class ItsLearningConfig { - @IsBooleanString() - public readonly ENABLED!: 'true' | 'false'; + @IsBoolean() + public readonly ENABLED!: boolean; @IsString() public readonly ENDPOINT!: string; diff --git a/src/shared/config/ox.config.ts b/src/shared/config/ox.config.ts index d8ada0442..4e6cdea3b 100644 --- a/src/shared/config/ox.config.ts +++ b/src/shared/config/ox.config.ts @@ -1,8 +1,8 @@ -import { IsBooleanString, IsNumberString, IsString } from 'class-validator'; +import { IsBoolean, IsNumberString, IsString } from 'class-validator'; export class OxConfig { - @IsBooleanString() - public readonly ENABLED!: 'true' | 'false'; + @IsBoolean() + public readonly ENABLED!: boolean; @IsString() public readonly ENDPOINT!: string; diff --git a/src/shared/config/utils.spec.ts b/src/shared/config/utils.spec.ts new file mode 100644 index 000000000..5e0ea2cfa --- /dev/null +++ b/src/shared/config/utils.spec.ts @@ -0,0 +1,28 @@ +import { envToOptionalBoolean } from './utils.js'; + +const TEST_KEY: string = 'CONFIG_UTIL_TEST_KEY'; + +describe('Config Utils', () => { + describe('envToOptionalBoolean', () => { + it.each([ + ['', undefined], + ['true', true], + ['TRUE', true], + ['false', false], + ['FALSE', false], + ])( + 'when environment variable is "%s", should return %s', + (input: string | undefined, expected: boolean | undefined) => { + process.env[TEST_KEY] = input; + + expect(envToOptionalBoolean(TEST_KEY)).toBe(expected); + }, + ); + + it('should throw error, if the environment variable is set to an invalid string', () => { + process.env[TEST_KEY] = 'INVALID'; + + expect(() => envToOptionalBoolean(TEST_KEY)).toThrow(); + }); + }); +}); diff --git a/src/shared/config/utils.ts b/src/shared/config/utils.ts new file mode 100644 index 000000000..d23571bff --- /dev/null +++ b/src/shared/config/utils.ts @@ -0,0 +1,28 @@ +/** + * Reads the environment variable and returns an optional boolean. + * Depending on the input: + * - undefined or empty string -> undefined + * - "true" (case insensitive) -> true + * - "false" (case insensitive) -> false + * - any other string -> throws error + * + * @param key The name of the environment variable + */ +export function envToOptionalBoolean(key: string): boolean | undefined { + const value: string | undefined = process.env[key]; + + if (!value) { + return undefined; + } + + const lower: string | undefined = value.toLowerCase(); + + switch (lower) { + case 'true': + return true; + case 'false': + return false; + default: + throw new Error(`Expected environment variable "${key}" to be "true" or "false", received "${value}".`); + } +} diff --git a/test/config.test.json b/test/config.test.json index 68cb339ed..f355a6024 100644 --- a/test/config.test.json +++ b/test/config.test.json @@ -48,7 +48,7 @@ "DEFAULT_LOG_LEVEL": "info" }, "ITSLEARNING": { - "ENABLED": "false", + "ENABLED": false, "ENDPOINT": "https://itslearning-test.example.com", "USERNAME": "username", "PASSWORD": "password", @@ -64,7 +64,7 @@ "REALM": "defrealm" }, "OX": { - "ENABLED": "false", + "ENABLED": false, "ENDPOINT": "https://ox_ip:ox_port/webservices/", "USERNAME": "username", "PASSWORD": "password"