@backstage/plugin-catalog-backend 3.5.0-next.2 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/ingestion/CatalogRules.cjs.js +6 -6
- package/dist/ingestion/CatalogRules.cjs.js.map +1 -1
- package/dist/permissions/rules/createPropertyRule.cjs.js +4 -4
- package/dist/permissions/rules/createPropertyRule.cjs.js.map +1 -1
- package/dist/permissions/rules/hasAnnotation.cjs.js +4 -4
- package/dist/permissions/rules/hasAnnotation.cjs.js.map +1 -1
- package/dist/permissions/rules/hasLabel.cjs.js +4 -4
- package/dist/permissions/rules/hasLabel.cjs.js.map +1 -1
- package/dist/permissions/rules/isEntityKind.cjs.js +3 -3
- package/dist/permissions/rules/isEntityKind.cjs.js.map +1 -1
- package/dist/permissions/rules/isEntityOwner.cjs.js +3 -3
- package/dist/permissions/rules/isEntityOwner.cjs.js.map +1 -1
- package/dist/service/DefaultEntitiesCatalog.cjs.js +43 -38
- package/dist/service/DefaultEntitiesCatalog.cjs.js.map +1 -1
- package/dist/service/createRouter.cjs.js +8 -8
- package/dist/service/createRouter.cjs.js.map +1 -1
- package/dist/service/util.cjs.js +21 -21
- package/dist/service/util.cjs.js.map +1 -1
- package/migrations/20260214000000_search_fk_final_entities.js +220 -36
- package/package.json +21 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 3.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a6b2819: Added `query-catalog-entities` action to the catalog backend actions registry. Supports predicate-based filtering with `$all`, `$any`, `$not`, `$exists`, `$in`, `$contains`, and `$hasPrefix` operators.
|
|
8
|
+
- 972f686: Added support for predicate-based filtering on the `/entities/by-refs` endpoint via the `query` field in the request body. Supports `$all`, `$any`, `$not`, `$exists`, `$in`, `$contains`, and `$hasPrefix` operators.
|
|
9
|
+
- 5d95e8e: Add an `onConflict` option to location creation that can refresh an existing location instead of throwing a conflict error.
|
|
10
|
+
- 56c908e: Added support for predicate-based filtering on the `/entity-facets` endpoint via a new `POST` method. Supports `$all`, `$any`, `$not`, `$exists`, `$in`, `$contains`, and `$hasPrefix` operators.
|
|
11
|
+
- 0fbcf23: Migrated OpenAPI schemas to 3.1.
|
|
12
|
+
- bf71677: Added opentelemetry metrics for SCM events:
|
|
13
|
+
|
|
14
|
+
- `catalog.events.scm.messages` with attribute `eventType`: Counter for the number of SCM events actually received by the catalog backend. The `eventType` is currently either `location` or `repository`.
|
|
15
|
+
|
|
16
|
+
- 51e23eb: Added predicate-based entity filtering via POST /entities/by-query endpoint.
|
|
17
|
+
|
|
18
|
+
Supports `$all`, `$any`, `$not`, `$exists`, `$in`, `$hasPrefix`, and (partially) `$contains` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support.
|
|
19
|
+
|
|
20
|
+
The catalog client's `queryEntities()` method automatically routes to the POST endpoint when a `query` predicate is provided.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- a91bd1b: Improved catalog entity deletion so parent invalidation and deferred relation restitch scheduling are coordinated more safely.
|
|
25
|
+
- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1
|
|
26
|
+
- 7416e8b: Moved stitch queue concerns out of `refresh_state` and `final_entities` into a dedicated `stitch_queue` table with `entity_ref` as the primary key. The `stitch_ticket` is used for optimistic concurrency control. When a stitch completes successfully and the ticket hasn't changed, the corresponding row is deleted from the queue. The migration handles existing data and is fully reversible.
|
|
27
|
+
- fbf382f: Minor internal optimisation
|
|
28
|
+
- 1ee5b28: Migrates existing catalog metrics to use the alpha MetricsService. This release is a 1:1 migration with no breaking changes.
|
|
29
|
+
- 72747b4: Deprecated two processors as they have been moved to the Community Plugins repo with their own backend modules:
|
|
30
|
+
|
|
31
|
+
- `AnnotateScmSlugEntityProcessor`: Use `@backstage-community/plugin-catalog-backend-module-annotate-scm-slug` instead
|
|
32
|
+
- `CodeOwnersProcessor`: Use `@backstage-community/plugin-catalog-backend-module-codeowners` instead
|
|
33
|
+
|
|
34
|
+
- 3644b72: Make the `search` foreign key catalog migration non-blocking on large tables by using batch deletes and PostgreSQL `NOT VALID`/`VALIDATE` to reduce lock duration
|
|
35
|
+
- a49a40d: Updated dependency `zod` to `^3.25.76 || ^4.0.0` & migrated to `/v3` or `/v4` imports.
|
|
36
|
+
- 3181973: Changed the `search` table foreign key to point to `final_entities` instead of `refresh_state`
|
|
37
|
+
- Updated dependencies
|
|
38
|
+
- @backstage/backend-plugin-api@1.8.0
|
|
39
|
+
- @backstage/catalog-client@1.14.0
|
|
40
|
+
- @backstage/integration@2.0.0
|
|
41
|
+
- @backstage/plugin-catalog-node@2.1.0
|
|
42
|
+
- @backstage/filter-predicates@0.1.1
|
|
43
|
+
- @backstage/plugin-permission-common@0.9.7
|
|
44
|
+
- @backstage/plugin-permission-node@0.10.11
|
|
45
|
+
- @backstage/catalog-model@1.7.7
|
|
46
|
+
- @backstage/backend-openapi-utils@0.6.7
|
|
47
|
+
- @backstage/plugin-events-node@0.4.20
|
|
48
|
+
|
|
3
49
|
## 3.5.0-next.2
|
|
4
50
|
|
|
5
51
|
### Minor Changes
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
var path = require('node:path');
|
|
4
4
|
var minimatch = require('minimatch');
|
|
5
|
-
var
|
|
5
|
+
var v3 = require('zod/v3');
|
|
6
6
|
|
|
7
7
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
8
8
|
|
|
9
9
|
var path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
10
10
|
|
|
11
|
-
const allowRuleParser =
|
|
12
|
-
|
|
13
|
-
kind:
|
|
14
|
-
"spec.type":
|
|
15
|
-
}).or(
|
|
11
|
+
const allowRuleParser = v3.z.array(
|
|
12
|
+
v3.z.object({
|
|
13
|
+
kind: v3.z.string(),
|
|
14
|
+
"spec.type": v3.z.string().optional()
|
|
15
|
+
}).or(v3.z.string()).transform((val) => typeof val === "string" ? { kind: val } : val)
|
|
16
16
|
);
|
|
17
17
|
class DefaultCatalogRulesEnforcer {
|
|
18
18
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogRules.cjs.js","sources":["../../src/ingestion/CatalogRules.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { Entity } from '@backstage/catalog-model';\nimport path from 'node:path';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { minimatch } from 'minimatch';\nimport { z } from 'zod';\n\n/**\n * Rules to apply to catalog entities.\n *\n * An undefined list of matchers means match all, an empty list of matchers means match none.\n */\nexport type CatalogRule = {\n allow: CatalogRuleAllow[];\n locations?: Array<{\n exact?: string;\n type: string;\n pattern?: string;\n }>;\n};\n\ntype CatalogRuleAllow = {\n kind: string;\n 'spec.type'?: string;\n};\n\n/**\n * Decides whether an entity from a given location is allowed to enter the\n * catalog, according to some rule set.\n */\nexport type CatalogRulesEnforcer = {\n isAllowed(entity: Entity, location: LocationSpec): boolean;\n};\n\nconst allowRuleParser = z.array(\n z\n .object({\n kind: z.string(),\n 'spec.type': z.string().optional(),\n })\n .or(z.string())\n .transform(val => (typeof val === 'string' ? { kind: val } : val)),\n);\n\n/**\n * Implements the default catalog rule set, consuming the config keys\n * `catalog.rules` and `catalog.locations.[].rules`.\n */\nexport class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {\n /**\n * Default rules used by the catalog.\n *\n * Denies any location from specifying user or group entities.\n */\n static readonly defaultRules: CatalogRule[] = [\n {\n allow: ['Component', 'API', 'Location'].map(kind => ({ kind })),\n },\n ];\n\n /**\n * Loads catalog rules from config.\n *\n * This reads `catalog.rules` and defaults to the default rules if no value is present.\n * The value of the config should be a list of config objects, each with a single `allow`\n * field which in turn is a list of entity kinds to allow.\n *\n * If there is no matching rule to allow an ingested entity, it will be rejected by the catalog.\n *\n * It also reads in rules from `catalog.locations`, where each location can have a list\n * of rules for that specific location, specified in a `rules` field.\n *\n * For example:\n *\n * ```yaml\n * catalog:\n * rules:\n * - allow: [Component, API]\n * - allow:\n * - kind: Resource\n * 'spec.type': database\n * - allow: [Template]\n * locations:\n * - type: url\n * pattern: https://github.com/org/*\\/blob/master/template.yaml\n * - allow: [Location]\n * locations:\n * - type: url\n * pattern: https://github.com/org/repo/blob/master/location.yaml\n *\n * locations:\n * - type: url\n * target: https://github.com/org/repo/blob/master/users.yaml\n * rules:\n * - allow: [User, Group]\n * - type: url\n * target: https://github.com/org/repo/blob/master/systems.yaml\n * rules:\n * - allow: [System]\n * ```\n */\n static fromConfig(config: Config) {\n const rules = new Array<CatalogRule>();\n\n if (config.has('catalog.rules')) {\n const globalRules = config\n .getConfigArray('catalog.rules')\n .map(ruleConf => ({\n allow: allowRuleParser.parse(ruleConf.get('allow')),\n locations: ruleConf\n .getOptionalConfigArray('locations')\n ?.map(locationConfig => {\n const location = {\n pattern: locationConfig.getOptionalString('pattern'),\n type: locationConfig.getString('type'),\n exact: locationConfig.getOptionalString('exact'),\n };\n if (location.pattern && location.exact) {\n throw new Error(\n 'A catalog rule location cannot have both exact and pattern values',\n );\n }\n return location;\n }),\n }));\n rules.push(...globalRules);\n } else {\n rules.push(...DefaultCatalogRulesEnforcer.defaultRules);\n }\n\n if (config.has('catalog.locations')) {\n const locationRules = config\n .getConfigArray('catalog.locations')\n .flatMap(locConf => {\n if (!locConf.has('rules')) {\n return [];\n }\n const type = locConf.getString('type');\n const exact = resolveTarget(type, locConf.getString('target'));\n\n return locConf.getConfigArray('rules').map(ruleConf => ({\n allow: ruleConf.getStringArray('allow').map(kind => ({ kind })),\n locations: [{ type, exact }],\n }));\n });\n\n rules.push(...locationRules);\n }\n\n return new DefaultCatalogRulesEnforcer(rules);\n }\n\n private readonly rules: CatalogRule[];\n\n constructor(rules: CatalogRule[]) {\n this.rules = rules;\n }\n\n /**\n * Checks whether a specific entity/location combination is allowed\n * according to the configured rules.\n */\n isAllowed(entity: Entity, location: LocationSpec) {\n for (const rule of this.rules) {\n if (!this.matchLocation(location, rule.locations)) {\n continue;\n }\n\n if (this.matchEntity(entity, rule.allow)) {\n return true;\n }\n }\n\n return false;\n }\n\n private matchLocation(\n location: LocationSpec,\n matchers?: { exact?: string; type: string; pattern?: string }[],\n ): boolean {\n if (!matchers) {\n return true;\n }\n\n for (const matcher of matchers) {\n if (matcher.type !== location?.type) {\n continue;\n }\n if (matcher.exact && matcher.exact !== location?.target) {\n continue;\n }\n if (\n matcher.pattern &&\n !minimatch(location?.target, matcher.pattern, {\n nocase: true,\n dot: true,\n })\n ) {\n continue;\n }\n return true;\n }\n\n return false;\n }\n\n private matchEntity(entity: Entity, matchers?: CatalogRuleAllow[]): boolean {\n if (!matchers) {\n return true;\n }\n\n for (const matcher of matchers) {\n if (\n entity.kind?.toLocaleLowerCase('en-US') !==\n matcher.kind.toLocaleLowerCase('en-US')\n ) {\n continue;\n }\n\n if (matcher['spec.type']) {\n if (typeof entity.spec?.type !== 'string') {\n continue;\n }\n if (\n matcher['spec.type'].toLocaleLowerCase('en-US') !==\n entity.spec.type.toLocaleLowerCase('en-US')\n ) {\n continue;\n }\n }\n\n return true;\n }\n\n return false;\n }\n}\n\nfunction resolveTarget(type: string, target: string): string {\n if (type !== 'file') {\n return target;\n }\n\n return path.resolve(target);\n}\n"],"names":["z","minimatch","path"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CatalogRules.cjs.js","sources":["../../src/ingestion/CatalogRules.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { Entity } from '@backstage/catalog-model';\nimport path from 'node:path';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { minimatch } from 'minimatch';\nimport { z } from 'zod/v3';\n\n/**\n * Rules to apply to catalog entities.\n *\n * An undefined list of matchers means match all, an empty list of matchers means match none.\n */\nexport type CatalogRule = {\n allow: CatalogRuleAllow[];\n locations?: Array<{\n exact?: string;\n type: string;\n pattern?: string;\n }>;\n};\n\ntype CatalogRuleAllow = {\n kind: string;\n 'spec.type'?: string;\n};\n\n/**\n * Decides whether an entity from a given location is allowed to enter the\n * catalog, according to some rule set.\n */\nexport type CatalogRulesEnforcer = {\n isAllowed(entity: Entity, location: LocationSpec): boolean;\n};\n\nconst allowRuleParser = z.array(\n z\n .object({\n kind: z.string(),\n 'spec.type': z.string().optional(),\n })\n .or(z.string())\n .transform(val => (typeof val === 'string' ? { kind: val } : val)),\n);\n\n/**\n * Implements the default catalog rule set, consuming the config keys\n * `catalog.rules` and `catalog.locations.[].rules`.\n */\nexport class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {\n /**\n * Default rules used by the catalog.\n *\n * Denies any location from specifying user or group entities.\n */\n static readonly defaultRules: CatalogRule[] = [\n {\n allow: ['Component', 'API', 'Location'].map(kind => ({ kind })),\n },\n ];\n\n /**\n * Loads catalog rules from config.\n *\n * This reads `catalog.rules` and defaults to the default rules if no value is present.\n * The value of the config should be a list of config objects, each with a single `allow`\n * field which in turn is a list of entity kinds to allow.\n *\n * If there is no matching rule to allow an ingested entity, it will be rejected by the catalog.\n *\n * It also reads in rules from `catalog.locations`, where each location can have a list\n * of rules for that specific location, specified in a `rules` field.\n *\n * For example:\n *\n * ```yaml\n * catalog:\n * rules:\n * - allow: [Component, API]\n * - allow:\n * - kind: Resource\n * 'spec.type': database\n * - allow: [Template]\n * locations:\n * - type: url\n * pattern: https://github.com/org/*\\/blob/master/template.yaml\n * - allow: [Location]\n * locations:\n * - type: url\n * pattern: https://github.com/org/repo/blob/master/location.yaml\n *\n * locations:\n * - type: url\n * target: https://github.com/org/repo/blob/master/users.yaml\n * rules:\n * - allow: [User, Group]\n * - type: url\n * target: https://github.com/org/repo/blob/master/systems.yaml\n * rules:\n * - allow: [System]\n * ```\n */\n static fromConfig(config: Config) {\n const rules = new Array<CatalogRule>();\n\n if (config.has('catalog.rules')) {\n const globalRules = config\n .getConfigArray('catalog.rules')\n .map(ruleConf => ({\n allow: allowRuleParser.parse(ruleConf.get('allow')),\n locations: ruleConf\n .getOptionalConfigArray('locations')\n ?.map(locationConfig => {\n const location = {\n pattern: locationConfig.getOptionalString('pattern'),\n type: locationConfig.getString('type'),\n exact: locationConfig.getOptionalString('exact'),\n };\n if (location.pattern && location.exact) {\n throw new Error(\n 'A catalog rule location cannot have both exact and pattern values',\n );\n }\n return location;\n }),\n }));\n rules.push(...globalRules);\n } else {\n rules.push(...DefaultCatalogRulesEnforcer.defaultRules);\n }\n\n if (config.has('catalog.locations')) {\n const locationRules = config\n .getConfigArray('catalog.locations')\n .flatMap(locConf => {\n if (!locConf.has('rules')) {\n return [];\n }\n const type = locConf.getString('type');\n const exact = resolveTarget(type, locConf.getString('target'));\n\n return locConf.getConfigArray('rules').map(ruleConf => ({\n allow: ruleConf.getStringArray('allow').map(kind => ({ kind })),\n locations: [{ type, exact }],\n }));\n });\n\n rules.push(...locationRules);\n }\n\n return new DefaultCatalogRulesEnforcer(rules);\n }\n\n private readonly rules: CatalogRule[];\n\n constructor(rules: CatalogRule[]) {\n this.rules = rules;\n }\n\n /**\n * Checks whether a specific entity/location combination is allowed\n * according to the configured rules.\n */\n isAllowed(entity: Entity, location: LocationSpec) {\n for (const rule of this.rules) {\n if (!this.matchLocation(location, rule.locations)) {\n continue;\n }\n\n if (this.matchEntity(entity, rule.allow)) {\n return true;\n }\n }\n\n return false;\n }\n\n private matchLocation(\n location: LocationSpec,\n matchers?: { exact?: string; type: string; pattern?: string }[],\n ): boolean {\n if (!matchers) {\n return true;\n }\n\n for (const matcher of matchers) {\n if (matcher.type !== location?.type) {\n continue;\n }\n if (matcher.exact && matcher.exact !== location?.target) {\n continue;\n }\n if (\n matcher.pattern &&\n !minimatch(location?.target, matcher.pattern, {\n nocase: true,\n dot: true,\n })\n ) {\n continue;\n }\n return true;\n }\n\n return false;\n }\n\n private matchEntity(entity: Entity, matchers?: CatalogRuleAllow[]): boolean {\n if (!matchers) {\n return true;\n }\n\n for (const matcher of matchers) {\n if (\n entity.kind?.toLocaleLowerCase('en-US') !==\n matcher.kind.toLocaleLowerCase('en-US')\n ) {\n continue;\n }\n\n if (matcher['spec.type']) {\n if (typeof entity.spec?.type !== 'string') {\n continue;\n }\n if (\n matcher['spec.type'].toLocaleLowerCase('en-US') !==\n entity.spec.type.toLocaleLowerCase('en-US')\n ) {\n continue;\n }\n }\n\n return true;\n }\n\n return false;\n }\n}\n\nfunction resolveTarget(type: string, target: string): string {\n if (type !== 'file') {\n return target;\n }\n\n return path.resolve(target);\n}\n"],"names":["z","minimatch","path"],"mappings":"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"}
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
4
4
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
5
|
var lodash = require('lodash');
|
|
6
|
-
var
|
|
6
|
+
var v3 = require('zod/v3');
|
|
7
7
|
|
|
8
8
|
const createPropertyRule = (propertyType) => pluginPermissionNode.createPermissionRule({
|
|
9
9
|
name: `HAS_${propertyType.toUpperCase()}`,
|
|
10
10
|
description: `Allow entities with the specified ${propertyType} subfield`,
|
|
11
11
|
resourceRef: alpha.catalogEntityPermissionResourceRef,
|
|
12
|
-
paramsSchema:
|
|
13
|
-
key:
|
|
14
|
-
value:
|
|
12
|
+
paramsSchema: v3.z.object({
|
|
13
|
+
key: v3.z.string().describe(`Property within the entities ${propertyType} to match on`),
|
|
14
|
+
value: v3.z.string().optional().describe(`Value of the given property to match on`)
|
|
15
15
|
}),
|
|
16
16
|
apply: (resource, { key, value }) => {
|
|
17
17
|
const foundValue = lodash.get(resource[propertyType], key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPropertyRule.cjs.js","sources":["../../../src/permissions/rules/createPropertyRule.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { get } from 'lodash';\nimport { z } from 'zod';\n\nexport const createPropertyRule = (propertyType: 'metadata' | 'spec') =>\n createPermissionRule({\n name: `HAS_${propertyType.toUpperCase()}`,\n description: `Allow entities with the specified ${propertyType} subfield`,\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n key: z\n .string()\n .describe(`Property within the entities ${propertyType} to match on`),\n value: z\n .string()\n .optional()\n .describe(`Value of the given property to match on`),\n }),\n apply: (resource, { key, value }) => {\n const foundValue = get(resource[propertyType], key);\n\n if (Array.isArray(foundValue)) {\n if (value !== undefined) {\n return foundValue.includes(value);\n }\n return foundValue.length > 0;\n }\n if (value !== undefined) {\n return value === foundValue;\n }\n return !!foundValue;\n },\n toQuery: ({ key, value }) => ({\n key: `${propertyType}.${key}`,\n ...(value !== undefined && { values: [value] }),\n }),\n });\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z","get"],"mappings":";;;;;;;AAqBO,MAAM,kBAAA,GAAqB,CAAC,YAAA,KACjCA,yCAAA,CAAqB;AAAA,EACnB,IAAA,EAAM,CAAA,IAAA,EAAO,YAAA,CAAa,WAAA,EAAa,CAAA,CAAA;AAAA,EACvC,WAAA,EAAa,qCAAqC,YAAY,CAAA,SAAA,CAAA;AAAA,EAC9D,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,
|
|
1
|
+
{"version":3,"file":"createPropertyRule.cjs.js","sources":["../../../src/permissions/rules/createPropertyRule.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { get } from 'lodash';\nimport { z } from 'zod/v3';\n\nexport const createPropertyRule = (propertyType: 'metadata' | 'spec') =>\n createPermissionRule({\n name: `HAS_${propertyType.toUpperCase()}`,\n description: `Allow entities with the specified ${propertyType} subfield`,\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n key: z\n .string()\n .describe(`Property within the entities ${propertyType} to match on`),\n value: z\n .string()\n .optional()\n .describe(`Value of the given property to match on`),\n }),\n apply: (resource, { key, value }) => {\n const foundValue = get(resource[propertyType], key);\n\n if (Array.isArray(foundValue)) {\n if (value !== undefined) {\n return foundValue.includes(value);\n }\n return foundValue.length > 0;\n }\n if (value !== undefined) {\n return value === foundValue;\n }\n return !!foundValue;\n },\n toQuery: ({ key, value }) => ({\n key: `${propertyType}.${key}`,\n ...(value !== undefined && { values: [value] }),\n }),\n });\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z","get"],"mappings":";;;;;;;AAqBO,MAAM,kBAAA,GAAqB,CAAC,YAAA,KACjCA,yCAAA,CAAqB;AAAA,EACnB,IAAA,EAAM,CAAA,IAAA,EAAO,YAAA,CAAa,WAAA,EAAa,CAAA,CAAA;AAAA,EACvC,WAAA,EAAa,qCAAqC,YAAY,CAAA,SAAA,CAAA;AAAA,EAC9D,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,KAAE,MAAA,CAAO;AAAA,IACrB,KAAKA,IAAA,CACF,MAAA,GACA,QAAA,CAAS,CAAA,6BAAA,EAAgC,YAAY,CAAA,YAAA,CAAc,CAAA;AAAA,IACtE,OAAOA,IAAA,CACJ,MAAA,GACA,QAAA,EAAS,CACT,SAAS,CAAA,uCAAA,CAAyC;AAAA,GACtD,CAAA;AAAA,EACD,OAAO,CAAC,QAAA,EAAU,EAAE,GAAA,EAAK,OAAM,KAAM;AACnC,IAAA,MAAM,UAAA,GAAaC,UAAA,CAAI,QAAA,CAAS,YAAY,GAAG,GAAG,CAAA;AAElD,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC7B,MAAA,IAAI,UAAU,MAAA,EAAW;AACvB,QAAA,OAAO,UAAA,CAAW,SAAS,KAAK,CAAA;AAAA,MAClC;AACA,MAAA,OAAO,WAAW,MAAA,GAAS,CAAA;AAAA,IAC7B;AACA,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,OAAO,KAAA,KAAU,UAAA;AAAA,IACnB;AACA,IAAA,OAAO,CAAC,CAAC,UAAA;AAAA,EACX,CAAA;AAAA,EACA,OAAA,EAAS,CAAC,EAAE,GAAA,EAAK,OAAM,MAAO;AAAA,IAC5B,GAAA,EAAK,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AAAA,IAC3B,GAAI,KAAA,KAAU,MAAA,IAAa,EAAE,MAAA,EAAQ,CAAC,KAAK,CAAA;AAAE,GAC/C;AACF,CAAC;;;;"}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
4
4
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
-
var
|
|
5
|
+
var v3 = require('zod/v3');
|
|
6
6
|
|
|
7
7
|
const hasAnnotation = pluginPermissionNode.createPermissionRule({
|
|
8
8
|
name: "HAS_ANNOTATION",
|
|
9
9
|
description: "Allow entities with the specified annotation",
|
|
10
10
|
resourceRef: alpha.catalogEntityPermissionResourceRef,
|
|
11
|
-
paramsSchema:
|
|
12
|
-
annotation:
|
|
13
|
-
value:
|
|
11
|
+
paramsSchema: v3.z.object({
|
|
12
|
+
annotation: v3.z.string().describe("Name of the annotation to match on"),
|
|
13
|
+
value: v3.z.string().optional().describe("Value of the annotation to match on")
|
|
14
14
|
}),
|
|
15
15
|
apply: (resource, { annotation, value }) => !!resource.metadata.annotations?.hasOwnProperty(annotation) && (value === void 0 ? true : resource.metadata.annotations?.[annotation] === value),
|
|
16
16
|
toQuery: ({ annotation, value }) => value === void 0 ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hasAnnotation.cjs.js","sources":["../../../src/permissions/rules/hasAnnotation.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for the presence of an annotation on a given entity.\n *\n * If a value is given, it filters for the annotation value, too.\n *\n * @alpha\n */\nexport const hasAnnotation = createPermissionRule({\n name: 'HAS_ANNOTATION',\n description: 'Allow entities with the specified annotation',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n annotation: z.string().describe('Name of the annotation to match on'),\n value: z\n .string()\n .optional()\n .describe('Value of the annotation to match on'),\n }),\n apply: (resource, { annotation, value }) =>\n !!resource.metadata.annotations?.hasOwnProperty(annotation) &&\n (value === undefined\n ? true\n : resource.metadata.annotations?.[annotation] === value),\n toQuery: ({ annotation, value }) =>\n value === undefined\n ? {\n key: `metadata.annotations.${annotation}`,\n }\n : {\n key: `metadata.annotations.${annotation}`,\n values: [value],\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AA4BO,MAAM,gBAAgBA,yCAAA,CAAqB;AAAA,EAChD,IAAA,EAAM,gBAAA;AAAA,EACN,WAAA,EAAa,8CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,
|
|
1
|
+
{"version":3,"file":"hasAnnotation.cjs.js","sources":["../../../src/permissions/rules/hasAnnotation.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod/v3';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for the presence of an annotation on a given entity.\n *\n * If a value is given, it filters for the annotation value, too.\n *\n * @alpha\n */\nexport const hasAnnotation = createPermissionRule({\n name: 'HAS_ANNOTATION',\n description: 'Allow entities with the specified annotation',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n annotation: z.string().describe('Name of the annotation to match on'),\n value: z\n .string()\n .optional()\n .describe('Value of the annotation to match on'),\n }),\n apply: (resource, { annotation, value }) =>\n !!resource.metadata.annotations?.hasOwnProperty(annotation) &&\n (value === undefined\n ? true\n : resource.metadata.annotations?.[annotation] === value),\n toQuery: ({ annotation, value }) =>\n value === undefined\n ? {\n key: `metadata.annotations.${annotation}`,\n }\n : {\n key: `metadata.annotations.${annotation}`,\n values: [value],\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AA4BO,MAAM,gBAAgBA,yCAAA,CAAqB;AAAA,EAChD,IAAA,EAAM,gBAAA;AAAA,EACN,WAAA,EAAa,8CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,KAAE,MAAA,CAAO;AAAA,IACrB,UAAA,EAAYA,IAAA,CAAE,MAAA,EAAO,CAAE,SAAS,oCAAoC,CAAA;AAAA,IACpE,OAAOA,IAAA,CACJ,MAAA,GACA,QAAA,EAAS,CACT,SAAS,qCAAqC;AAAA,GAClD,CAAA;AAAA,EACD,KAAA,EAAO,CAAC,QAAA,EAAU,EAAE,YAAY,KAAA,EAAM,KACpC,CAAC,CAAC,QAAA,CAAS,QAAA,CAAS,aAAa,cAAA,CAAe,UAAU,MACzD,KAAA,KAAU,MAAA,GACP,OACA,QAAA,CAAS,QAAA,CAAS,WAAA,GAAc,UAAU,CAAA,KAAM,KAAA,CAAA;AAAA,EACtD,SAAS,CAAC,EAAE,YAAY,KAAA,EAAM,KAC5B,UAAU,MAAA,GACN;AAAA,IACE,GAAA,EAAK,wBAAwB,UAAU,CAAA;AAAA,GACzC,GACA;AAAA,IACE,GAAA,EAAK,wBAAwB,UAAU,CAAA,CAAA;AAAA,IACvC,MAAA,EAAQ,CAAC,KAAK;AAAA;AAExB,CAAC;;;;"}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
4
4
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
-
var
|
|
5
|
+
var v3 = require('zod/v3');
|
|
6
6
|
|
|
7
7
|
const hasLabel = pluginPermissionNode.createPermissionRule({
|
|
8
8
|
name: "HAS_LABEL",
|
|
9
9
|
description: "Allow entities with the specified label",
|
|
10
10
|
resourceRef: alpha.catalogEntityPermissionResourceRef,
|
|
11
|
-
paramsSchema:
|
|
12
|
-
label:
|
|
13
|
-
value:
|
|
11
|
+
paramsSchema: v3.z.object({
|
|
12
|
+
label: v3.z.string().describe("Name of the label to match on"),
|
|
13
|
+
value: v3.z.string().optional().describe("Value of the label to match on")
|
|
14
14
|
}),
|
|
15
15
|
apply: (resource, { label, value }) => !!resource.metadata.labels?.hasOwnProperty(label) && (value === void 0 ? true : resource.metadata.labels?.[label] === value),
|
|
16
16
|
toQuery: ({ label, value }) => value === void 0 ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hasLabel.cjs.js","sources":["../../../src/permissions/rules/hasLabel.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified label in its metadata.\n * @alpha\n */\nexport const hasLabel = createPermissionRule({\n name: 'HAS_LABEL',\n description: 'Allow entities with the specified label',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n label: z.string().describe('Name of the label to match on'),\n value: z.string().optional().describe('Value of the label to match on'),\n }),\n apply: (resource, { label, value }) =>\n !!resource.metadata.labels?.hasOwnProperty(label) &&\n (value === undefined ? true : resource.metadata.labels?.[label] === value),\n toQuery: ({ label, value }) =>\n value === undefined\n ? {\n key: `metadata.labels.${label}`,\n }\n : {\n key: `metadata.labels.${label}`,\n values: [value],\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AAyBO,MAAM,WAAWA,yCAAA,CAAqB;AAAA,EAC3C,IAAA,EAAM,WAAA;AAAA,EACN,WAAA,EAAa,yCAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,
|
|
1
|
+
{"version":3,"file":"hasLabel.cjs.js","sources":["../../../src/permissions/rules/hasLabel.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod/v3';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified label in its metadata.\n * @alpha\n */\nexport const hasLabel = createPermissionRule({\n name: 'HAS_LABEL',\n description: 'Allow entities with the specified label',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n label: z.string().describe('Name of the label to match on'),\n value: z.string().optional().describe('Value of the label to match on'),\n }),\n apply: (resource, { label, value }) =>\n !!resource.metadata.labels?.hasOwnProperty(label) &&\n (value === undefined ? true : resource.metadata.labels?.[label] === value),\n toQuery: ({ label, value }) =>\n value === undefined\n ? {\n key: `metadata.labels.${label}`,\n }\n : {\n key: `metadata.labels.${label}`,\n values: [value],\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AAyBO,MAAM,WAAWA,yCAAA,CAAqB;AAAA,EAC3C,IAAA,EAAM,WAAA;AAAA,EACN,WAAA,EAAa,yCAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,KAAE,MAAA,CAAO;AAAA,IACrB,KAAA,EAAOA,IAAA,CAAE,MAAA,EAAO,CAAE,SAAS,+BAA+B,CAAA;AAAA,IAC1D,OAAOA,IAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,gCAAgC;AAAA,GACvE,CAAA;AAAA,EACD,KAAA,EAAO,CAAC,QAAA,EAAU,EAAE,OAAO,KAAA,EAAM,KAC/B,CAAC,CAAC,QAAA,CAAS,QAAA,CAAS,QAAQ,cAAA,CAAe,KAAK,MAC/C,KAAA,KAAU,MAAA,GAAY,OAAO,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,KAAK,CAAA,KAAM,KAAA,CAAA;AAAA,EACtE,SAAS,CAAC,EAAE,OAAO,KAAA,EAAM,KACvB,UAAU,MAAA,GACN;AAAA,IACE,GAAA,EAAK,mBAAmB,KAAK,CAAA;AAAA,GAC/B,GACA;AAAA,IACE,GAAA,EAAK,mBAAmB,KAAK,CAAA,CAAA;AAAA,IAC7B,MAAA,EAAQ,CAAC,KAAK;AAAA;AAExB,CAAC;;;;"}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
4
4
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
-
var
|
|
5
|
+
var v3 = require('zod/v3');
|
|
6
6
|
|
|
7
7
|
const isEntityKind = pluginPermissionNode.createPermissionRule({
|
|
8
8
|
name: "IS_ENTITY_KIND",
|
|
9
9
|
description: "Allow entities matching a specified kind",
|
|
10
10
|
resourceRef: alpha.catalogEntityPermissionResourceRef,
|
|
11
|
-
paramsSchema:
|
|
12
|
-
kinds:
|
|
11
|
+
paramsSchema: v3.z.object({
|
|
12
|
+
kinds: v3.z.array(v3.z.string()).describe("List of kinds to match at least one of")
|
|
13
13
|
}),
|
|
14
14
|
apply(resource, { kinds }) {
|
|
15
15
|
const resourceKind = resource.kind.toLocaleLowerCase("en-US");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isEntityKind.cjs.js","sources":["../../../src/permissions/rules/isEntityKind.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified kind.\n * @alpha\n */\nexport const isEntityKind = createPermissionRule({\n name: 'IS_ENTITY_KIND',\n description: 'Allow entities matching a specified kind',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n kinds: z\n .array(z.string())\n .describe('List of kinds to match at least one of'),\n }),\n apply(resource, { kinds }) {\n const resourceKind = resource.kind.toLocaleLowerCase('en-US');\n return kinds.some(kind => kind.toLocaleLowerCase('en-US') === resourceKind);\n },\n toQuery({ kinds }) {\n return {\n key: 'kind',\n values: kinds.map(kind => kind.toLocaleLowerCase('en-US')),\n };\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AAyBO,MAAM,eAAeA,yCAAA,CAAqB;AAAA,EAC/C,IAAA,EAAM,gBAAA;AAAA,EACN,WAAA,EAAa,0CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,
|
|
1
|
+
{"version":3,"file":"isEntityKind.cjs.js","sources":["../../../src/permissions/rules/isEntityKind.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod/v3';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified kind.\n * @alpha\n */\nexport const isEntityKind = createPermissionRule({\n name: 'IS_ENTITY_KIND',\n description: 'Allow entities matching a specified kind',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n kinds: z\n .array(z.string())\n .describe('List of kinds to match at least one of'),\n }),\n apply(resource, { kinds }) {\n const resourceKind = resource.kind.toLocaleLowerCase('en-US');\n return kinds.some(kind => kind.toLocaleLowerCase('en-US') === resourceKind);\n },\n toQuery({ kinds }) {\n return {\n key: 'kind',\n values: kinds.map(kind => kind.toLocaleLowerCase('en-US')),\n };\n },\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z"],"mappings":";;;;;;AAyBO,MAAM,eAAeA,yCAAA,CAAqB;AAAA,EAC/C,IAAA,EAAM,gBAAA;AAAA,EACN,WAAA,EAAa,0CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,KAAE,MAAA,CAAO;AAAA,IACrB,KAAA,EAAOA,KACJ,KAAA,CAAMA,IAAA,CAAE,QAAQ,CAAA,CAChB,SAAS,wCAAwC;AAAA,GACrD,CAAA;AAAA,EACD,KAAA,CAAM,QAAA,EAAU,EAAE,KAAA,EAAM,EAAG;AACzB,IAAA,MAAM,YAAA,GAAe,QAAA,CAAS,IAAA,CAAK,iBAAA,CAAkB,OAAO,CAAA;AAC5D,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAK,iBAAA,CAAkB,OAAO,MAAM,YAAY,CAAA;AAAA,EAC5E,CAAA;AAAA,EACA,OAAA,CAAQ,EAAE,KAAA,EAAM,EAAG;AACjB,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,MAAA;AAAA,MACL,QAAQ,KAAA,CAAM,GAAA,CAAI,UAAQ,IAAA,CAAK,iBAAA,CAAkB,OAAO,CAAC;AAAA,KAC3D;AAAA,EACF;AACF,CAAC;;;;"}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var catalogModel = require('@backstage/catalog-model');
|
|
4
4
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
-
var
|
|
5
|
+
var v3 = require('zod/v3');
|
|
6
6
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
7
7
|
|
|
8
8
|
const isEntityOwner = pluginPermissionNode.createPermissionRule({
|
|
9
9
|
name: "IS_ENTITY_OWNER",
|
|
10
10
|
description: "Allow entities owned by a specified claim",
|
|
11
11
|
resourceRef: alpha.catalogEntityPermissionResourceRef,
|
|
12
|
-
paramsSchema:
|
|
13
|
-
claims:
|
|
12
|
+
paramsSchema: v3.z.object({
|
|
13
|
+
claims: v3.z.array(v3.z.string()).describe(
|
|
14
14
|
`List of claims to match at least one on within ${catalogModel.RELATION_OWNED_BY}`
|
|
15
15
|
)
|
|
16
16
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isEntityOwner.cjs.js","sources":["../../../src/permissions/rules/isEntityOwner.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod';\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified owner.\n *\n * @alpha\n */\nexport const isEntityOwner = createPermissionRule({\n name: 'IS_ENTITY_OWNER',\n description: 'Allow entities owned by a specified claim',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n claims: z\n .array(z.string())\n .describe(\n `List of claims to match at least one on within ${RELATION_OWNED_BY}`,\n ),\n }),\n apply: (resource, { claims }) => {\n if (!resource.relations) {\n return false;\n }\n\n return resource.relations\n .filter(relation => relation.type === RELATION_OWNED_BY)\n .some(relation => claims.includes(relation.targetRef));\n },\n toQuery: ({ claims }) => ({\n key: 'relations.ownedBy',\n values: claims,\n }),\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z","RELATION_OWNED_BY"],"mappings":";;;;;;;AA2BO,MAAM,gBAAgBA,yCAAA,CAAqB;AAAA,EAChD,IAAA,EAAM,iBAAA;AAAA,EACN,WAAA,EAAa,2CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,
|
|
1
|
+
{"version":3,"file":"isEntityOwner.cjs.js","sources":["../../../src/permissions/rules/isEntityOwner.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod/v3';\nimport { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';\n\n/**\n * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which\n * filters for entities with a specified owner.\n *\n * @alpha\n */\nexport const isEntityOwner = createPermissionRule({\n name: 'IS_ENTITY_OWNER',\n description: 'Allow entities owned by a specified claim',\n resourceRef: catalogEntityPermissionResourceRef,\n paramsSchema: z.object({\n claims: z\n .array(z.string())\n .describe(\n `List of claims to match at least one on within ${RELATION_OWNED_BY}`,\n ),\n }),\n apply: (resource, { claims }) => {\n if (!resource.relations) {\n return false;\n }\n\n return resource.relations\n .filter(relation => relation.type === RELATION_OWNED_BY)\n .some(relation => claims.includes(relation.targetRef));\n },\n toQuery: ({ claims }) => ({\n key: 'relations.ownedBy',\n values: claims,\n }),\n});\n"],"names":["createPermissionRule","catalogEntityPermissionResourceRef","z","RELATION_OWNED_BY"],"mappings":";;;;;;;AA2BO,MAAM,gBAAgBA,yCAAA,CAAqB;AAAA,EAChD,IAAA,EAAM,iBAAA;AAAA,EACN,WAAA,EAAa,2CAAA;AAAA,EACb,WAAA,EAAaC,wCAAA;AAAA,EACb,YAAA,EAAcC,KAAE,MAAA,CAAO;AAAA,IACrB,QAAQA,IAAA,CACL,KAAA,CAAMA,IAAA,CAAE,MAAA,EAAQ,CAAA,CAChB,QAAA;AAAA,MACC,kDAAkDC,8BAAiB,CAAA;AAAA;AACrE,GACH,CAAA;AAAA,EACD,KAAA,EAAO,CAAC,QAAA,EAAU,EAAE,QAAO,KAAM;AAC/B,IAAA,IAAI,CAAC,SAAS,SAAA,EAAW;AACvB,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,QAAA,CAAS,SAAA,CACb,MAAA,CAAO,CAAA,QAAA,KAAY,SAAS,IAAA,KAASA,8BAAiB,CAAA,CACtD,IAAA,CAAK,CAAA,QAAA,KAAY,MAAA,CAAO,QAAA,CAAS,QAAA,CAAS,SAAS,CAAC,CAAA;AAAA,EACzD,CAAA;AAAA,EACA,OAAA,EAAS,CAAC,EAAE,MAAA,EAAO,MAAO;AAAA,IACxB,GAAA,EAAK,mBAAA;AAAA,IACL,MAAA,EAAQ;AAAA,GACV;AACF,CAAC;;;;"}
|
|
@@ -344,47 +344,52 @@ class DefaultEntitiesCatalog {
|
|
|
344
344
|
};
|
|
345
345
|
}
|
|
346
346
|
async removeEntityByUid(uid) {
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
"
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
347
|
+
const relationPeerRefs = await this.database.transaction(async (tx) => {
|
|
348
|
+
const dbConfig = tx.client.config;
|
|
349
|
+
if (dbConfig.client.includes("mysql")) {
|
|
350
|
+
const results = await tx("refresh_state").select("entity_id").whereIn("entity_ref", function parents(builder) {
|
|
351
|
+
return builder.from("refresh_state").innerJoin(
|
|
352
|
+
"refresh_state_references",
|
|
353
|
+
{
|
|
354
|
+
"refresh_state_references.target_entity_ref": "refresh_state.entity_ref"
|
|
355
|
+
}
|
|
356
|
+
).where("refresh_state.entity_id", "=", uid).select("refresh_state_references.source_entity_ref");
|
|
357
|
+
});
|
|
358
|
+
await tx("refresh_state").update({
|
|
359
|
+
result_hash: "child-was-deleted",
|
|
360
|
+
next_update_at: tx.fn.now()
|
|
361
|
+
}).whereIn(
|
|
362
|
+
"entity_id",
|
|
363
|
+
results.map((key) => key.entity_id)
|
|
364
|
+
);
|
|
365
|
+
} else {
|
|
366
|
+
await tx("refresh_state").update({
|
|
367
|
+
result_hash: "child-was-deleted",
|
|
368
|
+
next_update_at: tx.fn.now()
|
|
369
|
+
}).whereIn("entity_ref", function parents(builder) {
|
|
370
|
+
return builder.from("refresh_state").innerJoin(
|
|
371
|
+
"refresh_state_references",
|
|
372
|
+
{
|
|
373
|
+
"refresh_state_references.target_entity_ref": "refresh_state.entity_ref"
|
|
374
|
+
}
|
|
375
|
+
).where("refresh_state.entity_id", "=", uid).select("refresh_state_references.source_entity_ref");
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const relationPeers = await tx.from("relations").innerJoin("refresh_state", {
|
|
379
|
+
"refresh_state.entity_ref": "relations.target_entity_ref"
|
|
380
|
+
}).where("relations.originating_entity_id", "=", uid).andWhere("refresh_state.entity_id", "!=", uid).select({ ref: "relations.target_entity_ref" }).union(
|
|
381
|
+
(other) => other.from("relations").innerJoin("refresh_state", {
|
|
382
|
+
"refresh_state.entity_ref": "relations.source_entity_ref"
|
|
383
|
+
}).where("relations.originating_entity_id", "=", uid).andWhere("refresh_state.entity_id", "!=", uid).select({ ref: "relations.source_entity_ref" })
|
|
363
384
|
);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
"refresh_state_references",
|
|
371
|
-
{
|
|
372
|
-
"refresh_state_references.target_entity_ref": "refresh_state.entity_ref"
|
|
373
|
-
}
|
|
374
|
-
).where("refresh_state.entity_id", "=", uid).select("refresh_state_references.source_entity_ref");
|
|
385
|
+
await tx("refresh_state").where("entity_id", uid).delete();
|
|
386
|
+
return new Set(relationPeers.map((p) => p.ref));
|
|
387
|
+
});
|
|
388
|
+
if (relationPeerRefs.size > 0) {
|
|
389
|
+
await this.stitcher.stitch({
|
|
390
|
+
entityRefs: relationPeerRefs
|
|
375
391
|
});
|
|
376
392
|
}
|
|
377
|
-
const relationPeers = await this.database.from("relations").innerJoin("refresh_state", {
|
|
378
|
-
"refresh_state.entity_ref": "relations.target_entity_ref"
|
|
379
|
-
}).where("relations.originating_entity_id", "=", uid).andWhere("refresh_state.entity_id", "!=", uid).select({ ref: "relations.target_entity_ref" }).union(
|
|
380
|
-
(other) => other.from("relations").innerJoin("refresh_state", {
|
|
381
|
-
"refresh_state.entity_ref": "relations.source_entity_ref"
|
|
382
|
-
}).where("relations.originating_entity_id", "=", uid).andWhere("refresh_state.entity_id", "!=", uid).select({ ref: "relations.source_entity_ref" })
|
|
383
|
-
);
|
|
384
|
-
await this.database("refresh_state").where("entity_id", uid).delete();
|
|
385
|
-
await this.stitcher.stitch({
|
|
386
|
-
entityRefs: new Set(relationPeers.map((p) => p.ref))
|
|
387
|
-
});
|
|
388
393
|
}
|
|
389
394
|
async entityAncestry(rootRef) {
|
|
390
395
|
const [rootRow] = await this.database("final_entities").where("final_entities.entity_ref", "=", rootRef).select({
|