From dc3071f7d2be298f0bb02d86ec67ed1125cd3b49 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 1 Apr 2021 18:30:30 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80Bumped=20version=20to=20v0.10.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../RunnerSelfServiceController.ts | 454 +++++++++--------- 2 files changed, 228 insertions(+), 228 deletions(-) diff --git a/package.json b/package.json index 754a604..a3a6521 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odit/lfk-backend", - "version": "0.9.2", + "version": "0.10.0", "main": "src/app.ts", "repository": "https://git.odit.services/lfk/backend", "author": { diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index d8b96df..afa9379 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -1,228 +1,228 @@ -import { Request } from "express"; -import * as jwt from "jsonwebtoken"; -import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { getConnectionManager, Repository } from 'typeorm'; -import { config } from '../config'; -import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError'; -import { MailSendingError } from '../errors/MailErrors'; -import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors'; -import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; -import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; -import { JwtCreator } from '../jwtcreator'; -import { Mailer } from '../mailer'; -import ScanAuth from '../middlewares/ScanAuth'; -import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner'; -import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner'; -import { Runner } from '../models/entities/Runner'; -import { RunnerGroup } from '../models/entities/RunnerGroup'; -import { RunnerOrganization } from '../models/entities/RunnerOrganization'; -import { ScanStation } from '../models/entities/ScanStation'; -import { ResponseEmpty } from '../models/responses/ResponseEmpty'; -import { ResponseScanStation } from '../models/responses/ResponseScanStation'; -import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation'; -import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; -import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan'; -import { DonationController } from './DonationController'; -import { RunnerCardController } from './RunnerCardController'; -import { ScanController } from './ScanController'; - -@JsonController() -export class RunnerSelfServiceController { - private runnerRepository: Repository; - private orgRepository: Repository; - private stationRepository: Repository; - - /** - * Gets the repository of this controller's model/entity. - */ - constructor() { - this.runnerRepository = getConnectionManager().get().getRepository(Runner); - this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization); - this.stationRepository = getConnectionManager().get().getRepository(ScanStation); - } - - @Get('/runners/me/:jwt') - @ResponseSchema(ResponseSelfServiceRunner) - @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) - async get(@Param('jwt') token: string) { - return (new ResponseSelfServiceRunner(await this.getRunner(token))); - } - - @Delete('/runners/me/:jwt') - @ResponseSchema(ResponseSelfServiceRunner) - @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: 'Deletes all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) - async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) { - const responseRunner = await this.getRunner(token); - let runner = await this.runnerRepository.findOne({ id: responseRunner.id }); - - if (!runner) { return null; } - if (!runner) { - throw new RunnerNotFoundError(); - } - - const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations; - if (runnerDonations.length > 0 && !force) { - throw new RunnerHasDistanceDonationsError(); - } - const donationController = new DonationController(); - for (let donation of runnerDonations) { - await donationController.remove(donation.id, force); - } - - const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards; - const cardController = new RunnerCardController; - for (let card of runnerCards) { - await cardController.remove(card.id, force); - } - - const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; - const scanController = new ScanController; - for (let scan of runnerScans) { - await scanController.remove(scan.id, force); - } - - await this.runnerRepository.delete(runner); - return new ResponseSelfServiceRunner(responseRunner); - } - - @Get('/runners/me/:jwt/scans') - @ResponseSchema(ResponseSelfServiceScan, { isArray: true }) - @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: 'Lists all your (runner) scans.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please contact support.' }) - async getScans(@Param('jwt') token: string) { - const scans = (await this.getRunner(token)).scans; - let responseScans = new Array() - for (let scan of scans) { - responseScans.push(new ResponseSelfServiceScan(scan)); - } - return responseScans; - } - - @Get('/stations/me') - @UseBefore(ScanAuth) - @ResponseSchema(ResponseScanStation) - @ResponseSchema(ScanStationNotFoundError, { statusCode: 404 }) - @OnUndefined(ScanStationNotFoundError) - @OpenAPI({ description: 'Lists basic information about the station whose token got provided.
This includes it\'s associated track.', security: [{ "StationApiToken": [] }] }) - async getStationMe(@Req() req: Request) { - let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] }) - if (!scan) { throw new ScanStationNotFoundError(); } - return scan.toResponse(); - } - - @Post('/runners/forgot') - @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OnUndefined(ResponseEmpty) - @OpenAPI({ description: 'Use this endpoint to reuqest a new selfservice token/link to be sent to your mail address (rate limited to one mail every 24hrs).', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) - async requestNewToken(@QueryParam('mail') mail: string, @QueryParam("locale") locale: string = "en") { - if (!mail) { - throw new RunnerNotFoundError(); - } - const runner = await this.runnerRepository.findOne({ email: mail }); - if (!runner) { throw new RunnerNotFoundError(); } - - if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); } - const token = JwtCreator.createSelfService(runner); - - try { - await Mailer.sendSelfserviceForgottenMail(runner.email, token, locale) - } catch (error) { - throw new MailSendingError(); - } - - runner.resetRequestedTimestamp = Math.floor(Date.now() / 1000); - await this.runnerRepository.save(runner); - - return { token }; - } - - @Post('/runners/register') - @ResponseSchema(ResponseSelfServiceRunner) - @ResponseSchema(RunnerEmailNeededError, { statusCode: 406 }) - @OpenAPI({ description: 'Create a new selfservice runner in the citizen org.
This endpoint shoud be used to allow "everyday citizen" to register themselves.
You have to provide a mail address, b/c the future we\'ll implement email verification.', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) - async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") { - let runner = await createRunner.toEntity(); - - runner = await this.runnerRepository.save(runner); - let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] })); - response.token = JwtCreator.createSelfService(runner); - - try { - await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale) - } catch (error) { - throw new MailSendingError(); - } - - return response; - } - - @Post('/runners/register/:token') - @ResponseSchema(ResponseSelfServiceRunner) - @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) - @OpenAPI({ description: 'Create a new selfservice runner in a provided org.
The orgs get provided and authorized via api tokens that can be optained via the /organizations endpoint.', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) - async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner, @QueryParam("locale") locale: string = "en") { - const org = await this.getOrgansisation(token); - - let runner = await createRunner.toEntity(org); - runner = await this.runnerRepository.save(runner); - - let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] })); - response.token = JwtCreator.createSelfService(runner); - - try { - await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale) - } catch (error) { - throw new MailSendingError(); - } - - return response; - } - - @Get('/organizations/selfservice/:token') - @ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false }) - @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) - @OpenAPI({ description: 'Get the basic info and teams for a org.' }) - async getSelfserviceOrg(@Param('token') token: string) { - const orgid = (await this.getOrgansisation(token)).id; - const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] }) - - return new ResponseSelfServiceOrganisation(org); - } - - /** - * Get's a runner by a provided jwt token. - * @param token The runner jwt provided by the runner to identitfy themselves. - */ - private async getRunner(token: string): Promise { - if (token == "") { throw new JwtNotProvidedError(); } - let jwtPayload = undefined - try { - jwtPayload = jwt.verify(token, config.jwt_secret); - } catch (error) { - throw new InvalidCredentialsError(); - } - - const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }); - if (!runner) { throw new RunnerNotFoundError() } - return runner; - } - - /** - * Get's a runner org by a provided registration api key. - * @param token The organization's registration api token. - */ - private async getOrgansisation(token: string): Promise { - token = Buffer.from(token, 'base64').toString('utf8'); - - const organization = await this.orgRepository.findOne({ key: token }); - if (!organization) { throw new RunnerOrganizationNotFoundError; } - - return organization; - } +import { Request } from "express"; +import * as jwt from "jsonwebtoken"; +import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { config } from '../config'; +import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError'; +import { MailSendingError } from '../errors/MailErrors'; +import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors'; +import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; +import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; +import { JwtCreator } from '../jwtcreator'; +import { Mailer } from '../mailer'; +import ScanAuth from '../middlewares/ScanAuth'; +import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner'; +import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner'; +import { Runner } from '../models/entities/Runner'; +import { RunnerGroup } from '../models/entities/RunnerGroup'; +import { RunnerOrganization } from '../models/entities/RunnerOrganization'; +import { ScanStation } from '../models/entities/ScanStation'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; +import { ResponseScanStation } from '../models/responses/ResponseScanStation'; +import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation'; +import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; +import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan'; +import { DonationController } from './DonationController'; +import { RunnerCardController } from './RunnerCardController'; +import { ScanController } from './ScanController'; + +@JsonController() +export class RunnerSelfServiceController { + private runnerRepository: Repository; + private orgRepository: Repository; + private stationRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.runnerRepository = getConnectionManager().get().getRepository(Runner); + this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization); + this.stationRepository = getConnectionManager().get().getRepository(ScanStation); + } + + @Get('/runners/me/:jwt') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) + async get(@Param('jwt') token: string) { + return (new ResponseSelfServiceRunner(await this.getRunner(token))); + } + + @Delete('/runners/me/:jwt') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Deletes all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) + async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) { + const responseRunner = await this.getRunner(token); + let runner = await this.runnerRepository.findOne({ id: responseRunner.id }); + + if (!runner) { return null; } + if (!runner) { + throw new RunnerNotFoundError(); + } + + const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations; + if (runnerDonations.length > 0 && !force) { + throw new RunnerHasDistanceDonationsError(); + } + const donationController = new DonationController(); + for (let donation of runnerDonations) { + await donationController.remove(donation.id, force); + } + + const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards; + const cardController = new RunnerCardController; + for (let card of runnerCards) { + await cardController.remove(card.id, force); + } + + const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; + const scanController = new ScanController; + for (let scan of runnerScans) { + await scanController.remove(scan.id, force); + } + + await this.runnerRepository.delete(runner); + return new ResponseSelfServiceRunner(responseRunner); + } + + @Get('/runners/me/:jwt/scans') + @ResponseSchema(ResponseSelfServiceScan, { isArray: true }) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Lists all your (runner) scans.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please contact support.' }) + async getScans(@Param('jwt') token: string) { + const scans = (await this.getRunner(token)).scans; + let responseScans = new Array() + for (let scan of scans) { + responseScans.push(new ResponseSelfServiceScan(scan)); + } + return responseScans; + } + + @Get('/stations/me') + @UseBefore(ScanAuth) + @ResponseSchema(ResponseScanStation) + @ResponseSchema(ScanStationNotFoundError, { statusCode: 404 }) + @OnUndefined(ScanStationNotFoundError) + @OpenAPI({ description: 'Lists basic information about the station whose token got provided.
This includes it\'s associated track.', security: [{ "StationApiToken": [] }] }) + async getStationMe(@Req() req: Request) { + let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] }) + if (!scan) { throw new ScanStationNotFoundError(); } + return scan.toResponse(); + } + + @Post('/runners/forgot') + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(ResponseEmpty) + @OpenAPI({ description: 'Use this endpoint to reuqest a new selfservice token/link to be sent to your mail address (rate limited to one mail every 24hrs).', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) + async requestNewToken(@QueryParam('mail') mail: string, @QueryParam("locale") locale: string = "en") { + if (!mail) { + throw new RunnerNotFoundError(); + } + const runner = await this.runnerRepository.findOne({ email: mail }); + if (!runner) { throw new RunnerNotFoundError(); } + + if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); } + const token = JwtCreator.createSelfService(runner); + + try { + await Mailer.sendSelfserviceForgottenMail(runner.email, token, locale) + } catch (error) { + throw new MailSendingError(); + } + + runner.resetRequestedTimestamp = Math.floor(Date.now() / 1000); + await this.runnerRepository.save(runner); + + return { token }; + } + + @Post('/runners/register') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerEmailNeededError, { statusCode: 406 }) + @OpenAPI({ description: 'Create a new selfservice runner in the citizen org.
This endpoint shoud be used to allow "everyday citizen" to register themselves.
You have to provide a mail address, b/c the future we\'ll implement email verification.', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) + async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") { + let runner = await createRunner.toEntity(); + + runner = await this.runnerRepository.save(runner); + let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] })); + response.token = JwtCreator.createSelfService(runner); + + try { + await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale) + } catch (error) { + throw new MailSendingError(); + } + + return response; + } + + @Post('/runners/register/:token') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Create a new selfservice runner in a provided org.
The orgs get provided and authorized via api tokens that can be optained via the /organizations endpoint.', parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] }) + async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner, @QueryParam("locale") locale: string = "en") { + const org = await this.getOrgansisation(token); + + let runner = await createRunner.toEntity(org); + runner = await this.runnerRepository.save(runner); + + let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] })); + response.token = JwtCreator.createSelfService(runner); + + try { + await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale) + } catch (error) { + throw new MailSendingError(); + } + + return response; + } + + @Get('/organizations/selfservice/:token') + @ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false }) + @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Get the basic info and teams for a org.' }) + async getSelfserviceOrg(@Param('token') token: string) { + const orgid = (await this.getOrgansisation(token)).id; + const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] }) + + return new ResponseSelfServiceOrganisation(org); + } + + /** + * Get's a runner by a provided jwt token. + * @param token The runner jwt provided by the runner to identitfy themselves. + */ + private async getRunner(token: string): Promise { + if (token == "") { throw new JwtNotProvidedError(); } + let jwtPayload = undefined + try { + jwtPayload = jwt.verify(token, config.jwt_secret); + } catch (error) { + throw new InvalidCredentialsError(); + } + + const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }); + if (!runner) { throw new RunnerNotFoundError() } + return runner; + } + + /** + * Get's a runner org by a provided registration api key. + * @param token The organization's registration api token. + */ + private async getOrgansisation(token: string): Promise { + token = Buffer.from(token, 'base64').toString('utf8'); + + const organization = await this.orgRepository.findOne({ key: token }); + if (!organization) { throw new RunnerOrganizationNotFoundError; } + + return organization; + } } \ No newline at end of file