@backstage/plugin-catalog-backend 0.19.4 → 0.20.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 +26 -0
- package/dist/index.cjs.js +221 -23
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +89 -43
- package/package.json +15 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cd529c4094: In order to integrate the permissions system with the refresh endpoint in catalog-backend, a new AuthorizedRefreshService was created as a thin wrapper around the existing refresh service which performs authorization and handles the case when authorization is denied. In order to instantiate AuthorizedRefreshService, a permission client is required, which was added as a new field to `CatalogEnvironment`.
|
|
8
|
+
|
|
9
|
+
The new `permissions` field in `CatalogEnvironment` should already receive the permission client from the `PluginEnvrionment`, so there should be no changes required to the catalog backend setup. See [the create-app changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md) for more details.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 0ae759dad4: Add catalog permission rules.
|
|
14
|
+
- 3b4d8caff6: Allow a custom GithubCredentialsProvider to be passed to the GitHub processors.
|
|
15
|
+
- 6fd70f8bc8: Provide support for Bitbucket servers with custom BaseURLs.
|
|
16
|
+
- 5333451def: Cleaned up API exports
|
|
17
|
+
- 730d01ab1a: Add apply-conditions endpoint for evaluating conditional permissions in catalog backend.
|
|
18
|
+
- 0a6c68582a: Add authorization to catalog-backend entities GET endpoints
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @backstage/config@0.1.12
|
|
21
|
+
- @backstage/integration@0.7.1
|
|
22
|
+
- @backstage/backend-common@0.10.3
|
|
23
|
+
- @backstage/plugin-permission-node@0.3.0
|
|
24
|
+
- @backstage/errors@0.2.0
|
|
25
|
+
- @backstage/catalog-client@0.5.4
|
|
26
|
+
- @backstage/catalog-model@0.9.9
|
|
27
|
+
- @backstage/plugin-permission-common@0.3.1
|
|
28
|
+
|
|
3
29
|
## 0.19.4
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -30,6 +30,9 @@ var luxon = require('luxon');
|
|
|
30
30
|
var promClient = require('prom-client');
|
|
31
31
|
var stableStringify = require('fast-json-stable-stringify');
|
|
32
32
|
var catalogClient = require('@backstage/catalog-client');
|
|
33
|
+
var pluginCatalogCommon = require('@backstage/plugin-catalog-common');
|
|
34
|
+
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
35
|
+
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
33
36
|
|
|
34
37
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
35
38
|
|
|
@@ -604,12 +607,13 @@ async function readBitbucketCloud(client, target) {
|
|
|
604
607
|
}
|
|
605
608
|
function parseUrl$3(urlString) {
|
|
606
609
|
const url = new URL(urlString);
|
|
607
|
-
const
|
|
610
|
+
const indexOfProjectSegment = url.pathname.toLowerCase().indexOf("/projects/") + 1;
|
|
611
|
+
const path = url.pathname.substr(indexOfProjectSegment).split("/");
|
|
608
612
|
if (path.length > 3 && path[1].length && path[3].length) {
|
|
609
613
|
return {
|
|
610
614
|
projectSearchPath: escapeRegExp$1(decodeURIComponent(path[1])),
|
|
611
615
|
repoSearchPath: escapeRegExp$1(decodeURIComponent(path[3])),
|
|
612
|
-
catalogPath: `/${decodeURIComponent(path.slice(4).join("/"))}`
|
|
616
|
+
catalogPath: `/${decodeURIComponent(path.slice(4).join("/") + url.search)}`
|
|
613
617
|
};
|
|
614
618
|
}
|
|
615
619
|
throw new Error(`Failed to parse ${urlString}`);
|
|
@@ -1104,6 +1108,7 @@ class GithubDiscoveryProcessor {
|
|
|
1104
1108
|
constructor(options) {
|
|
1105
1109
|
this.integrations = options.integrations;
|
|
1106
1110
|
this.logger = options.logger;
|
|
1111
|
+
this.githubCredentialsProvider = options.githubCredentialsProvider || integration.DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);
|
|
1107
1112
|
}
|
|
1108
1113
|
async readLocation(location$1, _optional, emit) {
|
|
1109
1114
|
var _a, _b;
|
|
@@ -1116,7 +1121,9 @@ class GithubDiscoveryProcessor {
|
|
|
1116
1121
|
}
|
|
1117
1122
|
const { org, repoSearchPath, catalogPath, branch, host } = parseUrl$2(location$1.target);
|
|
1118
1123
|
const orgUrl = `https://${host}/${org}`;
|
|
1119
|
-
const { headers } = await
|
|
1124
|
+
const { headers } = await this.githubCredentialsProvider.getCredentials({
|
|
1125
|
+
url: orgUrl
|
|
1126
|
+
});
|
|
1120
1127
|
const client = graphql.graphql.defaults({
|
|
1121
1128
|
baseUrl: gitHubConfig.apiBaseUrl,
|
|
1122
1129
|
headers
|
|
@@ -1301,6 +1308,7 @@ class GithubOrgReaderProcessor {
|
|
|
1301
1308
|
}
|
|
1302
1309
|
constructor(options) {
|
|
1303
1310
|
this.integrations = options.integrations;
|
|
1311
|
+
this.githubCredentialsProvider = options.githubCredentialsProvider || integration.DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);
|
|
1304
1312
|
this.logger = options.logger;
|
|
1305
1313
|
}
|
|
1306
1314
|
async readLocation(location, _optional, emit) {
|
|
@@ -1331,8 +1339,7 @@ class GithubOrgReaderProcessor {
|
|
|
1331
1339
|
if (!gitHubConfig) {
|
|
1332
1340
|
throw new Error(`There is no GitHub Org provider that matches ${orgUrl}. Please add a configuration for an integration.`);
|
|
1333
1341
|
}
|
|
1334
|
-
const
|
|
1335
|
-
const { headers, type: tokenType } = await credentialsProvider.getCredentials({
|
|
1342
|
+
const { headers, type: tokenType } = await this.githubCredentialsProvider.getCredentials({
|
|
1336
1343
|
url: orgUrl
|
|
1337
1344
|
});
|
|
1338
1345
|
const client = graphql.graphql.defaults({
|
|
@@ -1357,6 +1364,7 @@ class GithubMultiOrgReaderProcessor {
|
|
|
1357
1364
|
this.integrations = options.integrations;
|
|
1358
1365
|
this.logger = options.logger;
|
|
1359
1366
|
this.orgs = options.orgs;
|
|
1367
|
+
this.githubCredentialsProvider = options.githubCredentialsProvider || integration.DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);
|
|
1360
1368
|
}
|
|
1361
1369
|
async readLocation(location, _optional, emit) {
|
|
1362
1370
|
var _a, _b;
|
|
@@ -1369,11 +1377,10 @@ class GithubMultiOrgReaderProcessor {
|
|
|
1369
1377
|
}
|
|
1370
1378
|
const allUsersMap = /* @__PURE__ */ new Map();
|
|
1371
1379
|
const baseUrl = new URL(location.target).origin;
|
|
1372
|
-
const credentialsProvider = integration.SingleInstanceGithubCredentialsProvider.create(gitHubConfig);
|
|
1373
1380
|
const orgsToProcess = this.orgs.length ? this.orgs : await this.getAllOrgs(gitHubConfig);
|
|
1374
1381
|
for (const orgConfig of orgsToProcess) {
|
|
1375
1382
|
try {
|
|
1376
|
-
const { headers, type: tokenType } = await
|
|
1383
|
+
const { headers, type: tokenType } = await this.githubCredentialsProvider.getCredentials({
|
|
1377
1384
|
url: `${baseUrl}/${orgConfig.name}`
|
|
1378
1385
|
});
|
|
1379
1386
|
const client = graphql.graphql.defaults({
|
|
@@ -1839,7 +1846,7 @@ const defaultEntityDataParser = async function* defaultEntityDataParser2({ data,
|
|
|
1839
1846
|
class GitHubOrgEntityProvider {
|
|
1840
1847
|
constructor(options) {
|
|
1841
1848
|
this.options = options;
|
|
1842
|
-
this.
|
|
1849
|
+
this.githubCredentialsProvider = options.githubCredentialsProvider || integration.SingleInstanceGithubCredentialsProvider.create(options.gitHubConfig);
|
|
1843
1850
|
}
|
|
1844
1851
|
static fromConfig(config, options) {
|
|
1845
1852
|
var _a;
|
|
@@ -1855,7 +1862,8 @@ class GitHubOrgEntityProvider {
|
|
|
1855
1862
|
id: options.id,
|
|
1856
1863
|
orgUrl: options.orgUrl,
|
|
1857
1864
|
logger,
|
|
1858
|
-
gitHubConfig
|
|
1865
|
+
gitHubConfig,
|
|
1866
|
+
githubCredentialsProvider: options.githubCredentialsProvider || integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations)
|
|
1859
1867
|
});
|
|
1860
1868
|
}
|
|
1861
1869
|
getProviderName() {
|
|
@@ -1869,7 +1877,7 @@ class GitHubOrgEntityProvider {
|
|
|
1869
1877
|
throw new Error("Not initialized");
|
|
1870
1878
|
}
|
|
1871
1879
|
const { markReadComplete } = trackProgress(this.options.logger);
|
|
1872
|
-
const { headers, type: tokenType } = await this.
|
|
1880
|
+
const { headers, type: tokenType } = await this.githubCredentialsProvider.getCredentials({
|
|
1873
1881
|
url: this.options.orgUrl
|
|
1874
1882
|
});
|
|
1875
1883
|
const client = graphql.graphql.defaults({
|
|
@@ -3102,7 +3110,8 @@ async function createNextRouter(options) {
|
|
|
3102
3110
|
locationService,
|
|
3103
3111
|
refreshService,
|
|
3104
3112
|
config,
|
|
3105
|
-
logger
|
|
3113
|
+
logger,
|
|
3114
|
+
permissionIntegrationRouter
|
|
3106
3115
|
} = options;
|
|
3107
3116
|
const router = Router__default["default"]();
|
|
3108
3117
|
router.use(express__default["default"].json());
|
|
@@ -3113,16 +3122,21 @@ async function createNextRouter(options) {
|
|
|
3113
3122
|
if (refreshService) {
|
|
3114
3123
|
router.post("/refresh", async (req, res) => {
|
|
3115
3124
|
const refreshOptions = req.body;
|
|
3125
|
+
refreshOptions.authorizationToken = getBearerToken(req.header("authorization"));
|
|
3116
3126
|
await refreshService.refresh(refreshOptions);
|
|
3117
3127
|
res.status(200).send();
|
|
3118
3128
|
});
|
|
3119
3129
|
}
|
|
3130
|
+
if (permissionIntegrationRouter) {
|
|
3131
|
+
router.use(permissionIntegrationRouter);
|
|
3132
|
+
}
|
|
3120
3133
|
if (entitiesCatalog) {
|
|
3121
3134
|
router.get("/entities", async (req, res) => {
|
|
3122
3135
|
const { entities, pageInfo } = await entitiesCatalog.entities({
|
|
3123
3136
|
filter: parseEntityFilterParams(req.query),
|
|
3124
3137
|
fields: parseEntityTransformParams(req.query),
|
|
3125
|
-
pagination: parseEntityPaginationParams(req.query)
|
|
3138
|
+
pagination: parseEntityPaginationParams(req.query),
|
|
3139
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3126
3140
|
});
|
|
3127
3141
|
if (pageInfo.hasNextPage) {
|
|
3128
3142
|
const url = new URL(`http://ignored${req.url}`);
|
|
@@ -3134,7 +3148,8 @@ async function createNextRouter(options) {
|
|
|
3134
3148
|
}).get("/entities/by-uid/:uid", async (req, res) => {
|
|
3135
3149
|
const { uid } = req.params;
|
|
3136
3150
|
const { entities } = await entitiesCatalog.entities({
|
|
3137
|
-
filter: basicEntityFilter({ "metadata.uid": uid })
|
|
3151
|
+
filter: basicEntityFilter({ "metadata.uid": uid }),
|
|
3152
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3138
3153
|
});
|
|
3139
3154
|
if (!entities.length) {
|
|
3140
3155
|
throw new errors.NotFoundError(`No entity with uid ${uid}`);
|
|
@@ -3151,7 +3166,8 @@ async function createNextRouter(options) {
|
|
|
3151
3166
|
kind,
|
|
3152
3167
|
"metadata.namespace": namespace,
|
|
3153
3168
|
"metadata.name": name
|
|
3154
|
-
})
|
|
3169
|
+
}),
|
|
3170
|
+
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3155
3171
|
});
|
|
3156
3172
|
if (!entities.length) {
|
|
3157
3173
|
throw new errors.NotFoundError(`No entity named '${name}' found, with kind '${kind}' in namespace '${namespace}'`);
|
|
@@ -3197,6 +3213,13 @@ async function createNextRouter(options) {
|
|
|
3197
3213
|
router.use(backendCommon.errorHandler());
|
|
3198
3214
|
return router;
|
|
3199
3215
|
}
|
|
3216
|
+
function getBearerToken(authorizationHeader) {
|
|
3217
|
+
if (typeof authorizationHeader !== "string") {
|
|
3218
|
+
return void 0;
|
|
3219
|
+
}
|
|
3220
|
+
const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
|
|
3221
|
+
return matches == null ? void 0 : matches[1];
|
|
3222
|
+
}
|
|
3200
3223
|
|
|
3201
3224
|
function isLocationEntity(entity) {
|
|
3202
3225
|
return entity.kind === "Location";
|
|
@@ -4858,6 +4881,25 @@ class DefaultRefreshService {
|
|
|
4858
4881
|
}
|
|
4859
4882
|
}
|
|
4860
4883
|
|
|
4884
|
+
class AuthorizedRefreshService {
|
|
4885
|
+
constructor(service, permissionApi) {
|
|
4886
|
+
this.service = service;
|
|
4887
|
+
this.permissionApi = permissionApi;
|
|
4888
|
+
}
|
|
4889
|
+
async refresh(options) {
|
|
4890
|
+
const authorizeResponse = (await this.permissionApi.authorize([
|
|
4891
|
+
{
|
|
4892
|
+
permission: pluginCatalogCommon.catalogEntityRefreshPermission,
|
|
4893
|
+
resourceRef: options.entityRef
|
|
4894
|
+
}
|
|
4895
|
+
], { token: options.authorizationToken }))[0];
|
|
4896
|
+
if (authorizeResponse.result !== pluginPermissionCommon.AuthorizeResult.ALLOW) {
|
|
4897
|
+
throw new errors.NotAllowedError();
|
|
4898
|
+
}
|
|
4899
|
+
await this.service.refresh(options);
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4861
4903
|
class Connection {
|
|
4862
4904
|
constructor(config) {
|
|
4863
4905
|
this.config = config;
|
|
@@ -4907,6 +4949,122 @@ async function connectEntityProviders(db, providers) {
|
|
|
4907
4949
|
}));
|
|
4908
4950
|
}
|
|
4909
4951
|
|
|
4952
|
+
const createCatalogPermissionRule = pluginPermissionNode.makeCreatePermissionRule();
|
|
4953
|
+
|
|
4954
|
+
const hasAnnotation = createCatalogPermissionRule({
|
|
4955
|
+
name: "HAS_ANNOTATION",
|
|
4956
|
+
description: "Allow entities which are annotated with the specified annotation",
|
|
4957
|
+
apply: (resource, annotation) => {
|
|
4958
|
+
var _a;
|
|
4959
|
+
return !!((_a = resource.metadata.annotations) == null ? void 0 : _a.hasOwnProperty(annotation));
|
|
4960
|
+
},
|
|
4961
|
+
toQuery: (annotation) => ({
|
|
4962
|
+
key: `metadata.annotations.${annotation}`
|
|
4963
|
+
})
|
|
4964
|
+
});
|
|
4965
|
+
|
|
4966
|
+
const isEntityKind = createCatalogPermissionRule({
|
|
4967
|
+
name: "IS_ENTITY_KIND",
|
|
4968
|
+
description: "Allow entities with the specified kind",
|
|
4969
|
+
apply(resource, kinds) {
|
|
4970
|
+
const resourceKind = resource.kind.toLocaleLowerCase("en-US");
|
|
4971
|
+
return kinds.some((kind) => kind.toLocaleLowerCase("en-US") === resourceKind);
|
|
4972
|
+
},
|
|
4973
|
+
toQuery(kinds) {
|
|
4974
|
+
return {
|
|
4975
|
+
key: "kind",
|
|
4976
|
+
values: kinds.map((kind) => kind.toLocaleLowerCase("en-US"))
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4979
|
+
});
|
|
4980
|
+
|
|
4981
|
+
const isEntityOwner = createCatalogPermissionRule({
|
|
4982
|
+
name: "IS_ENTITY_OWNER",
|
|
4983
|
+
description: "Allow entities owned by the current user",
|
|
4984
|
+
apply: (resource, claims) => {
|
|
4985
|
+
if (!resource.relations) {
|
|
4986
|
+
return false;
|
|
4987
|
+
}
|
|
4988
|
+
return resource.relations.filter((relation) => relation.type === catalogModel.RELATION_OWNED_BY).some((relation) => claims.includes(catalogModel.stringifyEntityRef(relation.target)));
|
|
4989
|
+
},
|
|
4990
|
+
toQuery: (claims) => ({
|
|
4991
|
+
key: "relations.ownedBy",
|
|
4992
|
+
values: claims
|
|
4993
|
+
})
|
|
4994
|
+
});
|
|
4995
|
+
|
|
4996
|
+
const hasLabel = createCatalogPermissionRule({
|
|
4997
|
+
name: "HAS_LABEL",
|
|
4998
|
+
description: "Allow entities which have the specified label metadata.",
|
|
4999
|
+
apply: (resource, label) => {
|
|
5000
|
+
var _a;
|
|
5001
|
+
return !!((_a = resource.metadata.labels) == null ? void 0 : _a.hasOwnProperty(label));
|
|
5002
|
+
},
|
|
5003
|
+
toQuery: (label) => ({
|
|
5004
|
+
key: `metadata.labels.${label}`
|
|
5005
|
+
})
|
|
5006
|
+
});
|
|
5007
|
+
|
|
5008
|
+
const createPropertyRule = (propertyType) => createCatalogPermissionRule({
|
|
5009
|
+
name: `HAS_${propertyType.toUpperCase()}`,
|
|
5010
|
+
description: `Allow entities which have the specified ${propertyType} subfield.`,
|
|
5011
|
+
apply: (resource, key, value) => {
|
|
5012
|
+
const foundValue = lodash.get(resource[propertyType], key);
|
|
5013
|
+
if (value !== void 0) {
|
|
5014
|
+
return value === foundValue;
|
|
5015
|
+
}
|
|
5016
|
+
return !!foundValue;
|
|
5017
|
+
},
|
|
5018
|
+
toQuery: (key, value) => ({
|
|
5019
|
+
key: `${propertyType}.${key}`,
|
|
5020
|
+
...value !== void 0 && { values: [value] }
|
|
5021
|
+
})
|
|
5022
|
+
});
|
|
5023
|
+
|
|
5024
|
+
const hasMetadata = createPropertyRule("metadata");
|
|
5025
|
+
|
|
5026
|
+
const hasSpec = createPropertyRule("spec");
|
|
5027
|
+
|
|
5028
|
+
const permissionRules = {
|
|
5029
|
+
hasAnnotation,
|
|
5030
|
+
hasLabel,
|
|
5031
|
+
hasMetadata,
|
|
5032
|
+
hasSpec,
|
|
5033
|
+
isEntityKind,
|
|
5034
|
+
isEntityOwner
|
|
5035
|
+
};
|
|
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
|
+
|
|
4910
5068
|
class NextCatalogBuilder {
|
|
4911
5069
|
constructor(env) {
|
|
4912
5070
|
this.refreshInterval = createRandomRefreshInterval({
|
|
@@ -4923,6 +5081,7 @@ class NextCatalogBuilder {
|
|
|
4923
5081
|
this.processors = [];
|
|
4924
5082
|
this.processorsReplace = false;
|
|
4925
5083
|
this.parser = void 0;
|
|
5084
|
+
this.permissionRules = Object.values(permissionRules);
|
|
4926
5085
|
}
|
|
4927
5086
|
addEntityPolicy(...policies) {
|
|
4928
5087
|
this.entityPolicies.push(...policies);
|
|
@@ -4972,12 +5131,19 @@ class NextCatalogBuilder {
|
|
|
4972
5131
|
getDefaultProcessors() {
|
|
4973
5132
|
const { config, logger, reader } = this.env;
|
|
4974
5133
|
const integrations = integration.ScmIntegrations.fromConfig(config);
|
|
5134
|
+
const githubCredentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
|
4975
5135
|
return [
|
|
4976
5136
|
new FileReaderProcessor(),
|
|
4977
5137
|
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
|
|
4978
5138
|
AzureDevOpsDiscoveryProcessor.fromConfig(config, { logger }),
|
|
4979
|
-
GithubDiscoveryProcessor.fromConfig(config, {
|
|
4980
|
-
|
|
5139
|
+
GithubDiscoveryProcessor.fromConfig(config, {
|
|
5140
|
+
logger,
|
|
5141
|
+
githubCredentialsProvider
|
|
5142
|
+
}),
|
|
5143
|
+
GithubOrgReaderProcessor.fromConfig(config, {
|
|
5144
|
+
logger,
|
|
5145
|
+
githubCredentialsProvider
|
|
5146
|
+
}),
|
|
4981
5147
|
GitLabDiscoveryProcessor.fromConfig(config, { logger }),
|
|
4982
5148
|
new UrlReaderProcessor({ reader, logger }),
|
|
4983
5149
|
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
|
|
@@ -4988,9 +5154,12 @@ class NextCatalogBuilder {
|
|
|
4988
5154
|
this.parser = parser;
|
|
4989
5155
|
return this;
|
|
4990
5156
|
}
|
|
5157
|
+
addPermissionRules(...permissionRules) {
|
|
5158
|
+
this.permissionRules.push(...permissionRules);
|
|
5159
|
+
}
|
|
4991
5160
|
async build() {
|
|
4992
5161
|
var _a, _b;
|
|
4993
|
-
const { config, database, logger } = this.env;
|
|
5162
|
+
const { config, database, logger, permissions } = this.env;
|
|
4994
5163
|
const policy = this.buildEntityPolicy();
|
|
4995
5164
|
const processors = this.buildProcessors();
|
|
4996
5165
|
const parser = this.parser || defaultEntityDataParser;
|
|
@@ -5015,7 +5184,28 @@ class NextCatalogBuilder {
|
|
|
5015
5184
|
parser,
|
|
5016
5185
|
policy
|
|
5017
5186
|
});
|
|
5018
|
-
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 entitiesCatalog.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
|
+
});
|
|
5019
5209
|
const stitcher = new Stitcher(dbClient, logger);
|
|
5020
5210
|
const locationStore = new DefaultLocationStore(dbClient);
|
|
5021
5211
|
const configLocationProvider = new ConfigLocationEntityProvider(config);
|
|
@@ -5024,16 +5214,15 @@ class NextCatalogBuilder {
|
|
|
5024
5214
|
const locationsCatalog = new DatabaseLocationsCatalog(db);
|
|
5025
5215
|
const locationAnalyzer = (_b = this.locationAnalyzer) != null ? _b : new RepoLocationAnalyzer(logger, integrations);
|
|
5026
5216
|
const locationService = new DefaultLocationService(locationStore, orchestrator);
|
|
5027
|
-
const refreshService = new DefaultRefreshService({
|
|
5028
|
-
database: processingDatabase
|
|
5029
|
-
});
|
|
5217
|
+
const refreshService = new AuthorizedRefreshService(new DefaultRefreshService({ database: processingDatabase }), permissions);
|
|
5030
5218
|
const router = await createNextRouter({
|
|
5031
5219
|
entitiesCatalog,
|
|
5032
5220
|
locationAnalyzer,
|
|
5033
5221
|
locationService,
|
|
5034
5222
|
refreshService,
|
|
5035
5223
|
logger,
|
|
5036
|
-
config
|
|
5224
|
+
config,
|
|
5225
|
+
permissionIntegrationRouter
|
|
5037
5226
|
});
|
|
5038
5227
|
await connectEntityProviders(processingDatabase, entityProviders);
|
|
5039
5228
|
return {
|
|
@@ -5180,6 +5369,7 @@ class CatalogBuilder {
|
|
|
5180
5369
|
buildProcessors() {
|
|
5181
5370
|
const { config, logger, reader } = this.env;
|
|
5182
5371
|
const integrations = integration.ScmIntegrations.fromConfig(config);
|
|
5372
|
+
const githubCredentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
|
5183
5373
|
this.checkDeprecatedReaderProcessors();
|
|
5184
5374
|
const placeholderResolvers = {
|
|
5185
5375
|
json: jsonPlaceholderResolver,
|
|
@@ -5197,7 +5387,13 @@ class CatalogBuilder {
|
|
|
5197
5387
|
new BuiltinKindsEntityProcessor()
|
|
5198
5388
|
];
|
|
5199
5389
|
if (!this.processorsReplace) {
|
|
5200
|
-
processors.push(new FileReaderProcessor(), BitbucketDiscoveryProcessor.fromConfig(config, { logger }), GithubDiscoveryProcessor.fromConfig(config, {
|
|
5390
|
+
processors.push(new FileReaderProcessor(), BitbucketDiscoveryProcessor.fromConfig(config, { logger }), GithubDiscoveryProcessor.fromConfig(config, {
|
|
5391
|
+
logger,
|
|
5392
|
+
githubCredentialsProvider
|
|
5393
|
+
}), AzureDevOpsDiscoveryProcessor.fromConfig(config, { logger }), GithubOrgReaderProcessor.fromConfig(config, {
|
|
5394
|
+
logger,
|
|
5395
|
+
githubCredentialsProvider
|
|
5396
|
+
}), GitLabDiscoveryProcessor.fromConfig(config, { logger }), new UrlReaderProcessor({ reader, logger }), CodeOwnersProcessor.fromConfig(config, { logger, reader }), new LocationEntityProcessor({ integrations }), new AnnotateLocationEntityProcessor({ integrations }));
|
|
5201
5397
|
}
|
|
5202
5398
|
processors.push(...this.processors);
|
|
5203
5399
|
return processors;
|
|
@@ -5451,11 +5647,13 @@ exports.NextCatalogBuilder = NextCatalogBuilder;
|
|
|
5451
5647
|
exports.PlaceholderProcessor = PlaceholderProcessor;
|
|
5452
5648
|
exports.StaticLocationProcessor = StaticLocationProcessor;
|
|
5453
5649
|
exports.UrlReaderProcessor = UrlReaderProcessor;
|
|
5650
|
+
exports.createCatalogPermissionRule = createCatalogPermissionRule;
|
|
5454
5651
|
exports.createNextRouter = createNextRouter;
|
|
5455
5652
|
exports.createRandomRefreshInterval = createRandomRefreshInterval;
|
|
5456
5653
|
exports.createRouter = createRouter;
|
|
5457
5654
|
exports.durationText = durationText;
|
|
5458
5655
|
exports.parseEntityYaml = parseEntityYaml;
|
|
5656
|
+
exports.permissionRules = permissionRules;
|
|
5459
5657
|
exports.results = results;
|
|
5460
5658
|
exports.runPeriodically = runPeriodically;
|
|
5461
5659
|
//# sourceMappingURL=index.cjs.js.map
|