comparison rlgwebd.js @ 39:e8ac0e3d2614

RLG-Web: separate logging in and starting a game. The user now logs in with a username and password, receiving a token which is then used for any actions requiring authentication. Starting a game is one such action. Games use a different set of id keys. This allows users to supply their passwords once and then play any number of successive games. Also, newly registered users do not need to supply their passwords again.
author John "Elwin" Edwards <elwin@sdf.org>
date Thu, 07 Jun 2012 15:43:06 -0700
parents 353be34de307
children f7116eb3f791
comparison
equal deleted inserted replaced
38:b06a14876645 39:e8ac0e3d2614
19 var dropToUID = 501; 19 var dropToUID = 501;
20 var dropToGID = 501; 20 var dropToGID = 501;
21 var serveStaticRoot = "/var/www/"; // inside the chroot 21 var serveStaticRoot = "/var/www/"; // inside the chroot
22 var playtimeout = 3600000; // Idle time before games are autosaved, in ms 22 var playtimeout = 3600000; // Idle time before games are autosaved, in ms
23 23
24 /* Global state */ 24 /* Data on the games available. */
25 var sessions = {};
26 var allowlogin = true;
27
28 var games = { 25 var games = {
29 "rogue3": { 26 "rogue3": {
30 "name": "Rogue V3", 27 "name": "Rogue V3",
31 "uname": "rogue3", 28 "uname": "rogue3",
32 "path": "/bin/rogue3" 29 "path": "/bin/rogue3"
46 "uname": "srogue", 43 "uname": "srogue",
47 "path": "/bin/srogue" 44 "path": "/bin/srogue"
48 } 45 }
49 }; 46 };
50 47
48 /* Global state */
49 var logins = {};
50 var sessions = {};
51 var allowlogin = true;
52
51 /* Constructor for TermSessions. Note that it opens the terminal and 53 /* Constructor for TermSessions. Note that it opens the terminal and
52 * adds itself to the sessions dict. It currently assumes the user has 54 * adds itself to the sessions dict. It currently assumes the user has
53 * been authenticated. 55 * been authenticated.
54 */ 56 */
55 function TermSession(game, user, files, dims) { 57 function TermSession(game, user, files, dims) {
62 return null; 64 return null;
63 } 65 }
64 this.player = user; 66 this.player = user;
65 /* This order seems to best avoid race conditions... */ 67 /* This order seems to best avoid race conditions... */
66 this.alive = false; 68 this.alive = false;
67 this.sessid = randkey(); 69 this.sessid = randkey(2);
68 while (this.sessid in sessions) { 70 while (this.sessid in sessions) {
69 this.sessid = randkey(); 71 this.sessid = randkey(2);
70 } 72 }
71 /* Grab a spot in the sessions table. */ 73 /* Grab a spot in the sessions table. */
72 sessions[this.sessid] = this; 74 sessions[this.sessid] = this;
73 /* State for messaging. */ 75 /* State for messaging. */
74 this.nsend = 0; 76 this.nsend = 0;
223 sd = dd.toISOString(); 225 sd = dd.toISOString();
224 sd = sd.slice(0, sd.indexOf(".")); 226 sd = sd.slice(0, sd.indexOf("."));
225 return sd.replace("T", "."); 227 return sd.replace("T", ".");
226 } 228 }
227 229
228 function randkey() { 230 function randkey(words) {
229 rnum = Math.floor(Math.random() * 65536 * 65536); 231 if (!words || !(words > 0))
230 hexstr = rnum.toString(16); 232 words = 1;
231 while (hexstr.length < 8) 233 function rand32() {
232 hexstr = "0" + hexstr; 234 rnum = Math.floor(Math.random() * 65536 * 65536);
233 return hexstr; 235 hexstr = rnum.toString(16);
236 while (hexstr.length < 8)
237 hexstr = "0" + hexstr;
238 return hexstr;
239 }
240 var key = "";
241 for (var i = 0; i < words; i++)
242 key += rand32();
243 return key;
234 } 244 }
235 245
236 function tslog() { 246 function tslog() {
237 arguments[0] = new Date().toISOString() + ": " + String(arguments[0]); 247 arguments[0] = new Date().toISOString() + ": " + String(arguments[0]);
238 console.log.apply(console, arguments); 248 console.log.apply(console, arguments);
293 function login(req, res, formdata) { 303 function login(req, res, formdata) {
294 if (!allowlogin) { 304 if (!allowlogin) {
295 sendError(res, 6, null); 305 sendError(res, 6, null);
296 return; 306 return;
297 } 307 }
298 if (!("game" in formdata)) { 308 if (!("name" in formdata)) {
299 sendError(res, 2, "No game specified.");
300 return;
301 }
302 else if (!("name" in formdata)) {
303 sendError(res, 2, "Username not given."); 309 sendError(res, 2, "Username not given.");
304 return; 310 return;
305 } 311 }
306 else if (!("pw" in formdata)) { 312 else if (!("pw" in formdata)) {
307 sendError(res, 2, "Password not given."); 313 sendError(res, 2, "Password not given.");
308 return; 314 return;
309 } 315 }
310 var username = formdata["name"]; 316 var username = String(formdata["name"]);
311 var password = formdata["pw"]; 317 var password = String(formdata["pw"]);
318 function checkit(code, signal) {
319 /* Checks the exit status, see sqlickrypt.c for details. */
320 if (code != 0) {
321 sendError(res, 3);
322 if (code == 1)
323 tslog("Password check failed for user %s", username);
324 else if (code == 2)
325 tslog("Attempted login by nonexistent user %s", username);
326 else
327 tslog("Login failed: sqlickrypt error %d", code);
328 return;
329 }
330 var lkey = randkey(2);
331 while (lkey in logins)
332 lkey = randkey(2);
333 logins[lkey] = {"name": username, "ts": new Date()};
334 res.writeHead(200, {'Content-Type': 'application/json'});
335 var reply = {"t": "l", "k": lkey, "u": username};
336 res.write(JSON.stringify(reply));
337 res.end();
338 tslog("%s has logged in (key %s)", username, lkey);
339 return;
340 }
341 /* Launch the sqlickrypt utility to check the password. */
342 var pwchecker = child_process.spawn("/bin/sqlickrypt", ["check"]);
343 pwchecker.on("exit", checkit);
344 pwchecker.stdin.end(username + '\n' + password + '\n', "utf8");
345 return;
346 }
347
348 function startgame(req, res, formdata) {
349 if (!allowlogin) {
350 sendError(res, 6, null);
351 return;
352 }
353 if (!("key" in formdata)) {
354 sendError(res, 2, "No key given.");
355 return;
356 }
357 else if (!("game" in formdata)) {
358 sendError(res, 2, "No game specified.");
359 return;
360 }
361 var lkey = String(formdata["key"]);
362 if (!(lkey in logins)) {
363 sendError(res, 1, null);
364 return;
365 }
366 else {
367 logins[lkey].ts = new Date();
368 }
369 var username = logins[lkey].name;
312 var gname = formdata["game"]; 370 var gname = formdata["game"];
313 var dims = [formdata["h"], formdata["w"]]; 371 var dims = [formdata["h"], formdata["w"]];
314 if (!(gname in games)) { 372 if (!(gname in games)) {
315 sendError(res, 2, "No such game: " + gname); 373 sendError(res, 2, "No such game: " + gname);
316 tslog("Request for nonexistant game \"%s\"", gname); 374 tslog("Request for nonexistant game \"%s\"", gname);
317 return; 375 return;
318 } 376 }
377 // check for an existing game
319 var progressdir = "/dgldir/inprogress-" + games[gname].uname; 378 var progressdir = "/dgldir/inprogress-" + games[gname].uname;
320 379 fs.readdir(progressdir, function(err, files) {
321 // This sets up the game once starting is approved. 380 if (!err) {
322 function startgame() {