Test Failed
Push — master ( 853a48...344ee9 )
by Emil
02:45
created

v2/models/auth.js   A

Complexity

Total Complexity 39
Complexity/F 2.44

Size

Lines of Code 305
Function Count 16

Duplication

Duplicated Lines 92
Ratio 30.16 %

Importance

Changes 0
Metric Value
cc 0
eloc 191
nc 2
dl 92
loc 305
rs 9.28
c 0
b 0
f 0
wmc 39
mnd 2
bc 40
fnc 16
bpm 2.5
cpm 2.4375
noi 8

7 Functions

Rating   Name   Duplication   Size   Complexity  
A auth.getUniqueAPIKey 33 37 1
A auth.register 34 51 3
A auth.checkToken 0 35 2
A auth.getNewAPIKey 0 36 3
A auth.isValidAPIKey 25 27 1
A auth.checkAPIKey 0 11 3
B auth.login 0 81 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
const db = require("../db/database.js");
2
const hat = require("hat");
3
const validator = require("email-validator");
4
5
const bcrypt = require('bcrypt');
6
const jwt = require('jsonwebtoken');
7
8
let config;
9
10
try {
11
    config = require('../../config/config.json');
12
} catch (error) {
13
    console.error(error);
14
}
15
16
const jwtSecret = process.env.JWT_SECRET || config.secret;
17
18
const auth = {
19
    checkAPIKey: function (req, res, next) {
20
        if ( req.path == '/') {
21
            return next();
22
        }
23
24
        if ( req.path == '/auth/api_key') {
25
            return next();
26
        }
27
28
        auth.isValidAPIKey(req.query.api_key || req.body.api_key, next, req.path, res);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
29
    },
30
31
    isValidAPIKey: function(apiKey, next, path, res) {
32 View Code Duplication
        db.get("SELECT email FROM apikeys WHERE key = ?", apiKey, (err, row) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
33
            if (err) {
34
                return res.status(500).json({
35
                    errors: {
36
                        status: 500,
37
                        source: path,
38
                        title: "Database error",
39
                        detail: err.message
40
                    }
41
                });
42
            }
43
44
            if (row !== undefined) {
45
                return next();
46
            }
47
48
            return res.status(401).json({
49
                errors: {
50
                    status: 401,
51
                    source: path,
52
                    title: "Valid API key",
53
                    detail: "No valid API key provided."
54
                }
55
            });
56
        });
57
    },
