Advertisement
Guest User

Tahtiputki: sync-3.0/src/web/account.js

a guest
Oct 8th, 2016
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.15 KB | None | 0 0
  1. /**
  2. * web/account.js - Webserver details for account management
  3. *
  4. * @author Calvin Montgomery <cyzon@cyzon.us>
  5. */
  6.  
  7. var webserver = require("./webserver");
  8. var sendPug = require("./pug").sendPug;
  9. var Logger = require("../logger");
  10. var db = require("../database");
  11. var $util = require("../utilities");
  12. var Config = require("../config");
  13. var Server = require("../server");
  14. var session = require("../session");
  15. var csrf = require("./csrf");
  16.  
  17. /**
  18. * Handles a GET request for /account/edit
  19. */
  20. function handleAccountEditPage(req, res) {
  21. if (webserver.redirectHttps(req, res)) {
  22. return;
  23. }
  24.  
  25. sendPug(res, "account-edit", {});
  26. }
  27.  
  28. /**
  29. * Handles a POST request to edit a user"s account
  30. */
  31. function handleAccountEdit(req, res) {
  32. csrf.verify(req);
  33.  
  34. var action = req.body.action;
  35. switch(action) {
  36. case "change_password":
  37. handleChangePassword(req, res);
  38. break;
  39. case "change_email":
  40. handleChangeEmail(req, res);
  41. break;
  42. default:
  43. res.send(400);
  44. break;
  45. }
  46. }
  47.  
  48. /**
  49. * Handles a request to change the user"s password
  50. */
  51. function handleChangePassword(req, res) {
  52. var name = req.body.name;
  53. var oldpassword = req.body.oldpassword;
  54. var newpassword = req.body.newpassword;
  55.  
  56. if (typeof name !== "string" ||
  57. typeof oldpassword !== "string" ||
  58. typeof newpassword !== "string") {
  59. res.send(400);
  60. return;
  61. }
  62.  
  63. if (newpassword.length === 0) {
  64. sendPug(res, "account-edit", {
  65. errorMessage: "Salasanasi ei voi olla tyhjä"
  66. });
  67. return;
  68. }
  69.  
  70. if (!req.user) {
  71. sendPug(res, "account-edit", {
  72. errorMessage: "Muistathan kirjautua sisään vaihtaaksesi salasanasi"
  73. });
  74. return;
  75. }
  76.  
  77. newpassword = newpassword.substring(0, 100);
  78.  
  79. db.users.verifyLogin(name, oldpassword, function (err, user) {
  80. if (err) {
  81. sendPug(res, "account-edit", {
  82. errorMessage: err
  83. });
  84. return;
  85. }
  86.  
  87. db.users.setPassword(name, newpassword, function (err, dbres) {
  88. if (err) {
  89. sendPug(res, "account-edit", {
  90. errorMessage: err
  91. });
  92. return;
  93. }
  94.  
  95. Logger.eventlog.log("[account] " + req.realIP +
  96. " changed password for " + name);
  97.  
  98. db.users.getUser(name, function (err, user) {
  99. if (err) {
  100. return sendPug(res, "account-edit", {
  101. errorMessage: err
  102. });
  103. }
  104.  
  105. res.user = user;
  106. var expiration = new Date(parseInt(req.signedCookies.auth.split(":")[1]));
  107. session.genSession(user, expiration, function (err, auth) {
  108. if (err) {
  109. return sendPug(res, "account-edit", {
  110. errorMessage: err
  111. });
  112. }
  113.  
  114. if (req.hostname.indexOf(Config.get("http.root-domain")) >= 0) {
  115. res.cookie("auth", auth, {
  116. domain: Config.get("http.root-domain-dotted"),
  117. expires: expiration,
  118. httpOnly: true,
  119. signed: true
  120. });
  121. } else {
  122. res.cookie("auth", auth, {
  123. expires: expiration,
  124. httpOnly: true,
  125. signed: true
  126. });
  127. }
  128.  
  129. sendPug(res, "account-edit", {
  130. successMessage: "Password changed."
  131. });
  132. });
  133. });
  134. });
  135. });
  136. }
  137.  
  138. /**
  139. * Handles a request to change the user"s email
  140. */
  141. function handleChangeEmail(req, res) {
  142. var name = req.body.name;
  143. var password = req.body.password;
  144. var email = req.body.email;
  145.  
  146. if (typeof name !== "string" ||
  147. typeof password !== "string" ||
  148. typeof email !== "string") {
  149. res.send(400);
  150. return;
  151. }
  152.  
  153. if (!$util.isValidEmail(email) && email !== "") {
  154. sendPug(res, "account-edit", {
  155. errorMessage: "Virheellinen sähköpostiosoite!"
  156. });
  157. return;
  158. }
  159.  
  160. db.users.verifyLogin(name, password, function (err, user) {
  161. if (err) {
  162. sendPug(res, "account-edit", {
  163. errorMessage: err
  164. });
  165. return;
  166. }
  167.  
  168. db.users.setEmail(name, email, function (err, dbres) {
  169. if (err) {
  170. sendPug(res, "account-edit", {
  171. errorMessage: err
  172. });
  173. return;
  174. }
  175. Logger.eventlog.log("[account] " + req.realIP +
  176. " changed email for " + name +
  177. " to " + email);
  178. sendPug(res, "account-edit", {
  179. successMessage: "Sähköpostiosoitteesi on vaihdettu."
  180. });
  181. });
  182. });
  183. }
  184.  
  185. /**
  186. * Handles a GET request for /account/channels
  187. */
  188. function handleAccountChannelPage(req, res) {
  189. if (webserver.redirectHttps(req, res)) {
  190. return;
  191. }
  192.  
  193. if (!req.user) {
  194. return sendPug(res, "account-channels", {
  195. channels: []
  196. });
  197. }
  198.  
  199. db.channels.listUserChannels(req.user.name, function (err, channels) {
  200. sendPug(res, "account-channels", {
  201. channels: channels
  202. });
  203. });
  204. }
  205.  
  206. /**
  207. * Handles a POST request to modify a user"s channels
  208. */
  209. function handleAccountChannel(req, res) {
  210. csrf.verify(req);
  211.  
  212. var action = req.body.action;
  213. switch(action) {
  214. case "new_channel":
  215. handleNewChannel(req, res);
  216. break;
  217. case "delete_channel":
  218. handleDeleteChannel(req, res);
  219. break;
  220. default:
  221. res.send(400);
  222. break;
  223. }
  224. }
  225.  
  226. /**
  227. * Handles a request to register a new channel
  228. */
  229. function handleNewChannel(req, res) {
  230.  
  231. var name = req.body.name;
  232. if (typeof name !== "string") {
  233. res.send(400);
  234. return;
  235. }
  236.  
  237. if (!req.user) {
  238. return sendPug(res, "account-channels", {
  239. channels: []
  240. });
  241. }
  242.  
  243. db.channels.listUserChannels(req.user.name, function (err, channels) {
  244. if (err) {
  245. sendPug(res, "account-channels", {
  246. channels: [],
  247. newChannelError: err
  248. });
  249. return;
  250. }
  251.  
  252. if (name.match(Config.get("reserved-names.channels"))) {
  253. sendPug(res, "account-channels", {
  254. channels: channels,
  255. newChannelError: "That channel name is reserved"
  256. });
  257. return;
  258. }
  259.  
  260. if (channels.length >= Config.get("max-channels-per-user") &&
  261. req.user.global_rank < 255) {
  262. sendPug(res, "account-channels", {
  263. channels: channels,
  264. newChannelError: "You are not allowed to register more than " +
  265. Config.get("max-channels-per-user") + " channels."
  266. });
  267. return;
  268. }
  269.  
  270. db.channels.register(name, req.user.name, function (err, channel) {
  271. if (!err) {
  272. Logger.eventlog.log("[channel] " + req.user.name + "@" +
  273. req.realIP +
  274. " registered channel " + name);
  275. var sv = Server.getServer();
  276. if (sv.isChannelLoaded(name)) {
  277. var chan = sv.getChannel(name);
  278. var users = Array.prototype.slice.call(chan.users);
  279. users.forEach(function (u) {
  280. u.kick("Channel reloading");
  281. });
  282.  
  283. if (!chan.dead) {
  284. chan.emit("empty");
  285. }
  286. }
  287. channels.push({
  288. name: name
  289. });
  290. }
  291.  
  292.  
  293. sendPug(res, "account-channels", {
  294. channels: channels,
  295. newChannelError: err ? err : undefined
  296. });
  297. });
  298. });
  299. }
  300.  
  301. /**
  302. * Handles a request to delete a new channel
  303. */
  304. function handleDeleteChannel(req, res) {
  305. var name = req.body.name;
  306. if (typeof name !== "string") {
  307. res.send(400);
  308. return;
  309. }
  310.  
  311. if (!req.user) {
  312. return sendPug(res, "account-channels", {
  313. channels: [],
  314. });
  315. }
  316.  
  317.  
  318. db.channels.lookup(name, function (err, channel) {
  319. if (err) {
  320. sendPug(res, "account-channels", {
  321. channels: [],
  322. deleteChannelError: err
  323. });
  324. return;
  325. }
  326.  
  327. if (channel.owner !== req.user.name && req.user.global_rank < 255) {
  328. db.channels.listUserChannels(req.user.name, function (err2, channels) {
  329. sendPug(res, "account-channels", {
  330. channels: err2 ? [] : channels,
  331. deleteChannelError: "You do not have permission to delete this channel"
  332. });
  333. });
  334. return;
  335. }
  336.  
  337. db.channels.drop(name, function (err) {
  338. if (!err) {
  339. Logger.eventlog.log("[channel] " + req.user.name + "@" +
  340. req.realIP + " deleted channel " +
  341. name);
  342. }
  343. var sv = Server.getServer();
  344. if (sv.isChannelLoaded(name)) {
  345. var chan = sv.getChannel(name);
  346. chan.clearFlag(require("../flags").C_REGISTERED);
  347. var users = Array.prototype.slice.call(chan.users);
  348. users.forEach(function (u) {
  349. u.kick("Channel reloading");
  350. });
  351.  
  352. if (!chan.dead) {
  353. chan.emit("empty");
  354. }
  355. }
  356. db.channels.listUserChannels(req.user.name, function (err2, channels) {
  357. sendPug(res, "account-channels", {
  358. channels: err2 ? [] : channels,
  359. deleteChannelError: err ? err : undefined
  360. });
  361. });
  362. });
  363. });
  364. }
  365.  
  366. /**
  367. * Handles a GET request for /account/profile
  368. */
  369. function handleAccountProfilePage(req, res) {
  370. if (webserver.redirectHttps(req, res)) {
  371. return;
  372. }
  373.  
  374. if (!req.user) {
  375. return sendPug(res, "account-profile", {
  376. profileImage: "",
  377. profileText: ""
  378. });
  379. }
  380.  
  381. db.users.getProfile(req.user.name, function (err, profile) {
  382. if (err) {
  383. sendPug(res, "account-profile", {
  384. profileError: err,
  385. profileImage: "",
  386. profileText: ""
  387. });
  388. return;
  389. }
  390.  
  391. sendPug(res, "account-profile", {
  392. profileImage: profile.image,
  393. profileText: profile.text,
  394. profileError: false
  395. });
  396. });
  397. }
  398.  
  399. /**
  400. * Handles a POST request to edit a profile
  401. */
  402. function handleAccountProfile(req, res) {
  403. csrf.verify(req);
  404.  
  405. if (!req.user) {
  406. return sendPug(res, "account-profile", {
  407. profileImage: "",
  408. profileText: "",
  409. profileError: "You must be logged in to edit your profile",
  410. });
  411. }
  412.  
  413. var image = req.body.image;
  414. var text = req.body.text;
  415.  
  416. db.users.setProfile(req.user.name, { image: image, text: text }, function (err) {
  417. if (err) {
  418. sendPug(res, "account-profile", {
  419. profileImage: "",
  420. profileText: "",
  421. profileError: err
  422. });
  423. return;
  424. }
  425.  
  426. sendPug(res, "account-profile", {
  427. profileImage: image,
  428. profileText: text,
  429. profileError: false
  430. });
  431. });
  432. }
  433.  
  434. /**
  435. * Handles a GET request for /account/passwordreset
  436. */
  437. function handlePasswordResetPage(req, res) {
  438. if (webserver.redirectHttps(req, res)) {
  439. return;
  440. }
  441.  
  442. sendPug(res, "account-passwordreset", {
  443. reset: false,
  444. resetEmail: "",
  445. resetErr: false
  446. });
  447. }
  448.  
  449. /**
  450. * Handles a POST request to reset a user's password
  451. */
  452. function handlePasswordReset(req, res) {
  453. csrf.verify(req);
  454.  
  455. var name = req.body.name,
  456. email = req.body.email;
  457.  
  458. if (typeof name !== "string" || typeof email !== "string") {
  459. res.send(400);
  460. return;
  461. }
  462.  
  463. if (!$util.isValidUserName(name)) {
  464. sendPug(res, "account-passwordreset", {
  465. reset: false,
  466. resetEmail: "",
  467. resetErr: "Invalid username '" + name + "'"
  468. });
  469. return;
  470. }
  471.  
  472. db.users.getEmail(name, function (err, actualEmail) {
  473. if (err) {
  474. sendPug(res, "account-passwordreset", {
  475. reset: false,
  476. resetEmail: "",
  477. resetErr: err
  478. });
  479. return;
  480. }
  481.  
  482. if (actualEmail !== email.trim()) {
  483. sendPug(res, "account-passwordreset", {
  484. reset: false,
  485. resetEmail: "",
  486. resetErr: "Provided email does not match the email address on record for " + name
  487. });
  488. return;
  489. } else if (actualEmail === "") {
  490. sendPug(res, "account-passwordreset", {
  491. reset: false,
  492. resetEmail: "",
  493. resetErr: name + " doesn't have an email address on record. Please contact an " +
  494. "administrator to manually reset your password."
  495. });
  496. return;
  497. }
  498.  
  499. var hash = $util.sha1($util.randomSalt(64));
  500. // 24-hour expiration
  501. var expire = Date.now() + 86400000;
  502. var ip = req.realIP;
  503.  
  504. db.addPasswordReset({
  505. ip: ip,
  506. name: name,
  507. email: email,
  508. hash: hash,
  509. expire: expire
  510. }, function (err, dbres) {
  511. if (err) {
  512. sendPug(res, "account-passwordreset", {
  513. reset: false,
  514. resetEmail: "",
  515. resetErr: err
  516. });
  517. return;
  518. }
  519.  
  520. Logger.eventlog.log("[account] " + ip + " requested password recovery for " +
  521. name + " <" + email + ">");
  522.  
  523. if (!Config.get("mail.enabled")) {
  524. sendPug(res, "account-passwordreset", {
  525. reset: false,
  526. resetEmail: email,
  527. resetErr: "Tämä palvelin ei valitettavasti tue sähköpostitoimintoa. Ota yhteyttä sen ylläpitäjään."
  528. });
  529. return;
  530. }
  531.  
  532. var msg = "A password reset request was issued for your " +
  533. "account `"+ name + "` on " + Config.get("http.domain") +
  534. ". This request is valid for 24 hours. If you did "+
  535. "not initiate this, there is no need to take action."+
  536. " To reset your password, copy and paste the " +
  537. "following link into your browser: " +
  538. Config.get("http.domain") + "/account/passwordrecover/"+hash;
  539.  
  540. var mail = {
  541. from: Config.get("mail.from-name") + " <" + Config.get("mail.from-address") + ">",
  542. to: email,
  543. subject: "Password reset request",
  544. text: msg
  545. };
  546.  
  547. Config.get("mail.nodemailer").sendMail(mail, function (err, response) {
  548. if (err) {
  549. Logger.errlog.log("mail fail: " + err);
  550. sendPug(res, "account-passwordreset", {
  551. reset: false,
  552. resetEmail: email,
  553. resetErr: "Sending reset email failed. Please contact an " +
  554. "administrator for assistance."
  555. });
  556. } else {
  557. sendPug(res, "account-passwordreset", {
  558. reset: true,
  559. resetEmail: email,
  560. resetErr: false
  561. });
  562. }
  563. });
  564. });
  565. });
  566. }
  567.  
  568. /**
  569. * Handles a request for /account/passwordrecover/<hash>
  570. */
  571. function handlePasswordRecover(req, res) {
  572. var hash = req.params.hash;
  573. if (typeof hash !== "string") {
  574. res.send(400);
  575. return;
  576. }
  577.  
  578. var ip = req.realIP;
  579.  
  580. db.lookupPasswordReset(hash, function (err, row) {
  581. if (err) {
  582. sendPug(res, "account-passwordrecover", {
  583. recovered: false,
  584. recoverErr: err
  585. });
  586. return;
  587. }
  588.  
  589. if (Date.now() >= row.expire) {
  590. sendPug(res, "account-passwordrecover", {
  591. recovered: false,
  592. recoverErr: "This password recovery link has expired. Password " +
  593. "recovery links are valid only for 24 hours after " +
  594. "submission."
  595. });
  596. return;
  597. }
  598.  
  599. var newpw = "";
  600. const avail = "abcdefgihkmnpqrstuvwxyz0123456789";
  601. for (var i = 0; i < 10; i++) {
  602. newpw += avail[Math.floor(Math.random() * avail.length)];
  603. }
  604. db.users.setPassword(row.name, newpw, function (err) {
  605. if (err) {
  606. sendPug(res, "account-passwordrecover", {
  607. recovered: false,
  608. recoverErr: "Database error. Please contact an administrator if " +
  609. "this persists."
  610.  
  611. });
  612. return;
  613. }
  614.  
  615. db.deletePasswordReset(hash);
  616. Logger.eventlog.log("[account] " + ip + " recovered password for " + row.name);
  617.  
  618. sendPug(res, "account-passwordrecover", {
  619. recovered: true,
  620. recoverPw: newpw
  621. });
  622. });
  623. });
  624. }
  625.  
  626. module.exports = {
  627. /**
  628. * Initialize the module
  629. */
  630. init: function (app) {
  631. app.get("/account/edit", handleAccountEditPage);
  632. app.post("/account/edit", handleAccountEdit);
  633. app.get("/account/channels", handleAccountChannelPage);
  634. app.post("/account/channels", handleAccountChannel);
  635. app.get("/account/profile", handleAccountProfilePage);
  636. app.post("/account/profile", handleAccountProfile);
  637. app.get("/account/passwordreset", handlePasswordResetPage);
  638. app.post("/account/passwordreset", handlePasswordReset);
  639. app.get("/account/passwordrecover/:hash", handlePasswordRecover);
  640. app.get("/account", function (req, res) {
  641. res.redirect("/login");
  642. });
  643. }
  644. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement