Browse Source

Merge pull request #728 from weseek/imprv/refactor-acl-list-policy

Imprv/refactor acl list policy
Yuki Takei 7 years ago
parent
commit
63179419b2

+ 6 - 1
resource/locales/en-US/translation.json

@@ -314,7 +314,12 @@
     "restrict_emails": "You can restrict registerable e-mail address.",
 		"for_instance": " For instance, if you use growi within a company, you can write ",
 		"only_those": " Only those whose e-mail address including the company address can register.",
-		"insert_single": "Please insert single e-mail address per line.",
+    "insert_single": "Please insert single e-mail address per line.",
+    "page_listing_1": "Page listing<br>restricted by 'Just Me'",
+    "page_listing_1_desc": "Show pages that are restricted by 'Just Me' option when listing",
+    "page_listing_2": "Page listing<br>restricted by User Group",
+    "page_listing_2_desc": "Show pages that are restricted by User Group when listing",
+
 		"Authentication mechanism settings": "Authentication mechanism settings",
     "note": "Note",
     "require_server_restart_change_auth": "Restarting the server is required if you switch the auth mechanism.",

+ 5 - 0
resource/locales/ja/translation.json

@@ -332,6 +332,11 @@
     "for_instance":"例えば、",
     "only_those":"と記載することで、そのドメインのメールアドレスを持っている人のみ登録可能になります。",
     "insert_single":"1行に1メールアドレス入力してください。",
+    "page_listing_1": "ページのリスト表示<br>'自分のみ'に閲覧制限しているページ",
+    "page_listing_1_desc": "ページのリスト表示時、'自分のみ'に閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+    "page_listing_2": "ページのリスト表示<br>特定グループに閲覧制限しているページ",
+    "page_listing_2_desc": "ページのリスト表示時、特定グループにのみ閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+
     "Authentication mechanism settings":"認証機構設定",
     "note": "メモ",
     "require_server_restart_change_auth": "認証機構の変更後はサーバーを再起動してください。",

+ 7 - 6
src/server/form/admin/securityGeneral.js

@@ -1,15 +1,16 @@
 'use strict';
 
-var form = require('express-form')
-  , field = form.field
-  , stringToArray = require('../../util/formUtil').stringToArrayFilter
-  , normalizeCRLF = require('../../util/formUtil').normalizeCRLFFilter
-  ;
+const form = require('express-form')
+const field = form.field;
+const stringToArray = require('../../util/formUtil').stringToArrayFilter;
+const normalizeCRLF = require('../../util/formUtil').normalizeCRLFFilter;
 
 module.exports = form(
   field('settingForm[security:basicName]'),
   field('settingForm[security:basicSecret]'),
   field('settingForm[security:restrictGuestMode]').required(),
   field('settingForm[security:registrationMode]').required(),
-  field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray)
+  field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray),
+  field('settingForm[security:list-policy:hideRestrictedByOwner]').trim().toBooleanStrict(),
+  field('settingForm[security:list-policy:hideRestrictedByGroup]').trim().toBooleanStrict(),
 );

+ 13 - 0
src/server/models/config.js

