const fastify = require('fastify')({ logger: true }) const fs = require('fs'); const path = require('path') var LdapAuth = require('ldapauth-fork'); var prankPath = "prankdata.txt"; if (!fs.existsSync(prankPath)) { fs.writeFileSync(prankPath, "{}"); } var activityPath = "activitydata.txt"; if (!fs.existsSync(activityPath)) { fs.writeFileSync(activityPath, "{}"); } let PrankData = JSON.parse(fs.readFileSync(prankPath)); let ActivityData = JSON.parse(fs.readFileSync(activityPath)); let AdminUsersUid = ["asyncnomi", "johan", "enthalpine", "fas", "arina", "billy", "remi", "pierre", "matmaz", "", "", ""]; let UsersToken = {}; let TokenDurationSecond = 3600; let MaxAmountCrepe = 10; let Supplements = ["nature", "sucre", "nutella", "confiture"]; var ldapConf = JSON.parse(fs.readFileSync("ldap-conf.json")); var LDAP = new LdapAuth({ url: 'ldap://10.5.0.44', bindDN: 'cn='+ ldapConf.bindUser +',ou=service-users,dc=ldap,dc=rezo-rm,dc=fr', bindCredentials: ldapConf.bindPassword, searchBase: 'dc=ldap,dc=rezo-rm,dc=fr', searchFilter: '(uid={{username}})', reconnect: true, }); LDAP.on('error', function (err) { console.error('LdapAuth: ', err); }); ldapConf = null; fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function(req, body, done) { try { var json = JSON.parse(body) done(null, json) } catch (err) { err.statusCode = 400 done(err, undefined) } }) fastify.register(require('@fastify/static'), { root: path.join(__dirname, 'static'), decorateReply: false }) fastify.get('/', async (request, reply) => { reply.redirect('/index.html') }) fastify.post('/login', async (request, reply) => { let content = request.body; if (content.hasOwnProperty("user") && content.hasOwnProperty("password")) { let res = await authenticate(content.user, content.password); if (res.authState) { let now = new Date(); UsersToken[res.authUser.uid] = { token: makeid(64), expire: now.setSeconds(now.getSeconds() + TokenDurationSecond) } return { success: true, user: { uid: res.authUser.uid, givenName: res.authUser.givenName }, token: UsersToken[res.authUser.uid].token } } else { return { success: false, why: "Wrong username or password" } } } else { return { success: false, why: "The username or password is missing" } } }) fastify.post('/addPrank', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { if ("type" in content) { let prankUid = makeid(16); if ("prankUid" in content) { let prankExists = checkPrank(content) if (prankExists.success) { if (PrankData[prankUid].state != "Pending") { return { success: false, why: "You cannot edit already accepted prank request" } } else { prankUid = content.prankUid; } } else { return prankExists; } } let note = ("note" in content) ? content.note : "N/A"; switch (content.type) { case "crêpe": if ("where" in content && "amount" in content && "supplement" in content) { let amount = parseInt(content.amount) if (!isNaN(amount)) { if (!Supplements.contains(content.supplement)) { if (amount < MaxAmountCrepe) { let prankUid = makeid(16); PrankData[prankUid] = { creator: content.uid, type: content.type, where: content.where, amount: amount, supplement: content.supplement, note: content.note, state: "Pending", manageBy: null } saveData(prankPath, PrankData); return { sucess: true, uid: prankUid, prank: PrankData[prankUid] } } else { return { success: false, why: "Too much" } } } else { return { success: false, why: "This supplement isn't available" } } } else { return { success: false, why: "Unable to parse the amount as integer" } } } else { return { success: false, why: "Missing amount, where or supplement" } } break; case "kidnap": if ("targetUid" in content && "when" in content) { let prankUid = makeid(16); PrankData[prankUid] = { creator: content.uid, type: content.type, targetUid: content.targetUid, when: content.when, note: content.note, state: "Pending", manageBy: null } saveData(prankPath, PrankData); return { sucess: true, uid: prankUid, prank: PrankData[prankUid] } } else { return { success: false, why: "Missing amount or where" } } break; default: return { success: false, why: "Unknow type" } } } else { return { success: false, why: "Missing type" } } } else { return auth } }) fastify.post('/delPrank', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { let prankExists = checkPrank(content) if (prankExists.success) { if (PrankData[content.prankUid].creator === content.uid) { delete PrankData[content.prankUid]; return { success: true, } } else { return { success: false, why: "Not allowed" } } } else { return prankExists } } else { return auth } }) fastify.post('/acceptPrank', async (request, reply) => { let content = request.body; let prankExists = checkManagePrank(content) if (prankExists.success) { PrankData[content.prankUid].state = "Accepted"; PrankData[content.prankUid].manageBy = content.uid; return { success: true, } } else { return prankExists } }) fastify.post('/donePrank', async (request, reply) => { let content = request.body; let prankExists = checkManagePrank(content) if (prankExists.success) { if (PrankData[content.prankUid].manageBy == content.uid) { PrankData[content.prankUid].state = "Done"; return { success: true, } } else { return { success: false, why: "Not allowed" } } } else { return prankExists } }) fastify.post('/refusePrank', async (request, reply) => { let content = request.body; let prankExists = checkManagePrank(content) if (prankExists.success) { PrankData[content.prankUid].state = "Refused"; PrankData[content.prankUid].manageBy = content.uid; return { success: true, } } else { return prankExists } }) fastify.post('/get', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { if ("type" in content) { switch (content.type) { case "prank": if (AdminUsersUid.includes(content.uid)) { return { sucess: true, prankData: PrankData } } else { return { success: false, why: "Not Allowed" } } break; case "activity": return { sucess: true, activityData: ActivityData } break; default: return { success: false, why: "Unknown type" } } } else { return { success: false, why: "Missing type" } } } else { return auth } }) fastify.post('/addActivity', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { if (AdminUsersUid.includes(content.uid)) { if ("type" in content && "title" in content && "desc" in content && "start" in content && "end" in content && "where" in content) { if (["event", "treasure"].contains(content.type)) { let activityUid = makeid(16); if ("activityUid" in content) { let activityExists = checkActivity(content) if (activityExists.success) { activityUid = content.activityUid; } else { return activityExists; } } ActivityData[activityUid] = { type: content.type, title: content.title, desc: content.desc, start: content.start, end: content.end, where: content.where } saveData(activityPath, ActivityData); return { sucess: true, uid: activityUid, activity: ActivityData[activityUid] } } else { return { success: false, why: "Unkonw type" } } } else { return { success: false, why: "Missing type, title, desc, start, end or where" } } } else { return { success: false, why: "Not Allowed" } } } }) fastify.post('/delActivity', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { if (AdminUsersUid.includes(content.uid)) { let activityExists = checkActivity(content) if (activityExists.success) { delete ActivityData[content.activityUid] } else { return activityExists } } else { return { success: false, why: "Not allowed" } } } else { return auth } }) fastify.post('/sendTreasure', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { let activityExists = checkActivity(content) if (activityExists.success) { } else { return activityExists } } else { return auth } }) function saveData(path, data) { fs.writeFileSync(path, JSON.stringify(data)); } function authenticate(user, pwd) { return new Promise((resolve, reject) => { LDAP.authenticate(user, pwd, function(err, user) { if (user && err == null) { resolve({ authState: true, authUser: user }); } else { resolve({ authState: false, authUser: null }); } }); }) } function checkAuthetification(content) { if (content.hasOwnProperty("uid") && content.hasOwnProperty("token")) { if (UsersToken.hasOwnProperty(content.uid) && UsersToken[content.uid].token === content.token) { if (UsersToken[content.uid].expire > new Date()) { return { success: true } } else { delete UsersToken[content.uid]; return { success: false, why: "Token expired" } } } else { return { success: false, why: "Not authentificated" } } } } function checkPrank(content) { if ("prankUid" in content) { if (content.prankUid in PrankData) { return { success: true, } } else { return { success: false, why: "Unknow prankUid" } } } else { return { success: false, why: "Missing prankUid" } } } function checkActivity(content) { if ("activityUid" in content) { if (content.activityUid in ActivityData) { return { success: true, } } else { return { success: false, why: "Unknow activityUid" } } } else { return { success: false, why: "Missing activityUid" } } } function checkManagePrank(content) { let auth = checkAuthetification(content); if (auth.success) { if (AdminUsersUid.includes(content.uid)) { let prankExists = checkPrank(content) if (prankExists.success) { return { success: true } } else { return prankExists } } else { return { success: false, why: "Not Allowed" } } } else { return auth } } function makeid(length) { var result = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for (var i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } const start = async () => { try { await fastify.listen({ port: 3000 , host: '127.0.0.1',}) } catch (err) { fastify.log.error(err) LDAP.close(function(err) { console.log(err); }) process.exit(1) } } start()