Completed
Push — master ( 7ccabd...e7e1b8 )
by Emil
06:26 queued 01:34
created

auth.checkAPIKey   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.027

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
nc 7
nop 3
dl 0
loc 23
ccs 10
cts 11
cp 0.9091
crap 6.027
rs 8.6666
c 1
b 0
f 0
1 1
const db = require("../db/database.js");
2 1
const hat = require("hat");
3 1
const validator = require("email-validator");
4
5 1
const bcrypt = require('bcrypt');
6 1
const jwt = require('jsonwebtoken');
7
8
let config;
9
10 1
try {
11 1
    config = require('../../config/config.json');
12
} catch (error) {
13 1
    console.error(error);
14
}
15
16 2
const jwtSecret = process.env.JWT_SECRET || config.secret;
17
18 1
const auth = {
19
    checkAPIKey: function (req, res, next) {
20 108
        if ( req.path == '/') {
21 1
            return next();
22
        }
23
24 107
        if ( req.path == '/v2') {
25
            return next();
26
        }
27
28 107
        if ( req.path == '/auth/api_key') {
29 1
            return next();
30
        }
31
32 106
        if ( req.path == '/auth/api_key/confirmation') {
33 11
            return next();
34
        }
35
36 95
        if ( req.path == '/auth/api_key/deregister') {
37
            return next();
38
        }
39
40 95
        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...
41 95
    },
42
43
    isValidAPIKey: function(apiKey, next, path, res) {
44 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...
45
            if (err) {
46
                return res.status(500).json({
47
                    errors: {
48
                        status: 500,
49
                        source: path,
50
                        title: "Database error",
51
                        detail: err.message
52 95
                    }
53 86
                });
54
            }
55
56 9
            if (row !== undefined) {
57
                return next();
58
            }
59
60
            return res.status(401).json({
61
                errors: {
62
                    status: 401,
63
                    source: path,
64
                    title: "Valid API key",
65
                    detail: "No valid API key provided."
66
                }
67
            });
68 9
        });
69
    },
70
71
    getNewAPIKey: function(res, email) {
72 9
        let data = {
73 1
            apiKey: ""
74 1
        };
75
76 1
        if (email === undefined || !validator.validate(email)) {
77
            data.message = "A valid email address is required to obtain an API key.";
78
            data.email = email;
79 8
80 8
            return res.render("api_key/form", data);
81
        }
82
83
        db.get("SELECT email, key FROM apikeys WHERE email = ?", email, (err, row) => {
84
            if (err) {
85
                data.message = "Database error: " + err.message;
86
                data.email = email;
87 8
88
                return res.render("api_key/form", data);
89
            }
90
91
            if (row !== undefined) {
92
                data.apiKey = row.key;
93 8
94
                return res.render("api_key/confirmation", data);
95
            }
96
97
            return auth.getUniqueAPIKey(res, email);
98 8
        });
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...
99 8
    },
100
101
    getUniqueAPIKey: function(res, email) {
102
        const apiKey = hat();
103 8
        let data = {
104 8
            apiKey: ""
105
        };
106
107
        db.get("SELECT key FROM apikeys WHERE key = ?", apiKey, (err, row) => {
108
            if (err) {
109
                data.message = "Database error: " + err.message;
110
                data.email = email;
111 8
112 8
                return res.render("api_key/form", data);
113
            }
114
115 8
            if (row === undefined) {
116
                db.run("INSERT INTO apikeys (key, email) VALUES (?, ?)",
117
                    apiKey,
118
                    email, (err) => {
119
                        if (err) {
120
                            data.message = "Database error: " + err.message;
121
                            data.email = email;
122 8
123
                            return res.render("api_key/form", data);
124 8
                        }
125
126
                        data.apiKey = apiKey;
127
128
                        return res.render("api_key/confirmation", data);
129
                    });
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...
130
            } else {
131
                return auth.getUniqueAPIKey(res, email);
132
            }
133 6
        });
134 6
    },
135 6
136
    deregister: function(res, body) {
137 6
        const email = body.email;
138 2
        const apiKey = body.apikey;
139
140
        db.get("SELECT key FROM apikeys WHERE key = ? and email = ?",
141
            apiKey,
142
            email,
143
            (err, row) => {
144
                if (err) {
145
                    let data = {
146
                        message: "Database error: " + err.message,
147
                        email: email,
148 4
                        apikey: apikey
0 ignored issues
show
Bug introduced by
The variable apikey seems to be never declared. If this is a global, consider adding a /** global: apikey */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
149
                    };
150
151
                    return res.render("api_key/deregister", data);
152 4
                }
153
154
                if (row === undefined) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if row === undefined is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
155
156
                }
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...
157
            });