@@ -60,6 +60,9 @@ module.exports = function(crowi) {
       'security:registrationMode'      : 'Open',
       'security:registrationWhiteList' : [],
 
+      'security:list-policy:hideRestrictedByOwner' : false,
+      'security:list-policy:hideRestrictedByGroup' : false,
+
       'security:isEnabledPassport' : false,
       'security:passport-ldap:isEnabled' : false,
       'security:passport-ldap:serverUrl' : undefined,
@@ -377,6 +380,16 @@ module.exports = function(crowi) {
     return SECURITY_RESTRICT_GUEST_MODE_READONLY === config.crowi['security:restrictGuestMode'];
   };
 
+  configSchema.statics.hidePagesRestrictedByOwnerInList = function(config) {
+    const key = 'security:list-policy:hideRestrictedByOwner';
+    return getValueForCrowiNS(config, key);
+  };
+
+  configSchema.statics.hidePagesRestrictedByGroupInList = function(config) {
+    const key = 'security:list-policy:hideRestrictedByGroup';
+    return getValueForCrowiNS(config, key);
+  };
+
   configSchema.statics.isEnabledPlugins = function(config) {
     const key = 'plugin:isEnabledPlugins';
     return getValueForCrowiNS(config, key);

+ 51 - 9
src/server/models/page.js

@@ -159,7 +159,14 @@ class PageQueryBuilder {
       {grant: GRANT_PUBLIC},
     ];
 
-    if (user != null) {
+    if (user == null) {
+      grantConditions.push(
+        {grant: GRANT_RESTRICTED},
+        {grant: GRANT_SPECIFIED},
+        {grant: GRANT_OWNER},
+      );
+    }
+    else {
       grantConditions.push(
         {grant: GRANT_RESTRICTED, grantedUsers: user._id},
         {grant: GRANT_SPECIFIED, grantedUsers: user._id},
@@ -167,7 +174,12 @@ class PageQueryBuilder {
       );
     }
 
-    if (userGroups != null) {
+    if (userGroups == null) {
+      grantConditions.push(
+        {grant: GRANT_USER_GROUP},
+      );
+    }
+    else {
       grantConditions.push(
         {grant: GRANT_USER_GROUP, grantedGroup: { $in: userGroups }},
       );
@@ -648,6 +660,8 @@ module.exports = function(crowi) {
   async function findListFromBuilderAndViewer(builder, user, option) {
     validateCrowi();
 
+    const User = crowi.model('User');
+
     const opt = Object.assign({sort: 'updatedAt', desc: -1}, option);
     const sortOpt = {};
     sortOpt[opt.sort] = opt.desc;
@@ -662,16 +676,10 @@ module.exports = function(crowi) {
     }
 
     // add grant conditions
-    let userGroups = null;
-    if (user != null) {
-      const UserGroupRelation = crowi.model('UserGroupRelation');
-      userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
-    }
-    builder.addConditionToFilteringByViewer(user, userGroups);
+    await addConditionToFilteringByViewerForList(builder, user);
 
     builder.addConditionToPagenate(opt.offset, opt.limit, sortOpt);
 
-    const User = crowi.model('User');
     const totalCount = await builder.query.exec('count');
     const q = builder.query
       .populate({
@@ -685,6 +693,40 @@ module.exports = function(crowi) {
     return result;
   }
 
+  /**
+   * Add condition that filter pages by viewer
+   *  by considering Config
+   *
+   * @param {PageQueryBuilder} builder
+   * @param {User} user
+   */
+  async function addConditionToFilteringByViewerForList(builder, user) {
+    validateCrowi();
+
+    const Config = crowi.model('Config');
+    const config = crowi.getConfig();
+
+    // determine User condition
+    const hidePagesRestrictedByOwner = Config.hidePagesRestrictedByOwnerInList(config);
+    const userCondition = hidePagesRestrictedByOwner ? user : null;
+
+    // determine UserGroup condition
+    let groupCondition = null;
+    const hidePagesRestrictedByGroup = Config.hidePagesRestrictedByGroupInList(config);
+    if (hidePagesRestrictedByGroup && user != null) {
+      const UserGroupRelation = crowi.model('UserGroupRelation');
+      groupCondition = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
+    }
+
+    return builder.addConditionToFilteringByViewer(userCondition, groupCondition);
+  }
+
+  /**
+   * export addConditionToFilteringByViewerForList as static method
+   */
+  pageSchema.statics.addConditionToFilteringByViewerForList = async function(builder, user) {
+    return addConditionToFilteringByViewerForList(builder, user);
+  };
 
   /**
    * Throw error for growi-lsx-plugin (v1.x)

+ 45 - 3
src/server/views/admin/security.html

@@ -51,7 +51,7 @@
               <input class="form-control" type="text" name="settingForm[security:basicSecret]" value="{{ settingForm['security:basicSecret']|default('') }}" {% if not isAclEnabled  %}readonly{% endif%}>
             </div>
             <div class="col-xs-offset-3 col-xs-9">
-              <p class="help-block">
+              <p class="help-block small">
                 {% if not isAclEnabled %}
                   {{ t("security_setting.basic_acl_disable") }}<br>
                 {% else %}
@@ -81,7 +81,7 @@
                 <option value="{{ t(modeValue) }}" {% if modeValue == settingForm['security:registrationMode'] %}selected{% endif %} >{{ t(modeLabel) }}</option>
                 {% endfor %}
               </select>
-              <p class="help-block">{{ t('The contents entered here will be shown in the header etc') }}</p>
+              <p class="help-block small">{{ t('The contents entered here will be shown in the header etc') }}</p>
             </div>
           </div>
 
@@ -89,11 +89,53 @@
             <label for="settingForm[security:registrationWhiteList]" class="col-xs-3 control-label">{{ t('The whitelist of registration permission E-mail address') }}</label>
             <div class="col-xs-8">
               <textarea class="form-control" type="textarea" name="settingForm[security:registrationWhiteList]" placeholder="{{ t('security_setting.example') }}: @growi.org">{{ settingForm['security:registrationWhiteList']|join('&#13')|raw }}</textarea>
-              <p class="help-block">{{ t("security_setting.restrict_emails") }}{{ t("security_setting.for_instance") }}<code>@growi.org</code>{{ t("security_setting.only_those") }}<br>
+              <p class="help-block small">{{ t("security_setting.restrict_emails") }}{{ t("security_setting.for_instance") }}<code>@growi.org</code>{{ t("security_setting.only_those") }}<br>
               {{ t("security_setting.insert_single") }}</p>
             </div>
           </div>
 
+          <div class="form-group">
+            {% set configName = 'settingForm[security:list-policy:hideRestrictedByOwner]' %}
+            {% set configValue = settingForm['security:list-policy:hideRestrictedByOwner'] %}
+            {% set isEnabled = !configValue %}
+            <label for="{{configName}}" class="col-xs-3 control-label">{{ t("security_setting.page_listing_1") }}</label>
+            <div class="col-xs-9">
+              <div class="btn-group btn-toggle" data-toggle="buttons">
+                <label class="btn btn-default btn-rounded btn-outline {% if isEnabled %}active{% endif %}" data-active-class="primary">
+                  <input name="{{configName}}" value="false" type="radio" {% if isEnabled %}checked{% endif %}> ON
+                </label>
+                <label class="btn btn-default btn-rounded btn-outline {% if !isEnabled %}active{% endif %}" data-active-class="default">
+                  <input name="{{configName}}" value="true" type="radio" {% if !isEnabled %}checked{% endif %}> OFF
+                </label>
+              </div>
+
+              <p class="help-block small">
+                {{ t("security_setting.page_listing_1_desc") }}
+              </p>
+            </div>
+          </div>
+
+          <div class="form-group">
+            {% set configName = 'settingForm[security:list-policy:hideRestrictedByGroup]' %}
+            {% set configValue = settingForm['security:list-policy:hideRestrictedByGroup'] %}
+            {% set isEnabled = !configValue %}
+            <label for="{{configName}}" class="col-xs-3 control-label">{{ t("security_setting.page_listing_2") }}</label>
+            <div class="col-xs-9">
+              <div class="btn-group btn-toggle" data-toggle="buttons">
+                <label class="btn btn-default btn-rounded btn-outline {% if isEnabled %}active{% endif %}" data-active-class="primary">
+                  <input name="{{configName}}" value="false" type="radio" {% if isEnabled %}checked{% endif %}> ON
+                </label>
+                <label class="btn btn-default btn-rounded btn-outline {% if !isEnabled %}active{% endif %}" data-active-class="default">
+                  <input name="{{configName}}" value="true" type="radio" {% if !isEnabled %}checked{% endif %}> OFF
+                </label>
+              </div>
+
+              <p class="help-block small">
+                {{ t("security_setting.page_listing_2_desc") }}
+              </p>
+            </div>
+          </div>
+
           <div class="form-group">
             <div class="col-xs-offset-3 col-xs-6">
               <input type="hidden" name="_csrf" value="{{ csrf() }}">