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

module.exports.constructor   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 280
Code Lines 180

Duplication

Lines 92
Ratio 32.86 %

Importance

Changes 0
Metric Value
cc 1
eloc 180
nc 1
nop 0
dl 92
loc 280
rs 7
c 0
b 0
f 0

15 Functions

Rating   Name   Duplication   Size   Complexity  
A auth.js ➔ ... ➔ ??? 0 23 3
A auth.js ➔ checkToken 0 33 2
A auth.js ➔ getNewAPIKey 0 36 3
A auth.js ➔ register 34 51 3
A auth.js ➔ ... ➔ bcrypt.hash 34 34 2
A auth.js ➔ isValidAPIKey 25 27 1
A auth.js ➔ ... ➔ ??? 0 35 3
A auth.js ➔ ... ➔ ??? 14 14 2
A auth.js ➔ getUniqueAPIKey 33 37 1
B auth.js ➔ ... ➔ ??? 0 61 3
A auth.js ➔ ... ➔ ??? 33 33 3
A auth.js ➔ ... ➔ ??? 25 25 3
B auth.js ➔ login 0 81 3
A auth.js ➔ ... ➔ ??? 18 18 2
A auth.js ➔ ... ➔ jwt.verify 0 18 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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