const fastify = require('fastify')({ logger: true }) const fs = require('fs'); const path = require('path') var LdapAuth = require('ldapauth-fork'); var prankPath = "prankdata.txt"; var activityPath = "activitydata.txt"; var treasurePath = "treasuredata.txt"; var goldenUsersPath = "goldenusers.txt"; initFs(); let PrankData = JSON.parse(fs.readFileSync(prankPath)); let ActivityData = JSON.parse(fs.readFileSync(activityPath)); let TreasureData = JSON.parse(fs.readFileSync(treasurePath)); let GoldenUsers = JSON.parse(fs.readFileSync(goldenUsersPath)); 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, isAdmin: AdminUsersUid.includes(res.authUser.uid) }, 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 = check(content, "prankUid", PrankData) 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.includes(content.supplement)) { if (amount < MaxAmountCrepe) { let prankUid = makeid(16); PrankData[prankUid] = { date: new Date(), 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 { success: 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 { success: 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 = check(content, "prankUid", PrankData) if (prankExists.success) { if (PrankData[content.prankUid].creator === content.uid && PrankData[content.prankUid].state === "Pending") { delete PrankData[content.prankUid]; saveData(prankPath, PrankData); return { success: true, } } else { return { success: false, why: "You can't delete prank that aren't yours or those already Accepted or Refused" } } } else { return prankExists } } else { return auth } }) fastify.post('/acceptPrank', async (request, reply) => { let content = request.body; let prankExists = checkManage(content, "prankUid", PrankData) if (prankExists.success) { PrankData[content.prankUid].state = "Accepted"; PrankData[content.prankUid].manageBy = content.uid; saveData(prankPath, PrankData); return { success: true, } } else { return prankExists } }) fastify.post('/donePrank', async (request, reply) => { let content = request.body; let prankExists = checkManage(content, "prankUid", PrankData) if (prankExists.success) { if (PrankData[content.prankUid].manageBy == content.uid) { PrankData[content.prankUid].state = "Done"; saveData(prankPath, PrankData); 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 = checkManage(content, "prankUid", PrankData) if (prankExists.success) { PrankData[content.prankUid].state = "Refused"; PrankData[content.prankUid].manageBy = content.uid; saveData(prankPath, PrankData); 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 { success: true, prankData: PrankData } } else { let prankData = {}; for (prank in PrankData) { if (PrankData[prank].creator == content.uid) { prankData[prank] = PrankData[prank]; } } return { success: true, prankData: prankData } } break; case "activity": return { success: true, activityData: ActivityData } break; case "treasure": let treasureData = JSON.parse(JSON.stringify(TreasureData)); for (treasure in treasureData) { treasureData[treasure].activity = ActivityData[treasureData[treasure].activityUid]; } if (AdminUsersUid.includes(content.uid)) { return { success: true, treasureData: treasureData } } else { let treasureDataUser = {}; for (treasure in treasureData) { if (treasureData[treasure].creator == content.uid) { treasureDataUser[treasure] = treasureData[treasure]; } } return { success: true, treasureData: treasureDataUser } } break; default: return { success: false, why: "Unknown type" } } } else { return { success: false, why: "Missing type" } } } else if ("type" in content) { switch (content.type) { case "activity": return { success: true, activityData: ActivityData } break; default: return auth; break; } } 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 && "where" in content) { let activityUid = makeid(16); if ("activityUid" in content) { let activityExists = check(content, "activityUid", ActivityData) if (activityExists.success) { activityUid = content.activityUid; } else { return activityExists; } } switch (content.type) { case "event": if ("end" in content) { ActivityData[activityUid] = { type: content.type, title: content.title, desc: content.desc, start: content.start, end: content.end, where: content.where, } saveData(activityPath, ActivityData); return { success: true, uid: activityUid, activity: ActivityData[activityUid] } } else { return { success: false, why: "Missing end" } } break; case "treasure": ActivityData[activityUid] = { type: content.type, title: content.title, desc: content.desc, start: content.start, where: content.where, treasureState: "Pending" } saveData(activityPath, ActivityData); return { success: true, uid: activityUid, activity: ActivityData[activityUid] } break; default: 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 activityExists = checkManage(content, "activityUid", ActivityData) if (activityExists.success) { delete ActivityData[content.activityUid] saveData(activityPath, ActivityData); return { success: true, } } else { return activityExists } }) fastify.post('/sendTreasure', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { let activityExists = check(content, "activityUid", ActivityData) if (activityExists.success) { if ("image" in content && "desc" in content && "activityUid" in content) { let treasureUid = makeid(16); if ("treasureUid" in content) { let treasureExists = check(content, "treasureUid", TreasureData) if (treasureExists.success) { if (TreasureData[content.treasureUid].state != "Pending" || TreasureData[content.treasureUid].creator != content.uid) { return { success: false, why: "You cannot edit already accepted or refused treasure request, or request form other people" } } else { treasureUid = content.treasureUid; } } else { return treasureExists; } } let activityExists = check(content, "activityUid", ActivityData) if (activityExists.success) { if (ActivityData[content.activityUid].type == "treasure") { let imageUid = makeid(128); let fileImage = `
` fs.writeFileSync("static/images/" + imageUid + ".html", fileImage); TreasureData[treasureUid] = { date: new Date(), creator: content.uid, image: imageUid, desc: content.desc, activityUid: content.activityUid, state: "Pending" } saveData(treasurePath, TreasureData); return { success: true, uid: treasureUid, treasure: TreasureData[treasureUid] } } else { return { success: false, why: "The given activityUid refers to an event and not a treasure quest" } } } else { return activityExists } } else { return { success: false, why: "Missing image, desc or activityUid" } } } else { return activityExists } } else { return auth } }) fastify.post('/delTreasure', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { let treasureExists = check(content, "treasureUid", TreasureData) if (treasureExists.success) { if (TreasureData[content.treasureUid].creator === content.uid && TreasureData[content.treasureUid].state == "Pending") { delete TreasureData[content.treasureUid]; saveData(treasurePath, TreasureData); return { success: true, } } else { return { success: false, why: "You can't delete treasure that aren't yours or those already Accepted or Refused" } } } else { return treasureExists } } else { return auth } }) fastify.post('/acceptTreasure', async (request, reply) => { let content = request.body; let treasureExists = checkManage(content, "treasureUid", TreasureData); if (treasureExists.success) { TreasureData[content.treasureUid].state = "Accepted"; saveData(treasurePath, TreasureData); ActivityData[TreasureData[content.treasureUid].activityUid].treasureState = "Accepted"; saveData(activityPath, ActivityData); GoldenUsers[TreasureData[content.treasureUid].activityUid] = { userUid: TreasureData[content.treasureUid].creator, activityUid: TreasureData[content.treasureUid].activityUid } saveData(goldenUsersPath, GoldenUsers); return { success: true, } } else { return treasureExists } }) fastify.post('/refuseTreasure', async (request, reply) => { let content = request.body; let treasureExists = checkManage(content, "treasureUid", TreasureData); if (treasureExists.success) { TreasureData[content.treasureUid].state = "Refused"; saveData(treasurePath, TreasureData); return { success: true, } } else { return treasureExists } }) fastify.post('/isGolden', async (request, reply) => { let content = request.body; let auth = checkAuthetification(content); if (auth.success) { for (activityUid in GoldenUsers) { if (GoldenUsers[activityUid].userUid === content.uid) { return { success: true, userUid: content.uid, activity: ActivityData[GoldenUsers[activityUid].activityUid] } } } return { success: false } } 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" } } } else { return { success: false, why: "Missing uid or token" } } } function check(content, input, data) { if (input in content) { if (content[input] in data) { return { success: true, } } else { return { success: false, why: "Unknow "+input } } } else { return { success: false, why: "Missing "+input } } } function checkManage(content, input, data) { let auth = checkAuthetification(content); if (auth.success) { if (AdminUsersUid.includes(content.uid)) { let exists = check(content, input, data) if (exists.success) { return { success: true } } else { return exists } } else { return { success: false, why: "Not Allowed" } } } else { return auth } } function initFs() { if (!fs.existsSync(prankPath)) { fs.writeFileSync(prankPath, "{}"); } if (!fs.existsSync(activityPath)) { fs.writeFileSync(activityPath, "{}"); } if (!fs.existsSync(treasurePath)) { fs.writeFileSync(treasurePath, "{}"); } if (!fs.existsSync(goldenUsersPath)) { fs.writeFileSync(goldenUsersPath, "{}"); } if (!fs.existsSync("static/images")){ fs.mkdirSync("static/images"); } } 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()