Skip to content

Commit 30ab623

Browse files
authored
Merge branch 'master' into master
2 parents 14f4643 + 3988f5a commit 30ab623

25 files changed

+5039
-23
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,7 @@ fabric.properties
120120

121121
enigma-types.h
122122
UIcode/.env.development
123+
124+
# exclude docker mongodb data
125+
backend/data/
126+

backend/README.md

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# COVID-19 Self-reporting API server User IDs
2+
3+
![GitHub pull request check state](https://img.shields.io/github/status/s/pulls/enigmampc/SafeTrace/52)
4+
![Docker-compose version](https://img.shields.io/badge/Docker--Ccompose-%5E1.21.2-brightgreen)
5+
![node-current (tag)](https://img.shields.io/node/v/nodemon/latest)
6+
![NPM version](https://img.shields.io/badge/NPM-6.14.4-verde)
7+
![Mongoose version](https://img.shields.io/badge/MONGO-%5E5.9.4-green)
8+
9+
10+
## Requirements
11+
12+
You need these packages installed on your machine:
13+
* [Docker and docker-compose](https://docs.docker.com/compose/install/)
14+
* [NPM](https://www.npmjs.com/get-npm)
15+
* [nodejs](https://nodejs.org/en/)
16+
17+
## Installation
18+
19+
Clone the project and run these commands.
20+
21+
```
22+
$ cd <your_project_folder>/backend/app/
23+
24+
$ npm install
25+
26+
$ cd ..
27+
28+
$ docker-compose up --build
29+
```
30+
31+
The node js project will run on http://localhost:4080/
32+
33+
admin-mongo interface will be displayed on http://localhost:8082/
34+
(this is deactivated by default. You need to uncomment lines in the docker-compose.yml to activate it)
35+
36+
Internal Mongo DB connection string
37+
`mongodb://mongo/safetrace`
38+
39+
External Mongo DB connection string (Robo3t)
40+
`mongodb://localhost:10975/safetrace` *In the docker-compose.yml file the port configuration is mapping the port 10975 to 27017 just to avoid expose the normal mongodb port.*
41+
42+
.env file has some configurations
43+
- PORT: port where run nodejs
44+
- MONGOURI: path to access to mongoDB database (`mongodb://mongo/safetrace`)
45+
- GOOGLE_CLIENT_ID= client id path to register and validate users for Google sign up service. More innformation [here](https://developers.google.com/identity/sign-in/web/backend-auth).
46+
47+
app/config/config.js
48+
- >secret: 'youSecretWord'
49+
- This configuration is used to encrypt the user id. It must be changed for every different app. *This is a temporary solution for the MVP version.*
50+
51+
# Google Sign In integration
52+
53+
## /user/glogin
54+
55+
This endpoint registers new Google users if it don't exist. And logs in the user. It returns the internal token that you have to use for the next requests (header `x-access-token`)
56+
57+
```
58+
curl --location --request POST 'http://localhost:4080/user/glogin' \
59+
--header 'Content-Type: application/json' \
60+
--data-raw '{
61+
"token": "eyJhbGciO0eXAiOi...LeLIU9ZMrVoCV2xA"
62+
}'
63+
```
64+
**Returns**
65+
66+
```
67+
{
68+
"token": "eyJhbGciOiJIUzI1NiIs...yiV2CNK12IVGESQ"
69+
}
70+
```
71+
72+
# JWT User endpoints
73+
74+
## /user/signup
75+
76+
```
77+
curl --location --request POST 'https://localhost:4080/user/signup' \
78+
--header 'Content-Type: application/json' \
79+
--data-raw '{
80+
"username": "username",
81+
"email": "[email protected]",
82+
"password": "yourpass",
83+
"agreeToBeNotified": true
84+
}'
85+
```
86+
**Returns**
87+
```
88+
{
89+
"token": "eyJhbGciOiJIUzI1NiI....bJI7QTsgyM3Qk0"
90+
}
91+
```
92+
93+
## /user/login
94+
95+
```
96+
curl --location --request POST 'http://localhost:4080/user/login' \
97+
--header 'Content-Type: application/json' \
98+
--data-raw '{
99+
"email": "[email protected]",
100+
"password": "yourpass"
101+
}'
102+
```
103+
**Returns**
104+
```
105+
{
106+
"token": "eyJhbGciOiJIUzI1NiIs....R-sJ9iHNUde0"
107+
}
108+
```
109+
110+
## /user/me
111+
112+
```
113+
curl --location --request GET 'http://localhost:4080/user/me' \
114+
--header 'x-access-token: eyJhbGci....blSXAPm0'
115+
116+
```
117+
**Returns**
118+
```
119+
{
120+
"agreeToBeNotified": false,
121+
"userType": 1,
122+
"createdAt": "2020-04-02T10:16:12.090Z",
123+
"_id": "5e85bf05420bac7bcfece08f",
124+
"username": "username",
125+
"email": "[email protected]",
126+
"password": "$2a$10$21w/RANQMJoc2Ge6FpcSSOCpY1S2ae6li5dv5xNeQEzewphkneGcS",
127+
"idUser": 1,
128+
"encryptedUserId": "5f6451160f76aac8a02493787dc940a0057746c6401cc49757664ce1032b8450",
129+
"__v": 0
130+
}
131+
```
132+
133+
`encryptedUserId` returns the encrypted email as user id.
134+
135+
# Report
136+
137+
## POST: /report
138+
139+
This let to add a result test to the report table.
140+
141+
```
142+
curl --location --request POST 'http://localhost:4080/report' \
143+
--header 'x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNWU4ODhhZDBlZGEwN2U2ZGFlYjY0ODBiIn0sImlhdCI6MTU4NjAxNjA2MiwiZXhwIjoxNTg2MDE5NjYyfQ.hmxFFAz1P80Yq4Q2iA6D8IpCOhI5_7xkfZYWDkQOHK4' \
144+
--header 'Content-Type: application/json' \
145+
--data-raw '{
146+
"idUser": "'9365df4e9acef8b63b45dc3534491225ac32630abe6991f6bf5a74c9803412fc'",
147+
"testDate": "03/02/2020",
148+
"testResult": 0
149+
}'
150+
```
151+
**Returns**
152+
153+
```
154+
{
155+
"report": {
156+
"createdAt": "2020-04-04T16:04:00.725Z",
157+
"_id": "5e88b01316714c664f5ef11e",
158+
"idUser": 1,
159+
"testDate": "2020-03-02T03:00:00.000Z",
160+
"testResult": 0,
161+
"idReport": 13,
162+
"__v": 0
163+
}
164+
}
165+
```
166+
167+
## GET: /report/{idUser}
168+
169+
Get the list of reported tests
170+
171+
172+
```
173+
curl --location --request GET 'http://localhost:4080/report/9365df4e9acef8b63b45dc3534491225ac32630abe6991f6bf5a74c9803412fc' \
174+
--header 'x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNWU4ODhhZDBlZGEwN2U2ZGFlYjY0ODBiIn0sImlhdCI6MTU4NjI4Njg4MiwiZXhwIjoxNTg2MjkwNDgyfQ.zVNwkDZCXtlxRJnDhonptMxbgpngrB3T7cNIy8vde_I'
175+
```
176+
**Returns**
177+
178+
```
179+
{
180+
"reports": [
181+
{
182+
"createdAt": "2020-04-07T19:24:31.388Z",
183+
"_id": "5e8cd38dc43d8007408e600d",
184+
"idUser": "9365df4e9acef8b63b45dc3534491225ac32630abe6991f6bf5a74c9803412fc",
185+
"testDate": "2020-03-04T03:00:00.000Z",
186+
"testResult": 1,
187+
"idReport": 17,
188+
"__v": 0
189+
},
190+
{
191+
...
192+
}
193+
]
194+
}
195+
```
196+
197+
## LICENSE
198+
199+
The code in this repository is released under the [MIT License](https://github.com/cmalfesi/SafeTrace/blob/master/LICENSE).

backend/app/.env

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
COMPOSE_CONVERT_WINDOWS_PATHS=1
2+
PORT=4080
3+
MONGOURI=mongodb://mongo/safetrace
4+
NODE_ENV=development
5+
GOOGLE_CLIENT_ID="119469794689-ojm97chi9mv1148av7d1lc8ghjkelddl.apps.googleusercontent.com"

backend/app/Dockerfile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#define the latest nodejs image to build from
2+
FROM node:latest
3+
RUN mkdir -p /usr/src/apiServerUsers
4+
5+
#create a working directory
6+
WORKDIR /usr/src/apiServerUsers
7+
RUN npm install -g nodemon --save
8+
9+
#copy package.json file under the working directory
10+
COPY package.json /usr/src/apiServerUsers/
11+
RUN npm install
12+
13+
#copy all your files under the working directory
14+
COPY . /usr/src/apiServerUsers/
15+
16+
EXPOSE 4080
17+
#start nodejs server
18+
CMD nodemon server.js

backend/app/config/config.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//secret word for validating tokens
2+
module.exports = {
3+
secret: 'safeTraceEnigma245237401975' //this value must be changed for every different app
4+
};

backend/app/helpers/crypto.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Nodejs encryption with CTR
2+
const crypto = require('crypto');
3+
const algorithm = 'aes-256-cbc';
4+
const key = crypto.randomBytes(32);
5+
const iv = crypto.randomBytes(16);
6+
7+
exports.encrypt = (text) => {
8+
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
9+
let encrypted = cipher.update(text);
10+
encrypted = Buffer.concat([encrypted, cipher.final()]);
11+
return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') };
12+
}
13+
14+
exports.decrypt = (text) => {
15+
let iv = Buffer.from(text.iv, 'hex');
16+
let encryptedText = Buffer.from(text.encryptedData, 'hex');
17+
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
18+
let decrypted = decipher.update(encryptedText);
19+
decrypted = Buffer.concat([decrypted, decipher.final()]);
20+
return decrypted.toString();
21+
}
22+

backend/app/middleware/auth.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const jwt = require("jsonwebtoken");
2+
const config = require('../config/config.js');
3+
4+
5+
module.exports = function(req, res, next) {
6+
let userType = req.headers['auth-user-type'] || 0;
7+
let token = req.headers['x-access-token'] || req.headers['authorization']; // Express headers are auto converted to lowercase
8+
9+
if (!token) return res.status(401).json({ message: "Auth Error" });
10+
11+
try {
12+
const decoded = jwt.verify(token, config.secret);
13+
req.user = decoded.user;
14+
next();
15+
} catch (e) {
16+
console.error(e);
17+
res.status(500).send({ message: "Invalid Token" });
18+
}
19+
};
20+
21+

backend/app/models/report.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var mongoose = require('mongoose');
2+
var Schema = mongoose.Schema;
3+
var autoIncrement = require('mongoose-easy-auto-increment');
4+
5+
var ReportSchema = new Schema({
6+
idReport: {
7+
type: Number,
8+
validate: {
9+
validator: Number.isInteger,
10+
message: '{VALUE} is not an integer value'
11+
}
12+
},
13+
idUser: {
14+
type: String,
15+
required: true
16+
},
17+
testDate: {
18+
type: Date,
19+
requered: true
20+
},
21+
testResult: {
22+
type: Number, //0 = not tested, no symptons, 1 positive, 2 high risk symptoms
23+
required: true
24+
},
25+
createdAt: {
26+
type: Date,
27+
default: Date.now()
28+
}
29+
});
30+
31+
ReportSchema.plugin(autoIncrement, { field: 'idReport', collection: 'counters' });
32+
var ReportModel = mongoose.model('report', ReportSchema);
33+
module.exports = ReportModel;

backend/app/models/user.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
var mongoose = require('mongoose');
2+
var Schema = mongoose.Schema;
3+
var autoIncrement = require('mongoose-easy-auto-increment');
4+
5+
6+
var UserSchema = new Schema({
7+
idUser: {
8+
type: Number,
9+
validate: {
10+
validator: Number.isInteger,
11+
message: '{VALUE} is not an integer value'
12+
}
13+
},
14+
username: {
15+
type: String,
16+
required: true
17+
},
18+
email: {
19+
type: String,
20+
required: true
21+
},
22+
password: {
23+
type: String,
24+
required: true
25+
},
26+
agreeToBeNotified: {
27+
type: Boolean,
28+
default: false
29+
},
30+
encryptedUserId: {
31+
type: String,
32+
required: true
33+
},
34+
userType: {
35+
type: Number, //0= internal JWT, 1= Google
36+
default: 0,
37+
validate: {
38+
validator: Number.isInteger,
39+
message: '{VALUE} is not an integer value'
40+
}
41+
},
42+
createdAt: {
43+
type: Date,
44+
default: Date.now()
45+
}
46+
});
47+
48+
UserSchema.plugin(autoIncrement, { field: 'idUser', collection: 'counters' });
49+
var UserModel = mongoose.model('user', UserSchema);
50+
module.exports = UserModel;

backend/app/nodemon.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"env": {
3+
"NODE_ENV":"development",
4+
"COMPOSE_CONVERT_WINDOWS_PATHS":1
5+
},
6+
"ext": "js json ejs"
7+
}

0 commit comments

Comments
 (0)