Skip to content

Commit

Permalink
Change new schema
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas committed May 23, 2024
1 parent 5184d21 commit 3b7c89f
Show file tree
Hide file tree
Showing 18 changed files with 235 additions and 196 deletions.
45 changes: 25 additions & 20 deletions x-pack/examples/alerting_example/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,27 @@ export class AlertingExamplePlugin implements Plugin<void, void, AlertingExample
insightsAndAlerting: ['triggersActions'],
},
category: DEFAULT_APP_CATEGORIES.management,
alerting: {
ruleTypeIds: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
consumers: [ALERTING_EXAMPLE_APP_ID],
},
alerting: [
{ ruleTypeId: alwaysFiringRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: peopleInSpaceRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: INDEX_THRESHOLD_ID, consumers: [ALERTING_EXAMPLE_APP_ID] },
],
privileges: {
all: {
alerting: {
rule: {
all: {
ruleTypeIds: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
consumers: [ALERTING_EXAMPLE_APP_ID],
},
all: [
{ ruleTypeId: alwaysFiringRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: peopleInSpaceRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: INDEX_THRESHOLD_ID, consumers: [ALERTING_EXAMPLE_APP_ID] },
],
},
alert: {
all: {
ruleTypeIds: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
consumers: [ALERTING_EXAMPLE_APP_ID],
},
all: [
{ ruleTypeId: alwaysFiringRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: peopleInSpaceRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: INDEX_THRESHOLD_ID, consumers: [ALERTING_EXAMPLE_APP_ID] },
],
},
},
savedObject: {
Expand All @@ -107,16 +110,18 @@ export class AlertingExamplePlugin implements Plugin<void, void, AlertingExample
read: {
alerting: {
rule: {
read: {
ruleTypeIds: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
consumers: [ALERTING_EXAMPLE_APP_ID],
},
read: [
{ ruleTypeId: alwaysFiringRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: peopleInSpaceRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: INDEX_THRESHOLD_ID, consumers: [ALERTING_EXAMPLE_APP_ID] },
],
},
alert: {
read: {
ruleTypeIds: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
consumers: [ALERTING_EXAMPLE_APP_ID],
},
read: [
{ ruleTypeId: alwaysFiringRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: peopleInSpaceRule.id, consumers: [ALERTING_EXAMPLE_APP_ID] },
{ ruleTypeId: INDEX_THRESHOLD_ID, consumers: [ALERTING_EXAMPLE_APP_ID] },
],
},
},
savedObject: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const SECURITY_RULE_TYPES = [
NEW_TERMS_RULE_TYPE_ID,
];

const alertingFeatures = SECURITY_RULE_TYPES.map((ruleTypeId) => ({
ruleTypeId,
consumers: [SERVER_APP_ID],
}));

export const getSecurityBaseKibanaFeature = ({
savedObjects,
}: SecurityFeatureParams): BaseKibanaFeatureConfig => ({
Expand All @@ -57,7 +62,7 @@ export const getSecurityBaseKibanaFeature = ({
management: {
insightsAndAlerting: ['triggersActions'],
},
alerting: { ruleTypeIds: SECURITY_RULE_TYPES, consumers: [SERVER_APP_ID] },
alerting: alertingFeatures,
privileges: {
all: {
app: [APP_ID, CLOUD_POSTURE_APP_ID, CLOUD_DEFEND_APP_ID, 'kibana'],
Expand All @@ -78,12 +83,8 @@ export const getSecurityBaseKibanaFeature = ({
read: [],
},
alerting: {
rule: {
all: { ruleTypeIds: SECURITY_RULE_TYPES, consumers: [SERVER_APP_ID] },
},
alert: {
all: { ruleTypeIds: SECURITY_RULE_TYPES, consumers: [SERVER_APP_ID] },
},
rule: { all: alertingFeatures },
alert: { all: alertingFeatures },
},
management: {
insightsAndAlerting: ['triggersActions'],
Expand All @@ -100,10 +101,10 @@ export const getSecurityBaseKibanaFeature = ({
},
alerting: {
rule: {
read: { ruleTypeIds: SECURITY_RULE_TYPES, consumers: [SERVER_APP_ID] },
read: alertingFeatures,
},
alert: {
all: { ruleTypeIds: SECURITY_RULE_TYPES, consumers: [SERVER_APP_ID] },
all: alertingFeatures,
},
},
management: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,17 @@ export class AlertingAuthorization {
// ignore features which are disabled in the user's space
!disabledFeatures.has(id) &&
// ignore features which don't grant privileges to alerting
((alerting?.ruleTypeIds?.length ?? 0 > 0) || (alerting?.consumers?.length ?? 0 > 0))
(alerting?.length ?? 0 > 0)
)
);

this.allPossibleConsumers = alertingFeaturesPromise.then((alertingFeatures) => {
const consumers = alertingFeatures
.flatMap((alertingFeature) => alertingFeature.alerting?.consumers)
.filter(Boolean) as string[];
const consumers = alertingFeatures.flatMap(
(alertingFeature) =>
alertingFeature.alerting
?.flatMap((feature) => feature.consumers ?? [])
.filter(Boolean) as string[]
);

return consumers.length
? asAuthorizedConsumers([ALERTING_FEATURE_ID, ...consumers], {
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/features/common/feature_kibana_privileges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface FeatureKibanaPrivileges {
* }
* ```
*/
all?: { ruleTypeIds?: readonly string[]; consumers?: readonly string[] };
all?: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>;
/**
* List of rule types which users should have read-only access to when granted this privilege.
* @example
Expand All @@ -113,7 +113,7 @@ export interface FeatureKibanaPrivileges {
* }
* ```
*/
read?: { ruleTypeIds?: readonly string[]; consumers?: readonly string[] };
read?: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>;
};
alert?: {
/**
Expand All @@ -125,7 +125,7 @@ export interface FeatureKibanaPrivileges {
* }
* ```
*/
all?: { ruleTypeIds?: readonly string[]; consumers?: readonly string[] };
all?: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>;
/**
* List of rule types for which users should have read-only access to their alert data when granted this privilege.
* @example
Expand All @@ -135,7 +135,7 @@ export interface FeatureKibanaPrivileges {
* }
* ```
*/
read?: { ruleTypeIds?: readonly string[]; consumers?: readonly string[] };
read?: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>;
};
};

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/features/common/kibana_feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export interface KibanaFeatureConfig {
* Include both Alert Types registered by the feature and external Alert Types such as built-in
* Alert Types and Alert Types provided by other features to which you wish to grant access.
*/
alerting?: { ruleTypeIds?: readonly string[]; consumers?: readonly string[] };
alerting?: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>;

/**
* If your feature grants access to specific case types, you can specify them here to control visibility based on the current space.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,50 +108,43 @@ function mergeWithSubFeatures(
subFeaturePrivilege.savedObject.read
);

const mergeAlertingEntries = (
entries: ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }>
): ReadonlyArray<{ ruleTypeId: string; consumers: readonly string[] }> => {
const alertingMap = new Map<string, Set<string>>();

for (const entry of entries) {
const consumers = alertingMap.get(entry.ruleTypeId) ?? new Set();
entry.consumers.forEach((consumer) => consumers.add(consumer));
alertingMap.set(entry.ruleTypeId, consumers);
}

return Array.from(alertingMap).map(([ruleTypeId, consumers]) => ({
ruleTypeId,
consumers: Array.from(consumers),
}));
};

mergedConfig.alerting = {
rule: {
all: {
ruleTypeIds: mergeArrays(
mergedConfig.alerting?.rule?.all?.ruleTypeIds ?? [],
subFeaturePrivilege.alerting?.rule?.all?.ruleTypeIds ?? []
),
consumers: mergeArrays(
mergedConfig.alerting?.rule?.all?.consumers ?? [],
subFeaturePrivilege.alerting?.rule?.all?.consumers ?? []
),
},
read: {
ruleTypeIds: mergeArrays(
mergedConfig.alerting?.rule?.read?.ruleTypeIds ?? [],
subFeaturePrivilege.alerting?.rule?.read?.ruleTypeIds ?? []
),
consumers: mergeArrays(
mergedConfig.alerting?.rule?.read?.consumers ?? [],
subFeaturePrivilege.alerting?.rule?.read?.consumers ?? []
),
},
all: mergeAlertingEntries([
...(mergedConfig.alerting?.rule?.all ?? []),
...(subFeaturePrivilege.alerting?.rule?.all ?? []),
]),
read: mergeAlertingEntries([
...(mergedConfig.alerting?.rule?.read ?? []),
...(subFeaturePrivilege.alerting?.rule?.read ?? []),
]),
},
alert: {
all: {
ruleTypeIds: mergeArrays(
mergedConfig.alerting?.alert?.all?.ruleTypeIds ?? [],
subFeaturePrivilege.alerting?.alert?.all?.ruleTypeIds ?? []
),
consumers: mergeArrays(
mergedConfig.alerting?.alert?.all?.consumers ?? [],
subFeaturePrivilege.alerting?.alert?.all?.consumers ?? []
),
},
read: {
ruleTypeIds: mergeArrays(
mergedConfig.alerting?.alert?.read?.ruleTypeIds ?? [],
subFeaturePrivilege.alerting?.alert?.read?.ruleTypeIds ?? []
),
consumers: mergeArrays(
mergedConfig.alerting?.alert?.read?.consumers ?? [],
subFeaturePrivilege.alerting?.alert?.read?.consumers ?? []
),
},
all: mergeAlertingEntries([
...(mergedConfig.alerting?.alert?.all ?? []),
...(subFeaturePrivilege.alerting?.alert?.all ?? []),
]),
read: mergeAlertingEntries([
...(mergedConfig.alerting?.alert?.read ?? []),
...(subFeaturePrivilege.alerting?.alert?.read ?? []),
]),
},
};

Expand Down
49 changes: 28 additions & 21 deletions x-pack/plugins/features/server/feature_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,13 @@ const managementSchema = schema.recordOf(
listOfCapabilitiesSchema
);
const catalogueSchema = listOfCapabilitiesSchema;
const alertingSchema = schema.object({
ruleTypeIds: schema.maybe(schema.arrayOf(schema.string())),
consumers: schema.maybe(schema.arrayOf(schema.string())),
});
const alertingSchema = schema.arrayOf(
schema.object({
ruleTypeId: schema.string(),
consumers: schema.arrayOf(schema.string()),
})
);

const casesSchema = schema.arrayOf(schema.string());

const appCategorySchema = schema.object({
Expand Down Expand Up @@ -277,13 +280,7 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
kibanaFeatureSchema.validate(feature);

// the following validation can't be enforced by the Joi schema, since it'd require us looking "up" the object graph for the list of valid value, which they explicitly forbid.
const {
app = [],
management = {},
catalogue = [],
alerting: { ruleTypeIds = [], consumers = [] } = {},
cases = [],
} = feature;
const { app = [], management = {}, catalogue = [], alerting = [], cases = [] } = feature;

const unseenApps = new Set(app);

Expand All @@ -296,7 +293,9 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {

const unseenCatalogue = new Set(catalogue);

const unseenAlertTypes = new Set([...ruleTypeIds, ...consumers]);
const unseenAlertTypes = new Set(
alerting.flatMap(({ ruleTypeId, consumers }) => [ruleTypeId, ...consumers])
);

const unseenCasesTypes = new Set(cases);

Expand Down Expand Up @@ -327,23 +326,31 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
}

function validateAlertingEntry(privilegeId: string, entry: FeatureKibanaPrivileges['alerting']) {
const getRuleTypeIdAndConsumers = ({
ruleTypeId,
consumers,
}: {
ruleTypeId: string;
consumers: readonly string[];
}) => [ruleTypeId, ...consumers];

const all: string[] = [
...(entry?.rule?.all?.consumers ?? []),
...(entry?.rule?.all?.ruleTypeIds ?? []),
...(entry?.alert?.all?.consumers ?? []),
...(entry?.alert?.all?.ruleTypeIds ?? []),
...(entry?.rule?.all?.flatMap(getRuleTypeIdAndConsumers) ?? []),
...(entry?.alert?.all?.flatMap(getRuleTypeIdAndConsumers) ?? []),
];
const read: string[] = [
...(entry?.rule?.read?.consumers ?? []),
...(entry?.rule?.read?.ruleTypeIds ?? []),
...(entry?.alert?.read?.consumers ?? []),
...(entry?.alert?.read?.ruleTypeIds ?? []),
...(entry?.rule?.read?.flatMap(getRuleTypeIdAndConsumers) ?? []),
...(entry?.alert?.read?.flatMap(getRuleTypeIdAndConsumers) ?? []),
];

all.forEach((privilegeAlertTypes) => unseenAlertTypes.delete(privilegeAlertTypes));
read.forEach((privilegeAlertTypes) => unseenAlertTypes.delete(privilegeAlertTypes));

const unknownAlertingEntries = difference([...all, ...read], [...ruleTypeIds, ...consumers]);
const unknownAlertingEntries = difference(
[...all, ...read],
alerting.flatMap(({ ruleTypeId, consumers }) => [ruleTypeId, ...consumers])
);

if (unknownAlertingEntries.length > 0) {
throw new Error(
`Feature privilege ${
Expand Down
13 changes: 9 additions & 4 deletions x-pack/plugins/ml/common/types/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export function getDefaultCapabilities(): MlCapabilities {
};
}

const alertingFeatures = Object.values(ML_ALERT_TYPES).map((ruleTypeId) => ({
ruleTypeId,
consumers: [PLUGIN_ID],
}));

export function getPluginPrivileges() {
const apmUserMlCapabilitiesKeys = Object.keys(apmUserMlCapabilities);
const userMlCapabilitiesKeys = Object.keys(userMlCapabilities);
Expand Down Expand Up @@ -149,10 +154,10 @@ export function getPluginPrivileges() {
},
alerting: {
rule: {
all: { ruleTypeIds: Object.values(ML_ALERT_TYPES), consumers: [PLUGIN_ID] },
all: alertingFeatures,
},
alert: {
all: { ruleTypeIds: Object.values(ML_ALERT_TYPES), consumers: [PLUGIN_ID] },
all: alertingFeatures,
},
},
},
Expand All @@ -171,10 +176,10 @@ export function getPluginPrivileges() {
},
alerting: {
rule: {
read: { ruleTypeIds: Object.values(ML_ALERT_TYPES), consumers: [PLUGIN_ID] },
read: alertingFeatures,
},
alert: {
read: { ruleTypeIds: Object.values(ML_ALERT_TYPES), consumers: [PLUGIN_ID] },
read: alertingFeatures,
},
},
},
Expand Down
Loading

0 comments on commit 3b7c89f

Please sign in to comment.