diff --git a/package.json b/package.json index 53d6a34..2daa028 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "cors": "^2.8.5", "debug": "^4.3.1", "dotenv": "^8.2.0", - "express": "^4.17.1", + "express": "^4.18.2", "guid-typescript": "^1.0.9", "mariadb": "^3.0.2", "random-words": "^1.1.1", @@ -31,8 +31,9 @@ "@types/app-root-path": "^1.2.4", "@types/bcrypt": "^3.0.1", "@types/debug": "^4.1.5", - "@types/express": "^4.17.11", + "@types/express": "^4.17.15", "@types/jest": "^28.1.3", + "@types/node": "^18.11.17", "@types/random-words": "^1.1.2", "@types/swagger-jsdoc": "^6.0.1", "@types/swagger-ui-express": "^4.1.3", @@ -43,7 +44,7 @@ "source-map-support": "^0.5.19", "ts-jest": "^28.0.5", "tslint": "^6.1.3", - "typescript": "^4.1.5" + "typescript": "^4.9.4" }, "jestSonar": { "sonar56x": true, diff --git a/src/models/calendar/events/credentials.service.ts b/src/models/calendar/events/credentials.service.ts new file mode 100644 index 0000000..ee302bc --- /dev/null +++ b/src/models/calendar/events/credentials.service.ts @@ -0,0 +1,29 @@ +import * as dotenv from 'dotenv'; + + +dotenv.config(); + +export const checkAdminPrivileges = (password: string) => { + return password == process.env.ADMIN_CREDENTIAL; +} + +export const checkMemberPrivileges = (password: string) => { + return password == process.env.MEMBER_CREDENTIAL; +} + +export const checkManagementPrivileges = (password: string) => { + return password == process.env.MANAGEMENT_CREDENTIAL; +} + +export const hasAccess = (calendarName: string, password: string) => { + switch (calendarName) { + case 'public': + return true; + case 'members': + return checkMemberPrivileges(password); + case 'management': + return checkManagementPrivileges(password); + default: + return false; + } +} diff --git a/src/models/calendar/events/events.router.ts b/src/models/calendar/events/events.router.ts index 0a6a0ff..ea8c00b 100644 --- a/src/models/calendar/events/events.router.ts +++ b/src/models/calendar/events/events.router.ts @@ -5,6 +5,7 @@ import express, {Request, Response} from 'express'; import * as EventService from './events.service'; import * as iCalService from './icalgenerator.service'; +import * as CredentialService from './credentials.service'; import {Guid} from 'guid-typescript'; import logger from '../../../middleware/logger'; @@ -18,17 +19,39 @@ export const eventsRouter = express.Router(); /** * Constants */ -const fileNames = ['Nachklang_calendar', 'Nachklang_internal_calendar', 'Nachklang_management_calendar']; +export const calendarNames = new Map ([ + ['public', {id: 1, name: 'Nachklang_calendar'}], + ['members', {id: 2, name: 'Nachklang_internal_calendar'}], + ['management', {id: 3, name: 'Nachklang_management_calendar'}] +]); /** * Controller Definitions */ -eventsRouter.get('/json', async (req: Request, res: Response) => { +eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => { try { // Get request params - let calendarId: number = parseInt(req.query.calendar as string ?? '', 10); + let calendarName: string = req.params.calendar as string ?? ''; + let password: string = req.query.password as string ?? ''; + + if (calendarName.length < 1) { + res.status(401).send({'message': 'Please state the name of the calendar you want events from.'}); + return; + } + + if(!Array.from(calendarNames.keys()).includes(calendarName)) { + res.status(401).send({'message': 'Unknown calendar.'}); + return; + } + + let calendarId: number = calendarNames.get(calendarName)!.id; + + if (!CredentialService.hasAccess(calendarName, password)) { + res.status(401).send({'message': 'You do not have access to the specified calendar.'}); + return; + } // Get events let events = await EventService.getAllEvents(calendarId); @@ -37,20 +60,38 @@ eventsRouter.get('/json', async (req: Request, res: Response) => { res.status(200).send(events); } catch (e: any) { console.log('Error handling a request: ' + e.message); - res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + res.status(500).send({'message': 'Internal Server Error. Try again later.'}); } }); -eventsRouter.get('/ical', async (req: Request, res: Response) => { +eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => { try { // Get request params - let calendarId: number = parseInt(req.query.calendar as string ?? '', 10); + let calendarName: string = req.params.calendar as string ?? ''; + let password: string = req.query.password as string ?? ''; + + if (calendarName.length < 1) { + res.status(401).send({'message': 'Please state the name of the calendar you want events from.'}); + return; + } + + if(!Array.from(calendarNames.keys()).includes(calendarName)) { + res.status(401).send({'message': 'Unknown calendar.'}); + return; + } + + let calendarId: number = calendarNames.get(calendarName)!.id; + + if (!CredentialService.hasAccess(calendarName, password)) { + res.status(401).send({'message': 'You do not have access to the specified calendar.'}); + return; + } // Get events let events = await EventService.getAllEvents(calendarId); // generate file name - let fileName = fileNames[calendarId]; + let fileName = calendarNames.get(calendarName)!.name; let file = await iCalService.convertToIcal(events); // Send the ical file back @@ -59,7 +100,6 @@ eventsRouter.get('/ical', async (req: Request, res: Response) => { } catch (e: any) { let errorGuid = Guid.create().toString(); logger.error('Error handling a request: ' + e.message, {reference: errorGuid}); - logger.error('Stacktrace: ' + e.stack, {reference: errorGuid}); res.status(500).send({ 'status': 'PROCESSING_ERROR', 'message': 'Internal Server Error. Try again later.', diff --git a/src/models/calendar/events/icalgenerator.service.ts b/src/models/calendar/events/icalgenerator.service.ts index c2de8c8..f79f6b8 100644 --- a/src/models/calendar/events/icalgenerator.service.ts +++ b/src/models/calendar/events/icalgenerator.service.ts @@ -49,7 +49,7 @@ const serializeIcalEvent = (icalevent: iCalEvent): string => { const generateHeaderInfo = (ical: iCalFile) => { ical.header = 'BEGIN:VCALENDAR\n' + 'VERSION:2.0\n' + - 'PRODID:-//hacksw/handcal//NONSGML v1.0//EN\n' + + 'PRODID:-//Nachklang e.V./Nachklang Calendar//NONSGML v1.0//EN\n' + 'CALSCALE:GREGORIAN\n' + 'BEGIN:VTIMEZONE\n' + 'TZID:Europe/Berlin\n' +