@backstage/plugin-catalog-backend 0.0.0-nightly-202201323044 → 0.0.0-nightly-202201422556
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 +20 -12
- package/dist/index.cjs.js +70 -26
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/package.json +16 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
-
## 0.0.0-nightly-
|
|
3
|
+
## 0.0.0-nightly-202201422556
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 68edbbeafd: Fix bug with resource loading in permission integration
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/backend-common@0.0.0-nightly-202201422556
|
|
10
|
+
- @backstage/config@0.0.0-nightly-202201422556
|
|
11
|
+
|
|
12
|
+
## 0.20.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
6
15
|
|
|
@@ -12,20 +21,19 @@
|
|
|
12
21
|
|
|
13
22
|
- 0ae759dad4: Add catalog permission rules.
|
|
14
23
|
- 3b4d8caff6: Allow a custom GithubCredentialsProvider to be passed to the GitHub processors.
|
|
24
|
+
- 6fd70f8bc8: Provide support for Bitbucket servers with custom BaseURLs.
|
|
15
25
|
- 5333451def: Cleaned up API exports
|
|
16
26
|
- 730d01ab1a: Add apply-conditions endpoint for evaluating conditional permissions in catalog backend.
|
|
27
|
+
- 0a6c68582a: Add authorization to catalog-backend entities GET endpoints
|
|
17
28
|
- Updated dependencies
|
|
18
|
-
- @backstage/config@0.
|
|
19
|
-
- @backstage/integration@0.
|
|
20
|
-
- @backstage/backend-common@0.
|
|
21
|
-
- @backstage/plugin-permission-node@0.
|
|
22
|
-
- @backstage/errors@0.
|
|
23
|
-
- @backstage/catalog-client@0.
|
|
24
|
-
- @backstage/catalog-model@0.
|
|
25
|
-
- @backstage/
|
|
26
|
-
- @backstage/types@0.0.0-nightly-202201323044
|
|
27
|
-
- @backstage/plugin-catalog-common@0.0.0-nightly-202201323044
|
|
28
|
-
- @backstage/plugin-permission-common@0.0.0-nightly-202201323044
|
|
29
|
+
- @backstage/config@0.1.12
|
|
30
|
+
- @backstage/integration@0.7.1
|
|
31
|
+
- @backstage/backend-common@0.10.3
|
|
32
|
+
- @backstage/plugin-permission-node@0.3.0
|
|
33
|
+
- @backstage/errors@0.2.0
|
|
34
|
+
- @backstage/catalog-client@0.5.4
|
|
35
|
+
- @backstage/catalog-model@0.9.9
|
|
36
|
+
- @backstage/plugin-permission-common@0.3.1
|
|
29
37
|
|
|
30
38
|
## 0.19.4
|
|
31
39
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -22,8 +22,6 @@ var backendCommon = require('@backstage/backend-common');
|
|
|
22
22
|
var yaml = require('yaml');
|
|
23
23
|
var uuid = require('uuid');
|
|
24
24
|
var knexFactory = require('knex');
|
|
25
|
-
var pluginCatalogCommon = require('@backstage/plugin-catalog-common');
|
|
26
|
-
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
27
25
|
var express = require('express');
|
|
28
26
|
var Router = require('express-promise-router');
|
|
29
27
|
var yn = require('yn');
|
|
@@ -32,7 +30,9 @@ var luxon = require('luxon');
|
|
|
32
30
|
var promClient = require('prom-client');
|
|
33
31
|
var stableStringify = require('fast-json-stable-stringify');
|
|
34
32
|
var catalogClient = require('@backstage/catalog-client');
|
|
33
|
+
var pluginCatalogCommon = require('@backstage/plugin-catalog-common');
|
|
35
34
|
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
35
|
+
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
36
36
|
|
|
37
37
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
38
38
|
|
|
@@ -607,12 +607,13 @@ async function readBitbucketCloud(client, target) {
|
|
|
607
607
|
}
|
|
608
608
|
function parseUrl$3(urlString) {
|
|
609
609
|
const url = new URL(urlString);
|
|
610
|
-
const
|
|
610
|
+
const indexOfProjectSegment = url.pathname.toLowerCase().indexOf("/projects/") + 1;
|
|
611
|
+
const path = url.pathname.substr(indexOfProjectSegment).split("/");
|
|
611
612
|
if (path.length > 3 && path[1].length && path[3].length) {
|
|
612
613
|
return {
|
|
613
614
|
projectSearchPath: escapeRegExp$1(decodeURIComponent(path[1])),
|
|
614
615
|
repoSearchPath: escapeRegExp$1(decodeURIComponent(path[3])),
|
|
615
|
-
catalogPath: `/${decodeURIComponent(path.slice(4).join("/"))}`
|
|
616
|
+
catalogPath: `/${decodeURIComponent(path.slice(4).join("/") + url.search)}`
|
|
616
617
|
};
|
|
617
618
|
}
|
|
618
619
|
throw new Error(`Failed to parse ${urlString}`);
|
|
@@ -3110,7 +3111,7 @@ async function createNextRouter(options) {
|
|
|
3110
3111
|
refreshService,
|
|
3111
3112
|
config,
|
|
3112
3113
|
logger,
|
|
3113
|
-
|
|
3114
|
+
permissionIntegrationRouter
|
|
3114
3115
|
} = options;
|
|
3115
3116
|
const router = Router__default["default"]();
|
|
3116
3117
|
router.use(express__default["default"].json());
|
|
@@ -3126,16 +3127,16 @@ async function createNextRouter(options) {
|
|
|
3126
3127
|
res.status(200).send();
|
|
3127
3128
|
});
|
|
3128
3129
|
}
|
|
3130
|
+
if (permissionIntegrationRouter) {
|
|
3131
|
+
router.use(permissionIntegrationRouter);
|
|
3132
|
+
}
|
|
3129
3133
|
if (entitiesCatalog) {
|
|
3130
|
-
router.
|
|
3131
|
-
resourceType: pluginCatalogCommon.RESOURCE_TYPE_CATALOG_ENTITY,
|
|
3132
|
-
getResource: (resourceRef) => getEntityResource(resourceRef, entitiesCatalog),
|
|
3133
|
-
rules: permissionRules != null ? permissionRules : []
|
|
3134
|
-
})).get("/entities", async (req, res) => {
|
|
3134
|
+
router.get("/entities", async (req, res) => {
|
|
3135
3135
|
const { entities, pageInfo } = await entitiesCatalog.entities({
|
|
3136
3136
|
filter: parseEntityFilterParams(req.query),
|
|
3137
3137
|
fields: parseEntityTransformParams(req.query),
|
|
3138
|
-
pagination: parseEntityPaginationParams(req.query)
|
|
3138
|
+
pagination: parseEntityPaginationParams(req.query),
|
|
3139
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3139
3140
|
});
|
|
3140
3141
|
if (pageInfo.hasNextPage) {
|
|
3141
3142
|
const url = new URL(`http://ignored${req.url}`);
|
|
@@ -3147,7 +3148,8 @@ async function createNextRouter(options) {
|
|
|
3147
3148
|
}).get("/entities/by-uid/:uid", async (req, res) => {
|
|
3148
3149
|
const { uid } = req.params;
|
|
3149
3150
|
const { entities } = await entitiesCatalog.entities({
|
|
3150
|
-
filter: basicEntityFilter({ "metadata.uid": uid })
|
|
3151
|
+
filter: basicEntityFilter({ "metadata.uid": uid }),
|
|
3152
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3151
3153
|
});
|
|
3152
3154
|
if (!entities.length) {
|
|
3153
3155
|
throw new errors.NotFoundError(`No entity with uid ${uid}`);
|
|
@@ -3164,7 +3166,8 @@ async function createNextRouter(options) {
|
|
|
3164
3166
|
kind,
|
|
3165
3167
|
"metadata.namespace": namespace,
|
|
3166
3168
|
"metadata.name": name
|
|
3167
|
-
})
|
|
3169
|
+
}),
|
|
3170
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3168
3171
|
});
|
|
3169
3172
|
if (!entities.length) {
|
|
3170
3173
|
throw new errors.NotFoundError(`No entity named '${name}' found, with kind '${kind}' in namespace '${namespace}'`);
|
|
@@ -3210,17 +3213,6 @@ async function createNextRouter(options) {
|
|
|
3210
3213
|
router.use(backendCommon.errorHandler());
|
|
3211
3214
|
return router;
|
|
3212
3215
|
}
|
|
3213
|
-
async function getEntityResource(resourceRef, entitiesCatalog) {
|
|
3214
|
-
const parsed = catalogModel.parseEntityRef(resourceRef);
|
|
3215
|
-
const { entities } = await entitiesCatalog.entities({
|
|
3216
|
-
filter: basicEntityFilter({
|
|
3217
|
-
kind: parsed.kind,
|
|
3218
|
-
"metadata.namespace": parsed.namespace,
|
|
3219
|
-
"metadata.name": parsed.name
|
|
3220
|
-
})
|
|
3221
|
-
});
|
|
3222
|
-
return entities[0];
|
|
3223
|
-
}
|
|
3224
3216
|
function getBearerToken(authorizationHeader) {
|
|
3225
3217
|
if (typeof authorizationHeader !== "string") {
|
|
3226
3218
|
return void 0;
|
|
@@ -5042,6 +5034,37 @@ const permissionRules = {
|
|
|
5042
5034
|
isEntityOwner
|
|
5043
5035
|
};
|
|
5044
5036
|
|
|
5037
|
+
class AuthorizedEntitiesCatalog {
|
|
5038
|
+
constructor(entitiesCatalog, permissionApi, transformConditions) {
|
|
5039
|
+
this.entitiesCatalog = entitiesCatalog;
|
|
5040
|
+
this.permissionApi = permissionApi;
|
|
5041
|
+
this.transformConditions = transformConditions;
|
|
5042
|
+
}
|
|
5043
|
+
async entities(request) {
|
|
5044
|
+
const authorizeResponse = (await this.permissionApi.authorize([{ permission: pluginCatalogCommon.catalogEntityReadPermission }], { token: request == null ? void 0 : request.authorizationToken }))[0];
|
|
5045
|
+
if (authorizeResponse.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
5046
|
+
return {
|
|
5047
|
+
entities: [],
|
|
5048
|
+
pageInfo: { hasNextPage: false }
|
|
5049
|
+
};
|
|
5050
|
+
}
|
|
5051
|
+
if (authorizeResponse.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
5052
|
+
const permissionFilter = this.transformConditions(authorizeResponse.conditions);
|
|
5053
|
+
return this.entitiesCatalog.entities({
|
|
5054
|
+
...request,
|
|
5055
|
+
filter: (request == null ? void 0 : request.filter) ? { allOf: [permissionFilter, request.filter] } : permissionFilter
|
|
5056
|
+
});
|
|
5057
|
+
}
|
|
5058
|
+
return this.entitiesCatalog.entities(request);
|
|
5059
|
+
}
|
|
5060
|
+
removeEntityByUid(uid) {
|
|
5061
|
+
return this.entitiesCatalog.removeEntityByUid(uid);
|
|
5062
|
+
}
|
|
5063
|
+
entityAncestry(entityRef) {
|
|
5064
|
+
return this.entitiesCatalog.entityAncestry(entityRef);
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
|
|
5045
5068
|
class NextCatalogBuilder {
|
|
5046
5069
|
constructor(env) {
|
|
5047
5070
|
this.refreshInterval = createRandomRefreshInterval({
|
|
@@ -5161,7 +5184,28 @@ class NextCatalogBuilder {
|
|
|
5161
5184
|
parser,
|
|
5162
5185
|
policy
|
|
5163
5186
|
});
|
|
5164
|
-
const
|
|
5187
|
+
const unauthorizedEntitiesCatalog = new NextEntitiesCatalog(dbClient);
|
|
5188
|
+
const entitiesCatalog = new AuthorizedEntitiesCatalog(unauthorizedEntitiesCatalog, permissions, pluginPermissionNode.createConditionTransformer(this.permissionRules));
|
|
5189
|
+
const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
|
5190
|
+
resourceType: pluginCatalogCommon.RESOURCE_TYPE_CATALOG_ENTITY,
|
|
5191
|
+
getResources: async (resourceRefs) => {
|
|
5192
|
+
const { entities } = await unauthorizedEntitiesCatalog.entities({
|
|
5193
|
+
filter: {
|
|
5194
|
+
anyOf: resourceRefs.map((resourceRef) => {
|
|
5195
|
+
const { kind, namespace, name } = catalogModel.parseEntityRef(resourceRef);
|
|
5196
|
+
return basicEntityFilter({
|
|
5197
|
+
kind,
|
|
5198
|
+
"metadata.namespace": namespace,
|
|
5199
|
+
"metadata.name": name
|
|
5200
|
+
});
|
|
5201
|
+
})
|
|
5202
|
+
}
|
|
5203
|
+
});
|
|
5204
|
+
const entitiesByRef = lodash.keyBy(entities, catalogModel.stringifyEntityRef);
|
|
5205
|
+
return resourceRefs.map((resourceRef) => entitiesByRef[catalogModel.stringifyEntityRef(catalogModel.parseEntityRef(resourceRef))]);
|
|
5206
|
+
},
|
|
5207
|
+
rules: this.permissionRules
|
|
5208
|
+
});
|
|
5165
5209
|
const stitcher = new Stitcher(dbClient, logger);
|
|
5166
5210
|
const locationStore = new DefaultLocationStore(dbClient);
|
|
5167
5211
|
const configLocationProvider = new ConfigLocationEntityProvider(config);
|
|
@@ -5178,7 +5222,7 @@ class NextCatalogBuilder {
|
|
|
5178
5222
|
refreshService,
|
|
5179
5223
|
logger,
|
|
5180
5224
|
config,
|
|
5181
|
-
|
|
5225
|
+
permissionIntegrationRouter
|
|
5182
5226
|
});
|
|
5183
5227
|
await connectEntityProviders(processingDatabase, entityProviders);
|
|
5184
5228
|
return {
|