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

BC-8621 move sync-indexes (legacy) to management api #5408

Merged
merged 9 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
21 changes: 21 additions & 0 deletions ansible/roles/schulcloud-server-init/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@
tags:
- configmap

- name: Management Api Configmap File
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: management-configmap.yml.j2
when: WITH_SCHULCLOUD_INIT
tags:
- configmap

- name: Remove Management Api Configmap File
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
state: absent
api_version: v1
kind: ConfigMap
name: api-management-configmap
when: not WITH_SCHULCLOUD_INIT
tags:
- configmap

- name: Management Deployment
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,46 @@ metadata:
data:
update.sh: |
#! /bin/bash
# necessary for secret handling and legacy indexes
git clone https://github.com/hpi-schul-cloud/schulcloud-server.git
cd /schulcloud-server
git checkout {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
npm ci
until mongosh $DATABASE__URL --eval "print(\"waited for connection\")"
do
sleep 1
done
mongosh $DATABASE__URL --eval 'rs.initiate({"_id" : "rs0", "members" : [{"_id" : 0, "host" : "localhost:27017"}]})'
sleep 3
if [[ $(mongosh --quiet --eval "db.isMaster().setName") != rs0 ]]
then
echo "replicaset config failed :("
else
echo "gg, hacky mongo replicaset"
fi
echo "seeding database"
curl --retry 360 --retry-all-errors --retry-delay 10 -X POST 'http://mgmt-svc:3333/api/management/database/seed?with-indexes=true'

get_secret() {
local plainText="$1"
local url="http://mgmt-svc:3333/api/management/database/encrypt-plain-text"

if [[ -z "$plainText" ]]; then
echo "Error: No plainText argument provided."
return 1
fi

# Use curl to capture both the response body and status code
local response
local http_code
response=$(curl -s -w "\n%{http_code}" -X POST "$url" \
-H "Content-Type: text/plain" \
--data "$plainText")

# Split the response into body and status code
http_code=$(echo "$response" | tail -n 1)
response_body=$(echo "$response" | head -n -1)

# Check if the response status code is 200
if [[ "$http_code" -ne 200 ]]; then
echo "Error: Request failed with status code $http_code."
return 1
fi

# Return the response body (presumably the secret)
echo "$response_body"
return 0
}
# Below is a series of a MongoDB-data initializations, meant for the development and testing
# purposes on various dev environments - most of them will only work there.

# Test OIDC system configuration used in conjunction with OIDCMOCK deployment.
OIDCMOCK_CLIENT_SECRET=$(node scripts/secret.js -s $AES_KEY -e $OIDCMOCK__CLIENT_SECRET)
OIDCMOCK_CLIENT_SECRET=$(get_secret $OIDCMOCK__CLIENT_SECRET)
# Test LDAP server (deployed in the sc-common namespace) configuration (stored in the 'systems' collection).
SEARCH_USER_PASSWORD=$(node scripts/secret.js -s $LDAP_PASSWORD_ENCRYPTION_KEY -e $SC_COMMON_LDAP_PASSWORD)
SEARCH_USER_PASSWORD=$(get_secret $SC_COMMON_LDAP_PASSWORD)
mongosh $DATABASE__URL --quiet --eval 'db.systems.insertMany([
{
"type" : "ldap",
Expand Down Expand Up @@ -149,7 +162,7 @@ data:
} );'

# Sanis configuration (stored in the 'systems' collection + some related documents in other collections).
SANIS_CLIENT_SECRET=$(node scripts/secret.js -s $AES_KEY -e $SANIS_CLIENT_SECRET)
SANIS_CLIENT_SECRET=$(get_secret $SANIS_CLIENT_SECRET)
SANIS_SYSTEM_ID=0000d186816abba584714c93
if [[ $SC_THEME == "n21" ]]; then
mongosh $DATABASE__URL --quiet --eval 'db.schools.updateMany(
Expand Down Expand Up @@ -223,8 +236,8 @@ data:
ISERV_SYSTEM_ID=0000d186816abba584714c92

