diff --git a/.codacy.yml b/.codacy.yml deleted file mode 100644 index ece54c71579..00000000000 --- a/.codacy.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -exclude_paths: - - ".idea" - - ".vscode" - - "**/backup/**" - - "**/migrations/**" - - "**/tests/**" - - "**.js" - - "**.spec.js" - - "**.spec.ts" diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7b19d3a17e7..0f19bc7837e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,14 +1,13 @@ # Description @@ -19,45 +18,16 @@ Base links to copy - https://ticketsystem.dbildungscloud.de/browse/BC-???? --> -## Changes -## Datasecurity - - -## Deployment - - -## New Repos, NPM pakages or vendor scripts - - ## Approval for review - [ ] DEV: If api was changed - `generate-client:server` was executed in vue frontend and changes were tested and put in a PR with the same branch name. - [ ] QA: In addition to review, the code has been manually tested (if manual testing is possible) - [ ] All points were discussed with the ticket creator, support-team or product owner. The code upholds all quality guidelines from the PR-template. -> Notice: Please remove the WIP label if the PR is ready to review, otherwise nobody will review it. diff --git a/.github/autolabeler.yml b/.github/autolabeler.yml deleted file mode 100644 index ebc2bbbfc48..00000000000 --- a/.github/autolabeler.yml +++ /dev/null @@ -1 +0,0 @@ -wip: * \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 43cbf08dcab..00000000000 --- a/SECURITY.md +++ /dev/null @@ -1,9 +0,0 @@ -# Security Policy - -## Supported Versions - -Please always use the latest release. - -## Reporting a Vulnerability - -Plase check https://dbildungscloud.de/security on how to report security issues. Thanks for your support. diff --git a/scripts/copy-legacy-tool-to-ctl.js b/scripts/copy-legacy-tool-to-ctl.js deleted file mode 100644 index 4f2455bd955..00000000000 --- a/scripts/copy-legacy-tool-to-ctl.js +++ /dev/null @@ -1,451 +0,0 @@ -/* eslint-disable no-await-in-loop */ -const mongoose = require('mongoose'); - -const { Schema } = mongoose; -const { program } = require('commander'); -const { v4: uuidv4 } = require('uuid'); - -program.requiredOption('-u, --url ', '(Required) URL of the MongoDB instance'); -program.parse(); - -const options = program.opts(); -const mongodbUrl = options.url; - -const close = async () => mongoose.connection.close(); - -const connect = async () => { - const mongooseOptions = { - useNewUrlParser: true, - useUnifiedTopology: true, - }; - - return mongoose.connect(mongodbUrl, mongooseOptions); -}; - -const customParameterEntrySchema = new Schema( - { - name: String, - value: String, - }, - { _id: false } -); - -const LtiTool = mongoose.model( - 'ltiTool0906202311481', - new Schema( - { - name: { type: String }, - url: { type: String, required: true }, - key: { type: String }, - secret: { type: String, required: true }, - logo_url: { type: String }, - lti_message_type: { type: String }, - lti_version: { type: String }, - resource_link_id: { type: String }, - roles: { - type: [ - { - type: String, - enum: ['Learner', 'Instructor', 'ContentDeveloper', 'Administrator', 'Mentor', 'TeachingAssistant'], - }, - ], - }, - privacy_permission: { - type: String, - enum: ['anonymous', 'e-mail', 'name', 'public', 'pseudonymous'], - default: 'anonymous', - }, - customs: { type: [{ key: { type: String }, value: { type: String } }] }, - isTemplate: { type: Boolean }, - isLocal: { type: Boolean }, - createdAt: { type: Date, default: Date.now }, - updatedAt: { type: Date, default: Date.now }, - originTool: { type: Schema.Types.ObjectId, ref: 'ltiTool0906202311481' }, - oAuthClientId: { type: String }, - friendlyUrl: { type: String, unique: true, sparse: true }, - skipConsent: { type: Boolean }, - openNewTab: { type: Boolean, default: false }, - frontchannel_logout_uri: { type: String }, - isHidden: { type: Boolean, default: false }, - }, - { - timestamps: true, - } - ), - 'ltitools' -); - -const ExternalTool = mongoose.model( - 'external_tool0906202311482', - new Schema( - { - name: { type: String, unique: true }, - url: String, - logoUrl: String, - config_type: String, - config_baseUrl: String, - config_clientId: String, - config_skipConsent: Boolean, - config_key: String, - config_secret: String, - config_lti_message_type: { - type: String, - enum: ['basic-lti-launch-request'], - }, - config_privacy_permission: { - type: String, - enum: ['anonymous', 'e-mail', 'name', 'public', 'pseudonymous'], - }, - parameters: [ - { - type: { - name: String, - displayName: String, - description: String, - default: String, - regex: String, - regexComment: String, - scope: { - type: String, - enum: ['global', 'school', 'context'], - }, - location: { - type: String, - enum: ['path', 'body', 'query'], - }, - type: { - type: String, - enum: ['string', 'number', 'boolean', 'auto_contextid', 'auto_contextname', 'auto_schoolid'], - }, - isOptional: Boolean, - isProtected: Boolean, - }, - }, - ], - - isHidden: Boolean, - openNewTab: Boolean, - version: Number, - isDeactivated: Boolean, - restrictToContexts: [], - }, - { - timestamps: true, - } - ), - 'external-tools' -); - -const SchoolExternalTool = mongoose.model( - 'school_external_tool0906202311483', - new Schema( - { - tool: { type: Schema.Types.ObjectId, ref: 'external_tool0906202311482' }, - school: { type: Schema.Types.ObjectId }, - schoolParameters: [customParameterEntrySchema], - toolVersion: Number, - isDeactivated: Boolean, - }, - { - timestamps: true, - } - ), - 'school-external-tools' -); - -const ContextExternalTool = mongoose.model( - 'context_external_tool0906202311484', - new Schema( - { - schoolTool: { type: Schema.Types.ObjectId, ref: 'school_external_tool0906202311483' }, - contextId: String, - contextType: { type: String, enum: ['course'] }, - displayName: String, - parameters: [customParameterEntrySchema], - toolVersion: Number, - }, - { - timestamps: true, - } - ), - 'context-external-tools' -); - -const Course = mongoose.model( - 'course0906202311485', - new mongoose.Schema( - { - school: Schema.Types.ObjectId, - ltiToolIds: [{ type: Schema.Types.ObjectId, ref: 'ltiTool0906202311481' }], - }, - { - timestamps: true, - } - ), - 'courses' -); - -const Pseudonym = mongoose.model( - 'pseudonym0906202311486', - new Schema( - { - userId: { type: Schema.Types.ObjectId }, - toolId: { type: Schema.Types.ObjectId, ref: 'ltiTool0906202311481' }, - pseudonym: { - type: String, - required: true, - unique: true, - default: uuidv4, - }, - }, - { - timestamps: true, - } - ), - 'pseudonyms' -); - -const Pseudonym2 = mongoose.model( - 'external-tool-pseudonyms0906202311486', - new Schema( - { - userId: { type: Schema.Types.ObjectId }, - toolId: { type: Schema.Types.ObjectId, ref: 'external_tool0906202311482' }, - pseudonym: { - type: String, - required: true, - unique: true, - default: uuidv4, - }, - }, - { - timestamps: true, - } - ), - 'external-tool-pseudonyms' -); - -function toolConfigMapper(ltiToolTemplate) { - let toolConfig = { - config_baseUrl: ltiToolTemplate.url, - config_type: 'basic', - }; - - if (ltiToolTemplate.oAuthClientId) { - toolConfig = { - ...toolConfig, - config_type: 'oauth2', - config_clientId: ltiToolTemplate.oAuthClientId, - config_skipConsent: ltiToolTemplate.skipConsent, - }; - } else if (ltiToolTemplate.key && ltiToolTemplate.key !== 'none') { - toolConfig = { - ...toolConfig, - config_type: 'lti11', - config_key: ltiToolTemplate.key, - config_secret: ltiToolTemplate.secret, - config_lti_message_type: ltiToolTemplate.lti_message_type, - config_privacy_permission: ltiToolTemplate.privacy_permission || 'anonymous', - }; - } - - return toolConfig; -} - -function mapToExternalTool(ltiToolTemplate) { - return { - name: ltiToolTemplate.name, - url: ltiToolTemplate.url, - logoUrl: ltiToolTemplate.logo_url, - parameters: [], - isHidden: ltiToolTemplate.isHidden, - openNewTab: ltiToolTemplate.openNewTab, - version: 1, - restrictToContexts: [], - isDeactivated: false, - ...toolConfigMapper(ltiToolTemplate), - }; -} - -function mapToSchoolExternalTool(externalTool, course) { - return { - tool: externalTool._id, - school: course.schoolId, - schoolParameters: [], - toolVersion: externalTool.version, - isDeactivated: false, - }; -} - -function mapToContextExternalTool(schoolExternalTool, course, externalToolName) { - return { - schoolTool: schoolExternalTool._id, - contextId: course._id, - contextType: 'course', - parameters: [], - toolVersion: schoolExternalTool.toolVersion, - displayName: externalToolName, - }; -} - -function mapPseudonyms(pseudonym, externalTool) { - return { - pseudonym: pseudonym.pseudonym, - toolId: externalTool._id, - userId: pseudonym.userId, - }; -} - -async function createPseudonyms(toolTemplate, externalTool) { - const pseudonymsLegacyTools = await Pseudonym.find({ - toolId: toolTemplate._id, - }) - .lean() - .exec(); - - const newPseudonyms = []; - for (const legacyPseudonym of pseudonymsLegacyTools) { - const existingPseudonym = await Pseudonym2.findOne({ - pseudonym: legacyPseudonym.pseudonym, - }) - .lean() - .exec(); - - if (!existingPseudonym) { - newPseudonyms.push({ - userId: legacyPseudonym.userId, - toolId: externalTool._id, - pseudonym: legacyPseudonym.pseudonym, - }); - } - } - await Pseudonym2.insertMany(newPseudonyms); -} - -async function createExternalTool(toolTemplate) { - let externalTool = await ExternalTool.findOne({ - name: { $regex: `${toolTemplate.name}` }, - }) - .lean() - .exec(); - - if (!externalTool) { - externalTool = mapToExternalTool(toolTemplate); - externalTool = (await ExternalTool.insertMany(externalTool))[0]; - } - - createPseudonyms(toolTemplate, externalTool); - - return externalTool; -} - -async function createSchoolExternalTool(externalTool, course) { - let schoolExternalTool = await SchoolExternalTool.findOne({ - school: course.schoolId, - tool: externalTool._id, - }) - .lean() - .exec(); - - // CHECK IF SCHOOLEXTERNALTOOL EXISTS - if (!schoolExternalTool) { - schoolExternalTool = mapToSchoolExternalTool(externalTool, course); - schoolExternalTool = (await SchoolExternalTool.insertMany(schoolExternalTool))[0]; - } - - return schoolExternalTool; -} - -async function createContextExternalTool(schoolExternalTool, course, externalToolName) { - const contextExternalTools = await ContextExternalTool.find({ - schoolTool: schoolExternalTool._id, - contextId: course._id, - contextType: 'course', - }) - .lean() - .exec(); - - // CHECK IF CONTEXTEXTERNALTOOL EXISTS - if ((contextExternalTools || []).length === 0) { - const contextExternalTool = mapToContextExternalTool(schoolExternalTool, course, externalToolName); - await ContextExternalTool.insertMany(contextExternalTool); - } -} - -const up = async () => { - await connect(); - - // FIND ALL LTI TOOL TEMPLATES - const ltiToolTemplates = await LtiTool.find({ - $or: [{ name: { $regex: /Bettermarks/i } }, { name: { $regex: /Nextcloud/i } }], - isTemplate: true, - }) - .lean() - .exec(); - - if ((ltiToolTemplates || []).length === 0) { - return Promise.reject(new Error('No LtiTool Template found.')); - } - - const ltiToolExternalToolIdTupelList = []; - - // FIND EXTERNAL TOOLS - const externalTools = await Promise.all( - ltiToolTemplates.map(async (toolTemplate) => { - const externalTool = await createExternalTool(toolTemplate); - ltiToolExternalToolIdTupelList.push({ - templateId: toolTemplate._id, - externalToolId: externalTool._id, - }); - - return externalTool; - }) - ); - - // FIND ALL LEGACY TOOLS - const ltiTools = await LtiTool.find({ - $or: [{ name: { $regex: /Bettermarks/i } }, { name: { $regex: /Nextcloud/i } }], - isTemplate: false, - }) - .lean() - .exec(); - - for (const ltiTool of ltiTools) { - // GET COURSE - const course = await Course.findOne({ - ltiToolIds: { $in: [ltiTool._id] }, - }) - .lean() - .exec(); - - if (!course) { - console.info(`No course found with LtiToolId: ${ltiTool._id}.`); - // eslint-disable-next-line no-continue - continue; - } - - // GET EXTERNALTOOL - const ltiToolExternalToolIdTupel = ltiToolExternalToolIdTupelList.find( - (idTupel) => idTupel.templateId.toString() === ltiTool.originTool.toString() - ); - const externalTool = externalTools.find((tool) => tool._id === ltiToolExternalToolIdTupel.externalToolId); - - // GET SCHOOLEXTERNALTOOL - const schoolExternalTool = await createSchoolExternalTool(externalTool, course); - - // CREATE CONTEXTEXTERNALTOOL - await createContextExternalTool(schoolExternalTool, course, externalTool.name); - } - - await close(); - return Promise.resolve(); -}; - -(async () => { - try { - await up(); - } catch (e) { - console.error(e); - process.exit(1); - } -})(); diff --git a/scripts/migrate-legacy-bbb.js b/scripts/migrate-legacy-bbb.js deleted file mode 100644 index 32d5a0c888e..00000000000 --- a/scripts/migrate-legacy-bbb.js +++ /dev/null @@ -1,109 +0,0 @@ -const mongoose = require('mongoose'); -// eslint-disable-next-line no-unused-vars - -const { Schema } = mongoose; - -const { program } = require('commander'); - -program.requiredOption('-u, --url ', '(Required) URL of the MongoDB instance'); -program.parse(); - -const options = program.opts(); -const mongodbUrl = options.url; - -const close = async () => mongoose.connection.close(); - -const connect = async () => { - const mongooseOptions = { - useNewUrlParser: true, - useUnifiedTopology: true, - }; - - return mongoose.connect(mongodbUrl, mongooseOptions); -}; - -const COURSE_FEATURES = { - VIDEOCONFERENCE: 'videoconference', -}; - -const LtiTool = mongoose.model( - 'ltiTools1688028372783', - new mongoose.Schema( - { - isTemplate: { type: Boolean }, - name: { type: String }, - }, - { - timestamps: true, - } - ), - 'ltitools' -); - -const Course = mongoose.model( - 'course1688028372783', - new mongoose.Schema( - { - ltiToolIds: [{ type: Schema.Types.ObjectId, required: true, ref: 'ltiTools1688028372783' }], - features: [{ type: String, enum: Object.values(COURSE_FEATURES) }], - }, - { - timestamps: true, - } - ), - 'courses' -); - -const up = async () => { - await connect(); - - // find all non-template bbb tools - const bbbTools = await LtiTool.find({ - $and: [{ name: 'Video-Konferenz mit BigBlueButton' }, { isTemplate: false }], - }) - .lean() - .exec(); - - if ((bbbTools || []).length === 0) { - console.error('No non-template videoconferences found. Nothing to migrate.'); - return; - } - - console.log(`Found ${bbbTools.length} tool(s) to migrate.`); - - // find all courses that use BBB - const coursesWithBbb = await Course.find({ ltiToolIds: { $in: bbbTools } }) - .lean() - .exec(); - - if ((coursesWithBbb || []).length === 0) { - console.error('No courses with BBB found. Nothing to migrate.'); - return; - } - - console.log(`Found ${coursesWithBbb.length} course(s) to update.`); - - // add videoconference feature to courses that use bbb - const addFeature = async () => { - for (const course of coursesWithBbb) { - await Course.updateOne( - { _id: course._id }, - { $addToSet: { features: { $each: [COURSE_FEATURES.VIDEOCONFERENCE] } } } - ).exec(); - } - }; - await addFeature(); - - console.log(`Updated ${coursesWithBbb.length} courses.`); - - await close(); -}; - -(async () => { - try { - await up(); - } catch (e) { - console.error(e); - process.exit(1); - } -})();