middlewares.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. var debug = require('debug')('crowi:lib:middlewares');
  2. exports.loginChecker = function(crowi, app) {
  3. return function(req, res, next) {
  4. var User = crowi.model('User');
  5. var csrfKey = (req.session && req.session.id) || 'anon';
  6. if (req.csrfToken === null) {
  7. req.csrfToken = crowi.getTokens().create(csrfKey);
  8. }
  9. // session に user object が入ってる
  10. if (req.session.user && '_id' in req.session.user) {
  11. User.findById(req.session.user._id, function(err, userData) {
  12. if (err) {
  13. next();
  14. } else {
  15. req.user = req.session.user = userData;
  16. res.locals.user = req.user;
  17. next();
  18. }
  19. });
  20. } else {
  21. req.user = req.session.user = false;
  22. res.locals.user = req.user;
  23. next();
  24. }
  25. };
  26. };
  27. exports.csrfVerify = function(crowi, app) {
  28. return function(req, res, next) {
  29. var token = req.body._csrf || req.query._csrf || null;
  30. var csrfKey = (req.session && req.session.id) || 'anon';
  31. if (req.skipCsrfVerify) {
  32. return next();
  33. }
  34. if (crowi.getTokens().verify(csrfKey, token)) {
  35. return next();
  36. }
  37. return res.sendStatus(403);
  38. };
  39. };
  40. exports.swigFunctions = function(crowi, app) {
  41. return function(req, res, next) {
  42. require('../util/swigFunctions')(crowi, app, req, res.locals);
  43. next();
  44. };
  45. };
  46. exports.swigFilters = function(app, swig) {
  47. return function(req, res, next) {
  48. swig.setFilter('path2name', function(string) {
  49. var name = string.replace(/(\/)$/, '');
  50. if (name.match(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/)) { // /.../hoge/YYYY/MM/DD 形式のページ
  51. return name.replace(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/, '$1');
  52. }
  53. if (name.match(/.+\/([^/]+\/\d{4}\/\d{2})$/)) { // /.../hoge/YYYY/MM 形式のページ
  54. return name.replace(/.+\/([^/]+\/\d{4}\/\d{2})$/, '$1');
  55. }
  56. if (name.match(/.+\/([^/]+\/\d{4})$/)) { // /.../hoge/YYYY 形式のページ
  57. return name.replace(/.+\/([^/]+\/\d{4})$/, '$1');
  58. }
  59. return name.replace(/.+\/(.+)?$/, '$1'); // ページの末尾を拾う
  60. });
  61. swig.setFilter('normalizeDateInPath', function(path) {
  62. // warning for /(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/
  63. if (path.match(/20(\d{2})(\d{2})(\d{2})/g)) {
  64. return path.replace(/20(\d{2})(\d{2})(\d{2})/g, '20$1/$2/$3');
  65. }
  66. if (path.match(/20(\d{2})(\d{2})/g)) {
  67. return path.replace(/20(\d{2})(\d{2})/g, '20$1/$2');
  68. }
  69. if (path.match(/20(\d{2})_(\d{1,2})_(\d{1,2})/g)) {
  70. return path.replace(/20(\d{2})_(\d{1,2})_(\d{1,2})/g, '20$1/$2/$3');
  71. }
  72. if (path.match(/20(\d{2})_(\d{1,2})/g)) {
  73. return path.replace(/20(\d{2})_(\d{1,2})/g, '20$1/$2');
  74. }
  75. return path;
  76. });
  77. swig.setFilter('datetz', function(input, format) {
  78. // timezone
  79. var swigFilters = require('swig/lib/filters');
  80. return swigFilters.date(input, format, app.get('tzoffset'));
  81. });
  82. swig.setFilter('nl2br', function(string) {
  83. return string
  84. .replace(/\n/g, '<br>');
  85. });
  86. swig.setFilter('insertSpaceToEachSlashes', function(string) {
  87. if (string == '/') {
  88. return string;
  89. }
  90. return string.replace(/\//g, ' / ');
  91. });
  92. swig.setFilter('removeLastSlash', function(string) {
  93. if (string == '/') {
  94. return string;
  95. }
  96. return string.substr(0, string.length - 1);
  97. });
  98. swig.setFilter('presentation', function(string) {
  99. // 手抜き
  100. return string
  101. .replace(/[\n]+#/g, '\n\n\n#')
  102. .replace(/\s(https?.+(jpe?g|png|gif))\s/, '\n\n\n![]($1)\n\n\n');
  103. });
  104. swig.setFilter('picture', function(user) {
  105. if (!user) {
  106. return '';
  107. }
  108. user.fbId = user.userId; // migration
  109. if (user.image && user.image != '/images/userpicture.png') {
  110. return user.image;
  111. } else if (user.fbId) {
  112. return '//graph.facebook.com/' + user.fbId + '/picture?size=square';
  113. } else {
  114. return '/images/userpicture.png';
  115. }
  116. });
  117. next();
  118. };
  119. };
  120. exports.adminRequired = function() {
  121. return function(req, res, next) {
  122. if (req.user && '_id' in req.user) {
  123. if (req.user.admin) {
  124. next();
  125. return;
  126. }
  127. return res.redirect('/');
  128. }
  129. return res.redirect('/login');
  130. };
  131. };
  132. exports.loginRequired = function(crowi, app) {
  133. return function(req, res, next) {
  134. var User = crowi.model('User')
  135. if (req.user && '_id' in req.user) {
  136. if (req.user.status === User.STATUS_ACTIVE) {
  137. // Active の人だけ先に進める
  138. return next();
  139. } else if (req.user.status === User.STATUS_REGISTERED) {
  140. return res.redirect('/login/error/registered');
  141. } else if (req.user.status === User.STATUS_SUSPENDED) {
  142. return res.redirect('/login/error/suspended');
  143. } else if (req.user.status === User.STATUS_INVITED) {
  144. return res.redirect('/login/invited');
  145. }
  146. }
  147. req.session.jumpTo = req.originalUrl;
  148. return res.redirect('/login');
  149. };
  150. };
  151. exports.accessTokenParser = function(crowi, app) {
  152. return function(req, res, next) {
  153. var accessToken = req.query.access_token || req.body.access_token || null;
  154. if (!accessToken) {
  155. return next();
  156. }
  157. var User = crowi.model('User')
  158. User.findUserByApiToken(accessToken)
  159. .then(function(userData) {
  160. req.user = userData;
  161. req.skipCsrfVerify = true;
  162. next();
  163. }).catch(function(err) {
  164. next();
  165. });
  166. };
  167. };
  168. // this is for Installer
  169. exports.applicationNotInstalled = function() {
  170. return function(req, res, next) {
  171. var config = req.config;
  172. if (Object.keys(config.crowi).length !== 1) {
  173. req.flash('errorMessage', 'Application already installed.');
  174. return res.redirect('admin'); // admin以外はadminRequiredで'/'にリダイレクトされる
  175. }
  176. return next();
  177. };
  178. };
  179. exports.applicationInstalled = function() {
  180. return function(req, res, next) {
  181. var config = req.config;
  182. if (Object.keys(config.crowi).length === 1) { // app:url is set by process
  183. return res.redirect('/installer');
  184. }
  185. return next();
  186. };
  187. };
  188. exports.awsEnabled = function() {
  189. return function (req, res, next) {
  190. var config = req.config;
  191. if (config.crowi['aws:region'] !== '' && config.crowi['aws:bucket'] !== '' && config.crowi['aws:accessKeyId'] !== '' && config.crowi['aws:secretAccessKey'] !== '') {
  192. req.flash('globalError', 'AWS settings required to use this function. Please ask the administrator.');
  193. return res.redirect('/');
  194. }
  195. return next();
  196. };
  197. };