Commit 828cb5b0 authored by Christopher Mark Fullarton's avatar Christopher Mark Fullarton

Merge remote-tracking branch 'origin/master'

parents 341379c1 ab3eb3df
{"export":{"page_header":"&LQuiz-Statistik und Ranglisten generiert in einer Live-Session mit arsnova.click&R__createdAt__","page_footer":"&L&A&Carsnova.click ist Open Source Software, siehe arsnova.io&R &P/&N","summary":"Zusammenfassung","exported_at_date":"Exportiert am","session_content":"Archivierte Quizfragen (Um das Quiz zu importieren, kopiere den vollständigen Zellinhalt in eine leere .json-Datei. Importiere dann die Datei mit dem entsprechenden Icon in der Fußzeile der App.)","cas_account_id":"CAS Account-ID","cas_account_email":"CAS E-Mail","quiz_name":"Quizname","question":"Quizfrage","question_type":"Typ der Quizfrage","number_of_answers":"Anzahl der Antworten","percent_correct":"Korrekte Antworten (in %)","answer":"Antwort","attendee":"Teilnehmer/in","attendee_complete_correct":"Teilnehmer/innen mit vollständig richtig beantworteten Fragen","attendee_complete_correct_none_available":"Keine Einträge vorhanden","correct_questions_none_available":"Keine","attendee_all_entries":"Alle Teilnehmer/innen","time":"Antwortzeit (in ms)","confidence_level":"Antwortsicherheit","correct_value":"Richtiger Wert","min_range":"Mindestwert","max_range":"Maximalwert","correct_questions":"Richtig beantwortete Fragen","overall_response_time":"Kumulierte Antwortzeit (in ms)","average_response_time":"Durchschnittliche Antwortzeit (in ms)","number_attendees":"Anzahl der Teilnehmer/innen","average_number_attendees_participated":"Durchschnittliche Teilnahmen am Quiz (in %)","average_correct_answered_questions":"Durchschnittlich richtig beantwortete Fragen","average_confidence":"Durchschnittliche Antwortsicherheit (in %)","exported_at":"um","exported_at_time":"Uhr","type":{"SurveyQuestion":"Umfragen","TrueFalseSingleChoiceQuestion":"Wahr-Falsch Frage","YesNoSingleChoiceQuestion":"Ja-Nein Frage","ABCDSingleChoiceQuestion":"ABCD Single-Choice Frage","SingleChoiceQuestion":"Single-Choice Frage","RangedQuestion":"Schätzfrage","FreeTextQuestion":"Freitext Frage","MultipleChoiceQuestion":"Multiple-Choice Frage"}},"view":{"answeroptions":{"free_text_question":{"config_case_sensitive":"Groß- und Kleinschreibung beachten","config_trim_whitespaces":"Leerzeichen entfernen","config_use_keywords":"Reihenfolge der Wörter beachten","config_use_punctuation":"Interpunktion beachten"}}},"global":{"no":"Nein","yes":"Ja"}}
\ No newline at end of file
{
"export": {
"page_header": "&LQuiz-Statistik und Ranglisten generiert in einer Live-Session mit arsnova.click&R__createdAt__",
"page_footer": "&L&A&Carsnova.click ist Open Source Software, siehe arsnova.io&R &P/&N",
"summary": "Zusammenfassung",
"exported_at_date": "Exportiert am",
"session_content": "Archivierte Quizfragen (Um das Quiz zu importieren, kopiere den vollständigen Zellinhalt in eine leere .json-Datei. Importiere dann die Datei mit dem entsprechenden Icon in der Fußzeile der App.)",
"cas_account_id": "CAS Account-ID",
"cas_account_email": "CAS E-Mail",
"quiz_name": "Quizname",
"question": "Quizfrage",
"question_type": "Typ der Quizfrage",
"number_of_answers": "Anzahl der Antworten",
"percent_correct": "Korrekte Antworten (in %)",
"answer": "Antwort",
"attendee": "Teilnehmer/in",
"attendee_complete_correct": "Teilnehmer/innen mit vollständig richtig beantworteten Fragen",
"attendee_complete_correct_none_available": "Keine Einträge vorhanden",
"correct_questions_none_available": "Keine",
"attendee_all_entries": "Alle Teilnehmer/innen",
"time": "Antwortzeit (in ms)",
"confidence_level": "Antwortsicherheit",
"correct_value": "Richtiger Wert",
"min_range": "Mindestwert",
"max_range": "Maximalwert",
"correct_questions": "Richtig beantwortete Fragen",
"overall_response_time": "Kumulierte Antwortzeit (in ms)",
"average_response_time": "Durchschnittliche Antwortzeit (in ms)",
"number_attendees": "Anzahl der Teilnehmer/innen",
"average_number_attendees_participated": "Durchschnittliche Teilnahmen am Quiz (in %)",
"average_correct_answered_questions": "Durchschnittlich richtig beantwortete Fragen",
"average_confidence": "Durchschnittliche Antwortsicherheit (in %)",
"exported_at": "um",
"exported_at_time": "Uhr",
"type": {
"SurveyQuestion": "Umfrage",
"TrueFalseSingleChoiceQuestion": "Wahr-Falsch Frage",
"YesNoSingleChoiceQuestion": "Ja-Nein Frage",
"ABCDSingleChoiceQuestion": "ABCD Single-Choice Frage",
"SingleChoiceQuestion": "Single-Choice Frage",
"RangedQuestion": "Schätzfrage",
"FreeTextQuestion": "Freitext Frage",
"MultipleChoiceQuestion": "Multiple-Choice Frage"
}
},
"view": {
"answeroptions": {
"free_text_question": {
"config_case_sensitive": "Groß- und Kleinschreibung beachten",
"config_trim_whitespaces": "Leerzeichen entfernen",
"config_use_keywords": "Reihenfolge der Wörter beachten",
"config_use_punctuation": "Interpunktion beachten"
}
}
},
"global": {
"no": "Nein",
"yes": "Ja"
}
}
\ No newline at end of file
......@@ -40,7 +40,7 @@ class DumpCryptor {
let salt = null;
if (fs.existsSync(this.pathToSalt)) {
salt = JSON.parse(fs.readFileSync(this.pathToSalt));
salt = JSON.parse(fs.readFileSync(this.pathToSalt, {encoding: 'UTF-8'}));
} else {
const bytesInSalt = 128 / 8;
salt = CryptoJS.lib.WordArray.random(bytesInSalt);
......
......@@ -15,80 +15,73 @@
"dependency-check": "npx --ignore-existing madge --circular --extensions ts src"
},
"dependencies": {
"api-spec-converter": "^2.7.32",
"body-parser": "^1.18.3",
"bull": "^3.7.0",
"api-spec-converter": "^2.8.3",
"body-parser": "^1.19.0",
"bunyan": "^1.8.12",
"cas": "0.0.3",
"compression": "^1.7.4",
"connect-busboy": "0.0.2",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"crypto-js": "^3.1.9-1",
"cssstyle": "^1.2.2",
"excel4node": "^1.7.1",
"express": "^4.16.4",
"excel4node": "^1.7.2",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"file-type": "^10.10.0",
"file-type": "^12.0.0",
"gitlab": "^4.5.1",
"i18n": "^0.8.3",
"jsonwebtoken": "^8.5.1",
"lowdb": "^1.0.0",
"mathjax-node": "^2.1.1",
"messageformat": "^2.1.0",
"mime": "^2.4.2",
"mime-types": "^2.1.22",
"messageformat": "^2.2.1",
"mime": "^2.4.4",
"mime-types": "^2.1.24",
"minimist": "^1.2.0",
"mongoose": "^5.4.22",
"morgan": "^1.9.1",
"multer": "^1.4.1",
"node-sass-middleware": "0.11.0",
"nodemailer": "^5.1.1",
"nodemailer": "^6.2.1",
"reflect-metadata": "^0.1.13",
"request": "^2.88.0",
"request-promise-native": "^1.0.7",
"routing-controllers": "^0.7.7",
"routing-controllers-openapi": "^1.6.0",
"source-map-support": "^0.5.11",
"swagger-ui-express": "^4.0.2",
"typegoose": "^5.6.0",
"url-join": "^4.0.0",
"ws": "^6.2.1",
"routing-controllers-openapi": "^1.7.0",
"source-map-support": "^0.5.12",
"swagger-ui-express": "^4.0.6",
"typegoose": "^5.7.2",
"ws": "^7.0.1",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@types/body-parser": "1.17.0",
"@types/bull": "^3.5.11",
"@types/bull": "^3.5.14",
"@types/bunyan": "^1.8.6",
"@types/busboy": "^0.2.3",
"@types/chai": "^4.1.7",
"@types/chai-http": "3.0.5",
"@types/chai-http": "4.2.0",
"@types/compression": "^0.0.36",
"@types/cors": "^2.8.4",
"@types/cors": "^2.8.5",
"@types/crypto-js": "^3.1.43",
"@types/express": "^4.16.1",
"@types/express": "^4.17.0",
"@types/file-type": "^10.9.1",
"@types/i18n": "^0.8.5",
"@types/jsonwebtoken": "^8.3.2",
"@types/lowdb": "^1.0.7",
"@types/minimist": "^1.2.0",
"@types/mocha": "^5.2.6",
"@types/mocha": "^5.2.7",
"@types/mongoose": "^5.3.24",
"@types/morgan": "^1.7.35",
"@types/node": "^10.14.4",
"@types/node": "^12.0.10",
"@types/request": "^2.48.1",
"@types/request-promise-native": "^1.0.15",
"@types/request-promise-native": "^1.0.16",
"@types/websocket": "0.0.40",
"@types/ws": "^6.0.1",
"@types/xml2js": "^0.4.4",
"chai": "^4.2.0",
"chai-http": "^4.2.1",
"mocha": "^5.2.0",
"chai-http": "^4.3.0",
"mocha": "^6.1.4",
"mocha-typescript": "^1.1.17",
"nyc": "^13.3.0",
"ts-loader": "^5.3.3",
"ts-node": "^8.0.3",
"tslint": "^5.15.0",
"typescript": "^3.4.2"
"nyc": "^14.1.1",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"typescript": "^3.5.2"
}
}
......@@ -63,11 +63,9 @@ class MongoDbConnector {
this._mongoURL += `${mongoHost}:${mongoPort}/${mongoDatabase}`;
const mongoURLOptions = [];
if (process.env.MONGODB_REPLICA_NAME) {
mongoURLOptions.push(`replicaSet=${mongoReplSet}`);
}
mongoURLOptions.push(`replicaSet=${mongoReplSet}`);
if (process.env.MONGODB_AUTH_SOURCE) {
if (Boolean(process.env.MONGODB_AUTH_SOURCE)) {
if (process.env.MONGODB_AUTH_SOURCE === 'true') {
mongoURLOptions.push(`authSource=${process.env.MONGODB_AUTH_SOURCE}`);
}
} else {
......
......@@ -94,6 +94,14 @@ class UserDAO extends AbstractDAO<{ [key: string]: IUserEntity }> {
return this.storage[name];
}
public getUserByTokenHash(tokenHash: string): IUserEntity {
if (this.isEmptyVars(tokenHash)) {
return null;
}
return Object.values(this.storage).find(user => user.tokenHash === tokenHash);
}
public getUserById(id: ObjectId): IUserEntity {
return Object.values(this.storage).find(val => val.id.equals(id));
}
......
......@@ -6,6 +6,16 @@ import { AuthService } from '../services/AuthService';
import { AbstractEntity } from './AbstractEntity';
export class UserEntity extends AbstractEntity implements IUserEntity {
private _tokenHash: string;
get tokenHash(): string {
return this._tokenHash;
}
set tokenHash(value: string) {
this._tokenHash = value;
}
private _token: string;
get token(): string {
......@@ -73,6 +83,7 @@ export class UserEntity extends AbstractEntity implements IUserEntity {
this._name = data.name;
this._privateKey = data.privateKey;
this._passwordHash = data.passwordHash;
this._tokenHash = data.tokenHash;
this._gitlabToken = data.gitlabToken;
this._token = data.token;
this._userAuthorizations = data.userAuthorizations.map(val => UserRole[val]);
......@@ -94,6 +105,7 @@ export class UserEntity extends AbstractEntity implements IUserEntity {
token: this.token,
name: this.name,
passwordHash: this.passwordHash,
tokenHash: this.tokenHash,
privateKey: this.privateKey,
gitlabToken: this.gitlabToken,
userAuthorizations: this.userAuthorizations,
......
......@@ -183,7 +183,7 @@ export class MemberEntity extends AbstractEntity implements IMemberEntity {
responses[i] = {
value: [],
responseTime: -1,
confidence: 0,
confidence: -1,
readingConfirmation: false,
};
}
......
import * as xlsx from 'excel4node';
import * as MessageFormat from 'messageformat';
import MemberDAO from '../db/MemberDAO';
import { AbstractQuestionEntity } from '../entities/question/AbstractQuestionEntity';
import { IMemberEntity } from '../interfaces/entities/Member/IMemberEntity';
import { ILeaderBoardItemBase } from '../interfaces/leaderboard/ILeaderBoardItemBase';
import { IQuizEntity } from '../interfaces/quizzes/IQuizEntity';
......@@ -78,7 +77,7 @@ export abstract class ExcelWorksheet {
});
} else {
this._responsesWithConfidenceValue = MemberDAO.getMembersOfQuiz(this._quiz.name).filter(nickname => {
return nickname.responses.filter(responseItem => responseItem.confidence > -1);
return nickname.responses.some(responseItem => responseItem.confidence > -1);
});
}
if (this._responsesWithConfidenceValue.length > 0) {
......@@ -100,27 +99,8 @@ export abstract class ExcelWorksheet {
protected getLeaderboardData(questionIndex: number): Array<ILeaderBoardItemBase> {
const leaderBoard = new Leaderboard();
const correctResponses: any = {};
const question: AbstractQuestionEntity = this.quiz.questionList[questionIndex];
MemberDAO.getMembersOfQuiz(this._quiz.name).forEach(attendee => {
if (leaderBoard.isCorrectResponse(attendee.responses[questionIndex], question) === 1) {
if (!correctResponses[attendee.name]) {
correctResponses[attendee.name] = {
responseTime: 0,
correctQuestions: [],
confidenceValue: 0,
};
}
correctResponses[attendee.name].responseTime += <number>attendee.responses[questionIndex].responseTime;
correctResponses[attendee.name].correctQuestions.push(questionIndex);
correctResponses[attendee.name].confidenceValue += <number>attendee.responses[questionIndex].confidence;
} else {
delete correctResponses[attendee.name];
}
});
return leaderBoard.objectToArray(correctResponses);
const { correctResponses } = leaderBoard.buildLeaderboard(this.quiz);
return leaderBoard.sortBy(correctResponses, 'score');
}
private prefixNumberWithZero(num: number): string {
......
......@@ -44,7 +44,7 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
this.ws.row(1).setHeight(20);
this.ws.column(1).setWidth(this.responsesWithConfidenceValue.length > 0 ? 40 : 30);
this.ws.column(2).setWidth(30);
this.ws.column(3).setWidth(35);
this.ws.column(3).setWidth(45);
this.ws.column(4).setWidth(35);
this.ws.cell(1, 1, 1, columnsToFormat).style(defaultStyles.quizNameRowStyle);
......@@ -156,7 +156,9 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork
${this.mf(answerOption.configTrimWhitespaces ? 'global.yes' : 'global.no')}`);
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
this.ws.cell(7, 3).string(`
......
......@@ -150,8 +150,12 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
this.ws.cell(2, 1).string(this.mf('export.question'));
this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':');
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(8, 1).string(this.mf('export.average_confidence') + ':');
let confidenceSummary = 0;
......@@ -160,22 +164,30 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc
});
this.ws.cell(8, 2).number(Math.round(confidenceSummary / this.responsesWithConfidenceValue.length));
}
this.ws.cell(4, 1).string(this._question.questionText.replace(/[#]*[*]*/g, ''));
for (let j = 0; j < answerList.length; j++) {
this.ws.cell(2, (j + 2)).string(this.mf('export.answer') + ' ' + (j + 1));
this.ws.cell(4, (j + 2)).string(answerList[j].answerText);
this.ws.cell(6, (j + 2)).number(calculateNumberOfAnswers(this.quiz, this._questionIndex, j));
}
let nextColumnIndex = 1;
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.attendee'));
if (this._isCasRequired) {
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.cas_account_id'));
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.cas_account_email'));
}
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.answer'));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.confidence_level'));
}
this.ws.cell(10, nextColumnIndex++).string(this.mf('export.time'));
let nextStartRow = 10;
......
......@@ -211,7 +211,9 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
this.ws.cell(6, 4).number(numberOfInputValuesPerGroup.maxRange);
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
......
......@@ -167,7 +167,9 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel
this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':');
this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':');
const correctResponsesPercentage: number = this.leaderBoardData.length / MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions)
.filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length
/ MemberDAO.getMembersOfQuiz(this.quiz.name).length * 100;
this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage)));
if (this.responsesWithConfidenceValue.length > 0) {
......
This diff is collapsed.
import MemberDAO from '../db/MemberDAO';
import { SurveyQuestionEntity } from '../entities/question/SurveyQuestionEntity';
import { IMemberEntity } from '../interfaces/entities/Member/IMemberEntity';
import { IExcelWorksheet } from '../interfaces/iExcel';
import { ExcelWorksheet } from './ExcelWorksheet';
import { calculateNumberOfAnswers } from './lib/excel_function_library';
......@@ -161,22 +160,17 @@ export class SurveyExcelWorksheet extends ExcelWorksheet implements IExcelWorksh
this.ws.cell(9, nextColumnIndex++).string(this.mf('export.time'));
let nextStartRow = 9;
this.leaderBoardData.forEach((leaderboardItem) => {
MemberDAO.getMembersOfQuiz(this.quiz.name).forEach((nickItem, indexInList) => {
nextColumnIndex = 1;
nextStartRow++;
this.ws.cell(nextStartRow, nextColumnIndex++).string(leaderboardItem.name);
const nickItem = MemberDAO.getMembersOfQuiz(this.quiz.name).find((nick: IMemberEntity) => {
return nick.name === leaderboardItem.name;
});
this.ws.cell(nextStartRow, nextColumnIndex++).string(nickItem.name);
if (this._isCasRequired) {
const profile = nickItem.casProfile;
this.ws.cell(nextStartRow, nextColumnIndex++).string(profile.username[0]);
this.ws.cell(nextStartRow, nextColumnIndex++).string(profile.mail[0]);
}
const chosenAnswer: Array<string> = <any>nickItem.responses[this._questionIndex].value;
chosenAnswer.forEach((answerIndex: string, index: number) => {
chosenAnswer[index] = this._question.answerOptionList[parseInt(answerIndex, 10)].answerText;
});
const chosenAnswer: Array<string> = (<Array<any>>nickItem.responses[this._questionIndex].value).map(
answerIndex => this._question.answerOptionList[parseInt(answerIndex, 10)].answerText);
this.ws.cell(nextStartRow, nextColumnIndex++).string(chosenAnswer.join(', '));
if (this.responsesWithConfidenceValue.length > 0) {
this.ws.cell(nextStartRow, nextColumnIndex++).number(Math.round(nickItem.responses[this._questionIndex].confidence));
......
......@@ -41,6 +41,7 @@ export class ExcelTheme {
alignment: {
wrapText: true,
vertical: 'top',
horizontal: 'left',
},
},
statisticsRowStyle: {
......
export interface IUserBase {
name: string;
passwordHash: string;
tokenHash: string;
privateKey: string;
token?: string;
gitlabToken?: string;
......
......@@ -37,7 +37,7 @@ export class Leaderboard {
case QuestionType.SingleChoiceQuestion:
case QuestionType.YesNoSingleChoiceQuestion:
case QuestionType.TrueFalseSingleChoiceQuestion:
return this.isCorrectSingleChoiceQuestion(<number>response.value, <AbstractChoiceQuestionEntity>question) ? 1 : -1;
return this.isCorrectSingleChoiceQuestion(<number>response.value[0], <AbstractChoiceQuestionEntity>question) ? 1 : -1;
case QuestionType.MultipleChoiceQuestion:
return this.isCorrectMultipleChoiceQuestion(<Array<number>>response.value, <AbstractChoiceQuestionEntity>question);
case QuestionType.ABCDSingleChoiceQuestion:
......
......@@ -52,7 +52,7 @@ interface IInetAddress {
}
process.on('unhandledRejection', rejectionToCreateDump);
process.on('uncaughtException', rejectionToCreateDump); // Throws exceptions when debugging with IntelliJ
// process.on('uncaughtException', rejectionToCreateDump); // Throws exceptions when debugging with IntelliJ
if (process.env.NODE_ENV === 'production') {
}
......
......@@ -8,7 +8,8 @@ import LoggerService from '../../services/LoggerService';
@index({ name: 1 }, { unique: true })
export class UserModelItem extends Typegoose implements IUserSerialized {
@prop({ required: true }) public name: string;
@prop({ required: true }) public passwordHash: string;
@prop({ required: false }) public passwordHash: string;
@prop({ required: false }) public tokenHash: string;
@prop({ required: true }) public userAuthorizations: Array<string>;
@prop({ required: true }) public privateKey: string;
@prop() public gitlabToken?: string;
......
......@@ -42,6 +42,7 @@ export class AdminRouter extends AbstractRouter {
@BodyParam('name') name: string, //
@BodyParam('privateKey') privateKey: string, //
@BodyParam('passwordHash') passwordHash: string, //
@BodyParam('passwordHash') tokenHash: string, //
@BodyParam('userAuthorizations') userAuthorizations: Array<string>, //
@BodyParam('gitlabToken', { required: false }) gitlabToken: string, //
): void {
......@@ -49,6 +50,7 @@ export class AdminRouter extends AbstractRouter {
const userData: IUserSerialized = {
name,
passwordHash,
tokenHash,
privateKey,
userAuthorizations,
gitlabToken,
......
......@@ -340,14 +340,36 @@ export class LibRouter extends AbstractRouter {
@Post('/authorize/static')
private async authorizeStatic(
@BodyParam('username') username: string,
@BodyParam('passwordHash') password: string,
@BodyParam('username', { required: false }) username: string,
@BodyParam('passwordHash', { required: false }) password: string,
@BodyParam('tokenHash', { required: false }) tokenHash: string,
@BodyParam('token', { required: false }) token: string,
): Promise<object> {
const user = UserDAO.getUser(username);
let user;
if (username) {
user = UserDAO.getUser(username);
if (!username || !password || !user || !UserDAO.validateUser(username, password)) {
if (!password || !user || !UserDAO.validateUser(username, password)) {
throw new UnauthorizedError(JSON.stringify({
status: StatusProtocol.Failed,
step: MessageProtocol.AuthenticateStatic,
payload: { reason: 'UNKOWN_LOGIN' },
}));
}
} else if (tokenHash) {
user = UserDAO.getUserByTokenHash(tokenHash);
if (!user) {
throw new UnauthorizedError(JSON.stringify({
status: StatusProtocol.Failed,
step: MessageProtocol.AuthenticateStatic,
payload: { reason: 'UNKOWN_LOGIN' },
}));
}
} else {
throw new UnauthorizedError(JSON.stringify({
status: StatusProtocol.Failed,
step: MessageProtocol.AuthenticateStatic,
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import { suite, test } from 'mocha-typescript';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as assert from 'assert';
import * as fs from 'fs';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import { suite, test } from 'mocha-typescript';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import { suite, test } from 'mocha-typescript';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import { suite, test } from 'mocha-typescript';
......@@ -28,6 +28,7 @@ class ExpiryQuizTestSuite {
LoginDAO.initUser({
name: 'testuser',
passwordHash: 'hash',
tokenHash: 'hash',
privateKey: 'mysecret',
gitlabToken: '',
userAuthorizations: [UserRole.CreateExpiredQuiz],
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import * as fs from 'fs';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import * as fs from 'fs';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import * as fs from 'fs';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import * as fs from 'fs';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import { suite, test } from 'mocha-typescript';
......
/// <reference path="../../../node_modules/@types/chai-http/index.d.ts" />
/// <reference path="../../../node_modules/chai-http/types/index.d.ts" />
import * as chai from 'chai';
import * as fs from 'fs';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment