Просмотр исходного кода

Merge commit '6d351524e24c0192908fbd9dbc7b6bcf5d78268e' into imprv/remove-PageTagRelation-when-complete-delete-page

yusuketk 6 лет назад
Родитель
Сommit
7ec812351c

+ 4 - 0
CHANGES.md

@@ -3,7 +3,11 @@
 ## 3.5.2-RC
 
 * Feature: Remain metadata option when Move/Rename page
+* Improvement: Support code highlight for Swift and Kotlin
 * Fix: Consider timezone on admin page
+* Fix: Editor doesn't work on Microsoft Edge
+* Support: Upgrade libs
+    * growi-commons
 
 ## 3.5.1
 

+ 4 - 0
README.md

@@ -172,6 +172,10 @@ Environment Variables
     * MONGO_GRIDFS_TOTAL_LIMIT: Total capacity limit of MongoDB GridFS (bytes). default: `Infinity`
     * SAML_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: If `true`, the system uses only the value of the environment variable as the value of the SAML option that can be set via the environment variable.
     * PUBLISH_OPEN_API: Publish GROWI OpenAPI resources with [ReDoc](https://github.com/Rebilly/ReDoc). Visit `/api-docs`.
+    * FORCE_WIKI_MODE: Forces wiki mode. default: undefined
+      * `public`  : Forces all pages to become public
+      * `private` : Forces all pages to become private
+      * undefined : Publicity will be configured by the admin security page settings
 * **Option to integrate with external systems**
     * HACKMD_URI: URI to connect to [HackMD(CodiMD)](https://hackmd.io/) server.
         * **This server must load the GROWI agent. [Here's how to prepare it](https://docs.growi.org/guide/admin-cookbook/integrate-with-hackmd.html).**

+ 1 - 1
config/env.dev.js

@@ -13,5 +13,5 @@ module.exports = {
   // PUBLISH_OPEN_API: true,
   // USER_UPPER_LIMIT: 0,
   // DEV_HTTPS: true,
-  // PUBLIC_WIKI_ONLY: true,
+  // FORCE_WIKI_MODE: 'private', // 'public', 'private', undefined
 };

+ 14 - 15
resource/locales/en-US/translation.json

@@ -27,6 +27,8 @@
   "Category": "Category",
   "User": "User",
   "status":"Status",
+  "account_id": "Account Id",
+
 
   "Update": "Update",
   "Update Page": "Update Page",
@@ -105,13 +107,12 @@
   "Customize": "Customize",
   "Notification Settings": "Notification Settings",
   "User_Management": "User Management",
-  "External Account management": "External Account management",
+  "external_account_management": "External Account Management",
   "UserGroup Management": "UserGroup Management",
   "Full Text Search Management": "Full Text Search Management",
   "Import Data": "Import Data",
   "Basic Settings": "Basic Settings",
   "Basic authentication": "Basic authentication",
-  "Guest users access": "Guest users access",
   "Register limitation": "Register limitation",
   "The contents entered here will be shown in the header etc": "The contents entered here will be shown in the header etc",
   "Public": "Public",
@@ -289,7 +290,7 @@
     "deleting_page": "Deleting Page",
     "delete_recursively": "Delete child pages recursively.",
     "delete_completely": "Delete Completely",
-    "delete_completely_restriction": "You have no admin to delete completely.",
+    "delete_completely_restriction": "You don't have the authority to delete pages completely.",
     "recursively": "Delete children of under <code>%s</code> recursively.",
     "completely": "Delete completely instead of putting it into trash."
   },
@@ -435,14 +436,12 @@
   },
 
   "security_setting": {
-		"Basic authentication": "Basic Authentication",
 		"Security settings": "Security settings",
-		"Guest users access": "Guest users access",
-		"Register limitation": "Register limitation",
+    "Guest Users Access": "Guest Users Access",
+    "Fixed by env var": "This is fixed by the env var <code>%s=%s</code>.",
+    "Register limitation": "Register limitation",
+    "Register limitation desc": "Restricts ways to register new user.",
 		"The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
-		"common_authentication": "If you set the basic authentication, common authentication is applied on the whole page.",
-		"without_encryption": "Please be noted that your ID and Password will be sent wihtout encryption.",
-		"basic_acl_disable": "Because of Public Wiki  setting, basic authentication can not be used.",
 		"users_without_account": "Users without account is not accessible",
     "example": "Example",
     "restrict_emails": "You can restrict registerable e-mail address.",
@@ -471,13 +470,13 @@
     "clientID": "Client ID",
     "client_secret": "Client Secret",
     "guest_mode": {
-      "deny": "Deny Unregistered Users",
-      "readonly": "View Only"
+      "deny": "Deny (Registered Users Only)",
+      "readonly": "Accept (Guests can read only)"
     },
     "registration_mode": {
-      "open": "Anyone",
-      "restricted": "Require Admin permission",
-      "closed": "Invitation Only"
+      "open": "Open (Anyone can registre)",
+      "restricted": "Restricted (Requires approval by administrators)",
+      "closed": "Closed (Invitation Only)"
     },
     "configuration": " Configuration",
     "optional": "Optional",
@@ -707,7 +706,7 @@
     "group_example": "e.g. : Group1",
     "created_group": "Group was created",
     "add_user": "Add a User to the Created Group",
-    "deny_create_group": "You can't create a new group with the current settings",
+    "deny_create_group": "You can't create a new group.",
     "is_loading_data": "Loading data...",
     "choose_action": "Choose an action for private pages",
     "delete_group": "Delete Group",

+ 8 - 11
resource/locales/ja/translation.json

@@ -27,6 +27,7 @@
   "Category": "カテゴリー",
   "User": "ユーザー",
   "status": "ステータス",
+  "account_id": "アカウントID",
 
   "Update": "更新",
   "Update Page": "ページを更新",
@@ -105,13 +106,11 @@
   "Customize": "カスタマイズ",
   "Notification Settings": "通知設定",
   "User_Management": "ユーザー管理",
-  "External Account management": "外部アカウント管理",
+  "external_account_management": "外部アカウント管理",
   "UserGroup Management": "グループ管理",
   "Full Text Search Management": "全文検索管理",
   "Import Data": "データインポート",
   "Basic Settings": "基本設定",
-  "Basic authentication": "Basic認証",
-  "Guest users access": "ゲストユーザーのアクセス",
   "Register limitation": "登録の制限",
   "The contents entered here will be shown in the header etc": "ここに入力した内容は、ヘッダー等に表示されます。",
   "Public": "公開",
@@ -435,13 +434,11 @@
    },
 
   "security_setting": {
-    "Basic authentication": "Basic認証",
-    "Guest users access": "ゲストユーザーのアクセス",
+    "Guest Users Access": "ゲストユーザーのアクセス",
+    "Fixed by env var": "環境変数 <code>%s=%s</code> により固定されています。",
     "Register limitation": "登録の制限",
+    "Register limitation desc": "新しいユーザーを登録する方法を制限します.",
     "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
-    "common_authentication": "Basic認証を設定すると、ページ全体に共通の認証がかかります。",
-    "without_encryption": "IDとパスワードは暗号化されずに送信されるのでご注意下さい。",
-    "basic_acl_disable": "Public Wiki の設定のため、Basic認証は利用できません。",
     "users_without_account": "アカウントを持たないユーザーはアクセス不可",
     "example": "例",
     "restrict_emails": "登録可能なメールアドレスを制限することができます。",
@@ -467,8 +464,8 @@
     "clientID": "クライアントID",
     "client_secret": "クライアントシークレット",
     "guest_mode": {
-      "deny": "アカウントを持たないユーザーはアクセス不可",
-      "readonly": "閲覧のみ可"
+      "deny": "拒否 (アカウントを持つユーザーのみ利用可能)",
+      "readonly": "許可 (ゲストユーザーも閲覧のみ可能)"
     },
     "registration_mode": {
       "open": "公開 (だれでも登録可能)",
@@ -692,7 +689,7 @@
     "group_example": "例: Group1",
     "created_group": "グループを作成しました",
     "add_user": "グループへのユーザー追加",
-    "deny_create_group": "現在の設定では新規グループの作成はできません。",
+    "deny_create_group": "新規グループの作成はできません。",
     "is_loading_data": "データを取得中です...",
     "choose_action": "削除するグループの限定公開ページの処理を選択してください",
     "delete_group": "グループの削除",

+ 1 - 1
src/server/form/admin/securityGeneral.js

@@ -5,7 +5,7 @@ const stringToArray = require('../../util/formUtil').stringToArrayFilter;
 const normalizeCRLF = require('../../util/formUtil').normalizeCRLFFilter;
 
 module.exports = form(
-  field('settingForm[security:restrictGuestMode]').required(),
+  field('settingForm[security:restrictGuestMode]'),
   field('settingForm[security:registrationMode]').required(),
   field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray),
   field('settingForm[security:list-policy:hideRestrictedByOwner]').trim().toBooleanStrict(),

+ 10 - 7
src/server/routes/admin.js

@@ -105,7 +105,13 @@ module.exports = function(crowi, app) {
   // app.get('/admin/security'                  , admin.security.index);
   actions.security = {};
   actions.security.index = function(req, res) {
-    return res.render('admin/security');
+    const isWikiModeForced = aclService.isWikiModeForced();
+    const guestModeValue = aclService.getGuestModeValue();
+
+    return res.render('admin/security', {
+      isWikiModeForced,
+      guestModeValue,
+    });
   };
 
   // app.get('/admin/markdown'                  , admin.markdown.index);
@@ -889,12 +895,9 @@ module.exports = function(crowi, app) {
     }
 
     const form = req.form.settingForm;
-    if (!aclService.isAclEnabled()) {
-      const guestMode = form['security:restrictGuestMode'];
-      if (guestMode === 'Deny') {
-        req.form.errors.push('Private Wikiへの設定変更はできません。');
-        return res.json({ status: false, message: req.form.errors.join('\n') });
-      }
+    if (aclService.isWikiModeForced()) {
+      logger.debug('security:restrictGuestMode will not be changed because wiki mode is forced to set');
+      delete form['security:restrictGuestMode'];
     }
 
     try {

+ 19 - 9
src/server/service/acl.js

@@ -17,22 +17,32 @@ class AclService {
   }
 
   isAclEnabled() {
-    const isPublicWikiOnly = this.configManager.getConfig('crowi', 'security:isPublicWikiOnly');
-    const isPrivateWikiOnly = this.configManager.getConfig('crowi', 'security:isPrivateWikiOnly');
+    const wikiMode = this.configManager.getConfig('crowi', 'security:wikiMode');
+    return wikiMode !== 'public';
+  }
+
+  isWikiModeForced() {
+    const wikiMode = this.configManager.getConfig('crowi', 'security:wikiMode');
+    const isPrivateOrPublic = wikiMode === 'private' || wikiMode === 'public';
+
+    return isPrivateOrPublic;
+  }
 
-    return !(isPublicWikiOnly || isPrivateWikiOnly);
+  getGuestModeValue() {
+    return this.isGuestAllowedToRead()
+      ? this.labels.SECURITY_RESTRICT_GUEST_MODE_READONLY
+      : this.labels.SECURITY_RESTRICT_GUEST_MODE_DENY;
   }
 
-  getIsGuestAllowedToRead() {
-    const isPublicWikiOnly = this.configManager.getConfig('crowi', 'security:isPublicWikiOnly');
-    const isPrivateWikiOnly = this.configManager.getConfig('crowi', 'security:isPrivateWikiOnly');
+  isGuestAllowedToRead() {
+    const wikiMode = this.configManager.getConfig('crowi', 'security:wikiMode');
 
     // return false if private wiki mode
-    if (isPrivateWikiOnly) {
+    if (wikiMode === 'private') {
       return false;
     }
-    // return true if puclic wiki mode
-    if (isPublicWikiOnly) {
+    // return true if public wiki mode
+    if (wikiMode === 'public') {
       return true;
     }
 

+ 4 - 10
src/server/service/config-loader.js

@@ -136,17 +136,11 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    TYPES.NUMBER,
     default: Infinity,
   },
-  PUBLIC_WIKI_ONLY: {
+  FORCE_WIKI_MODE: {
     ns:      'crowi',
-    key:     'security:isPublicWikiOnly',
-    type:    TYPES.BOOLEAN,
-    default: false,
-  },
-  PRIVATE_WIKI_ONLY: {
-    ns:      'crowi',
-    key:     'security:isPrivateWikiOnly',
-    type:    TYPES.BOOLEAN,
-    default: false,
+    key:     'security:wikiMode',
+    type:    TYPES.STRING,
+    default: undefined,
   },
   SAML_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
     ns:      'crowi',

+ 1 - 1
src/server/util/middlewares.js

@@ -194,7 +194,7 @@ module.exports = (crowi, app) => {
       // when the route is not strictly restricted
       if (!isStrictly) {
         // when allowed to read
-        if (crowi.aclService.getIsGuestAllowedToRead()) {
+        if (crowi.aclService.isGuestAllowedToRead()) {
           return next();
         }
       }

+ 2 - 2
src/server/views/admin/external-accounts.html

@@ -1,11 +1,11 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitle(t('External Account management')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitle(t('external_account_management')) }}{% endblock %}
 
 {% block content_header %}
 <div class="header-wrap">
   <header id="page-header">
-    <h1 id="admin-title" class="title">{{ t('User_management') }}/{{ t('External Account management') }}</h1>
+    <h1 id="admin-title" class="title">{{ t('User_Management') }} / {{ t('external_account_management') }}</h1>
   </header>
 </div>
 {% endblock %}

+ 12 - 4
src/server/views/admin/security.html

@@ -41,13 +41,21 @@
         <legend class="alert-anchor">{{ t('security_settings') }}</legend>
 
           <div class="form-group">
-            <label for="settingForm[security:restrictGuestMode]" class="col-xs-3 control-label">{{ t('Guest users access') }}</label>
+            <label for="settingForm[security:restrictGuestMode]" class="col-xs-3 control-label">{{ t('security_setting.Guest Users Access') }}</label>
             <div class="col-xs-6">
-              <select class="form-control selectpicker" name="settingForm[security:restrictGuestMode]" value="{{ getConfig('crowi', 'security:restrictGuestMode') }}">
+              {% set selectedValue = guestModeValue %}
+              <select class="form-control selectpicker" {% if isWikiModeForced %}disabled{% endif %}
+                  name="settingForm[security:restrictGuestMode]" value="{{ getConfig('crowi', 'security:restrictGuestMode') }}">
                 {% for modeValue, modeLabel in consts.restrictGuestMode %}
-                <option value="{{ t(modeValue) }}" {% if modeValue == getConfig('crowi', 'security:restrictGuestMode') %}selected{% endif %} >{{ t(modeLabel) }}</option>
+                  <option value="{{ t(modeValue) }}" {% if modeValue == selectedValue %}selected{% endif %}>{{ t(modeLabel) }}</option>
                 {% endfor %}
               </select>
+              {% if isWikiModeForced %}
+              <p class="alert alert-warning mt-2">
+                <i class="icon-exclamation icon-fw"></i><b>FIXED</b><br>
+                {{ t('security_setting.Fixed by env var', 'FORCE_WIKI_MODE', getConfig('crowi', 'security:wikiMode')) }}
+              </p>
+              {% endif %}
             </div>
           </div>
 
@@ -59,7 +67,7 @@
                 <option value="{{ t(modeValue) }}" {% if modeValue == getConfig('crowi', 'security:registrationMode') %}selected{% endif %} >{{ t(modeLabel) }}</option>
                 {% endfor %}
               </select>
-              <p class="help-block small">{{ t('The contents entered here will be shown in the header etc') }}</p>
+              <p class="help-block small">{{ t('security_setting.Register limitation desc') }}</p>
             </div>
           </div>
 

+ 4 - 1
src/server/views/admin/user-groups.html

@@ -36,7 +36,10 @@
         {% if isAclEnabled %}
           <button  data-toggle="collapse" class="btn btn-default" href="#createGroupForm">{{ t('user_group_management.create_group') }}</button>
         {% else %}
-          {{ t('user_group_management.deny_create_group')}}
+          <p class="alert alert-warning">
+            <i class="icon-exclamation icon-fw"></i><b>{{ t('user_group_management.deny_create_group')}}</b><br>
+            {{ t('security_setting.Fixed by env var', 'FORCE_WIKI_MODE', getConfig('crowi', 'security:wikiMode')) }}
+          </p>
         {% endif %}
       </p>
       <form role="form" action="/admin/user-group/create" method="post">

+ 0 - 8
src/test/models/page.test.js

@@ -23,14 +23,6 @@ describe('Page', () => {
     UserGroupRelation = mongoose.model('UserGroupRelation');
     Page = mongoose.model('Page');
 
-    // remove all
-    await Promise.all([
-      Page.remove({}),
-      User.remove({}),
-      UserGroup.remove({}),
-      UserGroupRelation.remove({}),
-    ]);
-
     await User.insertMany([
       { name: 'Anon 0', username: 'anonymous0', email: 'anonymous0@example.com' },
       { name: 'Anon 1', username: 'anonymous1', email: 'anonymous1@example.com' },

+ 0 - 5
src/test/models/user.test.js

@@ -12,11 +12,6 @@ describe('User', () => {
     crowi = await getInstance();
     User = mongoose.model('User');
 
-    // remove all
-    await Promise.all([
-      User.remove({}),
-    ]);
-
     await User.create({
       name: 'Example for User Test',
       username: 'usertest',