158
159
    },
160
161
    login: function(res, body) {
162
        const email = body.email;
163 4
        const password = body.password;
164 1
        const apiKey = body.api_key;
165
166
        if (!email || !password) {
167
            return res.status(401).json({
168
                errors: {
169
                    status: 401,
170
                    source: "/login",
171
                    title: "Email or password missing",
172
                    detail: "Email or password missing in request"
173
                }
174 3
            });
175
        }
176 3
177 3
        db.get("SELECT * FROM users WHERE apiKey = ? AND email = ?",
178
            apiKey,
179
            email,
180 View Code Duplication
            (err, rows) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
181
                if (err) {
182
                    return res.status(500).json({
183
                        errors: {
184
                            status: 500,
185
                            source: "/login",
186
                            title: "Database error",
187
                            detail: err.message
188 3
                        }
189 2
                    });
190 2
                }
191
192 2
                if (rows === undefined) {
193
                    return res.status(401).json({
194
                        errors: {
195
                            status: 401,
196
                            source: "/login",
197
                            title: "User not found",
198
                            detail: "User with provided email not found."
199
                        }
200
                    });
201
                }
202 1
203
                const user = rows;
204
205
                bcrypt.compare(password, user.password, (err, result) => {
206
                    if (err) {
207
                        return res.status(500).json({
208
                            errors: {
209
                                status: 500,
210
                                source: "/login",
211
                                title: "bcrypt error",
212
                                detail: "bcrypt error"
213
                            }
214
                        });
215 5
                    }
216 5
217 5
                    if (result) {
218
                        let payload = { api_key: user.apiKey, email: user.email };
219 5
                        let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: '24h' });
220 2
221
                        return res.json({
222
                            data: {
223
                                type: "success",
224
                                message: "User logged in",
225
                                user: payload,
226
                                token: jwtToken
227
                            }
228
                        });
229
                    }
230 3
231 3
                    return res.status(401).json({
232
                        errors: {
233
                            status: 401,
234
                            source: "/login",
235
                            title: "Wrong password",
236
                            detail: "Password is incorrect."
237
                        }
238
                    });
239
                });
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...
240
            });
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...
241
    },
242 3
243
    register: function(res, body) {
244
        const email = body.email;
245
        const password = body.password;
246 3
        const apiKey = body.api_key;
247 1
248
        if (!email || !password) {
249
            return res.status(401).json({
250
                errors: {
251
                    status: 401,
252
                    source: "/register",
253
                    title: "Email or password missing",
254
                    detail: "Email or password missing in request"
255
                }
256
            });
257 2
        }
258
259 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...
260
            if (err) {
261
                return res.status(500).json({
262
                    errors: {
263
                        status: 500,
264
                        source: "/register",
265
                        title: "bcrypt error",
266
                        detail: "bcrypt error"
267 14
                    }
268
                });
269 14
            }
270 11
271 11
            db.run("INSERT INTO users (apiKey, email, password) VALUES (?, ?, ?)",
272
                apiKey,
273
                email,
274
                hash, (err) => {
275
                    if (err) {
276
                        return res.status(500).json({
277
                            errors: {
278
                                status: 500,
279
                                source: "/register",
280
                                title: "Database error",
281
                                detail: err.message
282 11
                            }
283 11
                        });
284 11
                    }
285
286 11
                    return res.status(201).json({
287
                        data: {
288 11
                            message: "User successfully registered."
289
                        }
290
                    });
291 3
                });
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
        });
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...
293
    },
294
295
    checkToken: function(req, res, next) {
296
        var token = req.headers['x-access-token'];
297
298 View Code Duplication
        if (token) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
299
            jwt.verify(token, jwtSecret, function(err, decoded) {
300
                if (err) {
301
                    return res.status(500).json({
302
                        errors: {
303 1
                            status: 500,
304
                            source: req.path,
305
                            title: "Failed authentication",
306
                            detail: err.message
307
                        }
308
                    });
309
                }
310
311
                req.user = {};
312
                req.user.api_key = decoded.api_key;
313
                req.user.email = decoded.email;
314
315
                next();
316
317
                return undefined;
318
            });
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...
319
        } else {
320
            return res.status(401).json({
321
                errors: {
322
                    status: 401,
323
                    source: req.path,
324
                    title: "No token",
325
                    detail: "No token provided in request headers"
326
                }
327
            });
328
        }
329
    }
330
};
331
332
module.exports = auth;
333