Closed gdiazlo closed 1 year ago
Investigate: menu as a standalone plugin
The platform menu displays all the registered and visible applications. A plugin can register any number of applications in the platform menu. The app registration needs to define what will be rendered when the user access to the menu.
This means that if we do a plugin to register all the applications, then it must have access to the render methods of them. The rendering method can come from other plugins or itself. If they come from other plugins, this creates more dependency between these plugins.
The menu as a standalone plugin can need more logic than if each plugin manages the application registration.
I think that @asteriscos was working in a POC to register multiple applications. It would be interesting to know what approach followed.
Adding multiple applications breaks some features related to:
disabled_roles
setting of the wazuh.yml
extensions.*
in wazuh.yml
Settings > Modules
Settings > Modules is not included in the proposition.
Other problems related to the proposition:
The current plugin is a monolith and this can generate multiple problems if we want to decouple into different applications if this architecture is kept as mentioned ones:
This is a page that does some checks and creates or updates some elements that are needed to work the plugin.
Each route of the current plugin can do some checks. If some of them fail, then a redirection to the health check page will be done. This means we should decide how to accommodate the health check in the different applications. It could be a different application or a route in each future application if we want to keep the current logic.
I am not sure what it is referring to.
The current plugin uses as base AngularJS, despite part of the application being migrated to ReactJS. The routing is done with AngularJS.
The Events
tab of each module uses the URL to set the filters.
@asteriscos and I were researching how to create the applications in the side menu and clicking on them redirects to them.
This approach does the new applications redirect to the monolith plugin when the link in the side menu is clicked on. The monolith plugin should be hidden.
Pros:
Cons:
I found a problem with this approach that causes the plugin to be initialized each time the user uses the side menu links to navigate to the applications. This means the Redux store is reset. For some application flow, as the selected agent, is important to keep this state between the navigation. There are more states that should be kept in the navigation. To work around this, we should init the Redux store with a similar state to the previous one.
Create the application that redirects to the monolith application:
core.application.register({
id,
title,
mount: async (params: AppMountParameters) => {
window.location.href = href; // e.g.: /app/wazuh#/agents
},
category,
});
Hide the monolith application:
import {
AppNavLinkStatus,
} from '../../../src/core/public';
core.application.register({
id: `wazuh`,
title: 'Wazuh',
navLinkStatus: AppNavLinkStatus.hidden,
// rest of properties
})
Add applications to base Management
category:
import { DEFAULT_APP_CATEGORIES,} from '../../../src/core/public';
core.application.register({
id,
title,
mount: async (params: AppMountParameters) => {
window.location.href = href;
},
category: DEFAULT_APP_CATEGORIES.management,
})
This approach generates the routes of each application dynamically. The default route is changed to the current route that should be visible.
Pros:
Cons:
Change the routes dynamically:
// public/services/routes.ts
const redirectTo: getApplicationRedirectTo()
// router definition
.when('/', {
redirectTo,
outerAngularWrapperRoute: true,
})
.when('', {
redirectTo,
outerAngularWrapperRoute: true,
})
.otherwise({
redirectTo,
outerAngularWrapperRoute: true,
});
getRootRedirectTo is a getter. Use a setter when the application is mounted in the mount
method:
// public/plugin.ts
mount: (params){
setApplicationRedirectTo(redirectToRoot);
// rest of logic
}
Create the getter/setter:
// public/kibana_services.js
export const [getApplicationRedirectTo, setApplicationRedirectTo] =
createGetterSetter<any>('WzApplicationRedirectTo');
I was testing this trying to keep the shared state of the selected agent that is stored in Redux store. When I change from a Wazuh application to another one, despite the agentId parameter being added to the URL, it disappears due to the page being refreshed https://github.com/wazuh/wazuh-kibana-app/blob/4.6.0/plugins/main/public/plugin.ts#L96
window.location.reload()
Reloading the page is used in other situations. So, fixing is not easy because the Redux store is kept, and the page can't be refreshed. Both situations are mutually exclusive.
If we need to want to use the Redux store to share critical state, then it seems the page should not be refreshed. As the user can refresh manually the page, this could cause some problems, so it seems these states should be managed in another way, such as local storage/session storage or URL.
Update 2023/08/21
In a meeting with @asteriscos , we tried to use the session store for the selected agent. So the selected agent is stored in the Redux store and session storage. If the page is refreshed (by the user or the application itself), then the Redux store will use the value of the selected agent in the session storage to initiate the Redux store. A mechanism that exists in the current application will set the agentId
query parameter in the URL if the agent data is stored in the Redux store. We will continue with this approach.
// plugins/main/public/redux/reducers/appStateReducers.js
const initialState = {
currentAPI: '',
showMenu: false,
wazuhNotReadyYet: '',
currentTab: '',
extensions: {},
selected_settings_section: '',
currentPlatform: false,
currentAgentData: JSON.parse(
window.sessionStorage.getItem('wz-shared-selected-agent') || '{}',
),
showExploreAgentModal: false,
showExploreAgentModalGlobal: false,
userPermissions: false,
userRoles: [],
toastNotification: false,
withUserLogged: false,
allowedAgents: [],
logtestToken: '',
};
// ....
if (action.type === 'UPDATE_SELECTED_AGENT_DATA') {
window.sessionStorage.setItem(
'wz-shared-selected-agent',
JSON.stringify(action.currentAgentData),
);
return {
...state,
currentAgentData: action.currentAgentData,
};
}
Following the approach: https://github.com/wazuh/wazuh-dashboard/issues/83#issuecomment-1683978025
Base development branch: feat/83-move-menu-items-to-applications
A plugin registers each application in the side menu. The initial solution uses the same plugin, and we don't consider to be needed another plugin to register the application in the side menu because it creates more complexity. Each application mounts the monolith application whose entry point is modified to display the expected view.
Tabs in Server Management - RBAC don't work
When you want to navigate between the Endpoint security, Thread intelligence, Security operations and Cloud security modules and go through the heath check instead of redirecting to the desired view, it redirects to the overview.
On the other hand, changing views is changing the plugin, as the URL changes and the application takes longer to load the views, so it slows down the application and in my environment it doesn't have much data to load.
Tabs in Server Management - RBAC don't work
Fixed here: https://github.com/wazuh/wazuh-kibana-app/commit/bc7739fc3554614a2c896e1d50c818f515286fc1
Category | Name | Section in monolith plugin |
---|---|---|
Management | Modules | Settings > Modules |
Management | Miscellaneous | Settings > Miscellaneous |
Notes: Settings > Miscellaneus should be moved to Configuration Modules app only contains the visibility of some applications. This should be refactored to support all the applications.
When we click on the eye icon to navigate to Agent-preview, we go to /app/wz-groups#/agents?_g=(filters
, using monolithic logic. The function that works over the eye button is showAgent() which works like:
The expected result is to navigate to the Endpoints summary:
But keep into Groups
We can find that code in → plugins/main/public/controllers/management/groups.js That code modified the URL after "#" to navigate a new web section, but it doesn't work in the new menu bar logic, because we navigate across the menu bar.
To fix it, there are 2 main branches; create a new function that works in all redirection code lines, or change old logic code lines with a HREF.
When we click on the settings button, we go to /app/wz-statistics#/settings?tab=configuration&category=statistics
, using monolithic logic. The function that works over the button is navigateToModule() which works like:
The expected result is to navigate to Configuration:
But keep in Statistics
We can find that code in → plugins/main/public/controllers/management/components/management/statistics/statistics-overview.js
That code modified the URL after "#" to navigate a new web section, but it doesn't work in the new menu bar logic, because we navigate across the menu bar.
To fix it, there are 2 main branches; create a new function that works in all redirection code lines, or change old logic code lines with a href.
When we click on the Agents button, we go to /app/wz-cluster#/agents-preview/?_g=(filters:
, using monolithic logic. The function that works over the button is a href.
The expected result is to navigate to Configuration:
But keep in Statistics
We can find that code in → plugins/main/public/controllers/management/monitoring.js
That code modified the URL with a href. but that means that there are at least 3 different ways to navigate over the different parts of the app.
To fix it there are 2 main branches; create a new function that works in all redirection code lines, or change old logic code lines on the href.
If we fix those changes all navigates code lines to hrefs the code will be too much dirt, making it more difficult to fix bugs in the future, and making the code less reusable. Conversely, it means that we have to take more time to make reusable and cleaner code.
Research the impact of: API hosts and valid index patterns are updated when opening or closing the current menu. Should it behavior keep and how?
Requests are made to update the index pattern and apis selectors. This is not very reliable because if the user adds an index pattern or a new api and opens the selector, he will not see the changes until he opens the menu or refreshes the page. With the index pattern it would only affect if you have more than 1 window open with Wazuh, because if the user only handles 1 window when adding an index pattern and then navigates to another view, the requests would be executed when mounting the component. For me, it is better to add to the documentation that after adding a new api or index pattern you have to restart the browser, and save us from making those requests every so often or when the user makes a specific action. We can also use the onMouseEnter function to execute the request when the user puts the mouse over the selector, but this may cause unnecessary requests.
Category | Name | Section in monolith plugin |
---|---|---|
Endpoint security | Overview | Modules directory |
We can also use the onMouseEnter function to execute the request when the user puts the mouse over the selector, but this may cause unnecessary requests.
Problems
Comment
Introduction
This UX redesign tier 1 scope involves mostly a new general menu for the whole interface.
Now that we have control over the complete dashboard, we want to improve the user experience, making it easier to access Wazuh features. Our first step will be to create a new menu experience in line with what the platform is designed to work with: the left navigation drawer.
Our current menu is embedded into our main application in the top bar:
We want to incorporate this accesses into the global navigation drawer, transforming the current look (on the left) to something like:
Of these, we want to pursue the 1st one, as it allows us to implement the global navigation drawer in a simple and fast pace manner, and allows us to evolve the main plugin with time, iteratively, instead of a big bang devel approach, unlike option 2 and 3.
It is important that we leverage the built-in styles and looks so that our menu can evolve with the new upstream changes which are on their way (for example https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4298).
We must customize the icons for each category. The UI library provides a set of icons we could use.
It's also important to study what would happen in the case of newer applications arriving from upstream that use the same categories as we propose here.
Functional requirements
Non-functional requirements
Plan