# Encrypt secrets that contain IServ's OAuth client secret and LDAP server's search user password.
ISERV_OAUTH_CLIENT_SECRET=$(node scripts/secret.js -s $AES_KEY -e $ISERV_OAUTH_CLIENT_SECRET)
ISERV_LDAP_SEARCH_USER_PASSWORD=$(node scripts/secret.js -s $AES_KEY -e $ISERV_LDAP_SEARCH_USER_PASSWORD)
ISERV_OAUTH_CLIENT_SECRET=$(get_secret $ISERV_OAUTH_CLIENT_SECRET)
ISERV_LDAP_SEARCH_USER_PASSWORD=$(get_secret $ISERV_LDAP_SEARCH_USER_PASSWORD)

# Add (or replace) document with the Dev IServ configuration.
mongosh $DATABASE__URL --quiet --eval 'db.systems.replaceOne(
Expand Down Expand Up @@ -269,7 +282,7 @@ data:
UNIVENTION_LDAP_FEDERAL_STATE_ID=0000b186816abba584714c53

# Encrypt LDAP server's search user password.
UNIVENTION_LDAP_SEARCH_USER_PASSWORD=$(node scripts/secret.js -s $AES_KEY -e $UNIVENTION_LDAP_SEARCH_USER_PASSWORD)
UNIVENTION_LDAP_SEARCH_USER_PASSWORD=$(get_secret $UNIVENTION_LDAP_SEARCH_USER_PASSWORD)

# Add (or replace) document with the test BRB Univention LDAP system configuration.
mongosh $DATABASE__URL --quiet --eval 'db.systems.replaceOne(
Expand Down Expand Up @@ -329,40 +342,6 @@ data:

# Perform the final Bettermarks config data init if client secret and URL has been properly set.
if [ -n "$BETTERMARKS_CLIENT_SECRET" ] && [ -n "$BETTERMARKS_URL" ] && [ -n "$BETTERMARKS_REDIRECT_DOMAIN" ]; then
# Add document to the 'ltitools' collection with Bettermarks tool configuration.
mongosh $DATABASE__URL --quiet --eval 'db.getCollection("ltitools").replaceOne(
{
"name": "bettermarks",
"isTemplate": true
},
{
"roles": [],
"privacy_permission": "anonymous",
"openNewTab": true,
"name": "bettermarks",
"url": "'$BETTERMARKS_URL'",
"key": null,
"secret": "'$BETTERMARKS_CLIENT_SECRET'",
"logo_url": "'$BETTERMARKS_LOGO_URL'",
"oAuthClientId": "'$BETTERMARKS_OAUTH_CLIENT_ID'",
"isLocal": true,
"resource_link_id": null,
"lti_version": null,
"lti_message_type": null,
"isTemplate": true,
"skipConsent": false,
"customs": [],
"createdAt": new Date(),
"updatedAt": new Date(),
"__v": 0,
"isHidden": false,
"frontchannel_logout_uri": null
},
{
"upsert": true
}
);'

# The two steps below (Hydra call and MongoDB insert) were added to automate the actions performed inside
# the server when Bettermarks' OAuth client configuration is added manually in SuperHero Dashboard.

Expand Down Expand Up @@ -423,42 +402,6 @@ data:

# This configures nextcloud in superhero dashboard as oauth2 tool and also in hydra
if [ -n "$NEXTCLOUD_CLIENT_SECRET" ] && [ -n "$NEXTCLOUD_SOCIALLOGIN_OIDC_INTERNAL_NAME" ]; then
echo "Inserting nextcloud to ltitools..."
# Add document to the 'ltitools' collection
mongosh $DATABASE__URL --quiet --eval 'db.getCollection("ltitools").updateOne(
{
"name": "'$NEXTCLOUD_SOCIALLOGIN_OIDC_INTERNAL_NAME'",
"isTemplate": true
},
{ $setOnInsert: {
"roles": [],
"privacy_permission": "anonymous",
"openNewTab": true,
"name": "'$NEXTCLOUD_SOCIALLOGIN_OIDC_INTERNAL_NAME'",
"url": "'$NEXTCLOUD_BASE_URL'",
"key": null,
"secret": "'$NEXTCLOUD_CLIENT_SECRET'",
"logo_url": "",
"oAuthClientId": "'$NEXTCLOUD_CLIENT_ID'",
"isLocal": true,
"resource_link_id": null,
"lti_version": null,
"lti_message_type": null,
"isTemplate": true,
"skipConsent": true,
"customs": [],
"createdAt": new Date(),
"updatedAt": new Date(),
"__v": 0,
"isHidden": true,
"frontchannel_logout_uri": "'$NEXTCLOUD_BASE_URL'apps/schulcloud/logout"
} },
{
"upsert": true
}
);'
echo "Inserted nextcloud to ltitools."

