middlewares.js 6.9 KB

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