feature/197-duplicate_runner_mail #198

Merged
niggl merged 7 commits from feature/197-duplicate_runner_mail into dev 2023-02-02 11:51:23 +00:00
8 changed files with 172 additions and 132 deletions

View File

@ -51,6 +51,7 @@
"validator": "13.5.2"
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@odit/license-exporter": "0.0.9",
"@types/cors": "2.8.9",
"@types/csvtojson": "1.1.5",

View File

@ -1,6 +1,6 @@
import { Request } from "express";
import * as jwt from "jsonwebtoken";
import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers';
import { BadRequestError, 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';
@ -116,7 +116,7 @@ export class RunnerSelfServiceController {
return scan.toResponse();
}
@Post('/runners/forgot')
@Post('/runners/login')
@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).' })
@ -148,8 +148,11 @@ export class RunnerSelfServiceController {
@OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' })
async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") {
let runner = await createRunner.toEntity();
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
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);
@ -170,6 +173,9 @@ export class RunnerSelfServiceController {
const org = await this.getOrgansisation(token);
let runner = await createRunner.toEntity(org);
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
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'] }));
@ -225,4 +231,14 @@ export class RunnerSelfServiceController {
return organization;
}
/**
* Checks if a runner already exists
* @param email The runner's email address
* @returns Boolean (true if exists, false if not)
*/
private async getRunnerExistsByMail(email: string): Promise<boolean> {
const runner = await this.runnerRepository.findOne({ email });
return runner != undefined
}
}

View File

@ -114,7 +114,7 @@ export class CreateUser {
newUser.groups = await this.getGroups();
newUser.enabled = this.enabled;
if (!this.profilePic) { newUser.profilePic = `https://dev.lauf-fuer-kaya.de/lfk-logo.png`; }
if (!this.profilePic) { newUser.profilePic = `https://lauf-fuer-kaya.de/lfk-logo.png`; }
else { newUser.profilePic = this.profilePic; }
return newUser;

View File

@ -124,7 +124,7 @@ export class UpdateUser {
user.phone = this.phone;
user.groups = await this.getGroups();
if (!this.profilePic) { user.profilePic = `https://dev.lauf-fuer-kaya.de/lfk-logo.png`; }
if (!this.profilePic) { user.profilePic = `https://lauf-fuer-kaya.de/lfk-logo.png`; }
else { user.profilePic = this.profilePic; }
return user;

View File

@ -1,5 +1,7 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
@ -21,7 +23,7 @@ describe('delete selfservice runner invalid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
added_runner = res.data;
expect(res.status).toEqual(200);
@ -50,7 +52,7 @@ describe('delete selfservice runner valid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
added_runner = res.data;
expect(res.status).toEqual(200);

View File

@ -15,20 +15,20 @@ beforeAll(async () => {
};
});
describe('POST /api/runners/me/forgot invalid syntax/mail should fail', () => {
describe('POST /api/runners/me/login invalid syntax/mail should fail', () => {
it('get without mail return 404', async () => {
const res = await axios.post(base + '/api/runners/forgot', null, axios_config);
const res = await axios.post(base + '/api/runners/login', null, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json");
});
it('get without bs mail return 404', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=asdasdasdasdasd@tester.test.dev.lauf-fuer-kaya.de', null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=asdasdasdasdasd@tester.test.dev.lauf-fuer-kaya.de', null, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json");
});
});
// ---------------
describe('POST /api/runners/me/forgot 2 times within timeout should fail', () => {
describe('POST /api/runners/me/login 2 times within timeout should fail', () => {
let added_runner;
it('registering as citizen should return 200', async () => {
const res = await axios.post(base + '/api/runners/register', {
@ -42,19 +42,19 @@ describe('POST /api/runners/me/forgot 2 times within timeout should fail', () =>
added_runner = res.data;
});
it('post with valid mail should return 200', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('2nd post with valid mail should return 406', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(406);
expect(res.headers['content-type']).toContain("application/json");
});
});
// ---------------
describe('POST /api/runners/me/forgot valid should return 200', () => {
describe('POST /api/runners/me/login valid should return 200', () => {
let added_runner;
let new_token;
it('registering as citizen should return 200', async () => {
@ -69,7 +69,7 @@ describe('POST /api/runners/me/forgot valid should return 200', () => {
added_runner = res.data;
});
it('post with valid mail should return 200', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
new_token = res.data.token;

View File

@ -1,3 +1,4 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
@ -30,7 +31,7 @@ describe('register + get should return 200', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");

View File

@ -1,3 +1,4 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
@ -39,7 +40,7 @@ describe('register invalid citizen', () => {
const res = await axios.post(base + '/api/runners/register', {
"middlename": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -48,7 +49,7 @@ describe('register invalid citizen', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -59,7 +60,26 @@ describe('register invalid citizen', () => {
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
});
it('registering as citizen with duplicate mail should return 400', async () => {
const mail = faker.internet.exampleEmail();
await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": mail,
}, axios_config);
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": mail,
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -71,7 +91,7 @@ describe('register citizen valid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
@ -81,7 +101,7 @@ describe('register citizen valid', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -187,7 +207,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -214,7 +234,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -232,7 +252,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",