Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solutions] Add PLI authorisation for Threat Intelligence #162562

Merged
merged 7 commits into from
Aug 1, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export enum SecurityPageName {
* Warning: Computed values are not permitted in an enum with string valued members
* All threat intelligence page names must match `TIPageId` in x-pack/plugins/threat_intelligence/public/common/navigation/types.ts
*/
threatIntelligenceIndicators = 'threat_intelligence-indicators',
threatIntelligence = 'threat_intelligence',
timelines = 'timelines',
timelinesTemplates = 'timelines-templates',
trustedApps = 'trusted_apps',
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/security_solution/common/types/app_features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export enum AppFeatureSecurityKey {
* Enables Endpoint Exceptions like isolate host, trusted apps, blocklist, etc.
*/
endpointExceptions = 'endpoint_exceptions',

/**
* Enables Threat Intelligence
*/
threatIntelligence = 'threat-intelligence',
}

export enum AppFeatureCasesKey {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ export const NETWORK = '[data-test-subj="solutionSideNavPanelLink-network"]';

export const USERS = '[data-test-subj="solutionSideNavPanelLink-users"]';

export const INDICATORS =
'[data-test-subj="solutionSideNavItemLink-threat_intelligence-indicators"]';
export const INDICATORS = '[data-test-subj="solutionSideNavItemLink-threat_intelligence"]';

export const RULES = '[data-test-subj="solutionSideNavPanelLink-rules"]';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [
type: LinkCategoryType.separator,
linkIds: [
SecurityPageName.timelines,
SecurityPageName.threatIntelligenceIndicators,
SecurityPageName.threatIntelligence,
SecurityPageName.exploreLanding,
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const ThreatIntelPanelView: React.FC<LinkPanelViewProps> = ({
() => (
<SecuritySolutionLinkButton
data-test-subj="cti-view-indicators"
deepLinkId={SecurityPageName.threatIntelligenceIndicators}
deepLinkId={SecurityPageName.threatIntelligence}
>
<FormattedMessage
id="xpack.securitySolution.overview.threatIndicatorsAction"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ import type { LinkItem } from '../common/links';
export const indicatorsLinks: LinkItem = {
...getSecuritySolutionLink<SecurityPageName>('indicators'),
globalNavPosition: 7,
capabilities: [`${SERVER_APP_ID}.show`],
capabilities: [`${SERVER_APP_ID}.threat-intelligence`],
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import React, { memo } from 'react';
import { TrackApplicationView } from '@kbn/usage-collection-plugin/public';
import type { SecuritySolutionPluginContext } from '@kbn/threat-intelligence-plugin/public';
import { THREAT_INTELLIGENCE_BASE_PATH } from '@kbn/threat-intelligence-plugin/public';
import type { SourcererDataView } from '@kbn/threat-intelligence-plugin/public/types';
Expand All @@ -31,6 +30,7 @@ import { useGlobalTime } from '../common/containers/use_global_time';
import { deleteOneQuery, setQuery } from '../common/store/inputs/actions';
import { InputsModelId } from '../common/store/inputs/constants';
import { ArtifactFlyout } from '../management/components/artifact_list_page/components/artifact_flyout';
import { SecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper';

const ThreatIntelligence = memo(() => {
const { threatIntelligence, http } = useKibana().services;
Expand Down Expand Up @@ -85,10 +85,10 @@ const ThreatIntelligence = memo(() => {
};

return (
<TrackApplicationView viewId="threat_intelligence">
<SecurityRoutePageWrapper pageName={SecurityPageName.threatIntelligence}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecurityRoutePageWrapper is necessary for displaying the Upsell page.

<ThreatIntelligencePlugin securitySolutionContext={securitySolutionContext} />
<SpyRoute pageName={SecurityPageName.threatIntelligenceIndicators} />
</TrackApplicationView>
<SpyRoute pageName={SecurityPageName.threatIntelligence} />
</SecurityRoutePageWrapper>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,19 @@ export const getSecurityAppFeaturesConfig = (
},
},

[AppFeatureSecurityKey.threatIntelligence]: {
privileges: {
all: {
ui: ['threat-intelligence'],
api: [`${APP_ID}-threat-intelligence`],
},
read: {
ui: ['threat-intelligence'],
api: [`${APP_ID}-threat-intelligence`],
},
},
},

[AppFeatureSecurityKey.endpointResponseActions]: {
subFeatureIds: [
SecuritySubFeatureId.processOperations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ type PliAppFeatures = Readonly<
export const PLI_APP_FEATURES: PliAppFeatures = {
security: {
essentials: [],
complete: [AppFeatureKey.advancedInsights, AppFeatureKey.casesConnectors],
complete: [
AppFeatureKey.advancedInsights,
AppFeatureKey.threatIntelligence,
AppFeatureKey.casesConnectors,
],
},
endpoint: {
essentials: [AppFeatureKey.endpointExceptions],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [
type: LinkCategoryType.separator,
linkIds: [
SecurityPageName.timelines,
SecurityPageName.threatIntelligenceIndicators,
SecurityPageName.threatIntelligence,
SecurityPageName.exploreLanding,
],
},
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of how we're going to handle things for ESS and Serverless, but I'm wondering why do we duplicate this component here? We already have a paywal component in the Threat Intelligence plugin. Shouldn't we make that one smarter and handle both ESS and Serverless?
With the current approach, we basically make the paywall component within the Threat Intelligence plugin useless when loaded in serverless mode... but the component is still there.
It seems confusing to me that we'd have similar things in different places

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approaches to authorization for ESS and Serverless are fundamentally different. ESS has several "if platinum" checks throughout the app, whereas Serverless has a centralized, privilege-based system. We can utilize the security_solutions_serverless plugin to register Upselling pages or components and retrieve them with hooks when the page is unauthorized.

While it's possible to repurpose the paywal component (if it's accessible on the serverless plugin) and make it more intelligent, the UX team is still working on the final design for serverless Upselling. It's conceivable that there will be a generic Upselling component for all features, so I don't believe it's necessary to refactor the paywal component at this time. But we need to revisit this decision after the designs are ready.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, thanks for explaining!

I think ultimately we should aim at having a similar way to manage these paywall between ESS and Servesless, but I guess this is a discussion for another time :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am currently implementing your suggestion at the following PR: #163406.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiEmptyPrompt, EuiIcon } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import type { AppFeatureKey } from '@kbn/security-solution-plugin/common';
import { useProductTypeByPLI } from '../hooks/use_product_type_by_pli';

const ThreatIntelligencePaywall: React.FC<{ requiredPLI: AppFeatureKey }> = React.memo(
function PaywallComponent({ requiredPLI }) {
const productTypeRequired = useProductTypeByPLI(requiredPLI);

return (
machadoum marked this conversation as resolved.
Show resolved Hide resolved
<EuiEmptyPrompt
icon={<EuiIcon type="logoSecurity" size="xl" />}
color="subdued"
title={
<h2>
<FormattedMessage
id="xpack.securitySolutionServerless.threatIntelligence.paywall.title"
defaultMessage="Do more with Security!"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="xpack.securitySolutionServerless.threatIntelligence.paywall.body"
defaultMessage="Upgrade your license to {productTypeRequired} to use threat intelligence."
values={{ productTypeRequired }}
/>
</p>
}
/>
);
}
);

ThreatIntelligencePaywall.displayName = 'ThreatIntelligencePaywall';

// eslint-disable-next-line import/no-default-export
export { ThreatIntelligencePaywall as default };
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { SecurityPageName, AppFeatureKey } from '@kbn/security-solution-plugin/common';
import { SecurityPageName, AppFeatureKey } from '@kbn/security-solution-plugin/common';
import type {
UpsellingService,
PageUpsellings,
SectionUpsellings,
UpsellingSectionId,
} from '@kbn/security-solution-plugin/public';
import React, { lazy } from 'react';
import type { SecurityProductTypes } from '../../common/config';
import { getProductAppFeatures } from '../../common/pli/pli_features';

const ThreatIntelligencePaywallLazy = lazy(() => import('./pages/threat_intelligence_paywall'));
interface UpsellingsConfig {
pli: AppFeatureKey;
component: React.ComponentType;
Expand Down Expand Up @@ -61,6 +63,13 @@ export const upsellingPages: UpsellingPages = [
// pli: AppFeatureKey.advancedInsights,
// component: () => <GenericUpsellingPageLazy requiredPLI={AppFeatureKey.advancedInsights} />,
// },
{
pageName: SecurityPageName.threatIntelligence,
pli: AppFeatureKey.threatIntelligence,
component: () => (
<ThreatIntelligencePaywallLazy requiredPLI={AppFeatureKey.threatIntelligence} />
),
},
];

// Upsellings for sections, linked by arbitrary ids
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@

export const UPDATE_STATUS = `[data-test-subj="updateStatus"]`;
export const SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM = `[data-test-subj="solutionSideNavItemLink-administration"]`;
export const SECURITY_SOLUTION_NAVBAR_THREAT_INTELLIGENCE_ITEM = `[data-test-subj="solutionSideNavItemLink-threat_intelligence-indicators"]`;
export const SECURITY_SOLUTION_NAVBAR_THREAT_INTELLIGENCE_ITEM = `[data-test-subj="solutionSideNavItemLink-threat_intelligence"]`;
export const MANAGE_NAVIGATION_ITEMS = `.euiLink`;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const threatIntelligencePages: Record<TIPage, TIPageProperties> = {
oldNavigationName: INDICATORS,
newNavigationName: INTELLIGENCE,
path: `${THREAT_INTELLIGENCE_BASE_PATH}/indicators`,
id: 'threat_intelligence-indicators',
id: 'threat_intelligence',
description: DESCRIPTION,
globalSearchKeywords: [INTELLIGENCE],
keywords: [INTELLIGENCE],
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/threat_intelligence/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ export type TIPage = 'indicators';
* This needs to match the threat intelligence page entries in SecurityPageName` (x-pack/plugins/security_solution/common/constants.ts).
*
* Example to add more IDs:
* export type TIPageId = 'threat_intelligence-indicators' | 'threat_intelligence-feed';
* export type TIPageId = 'threat_intelligence' | 'threat_intelligence-feed';
*/
export type TIPageId = 'threat_intelligence-indicators';
export type TIPageId = 'threat_intelligence';

/**
* A record of all the properties that will be used to build deeplinks, links and navtabs objects.
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -4996,7 +4996,6 @@
"savedObjectsManagement.view.indexPatternDoesNotExistErrorMessage": "La vue de données associée à cet objet n'existe plus.",
"savedObjectsManagement.view.savedObjectProblemErrorMessage": "Un problème est survenu avec cet objet enregistré.",
"savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage": "La recherche enregistrée associée à cet objet n'existe plus.",
"savedSearch.legacyURLConflict.errorMessage": "Cette recherche a la même URL qu'un alias hérité. Désactiver l'alias pour résoudre cette erreur : {json}",
"securitySolutionPackages.dataTable.eventsTab.unit": "{totalCount, plural, =1 {alerte} one {alertes} many {alertes} other {alertes}}",
"securitySolutionPackages.dataTable.unit": "{totalCount, plural, =1 {alerte} one {alertes} many {alertes} other {alertes}}",
"securitySolutionPackages.ecsDataQualityDashboard.allTab.allFieldsTableTitle": "Tous les champs – {indexName}",
Expand Down