CESI - Exo Microbloggr Solution - Authentification fonctionnelle
This commit is contained in:
parent
68ccdca3a4
commit
eaa659f3c6
|
@ -1,14 +1,76 @@
|
||||||
version: '2'
|
version: '2.2'
|
||||||
services:
|
services:
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
build:
|
build:
|
||||||
context: containers/gateway
|
context: services/gateway
|
||||||
|
args:
|
||||||
|
- HTTP_PROXY=${HTTP_PROXY}
|
||||||
|
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||||
|
- http_proxy=${HTTP_PROXY}
|
||||||
|
- https_proxy=${HTTP_PROXY}
|
||||||
|
links:
|
||||||
|
- home_page
|
||||||
|
- login_page
|
||||||
|
- logout
|
||||||
ports:
|
ports:
|
||||||
- 8080:80
|
- 8080:80
|
||||||
|
|
||||||
home_page:
|
home_page:
|
||||||
build:
|
build:
|
||||||
context: containers/home_page
|
context: services/home_page
|
||||||
|
args:
|
||||||
|
- HTTP_PROXY=${HTTP_PROXY}
|
||||||
|
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||||
|
- http_proxy=${HTTP_PROXY}
|
||||||
|
- https_proxy=${HTTP_PROXY}
|
||||||
|
environment:
|
||||||
|
SESSION_SECRET: "absolutly_not_secret"
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
volumes:
|
||||||
|
- ./services/home_page/src:/app/src:ro
|
||||||
|
|
||||||
login_page:
|
login_page:
|
||||||
build:
|
build:
|
||||||
context: containers/login_page
|
context: services/login_page
|
||||||
|
args:
|
||||||
|
- HTTP_PROXY=${HTTP_PROXY}
|
||||||
|
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||||
|
- http_proxy=${HTTP_PROXY}
|
||||||
|
- https_proxy=${HTTP_PROXY}
|
||||||
|
links:
|
||||||
|
- users
|
||||||
|
- redis
|
||||||
|
volumes:
|
||||||
|
- ./services/login_page/src:/app/src:ro
|
||||||
|
environment:
|
||||||
|
SESSION_SECRET: "absolutly_not_secret"
|
||||||
|
|
||||||
|
|
||||||
|
logout:
|
||||||
|
build:
|
||||||
|
context: services/logout
|
||||||
|
args:
|
||||||
|
- HTTP_PROXY=${HTTP_PROXY}
|
||||||
|
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||||
|
- http_proxy=${HTTP_PROXY}
|
||||||
|
- https_proxy=${HTTP_PROXY}
|
||||||
|
volumes:
|
||||||
|
- ./services/logout/src:/app/src:ro
|
||||||
|
environment:
|
||||||
|
SESSION_SECRET: "absolutly_not_secret"
|
||||||
|
|
||||||
|
users:
|
||||||
|
build:
|
||||||
|
context: services/users
|
||||||
|
args:
|
||||||
|
- HTTP_PROXY=${HTTP_PROXY}
|
||||||
|
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||||
|
- http_proxy=${HTTP_PROXY}
|
||||||
|
- https_proxy=${HTTP_PROXY}
|
||||||
|
volumes:
|
||||||
|
- ./services/users/src:/app/src:ro
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:5.0-alpine
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.2
|
FROM alpine:3.7
|
||||||
|
|
||||||
RUN apk add --no-cache nginx
|
RUN apk add --no-cache nginx
|
||||||
COPY microbloggr.conf /etc/nginx/microbloggr.conf
|
COPY microbloggr.conf /etc/nginx/microbloggr.conf
|
||||||
|
|
|
@ -28,6 +28,10 @@ http {
|
||||||
proxy_pass http://login_page:8080/;
|
proxy_pass http://login_page:8080/;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /logout {
|
||||||
|
proxy_pass http://logout:8080/;
|
||||||
|
}
|
||||||
|
|
||||||
# location /profile {
|
# location /profile {
|
||||||
# proxy_pass http://profile_page:8080/;
|
# proxy_pass http://profile_page:8080/;
|
||||||
# }
|
# }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
/app/node_modules
|
/node_modules
|
||||||
|
|
|
@ -2,9 +2,10 @@ FROM alpine:3.7
|
||||||
|
|
||||||
RUN apk add --no-cache nodejs
|
RUN apk add --no-cache nodejs
|
||||||
|
|
||||||
COPY app /app
|
COPY ./ /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
RUN npm install -g nodemon
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["nodemon", "src/server.js"]
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"name": "login_page",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"morgan": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "~2.0.0",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"on-headers": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,8 @@
|
||||||
"ejs": "^2.5.7",
|
"ejs": "^2.5.7",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"express-session": "^1.15.6",
|
"express-session": "^1.15.6",
|
||||||
|
"connect-redis": "^3.4.0",
|
||||||
|
"morgan": "^1.9.1",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
"request-promise-native": "^1.0.5"
|
"request-promise-native": "^1.0.5"
|
||||||
}
|
}
|
|
@ -1,11 +1,17 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const session = require('express-session');
|
const session = require('express-session');
|
||||||
|
var RedisStore = require('connect-redis')(session);
|
||||||
const app = express();
|
const app = express();
|
||||||
|
const morgan = require('morgan');
|
||||||
|
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
|
app.set('views', __dirname + '/views');
|
||||||
|
|
||||||
|
app.use(morgan('combined'));
|
||||||
app.use(session({
|
app.use(session({
|
||||||
secret: 'AbsolutlyNotSecret', // Ce secret doit être partagé par tous les microservices *_page
|
store: new RedisStore({host: 'redis'}),
|
||||||
|
secret: process.env.SESSION_SECRET, // Ce secret doit être partagé par tous les microservices *_page
|
||||||
|
cookie: { maxAge: 60000 },
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
}));
|
}));
|
||||||
|
@ -20,10 +26,10 @@ app.get('/', (req, res) => {
|
||||||
// On vérifie si un utilisateur a été associé à la session HTTP
|
// On vérifie si un utilisateur a été associé à la session HTTP
|
||||||
if (req.session.user) {
|
if (req.session.user) {
|
||||||
// Si oui, on affichage la page d'accueil
|
// Si oui, on affichage la page d'accueil
|
||||||
return res.render('index');
|
return res.render('index', { user: req.session.user });
|
||||||
} else {
|
} else {
|
||||||
// Si non, on redirige l'utilisateur vers la page de login
|
// Si non, on redirige l'utilisateur vers la page de login
|
||||||
return res.redirect(301, '/login');
|
return res.redirect(302, '/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
|
@ -4,6 +4,10 @@
|
||||||
<nav class="main">
|
<nav class="main">
|
||||||
<h1 class="brand">MicroBloggr</div>
|
<h1 class="brand">MicroBloggr</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="content">
|
||||||
|
Bienvenue <%= user.username; %> !
|
||||||
|
<a href="/logout">Se déconnecter</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1 +1 @@
|
||||||
/app/node_modules
|
/node_modules
|
||||||
|
|
|
@ -2,9 +2,10 @@ FROM alpine:3.7
|
||||||
|
|
||||||
RUN apk add --no-cache nodejs
|
RUN apk add --no-cache nodejs
|
||||||
|
|
||||||
COPY app /app
|
COPY ./ /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
RUN npm install -g nodemon
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["nodemon", "src/server.js"]
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const app = express();
|
|
||||||
const session = require('express-session');
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const request = require('request-promise-native');
|
|
||||||
|
|
||||||
app.set('view engine', 'ejs');
|
|
||||||
|
|
||||||
app.use(session({
|
|
||||||
secret: 'AbsolutlyNotSecret', // Ce secret doit être partagé par tous les microservices *_page
|
|
||||||
resave: false,
|
|
||||||
saveUninitialized: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// On utilise le module "body-parser"
|
|
||||||
// pour faciliter la désérialisation des données
|
|
||||||
// du formulaire
|
|
||||||
app.use(bodyParser.urlencoded({extended: false}));
|
|
||||||
|
|
||||||
// GET /
|
|
||||||
// Affiche la page de login
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.render('index', { error: null });
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// POST /
|
|
||||||
// Récupère les données du formulaire de login
|
|
||||||
app.post('/', (req, res) => {
|
|
||||||
|
|
||||||
const form = req.body;
|
|
||||||
|
|
||||||
// Si le formulaire ne contient pas les données attendues
|
|
||||||
// on renvoie un code HTTP 400 pour indiquer une requête invalide.
|
|
||||||
if(!form.username || !form.password) {
|
|
||||||
return res.render('index', { error: 'Identifiant ou mot de passe manquant.'});
|
|
||||||
}
|
|
||||||
|
|
||||||
// On interroge le microservices "users"
|
|
||||||
// pour récupérer l'utilisateur associé à l'identifiant
|
|
||||||
// fourni dans le formulaire
|
|
||||||
findUserByName(form.username)
|
|
||||||
.then(user => {
|
|
||||||
|
|
||||||
// Si l'utilisateur n'existe pas, on affiche un message d'erreur
|
|
||||||
if(!user) {
|
|
||||||
return res.render('index', { error: 'Identifiant invalide !'});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si le mot de passe de l'utilisateur correspond à celui enregistré sur le
|
|
||||||
// microservice "users", on attache l'identifiant de l'utilisateur à la session
|
|
||||||
// HTTP puis on le redirige sur la page d'accueil
|
|
||||||
if(user.password === form.password) {
|
|
||||||
req.session.user = user;
|
|
||||||
return res.redirect(302, '/');
|
|
||||||
} else {
|
|
||||||
// Sinon, on affiche un message d'erreur
|
|
||||||
return res.render('index', { error: 'Mot de passe invalide !'});
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// Une erreur s'est produite en essayant de contacter le microservices "users"
|
|
||||||
// On affiche un message d'erreur à l'utilisateur
|
|
||||||
return res.render('index', {
|
|
||||||
error: 'Une erreur s\'est produite pendant le processus d\'authentification.'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(8080, () => console.log('listening on port 8080'));
|
|
||||||
|
|
||||||
// Récupère un utilisateur via son identifiant
|
|
||||||
// sur le microservice "users"
|
|
||||||
function findUserByName(username) {
|
|
||||||
return request({
|
|
||||||
uri: `http://users:8080/users/${username}`,
|
|
||||||
json: true
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// Si le microservice "users" répond avec un code HTTP 404
|
|
||||||
// c'est que l'utilisateur n'existe pas
|
|
||||||
if (err.response.statusCode === 404) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
;
|
|
||||||
}
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
{
|
||||||
|
"name": "login_page",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connect-redis": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-YKPSO9tLwzUr8jzhsGMdSJUxevWrDt0ggXRcTMb+mtnJ/vWGlWV7RC4VUMgqvZv3uTGDFye8Bf7d6No0oSVkOQ==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^4.0.1",
|
||||||
|
"redis": "^2.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"double-ended-queue": {
|
||||||
|
"version": "2.1.0-0",
|
||||||
|
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
|
||||||
|
"integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"morgan": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "~2.0.0",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"on-headers": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
|
||||||
|
},
|
||||||
|
"redis": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
|
||||||
|
"requires": {
|
||||||
|
"double-ended-queue": "^2.1.0-0",
|
||||||
|
"redis-commands": "^1.2.0",
|
||||||
|
"redis-parser": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis-commands": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw=="
|
||||||
|
},
|
||||||
|
"redis-parser": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
|
||||||
|
"integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,11 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
|
"connect-redis": "^3.4.0",
|
||||||
"ejs": "^2.5.7",
|
"ejs": "^2.5.7",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"express-session": "^1.15.6",
|
"express-session": "^1.15.6",
|
||||||
|
"morgan": "^1.9.1",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
"request-promise-native": "^1.0.5"
|
"request-promise-native": "^1.0.5"
|
||||||
}
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const session = require('express-session');
|
||||||
|
var RedisStore = require('connect-redis')(session);
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const request = require('request-promise-native');
|
||||||
|
const morgan = require('morgan');
|
||||||
|
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
app.set('views', __dirname + '/views');
|
||||||
|
|
||||||
|
app.use(morgan('combined'));
|
||||||
|
app.use(session({
|
||||||
|
store: new RedisStore({host: 'redis'}),
|
||||||
|
secret: process.env.SESSION_SECRET, // Ce secret doit être partagé par tous les microservices *_page
|
||||||
|
cookie: { maxAge: 60000 },
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// On utilise le module "body-parser"
|
||||||
|
// pour faciliter la désérialisation des données
|
||||||
|
// du formulaire
|
||||||
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
|
|
||||||
|
// GET /
|
||||||
|
// Affiche la page de login
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.render('index', { error: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /
|
||||||
|
// Récupère les données du formulaire de login
|
||||||
|
app.post('/', (req, res) => {
|
||||||
|
|
||||||
|
const form = req.body;
|
||||||
|
|
||||||
|
// Si le formulaire ne contient pas les données attendues
|
||||||
|
// on renvoie un code HTTP 400 pour indiquer une requête invalide.
|
||||||
|
if(!form.username || !form.password) {
|
||||||
|
return res.render('index', { error: 'Identifiant ou mot de passe manquant.'});
|
||||||
|
}
|
||||||
|
|
||||||
|
// On interroge le microservices "users"
|
||||||
|
// pour récupérer l'utilisateur associé à l'identifiant
|
||||||
|
// fourni dans le formulaire
|
||||||
|
authenticateUser(form.username, form.password)
|
||||||
|
.then(result => {
|
||||||
|
|
||||||
|
// Si l'utilisateur n'existe pas, on affiche un message d'erreur
|
||||||
|
if(!result.ok) {
|
||||||
|
return res.render('index', { error: result.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si le microservice "users" renvoie "ok", alors cela signifie
|
||||||
|
// que le mot de passe est valide
|
||||||
|
req.session.user = result.user;
|
||||||
|
return res.redirect(303, '/');
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
return res.end(500).send(err.message);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(8080, () => console.log('listening on port 8080'));
|
||||||
|
|
||||||
|
// Authentifie un utilisateur via son couple identifiant/mot de passe
|
||||||
|
// sur le microservice "users"
|
||||||
|
function authenticateUser(username, password) {
|
||||||
|
return request({
|
||||||
|
uri: `http://users:8080/auth`,
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
username, password
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
/node_modules
|
|
@ -0,0 +1 @@
|
||||||
|
/node_modules
|
|
@ -2,9 +2,10 @@ FROM alpine:3.7
|
||||||
|
|
||||||
RUN apk add --no-cache nodejs
|
RUN apk add --no-cache nodejs
|
||||||
|
|
||||||
COPY app /app
|
COPY ./ /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
RUN npm install -g nodemon
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["nodemon", "src/server.js"]
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "logout",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"connect-redis": "^3.4.0",
|
||||||
|
"express-session": "^1.15.6",
|
||||||
|
"express": "^4.16.2",
|
||||||
|
"morgan": "^1.9.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const session = require('express-session');
|
||||||
|
var RedisStore = require('connect-redis')(session);
|
||||||
|
const morgan = require('morgan');
|
||||||
|
|
||||||
|
app.use(morgan('combined'));
|
||||||
|
app.use(session({
|
||||||
|
store: new RedisStore({host: 'redis'}),
|
||||||
|
secret: process.env.SESSION_SECRET, // Ce secret doit être partagé par tous les microservices accédant à la session
|
||||||
|
cookie: { maxAge: 60000 },
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// GET /
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
req.session.destroy(err => {
|
||||||
|
if (err) return res.status(500).send(err.stack);
|
||||||
|
return res.status(303).redirect("/");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(8080, () => console.log('listening on port 8080'));
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
/app/node_modules
|
|
|
@ -1,102 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
const sessions = {};
|
|
||||||
let sessionCounter = 0;
|
|
||||||
|
|
||||||
// On utilise le middleware "body-parser"
|
|
||||||
// pour désérialiser automatiquement
|
|
||||||
// les objets JSON potentiellement
|
|
||||||
// fournis par les requêtes
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
|
|
||||||
// GET /sessions
|
|
||||||
// Permet de récupérer la liste des sessions en cours
|
|
||||||
app.get('/sessions', (req, res) => {
|
|
||||||
res.json(sessions);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// GET /sessions/:id
|
|
||||||
// Permet de récupérer une session via son identifiant
|
|
||||||
app.get('/sessions/:id', (req, res) => {
|
|
||||||
|
|
||||||
// On récupère l'identifiant de session
|
|
||||||
// depuis l'URL
|
|
||||||
const sessionId = req.params.id;
|
|
||||||
|
|
||||||
// On vérifie que la session existe.
|
|
||||||
// Si ce n'est pas le cas, on retourne un code
|
|
||||||
// HTTP 404 pour indiquer que la ressource
|
|
||||||
// n'existe pas
|
|
||||||
if (!sessions.hasOwnProperty(sessionId)) {
|
|
||||||
return res.status(404).end('Not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// On retourne la session sérialisée en JSON
|
|
||||||
return res.json(sessions[sessionId]);
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
// POST /sessions
|
|
||||||
// Body: {"user": <user_id> }
|
|
||||||
// Permet de créer une nouvelle session pour l'utilisateur donné
|
|
||||||
app.post('/sessions', (req, res) => {
|
|
||||||
|
|
||||||
const payload = req.body;
|
|
||||||
|
|
||||||
// On valide la présence de l'attribut "user"
|
|
||||||
// sur les données fournies.
|
|
||||||
// Si il n'est pas présent, on retourne un code HTTP
|
|
||||||
// 400 pour indiquer que la requête est invalide.
|
|
||||||
if (!payload.user) {
|
|
||||||
return res.status(400).end('Bad request.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// On créait une nouvelle session
|
|
||||||
const sess = {
|
|
||||||
user: payload.user,
|
|
||||||
id: getSessionId(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// On la "stocke" dans notre registre
|
|
||||||
sessions[session.id] = sess;
|
|
||||||
|
|
||||||
// On retourne la session
|
|
||||||
// nouvellement créée
|
|
||||||
res.json(sess);
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// DELETE /sessions/:id
|
|
||||||
// Permet de supprimer une session dans le registre via son identifiant
|
|
||||||
app.delete('/sessions/:id', (req, res) => {
|
|
||||||
|
|
||||||
// On récupère l'identifiant de session
|
|
||||||
// depuis l'URL
|
|
||||||
const sessionId = req.params.id;
|
|
||||||
|
|
||||||
// On vérifie que la session existe.
|
|
||||||
// Si ce n'est pas le cas, on retourne un code
|
|
||||||
// HTTP 404 pour indiquer que la ressource
|
|
||||||
// n'existe pas
|
|
||||||
if (!sessions.hasOwnProperty(sessionId)) {
|
|
||||||
return res.status(404).end('Not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
delete sessions[sessionId];
|
|
||||||
|
|
||||||
// On retourne un code HTTP 204 pour indiquer que l'opération
|
|
||||||
// s'est déroulée correctement mais qu'aucun "contenu" n'est disponible
|
|
||||||
return res.status(204);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.listen(8080, () => console.log('listening on port 8080'));
|
|
||||||
|
|
||||||
function getSessionId() {
|
|
||||||
return `${Date.now()}-${sessionCounter++}`;
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
/node_modules
|
|
@ -0,0 +1 @@
|
||||||
|
/node_modules
|
|
@ -0,0 +1,11 @@
|
||||||
|
FROM alpine:3.7
|
||||||
|
|
||||||
|
RUN apk add --no-cache nodejs
|
||||||
|
|
||||||
|
COPY ./ /app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN npm install --production
|
||||||
|
RUN npm install -g nodemon
|
||||||
|
|
||||||
|
CMD ["nodemon", "src/server.js"]
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "sessions",
|
"name": "users",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "server.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"express": "^4.16.2"
|
"express": "^4.16.2",
|
||||||
|
"morgan": "^1.9.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const morgan = require('morgan');
|
||||||
|
const users = require('./users');
|
||||||
|
|
||||||
|
app.use(morgan('combined'));
|
||||||
|
|
||||||
|
// On utilise le module "body-parser"
|
||||||
|
// pour faciliter la désérialisation des données
|
||||||
|
// des appels à l'API
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
// POST /auth
|
||||||
|
// Effectue une authentification pour un couple d'identifiants donné
|
||||||
|
app.post('/auth', (req, res) => {
|
||||||
|
|
||||||
|
const credentials = req.body;
|
||||||
|
|
||||||
|
if(!credentials.username || !credentials.password) {
|
||||||
|
return res.status(200).send({
|
||||||
|
ok: false,
|
||||||
|
error: "Identifiants invalides.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = users[credentials.username];
|
||||||
|
|
||||||
|
if (!user || user.password !== credentials.password) {
|
||||||
|
return res.status(200).send({
|
||||||
|
ok: false,
|
||||||
|
error: "Identifiants invalides.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
ok: true,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(8080, () => console.log('listening on port 8080'));
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Liste des utilisateurs définie statiquement
|
||||||
|
// Dans une situation réelle, ces comptes seraient stockés
|
||||||
|
// en BDD et les mots de passe hachés/salés.
|
||||||
|
module.exports = {
|
||||||
|
admin: {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'admin'
|
||||||
|
},
|
||||||
|
user1: {
|
||||||
|
username: 'user1',
|
||||||
|
password: 'user1'
|
||||||
|
},
|
||||||
|
user2: {
|
||||||
|
username: 'user2',
|
||||||
|
password: 'user2'
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue