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

Merge branch 'feat/auditlog' of https://github.com/weseek/growi into feat/add-site-url-update-activity

keigo-h 3 лет назад
Родитель
Сommit
030775f7d1

+ 1 - 1
package.json

@@ -83,7 +83,7 @@
     "ts-jest": "^27.0.4",
     "ts-node": "^9.1.1",
     "tsconfig-paths": "^3.9.0",
-    "typescript": "^4.2.3",
+    "typescript": "~4.6",
     "yargs": "^17.3.1"
   },
   "engines": {

+ 1 - 0
packages/app/.env.development

@@ -30,3 +30,4 @@ OGP_URI="http://ogp:8088"
 # GROWI_CLOUD_URI='http://growi.cloud'
 # GROWI_APP_ID_FOR_GROWI_CLOUD=012345
 # ACTIVITY_EXPIRATION_SECONDS=2592000
+# AUDIT_LOG_ACTION_GROUP_SIZE=SMALL

+ 1 - 1
packages/app/src/components/UncontrolledCodeMirror.tsx

@@ -21,7 +21,7 @@ interface UncontrolledCodeMirrorCoreProps extends UncontrolledCodeMirrorProps {
 
 class UncontrolledCodeMirrorCore extends AbstractEditor<UncontrolledCodeMirrorCoreProps> {
 
-  render(): ReactNode {
+  override render(): ReactNode {
 
     const {
       value, isGfmMode, lineNumbers, options, forwardedRef,

+ 62 - 0
packages/app/src/interfaces/activity.ts

@@ -103,6 +103,65 @@ export const SupportedAction = {
   ACTION_ADMIN_SITE_URL_UPDATE,
 } as const;
 
+export const ActionGroupSize = {
+  Small: 'SMALL',
+  Medium: 'MEDIUM',
+  Large: 'LARGE',
+} as const;
+
+export const SmallActionGroup = {
+  ACTION_LOGIN_SUCCESS,
+  ACTION_LOGIN_FAILURE,
+  ACTION_LOGOUT,
+  ACTION_PAGE_CREATE,
+  ACTION_PAGE_DELETE,
+} as const;
+
+// SmallActionGroup + Action by all General Users - PAGE_VIEW
+export const MediumActionGroup = {
+  ...SmallActionGroup,
+  ACTION_USER_PERSONAL_SETTINGS_UPDATE,
+  ACTION_USER_IMAGE_TYPE_UPDATE,
+  ACTION_USER_LDAP_ACCOUNT_ASSOCIATE,
+  ACTION_USER_LDAP_ACCOUNT_DISCONNECT,
+  ACTION_USER_PASSWORD_UPDATE,
+  ACTION_USER_API_TOKEN_UPDATE,
+  ACTION_USER_EDITOR_SETTINGS_UPDATE,
+  ACTION_USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE,
+  ACTION_PAGE_LIKE,
+  ACTION_PAGE_UNLIKE,
+  ACTION_PAGE_BOOKMARK,
+  ACTION_PAGE_UNBOOKMARK,
+  ACTION_PAGE_CREATE,
+  ACTION_PAGE_UPDATE,
+  ACTION_PAGE_RENAME,
+  ACTION_PAGE_DUPLICATE,
+  ACTION_PAGE_DELETE,
+  ACTION_PAGE_DELETE_COMPLETELY,
+  ACTION_PAGE_REVERT,
+  ACTION_COMMENT_CREATE,
+  ACTION_COMMENT_UPDATE,
+  ACTION_COMMENT_REMOVE,
+} as const;
+
+// MediumActionGroup + All Actions by Admin Users - PAGE_VIEW
+export const LargeActionGroup = {
+  ...MediumActionGroup,
+  ACTION_ADMIN_APP_SETTINGS_UPDATE,
+  ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
+  ACTION_ADMIN_LINE_BREAK_UPDATE,
+  ACTION_ADMIN_LAYOUT_UPDATE,
+  ACTION_ADMIN_ARCHIVE_DATA_UPLOAD,
+  ACTION_ADMIN_ARCHIVE_DATA_CREATE,
+  ACTION_ADMIN_USER_NOTIFICATION_SETTINGS_ADD,
+  ACTION_ADMIN_SLACK_WORKSPACE_CREATE,
+  ACTION_ADMIN_SLACK_CONFIGURATION_SETTING_UPDATE,
+  ACTION_ADMIN_USERS_INVITE,
+  ACTION_ADMIN_USER_GROUP_CREATE,
+  ACTION_ADMIN_SEARCH_INDICES_NORMALIZE,
+  ACTION_ADMIN_SEARCH_INDICES_REBUILD,
+} as const;
+
 export const SupportedActionToNotified = {
   ACTION_PAGE_LIKE,
   ACTION_PAGE_BOOKMARK,
@@ -142,6 +201,9 @@ export const AllSupportedTargetModel = Object.values(SupportedTargetModel);
 export const AllSupportedEventModel = Object.values(SupportedEventModel);
 export const AllSupportedAction = Object.values(SupportedAction);
 export const AllSupportedActionToNotified = Object.values(SupportedActionToNotified);
+export const AllSmallGroupActions = Object.values(SmallActionGroup);
+export const AllMediumGroupActions = Object.values(MediumActionGroup);
+export const AllLargeGroupActions = Object.values(LargeActionGroup);
 
 /*
  * Type

+ 1 - 0
packages/app/src/server/models/activity.ts

@@ -119,6 +119,7 @@ activitySchema.statics.createByParameters = async function(parameters): Promise<
   return activity;
 };
 
+// When using this method, ensure that activity updates are allowed using ActivityService.shoutUpdateActivity
 activitySchema.statics.updateByParameters = async function(activityId: string, parameters): Promise<IActivity> {
   const activity = await this.findOneAndUpdate({ _id: activityId }, parameters, { new: true }) as unknown as IActivity;
 

+ 45 - 9
packages/app/src/server/service/activity.ts

@@ -1,6 +1,8 @@
 import mongoose from 'mongoose';
 
-import { IActivity } from '~/interfaces/activity';
+import {
+  IActivity, SupportedActionType, ActionGroupSize, AllSmallGroupActions, AllMediumGroupActions, AllLargeGroupActions, AllSupportedActionToNotified,
+} from '~/interfaces/activity';
 import { IPage } from '~/interfaces/page';
 import Activity from '~/server/models/activity';
 
@@ -20,24 +22,58 @@ class ActivityService {
     this.crowi = crowi;
     this.activityEvent = crowi.event('activity');
 
+    this.getAvailableActions = this.getAvailableActions.bind(this);
+    this.shoudUpdateActivity = this.shoudUpdateActivity.bind(this);
+
     this.initActivityEventListeners();
   }
 
   initActivityEventListeners(): void {
     this.activityEvent.on('update', async(activityId: string, parameters, target?: IPage) => {
       let activity: IActivity;
-      try {
-        activity = await Activity.updateByParameters(activityId, parameters);
-      }
-      catch (err) {
-        logger.error('Update activity failed', err);
-        return;
+      const shoudUpdate = this.shoudUpdateActivity(parameters.action);
+
+      if (shoudUpdate) {
+        try {
+          activity = await Activity.updateByParameters(activityId, parameters);
+        }
+        catch (err) {
+          logger.error('Update activity failed', err);
+          return;
+        }
+
+        this.activityEvent.emit('updated', activity, target);
       }
-
-      this.activityEvent.emit('updated', activity, target);
     });
   }
 
+  getAvailableActions = function(): SupportedActionType[] {
+    const auditLogActionGroupSize = this.crowi.configManager.getConfig('crowi', 'app:auditLogActionGroupSize') || ActionGroupSize.Small;
+
+    // TODO: 97982, 97986
+    // Update AvailableActions taking into account the values of "AUDIT_LOG_EXCLUDE_ACTIONS" and “AUDIT_LOG_ADDITONAL_ACTIONS"
+
+    const availableActions: SupportedActionType[] = [...AllSupportedActionToNotified];
+
+    switch (auditLogActionGroupSize) {
+      case ActionGroupSize.Small:
+        availableActions.push(...AllSmallGroupActions);
+        break;
+      case ActionGroupSize.Medium:
+        availableActions.push(...AllMediumGroupActions);
+        break;
+      case ActionGroupSize.Large:
+        availableActions.push(...AllLargeGroupActions);
+        break;
+    }
+
+    return Array.from(new Set(availableActions));
+  }
+
+  shoudUpdateActivity = function(action: SupportedActionType): boolean {
+    return this.getAvailableActions().includes(action);
+  }
+
   createTtlIndex = async function() {
     const configManager = this.crowi.configManager;
     const activityExpirationSeconds = configManager != null ? configManager.getConfig('crowi', 'app:activityExpirationSeconds') : 2592000;

+ 6 - 0
packages/app/src/server/service/config-loader.ts

@@ -628,6 +628,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type: ValueType.NUMBER,
     default: 2592000, // 30 days
   },
+  AUDIT_LOG_ACTION_GROUP_SIZE: {
+    ns: 'crowi',
+    key: 'app:auditLogActionGroupSize',
+    type: ValueType.STRING,
+    default: 'SMALL',
+  },
 };
 
 

+ 6 - 4
packages/app/src/server/service/s2s-messaging/nchan.ts

@@ -1,11 +1,13 @@
 import path from 'path';
-import WebSocket from 'ws';
+
 import ReconnectingWebSocket from 'reconnecting-websocket';
+import WebSocket from 'ws';
 
 import axios from '~/utils/axios';
 import loggerFactory from '~/utils/logger';
 
 import S2sMessage from '../../models/vo/s2s-message';
+
 import { AbstractS2sMessagingService } from './base';
 
 const logger = loggerFactory('growi:service:s2s-messaging:nchan');
@@ -56,7 +58,7 @@ class NchanDelegator extends AbstractS2sMessagingService {
   /**
    * @inheritdoc
    */
-  async publish(s2sMessage: S2sMessage): Promise<void> {
+  override async publish(s2sMessage: S2sMessage): Promise<void> {
     await super.publish(s2sMessage);
 
     const url = this.constructUrl(this.publishPath).toString();
@@ -69,7 +71,7 @@ class NchanDelegator extends AbstractS2sMessagingService {
   /**
    * @inheritdoc
    */
-  addMessageHandler(handlable) {
+  override addMessageHandler(handlable) {
     if (this.socket == null) {
       logger.error('socket has not initialized yet.');
       return;
@@ -82,7 +84,7 @@ class NchanDelegator extends AbstractS2sMessagingService {
   /**
    * @inheritdoc
    */
-  removeMessageHandler(handlable) {
+  override removeMessageHandler(handlable) {
     if (this.socket == null) {
       logger.error('socket has not initialized yet.');
       return;

+ 1 - 0
tsconfig.base.json

@@ -15,6 +15,7 @@
     // "strict": true,
     "strictNullChecks": true,
     "noImplicitAny": false,
+    "noImplicitOverride": true,
 
     /* Additional Checks */
     "noUnusedLocals": false,

+ 4 - 4
yarn.lock

@@ -21490,10 +21490,10 @@ typeorm@^0.2.31:
     yargs "^16.2.0"
     zen-observable-ts "^1.0.0"
 
-typescript@^4.2.3:
-  version "4.2.4"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
-  integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
+typescript@~4.6:
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
+  integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
 
 typpy@2.3.11:
   version "2.3.11"