58
59
    getNewAPIKey: function(res, path, email) {
60
        if (email === undefined || !validator.validate(email)) {
61
            return res.status(401).json({
62
                errors: {
63
                    status: 401,
64
                    source: path,
65
                    title: "Valid email",
66
                    detail: "A valid email address is required to obtain an API key."
67
                }
68
            });
69
        }
70
71
        db.get("SELECT email, key FROM apikeys WHERE email = ?", email, (err, row) => {
72
            if (err) {
73
                return res.status(500).json({
74
                    errors: {
75
                        status: 500,
76
                        source: path,
77
                        title: "Database error",
78
                        detail: err.message
79
                    }
80
                });
81
            }
82
83
            if (row !== undefined) {
84
                return res.json({
85
                    data: {
86
                        message: "Email address already used for api key.",
87
                        apiKey: row.key
88
                    }
89
                });
90
            }
91
92
            return auth.getUniqueAPIKey(res, path, email);
93
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
94
    },
95
96
    getUniqueAPIKey: function(res, path, email) {
97
        const apiKey = hat();
98
99 View Code Duplication
        db.get("SELECT key FROM apikeys WHERE key = ?", apiKey, (err, row) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
100
            if (err) {
101
                return res.status(401).json({
102
                    errors: {
103
                        status: 401,
104
                        source: path,
105
                        title: "Database error",
106
                        detail: err.message
107
                    }
108
                });
109
            }
110
111
            if (row === undefined) {
112
                db.run("INSERT INTO apikeys (key, email) VALUES (?, ?)",
113
                    apiKey,
114
                    email, (err) => {
115
                        if (err) {
116
                            return res.status(401).json({
117
                                errors: {
118
                                    status: 401,
119
                                    source: path,
120
                                    title: "Database error",
121
                                    detail: err.message
122
                                }
123
                            });
124
                        }
125
126
                        return res.json({ data: { key: apiKey }});
127
                    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
128
            } else {
129
                return auth.getUniqueAPIKey(res, email);
130
            }
131
        });
132
    },
133
134
    login: function(res, body) {
135
        const email = body.email;
136
        const password = body.password;
137
        const apiKey = body.api_key;
138
139
        if (!email || !password) {
140
            return res.status(401).json({
141
                errors: {
142
                    status: 401,
143
                    source: "/login",
144
                    title: "Email or password missing",
145
                    detail: "Email or password missing in request"
146
                }
147
            });
148
        }
149
150
        db.get("SELECT * FROM users WHERE apiKey = ? AND email = ?",
151
            apiKey,
152
            email,
153
            (err, rows) => {
154
                if (err) {
155
                    return res.status(500).json({
156
                        errors: {
157
                            status: 500,
158
                            source: "/login",
159
                            title: "Database error",
160
                            detail: err.message
161
                        }
162
                    });
163
                }
164
165
                if (rows === undefined) {
166
                    return res.status(401).json({
167
                        errors: {
168
                            status: 401,
169
                            source: "/login",
170
                            title: "User not found",
171
                            detail: "User with provided email not found."
172
                        }
173
                    });
174
                }
175
176
                const user = rows;
177
178
                bcrypt.compare(password, user.password, (err, result) => {
179
                    if (err) {
180
                        return res.status(500).json({
181
                            errors: {
182
                                status: 500,
183
                                source: "/login",
184
                                title: "bcrypt error",
185
                                detail: "bcrypt error"
186
                            }
187
                        });
188
                    }
189
190
                    if (result) {
191
                        let payload = { api_key: user.apiKey, email: user.email };
192
                        let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: '24h' });
193
194
                        return res.json({
195
                            data: {
196
                                type: "success",
197
                                message: "User logged in",
198
                                user: payload,
199
                                token: jwtToken
200
                            }
201
                        });
202
                    }
203
204
                    return res.status(401).json({
205
                        errors: {
206
                            status: 401,
207
                            source: "/login",
208
                            title: "Wrong password",
209
                            detail: "Password is incorrect."
210
                        }
211
                    });
212
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
213
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
214
    },
215
216
    register: function(res, body) {
217
        const email = body.email;
218
        const password = body.password;
219
        const apiKey = body.api_key;
220
221
        if (!email || !password) {
222
            return res.status(401).json({
223
                errors: {
224
                    status: 401,
225
                    source: "/register",
226
                    title: "Email or password missing",
227
                    detail: "Email or password missing in request"
228
                }
229
            });
230
        }
231
232 View Code Duplication
        bcrypt.hash(password, 10, function(err, hash) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
233
            if (err) {
234
                return res.status(500).json({
235
                    errors: {
236
                        status: 500,
237
                        source: "/register",
238
                        title: "bcrypt error",
239
                        detail: "bcrypt error"
240
                    }
241
                });
242
            }
243
244
            db.run("INSERT INTO users (apiKey, email, password) VALUES (?, ?, ?)",
245
                apiKey,
246
                email,
247
                hash, (err) => {
248
                    if (err) {
249
                        return res.status(500).json({
250
                            errors: {
251
                                status: 500,
252
                                source: "/register",
253
                                title: "Database error",
254
                                detail: err.message
255
                            }
256
                        });
257
                    }
258
259
                    return res.status(201).json({
260
                        data: {
261
                            message: "User successfully registered."
262
                        }
263
                    });
264
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
265
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
266
    },
267
268
    checkToken: function(req, res, next) {
269
        var token = req.headers['x-access-token'];
270
271
        if (token) {
272
            jwt.verify(token, jwtSecret, function(err, decoded) {
273
                if (err) {
274
                    return res.status(500).json({
275
                        errors: {
276
                            status: 500,
277
                            source: req.path,
278
                            title: "Failed authentication",
279
                            detail: err.message
280
                        }
281
                    });
282
                }
283
284
                req.user = {};
285
                req.user.api_key = decoded.api_key;
286
                req.user.email = decoded.email;
287
288
                next();
289
290
                return undefined;
291
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
292
        } else {
293
            return res.status(401).json({
294
                errors: {
295
                    status: 401,
296
                    source: req.path,
297
                    title: "No token",
298
                    detail: "No token provided in request headers"
299
                }
300
            });
301
        }
302
    }
303
};
304
305
module.exports = auth;
306