From 848db6a4e3f7091dc4a2e20dcae47f6669801e99 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Sat, 29 Oct 2016 23:21:32 +0200 Subject: todo: Introducing multi-user TODO! --- modules/$common.js | 1 + modules/todo/todo.html | 28 ++++++++-- modules/todo/todo.js | 121 ++++++++++++++++++++++++++++++++++++------ modules/todo/unknownuser.html | 87 ++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 19 deletions(-) create mode 100644 modules/todo/unknownuser.html (limited to 'modules') diff --git a/modules/$common.js b/modules/$common.js index fa29c50..c5c3635 100644 --- a/modules/$common.js +++ b/modules/$common.js @@ -24,6 +24,7 @@ module.exports={ var i; for(i=0;i "use strict"; -function fetch(method,url,data/*?*/,cb){ - if(!cb){ +function fetch(method,url,data/*?*/,creds/*?*/,cb){ + if(!creds){ cb=data; data=undefined; - if(!cb)throw new Error("No callback passed to fetch"); + creds=undefined; + } else if(!cb){ + cb=creds; + creds=undefined; } + if(!cb)throw new Error("No callback passed to fetch"); var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(ev){ if(xhr.readyState<4)return; cb(xhr.status,xhr.responseText); }; - xhr.open(method,url); + if(creds){ + xhr.open(method,url,true,creds[0],creds[1]); + } else { + xhr.open(method,url); + } xhr.send(data); } @@ -187,6 +195,12 @@ function doAddTask(ev){ }); } +function logoutReload(){ + fetch("GET","/todo/authfail",undefined,["baduser","badpass"],function(status,body){ + location.href=location.href; + }); +} + window.addEventListener("load",getlist); +
+ +

TODO



diff --git a/modules/todo/todo.js b/modules/todo/todo.js index 9e53c76..58322f2 100644 --- a/modules/todo/todo.js +++ b/modules/todo/todo.js @@ -3,7 +3,11 @@ // {"key":"tasks","value":[{"id":1,"subject":"kaas rep","repweeks":0,"date":"2016-10-25T07:46:54.493Z"},{"id":2,"subject":"kaas","repweeks":0,"date":"2016-10-27T07:46:54.493Z"}]} var cmn=require("../$common.js"), - persist=require("node-persist"); + persist=require("node-persist"), + bcrypt=require("bcrypt"), + basicAuth=require("basic-auth"); + +var bcryptHashRounds=10; var moddir=null; @@ -14,45 +18,132 @@ persist=persist.create({ }); persist.initSync(); -//tasks: [{id: Int,subject: String,repweeks: Int,date: Date}] +//tasks: {"user": [{id: Int,subject: String,repweeks: Int,date: Date}]} +//accounts: {"user": hash (String)} //repweeks==0 implies no repetition var nextid=persist.getItemSync("nextid"); -if(nextid==null)nextid=1; +if(nextid==null){ + nextid=1; + persist.setItemSync("nextid",nextid); +} var tasks=persist.getItemSync("tasks"); (function(){ - if(!tasks){ - tasks=[]; + if(tasks==null){ + tasks={}; persist.setItemSync("tasks",tasks); } else { - for(var task of tasks){ - task.date=new Date(task.date); - if(nextid<=task.id)nextid=task.id+1; + for(var user in tasks){ + for(var task of tasks[user]){ + task.date=new Date(task.date); + if(nextid<=task.id)nextid=task.id+1; + } } persist.setItemSync("nextid",nextid); } })(); +var accounts=persist.getItemSync("accounts"); +if(accounts==null){ + accounts={}; + persist.setItemSync("accounts",accounts); +} + + +function sendUnauth(res){ + res.set("WWW-Authenticate","Basic realm=Authorization required"); + return res.sendStatus(401); +} + +function unknownUserHandler(req,res,next){ + res.sendFile(moddir+"/unknownuser.html"); +} + +function authMiddleware(req,res,next){ + var user=basicAuth(req); + req.authuser=null; + if(!user||!user.name){ + sendUnauth(res); + return; + } + req.authuser=user.name; + if(accounts[req.authuser]){ + bcrypt.compare(user.pass,accounts[req.authuser],function(err,ok){ + if(ok)next(); + else sendUnauth(res); + }); + } else { + unknownUserHandler(req,res,next); + } +} + +function asciiValid(str){ + var i,c; + for(i=0;i=127)return false; + } + return true; +} module.exports=function(app,io,_moddir){ moddir=_moddir; - app.all("/todo/task",cmn.authgen()); + + //first the endpoints that need to bypass authMiddleware + app.get("/todo/authfail",function(req,res){ + sendUnauth(res); + }); + app.post("/todo/createuser",function(req,res){ + var user=basicAuth(req); + if(!user||!user.name){ + res.status(400).send("No credentials sent"); + return; + } + if(user.name.length<3||user.name.length>32||!asciiValid(user.name)){ + res.status(400).send("Invalid username"); + return; + } + if(user.pass.length<3||user.pass.length>32||!asciiValid(user.pass)){ + res.status(400).send("Invalid password"); + return; + } + if(accounts[user.name]){ + res.status(400).send("User already exists"); + return; + } + bcrypt.hash(user.pass,bcryptHashRounds,function(err,hash){ + if(!hash){ + res.status(500).send("Something went wrong..."); + console.log(err); + return; + } + accounts[user.name]=hash; + tasks[user.name]=[]; + persist.setItemSync("accounts",accounts); + persist.setItemSync("tasks",tasks); + res.status(200).end(); + }); + }); + + app.all(["/todo","/todo/*"],authMiddleware); //for all the other endpoints + app.get("/todo",function(req,res){ res.sendFile(moddir+"/todo.html"); }); app.get("/todo/list",function(req,res){ - res.json(tasks); + res.json(tasks[req.authuser]); }); app.delete("/todo/task",function(req,res){ var id=+req.body; var i; var fail=false; - if(id<0||~~id!=id||isNaN(id)){ + var usertasks=tasks[req.authuser]; + if(id<0||~~id!=id||isNaN(id)||!usertasks){ fail=true; } else { - for(i=0;i + + + +TODO: Unknown user + + + + +

TODO: Unknown user

+

The user you entered is not known in the system. You can use the below form to create a new user. +Be aware: this system is not secure.

+Username:
+Password:
+ +

+

You can also if you just can't type.

+ + -- cgit v1.2.3-70-g09d2