729 lines
24 KiB
JavaScript
729 lines
24 KiB
JavaScript
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].activity];
|
|
}
|
|
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 {
|
|
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, "activityUid", ActivityData)
|
|
if (treasureExists.success) {
|
|
if (treasureData[treasureUid].state != "Pending"
|
|
&& treasureData[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.prankUid;
|
|
}
|
|
} else {
|
|
return treasureExists;
|
|
}
|
|
}
|
|
let activityExists = check(content, "activityUid", ActivityData)
|
|
if (activityExists.success) {
|
|
if (ActivityData[content.activityUid].type == "treasure") {
|
|
let imageUid = makeid(128);
|
|
fs.writeFileSync("static/images/"+imageUid, content.image);
|
|
TreasureData[treasureUid] = {
|
|
date: new Date(),
|
|
creator: content.uid,
|
|
image: imageUid,
|
|
desc: content.desc,
|
|
activity: content.activityUid,
|
|
state: "Pending"
|
|
}
|
|
saveData(treasurePath, TreasureData);
|
|
} 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[GoldenUsers[activityUid].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, "{}");
|
|
}
|
|
}
|
|
|
|
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()
|