{
)}
- {row.name}
+ {row.name}
+
{
@@ -155,7 +163,7 @@ const RoleTableDisplay = () => {
{roles.length == 0
- ? t('access-management.roles.no-roles-body')
+ ? t('access-management.roles.no-roles-body', {brandname: brandname})
: t('access-management.roles.no-filtered-roles-body')}
@@ -164,6 +172,7 @@ const RoleTableDisplay = () => {
);
};
+
const createRoleButtonHelper = (isEmptyPage?: boolean) => {
const emptyPageButtonProp = { style: { marginTop: global_spacer_xl.value } };
const normalPageButtonProps = { style: { marginLeft: global_spacer_sm.value } };
@@ -180,34 +189,6 @@ const RoleTableDisplay = () => {
);
};
- if (loading) {
- return (
-
-
- {t('access-management.roles.loading-roles')}>}
- icon={}
- headingLevel="h4"
- />
-
-
- );
- }
-
- if (error) {
- return (
-
-
- {t('access-management.roles.loading-roles-error')}>}
- icon={}
- headingLevel="h4"
- />
-
-
- );
- }
-
const displayContent = () => {
if (roles.length === 0) {
return (
@@ -222,6 +203,19 @@ const RoleTableDisplay = () => {
);
}
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ );
+ }
+
return (
@@ -258,7 +252,7 @@ const RoleTableDisplay = () => {
}
}}
>
- {columnNames.name}
+ {columnNames.name}
{columnNames.permissions} |
{columnNames.description} |
diff --git a/src/app/Caches/Create/CreateCacheWizard.tsx b/src/app/Caches/Create/CreateCacheWizard.tsx
index 6601becb2..7e3463bd4 100644
--- a/src/app/Caches/Create/CreateCacheWizard.tsx
+++ b/src/app/Caches/Create/CreateCacheWizard.tsx
@@ -245,7 +245,7 @@ const CreateCacheWizard = (props: { cacheManager: CacheManager; create: boolean
onClick={(event) => getPreviousStep(event, activeStep, onBack)}
data-cy="wizardBackButton"
>
- {t('caches.create.back-button-label')}
+ {t('common.actions.back')}
);
diff --git a/src/app/Caches/DetailCache.tsx b/src/app/Caches/DetailCache.tsx
index 71ff568e2..9ec501597 100644
--- a/src/app/Caches/DetailCache.tsx
+++ b/src/app/Caches/DetailCache.tsx
@@ -183,7 +183,7 @@ const DetailCache = (props: { cacheName: string }) => {
search: location.search
}}
>
-
+
diff --git a/src/app/Common/DataContainerBreadcrumb.tsx b/src/app/Common/DataContainerBreadcrumb.tsx
index 35eb1b5c2..8b86bb3d1 100644
--- a/src/app/Common/DataContainerBreadcrumb.tsx
+++ b/src/app/Common/DataContainerBreadcrumb.tsx
@@ -1,8 +1,10 @@
import { Breadcrumb, BreadcrumbItem } from '@patternfly/react-core';
import { Link } from 'react-router-dom';
import * as React from 'react';
+import { useTranslation } from 'react-i18next';
-const DataContainerBreadcrumb = (props: { currentPage: string; cacheName?: string }) => {
+const DataContainerBreadcrumb = (props: { currentPage: string; parentPage?: string, cacheName?: string, label?: string }) => {
+ const { t } = useTranslation();
const addCacheName = () => {
if (props.cacheName) {
return (
@@ -20,17 +22,17 @@ const DataContainerBreadcrumb = (props: { currentPage: string; cacheName?: strin
}
return;
};
-
+ const label = props.label? props.label : 'cache-managers.title';
return (
-
+
- Data container
+ {t(label)}
{addCacheName()}
diff --git a/src/app/Common/TableErrorState.tsx b/src/app/Common/TableErrorState.tsx
index 9f1855a14..ad8bfff7e 100644
--- a/src/app/Common/TableErrorState.tsx
+++ b/src/app/Common/TableErrorState.tsx
@@ -2,9 +2,9 @@ import {
Bullseye,
EmptyState,
EmptyStateBody,
+ EmptyStateHeader,
EmptyStateIcon,
- EmptyStateVariant,
- EmptyStateHeader
+ EmptyStateVariant
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { global_danger_color_200 } from '@patternfly/react-tokens';
@@ -12,17 +12,15 @@ import * as React from 'react';
import { useTranslation } from 'react-i18next';
const TableErrorState = (props: { error: string; detail?: string }) => {
+ const { t } = useTranslation();
const displayErrorBody = () => {
if (props.detail) {
return props.detail as string;
} else {
- return 'There was an error retrieving data.\n' + ' Check your connection and try again.';
+ return t('common.loading-error-message');
}
};
- const { t } = useTranslation();
- const brandname = t('brandname.brandname');
-
return (
diff --git a/src/app/Common/TableLoadingState.tsx b/src/app/Common/TableLoadingState.tsx
new file mode 100644
index 000000000..666fb13e9
--- /dev/null
+++ b/src/app/Common/TableLoadingState.tsx
@@ -0,0 +1,24 @@
+import {
+ Bullseye,
+ EmptyState,
+ EmptyStateHeader,
+ EmptyStateIcon,
+ EmptyStateVariant,
+ Spinner
+} from '@patternfly/react-core';
+import * as React from 'react';
+
+const TableLoadingState = (props: { message: string }) => {
+ return (
+
+
+ }
+ headingLevel="h4"
+ />
+
+
+ );
+};
+export { TableLoadingState };
diff --git a/src/app/IndexManagement/IndexManagement.tsx b/src/app/IndexManagement/IndexManagement.tsx
index b93c5f206..fdda93eea 100644
--- a/src/app/IndexManagement/IndexManagement.tsx
+++ b/src/app/IndexManagement/IndexManagement.tsx
@@ -169,7 +169,7 @@ const IndexManagement = (props) => {
}}
>
diff --git a/src/app/assets/languages/en.json b/src/app/assets/languages/en.json
index 22c0aa2fb..b88a13e00 100644
--- a/src/app/assets/languages/en.json
+++ b/src/app/assets/languages/en.json
@@ -7,6 +7,14 @@
"configuration-docs-link": "https://infinispan.org/docs/stable/titles/configuring/configuring.html",
"default-roles-docs-link": "https://infinispan.org/docs/stable/titles/server/server.html#default-user-roles_server-getting-started"
},
+ "common" : {
+ "actions" : {
+ "back": "Back",
+ "cancel": "Cancel",
+ "save": "Save"
+ },
+ "loading-error-message": "There was an error retrieving data. Check your connection and try again."
+ },
"layout": {
"console-name": "Server Management Console",
"skip-to-content": "Skip to Content",
@@ -219,7 +227,6 @@
"templates-placeholder": "Select a cache template",
"create-button-label": "Create",
"next-button-label": "Next",
- "back-button-label": "Back",
"cancel-button-label": "Cancel",
"download-button-label": "Download",
"getting-started": {
@@ -534,8 +541,7 @@
"action-see-less": "See fewer cache details",
"action-see-more": "See more cache details",
"action-manage-indexes": "Manage indexes",
- "refresh": "Refresh",
- "back": "Back"
+ "refresh": "Refresh"
},
"entries": {
"action-edit": "Edit",
@@ -642,8 +648,7 @@
"size": "Index size in bytes",
"indexing-status": "Indexing",
"button-clear": "Clear index",
- "button-rebuild": "Rebuild index",
- "button-back-to-cache-detail": "Back"
+ "button-rebuild": "Rebuild index"
},
"rebalancing": {
"rebalancing": "Rebalancing",
@@ -853,6 +858,7 @@
"no-filtered-roles-body": "No roles match your search criteria.",
"roles-hint-link": "Learn more in the {{brandname}} documentation",
"loading-roles": "Loading roles",
+ "loading-roles-error": "Unexpected error loading roles",
"create-button": "Create role",
"delete-action": "Delete",
"no-roles-status": "No roles",
@@ -886,6 +892,21 @@
"modal-delete-description-2": "You can always recreate the role.",
"delete-success": "Role {{name}} has been deleted",
"delete-error": "Unexpected error deleting the role {{name}}"
+ },
+ "role" : {
+ "breadcrumb": "Detail of role {{roleName}}",
+ "tab-general": "General settings",
+ "tab-permissions": "Permissions",
+ "tab-caches": "Accessible caches",
+ "loading": "Loading role {{roleName}}",
+ "error": "An error occurred while retrieving the role {{roleName}}",
+ "implicit-warning": "The general settings for predefined roles are not editable.",
+ "permission-name": "Permission name",
+ "permission-description": "Description",
+ "add-permission-button": "Add permission",
+ "permissions-search-placeholder": "Filter by name",
+ "no-permissions-found": "No permission found",
+ "no-filtered-permissions-body": "No permission match your search criteria."
}
}
}
diff --git a/src/app/routes.tsx b/src/app/routes.tsx
index b30efe4ef..8f165749d 100644
--- a/src/app/routes.tsx
+++ b/src/app/routes.tsx
@@ -19,6 +19,7 @@ import { ConsoleServices } from '@services/ConsoleServices';
import { ConsoleACL } from '@services/securityService';
import { NotAuthorized } from '@app/NotAuthorized/NotAuthorized';
import { NotFound } from '@app/NotFound/NotFound';
+import { RoleDetail } from '@app/AccessManagement/RoleDetail';
let routeFocusTimer: number;
@@ -186,7 +187,16 @@ const routes: IAppRoute[] = [
title: 'Connected Clients',
menu: true,
admin: true
- }
+ },
+ {
+ component: RoleDetail,
+ exact: true,
+ label: 'Role detail',
+ path: '/role/:roleName',
+ title: 'Role detail',
+ menu: false,
+ admin: true
+ },
];
const AppRoutes = (props: { init: string }) => {
diff --git a/src/app/services/rolesHook.ts b/src/app/services/rolesHook.ts
index 750fb099b..8b7dd8a56 100644
--- a/src/app/services/rolesHook.ts
+++ b/src/app/services/rolesHook.ts
@@ -77,3 +77,29 @@ export function useDeleteRole(roleName: string, call: () => void) {
onDeleteRole
};
}
+
+export function useDescribeRole(roleName: string) {
+ const [role, setRole] = useState();
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState('');
+
+ useEffect(() => {
+ if (loading) {
+ ConsoleServices.security()
+ .describeRole(
+ roleName
+ )
+ .then((either) => {
+ if (either.isRight()) {
+ setRole(either.value);
+ } else {
+ setError(either.value.message);
+ }
+ }).finally(() => setLoading(false));
+ }
+ }, [loading])
+
+ return {
+ role, loading, error, setLoading
+ };
+}
diff --git a/src/services/displayUtils.ts b/src/services/displayUtils.ts
index 44b8664b1..d5a592f89 100644
--- a/src/services/displayUtils.ts
+++ b/src/services/displayUtils.ts
@@ -465,12 +465,12 @@ class DisplayUtils {
return featureChipGroup;
}
- public formatContentToDisplay(content: any, contentType?: ContentType): string {
+ public formatContentToDisplay(content: never, contentType?: ContentType): string {
if (!contentType || contentType == ContentType.JSON || contentType == ContentType.customType) {
// Try parse and stringify
try {
return JSON.stringify(JSON.parse(content), null, 2);
- } catch (err) {}
+ } catch (err) { /* empty */ }
}
return content as string;
diff --git a/src/services/infinispanRefData.ts b/src/services/infinispanRefData.ts
index 65ccb08c7..dfe520e3a 100644
--- a/src/services/infinispanRefData.ts
+++ b/src/services/infinispanRefData.ts
@@ -1,11 +1,4 @@
-import {
- fileStore,
- jdbcStore,
- querySqlStore,
- remoteStore,
- rocksDB,
- tableSqlStore
-} from '@app/utils/persistentStorageTemplate';
+import { fileStore, jdbcStore, querySqlStore, remoteStore, tableSqlStore } from '@app/utils/persistentStorageTemplate';
export enum ComponentHealth {
HEALTHY = 'HEALTHY',
@@ -247,3 +240,24 @@ export enum RoleFilterOption {
cacheManagerPermissions = 'Cache Manager Permissions',
cachePermissions = 'Cache Permissions'
}
+
+export enum Permission {
+ ALL = 'all',
+}
+
+export const PERMISSIONS_MAP = new Map([
+ ["ALL", "access-management.roles.permission-all"],
+ ["ADMIN", "access-management.roles.permission-admin"],
+ ["ALL_READ", "access-management.roles.permission-all-read"],
+ ["READ", "access-management.roles.permission-read"],
+ ["BULK_READ", "access-management.roles.permission-bulk-read"],
+ ["ALL_WRITE", "access-management.roles.permission-all-write"],
+ ["WRITE", "access-management.roles.permission-write"],
+ ["BULK_WRITE", "access-management.roles.permission-bulk-write"],
+ ["MONITOR", "access-management.roles.permission-monitor"],
+ ["CREATE", "access-management.roles.permission-create"],
+ ["EXEC", "access-management.roles.permission-exec"],
+ ["LISTEN", "access-management.roles.permission-listen"],
+ ["LIFECYCLE", "access-management.roles.permission-lifecycle"],
+ ["NONE", "access-management.roles.permission-none"],
+]);
diff --git a/src/services/securityService.ts b/src/services/securityService.ts
index 9a62a4b72..61f2b3096 100644
--- a/src/services/securityService.ts
+++ b/src/services/securityService.ts
@@ -178,6 +178,21 @@ export class SecurityService {
);
}
+ /**
+ * Retrieve security roles
+ *
+ */
+ public async describeRole(roleName: string): Promise> {
+ return this.fetchCaller.get(this.endpoint + '/permissions/' + roleName, (data) =>
+ {
+ name: roleName,
+ description: data.description,
+ permissions: data.permissions,
+ implicit: data.implicit
+ }
+ );
+ }
+
/**
* Created a new role
* @param roleName
|