Skip to content

Commit fea9686

Browse files
author
Zoltán Takács
committed
Authentication
1 parent 0a17301 commit fea9686

23 files changed

+304
-82
lines changed

package-lock.json

+92-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"cors": "^2.8.5",
2020
"express": "^4.16.4",
2121
"fs-extra": "^7.0.1",
22+
"jsonwebtoken": "^8.5.1",
2223
"shortid": "^2.2.14"
2324
},
2425
"devDependencies": {

src/fileStorage.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
11
const fs = require('fs-extra');
2-
const inmemoryStorage = require('./inmemoryStorage');
32

4-
const FILE_NAME = 'todos.json';
3+
const TODO_FILE_NAME = 'todos.json';
4+
const USER_FILE_NAME = 'users.json';
55

66
const updateTodoFile = async todos => {
77
try {
8-
await fs.ensureFile(FILE_NAME);
8+
await fs.ensureFile(TODO_FILE_NAME);
99

10-
fs.writeJSON(FILE_NAME, todos);
10+
fs.writeJSON(TODO_FILE_NAME, todos);
1111
} catch (err) {
1212
console.error(err);
1313
}
1414
};
1515

16-
const readTodoFile = async () => {
17-
const exists = await fs.pathExists(FILE_NAME);
16+
const readFile = async (fileName) => {
17+
const exists = await fs.pathExists(fileName);
1818

1919
if (!exists) {
2020
return [];
2121
}
2222

23-
const todos = await fs.readJsonSync(FILE_NAME);
23+
const todos = await fs.readJsonSync(fileName);
2424

2525
return todos;
2626
};
2727

2828
module.exports = {
2929
updateTodoFile,
30-
readTodoFile
30+
readTodoFile: readFile.bind(null, TODO_FILE_NAME),
31+
readUserFile: readFile.bind(null, USER_FILE_NAME)
3132
};

src/index.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ const fileStorage = require('./fileStorage');
1313
const inmemoryStorage = require('./inmemoryStorage');
1414

1515
const initContext = () => {
16-
const todoStorage = inmemoryStorage();
17-
return { fileStorage, todoStorage };
16+
const memoryStorage = inmemoryStorage();
17+
return { fileStorage, memoryStorage: memoryStorage };
1818
};
1919

2020
const initInMemoryStorage = async context => {
21-
const fileContent = await context.fileStorage.readTodoFile();
22-
context.todoStorage.setTodos(fileContent);
21+
const todos = await context.fileStorage.readTodoFile();
22+
const users = await context.fileStorage.readUserFile();
23+
context.memoryStorage.setTodos(todos);
24+
context.memoryStorage.setUsers(users);
2325
};
2426

2527
const initApp = context => {
@@ -28,16 +30,18 @@ const initApp = context => {
2830
app.use(cors());
2931
app.use(bodyParser.json());
3032

31-
const todos = express.Router();
33+
// const todos = express.Router();
3234

33-
todos.use(contextMiddleware(context));
35+
// todos.use(contextMiddleware(context));
36+
app.use(contextMiddleware(context));
3437

3538
routes.forEach(route => {
3639
const method = route.method ? route.method.toLowerCase() : 'get';
37-
return todos[method](route.path, route.handler);
40+
const handlers = route.preHandlers ? route.preHandlers.concat(route.handler) : route.handler
41+
return app[method](route.path, handlers);
3842
});
3943

40-
app.use('/todos', todos);
44+
// app.use('/todos', todos);
4145
app.use(errorMiddleware);
4246

4347
return app;

src/inmemoryStorage.js

+36-9
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@ const shortid = require('shortid');
22

33
module.exports = () => {
44
let todos = [];
5+
let users = [];
56

67
const getTodos = filterFn => (filterFn ? filterFn(todos) : todos);
78

9+
const getUserTodos = (userId, filterFn) =>
10+
getTodos(filterFn).filter(todo => todo.userId === userId);
11+
812
const setTodos = initial => (todos = initial);
913

10-
const getTodoById = id => todos.find(todo => todo.id === id);
14+
const getTodoById = (id, userId) => todos.find(todo => todo.id === id && todo.userId === userId);
1115

12-
const addTodo = title => {
16+
const addTodo = (title, userId) => {
1317
const todo = {
1418
id: shortid.generate(),
1519
title,
20+
userId,
1621
completed: false
1722
};
1823

@@ -21,22 +26,44 @@ module.exports = () => {
2126
};
2227

2328
const patchTodo = (patchId, newProps) =>
24-
todos.map(todo =>
25-
todo.id === patchId ? Object.assign(todo, newProps) : todo
26-
);
29+
(todos = todos.map(todo => (todo.id === patchId ? Object.assign(todo, newProps) : todo)));
30+
31+
const deleteTodo = deleteId => (todos = todos.filter(todo => todo.id !== deleteId));
2732

28-
const deleteTodo = deleteId =>
29-
(todos = todos.filter(todo => todo.id !== deleteId));
33+
const clearUserTodos = userId => {
34+
todos = todos.filter(todo => todo.userId !== userId);
35+
return [];
36+
};
37+
38+
const clearUserCompletedTodos = userId => {
39+
todos = todos.filter(todo => (!todo.completed && todo.userId === userId) || todo.userId !== userId);
40+
return todos.filter(todo => todo.userId === userId);
41+
};
3042

31-
const clearTodos = filterFn => (filterFn ? filterFn(todos) : []);
43+
const getUsers = () => users;
44+
45+
const setUsers = initial => (users = initial);
46+
47+
const getUserById = id => users.find(user => user.id === id);
48+
49+
const authenticateUser = (username, password) => {
50+
const user = users.find(user => user.username === username && user.password === password);
51+
return Object.assign({}, user, { password: undefined });
52+
};
3253

3354
return {
3455
getTodos,
56+
getUserTodos,
3557
setTodos,
3658
getTodoById,
3759
addTodo,
3860
patchTodo,
3961
deleteTodo,
40-
clearTodos
62+
clearUserTodos,
63+
clearUserCompletedTodos,
64+
getUsers,
65+
setUsers,
66+
getUserById,
67+
authenticateUser
4168
};
4269
};

src/login/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
login: require('./login'),
3+
renew: require('./renew')
4+
};

src/login/login.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const jwt = require('jsonwebtoken');
2+
3+
module.exports = (req, res) => {
4+
const { username, password } = req.body;
5+
6+
const me = req.context.memoryStorage.authenticateUser(username, password);
7+
8+
if (!me) {
9+
throw new Error('The resource could not be found.|404|NOT_FOUND');
10+
}
11+
12+
const token = jwt.sign({ userId: me.id }, 'almafa', {
13+
expiresIn: '30 days'
14+
});
15+
16+
return res.send({ token, me });
17+
};

src/login/renew.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const jwt = require('jsonwebtoken');
2+
3+
module.exports = (req, res) => {
4+
const { prevJwt } = req.body;
5+
6+
const { userId } = jwt.verify(prevJwt, 'almafa');
7+
8+
const me = req.context.memoryStorage.getUserById(userId);
9+
10+
if (!me) {
11+
throw new Error('The resource could not be found.|404|NOT_FOUND');
12+
}
13+
14+
const token = jwt.sign({ userId: me.id }, 'almafa');
15+
16+
return res.send({ token, me });
17+
};

src/middlewares/auth.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const jwt = require('jsonwebtoken');
2+
3+
const auth = (req, res, next) => {
4+
const token = req.get('authorization').replace(/Bearer\s?/g, '');
5+
6+
if (!token) {
7+
throw new Error('Unauthorized|401|UNAUTHORIZED');
8+
}
9+
10+
const { userId } = jwt.verify(token, 'almafa');
11+
12+
if (!userId) {
13+
throw new Error('Unauthorized|401|UNAUTHORIZED');
14+
}
15+
16+
const user = req.context.memoryStorage.getUserById(userId);
17+
18+
req.user = user;
19+
20+
next();
21+
};
22+
23+
const checkPermission = permission => (req, res, next) => {
24+
if (!req.user.permissions.includes(permission)) {
25+
throw new Error('You have no permission for that!|403|FORBIDDEN');
26+
}
27+
28+
next();
29+
};
30+
31+
module.exports = {
32+
auth,
33+
checkPermission
34+
};

src/middlewares/error.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ module.exports = (err, req, res, next) => {
33

44
res
55
.status(status)
6-
.send({ message: status === '500' ? 'Something went wrong' : message, status: Number(status), code });
6+
.send({
7+
message: status === '500' ? 'Something went wrong' : message,
8+
status: Number(status),
9+
code
10+
});
711
next();
812
};

0 commit comments

Comments
 (0)