From 7e53d01d0fa4f40c2d0e50ceae69c2a2353a1691 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 9 Apr 2024 08:10:25 +0300 Subject: [PATCH] refactor(activity-logs): migrate auth logs table to react [EE-4715] (#10890) --- .../react/components/activity-logs.ts | 24 +++++ app/portainer/react/components/index.ts | 2 + .../auth-logs-datatable.controller.js | 68 ------------- .../auth-logs-datatable.html | 99 ------------------- .../auth-logs-datatable/index.js | 25 ----- .../auth-logs-view/auth-logs-view.html | 6 +- .../user-activity/auth-logs-view/index.js | 3 +- .../logs/AuthenticationLogsView/.keep | 0 .../AuthenticationLogsTable.tsx | 57 +++++++++++ .../logs/AuthenticationLogsView/columns.tsx | 74 ++++++++++++++ .../logs/AuthenticationLogsView/types.ts | 20 ++++ 11 files changed, 181 insertions(+), 197 deletions(-) create mode 100644 app/portainer/react/components/activity-logs.ts delete mode 100644 app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.controller.js delete mode 100644 app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.html delete mode 100644 app/portainer/user-activity/auth-logs-view/auth-logs-datatable/index.js delete mode 100644 app/react/portainer/logs/AuthenticationLogsView/.keep create mode 100644 app/react/portainer/logs/AuthenticationLogsView/AuthenticationLogsTable.tsx create mode 100644 app/react/portainer/logs/AuthenticationLogsView/columns.tsx create mode 100644 app/react/portainer/logs/AuthenticationLogsView/types.ts diff --git a/app/portainer/react/components/activity-logs.ts b/app/portainer/react/components/activity-logs.ts new file mode 100644 index 000000000..4c837255d --- /dev/null +++ b/app/portainer/react/components/activity-logs.ts @@ -0,0 +1,24 @@ +import angular from 'angular'; + +import { r2a } from '@/react-tools/react2angular'; +import { withUIRouter } from '@/react-tools/withUIRouter'; +import { withReactQuery } from '@/react-tools/withReactQuery'; +import { AuthenticationLogsTable } from '@/react/portainer/logs/AuthenticationLogsView/AuthenticationLogsTable'; + +export const activityLogsModule = angular + .module('portainer.app.react.components.activity-logs', []) + .component( + 'authenticationLogsTable', + r2a(withUIRouter(withReactQuery(AuthenticationLogsTable)), [ + 'currentPage', + 'dataset', + 'keyword', + 'limit', + 'totalItems', + 'sort', + 'onChangeSort', + 'onChangePage', + 'onChangeLimit', + 'onChangeKeyword', + ]) + ).name; diff --git a/app/portainer/react/components/index.ts b/app/portainer/react/components/index.ts index 61269e8ed..468b40d4d 100644 --- a/app/portainer/react/components/index.ts +++ b/app/portainer/react/components/index.ts @@ -48,6 +48,7 @@ import { environmentsModule } from './environments'; import { registriesModule } from './registries'; import { accountModule } from './account'; import { usersModule } from './users'; +import { activityLogsModule } from './activity-logs'; export const ngModule = angular .module('portainer.app.react.components', [ @@ -59,6 +60,7 @@ export const ngModule = angular settingsModule, accountModule, usersModule, + activityLogsModule, ]) .component( 'tagSelector', diff --git a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.controller.js b/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.controller.js deleted file mode 100644 index cd053f5c7..000000000 --- a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.controller.js +++ /dev/null @@ -1,68 +0,0 @@ -import { authenticationMethodTypesMap, authenticationMethodTypesLabels } from '@/portainer/settings/authentication/auth-method-constants'; -import { authenticationActivityTypesMap, authenticationActivityTypesLabels } from '@/portainer/settings/authentication/auth-type-constants'; - -class ActivityLogsDatatableController { - /* @ngInject */ - constructor($controller, $scope, PaginationService) { - this.PaginationService = PaginationService; - - this.tableKey = 'authLogs'; - - this.contextFilterLabels = Object.values(authenticationMethodTypesMap).map((value) => ({ value, label: authenticationMethodTypesLabels[value] })); - this.typeFilterLabels = Object.values(authenticationActivityTypesMap).map((value) => ({ value, label: authenticationActivityTypesLabels[value] })); - - const $onInit = this.$onInit; - angular.extend(this, $controller('GenericDatatableController', { $scope })); - this.$onInit = $onInit.bind(this); - - this.changeSort = this.changeSort.bind(this); - this.handleChangeLimit = this.handleChangeLimit.bind(this); - } - - changeSort(key) { - let desc = false; - if (key === this.sort.key) { - desc = !this.sort.desc; - } - - this.onChangeSort({ key, desc }); - } - - contextType(context) { - if (!(context in authenticationMethodTypesLabels)) { - return ''; - } - return authenticationMethodTypesLabels[context]; - } - - activityType(type) { - if (!(type in authenticationActivityTypesLabels)) { - return ''; - } - return authenticationActivityTypesLabels[type]; - } - - isAuthSuccess(type) { - return type === authenticationActivityTypesMap.AuthSuccess; - } - - isAuthFailure(type) { - return type === authenticationActivityTypesMap.AuthFailure; - } - - handleChangeLimit(limit) { - this.PaginationService.setPaginationLimit(this.tableKey, limit); - this.onChangeLimit(limit); - } - - $onInit() { - this.$onInitGeneric(); - - const limit = this.PaginationService.getPaginationLimit(this.tableKey); - if (limit) { - this.handleChangeLimit(+limit); - } - } -} - -export default ActivityLogsDatatableController; diff --git a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.html b/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.html deleted file mode 100644 index a72179f6d..000000000 --- a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/auth-logs-datatable.html +++ /dev/null @@ -1,99 +0,0 @@ -
- - -
-
-
- -
- Authentication Events -
-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- - -
-
-
- - - - -
-
-
- - -
-
-
- - -
-
Loading...
No logs available.
-
- -
-
-
diff --git a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/index.js b/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/index.js deleted file mode 100644 index 6488f3549..000000000 --- a/app/portainer/user-activity/auth-logs-view/auth-logs-datatable/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import controller from './auth-logs-datatable.controller'; - -export const authLogsDatatable = { - templateUrl: './auth-logs-datatable.html', - controller, - bindings: { - logs: '<', - keyword: '<', - sort: '<', - limit: '<', - totalItems: '<', - currentPage: '<', - contextFilter: '<', - typeFilter: '<', - feature: '@', - - onChangeContextFilter: '<', - onChangeTypeFilter: '<', - onChangeKeyword: '<', - onChangeSort: '<', - - onChangeLimit: '<', - onChangePage: '<', - }, -}; diff --git a/app/portainer/user-activity/auth-logs-view/auth-logs-view.html b/app/portainer/user-activity/auth-logs-view/auth-logs-view.html index 622d1bff3..86c1de947 100644 --- a/app/portainer/user-activity/auth-logs-view/auth-logs-view.html +++ b/app/portainer/user-activity/auth-logs-view/auth-logs-view.html @@ -26,8 +26,8 @@
- + >
diff --git a/app/portainer/user-activity/auth-logs-view/index.js b/app/portainer/user-activity/auth-logs-view/index.js index 6aa7f7e85..5b17122ae 100644 --- a/app/portainer/user-activity/auth-logs-view/index.js +++ b/app/portainer/user-activity/auth-logs-view/index.js @@ -1,6 +1,5 @@ import angular from 'angular'; import { authLogsView } from './auth-logs-view'; -import { authLogsDatatable } from './auth-logs-datatable'; -export default angular.module('portainer.app.user-activity.auth-logs-view', []).component('authLogsView', authLogsView).component('authLogsDatatable', authLogsDatatable).name; +export default angular.module('portainer.app.user-activity.auth-logs-view', []).component('authLogsView', authLogsView).name; diff --git a/app/react/portainer/logs/AuthenticationLogsView/.keep b/app/react/portainer/logs/AuthenticationLogsView/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/react/portainer/logs/AuthenticationLogsView/AuthenticationLogsTable.tsx b/app/react/portainer/logs/AuthenticationLogsView/AuthenticationLogsTable.tsx new file mode 100644 index 000000000..da7122ec8 --- /dev/null +++ b/app/react/portainer/logs/AuthenticationLogsView/AuthenticationLogsTable.tsx @@ -0,0 +1,57 @@ +import { History } from 'lucide-react'; + +import { Datatable } from '@@/datatables'; + +import { AuthLog } from './types'; +import { columns } from './columns'; + +export function AuthenticationLogsTable({ + dataset, + currentPage, + keyword, + limit, + onChangeKeyword, + onChangeLimit, + onChangePage, + onChangeSort, + sort, + totalItems, +}: { + keyword: string; + onChangeKeyword(keyword: string): void; + sort: { key: string; desc: boolean }; + onChangeSort(sort: { key: string; desc: boolean }): void; + limit: number; + onChangeLimit(limit: number): void; + currentPage: number; + onChangePage(page: number): void; + totalItems: number; + dataset?: Array; +}) { + return ( + + title="Authentication Events" + titleIcon={History} + columns={columns} + dataset={dataset || []} + isLoading={!dataset} + settingsManager={{ + pageSize: limit, + search: keyword, + setPageSize: onChangeLimit, + setSearch: onChangeKeyword, + setSortBy: (key, desc) => + onChangeSort({ key: key || 'timestamp', desc }), + sortBy: { + id: sort.key, + desc: sort.desc, + }, + }} + page={currentPage} + onPageChange={onChangePage} + isServerSidePagination + totalCount={totalItems} + disableSelect + /> + ); +} diff --git a/app/react/portainer/logs/AuthenticationLogsView/columns.tsx b/app/react/portainer/logs/AuthenticationLogsView/columns.tsx new file mode 100644 index 000000000..839bbfe7a --- /dev/null +++ b/app/react/portainer/logs/AuthenticationLogsView/columns.tsx @@ -0,0 +1,74 @@ +import { createColumnHelper } from '@tanstack/react-table'; +import { Check, X } from 'lucide-react'; + +import { isoDateFromTimestamp } from '@/portainer/filters/filters'; + +import { multiple } from '@@/datatables/filter-types'; +import { filterHOC } from '@@/datatables/Filter'; +import { Icon } from '@@/Icon'; + +import { ActivityType, AuthLog, AuthMethodType } from './types'; + +const activityTypesProps = { + [ActivityType.AuthSuccess]: { + label: 'Authentication success', + icon: Check, + mode: 'success', + }, + [ActivityType.AuthFailure]: { + label: 'Authentication failure', + icon: X, + mode: 'danger', + }, + [ActivityType.Logout]: { label: 'Logout', icon: undefined, mode: undefined }, +} as const; + +const columnHelper = createColumnHelper(); + +export const columns = [ + columnHelper.accessor('timestamp', { + header: 'Time', + cell: ({ getValue }) => { + const value = getValue(); + return value ? isoDateFromTimestamp(value) : ''; + }, + }), + columnHelper.accessor('origin', { + header: 'Origin', + }), + columnHelper.accessor(({ context }) => AuthMethodType[context] || '', { + header: 'Context', + enableColumnFilter: true, + filterFn: multiple, + meta: { + filter: filterHOC('Filter'), + }, + }), + columnHelper.accessor('username', { + header: 'User', + }), + + columnHelper.accessor((item) => activityTypesProps[item.type].label, { + header: 'Result', + enableColumnFilter: true, + filterFn: multiple, + meta: { + filter: filterHOC('Filter'), + }, + cell({ row: { original: item } }) { + const props = activityTypesProps[item.type]; + if (!props) { + return null; + } + + const { label, icon, mode } = props; + + return ( + + {label} + {icon && mode && } + + ); + }, + }), +]; diff --git a/app/react/portainer/logs/AuthenticationLogsView/types.ts b/app/react/portainer/logs/AuthenticationLogsView/types.ts new file mode 100644 index 000000000..65686fc0c --- /dev/null +++ b/app/react/portainer/logs/AuthenticationLogsView/types.ts @@ -0,0 +1,20 @@ +export enum AuthMethodType { + Internal = 1, + LDAP, + OAuth, +} + +export enum ActivityType { + AuthSuccess = 1, + AuthFailure, + Logout, +} + +export interface AuthLog { + timestamp: number; + context: AuthMethodType; + id: number; + username: string; + type: ActivityType; + origin: string; +}