Skip to content

Commit

Permalink
[serverless] Create Security Serverless plugin (#156104)
Browse files Browse the repository at this point in the history
> Derived from #153274
> Builds upon #155582

## Summary

This PR creates the Serverless Security plugin, based on the work from
#153274:

- creates the plugin,
- adds API to hide the solution navigation from Security,
- calls that API if the chrome style is `project`.

<img width="1688" alt="Screenshot 2023-04-27 at 12 37 46 PM"
src="https://user-images.githubusercontent.com/297604/234979670-425bfb12-8194-4916-8f92-efff7804b577.png">

## Next Steps

- render the left nav from #153274
using an API provided by @elastic/appex-sharedux
  - this low-level API should be coming in the next few days.

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
clintandrewhall and kibanamachine authored May 2, 2023
1 parent a16930f commit b217dbf
Show file tree
Hide file tree
Showing 49 changed files with 568 additions and 248 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ x-pack/plugins/serverless @elastic/appex-sharedux
x-pack/plugins/serverless_observability @elastic/appex-sharedux
packages/serverless/project_switcher @elastic/appex-sharedux
x-pack/plugins/serverless_search @elastic/appex-sharedux
x-pack/plugins/serverless_security @elastic/appex-sharedux
packages/serverless/storybook/config @elastic/appex-sharedux
packages/serverless/types @elastic/appex-sharedux
test/plugin_functional/plugins/session_notifications @elastic/kibana-core
Expand Down
14 changes: 14 additions & 0 deletions config/serverless.security.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# Security Project config

## Disable plugins
enterpriseSearch.enabled: false
xpack.apm.enabled: false
xpack.observability.enabled: false
xpack.uptime.enabled: false

## Enable the Serverless Security plugin
xpack.serverless.security.enabled: true

## Set the home route
uiSettings.overrides.defaultRoute: /app/security/get_started

## Set the dev project switcher current type
xpack.serverless.plugin.developer.projectSwitcher.currentType: 'security'
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,10 @@ Kibana.
|This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana.
|{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.mdx[serverlessSecurity]
|This plugin contains configuration and code used to create a Serverless Security project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana.
|{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView]
|Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@
"@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability",
"@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher",
"@kbn/serverless-search": "link:x-pack/plugins/serverless_search",
"@kbn/serverless-security": "link:x-pack/plugins/serverless_security",
"@kbn/serverless-types": "link:packages/serverless/types",
"@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications",
"@kbn/session-view-plugin": "link:x-pack/plugins/session_view",
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pageLoadAssetSize:
serverless: 16573
serverlessObservability: 16582
serverlessSearch: 17548
serverlessSecurity: 41807
sessionView: 77750
share: 71239
snapshotRestore: 79032
Expand Down
11 changes: 1 addition & 10 deletions src/plugins/management/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@ export const renderApp = async (
{ history, appBasePath, element, theme$ }: AppMountParameters,
dependencies: ManagementAppDependencies
) => {
ReactDOM.render(
<ManagementApp
dependencies={dependencies}
appBasePath={appBasePath}
history={history}
theme$={theme$}
/>,
element
);

ReactDOM.render(<ManagementApp {...{ history, appBasePath, theme$, dependencies }} />, element);
return () => ReactDOM.unmountComponentAtNode(element);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
import './management_app.scss';

import React, { useState, useEffect, useCallback } from 'react';
import { BehaviorSubject } from 'rxjs';

import { I18nProvider } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from '@kbn/core/public';

import { reactRouterNavigate, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { KibanaPageTemplate, KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import useObservable from 'react-use/lib/useObservable';
import {
ManagementSection,
MANAGEMENT_BREADCRUMB,
Expand All @@ -34,12 +37,14 @@ export interface ManagementAppDependencies {
sections: SectionsServiceStart;
kibanaVersion: string;
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
isSidebarEnabled$: BehaviorSubject<boolean>;
}

export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppProps) => {
const { setBreadcrumbs } = dependencies;
const { setBreadcrumbs, isSidebarEnabled$ } = dependencies;
const [selectedId, setSelectedId] = useState<string>('');
const [sections, setSections] = useState<ManagementSection[]>();
const isSidebarEnabled = useObservable(isSidebarEnabled$);

const onAppMounted = useCallback((id: string) => {
setSelectedId(id);
Expand Down Expand Up @@ -75,18 +80,20 @@ export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppPr
return null;
}

const solution: KibanaPageTemplateProps['solutionNav'] = {
name: i18n.translate('management.nav.label', {
defaultMessage: 'Management',
}),
icon: 'managementApp',
'data-test-subj': 'mgtSideBarNav',
items: managementSidebarNav({
selectedId,
sections,
history,
}),
};
const solution: KibanaPageTemplateProps['solutionNav'] | undefined = isSidebarEnabled
? {
name: i18n.translate('management.nav.label', {
defaultMessage: 'Management',
}),
icon: 'managementApp',
'data-test-subj': 'mgtSideBarNav',
items: managementSidebarNav({
selectedId,
sections,
history,
}),
}
: undefined;

return (
<I18nProvider>
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/management/public/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const createSetupContract = (): ManagementSetup => ({
});

const createStartContract = (): ManagementStart => ({
sections: {},
setIsSidebarEnabled: jest.fn(),
});

export const managementPluginMock = {
Expand Down
11 changes: 9 additions & 2 deletions src/plugins/management/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,14 @@ export class ManagementPlugin

private hasAnyEnabledApps = true;

private isSidebarEnabled$ = new BehaviorSubject<boolean>(true);

constructor(private initializerContext: PluginInitializerContext) {}

public setup(core: CoreSetup, { home, share }: ManagementSetupDependencies) {
const kibanaVersion = this.initializerContext.env.packageInfo.version;
const locator = share.url.locators.create(new ManagementAppLocatorDefinition());
const managementPlugin = this;

if (home) {
home.featureCatalogue.register({
Expand Down Expand Up @@ -111,6 +114,7 @@ export class ManagementPlugin
sections: getSectionsServiceStartPrivate(),
kibanaVersion,
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
isSidebarEnabled$: managementPlugin.isSidebarEnabled$,
});
},
});
Expand All @@ -121,7 +125,7 @@ export class ManagementPlugin
};
}

public start(core: CoreStart, plugins: ManagementStartDependencies) {
public start(core: CoreStart, _plugins: ManagementStartDependencies): ManagementStart {
this.managementSections.start({ capabilities: core.application.capabilities });
this.hasAnyEnabledApps = getSectionsServiceStartPrivate()
.getSectionsEnabled()
Expand All @@ -136,6 +140,9 @@ export class ManagementPlugin
});
}

return {};
return {
setIsSidebarEnabled: (isSidebarEnabled: boolean) =>
this.isSidebarEnabled$.next(isSidebarEnabled),
};
}
}
5 changes: 3 additions & 2 deletions src/plugins/management/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ export interface DefinedSections {
stack: ManagementSection;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ManagementStart {}
export interface ManagementStart {
setIsSidebarEnabled: (enabled: boolean) => void;
}

export interface ManagementSectionsStartPrivate {
getSectionsEnabled: () => ManagementSection[];
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,8 @@
"@kbn/serverless-project-switcher/*": ["packages/serverless/project_switcher/*"],
"@kbn/serverless-search": ["x-pack/plugins/serverless_search"],
"@kbn/serverless-search/*": ["x-pack/plugins/serverless_search/*"],
"@kbn/serverless-security": ["x-pack/plugins/serverless_security"],
"@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"],
"@kbn/serverless-storybook-config": ["packages/serverless/storybook/config"],
"@kbn/serverless-storybook-config/*": ["packages/serverless/storybook/config/*"],
"@kbn/serverless-types": ["packages/serverless/types"],
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// TODO(jbudz): should be removed when upgrading to [email protected]
// this is a skip for the errors created when typechecking with isolatedModules
export {};
export { APP_UI_ID, SecurityPageName } from './constants';
export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants';

// Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ import {
USERS_PATH,
} from '../../../common/constants';
import type { ExperimentalFeatures } from '../../../common/experimental_features';
import { hasCapabilities, subscribeAppLinks } from '../../common/links';
import { appLinks$, hasCapabilities } from '../../common/links';
import type { AppLinkItems } from '../../common/links/types';

export const FEATURE = {
Expand Down Expand Up @@ -630,7 +630,7 @@ const formatDeepLinks = (appLinks: AppLinkItems): AppDeepLink[] =>
* Registers any change in appLinks to be updated in app deepLinks
*/
export const registerDeepLinksUpdater = (appUpdater$: Subject<AppUpdater>): Subscription => {
return subscribeAppLinks((appLinks) => {
return appLinks$.subscribe((appLinks) => {
appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent main security link to switch to visible after update
deepLinks: formatDeepLinks(appLinks),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ jest.mock('../../../links', () => ({
getAncestorLinksInfo: (id: string) => [{ id }],
}));

const mockUseAppNavLinks = jest.fn();
jest.mock('../nav_links', () => ({
useAppNavLinks: () => mockUseAppNavLinks(),
const mockUseNavLinks = jest.fn();
jest.mock('../../../links/nav_links', () => ({
useNavLinks: () => mockUseNavLinks(),
}));
jest.mock('../../links', () => ({
useGetSecuritySolutionLinkProps:
Expand All @@ -80,14 +80,14 @@ const renderNav = () =>
describe('SecuritySideNav', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseAppNavLinks.mockReturnValue([alertsNavLink, manageNavLink]);
mockUseNavLinks.mockReturnValue([alertsNavLink, manageNavLink]);
useKibana().services.chrome.hasHeaderBanner$ = jest.fn(() =>
new BehaviorSubject(false).asObservable()
);
});

it('should render main items', () => {
mockUseAppNavLinks.mockReturnValue([alertsNavLink]);
mockUseNavLinks.mockReturnValue([alertsNavLink]);
renderNav();
expect(mockSolutionSideNav).toHaveBeenCalledWith({
selectedId: SecurityPageName.alerts,
Expand All @@ -104,7 +104,7 @@ describe('SecuritySideNav', () => {
});

it('should render the loader if items are still empty', () => {
mockUseAppNavLinks.mockReturnValue([]);
mockUseNavLinks.mockReturnValue([]);
const result = renderNav();
expect(result.getByTestId('sideNavLoader')).toBeInTheDocument();
expect(mockSolutionSideNav).not.toHaveBeenCalled();
Expand All @@ -121,7 +121,7 @@ describe('SecuritySideNav', () => {
});

it('should render footer items', () => {
mockUseAppNavLinks.mockReturnValue([manageNavLink]);
mockUseNavLinks.mockReturnValue([manageNavLink]);
renderNav();
expect(mockSolutionSideNav).toHaveBeenCalledWith(
expect.objectContaining({
Expand All @@ -148,7 +148,7 @@ describe('SecuritySideNav', () => {
});

it('should not render disabled items', () => {
mockUseAppNavLinks.mockReturnValue([{ ...alertsNavLink, disabled: true }, manageNavLink]);
mockUseNavLinks.mockReturnValue([{ ...alertsNavLink, disabled: true }, manageNavLink]);
renderNav();
expect(mockSolutionSideNav).toHaveBeenCalledWith(
expect.objectContaining({
Expand All @@ -163,7 +163,7 @@ describe('SecuritySideNav', () => {
});

it('should render custom item', () => {
mockUseAppNavLinks.mockReturnValue([{ id: SecurityPageName.landing, title: 'get started' }]);
mockUseNavLinks.mockReturnValue([{ id: SecurityPageName.landing, title: 'get started' }]);
renderNav();
expect(mockSolutionSideNav).toHaveBeenCalledWith(
expect.objectContaining({
Expand Down
Loading

0 comments on commit b217dbf

Please sign in to comment.