Ver código fonte

WIP: GC-1185: consider restriction when searching

Yuki Takei 7 anos atrás
pai
commit
1e996c289d
2 arquivos alterados com 73 adições e 14 exclusões
  1. 9 2
      src/server/routes/search.js
  2. 64 12
      src/server/util/search.js

+ 9 - 2
src/server/routes/search.js

@@ -31,6 +31,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} limit
    */
   api.search = async function(req, res) {
+    const user = req.user;
     const { q: keyword = null, tree = null, type = null } = req.query;
     let paginateOpts;
 
@@ -50,16 +51,22 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.error('Configuration of ELASTICSEARCH_URI is required.'));
     }
 
+    let userGroups = [];
+    if (user != null) {
+      const UserGroupRelation = crowi.model('UserGroupRelation');
+      userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
+    }
+
     const searchOpts = { ...paginateOpts, type };
 
     const result = {};
     try {
       let esResult;
       if (tree) {
-        esResult = await search.searchKeywordUnderPath(keyword, tree, searchOpts);
+        esResult = await search.searchKeywordUnderPath(keyword, tree, user, userGroups, searchOpts);
       }
       else {
-        esResult = await search.searchKeyword(keyword, searchOpts);
+        esResult = await search.searchKeyword(keyword, user, userGroups, searchOpts);
       }
 
       const findResult = await Page.findListByPageIds(esResult.data, { limit: 50 });

+ 64 - 12
src/server/util/search.js

@@ -342,9 +342,20 @@ SearchClient.prototype.addAllPages = async function() {
  *   data: [ pages ...],
  * }
  */
-SearchClient.prototype.search = function(query) {
+SearchClient.prototype.search = async function(query) {
   let self = this;
 
+  // for debug
+  if (process.env.NODE_ENV === 'development') {
+    const result = await this.client.indices.validateQuery({
+      explain: true,
+      body: {
+        query: query.body.query
+      },
+    });
+    logger.info('ES returns explanations: ', result.explanations);
+  }
+
   return new Promise(function(resolve, reject) {
     self.client
       .search(query)
@@ -527,6 +538,52 @@ SearchClient.prototype.appendCriteriaForPathFilter = function(query, path) {
   });
 };
 
+SearchClient.prototype.filterPagesByViewer = function(query, user, userGroups) {
+  query = this.initializeBoolQuery(query);
+
+  const Page = this.crowi.model('Page');
+  const { GRANT_PUBLIC, GRANT_RESTRICTED, GRANT_SPECIFIED, GRANT_OWNER, GRANT_USER_GROUP } = Page;
+
+  const grantConditions = [
+    // { term: { grant: null } },
+    { term: { grant: GRANT_PUBLIC } },
+  ];
+
+  // if (user == null) {
+  //   grantConditions.push(
+  //     { should: [
+  //       { term: { grant: GRANT_RESTRICTED } },
+  //       { term: { grant: GRANT_SPECIFIED } },
+  //       { term: { grant: GRANT_OWNER } },
+  //     ] }
+  //   );
+  // }
+  // else {
+  //   grantConditions.push(
+  //     { must: [
+  //       { should: [
+  //         { term: { grant: GRANT_RESTRICTED } },
+  //         { term: { grant: GRANT_SPECIFIED } },
+  //         { term: { grant: GRANT_OWNER } },
+  //       ] },
+  //       { term: { grantedUsers: user._id.toString() } }
+  //     ] },
+  //   );
+  // }
+
+  // if (userGroups != null && userGroups.length > 0) {
+  //   const userGroupIds = userGroups.map(group => group._id.toString() );
+  //   grantConditions.push(
+  //     { must: [
+  //       { term: { grant: GRANT_USER_GROUP } },
+  //       { terms: { grantedGroup: userGroupIds } }
+  //     ] },
+  //   );
+  // }
+
+  query.body.query.bool.filter.push({ bool: { should: grantConditions } });
+};
+
 SearchClient.prototype.filterPortalPages = function(query) {
   query = this.initializeBoolQuery(query);
 
@@ -580,26 +637,20 @@ SearchClient.prototype.appendFunctionScore = function(query) {
   };
 };
 
-SearchClient.prototype.searchKeyword = function(keyword, option) {
+SearchClient.prototype.searchKeyword = function(keyword, user, userGroups, option) {
   const from = option.offset || null;
   const size = option.limit || null;
   const type = option.type || null;
   const query = this.createSearchQuerySortedByScore();
   this.appendCriteriaForKeywordContains(query, keyword);
 
-  this.filterPagesByType(query, type);
+  // this.filterPagesByType(query, type);
+  this.filterPagesByViewer(query, user, userGroups);
 
   this.appendResultSize(query, from, size);
 
   this.appendFunctionScore(query);
 
-  const bool = query.body.query.function_score.query.bool;
-
-  debug('searching ...', keyword, type);
-  debug('filter', bool.filter);
-  debug('must', bool.must);
-  debug('must_not', bool.must_not);
-
   return this.search(query);
 };
 
@@ -607,7 +658,7 @@ SearchClient.prototype.searchByPath = function(keyword, prefix) {
   // TODO path 名だけから検索
 };
 
-SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option) {
+SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, user, userGroups, option) {
   const from = option.offset || null;
   const size = option.limit || null;
   const type = option.type || null;
@@ -615,7 +666,8 @@ SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option)
   this.appendCriteriaForKeywordContains(query, keyword);
   this.appendCriteriaForPathFilter(query, path);
 
-  this.filterPagesByType(query, type);
+  // this.filterPagesByType(query, type);
+  this.filterPagesByViewer(query, user, userGroups);
 
   this.appendResultSize(query, from, size);