# Add Nextcloud client in hydra
echo "POSTing nextcloud to hydra..."
curl --retry 10 --retry-all-errors --retry-delay 10 \
Expand Down Expand Up @@ -521,7 +464,7 @@ data:

if [ -n "$CTL_SEED_SECRET_ONLINE_DIA_MATHE" ]; then
# Encrypt secrets of external tools that contain an lti11 config.
CTL_SEED_SECRET_ONLINE_DIA_MATHE=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_MATHE)
CTL_SEED_SECRET_ONLINE_DIA_MATHE=$(get_secret $CTL_SEED_SECRET_ONLINE_DIA_MATHE)
mongosh $DATABASE__URL --quiet --eval 'db.getCollection("external-tools").updateOne(
{
"name": "Product Test Onlinediagnose Grundschule - Mathematik",
Expand All @@ -536,7 +479,7 @@ data:
fi
if [ -n "$CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH" ]; then
# Encrypt secrets of external tools that contain an lti11 config.
CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH)
CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH=$(get_secret $CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH)
mongosh $DATABASE__URL --quiet --eval 'db.getCollection("external-tools").updateOne(
{
"name": "Product Test Onlinediagnose Grundschule - Deutsch",
Expand All @@ -551,7 +494,7 @@ data:
fi
if [ -n "$CTL_SEED_SECRET_MERLIN" ]; then
# Encrypt secrets of external tools that contain an lti11 config.
CTL_SEED_SECRET_MERLIN=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_MERLIN)
CTL_SEED_SECRET_MERLIN=$(get_secret $CTL_SEED_SECRET_MERLIN)
mongosh $DATABASE__URL --quiet --eval 'db.getCollection("external-tools").updateOne(
{
"name": "Merlin Bibliothek",
Expand Down Expand Up @@ -583,7 +526,7 @@ data:
if [[ $SC_THEME == "thr" ]]; then
echo "Adding TSP system to systems collection"

TSP_SYSTEM_OAUTH_CLIENT_SECRET=$(node scripts/secret.js -s $AES_KEY -e $TSP_SYSTEM_OAUTH_CLIENT_SECRET)
TSP_SYSTEM_OAUTH_CLIENT_SECRET=$(get_secret $TSP_SYSTEM_OAUTH_CLIENT_SECRET)
mongosh $DATABASE__URL --quiet --eval 'db.systems.insertOne(
{
"_id": ObjectId("66d707f5c5202ba10c5e6256"),
Expand Down Expand Up @@ -611,5 +554,3 @@ data:
fi
# ========== End of TSP system creation

# Database indexes synchronization, it's crucial until we have all the entities in NestJS app.
npm run syncIndexes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: api-management-configmap
namespace: {{ NAMESPACE }}
labels:
app: management-deployment
data:
NEST_LOG_LEVEL: "{{ NEST_LOG_LEVEL }}"
EXIT_ON_ERROR: "true"
ENABLE_SYNC_LEGACY_INDEXES_VIA_FEATHERS_SERVICE: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ spec:
envFrom:
- configMapRef:
name: api-configmap
- configMapRef:
name: api-management-configmap
- secretRef:
name: api-secret
- secretRef:
Expand Down
21 changes: 16 additions & 5 deletions apps/server/src/apps/management.app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import express from 'express';

// register source-map-support for debugging
import { install as sourceMapInstall } from 'source-map-support';

// application imports
import { LegacyLogger } from '@src/core/logger';
import { ManagementServerModule } from '@modules/management';
import { MikroORM } from '@mikro-orm/core';
import legacyAppPromise = require('../../../../src/app');
import { createRequestLoggerMiddleware } from './helpers/request-logger-middleware';
import { enableOpenApiDocs } from './helpers';

Expand All @@ -21,12 +20,24 @@ async function bootstrap() {

const nestExpressAdapter = new ExpressAdapter(nestExpress);
const nestApp = await NestFactory.create(ManagementServerModule, nestExpressAdapter);
const orm = nestApp.get(MikroORM);

nestApp.use(createRequestLoggerMiddleware());

// WinstonLogger
nestApp.useLogger(await nestApp.resolve(LegacyLogger));

// load the legacy feathers/express server
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const feathersExpress = await legacyAppPromise(orm);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
await feathersExpress.setup();

// set reference to legacy app as an express setting so we can
// access it over the current request within FeathersServiceProvider
// TODO remove if not needed anymore, needed for legacy indexes
nestExpress.set('feathersApp', feathersExpress);

// customize nest app settings
nestApp.enableCors();
enableOpenApiDocs(nestApp, 'docs');
Expand All @@ -45,8 +56,8 @@ async function bootstrap() {

console.log('#################################');
console.log(`### Start Management Server ###`);
console.log(`### Port: ${port} ###`);
console.log(`### Base path: ${basePath} ###`);
console.log(`### Port: ${port} ###`);
console.log(`### Base path: ${basePath} ###`);
console.log('#################################');
}
void bootstrap();
4 changes: 2 additions & 2 deletions apps/server/src/infra/encryption/encryption.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { LegacyLogger, LoggerModule } from '@src/core/logger';
import { DefaultEncryptionService, LdapEncryptionService } from './encryption.interface';
import { SymetricKeyEncryptionService } from './encryption.service';
import { SymmetricKeyEncryptionService } from './encryption.service';

function encryptionProviderFactory(configService: ConfigService, logger: LegacyLogger, aesKey: string) {
const key = configService.get<string>(aesKey);
return new SymetricKeyEncryptionService(logger, key);
return new SymmetricKeyEncryptionService(logger, key);
}

@Module({
Expand Down
6 changes: 3 additions & 3 deletions apps/server/src/infra/encryption/encryption.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createMock } from '@golevelup/ts-jest';
import { LegacyLogger } from '@src/core/logger';
import { SymetricKeyEncryptionService } from './encryption.service';
import { SymmetricKeyEncryptionService } from './encryption.service';

describe('SymetricKeyEncryptionService', () => {
describe('with configure encryption key', () => {
const encryptionKey = 'abcdefghijklmnop';
const logger = createMock<LegacyLogger>();
const encryptionService = new SymetricKeyEncryptionService(logger, encryptionKey);
const encryptionService = new SymmetricKeyEncryptionService(logger, encryptionKey);
const testInput = 'testInput';

it('encrypts the input', () => {
Expand All @@ -33,7 +33,7 @@ describe('SymetricKeyEncryptionService', () => {

describe('without configured encryption key', () => {
const logger = createMock<LegacyLogger>();
const encryptionService = new SymetricKeyEncryptionService(logger);
const encryptionService = new SymmetricKeyEncryptionService(logger);
const testInput = 'testInput';

beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/infra/encryption/encryption.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LegacyLogger } from '@src/core/logger';
import { EncryptionService } from './encryption.interface';

@Injectable()
export class SymetricKeyEncryptionService implements EncryptionService {
export class SymmetricKeyEncryptionService implements EncryptionService {
constructor(private logger: LegacyLogger, private key?: string) {
if (!this.key) {
this.logger.warn('No AES key defined. Encryption will no work');
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/infra/feathers/feathers-service.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ export interface FeathersService {
*
* @param id
* @param params
* @deprecated Access legacy eathers service get method
* @deprecated Access legacy feathers service get method
*/
get(id: string, params?: FeathersServiceParams): Promise<FeathersServiceResponse>;
/**
*
* @param params
* @deprecated Access legacy eathers service find method
* @deprecated Access legacy feathers service find method
*/
find(params?: FeathersServiceParams): Promise<FeathersServiceResponse>;
/**
Expand Down
Loading
Loading